2024.6.2 IMDSV2(インスタンスメタデータV2)の利用パターンを追加
作業の経緯
実験用のAWSのEC2上のWebサーバーでhttpsを有効化するため、固定IPアドレス(Elastic IP)を使用しています。
サーバーは使用する時だけ起動してコスト削減を図っているのですが、ElasticIPはずっと課金されてしまうため、これを節約したいというのが今回の経緯です。
この記事で使用したEC2インスタンスはかなり古いモノですので、メタデータのバージョンがIMDSv1です。
IMDSv2の場合のスクリプトを追加しました。
IMDSv1は現在非推奨となっており、今年中には新しいインスタンスでIMDSv2が強制されるようになるはずですが、IMDSv2ではパブリックIPアドレスの取得方法が変わっています。
この手順は今後確かめて、改めて掲載しようと思います。
作業前の状態
- EC2インスタンスでWebサーバーを構築
- ElasticIPを確保して上記インスタンスに割り当て
- お名前.comで独自ドメインを取得してRoute53サービスに権限を移譲
- Route53のホストゾーンにサーバー名を登録
- Let's Encryptで上記サーバー名の証明書を作成
作業後のイメージ
- ElasticIPを解放してサーバーのIPアドレスを動的割り当てとする
- サーバー起動時にパブリックIPアドレスを取得する
- 取得したパブリックIPアドレスでRoute53のレコードを自動更新する
作業手順
パブリックIPアドレスの取得
EC3インスタンス上でパブリックIPアドレスを取得するには、以下のURLでメタデータを取得します。
このIPアドレスはメタデータを取得するために特別に割り当てられているようです。
【IMDSv1の場合】
curl -H "Metadata-Flavor: AWS-V2" http://169.254.169.254/latest/meta-data/public-ipv4
【IMDSv2の場合】
IMDSv2では、最初にPUTリクエストで一時トークンを生成、トークンを渡してメタデータを取得します。
(一時トークン)
curl -X PUT -H "X-aws-ec2-metadata-token-ttl-seconds:300" http://169.254.169.254/latest/api/token
(結果例)
AQAAAP3MH9PAkcyqtZmB_S_q9n5Ah9eX8YawSmzb0n8L5UcKHpLJLQ==
(メタデータ取得)
curl -X GET -H "X-aws-ec2-metadata-token:AQAAAP3MH9PAkcyqtZmB_S_q9n5Ah9eX8YawSmzb0n8L5UcKHpLJLQ==" http://169.254.169.254/latest/meta-data/public-ipv4
レコード更新のための権限付与
IAMポリシーの作成
Route53のレコードを更新するためのIAMポリシーを作成する必要があります。
- IAMサービスを開く
- [アクセス管理]-[ポリシー]を開く
- [ポリシーの作成]を選択
- サービスの選択でRoute53を選ぶ
- アクションの選択でChangeResourceRecordSetsを選ぶ
- リソースARNにarn:aws:route53:::hostedzone/[ホストゾーンID]を選ぶ(ホストゾーンIDはRoute53サービスで自分のゾーンを選ぶと表示されている)
- ポリシー名を付けて保存
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "VisualEditor0",
"Effect": "Allow",
"Action": "route53:ChangeResourceRecordSets",
"Resource": "arn:aws:route53:::hostedzone/<ホストゾーンID>"
}
]
}
IAMロールの作成
EC2にポリシーを割り当てるためにIAMロールを作成します。
- IAMサービスを開く
- [アクセス管理]-[ロール]を開く
- [ロールの作成]を選択
- 信頼されたエンティティタイプで[AWSサービス]を選択
- ユースケースで[EC2]を選ぶ
- 許可ポリシーとして、前項で作成したポリシーを選択する
- ロール名を付けて保存
EC2インスタンスへの割り当て
- EC2サービスを開く
- Webサーバーのインスタンスを選択
- [アクション]-[セキュリティ]-[IAMロールの変更]を選択
- 前項で作成したIAMロールを選択する
※もともとEC2にIAMロールを割り当てている場合は、そのロールに前述の手順で作成したポリシーを割り当てるか、新しいIAMロールに必要なポリシーを割り当てる必要があります。
Route53のレコード更新
AWS-CLIのインストール
Route53のレコードを更新するには、AWS-CLIコマンドを使用します。
私が使っているUbuntuのAMIにはデフォルトでAWS-CLIが入っていないのでインストールします。
sudo apt install aws
Route53レコード更新
AWS-CLIのコマンドは以下のような感じです。
aws route53 change-resource-record-sets --hosted-zone-id <Route53のゾーンID> --change-batch {"Changes": [
{
"Action": "UPSERT",
"ResourceRecordSet": {
"Name": "<サーバーのFQDN>",
"Type": "A",
"TTL": 300,
"ResourceRecords": [
{
"Value": "<パブリックIPv4アドレス>"
}
]
}
}
]
}
自動更新のスクリプト作成
レコードの更新を自動化するため、スクリプトを作成します。
【IMDSv1の場合】
#!/bin/bash
PUBIP=`curl -H "Metadata-Flavor: AWS-V2" http://169.254.169.254/latest/meta-data/public-ipv4`
ZONEID=${1:-"<ホストゾーンID>"}
JSON="{\"Changes\": [
{
\"Action\": \"UPSERT\",
\"ResourceRecordSet\": {
\"Name\": \"<サーバーのFQDN>\",
\"Type\": \"A\",
\"TTL\": 300,
\"ResourceRecords\": [
{
\"Value\": \"$PUBIP\"
}
]
}
}
]
}"
【IMDSv2の場合】
#!/bin/bash
# Route 53のホストゾーンID
ZONEID=${1:-"ZZZZAAAABBBBB11111"}
# サーバーのFQDN
FQDN=www.example.com
# パブリックIPv4アドレスを取得する
TOKEN=`curl -X PUT -H "X-aws-ec2-metadata-token-ttl-seconds:300" http://169.254.169.254/latest/api/token`
PUBIP=`curl -X GET -H "X-aws-ec2-metadata-token:${TOKEN}" http://169.254.169.254/latest/meta-data/public-ipv4`
echo $PUBIP
# Route53のレコードを更新するためのJSON
JSON="{\"Changes\": [
{
\"Action\": \"UPSERT\",
\"ResourceRecordSet\": {
\"Name\": \"${FQDN}\",
\"Type\": \"A\",
\"TTL\": 300,
\"ResourceRecords\": [
{
\"Value\": \"$PUBIP\"
}
]
}
}
]
}"
# aws-cliコマンドによりRoute53のレコードを更新する
aws route53 change-resource-record-sets --hosted-zone-id $ZONEID --change-batch "$JSON"
このシェルスクリプトを実行すると、EC2インスタンスのパブリックIPアドレスをホストゾーンのサーバー名のAレコードに登録します。
chmod +x update.sh
./update.sh <ホストゾーンID>
サービス登録
サーバー起動時にシェルを自動実行するため、サービス定義を作成して登録します。
[Unit]
Description=Update Route53 Record to my address.
After=network.target
[Service]
Type=simple
User=root
ExecStart=/home/ubuntu/update.sh
[Install]
WantedBy=multi-user.target
sudo systemctl enable dns-update
以上で、サーバー起動時にパブリックIPアドレスを取得して更新します。
補足
EC2のインスタンス生成時にAmazon Linux 2023を使用すると、デフォルトでIMDSv2のみが有効になっており、IMDSv1は利用できません。
古いAMI(Amazon Linux 2など)を利用している場合は両方が有効になっていますが、セキュリティを向上するためにIMDSv1を無効化することが推奨されています。
EC2インスタンスのIMDSをv2のみに制限する
- インスタンス一覧でインスタンスを選択し、「アクション」-「インスタンスの設定」-「インスタンスメタデータオプションの変更」を選択する
- 「IMDSv2」の欄を[Optional]から[必須]に変更する