サーバー間のトラフィックを保護するためにIptablesファイアウォールを設定する方法
序章
アプリケーションセットアップの個別のコンポーネントを異なるノードにデプロイすることは、負荷を減らし、水平方向のスケーリングを開始するための一般的な方法です。 典型的な例は、アプリケーションとは別のサーバーでデータベースを構成することです。 この設定には多くの利点がありますが、ネットワークを介した接続には、新しい一連のセキュリティ上の懸念が伴います。
このガイドでは、分散セットアップで各サーバーに単純なファイアウォールをセットアップする方法を示します。 他のトラフィックを拒否しながら、コンポーネント間の正当なトラフィックを許可するようにポリシーを構成します。
このガイドのデモでは、2台のUbuntu14.04サーバーを使用します。 1つはNginxで提供されるWordPressインスタンスを持ち、もう1つはアプリケーションのMySQLデータベースをホストします。 この設定を例として使用しますが、独自のサーバー要件に適合するように関連する手法を推定できるはずです。
前提条件
開始するには、2台の新しいUbuntu14.04サーバーが必要です。 それぞれにsudo
権限を持つ通常のユーザーアカウントを追加します。 これを正しく行う方法については、Ubuntu14.04初期サーバーセットアップガイドに従ってください。
保護するアプリケーションのセットアップは、このガイドに基づいています。 フォローする場合は、そのチュートリアルに示されているように、アプリケーションサーバーとデータベースサーバーをセットアップします。
基本的なファイアウォールの設定
まず、各サーバーにベースラインファイアウォール構成を実装します。 実装するポリシーは、セキュリティファーストのアプローチを採用しています。 SSHトラフィック以外のほとんどすべてをロックダウンしてから、特定のアプリケーション用にファイアウォールに穴を開けます。
このガイドのファイアウォールは、必要な基本的なセットアップを提供します。 iptables-persistent
パッケージをインストールし、基本的なルールを/etc/iptables/rules.v4
ファイルに貼り付けます。
sudo apt-get update sudo apt-get install iptables-persistent sudo nano /etc/iptables/rules.v4
/etc/iptables/rules.v4
*filter # Allow all outgoing, but drop incoming and forwarding packets by default :INPUT DROP [0:0] :FORWARD DROP [0:0] :OUTPUT ACCEPT [0:0] # Custom per-protocol chains :UDP - [0:0] :TCP - [0:0] :ICMP - [0:0] # Acceptable UDP traffic # Acceptable TCP traffic -A TCP -p tcp --dport 22 -j ACCEPT # Acceptable ICMP traffic # Boilerplate acceptance policy -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT -A INPUT -i lo -j ACCEPT # Drop invalid packets -A INPUT -m conntrack --ctstate INVALID -j DROP # Pass traffic to protocol-specific chains ## Only allow new connections (established and related should already be handled) ## For TCP, additionally only allow new SYN packets since that is the only valid ## method for establishing a new TCP connection -A INPUT -p udp -m conntrack --ctstate NEW -j UDP -A INPUT -p tcp --syn -m conntrack --ctstate NEW -j TCP -A INPUT -p icmp -m conntrack --ctstate NEW -j ICMP # Reject anything that's fallen through to this point ## Try to be protocol-specific w/ rejection message -A INPUT -p udp -j REJECT --reject-with icmp-port-unreachable -A INPUT -p tcp -j REJECT --reject-with tcp-reset -A INPUT -j REJECT --reject-with icmp-proto-unreachable # Commit the changes COMMIT *raw :PREROUTING ACCEPT [0:0] :OUTPUT ACCEPT [0:0] COMMIT *nat :PREROUTING ACCEPT [0:0] :INPUT ACCEPT [0:0] :OUTPUT ACCEPT [0:0] :POSTROUTING ACCEPT [0:0] COMMIT *security :INPUT ACCEPT [0:0] :FORWARD ACCEPT [0:0] :OUTPUT ACCEPT [0:0] COMMIT *mangle :PREROUTING ACCEPT [0:0] :INPUT ACCEPT [0:0] :FORWARD ACCEPT [0:0] :OUTPUT ACCEPT [0:0] :POSTROUTING ACCEPT [0:0] COMMIT
これをライブ環境で実装している場合ファイアウォールルールをまだリロードしないでください。 ここで概説されている基本的なルールセットをロードすると、アプリケーションとデータベースサーバー間の接続がすぐに切断されます。 リロードする前に、運用上のニーズを反映するようにルールを調整する必要があります。
サービスで使用されているポートを確認する
コンポーネント間の通信を可能にする例外を追加するには、使用されているネットワークポートを知る必要があります。 構成ファイルを調べることで正しいネットワークポートを見つけることができますが、アプリケーションに依存しない正しいポートを見つける方法は、各マシンで接続をリッスンしているサービスを確認することです。
netstat
ツールを使用してこれを見つけることができます。 このアプリケーションはIPv4を介してのみ通信しているため、-4
引数を追加しますが、IPv6を使用している場合はそれを削除できます。 実行中のサービスを見つけるために必要な他の引数は-plunt
です。
Webサーバーでは、次のように表示されます。
sudo netstat -4plunt
OutputActive Internet connections (only servers) Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN 1058/sshd tcp 0 0 0.0.0.0:80 0.0.0.0:* LISTEN 4187/nginx
最初に強調表示されている列は、回線の終わりに向かって強調表示されているサービスがリッスンしているIPアドレスとポートを示しています。 特別な0.0.0.0
アドレスは、問題のサービスが使用可能なすべてのアドレスをリッスンしていることを意味します。
データベースサーバーでは、次のように表示されます。
sudo netstat -4plunt
OutputActive Internet connections (only servers) Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN 1097/sshd tcp 0 0 192.0.2.30:3306 0.0.0.0:* LISTEN 3112/mysqld
これらの列はまったく同じように読み取ることができます。 上記の例では、192.0.2.30
アドレスはデータベースサーバーのプライベートIPアドレスを表します。 アプリケーションのセットアップでは、セキュリティ上の理由からMySQLをプライベートインターフェイスにロックダウンしました。
このステップで見つけた値に注意してください。 これらは、ファイアウォール構成を調整するために必要なネットワークの詳細です。
このシナリオ例では、Webサーバーで、次のポートにアクセスできることを確認する必要があることに注意してください。
- すべてのアドレスのポート80
- すべてのアドレスのポート22(ファイアウォールルールですでに考慮されています)
データベースサーバーは、次のポートにアクセスできることを確認する必要があります。
- アドレス
192.0.2.30
(またはそれに関連付けられたインターフェイス)のポート3306 - すべてのアドレスのポート22(ファイアウォールルールですでに考慮されています)
Webサーバーファイアウォールルールを調整する
必要なポート情報が得られたので、Webサーバーのファイアウォールルールセットを調整します。 sudo
権限でエディターでルールファイルを開きます。
sudo nano /etc/iptables/rules.v4
Webサーバーでは、受け入れ可能なトラフィックのリストにポート80を追加する必要があります。 サーバーは使用可能なすべてのアドレスをリッスンしているため、インターフェースまたは宛先アドレスによってルールを制限することはありません。
Web訪問者は、TCPプロトコルを使用して接続します。 基本的なフレームワークには、TCPアプリケーションの例外用にTCP
と呼ばれるカスタムチェーンがすでにあります。 SSHポートの例外のすぐ下にあるそのチェーンにポート80を追加できます。
/etc/iptables/rules.v4
*filter . . . # Acceptable TCP traffic -A TCP -p tcp --dport 22 -j ACCEPT -A TCP -p tcp --dport 80 -j ACCEPT . . .
Webサーバーがデータベースサーバーとの接続を開始します。 発信トラフィックはファイアウォールで制限されておらず、確立された接続に関連付けられた着信トラフィックが許可されているため、このサーバーで追加のポートを開いてこの接続を許可する必要はありません。
終了したら、ファイルを保存して閉じます。 現在、Webサーバーには、他のすべてをブロックしながらすべての正当なトラフィックを許可するファイアウォールポリシーがあります。
ルールファイルの構文エラーをテストします。
sudo iptables-restore -t < /etc/iptables/rules.v4
構文エラーが表示されない場合は、ファイアウォールをリロードして新しいルールセットを実装します。
sudo service iptables-persistent reload
データベースサーバーのファイアウォールルールを調整する
データベースサーバーでは、サーバーのプライベートIPアドレスのポート3306
へのアクセスを許可する必要があります。 この場合、そのアドレスは192.0.2.30
でした。 このアドレス宛てのアクセスを具体的に制限することも、そのアドレスが割り当てられているインターフェイスと照合してアクセスを制限することもできます。
そのアドレスに関連付けられているネットワークインターフェイスを見つけるには、次のように入力します。
ip -4 addr show scope global
Output2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000 inet 203.0.113.5/24 brd 104.236.113.255 scope global eth0 valid_lft forever preferred_lft forever 3: eth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000 inet 192.0.2.30/24 brd 192.0.2.255 scope global eth1 valid_lft forever preferred_lft forever
強調表示された領域は、eth1
インターフェースがそのアドレスに関連付けられていることを示しています。
次に、データベースサーバーのファイアウォールルールを調整します。 データベースサーバーでsudo
権限を持つルールファイルを開きます。
sudo nano /etc/iptables/rules.v4
ここでも、TCP
チェーンにルールを追加して、Webサーバーとデータベースサーバー間の接続の例外を形成します。
問題の実際のアドレスに基づいてアクセスを制限する場合は、次のようなルールを追加します。
/etc/iptables/rules.v4
*filter . . . # Acceptable TCP traffic -A TCP -p tcp --dport 22 -j ACCEPT -A TCP -p tcp --dport 3306 -d 192.0.2.30 -j ACCEPT . . .
そのアドレスを収容するインターフェースに基づいて例外を許可したい場合は、代わりに次のようなルールを追加できます。
/etc/iptables/rules.v4
*filter . . . # Acceptable TCP traffic -A TCP -p tcp --dport 22 -j ACCEPT -A TCP -p tcp --dport 3306 -i eth1 -j ACCEPT . . .
終了したら、ファイルを保存して閉じます。
次のコマンドで構文エラーを確認します。
sudo iptables-restore -t < /etc/iptables/rules.v4
準備ができたら、ファイアウォールルールをリロードします。
sudo service iptables-persistent reload
これで、サーバー間の必要なデータフローを制限することなく、両方のサーバーを保護する必要があります。
結論
適切なファイアウォールの実装は、アプリケーションをセットアップするときに常に展開計画の一部である必要があります。 NginxとMySQLを実行している2つのサーバーを使用してWordPressインスタンスを提供するこの構成を示しましたが、上記の手法は、特定のテクノロジーの選択に関係なく適用できます。
ファイアウォールとiptables
の詳細については、次のガイドを参照してください。