前提
mTLSで必要なファイル
CA関連
| ファイル名 | 役割 |
|---|---|
ca.csr | CAの申請書(AWS Private CAが内部で生成) |
ca-cert.pem | CA証明書。「このCAは信頼できる」という証明 |
サーバー関連
| ファイル名 | 役割 |
|---|---|
server-key.pem | サーバーの秘密鍵 |
server.csr | サーバーの申請書 |
server-cert.pem | サーバー証明書。「このサーバーは本物」とCAが署名 |
クライアント関連
| ファイル名 | 役割 |
|---|---|
client-key.pem | クライアントの秘密鍵 |
client.csr | クライアントの申請書 |
client-cert.pem | クライアント証明書。「このクライアントは本物」とCAが署名 |
mTLS通信時に誰が何を使うか
サーバーに配置:
server-key.pem- 自分の秘密鍵server-cert.pem- 自分の証明書(クライアントに提示)ca-cert.pem- クライアント証明書を検証するため
クライアントがリクエスト時に使用:
client-key.pem- 自分の秘密鍵client-cert.pem- 自分の証明書(サーバーに提示)ca-cert.pem- サーバー証明書を検証するため
なぜ両方にca-cert.pemが必要か
- mTLS-Server: 「このクライアント証明書は信頼できるCAが発行したか?」を確認
- mTLS-Client: 「このサーバー証明書は信頼できるCAが発行したか?」を確認
同じCA(mTLS-Adminで作成したPrivate CA)が両方の証明書を発行しているので、同じ ca-cert.pem で検証できる。
今回はクライアント、サーバー証明書をそれぞれ同じCAで署名したが、異なるCAを用いてもよい
mTLSリクエスト時に必要な情報
例えば、curlを用いてクライアントから以下のようにリクエストする
curl --cacert ca-cert.pem \
--cert client-cert.pem \
--key client-key.pem \
https://<サーバーのホスト>| オプション | 送るもの | 目的 |
|---|---|---|
--cacert ca-cert.pem | (送らない。検証に使う) | サーバー証明書が信頼できるCAから発行されたか確認 |
--cert client-cert.pem | クライアント証明書 | 「私はこの証明書の持ち主です」とサーバーに提示 |
--key client-key.pem | (送らない。署名に使う) | TLSハンドシェイク中の署名に使用。秘密鍵を持っている証明 |
セットアップ
mtls-environment.yamlを用いて EC2インスタンスを3つ用意し、mTLSを試す。
mtls-environment.yaml
AWSTemplateFormatVersion: '2010-09-09' Description: mTLS verification environment with CA-Admin/Client/Server EC2 instances Parameters: VpcId: Type: AWS::EC2::VPC::Id Description: VPC ID SubnetId: Type: AWS::EC2::Subnet::Id Description: Subnet ID for all instances Resources: EC2SecurityGroup: Type: AWS::EC2::SecurityGroup Properties: GroupDescription: Security group for mTLS EC2 instances VpcId: !Ref VpcId SecurityGroupIngress: Type: AWS::EC2::SecurityGroupIngress Properties: GroupId: !Ref EC2SecurityGroup IpProtocol: -1 SourceSecurityGroupId: !Ref EC2SecurityGroup EC2Admin: Type: AWS::EC2::Instance Properties: ImageId: !Sub '{{resolve:ssm:/aws/service/ami-amazon-linux-latest/al2023-ami-kernel-default-x86_64}}' InstanceType: t3.micro SubnetId: !Ref SubnetId SecurityGroupIds: - !Ref EC2SecurityGroup Tags: - Key: Name Value: mTLS-Admin EC2Client: Type: AWS::EC2::Instance Properties: ImageId: !Sub '{{resolve:ssm:/aws/service/ami-amazon-linux-latest/al2023-ami-kernel-default-x86_64}}' InstanceType: t3.micro SubnetId: !Ref SubnetId SecurityGroupIds: - !Ref EC2SecurityGroup Tags: - Key: Name Value: mTLS-Client EC2Server: Type: AWS::EC2::Instance Properties: ImageId: !Sub '{{resolve:ssm:/aws/service/ami-amazon-linux-latest/al2023-ami-kernel-default-x86_64}}' InstanceType: t3.micro SubnetId: !Ref SubnetId SecurityGroupIds: - !Ref EC2SecurityGroup Tags: - Key: Name Value: mTLS-Server Outputs: AdminInstanceId: Value: !Ref EC2Admin AdminPrivateIp: Value: !GetAtt EC2Admin.PrivateIp ClientInstanceId: Value: !Ref EC2Client ClientPrivateIp: Value: !GetAtt EC2Client.PrivateIp ServerInstanceId: Value: !Ref EC2Server ServerPrivateIp: Value: !GetAtt EC2Server.PrivateIp
mtls-environment.yaml でデプロイされる環境:
| インスタンス | 役割 |
|---|---|
| mTLS-Admin | CA管理。証明書の発行・配布を行う |
| mTLS-Client | curlでmTLSリクエストを送信 |
| mTLS-Server | Nginxでmtls接続を受け付ける |
全インスタンスは同一セキュリティグループ内で相互通信可能
環境デプロイ
aws cloudformation create-stack \
--stack-name mtls-env \
--template-body file://mtls-environment.yaml \
--parameters \
ParameterKey=VpcId,ParameterValue=vpc-xxxxxxxx \
ParameterKey=SubnetId,ParameterValue=subnet-xxxxxxxx初期設定
【Admin】ホスト名変更
sudo hostnamectl set-hostname mtls-admin
exec bash【Client】ホスト名変更
sudo hostnamectl set-hostname mtls-client
exec bash【Server】ホスト名変更
sudo hostnamectl set-hostname mtls-server
exec bash[Admin] aws cli 認証情報の設定
v 2.32.0 以降では、aws loginコマンドによりAWSコンソールの認証情報を設定可能。
Route53へのレコード追加、Private CAリソース作成に十分な権限を持ったIAMユーザー/ロールでAWSコンソールにログインしておく
参考: Sign in through the AWS Command Line Interface
// AWS CLI を最新にupdate
// http://docs.aws.amazon.com/ja_jp/cli/latest/userguide/getting-started-install.html
curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
unzip awscliv2.zip
sudo ./aws/install --bin-dir /usr/local/bin --install-dir /usr/local/aws-cli --update
source ~/.bashrc
aws --version
aws login --remote
aws sts get-caller-identity
Private CA作成
【Admin】CA作成
CA_ARN=$(aws acm-pca create-certificate-authority \
--certificate-authority-configuration '{
"KeyAlgorithm": "RSA_2048",
"SigningAlgorithm": "SHA256WITHRSA",
"Subject": {
"Country": "JP",
"Organization": "TestOrg",
"OrganizationalUnit": "IT",
"CommonName": "Test-Private-CA"
}
}' \
--certificate-authority-type ROOT \
--usage-mode GENERAL_PURPOSE \
--query 'CertificateAuthorityArn' --output text)
echo "Private CA ARN: $CA_ARN"【Admin】CA CSR取得
aws acm-pca get-certificate-authority-csr \
--certificate-authority-arn $CA_ARN \
--output text > ca.csrca.csr の内容
//Subject: 申請者の情報 //Subject Public Key Info: Privater CAの秘密鍵に対応する公開鍵の情報 //Attributes: CA:RUE はCA証明書用のCSRであることを示す $ openssl req -text < ca.csr Warning: Will read cert request from stdin since no -in option is given Certificate Request: Data: Version: 1 (0x0) Subject: C=JP, O=TestOrg, OU=IT, CN=Test-Private-CA Subject Public Key Info: Public Key Algorithm: rsaEncryption Public-Key: (2048 bit) Modulus: ... Exponent: 65537 (0x10001) Attributes: Requested Extensions: X509v3 Basic Constraints: critical CA:TRUE Signature Algorithm: sha256WithRSAEncryption Signature Value: ... -----BEGIN CERTIFICATE REQUEST----- ... -----END CERTIFICATE REQUEST-----
【Admin】Root CAとして自己署名
CSR をもとに証明書を発行する。
issue-certificate は Private CA 側に証明書を作成するコマンド。
証明書自体は別途手元に取得する必要がある
CERT_ARN=$(aws acm-pca issue-certificate \
--certificate-authority-arn $CA_ARN \
--csr fileb://ca.csr \
--signing-algorithm SHA256WITHRSA \
--template-arn arn:aws:acm-pca:::template/RootCACertificate/V1 \
--validity Value=3650,Type=DAYS \
--query 'CertificateArn' --output text)
echo $CERT_ARN【Admin】CA証明書取得・インポート
前ステップでCA証明書を作成したが、まだ作成したCA(CA_ARN)で使用できる状態ではない。
Private CA では外部CAで署名した証明書を使うこともできるため、インポートという手順を踏みCAを有効化する必要がある
aws acm-pca get-certificate \
--certificate-authority-arn $CA_ARN \
--certificate-arn $CERT_ARN \
--query 'Certificate' --output text > ca-cert.pem
aws acm-pca import-certificate-authority-certificate \
--certificate-authority-arn $CA_ARN \
--certificate fileb://ca-cert.pem⇒これにより、CAのステータスがアクティブとなる
クライアント証明書発行
【Client】秘密鍵・CSR作成
openssl genrsa -out client-key.pem 2048
openssl req -new -key client-key.pem -out client.csr \
-subj "/C=JP/O=TestOrg/CN=test-client"
# CSRの内容を表示(これをコピー)
cat client.csr【Admin】CSRを受け取り・クライアント証明書発行
# ClientでコピーしたCSRを貼り付け
cat > client.csr << 'EOF'
-----BEGIN CERTIFICATE REQUEST-----
(Clientでコピーした内容を貼り付け)
-----END CERTIFICATE REQUEST-----
EOF
CLIENT_CERT_ARN=$(aws acm-pca issue-certificate \
--certificate-authority-arn $CA_ARN \
--csr fileb://client.csr \
--signing-algorithm SHA256WITHRSA \
--template-arn arn:aws:acm-pca:::template/EndEntityCertificate/V1 \
--validity Value=365,Type=DAYS \
--query 'CertificateArn' --output text)
aws acm-pca get-certificate \
--certificate-authority-arn $CA_ARN \
--certificate-arn $CLIENT_CERT_ARN \
--query 'Certificate' --output text > client-cert.pem
# クライアント証明書を表示(これをコピー)
cat client-cert.pem
# CA証明書も表示(これもコピー)
cat ca-cert.pem【Client】証明書を受け取り・配置
# Adminでコピーしたクライアント証明書を貼り付け
cat > ~/client-cert.pem << 'EOF'
-----BEGIN CERTIFICATE-----
(Adminでコピーした内容を貼り付け)
-----END CERTIFICATE-----
EOF
# Adminでコピーしたca-cert.pemを貼り付け
cat > ~/ca-cert.pem << 'EOF'
-----BEGIN CERTIFICATE-----
(Adminでコピーした内容を貼り付け)
-----END CERTIFICATE-----
EOF
mv client-key.pem ~/client-cert.pem の内容
// Issuer: 発行したCAの情報 // Subject: クライアントの情報(CSRで指定したもの) // CA:FALSE はエンドエンティティ証明書(CA証明書ではない)ことを示す $ openssl x509 -text -noout < ~/client-cert.pem Certificate: Data: Version: 3 (0x2) Serial Number: ... Signature Algorithm: sha256WithRSAEncryption Issuer: C=JP, O=TestOrg, OU=IT, CN=Test-Private-CA Validity Not Before: ... Not After : ... Subject: C=JP, O=TestOrg, CN=test-client Subject Public Key Info: Public Key Algorithm: rsaEncryption Public-Key: (2048 bit) Modulus: ... Exponent: 65537 (0x10001) X509v3 extensions: X509v3 Basic Constraints: CA:FALSE ... Signature Algorithm: sha256WithRSAEncryption Signature Value: ...
サーバー証明書発行
【Server】秘密鍵・CSR作成
openssl genrsa -out server-key.pem 2048
# CNにはServerのPrivate IPを指定
openssl req -new -key server-key.pem -out server.csr \
-subj "/C=JP/O=TestOrg/CN=<SERVER_IP>"
# CSRの内容を表示(これをコピー)
cat server.csr【Admin】CSRを受け取り・サーバー証明書発行
# ServerでコピーしたCSRを貼り付け
cat > server.csr << 'EOF'
-----BEGIN CERTIFICATE REQUEST-----
(Serverでコピーした内容を貼り付け)
-----END CERTIFICATE REQUEST-----
EOF
SERVER_CERT_ARN=$(aws acm-pca issue-certificate \
--certificate-authority-arn $CA_ARN \
--csr fileb://server.csr \
--signing-algorithm SHA256WITHRSA \
--template-arn arn:aws:acm-pca:::template/EndEntityCertificate/V1 \
--validity Value=365,Type=DAYS \
--query 'CertificateArn' --output text)
aws acm-pca get-certificate \
--certificate-authority-arn $CA_ARN \
--certificate-arn $SERVER_CERT_ARN \
--query 'Certificate' --output text > server-cert.pem
# サーバー証明書を表示(これをコピー)
cat server-cert.pem
# CA証明書も表示(これもコピー)
cat ca-cert.pem【Server】証明書を受け取り・配置
# Adminでコピーしたサーバー証明書を貼り付け
cat > /tmp/server-cert.pem << 'EOF'
-----BEGIN CERTIFICATE-----
(Adminでコピーした内容を貼り付け)
-----END CERTIFICATE-----
EOF
# Adminでコピーしたca-cert.pemを貼り付け
cat > /tmp/ca-cert.pem << 'EOF'
-----BEGIN CERTIFICATE-----
(Adminでコピーした内容を貼り付け)
-----END CERTIFICATE-----
EOF
sudo mkdir -p /etc/ssl/private
sudo cp /tmp/ca-cert.pem /etc/ssl/certs/
sudo cp /tmp/server-cert.pem /etc/ssl/certs/
sudo mv server-key.pem /etc/ssl/private/
sudo chmod 600 /etc/ssl/private/server-key.pem
sudo chmod 644 /etc/ssl/certs/*.pem
rm /tmp/server-cert.pem /tmp/ca-cert.pemServer設定(Nginxセットアップ)
【Server】Nginxインストール・設定
sudo dnf install -y nginx
sudo systemctl enable nginx# <SERVER_IP> はServerのPrivate IP(証明書のCNと一致させる)
sudo tee /etc/nginx/conf.d/mtls.conf << 'EOF'
server {
listen 443 ssl;
server_name <SERVER_IP>;
ssl_certificate /etc/ssl/certs/server-cert.pem;
ssl_certificate_key /etc/ssl/private/server-key.pem;
ssl_client_certificate /etc/ssl/certs/ca-cert.pem;
ssl_verify_client on;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
location /debug-header {
add_header Content-Type text/plain;
return 200 "=== mTLS Success ===
Client DN: $ssl_client_s_dn
Client Verify: $ssl_client_verify
TLS Version: $ssl_protocol
";
}
location /health {
return 200 "OK";
}
}
EOF
sudo systemctl restart nginxmTLSテスト
注意: 以下の <SERVER_IP> はServerのPrivate IPに置き換えること
【Client】証明書なしテスト(失敗する)
curl https://<SERVER_IP>/debug-header以下のエラーが発生する
curl: (60) SSL certificate problem: unable to get local issuer certificate
More details here: https://curl.se/docs/sslcerts.html
curl failed to verify the legitimacy of the server and therefore could not
establish a secure connection to it. To learn more about this situation and
how to fix it, please visit the webpage mentioned above.
【Client】CA証明書のみテスト(失敗する)
curl --cacert ca-cert.pem https://<SERVER_IP>/debug-header<html>
<head><title>400 No required SSL certificate was sent</title></head>
<body>
<center><h1>400 Bad Request</h1></center>
<center>No required SSL certificate was sent</center>
<hr><center>nginx/1.28.0</center>
</body>
</html>
【Client】mTLSテスト(成功する)
curl --cacert ca-cert.pem \
--cert client-cert.pem \
--key client-key.pem \
https://<SERVER_IP>/debug-header=== mTLS Success ===
Client DN: CN=test-client,O=TestOrg,C=JP
Client Verify: SUCCESS
TLS Version: TLSv1.3
⇒成功
【Client】無効な証明書テスト(失敗する)
openssl genrsa -out fake-key.pem 2048
openssl req -new -x509 -days 365 -key fake-key.pem -out fake-cert.pem \
-subj "/C=JP/O=FakeOrg/CN=fake-client"
curl --cacert ca-cert.pem \
--cert fake-cert.pem \
--key fake-key.pem \
https://<SERVER_IP>/debug-header<html>
<head><title>400 The SSL certificate error</title></head>
<body>
<center><h1>400 Bad Request</h1></center>
<center>The SSL certificate error</center>
<hr><center>nginx/1.28.0</center>
</body>
</html>
より詳細を見てみる
opensslコマンドでmTLSのやり取りを確認
openssl s_client を使うとTLSハンドシェイクの詳細が確認できる。
openssl s_client -connect 172.31.32.25:443 \
-CAfile ~/ca-cert.pem \
-cert ~/client-cert.pem \
-key ~/client-key.pem \
-state実行結果
Connecting to <SERVER_IP> CONNECTED(00000003) SSL_connect:before SSL initialization SSL_connect:SSLv3/TLS write client hello SSL_connect:SSLv3/TLS write client hello SSL_connect:SSLv3/TLS read server hello Can't use SSL_get_servername SSL_connect:TLSv1.3 read encrypted extensions SSL_connect:SSLv3/TLS read server certificate request depth=1 C=JP, O=TestOrg, OU=IT, CN=Test-Private-CA verify return:1 depth=0 C=JP, O=TestOrg, CN=<SERVER_IP> verify return:1 SSL_connect:SSLv3/TLS read server certificate SSL_connect:TLSv1.3 read server certificate verify SSL_connect:SSLv3/TLS read finished SSL_connect:SSLv3/TLS write change cipher spec SSL_connect:TLSv1.3 write client compressed certificate SSL_connect:SSLv3/TLS write certificate verify SSL_connect:SSLv3/TLS write finished --- Certificate chain 0 s:C=JP, O=TestOrg, CN=<SERVER_IP> i:C=JP, O=TestOrg, OU=IT, CN=Test-Private-CA a:PKEY: rsaEncryption, 2048 (bit); sigalg: RSA-SHA256 v:NotBefore: Nov 29 05:32:11 2025 GMT; NotAfter: Nov 29 06:32:11 2026 GMT --- Server certificate -----BEGIN CERTIFICATE----- MIIDgjCCAmqgAwIBAgIQdN68Ffygt4Cg5BM1XQcTXDANBgkqhkiG9w0BAQsFADBG ... -----END CERTIFICATE----- subject=C=JP, O=TestOrg, CN=<SERVER_IP> issuer=C=JP, O=TestOrg, OU=IT, CN=Test-Private-CA --- Acceptable client certificate CA names C=JP, O=TestOrg, OU=IT, CN=Test-Private-CA Requested Signature Algorithms: ECDSA+SHA256:ECDSA+SHA384:ECDSA+SHA512:Ed25519:Ed448:RSA-PSS+SHA256:RSA-PSS+SHA384:RSA-PSS+SHA512:RSA-PSS+SHA256:RSA-PSS+SHA384:RSA-PSS+SHA512:RSA+SHA256:RSA+SHA384:RSA+SHA512:ECDSA+SHA224:RSA+SHA224 Shared Requested Signature Algorithms: ECDSA+SHA256:ECDSA+SHA384:ECDSA+SHA512:Ed25519:Ed448:RSA-PSS+SHA256:RSA-PSS+SHA384:RSA-PSS+SHA512:RSA-PSS+SHA256:RSA-PSS+SHA384:RSA-PSS+SHA512:RSA+SHA256:RSA+SHA384:RSA+SHA512 Peer signing digest: SHA256 Peer signature type: RSA-PSS Server Temp Key: X25519, 253 bits --- SSL handshake has read 1612 bytes and written 2179 bytes Verification: OK --- New, TLSv1.3, Cipher is TLS_AES_256_GCM_SHA384 Server public key is 2048 bit This TLS version forbids renegotiation. Compression: NONE Expansion: NONE No ALPN negotiated Early data was not sent Verify return code: 0 (ok) --- SSL_connect:SSL negotiation finished successfully SSL_connect:SSL negotiation finished successfully --- Post-Handshake New Session Ticket arrived: SSL-Session: Protocol : TLSv1.3 Cipher : TLS_AES_256_GCM_SHA384 Session-ID: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx Session-ID-ctx: Resumption PSK: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx PSK identity: None PSK identity hint: None SRP username: None TLS session ticket lifetime hint: 300 (seconds) TLS session ticket: 0000 - xx xx xx xx xx xx xx xx-xx xx xx xx xx xx xx xx ................ ... Start Time: xxxxxxxxxx Timeout : 7200 (sec) Verify return code: 0 (ok) Extended master secret: no Max Early Data: 0 --- SSL_connect:SSLv3/TLS read server session ticket read R BLOCK SSL_connect:SSL negotiation finished successfully SSL_connect:SSL negotiation finished successfully --- Post-Handshake New Session Ticket arrived: SSL-Session: Protocol : TLSv1.3 Cipher : TLS_AES_256_GCM_SHA384 Session-ID: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx ... Start Time: xxxxxxxxxx Timeout : 7200 (sec) Verify return code: 0 (ok) Extended master secret: no Max Early Data: 0 --- SSL_connect:SSLv3/TLS read server session ticket read R BLOCK
パケットキャプチャして暗号化されているか確認
tcpdumpでパケットをキャプチャし、TLS通信が暗号化されていることを確認する
- SesrverのプライベートIP: 172.31.32.25
- ClientのプライベートIP: 172.31.47.173
比較:暗号化なしの場合(HTTP)
Nginx の設定を変更し、http のリクエストを受けるように変更
# Server側でHTTPでもリッスン
sudo tee /etc/nginx/conf.d/http.conf << 'EOF'
server {
listen 8080;
location /test {
return 200 "SECRET DATA\n";
}
}
EOF
sudo systemctl reload nginx
# キャプチャ開始
sudo tcpdump -i any port 8080 -w /tmp/http.pcap &
# Client側からHTTPリクエスト
curl http://<SERVER_IP>:8080/test
# キャプチャ停止・確認
sudo pkill tcpdump
10 packets captured
11 packets received by filter
0 packets dropped by kernelmTLSの場合のキャプチャ内容(mtls.pcap)
$ tcpdump -r /tmp/http.pcap | nl reading from file /tmp/http.pcap, link-type LINUX_SLL2 (Linux cooked v2), snapshot length 262144 Warning: interface names might be incorrect 1 07:19:04.430066 ens5 In IP ip-172-31-47-173.ap-northeast-1.compute.internal.33258 > ip-172-31-32-25.ap-northeast-1.compute.internal.webcache: Flags [S], seq 2582238348, win 62727, options [mss 8961,sackOK,TS val 2836648391 ecr 0,nop,wscale 7], length 0 2 07:19:04.430138 ens5 Out IP ip-172-31-32-25.ap-northeast-1.compute.internal.webcache > ip-172-31-47-173.ap-northeast-1.compute.internal.33258: Flags [S.], seq 2344356564, ack 2582238349, win 62643, options [mss 8961,sackOK,TS val 2601695062 ecr 2836648391,nop,wscale 7],length 0 3 07:19:04.430331 ens5 In IP ip-172-31-47-173.ap-northeast-1.compute.internal.33258 > ip-172-31-32-25.ap-northeast-1.compute.internal.webcache: Flags [.], ack 1, win 491, options [nop,nop,TS val 2836648391 ecr 2601695062], length 0 4 07:19:04.430331 ens5 In IP ip-172-31-47-173.ap-northeast-1.compute.internal.33258 > ip-172-31-32-25.ap-northeast-1.compute.internal.webcache: Flags [P.], seq 1:86, ack 1, win 491, options [nop,nop,TS val 2836648391 ecr 2601695062], length 85: HTTP: GET /test HTTP/1.1 5 07:19:04.430379 ens5 Out IP ip-172-31-32-25.ap-northeast-1.compute.internal.webcache > ip-172-31-47-173.ap-northeast-1.compute.internal.33258: Flags [.], ack 86, win 489, options [nop,nop,TS val 2601695063 ecr 2836648391], length 0 6 07:19:04.430549 ens5 Out IP ip-172-31-32-25.ap-northeast-1.compute.internal.webcache > ip-172-31-47-173.ap-northeast-1.compute.internal.33258: Flags [P.], seq 1:175, ack 86, win 489, options [nop,nop,TS val 2601695063 ecr 2836648391], length 174: HTTP: HTTP/1.1 200 OK 7 07:19:04.430709 ens5 In IP ip-172-31-47-173.ap-northeast-1.compute.internal.33258 > ip-172-31-32-25.ap-northeast-1.compute.internal.webcache: Flags [.], ack 175, win 490, options [nop,nop,TS val 2836648391 ecr 2601695063], length 0 8 07:19:04.430868 ens5 In IP ip-172-31-47-173.ap-northeast-1.compute.internal.33258 > ip-172-31-32-25.ap-northeast-1.compute.internal.webcache: Flags [F.], seq 86, ack 175, win 490, options [nop,nop,TS val 2836648391 ecr 2601695063], length 0 9 07:19:04.430915 ens5 Out IP ip-172-31-32-25.ap-northeast-1.compute.internal.webcache > ip-172-31-47-173.ap-northeast-1.compute.internal.33258: Flags [F.], seq 175, ack 87, win 489, options [nop,nop,TS val 2601695063 ecr 2836648391], length 0 10 07:19:04.431083 ens5 In IP ip-172-31-47-173.ap-northeast-1.compute.internal.33258 > ip-172-31-32-25.ap-northeast-1.compute.internal.webcache: Flags [.], ack 176, win 490, options [nop,nop,TS val 2836648392 ecr 2601695063], length 0詳細
// 1-3: TCP 3ウェイハンドシェイク 1 In Flags [S] - クライアント→サーバー: SYN(接続要求) 2 Out Flags [S.] - サーバー→クライアント: SYN+ACK(接続承認) 3 In Flags [.] - クライアント→サーバー: ACK(確認) →TCP接続確立完了 // 4: HTTPリクエスト 4 In Flags [P.], length 85: HTTP: GET /test HTTP/1.1 →クライアントがGETリクエスト送信。P=PUSH(データ送信) // 5: ACK 5 Out Flags [.], ack 86 →サーバーがリクエスト受信を確認。 // 6: HTTPレスポンス 6 Out Flags [P.], length 174: HTTP: HTTP/1.1 200 OK →サーバーが200 OKとレスポンスボディ(SECRET DATA含む)を送信。 // 7: ACK 7 In Flags [.], ack 175 →クライアントがレスポンス受信を確認。 // 8-10: TCP 4ウェイハンドシェイク(切断) 8 In Flags [F.] - クライアント→サーバー: FIN(切断要求) 9 Out Flags [F.] - サーバー→クライアント: FIN+ACK 10 In Flags [.] - クライアント→サーバー: ACK
HTTPの場合は
SECRET DATAが平文で見えるtcpdump -r /tmp/http.pcap -A ... 07:19:04.430549 ens5 Out IP ip-172-31-32-25.ap-northeast-1.compute.internal.webcache > ip-172-31-47-173.ap-northeast-1.compute.internal.33258: Flags [P.], seq 1:175, ack 86, win 489, options [nop,nop,TS val 2601695063 ecr 2836648391], length 174: HTTP: HTTP/1.1 200 OK E.....@...=L.. .../....... ................ ...W....HTTP/1.1 200 OK Server: nginx/1.28.0 Date: Sat, 29 Nov 2025 07:19:04 GMT Content-Type: application/octet-stream Content-Length: 12 Connection: keep-alive SECRET DATA ...
比較:暗号化ありの場合(mTLS)
【Server】tcpdumpでキャプチャ開始
sudo tcpdump -i any port 443 -w /tmp/mtls.pcap &【Client】mTLSリクエスト送信
curl --cacert ~/ca-cert.pem \
--cert ~/client-cert.pem \
--key ~/client-key.pem \
https://<SERVER_IP>/debug-header【Server】キャプチャ停止・内容確認
sudo pkill tcpdump
55 packets captured
61 packets received by filter
0 packets dropped by kernelmTLSの場合のキャプチャ内容(mtls.pcap)
$ tcpdump -r /tmp/mtls.pcap host 172.31.47.173 | nlreading from file /tmp/mtls.pcap, link-type LINUX_SLL2 (Linux cooked v2), snapshot length 262144 Warning: interface names might be incorrect 1 07:17:03.506677 ens5 In IP ip-172-31-47-173.ap-northeast-1.compute.internal.34322 > ip-172-31-32-25.ap-northeast-1.compute.internal.https: Flags [S], seq 2981894960, win 62727, options [mss 8961,sackOK,TS val 2836527467 ecr 0,nop,wscale 7], length 0 2 07:17:03.506714 ens5 Out IP ip-172-31-32-25.ap-northeast-1.compute.internal.https > ip-172-31-47-173.ap-northeast-1.compute.internal.34322: Flags [S.], seq 4110597958, ack 2981894961, win 62643, options [mss 8961,sackOK,TS val 2601574139 ecr 2836527467,nop,wscale 7], length 0 3 07:17:03.506835 ens5 In IP ip-172-31-47-173.ap-northeast-1.compute.internal.34322 > ip-172-31-32-25.ap-northeast-1.compute.internal.https: Flags [.], ack 1, win 491, options [nop,nop,TS val 2836527467 ecr 2601574139], length 0 4 07:17:03.509274 ens5 In IP ip-172-31-47-173.ap-northeast-1.compute.internal.34322 > ip-172-31-32-25.ap-northeast-1.compute.internal.https: Flags [P.], seq 1:518, ack 1, win 491, options [nop,nop,TS val 2836527470 ecr 2601574139], length 517 5 07:17:03.509374 ens5 Out IP ip-172-31-32-25.ap-northeast-1.compute.internal.https > ip-172-31-47-173.ap-northeast-1.compute.internal.34322: Flags [.], ack 518, win 486, options [nop,nop,TS val 2601574142 ecr 2836527470], length 0 6 07:17:03.510573 ens5 Out IP ip-172-31-32-25.ap-northeast-1.compute.internal.https > ip-172-31-47-173.ap-northeast-1.compute.internal.34322: Flags [P.], seq 1:1628, ack 518, win 486, options [nop,nop,TS val 2601574143 ecr 2836527470], length 1627 7 07:17:03.510677 ens5 In IP ip-172-31-47-173.ap-northeast-1.compute.internal.34322 > ip-172-31-32-25.ap-northeast-1.compute.internal.https: Flags [.], ack 1628, win 479, options [nop,nop,TS val 2836527471 ecr 2601574143], length 0 8 07:17:03.512572 ens5 In IP ip-172-31-47-173.ap-northeast-1.compute.internal.34322 > ip-172-31-32-25.ap-northeast-1.compute.internal.https: Flags [P.], seq 518:2395, ack 1628, win 479, options [nop,nop,TS val 2836527473 ecr 2601574143], length 1877 9 07:17:03.512677 ens5 In IP ip-172-31-47-173.ap-northeast-1.compute.internal.34322 > ip-172-31-32-25.ap-northeast-1.compute.internal.https: Flags [P.], seq 2395:2505, ack 1628, win 479, options [nop,nop,TS val 2836527473 ecr 2601574143], length 110 10 07:17:03.512914 ens5 Out IP ip-172-31-32-25.ap-northeast-1.compute.internal.https > ip-172-31-47-173.ap-northeast-1.compute.internal.34322: Flags [.], ack 2505, win 471, options [nop,nop,TS val 2601574145 ecr 2836527473], length 0 11 07:17:03.513214 ens5 Out IP ip-172-31-32-25.ap-northeast-1.compute.internal.https > ip-172-31-47-173.ap-northeast-1.compute.internal.34322: Flags [P.], seq 1628:2811, ack 2505, win 471, options [nop,nop,TS val 2601574145 ecr 2836527473], length 1183 12 07:17:03.513349 ens5 Out IP ip-172-31-32-25.ap-northeast-1.compute.internal.https > ip-172-31-47-173.ap-northeast-1.compute.internal.34322: Flags [P.], seq 2811:3994, ack 2505, win 471, options [nop,nop,TS val 2601574146 ecr 2836527473], length 1183 13 07:17:03.513412 ens5 Out IP ip-172-31-32-25.ap-northeast-1.compute.internal.https > ip-172-31-47-173.ap-northeast-1.compute.internal.34322: Flags [P.], seq 3994:4311, ack 2505, win 471, options [nop,nop,TS val 2601574146 ecr 2836527473], length 317 14 07:17:03.513429 ens5 In IP ip-172-31-47-173.ap-northeast-1.compute.internal.34322 > ip-172-31-32-25.ap-northeast-1.compute.internal.https: Flags [.], ack 3994, win 461, options [nop,nop,TS val 2836527474 ecr 2601574145], length 0 15 07:17:03.513574 ens5 In IP ip-172-31-47-173.ap-northeast-1.compute.internal.34322 > ip-172-31-32-25.ap-northeast-1.compute.internal.https: Flags [P.], seq 2505:2529, ack 4311, win 459, options [nop,nop,TS val 2836527474 ecr 2601574146], length 24 16 07:17:03.513627 ens5 Out IP ip-172-31-32-25.ap-northeast-1.compute.internal.https > ip-172-31-47-173.ap-northeast-1.compute.internal.34322: Flags [F.], seq 4311, ack 2529, win 471, options [nop,nop,TS val 2601574146 ecr 2836527474], length 0 17 07:17:03.513700 ens5 In IP ip-172-31-47-173.ap-northeast-1.compute.internal.34322 > ip-172-31-32-25.ap-northeast-1.compute.internal.https: Flags [F.], seq 2529, ack 4311, win 459, options [nop,nop,TS val 2836527474 ecr 2601574146], length 0 18 07:17:03.513700 ens5 In IP ip-172-31-47-173.ap-northeast-1.compute.internal.34322 > ip-172-31-32-25.ap-northeast-1.compute.internal.https: Flags [.], ack 4312, win 459, options [nop,nop,TS val 2836527474 ecr 2601574146], length 0 19 07:17:03.513706 ens5 Out IP ip-172-31-32-25.ap-northeast-1.compute.internal.https > ip-172-31-47-173.ap-northeast-1.compute.internal.34322: Flags [.], ack 2530, win 471, options [nop,nop,TS val 2601574146 ecr 2836527474], length 0
mTLS(TLS)の場合は
SECRET DATAが暗号化されており見えない$ tcpdump -r /tmp/mtls.pcap -A | grep SECRET reading from file /tmp/mtls.pcap, link-type LINUX_SLL2 (Linux cooked v2), snapshot length 262144Warning: interface names might be incorrect // SECRET DATA がヒットしない
リソース削除
【Admin】Private CA削除
# CA無効化
aws acm-pca update-certificate-authority \
--certificate-authority-arn $CA_ARN \
--status DISABLED
# CA削除
aws acm-pca delete-certificate-authority \
--certificate-authority-arn $CA_ARN \
--permanent-deletion-time-in-days 7CloudFormationスタック削除
aws cloudformation delete-stack --stack-name mtls-env