はじめに
社会人 20 年、AWS をお触りするようになり 10 年が経ちました。
これまで AWS の VPC Flow Logs のお世話になってきました。
しかし、通信の行きと戻りを意識したり、順次追加されてきた
カスタム形式のログフィールドについて、じっくりと観察したことがありませんでした。
ということで、今回はいくつかのシナリオでトラフィックを流してみて
特に TCPフラグがどのような値になっているか見てみました。
【参考】
・ VPC フローログを使用した IP トラフィックのログ記録
利用環境
・ Private subnet に EC2 インスタンスを2台用意し、VPC Flow Logs は CloudWatch Logs に出力する構成としています。
・ vm01 から vm02 に対して、PING や SSH (SFTP) で通信できるようにセキュリティグループを設定しています。
・ VPC Flow Logs は VPC に対して作成しています。
【EC2情報】
# | 名前 | OS | Instance Type | instance-id | eni | IPアドレス |
---|---|---|---|---|---|---|
01 | vm01 | Amazon Linux 2 | t2.micro | i-0360afced64ba1444 | eni-0fb7dcfbbbb2f6573 | 10.0.129.221 |
02 | vm02 | Amazon Linux 2 | t2.micro | i-026cb4d7681325c92 | eni-0652da0ac0ca00dee | 10.0.130.11 |
【セキュリティグループ設定】
方向 | タイプ | プロトコル | ポート範囲 | ソース |
---|---|---|---|---|
インバウンドルール | すべての ICMP - IPv4 | ICMP | すべて | 10.0.128.0/20 |
インバウンドルール | SSH | TCP | 22 | 10.0.128.0/20 |
アウトバウンドルール | すべてのトラフィック | すべて | すべて | 0.0.0.0/0 |
【VPC FLow Logsの設定】
項目 | 内容 |
---|---|
フィルタ | すべて |
最大集約間隔 | 1 分間 |
送信先 | CloudWatch Logs に送信 |
ログレコード形式 | カスタム形式 |
ログ形式 | すべて選択(詳細は「形式のプレビュー」参照) |
${account-id} ${action} ${az-id} ${bytes} ${dstaddr} ${dstport} ${end} ${flow-direction} ${instance-id} ${interface-id} ${log-status} ${packets} ${pkt-dst-aws-service} ${pkt-dstaddr} ${pkt-src-aws-service} ${pkt-srcaddr} ${protocol} ${region} ${srcaddr} ${srcport} ${start} ${sublocation-id} ${sublocation-type} ${subnet-id} ${tcp-flags} ${traffic-path} ${type} ${version} ${vpc-id}
【参考】
・ VPCフローログの取得方法(CloudWatch Logsに保存)
実施内容
・vm01 から下記 3 つの通信を実施し、結果のログを CloudWatch Logs Insight でクエリしてみます。
- PING 疎通
- curl コマンドで Web アクセス
- SFTP でファイル転送
1. PING 疎通
- まずはシンプルに vm01 から vm02 に対して、PING (毎秒 10,000 Bytes ) を実施しました。
[root@ip-10-0-129-221 bin]# ping -s 10000 10.0.130.11
- 次に CloudWatch Logs Insight で確認します。
- [CloudWatch] > [ログ] > [ログのインサイト]を開きます。
- 画面上部で VPC Flow Logs を出力している ロググループ を選択します。(今回は
vpcflowlogs
) - 下記のクエリ文で [クエリの実行] をおこないます。
【参考】
・ CloudWatch Logs Insights のクエリ構文
fields @timestamp
| parse '* * * * * * * * * * * * * * * * * * * * * * * * * * * * *' as `account-id`, action, `az-id`, bytes, dstaddr, dstport, end, `flow-direction`, `instance-id`, `interface-id`, logstatus, packets, `pkt-dst-aws-service`, pktdstaddr, `pkt-src-aws-service`, pktsrcaddr, protocol, region, srcaddr, srcport, start, sublocationid, sublocationtype, `subnet-id`, tcpflags, `traffic-path`, type, version, `vpc-id`
| display @timestamp, start, end, action, logstatus, srcaddr, srcport, dstaddr, dstport, `interface-id`, `flow-direction`, packets, bytes, protocol, tcpflags
| filter `instance-id` in ['i-0360afced64ba1444','i-026cb4d7681325c92']
| filter dstaddr in ['10.0.129.221','10.0.130.11']
| filter srcaddr in ['10.0.129.221','10.0.130.11']
| filter protocol=1
| sort @timestamp desc
【補足説明】
・ カスタム形式のログに合わせて、parse
句でログを解析してフィールド抽出しています。
・ ログ分析に必要なフィールドだけに絞るため、display
句で表示するフィールドを指定しています。
・ vm01 と vm02 間の通信に絞るため、filter
句で インスタンス ID、宛先 IP、送信元 IPを使って絞っています。
・ ICMP 通信に絞るため、filter
句でプロトコル番号を 1 に絞っています。
・ イベント発生時刻の新しい順にsort
句で並び替えています。
【考察】
・ フローログの最大集約間隔が 1 分間 のため、1 分以内の間隔に集約されています。
・ IP アドレスから #2 がエコー要求、 #1 がエコー応答のはずですが、情報がないため行きと帰りの判断ができませんでした。
・ ICMP 通信は TCP ではないため、当然tcpflag
は0
です。
2. curl コマンドで Web アクセス
- 事前に nslookup コマンドで
www.google.com
の名前解決を実施し、IP アドレスを確認しました。 - IP アドレスを確認した上で curl コマンドで
www.google.com
に Web アクセスを実施しました。
[root@ip-10-0-129-221 bin]# nslookup www.google.com
Server: 10.0.0.2
Address: 10.0.0.2#53
Non-authoritative answer:
Name: www.google.com
Address: 172.217.175.36
Name: www.google.com
Address: 2404:6800:4004:822::2004
[root@ip-10-0-129-221 bin]# curl www.google.com
【シーケンス図】
・ まずは、TCP スリーウェイハンドシェークによるコネクション接続開始フェーズです。
※ 公式ドキュメントの抜粋ですが、ACK のみの場合はtcpflag
が0
になります。
フローログエントリが ACK パケットのみで構成されている場合、フラグ値は 16 ではなく 0 になります。
・ 最後に TCP コネクションを切断します。(腑に落ちない点がありますが、クエリ結果で後述します)
- CloudWatch Logs Insight において、下記内容で [クエリの実行] をおこないます。
fields @timestamp
| parse '* * * * * * * * * * * * * * * * * * * * * * * * * * * * *' as `account-id`, action, `az-id`, bytes, dstaddr, dstport, end, `flow-direction`, `instance-id`, `interface-id`, logstatus, packets, `pkt-dst-aws-service`, pktdstaddr, `pkt-src-aws-service`, pktsrcaddr, protocol, region, srcaddr, srcport, start, sublocationid, sublocationtype, `subnet-id`, tcpflags, `traffic-path`, type, version, `vpc-id`
| display @timestamp, start, end, action, logstatus, srcaddr, srcport, dstaddr, dstport, `interface-id`, `flow-direction`, packets, bytes, protocol, tcpflags
| filter dstaddr in ['10.0.129.221','172.217.175.36']
| filter srcaddr in ['10.0.129.221','172.217.175.36']
| filter protocol=6
| sort @timestamp desc
【補足説明】
・ カスタム形式のログに合わせて、 parse
句でログを解析してフィールド抽出しています。
・ ログ分析に必要なフィールドだけに絞るため、 display
句で表示するフィールドを指定しています。
・ vm01 と www.google.com
間の通信に絞るため、filter
句で 宛先 IP、送信元 IPを使って絞っています。
・ google の IP は、nslookup コマンドで名前解決した172.217.175.36
としています。
・ TCP 通信に絞るため、filter
句でプロトコル番号を 6 に絞っています。
・ イベント発生時刻の新しい順に sort
句で並び替えています。
【考察】
・ フローログの最大集約間隔が 1分間 のため、1分以内の間隔に集約されています。
・ #2 の宛先ポートが80
、TCP フラグが ❶SYN(2
)+❺FIN(1
)で合計3
となっているため、行きの通信だと判断できます。
・ #1 の送信元ポートが80
、TCP フラグが ❷SYN(2
)+ACK(16
)+❼FIN(1
)で合計19
となっているため、戻りの通信だと判断できます。
・ どうも❺と❼のTCP切断要求時のACK(16
)は、計算に入らないようです。理由までは解明できませんでした。
※ 公式ドキュメントの抜粋ですが、集約間隔内だと各パケットの TCPフラグ は合計されます。
TCP フラグは、集約間隔内に OR 処理することができます。短い接続の場合、フラグがフローログレコードの同じ行に設定されることがあります (例えば、SYN-ACK と FIN の場合は 19、SYN と FIN の場合は 3 など)。例については、「TCP フラグシーケンス」を参照してください。
【参考】
・ TCPフラグ値 早見表
・ TCP three-way handshaking
3. SFTP でファイル転送
- 最後に SFTP 接続を行い、410 Bytes の適用なテキストファイルを転送してみます。
- また、SFTP の切断はデータ転送後、6 分の間をおいてみました。
[root@ip-10-0-129-221 tmp]# sftp hibino@10.0.130.11
hibino@10.0.130.11's password:
Connected to 10.0.130.11.
sftp> put test.txt
Uploading test.txt to /home/hibino/test.txt
test.txt
100% 410 777.3KB/s 00:00
< 約 6 分放置 >
sftp> quit
【シーケンス図】
・ まずは、TCP スリーウェイハンドシェークによるコネクション接続開始フェーズです。
- CloudWatch Logs Insight において、下記内容で [クエリの実行] をおこないます。
fields @timestamp
| parse '* * * * * * * * * * * * * * * * * * * * * * * * * * * * *' as `account-id`, action, `az-id`, bytes, dstaddr, dstport, end, `flow-direction`, `instance-id`, `interface-id`, logstatus, packets, `pkt-dst-aws-service`, pktdstaddr, `pkt-src-aws-service`, pktsrcaddr, protocol, region, srcaddr, srcport, start, sublocationid, sublocationtype, `subnet-id`, tcpflags, `traffic-path`, type, version, `vpc-id`
| display @timestamp, start, end, action, logstatus, srcaddr, srcport, dstaddr, dstport, `interface-id`, `flow-direction`, packets, bytes, protocol, tcpflags
| filter `instance-id` in ['i-0360afced64ba1444','i-026cb4d7681325c92']
| filter dstaddr in ['10.0.129.221','10.0.130.11']
| filter srcaddr in ['10.0.129.221','10.0.130.11']
| filter protocol=6
| sort @timestamp desc
【補足説明】
・ カスタム形式のログに合わせて、 parse
句でログを解析してフィールド抽出しています。
・ ログ分析に必要なフィールドだけに絞るため、 display
句で表示するフィールドを指定しています。
・ vm01 と vm02 間の通信に絞るため、filter
句で インスタンス ID、宛先 IP、送信元 IPを使って絞っています。
・ TCP 通信に絞るため、filter
句でプロトコル番号を 6 に絞っています。
・ イベント発生時刻の新しい順に sort
句で並び替えています。
【考察】
・ データ転送時の #7 と 8 では、ACK のみのため、やはりtcpflag
は0
でした。
・ SFTP 切断時の #1 〜 4 では、ENI ごとに ingress と egress で計 4 つありましたが、いずれもtcpflag
は FYN(1
)でした。
まとめ
さて、いかがでしたでしょうか?
通信の方向は、ポート番号と TCP フラグを組み合わせてみることで判断できることが分かりました。
戻り通信の場合、TCP フラグ値は SNY/ACK(2+16=18
)以上になっていることでも判断材料になります。
しかし、コネクション切断時の TCP フラグ値が FYN(1
) のみであることは腑に落ちませんでした。
検証が終わった後に、確認した内容が全く同じように公式ドキュメントに書かれていて落ち込みました。
あとは最大集約間隔 1 分間でフローをまとめられてしまうため、正確な通信を把握したい場合は
やはり VPC Traffic Mirroring を使って、フルパケットキャプチャするしかないですね。