PaloAltoでWebアクセスへのパケット検知してみた~Gateway Load Balancerを添えて~
はじめに
こんにちは。コンサルティング部の津谷です。
今回はゲートウェイロードバランサーについて記事を書きます。
皆さんは、Webサーバへのパブリックアクセスの際に不正検知・防御を行った経験はありますでしょうか。AWSのネイティブ機能でもセキュリティ対策は可能ですが、個別のファイアウォール機能やIPS/IDS機能を使う要件がある場合、他社製品のアプライアンスを使うケースもあるかと思います。今回はゲートウェイロードバランサーの勉強もかねて、PaloAltoを利用したWebアクセスへのパケット検知をしてみたいと思います。個別のアプライアンスを使うことで、通信のペイロード確認や、個別アプリケーションへの疎通管理、URLでのフィルタリングやマルウェア等のシグネチャ設定など1つの管理コンソール上で設定することが可能です。
IDS/IPS製品をAWS上で1から構築する記事は多くはないのですが、
こちらの記事が大変参考になりました。とても助かりました。ありがとうございます。
構成
構成は上記のようになります。
ブラウザから、パブリックサブネット上に立てたEC2(Webサーバ)にアクセスします。EC2にはNginxをインストール済みでアクセス時には、必ずゲートウェイロードバランサーを経由してPaloAltoでパケット検査を行います。
ゲートウェイロードバランサーにアクセスするためのエンドポイントサービスを作成しておき、Webシステム側のVPCのエンドポイントと関連付けをしておけば、VPC Peering等を張らずにプライベートアクセスが可能になります。
今回は検証のため、Webシステム用のVPCは1つしかありませんが、仮想アプライアンス群を別VPCに切り分けることで複数のサービスへインターネットからアクセスする場合に対応できます。エンドポイントをVPCごとに設置する必要があるので、エンドポイントをアプライアンスVPC側に集約させて、Transit Gatewayを使って複数VPC、マルチアカウント間を接続させる構成の方が拡張性はあるかと思います。このあたりの情報はBlack Beltが構成図を交えて説明してくれておりますのでご参照ください。
PaloAltoはAWSのMarcketPlaceで提供されているAMIがあるため、個別でのインストール等は必要ありません。以下は15日間の無料お試しが可能です。検証目的で利用される場合はサブスクリプションを解除すればライセンス費用は掛かりません。(インスタンスの費用は別途かかります)
- VM-Series Next-Gen Virtual Firewall w/ Advanced Security Subs (PAYG)
- VM-Series Next-Gen Virtual Firewall w/Advanced Threat Prevention (PAYG)
- VM-Series Next-Generation Firewall Bundle 1 [VM-300]
- VM-Series Virtual NextGen Firewall for ARM - PAYG
今回はVM-Series Next-Gen Virtual Firewall w/ Advanced Security Subs (PAYG)を利用してみました。
ゲートウェイロードバランサー
ゲートウェイロードバランサーと仮想アプライアンス間の通信について補足します。ゲートウェイロードバランサーはOSIのレイヤー3で機能し、GENEVEプロトコルを使ってインターネットから送られたパケットを透過して(カプセル化)して仮想アプライアンスに渡しています。文字通り、ロードバランサーは負荷分散なので複数の仮想アプライアンスを冗長的に利用する際に適したサービスです。ポートは6081を固定で利用します。
レイヤー3の機能なので、パケットをIP単位でしか判断できずどのアプリケーション(ポート)にとどけるかを判断できません。通信の終端として機能しないので、レイヤー4で利用するNLBのようにセキュリティグループを適用することもありません。
構築手順
⓪Webサーバを立てておきます。
AMIは「Amazon Linux 2023 lernel-6.1 AMI」を利用します。
Nginxのデフォルトページを出力するだけなので、インスタンスタイプは「t2.micro」で問題ありません。Nginxインストール時のみ、インターネット向けのルートを追加した方が楽です。(あとで、ゲートウェイロードバランサー向けに切り替えればよいので)
ユーザデータで以下を記載してインスタンス作成時にインストールしておきます。
sudo dnf update -y
sudo dnf install nginx -y
sudo systemcl start nginx
sudo systemctl enable nginx
①ネットワーク設定をしておきます。
各種ルートテーブルの設定について確認しておきます。
Internet Gatewayのルートテーブルを設定するパターンはあまり皆様なじみがないかと思います。本来は、指定したIPにそのままルーティングするところを別のネットワークインターフェースに飛ばす際に有効になります。今回の場合はWebサーバへのアクセスをアプライアンスに飛ばすので、中継となるゲートウェイロードバランサーのエンドポイントENIにネクストホップを向けています。
ゲートウェイルートテーブルの説明に関しては下記のブログが大変参考になるのでご参照ください。
ゲートウェイへのルートテーブルの関連付けはダッシュボードの「アクション」-「Edgeの関連付けを編集」から可能です。
関連付けができていることを確認しましょう。
Webサーバ側のルートに関しても確認しましょう。今回は、ゲートウェイロードバランサー用のエンドポイント向けに0.0.0.0/0で許可しています。
これは、インターネットへの通信を想定しています。外向きの通信もアプライアンス経由で実装しています。そのため、エンドポイントのサブネットのルートはインターネットゲートウェイをネクストホップとして0.0.0.0/0で許可しております。
アプライアンス側はローカルルートだけあればよいですが、PaloAltoの管理コンソール用のENIはパブリックサブネットに配置するので、ブラウザからhttpとsshでアクセスできるようにインターネットゲートウェイへの経路を追加しております。
各種セキュリティグループの設定もしていきます。
■Webサーバ(WebServer-sg)
インバウンド
タイプ | プロトコル | ポート範囲 | ソース |
---|---|---|---|
HTTP | TCP | 80 | 0.0.0.0/0 |
アウトバウンド
タイプ | プロトコル | ポート範囲 | 送信先 |
---|---|---|---|
すべてのトラフィック | すべて | すべて | 0.0.0.0/0 |
■仮想アプライアンス
①内部パケット転送用ENI(Appliance-sg)
インバウンド
タイプ | プロトコル | ポート範囲 | ソース |
---|---|---|---|
カスタムUDP | UDP | 6081 | 10.0.1.0/27 |
HTTP | TCP | 80 | 10.0.1.0/27 |
ゲートウェイロードバランサーからの通信はGENEVEプロトコルで受け取るので6081をポート指定しています。CIDRについてはセキュリティグループのIDを参照できないのでゲートウェイロードバランサーのサブネットCIDRで指定しています。(ゲートウェイロードバランサーのプライベートIPは内部で自動割り当てされます)
ポート80で開けているのは、ヘルスチェック用のポートになります。通常AWS上で、ヘルスチェックを行う際デフォルト設定はTCP:80のためです。こちらのHTTP通信をリッスンする際はPaloAlto側でも設定が必要になります。(後続で説明)
②管理コンソール用ENI(Management-sg)
インバウンド
タイプ | プロトコル | ポート範囲 | ソース |
---|---|---|---|
SSH | TCP | 22 | 0.0.0.0/0 |
HTTP | TCP | 80 | 0.0.0.0/0 |
アウトバウンド
タイプ | プロトコル | ポート範囲 | 送信先 |
---|---|---|---|
すべてのトラフィック | すべて | すべて | 0.0.0.0/0 |
こちらは、ブラウザからPaloAltoの管理コンソール上でENIの関連付けや、通信許可を行う際に必要になります。
SSH:22を開けているのは、管理ユーザのパスワード設定がSSH経由でしか実施するためです。(後続で説明)
②仮想アプライアンスを構築します。
AWS Marcket AMIから「VM-Series Next-Gen Virtual Firewall w/ Advanced Security Subs (PAYG)」を選択しました。サブスクリプションは15日間無料トライアルなので検証する場合は必ずサブスクリプションを停止することを忘れないようにしましょう。インスタンスタイプは「c5n.xlarge」が推奨されています。
後にSSHログインするので、キーペアを払い出しておきましょう。
ネットワークインターフェースは2つ作成する必要があります。
ゲートウェイロードバランサーと透過通信をするENIの設定をしています。
プライベートサブネットに配置しますが、管理コンソール用のENIとセキュリティグループは共通利用しないので、何も選択しなくて良いです。
セキュリティグループは「Appliance-sg」を指定しています。
ENIを追加します。
管理コンソール用のENIになります。サブネットは指定のパブリックサブネットを指定しましょう。こちらはパブリックIPを割り当てるのですが、ElasticIPを付与するのでここでは無効で問題ありません。
ユーザデータで以下を記載する必要があります。
mgmt-interface-swap=enable
plugin-op-commands=aws-gwlb-inspect:enable
PaloAltoでは、何も設定していない場合は、eth0のみに通信が流れます。
管理インターフェースをeth1として分離する場合はスワップ処理をして、PaloAlto側でENIを判別させる必要があるのです。プラグインコマンドで、PaloAlto側でゲートウェイロードバランサーからの通信を許可するためのプラグインを有効化しています。どちらも必要なコマンドなので忘れずに記載しておきます。
こちらにコマンドの意味が記載されています。
管理コンソール用ENIにはElasticIPを付与しておきます。作成したElasticIPを関連付けします。先ほど作成した管理コンソール用ENIのIDを指定してプライベートIPアドレスが正しいか確認しましょう。
③仮想アプライアンスのセットアップを行います。
まず、SSHでアプライアンスにログインします。
今回はTeratermでアクセスしています。ユーザ名は「admin」でキーペア「management」でログインしました。初期セットアップでパスワードを設定します。
「cofigure」コマンドで設定に入ります。以下のコマンドで管理コンソールのパスワードを設定します。
set mgt-config usersadmin password
パスワードを入力したら、Commitをしましょう。再起動時に設定が反映されなくなるので注意です。
実際にアプライアンスの管理コンソールENIのElasticIPでログインしてみます。
自己証明書でアクセスしているので、安全な通信ではありませんが、一旦飛ばして問題ないです。
ユーザ名は「admin」でパスワードは先ほど設定したものを入力します。
ダッシュボードに入れますので、いくつか設定をしていきます。
ゲートウェイロードバランサーからの通信を待ち受けるのは「ethernet1/1」になります。[NETWORK]-[Ethernet]タブから指定のEthernetを押下します。
ゲートウェイロードバランサーはLayer3の通信なので、Interface Typeは「Layer3」を選択します。[Config]タブから設定入れていきます。Virtual Routerは「default」で問題ありません。Security Zoneは新規で「test-zone」という名前で作成しました。
Zoneはセキュリティポリシーの適用範囲になります。PaloAltoではこのセキュリティポリシーで通信許可を行っており、ENIと関連付けることで、そのインターフェースを経由するトラフィックに対してZoneベースのセキュリティルールが適用されます。設定値はデフォルトのまま「OK」を実行して問題ないです。
AWS側でENIを設定しても、PaloAlto側でそのENIを使う設定をしないとゲートウェイロードバランサーとの通信はできません。そのため上記の設定が必要になります。(ボリュームの設定をAWS側だけでなくOS側(マウント)で設定するみたいな感覚ですね。)
[IPv4]タブでは、DHCP Clientにチェックを入れておきます。
これによって、このENIに動的にeth0のプライベートのIPアドレスが割り当てられます。
[Advanced]タブでManagement Profileの設定します。
ここは、ゲートウェイロードバランサーからアプライアンスへのヘルスチェックにかかわる部分なので一旦飛ばします。
④ゲートウェイロードバランサーを作成します。
ロードバランサーを作成する前に、ターゲットグループの準備をしておきます。
ターゲットタイプの選択は「インスタンス」を指定します。
ターゲットグループ名は「Appliance」としました。
プロトコルですが、今回はGENEVEプロトコルでポートは6081を指定します。
VPCはアプライアンス用のVPCを指定します。
ヘルスチェックの設定をします。ゲートウェイロードバランサーとアプライアンス間の通信が正常かを確認すればよいので、プロトコルは「TCP」でトラフィックポートは「80」にしておきます。そのほかの値はデフォルトで問題ありません。
ターゲットとして、アプライアンス用EC2を指定します。
「保留中として以下を含める」を押下することで、ターゲットに含まれます。
ロードバランサーも作成していきましょう。
「Gateway Load Balancer」を指定します。
ロードバランサー名は「GWLB」にしました。
ネットワークマッピングですが、アプライアンス用VPCを指定しましょう。
IPリスナーのルーティングでは先ほど作成したターゲットグループを指定します。
作成後、しばらくするとヘルスチェックが「Unhealthy」になるかと思います。
これは、AWS側の設定の不備ではなく、PaloAltoの管理コンソール側でHTTPアクセスのリッスンポートが空いていないためです。PaloAlto側の設定に戻る前にAWS側のリソース作成を終わらせておきます。
⑤AWS側でエンドポイントサービスを作成します。
エンドポイントサービスを作成することで、異なるVPC間のロードバランサーに対して通信することが可能です。
エンドポイントサービスの名前は「GWLB-Endpoint-Service」とします。
先ほど作成したゲートウェイロードバランサーを指定します。エンドポイントをエンドポイントサービスに関連付ける際の承認にチェックを入れて、IPv4をサポートします。
⑥エンドポイントをWebシステム側のVPCに作成していきます。
名前は「GWLB-Endpoint」にしました。
タイプは「NLBとGWLBを使用するエンドポイントサービス」を選択しましょう。
サービス名の検証ですが、先ほど作ったエンドポイントサービスのコンソールからコピーします。検証が成功したら利用可能です。
ネットワークの設定も行いましょう。
今回はWebシステムVPCのエンドポイント用パブリックサブネットに配置します。
エンドポイントサービス側で、作成したエンドポイントを承認しましょう。
ステータスが利用可能にならないと接続されません。
⑦Webアクセス時にPaloAltoを経由するように管理コンソール側で設定を行います。
ethernet1/1の設定を開き、[Advanced]タブを開きます。
Management Profileを新規作成します。名前は「gwlb-health」にしました。
どの通信プロトコルでENIを公開するか設定可能です。Ping通信などもこちらで設定が必要になります。今回はヘルスチェックでTCP:80を通したいので「HTTP」にチェックを入れておきます。設定が完了したら、忘れずCommitをしておきましょう。
最後にSecurity Policyを設定します。Zone単位(もちろんIPでもOK)で、送信元・送信先の指定を行うことが可能です。ゲートウェイロードバランサーの通信では、行きもeth1/1を通り、戻りもeth1/1を通ります。そのため、SourceもDestinationも同じ「test-zone」を指定してあげます。ゲートウェイロードバランサーを使わない場合などは、Zoneを2つ(「Untrust」「Trust」など)を用意してZone間の通信を制御するのがよいかと思いました。
[POLICIES]-[Security]タブで[add]を押下して新規ポリシーを作成します。
名前は「test-zone-policy」にしました。
Source Zoneで送信元を「test-zone」にします。
Destination Zoneで同様に「test-zone」を指定します。
作成後はCommitを忘れないようにしましょう。
疎通確認
設定がここまで終わったら、PaloAltoに通信が流れるか確認してみます。
WebサーバのパブリックDNS名でアクセスしてみます。
ルートは、ゲートウェイロードバランサーに流れるようにインタネットゲートウェイ側のルートテーブルで設定済みなので、Nginxの画面が出力されれば正常に通信が流れている状態になります。
できてました。外向きの通信も確認してみます。
Webサーバのセッションマネージャーから、どこかのWebサイトにアクセスしてみます。
PaloAlto側で確認してみましょう。[MONITOR]-[Traffic]タブを開きます。
IPをマスキングしているのでわかりづらいですが、かいつまんで確認します。
1行目はWebサーバからどこかのサイトへのアクセスをする際のトラフィックログです。送信先はグローバルIPになっておりポートは80になっています。
2行目・3行目は、ヘルスチェック通信です。送信元はゲートウェイロードバランサーのプライベートIPになっており、送信先はアプライアンスのデータ受信用のプライベートIP(eth0)になっております。こちらもポートは80です。
最後の行は、インターネットからWebサーバへのブラウザアクセスを指しています。送信元がグローバルIPで、送信先はWebサーバのプライベートIPになっております。こちらもポート80で成功しています。
【おまけ】
通信がアプライアンスに透過しているのは確認してみましたが、まだアクセス制御はしていない状態なのでゲートウェイロードバランサーはインターネットからの通信、インターネットへの通信を許可している状態です。
せっかくなので、何か制御してみます。試しにUser-Agentのヘッダー値でフィルタリングしてみます。ブラウザのリクエストヘッダーのUser-AgentがEdgeの場合のみアクセス拒否するといったことを実装してみようと思います。
今回カスタムアプリケーションを作成してみます。すでに標準で入っているアプリケーションもありますが、PaloAlto側で通信の中身を識別するアプリケーション設定が既存ではされていません。そのため、独自でアプリケーションを作成し、シグネチャを追加設定することで「このリクエストパターンの時はこのアプリケーショーションの通信だ」と認識できるようにする必要があります。アプリケーションを対象として、アクセス制御はセキュリティポリシーで実装可能です。
カスタムアプリケーションを作っていきます。
[OBJECTS]-[Applications]タブから「Add」を押下します。
まずは[Configuration]タブから設定していきます。
名前は「edge-block」にしました。プロパティ設定からCategoryは「general-internet」、SubCategoryは「internet-utility」、Technologyは「browser-based」を指定します。次に「Signatures」を設定してきます。
名前は「test-signature」にしました。条件追加([Add And Condition]を押下)でパターンマッチを実装します。今回はCONTEXT設定で「http-req-headers」、PATTERNを「Edg/」にしました。
次にセキュリティポリシーを設定します。現状は「test-zone」間の通信を許可している状態ですが、Edgeからの通信をはじく設定を入れてみます。
名前は「edge-block-policy」とします。SourceとDestinationは「test-zone」で問題ないです。
Applicationの設定で先ほど作成した「edge-block」を指定します。
Actionはdenyです。MONITORからトラフィックを確認できるようにLog機能はオンにしておきます。
EdgeブラウザからWebサーバのパブリックDNSを名前解決してみます。
アクセスできなくなりました。
[MONITOR]-[Traffic]タブからも確認してみます。
グローバルIPからWebサーバへのアクセスをdenyしていますね。アプリケーションは「edge-deny-policy」と判別していることも確認しました。
最後に
今回はゲートウェイロードバランサーを使って、WebアクセスをPaloAltoに流して通信チェックさせました。NetworkFirewallなどもAWSネイティブサービスとして機能しますので、そちらを利用される方も多いかと思いますが仮想アプライアンスの仕組みを知るという意味でも勉強になりました。皆さんもIDS/IPSの機能で通信を精査する場合は、この構成参考にしてみてください。