サーバー間のトラフィックを保護するためのIptablesファイアウォールの設定方法

はじめに

アプリケーションのセットアップにおいて、異なるノードに離散的なコンポーネントを展開することは、負荷を軽減し水平方向にスケーリングを行う一般的な方法です。典型的な例として、データベースをアプリケーションとは別のサーバーに設定することが挙げられます。このようなセットアップにはいくつかの利点がありますが、ネットワークを介して接続する場合には新たなセキュリティ上の懸念が生じます。

このガイドでは、分散型のセットアップにおいて各サーバーにファイアウォールを設定する方法を説明します。コンポーネント間の意図したトラフィックを許可し、他のトラフィックを拒否するようにポリシーを設定します。

また、DigitalOceanのCloud Firewallsを設定することもできます。これはDigitalOceanのインフラストラクチャ上のサーバーに対して追加の外部レイヤーとして実行されます。これにより、サーバー自体にファイアウォールを設定する必要はありません。

このガイドのデモンストレーションでは、2つのUbuntu 22.04サーバーを使用します。1つはNginxで提供されるWebアプリケーションを持ち、もう1つはアプリケーションのMySQLデータベースをホストします。このセットアップを例として使用しますが、自分のサーバーの要件に合わせて関連する手法を応用することができるはずです。

前提条件

はじめるには、2つの新しいUbuntu 22.04サーバが必要です。それぞれにsudo権限を持つ通常のユーザーアカウントを追加します。これを行うには、Ubuntu 22.04初期サーバーセットアップガイドに従ってください。

セキュリティを確保するアプリケーションのセットアップは、このガイドに基づいています。その例に続く場合は、チュートリアルで指示されているようにアプリケーションとデータベースサーバーをセットアップしてください。それ以外の場合は、この記事を一般的な参考として使用できます。

ステップ1 — ファイアウォールの設定

まず、各サーバーに基本的なファイアウォール設定を実装します。この設定では、セキュリティを最優先に考えてほとんどすべてを制限し、SSHトラフィック以外の通信を遮断します。その後、特定のアプリケーションのためにファイアウォールに穴を開けます。

このガイドではiptablesの構文を使用します。Ubuntu 22.04では、nftablesバックエンドを使用してiptablesが自動的にインストールされるため、追加のパッケージをインストールする必要はありません。

nanoまたはお好きなテキストエディタを使用して、/etc/iptables/rules.v4ファイルを開きます。

  1. sudo nano /etc/iptables/rules.v4

ファイアウォールテンプレートガイドから設定を貼り付けてください。

/etc/iptables/rules.v4
*filter
# デフォルトではすべての送信を許可し、受信および転送パケットをドロップします
:INPUT DROP [0:0]
:FORWARD DROP [0:0]
:OUTPUT ACCEPT [0:0]

# プロトコルごとのカスタムチェーン
:UDP - [0:0]
:TCP - [0:0]
:ICMP - [0:0]

# 許可されるUDPトラフィック

# 許可されるTCPトラフィック
-A TCP -p tcp --dport 22 -j ACCEPT

# 許可されるICMPトラフィック

# 標準的な受け入れポリシー
-A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
-A INPUT -i lo -j ACCEPT

# 無効なパケットをドロップ
-A INPUT -m conntrack --ctstate INVALID -j DROP

# プロトコル固有のチェーンにトラフィックを渡す
## 新しい接続のみ許可する(確立および関連付けられた接続は既に処理されているはず)
## TCPの場合、新しいSYNパケットのみ許可する(新しいTCP接続を確立するための唯一の有効な方法)

-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

# ここまで到達した場合はすべて拒否
## プロトコルごとの拒否メッセージを試みる
-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

*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

ファイルを保存して閉じます。 nanoを使用している場合は、Ctrl+Xを押して終了し、プロンプトが表示されたらYを押してEnterキーを押します。

ライブ環境でこれを実装している場合は、まだファイアウォールのルールをリロードしないでください。ここで概説したルールセットをロードすると、アプリケーションとデータベースサーバーの接続が直ちに切断されます。ルールを運用要件に合わせて調整する必要があります。

ステップ2 — サービスで使用されているポートを特定する

あなたのコンポーネント間での通信を許可するためには、使用されているネットワークポートを知る必要があります。正しいネットワークポートを見つけるためには、設定ファイルを調べることができますが、アプリケーションに依存しない方法として、各マシンで接続を待ち受けているサービスを確認するだけで正しいポートを見つけることができます。

これを知るためには、netstat ツールを使用することができます。アプリケーションがIPv4で通信している場合は、-4 引数を追加しますが、IPv6を使用している場合はそれを削除することができます。実行中のサービスを見つけるために必要な他の引数は、-p-l-u-n、および -t であり、これらを -plunt として提供します。

これらの引数は次のように分解できます:

  • p: Show the PID and name of the program to which each socket belongs.
  • l: Show only listening sockets.
  • u: Show UDP traffic.
  • n: Show numeric output instead of service names.
  • t: Show TCP traffic.
  1. sudo netstat -4plunt

Webサーバーでは、出力は次のようになる可能性があります:

Output
Active 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 アドレスは、対象のサービスがすべての利用可能なアドレスで待ち受けていることを意味します。

データベースサーバーでは、出力は次のようになる可能性があります:

  1. sudo netstat -4plunt
Output
Active 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

ステップ3 — ウェブサーバーのファイアウォールルールの調整

必要なポート情報を取得したら、ウェブサーバーのファイアウォールルールを調整します。エディタでルールファイルをsudo権限で開きます:

  1. sudo nano /etc/iptables/rules.v4

ウェブサーバーで、許可されるトラフィックのリストにポート80を追加する必要があります。サーバーはすべての利用可能なアドレスでリスニングしているため(通常、ウェブサーバーはどこからでもアクセスできることが期待されます)、インターフェースや宛先アドレスでルールを制限しません。

ウェブ訪問者はTCPプロトコルを使用して接続します。フレームワークにはすでにTCPアプリケーションの例外のためのカスタムチェーンTCPがあります。SSHポートの例外のすぐ下にポート80を追加できます:

/etc/iptables/rules.v4
*filter
. . .

# 許可されるTCPトラフィック
-A TCP -p tcp --dport 22 -j ACCEPT
-A TCP -p tcp --dport 80 -j ACCEPT

. . .

あなたのウェブサーバーはデータベースサーバーとの接続を開始します。ファイアウォールでは送信トラフィックは制限されず、確立した接続に関連する着信トラフィックは許可されますので、この接続を許可するために追加のポートを開く必要はありません。

作業が終了したら、ファイルを保存して閉じます。ウェブサーバーには、合法的なトラフィックを許可し、それ以外のすべてをブロックするファイアウォールポリシーが設定されました。

構文エラーがないかルールファイルをテストします。

  1. sudo iptables-restore -t < /etc/iptables/rules.v4

構文エラーが表示されない場合、新しいルールセットを実装するためにファイアウォールをリロードします。

  1. sudo service iptables-persistent reload

ステップ4 — データベースサーバーのファイアウォールルールを調整する

データベースサーバーでは、サーバーのプライベートIPアドレスのポート3306へのアクセスを許可する必要があります。この場合、そのアドレスは192.0.2.30でした。このアドレスに特定のアクセスを制限するか、そのアドレスに割り当てられたインターフェースに一致するようにアクセスを制限することができます。

そのアドレスに関連付けられたネットワークインターフェースを見つけるには、ip -4 addr show scope globalを実行します。

  1. ip -4 addr show scope global
Output
2: 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特権を持ってルールファイルを開きます。

  1. sudo nano /etc/iptables/rules.v4

再び、ウェブサーバーとデータベースサーバー間の接続に例外を形成するために、TCPチェインにルールを追加します。

問題の実際のアドレスに基づいてアクセスを制限する場合は、次のようにルールを追加します:

/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

. . .

作業が終了したら、ファイルを保存して閉じます。

次のコマンドで構文エラーをチェックします:

  1. sudo iptables-restore -t < /etc/iptables/rules.v4

準備ができたら、ファイアウォールのルールをリロードします:

  1. sudo service iptables-persistent reload

これで、両方のサーバーは必要なデータの流れを制限することなく保護されるはずです。

結論

アプリケーションをセットアップする際には、適切なファイアウォールの実装が常に展開計画の一部であるべきです。今回はNginxとMySQLを実行する2つのサーバーを使用してこの設定を説明しましたが、上記で示したテクニックは、特定の技術選択に関係なく適用できます。

ファイアウォールとiptablesについて詳しく学ぶには、以下のガイドを参照してください:

Source:
https://www.digitalocean.com/community/tutorials/how-to-set-up-an-iptables-firewall-to-protect-traffic-between-your-servers