AWS Network Firewallはアウトバウンド通信制御のセキュリティ対策にはならない
AWS Network FirewallによってSquid等を用いたプロキシサーバを廃止できる。
そんなふうに考えていた時期が私にもありました。
もし同じように考えている方がいれば注意が必要です。
本記事執筆時点において、AWS Network Firewallではプロキシサーバとは異なり、悪意のあるアウトバウンド通信を宛先ドメイン名ベースで制限することはできません。
AWS Network Firewallとは
公式ドキュメントより
AWS Network Firewall のステートフルファイアウォールは、接続の追跡やプロトコルの識別などのトラフィックフローからコンテキストを組み込んで、VPC が不正なプロトコルを使用してドメインにアクセスするのを防ぐなどのポリシーを適用できます。AWS Network Firewall の侵入防止システム (IPS) は、アクティブなトラフィックフロー検査を提供するため、シグネチャベースの検出を使用して脆弱性の悪用を特定してブロックできます。また、AWS Network Firewall は、既知の不正な URL へのトラフィックを停止し、完全修飾ドメイン名を監視できるウェブフィルタリングも提供します。
https://aws.amazon.com/jp/network-firewall/
プロキシの代替としての利用も期待できる機能説明です。そうしたケースも含めて、以下のページでの解説がわかりやすいでしょう。
Network Firewallのドメイン名によるフィルタリングは何を検査しているか
Network Firewallのドメインリストルールでは、HTTPとHTTPSの2つのプロトコルに対応しています。ドキュメントに記載されているドメインリスト利用時に解釈されるSuricataルールから、それぞれ検査している内容は以下の通りと理解できます。
- HTTP: HTTPリクエストのHostヘッダ
- HTTPS: TLSハンドシェイク時のSNI拡張におけるserver_name
これらはどちらも通信の内容を検査しているのであって、実際の通信の宛先を検査しているわけでも制御しているわけでもありません。そして、リクエスト生成元において、実際の宛先とこれらの情報を別にしたリクエストを生成することは容易です。
なお、SNIによるフィルタについては、以下のページでわかりやすく説明されております。
Network Firewallでは制御できないことを確認する
検証のために下図の構成を構築し、実際に試してみます。
検証環境は下記のCloudFormationテンプレートで構築できます。Network Firewallは作成に時間がかかるため、Stackの作成完了まで7分程度かかりました。
CloudFormationテンプレート
AWSTemplateFormatVersion: 2010-09-09
Resources:
VPC:
Type: AWS::EC2::VPC
Properties:
CidrBlock: "10.0.0.0/16"
PrivateSubnet:
Type: AWS::EC2::Subnet
Properties:
CidrBlock: "10.0.1.0/24"
VpcId: !Ref VPC
AvailabilityZone: !Select
- '0'
- !GetAZs
Ref: 'AWS::Region'
FirewallSubnet:
Type: AWS::EC2::Subnet
Properties:
CidrBlock: "10.0.2.0/24"
VpcId: !Ref VPC
AvailabilityZone: !Select
- '0'
- !GetAZs
Ref: 'AWS::Region'
PublicSubnet:
Type: AWS::EC2::Subnet
Properties:
CidrBlock: "10.0.3.0/24"
VpcId: !Ref VPC
AvailabilityZone: !Select
- '0'
- !GetAZs
Ref: 'AWS::Region'
InternetGateway:
Type: AWS::EC2::InternetGateway
InternetGatewayAttachment:
Type: AWS::EC2::VPCGatewayAttachment
Properties:
VpcId: !Ref VPC
InternetGatewayId: !Ref InternetGateway
NATGateway:
Type: AWS::EC2::NatGateway
Properties:
SubnetId: !Ref PublicSubnet
AllocationId: !GetAtt NATEIP.AllocationId
NATEIP:
Type: AWS::EC2::EIP
NetworkFirewall:
Type: AWS::NetworkFirewall::Firewall
Properties:
FirewallName: NetworkFirwallTestXYZ
FirewallPolicyArn: !Ref NetworkFirewallPolicy
VpcId: !Ref VPC
SubnetMappings:
- SubnetId: !Ref FirewallSubnet
NetworkFirewallPolicy:
Type: AWS::NetworkFirewall::FirewallPolicy
Properties:
FirewallPolicyName: NetworkFirewallPolicyTestXYZ
FirewallPolicy:
StatelessDefaultActions:
- 'aws:forward_to_sfe'
StatelessFragmentDefaultActions:
- 'aws:forward_to_sfe'
StatefulRuleGroupReferences:
- ResourceArn: !Ref StatefulRuleGroup
Tags:
- Key: Name
Value: NetworkFirewallPolicy
StatefulRuleGroup:
Type: AWS::NetworkFirewall::RuleGroup
Properties:
RuleGroupName: StatefulRuleGroupTestXYZ
Type: STATEFUL
Capacity: 100
RuleGroup:
RuleVariables:
IPSets:
HOME_NET:
Definition:
- "10.0.0.0/16"
RulesSource:
RulesSourceList:
Targets:
- "www.yahoo.co.jp"
- ".ap-northeast-1.amazonaws.com"
TargetTypes:
- "TLS_SNI"
- "HTTP_HOST"
GeneratedRulesType: "ALLOWLIST"
Tags:
- Key: Name
Value: AllowDomainList
PrivateRouteTable:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref VPC
Tags:
- Key: Name
Value: PrivateRouteTable
PrivateRoute1:
Type: AWS::EC2::Route
Properties:
RouteTableId: !Ref PrivateRouteTable
DestinationCidrBlock: "0.0.0.0/0"
VpcEndpointId: !Select ["1", !Split [":", !Select ["0", !GetAtt NetworkFirewall.EndpointIds]]]
PrivateRouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
RouteTableId: !Ref PrivateRouteTable
SubnetId: !Ref PrivateSubnet
FirewallRouteTable:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref VPC
Tags:
- Key: Name
Value: FirewallRouteTable
FirewallRoute1:
Type: AWS::EC2::Route
Properties:
RouteTableId: !Ref FirewallRouteTable
DestinationCidrBlock: "0.0.0.0/0"
NatGatewayId: !Ref NATGateway
FirewallRouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
RouteTableId: !Ref FirewallRouteTable
SubnetId: !Ref FirewallSubnet
PublicRouteTable:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref VPC
Tags:
- Key: Name
Value: PublicRouteTable
PublicRoute1:
Type: AWS::EC2::Route
Properties:
RouteTableId: !Ref PublicRouteTable
DestinationCidrBlock: "0.0.0.0/0"
GatewayId: !Ref InternetGateway
PublicRoute2:
Type: AWS::EC2::Route
Properties:
RouteTableId: !Ref PublicRouteTable
DestinationCidrBlock: "10.0.1.0/24"
VpcEndpointId: !Select ["1", !Split [":", !Select ["0", !GetAtt NetworkFirewall.EndpointIds]]]
PublicRouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
RouteTableId: !Ref PublicRouteTable
SubnetId: !Ref PublicSubnet
InstanceSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: "Allow All"
GroupName: "AllowAll"
SecurityGroupEgress:
- IpProtocol: tcp
FromPort: '0'
ToPort: '65535'
CidrIp: '0.0.0.0/0'
VpcId: !Ref VPC
RoleForSSM:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: "Allow"
Principal:
Service:
- "ec2.amazonaws.com"
Action:
- "sts:AssumeRole"
Path: /
ManagedPolicyArns:
- 'arn:aws:iam::aws:policy/AdministratorAccess'
InstanceProfileForSSM:
Type: AWS::IAM::InstanceProfile
Properties:
Roles:
- !Ref RoleForSSM
EC2Instance:
Type: AWS::EC2::Instance
Properties:
ImageId: ami-03d79d440297083e3
InstanceType: t2.micro
NetworkInterfaces:
- DeviceIndex: "0"
SubnetId: !Ref PrivateSubnet
GroupSet:
- !Ref InstanceSecurityGroup
IamInstanceProfile: !Ref InstanceProfileForSSM
Tags:
- Key: Name
Value: NetworkFirewallTest
DependsOn:
- NetworkFirewall
Network Firewallではドメインリストルールでwww.yahoo.co.jp及び*.ap-northeast-1.amazonaws.comのみを許可しています。後者を許可している理由はSession Managerでのサーバログインを可能とするためです。
サーバから以下のコマンドを実行すると、許可したwww.yahoo.co.jpにアクセスできることが確認できます。
curl http://www.yahoo.co.jp/ -v
curl https://www.yahoo.co.jp/ -v
以下のコマンドで許可されていないwww.google.co.jpへの通信はDropされていることが確認できます。(応答待ちとなるため、Ctrl+Cで中断する必要があります)
curl http://www.google.co.jp/ -v
curl https://www.google.co.jp/ -v
これでドメインの制御がちゃんとできるいると思いがちですが、違います。
下記のコマンドを打つと、Googleのサーバから応答が得られていることが確認できます。
curl http://172.217.31.131/ -H "Host: www.yahoo.co.jp" -v
curl https://www.yahoo.co.jp/ --resolv www.yahoo.co.jp:443:172.217.31.131 -H "Host: www.google.co.jp" --insecure -v
HostヘッダやSNIのserver_nameを許可されているドメイン名に設定したリクエストにすれば、任意のIPアドレスの宛先(上記の例ではGoogleのIPアドレス)と通信することが可能なのです。
ドメインリストルール以外の場合には
この問題は、ドメインリストルールだけではなく、より細かく制御可能なSuricataルールでも対応できません。SuricataにおいてHTTP/TLSで利用可能なキーワードは以下に記載がありますが、いずれのキーワードを用いても対応できません。
TLSにおいては、通信先の証明書のfingerprintで制御する方法も考えられなくはありませんが、通信相手の予期できない証明書更新への追随のための運用を考えれば適切な方法とは言えないでしょう。証明書のサブジェクトで検査する場合には証明書チェーンの検証機能も伴わなければ効果がありませんが、証明書チェーンの検証機能はありません。
Network Firewallのドメインリストでアウトバウンド通信を制御する意味
例えば、最近発生したLog4Shell[1]は外部のリソースから任意のコードを読み込んで任意の処理を実行させるものでしたが、外部のリソースからコードを取得する部分のリクエスト自体を自力で組み立てることはできなかったため、このケースについては、Network Firewallによるドメイン制御でも対処はできたでしょう。また、ミス等で外部と通信してしまうことを防ぐこともできます。その点で、全く意味がないというわけではありません。
一方で、セキュリティ観点におけるアウトバウンドの通信制限とは、システム内部から任意の外部の宛先に対して情報を流出させないことを目的とすることが多いかと思います。この脅威に対する対策として、十分であるとはいえません。
Azure Firewallの場合
Azureには類似のサービスとして、Azure Firewallがあります。こちらもアウトバウンド通信の制御を1つの目的として利用することができます。HTTPではHostヘッダ、HTTPSではSNIの値に基づいて制御する点ではAWS Network Firewallと同様ですが、こちらは上述のNetwork Firewallのような問題は起こりません。
Azure Firewallのアプリケーションルールでは、送信元が指定したIPアドレスを実際の通信先として利用しません。リクエストの送信元で宛先IPアドレスに何を指定しようとも、HTTPであればHostヘッダ、HTTPSであればSNIのserver_nameの値に従ってAzure Firewallが名前解決し通信先を決定し、名前解決した宛先にDNATします。従って、AWS Network Firewallのような問題はなく、アウトバウンド通信をドメイン名ベースで制限するという目的に合致した動作をします。AWSに先んじて提供されていたAzureのサービスでは期待通りの動作をしていたことも、Network Firewallに誤った期待してしまう一因かもしれません。
Network 規則への一致がなく、プロトコルに HTTP、HTTPS または MSSQL を使用している場合は、その後で、 Application 規則により、優先度順で、パケットを評価します。
HTTP については、Azure Firewall で Application 規則への一致を調べるとき、ホスト ヘッダーの情報を使用します。 HTTPS については、Azure Firewall で Application 規則への一致を調べるとき、SNI のみを使用します。
HTTP と TLS インスペクションを有効にした HTTPS では、ファイアウォールでパケットの宛先 IP アドレスを無視し、DNS でホスト ヘッダーから解決した IP アドレスを使用します。
AWSに期待すること
Azure Firewall同様、Firewall自身が通信先を制御することで、抜け道なくアウトバウンド通信をドメイン名ベースで制御可能な機能の追加は期待したいところです。ただ、Azureの場合元々FirewallがNAT装置でありましたが、AWSの場合はGateway Load Balancerを利用してパケットを改変せずに取り扱っているので、本機能を実装することはアーキテクチャの思想に合致しない点があるかもしれません。
AWS Network FirewallはほぼAmazon Managed IPS for Suricataともいえるものです。最近ではマネージドルールの提供も開始して、IPSとしての充実度が上がってきました。Suricataとして様々なプロトコルに対応したルールを定義できる良さもあります。これはこれで有用なサービスです。
これをアウトバウンド通信を制御するプロキシの代替となる、と考えてしまうことが誤りなのです。とはいえ、そうした誤解を招きうる機能説明がされている印象はあります。セキュリティに関する内容でもあるので、この点の注意喚起はドキュメント等に記載されても良いのではないかと思います。
なお、aws-samplesにはsquid定義からの移行用コードが公開されています。
また、アウトバウンドの通信制御に関して必要だったものは、マネージドなSuricataではなく、マネージドなSquidです。ドメイン制御の留まらないより細かな制御を実現するためにも、TLS復号によるコンテンツフィルタ、透過型構成も選択可能な、ユーザフレンドリな設定UIを具備した、Amazon Managed Proxy for Squidの登場に期待します。
結論
Network Firewallは、プロキシサーバが担っていたドメイン名ベースでの通信先制御が、マネージドサービスで実現できることを期待させるものでした。が、Network Firewallのドメインリストルールではセキュリティ的な意味での制御には不十分です。依然として、AWS上における本目的の達成にはプロキシサーバないし十分な機能を備えたセキュリティアプライアンスの方が適切と言えます。プロキシサーバの構築/運用という作業からはまだしばらく解放されなそうです。
Discussion