
ACM の ACME 証明書発行が PrivateLink に対応したので、IGW も NAT Gateway もない VPC で試してみた
はじめに
2026年10月6日、ACM の ACME 証明書発行を VPC のインターフェースエンドポイント経由で行えるようになりました。
今回は、インターネットゲートウェイ(IGW)と NAT Gateway のない VPC に EC2 を用意しました。ディレクトリ URL のホスト名はそのままで、プライベート DNS によってインターフェースエンドポイントのプライベート IP に解決されます。この EC2 から PrivateLink 経由で certbot により証明書を発行し、Nginx の HTTPS に使えるかを試しました。
あわせて、オンプレミスの環境にみたてた別 VPC を用意し、そこからの到達性(名前解決、ディレクトリ取得、アカウント登録)も確認しました。
ACM の ACME をパブリックなエンドポイントで使う手順は7月に紹介済みのため、この記事では省きます。
検証の構成
東京リージョンに VPC(10.20.0.0/16、以下 VPC-A)とプライベートサブネットを1つ作りました。構成は次のとおりです。

VPC-A のルートと IGW・NAT Gateway の確認
aws ec2 describe-route-tables --filters Name=vpc-id,Values=<vpc-id> \
--query 'RouteTables[].Routes[].{Dest:DestinationCidrBlock,Target:GatewayId,Prefix:DestinationPrefixListId}'
[
{
"Dest": "10.20.0.0/16",
"Target": "local",
"Prefix": null
},
{
"Dest": null,
"Target": "<s3-gateway-endpoint-id>",
"Prefix": "pl-61a54008"
}
]
aws ec2 describe-internet-gateways --filters Name=attachment.vpc-id,Values=<vpc-id> --query 'InternetGateways'
[]
aws ec2 describe-nat-gateways --filter Name=vpc-id,Values=<vpc-id> --query 'NatGateways'
[]
ルートは local と S3 ゲートウェイエンドポイント向けだけで、IGW と NAT Gateway はありません。
EC2 は Amazon Linux 2023 の t3.micro で、パブリック IP は付けていません。SSH は EC2 Instance Connect Endpoint 経由にしました。certbot 2.6.0 と nginx 1.30.5 は、S3 ゲートウェイエンドポイント経由の dnf install でインストールしました。閉域網では、パッケージの取得経路と SSH の経路を自分で用意することになります。
プライベート DNS を使うため、create-vpc で作った VPC の DNS サポートと DNS ホスト名を有効にしました。
aws ec2 modify-vpc-attribute --vpc-id <vpc-id> --enable-dns-support '{"Value":true}'
aws ec2 modify-vpc-attribute --vpc-id <vpc-id> --enable-dns-hostnames '{"Value":true}'
ACME エンドポイント、ドメイン検証、EAB は VPC の外で、前作と同じ手順で用意しました。ACME エンドポイントは --authorization-behavior PRE_APPROVED で作成しています。ACM の ACME は管理者が事前承認したドメインを使う方式で、クライアントが毎回ドメインの所有を証明する一般的な公開 ACME サービスとは異なります。
ACME で発行した証明書の有効期間は45日で、更新はクライアントが行います。ELB、CloudFront、API Gateway には紐づけられません。
エンドポイントがないと発行できない
同じ VPC で、インターフェースエンドポイントを作る前にディレクトリ URL へアクセスしました。ホスト名は公開 IP に解決されました。
$ getent ahostsv4 acm-acme-enroll.ap-northeast-1.api.aws
203.0.113.10 STREAM acm-acme-enroll.ap-northeast-1.api.aws
203.0.113.20 STREAM
203.0.113.30 STREAM
$ curl -sS -m 15 https://acm-acme-enroll.ap-northeast-1.api.aws/<directory-id>/directory
curl: (28) Connection timed out after 15000 milliseconds
この状態で certbot を実行すると、約2分16秒後に終了コード 1 で失敗しました。EAB にはダミー値を渡しましたが、エラーの内容は EAB やドメイン検証に関するものではなく、ACME エンドポイントに到達できないというものです。
An unexpected error occurred:
ValueError: Requesting acm-acme-enroll.ap-northeast-1.api.aws/<directory-id>: Network is unreachable
インターフェースエンドポイントを作る
PrivateLink 用のエンドポイントサービスは、管理操作用の acm-acme とプロトコル用の acm-acme-enroll の2つで、互いに独立しています。今回 VPC に作ったのは acm-acme-enroll のインターフェースエンドポイント1つで、acm-acme のエンドポイントは作っていません。ユーザーガイドには、プライベート DNS ホスト名の有効化が必須であること、ACME エンドポイントと同じリージョンに作る必要があること、一部の AZ が非対応であることが書かれています。
エンドポイントはプライベート DNS を有効にして作り、セキュリティグループには VPC の CIDR からの TCP 443 を許可しました。
aws ec2 create-vpc-endpoint \
--vpc-id <vpc-id> \
--service-name com.amazonaws.ap-northeast-1.acm-acme-enroll \
--vpc-endpoint-type Interface \
--subnet-ids <subnet-id> \
--security-group-ids <endpoint-sg-id> \
--private-dns-enabled
aws ec2 authorize-security-group-ingress \
--group-id <endpoint-sg-id> \
--protocol tcp --port 443 --cidr 10.20.0.0/16
東京リージョンで対応している AZ は、AZ ID で apne1-az4、apne1-az1、apne1-az2 です。このアカウントでは ap-northeast-1a、1c、1d にあたります。AZ 名と AZ ID の対応はアカウントごとに異なるため、サブネットの AZ は AZ ID で確認します。対応 AZ は describe-vpc-endpoint-services の AvailabilityZoneIds で確認できます。
エンドポイントは作成要求から1分以内に available になりました。ただし、その直後(作成要求の約40秒後)はホスト名が公開 IP のままで、curl は15秒でタイムアウトしました。作成要求の約2分後にはプライベート IP に解決され、約3分後にはディレクトリ URL が HTTP 200 を返しました。
$ getent ahostsv4 acm-acme-enroll.ap-northeast-1.api.aws | head -1
10.20.1.158 STREAM acm-acme-enroll.ap-northeast-1.api.aws
available になった直後は公開 IP に解決されるため、ホスト名がプライベート IP に解決されるのを確かめてから certbot を実行します。
証明書を発行して Nginx で使う
ディレクトリ URL はエンドポイントがない場合と同じホスト名のままで、EAB だけ実際の値に替えて certbot を実行しました。発行は1回で成功しました。
sudo certbot certonly --standalone --non-interactive --agree-tos \
--register-unsafely-without-email \
--server https://acm-acme-enroll.ap-northeast-1.api.aws/<directory-id>/directory \
--eab-kid <eab-kid> --eab-hmac-key <eab-hmac-key> \
--issuance-timeout 120 \
-d acme-pl.cm.suama.net
Saving debug log to /var/log/letsencrypt/letsencrypt.log
Account registered.
Requesting a certificate for acme-pl.cm.suama.net
Successfully received certificate.
Certificate is saved at: /etc/letsencrypt/live/acme-pl.cm.suama.net/fullchain.pem
Key is saved at: /etc/letsencrypt/live/acme-pl.cm.suama.net/privkey.pem
This certificate expires on 2026-11-21.
These files will be updated when the certificate renews.
Certbot has set up a scheduled task to automatically renew this certificate in the background.
発行された証明書の subject は空で、指定した FQDN は SAN に入っています。
subject=
issuer=C=US, O=Amazon, CN=Amazon ECDSA 256 S10
notBefore=Oct 7 06:57:18 2026 GMT
notAfter=Nov 21 07:57:18 2026 GMT
X509v3 Subject Alternative Name: critical
DNS:acme-pl.cm.suama.net
Nginx の設定は /etc/nginx/conf.d/ssl.conf に置きました。
sudo cp /tmp/ssl.conf /etc/nginx/conf.d/ssl.conf
sudo nginx -t
sudo systemctl enable --now nginx
ssl.conf
server {
listen 443 ssl;
server_name acme-pl.cm.suama.net;
ssl_certificate /etc/letsencrypt/live/acme-pl.cm.suama.net/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/acme-pl.cm.suama.net/privkey.pem;
location / {
return 200 "hello from closed network via ACM ACME\n";
add_header Content-Type text/plain;
}
}
EC2 自身から、証明書の検証を省く -k を付けずに HTTPS でアクセスしました。
$ curl -sS --resolve acme-pl.cm.suama.net:443:127.0.0.1 -v https://acme-pl.cm.suama.net/
* SSL connection using TLSv1.3 / TLS_AES_256_GCM_SHA384 / x25519 / id-ecPublicKey
* subject:
* expire date: Nov 21 07:57:18 2026 GMT
* issuer: C=US; O=Amazon; CN=Amazon ECDSA 256 S10
< HTTP/1.1 200 OK
hello from closed network via ACM ACME
ACM 側での確認
証明書の ARN を指定した describe-certificate で、ACME 証明書として確認できました。応答のうち関連するフィールドは次のとおりです。
"DomainName": "acme-pl.cm.suama.net",
"Status": "ISSUED",
"Type": "AMAZON_ISSUED",
"CertificateKeyPairOrigin": "ACME",
"AcmeEndpointArn": "arn:aws:acm:ap-northeast-1:111122223333:acme-endpoint/<acme-endpoint-id>",
"AcmeAccountId": "<acme-account-id>"
発行は成功しましたが、前作で案内した list-certificates --certificate-key-pair-origins ACME は、発行の約1分後と約6分後のどちらでも、この環境では空のリスト("CertificateSummaryList": [])を返しました。
別 VPC から到達する
オンプレミス接続の代わりに、VPC-B(10.30.0.0/16、IGW・NAT Gateway なし)を作り、VPC-A とピアリングで接続しました。VPC-B でも DNS サポートと DNS ホスト名を有効にし、VPC-A のエンドポイントのセキュリティグループには VPC-B の CIDR からの TCP 443 を追加しています。
構成は次のとおりです。

VPC-B にはエンドポイントを作らず、プライベートホストゾーン(PHZ)で名前を VPC-A のエンドポイントへ向けました。ゾーン名はディレクトリ URL のホスト名と同じ acm-acme-enroll.ap-northeast-1.api.aws で、api.aws 配下の名前でもホストゾーンを作成できました。ゾーン頂点には、VPC-A のエンドポイントを指すエイリアスレコードを置きます。
aws route53 create-hosted-zone \
--name acm-acme-enroll.ap-northeast-1.api.aws \
--caller-reference <unique-string> \
--vpc VPCRegion=ap-northeast-1,VPCId=<vpc-b-id> \
--hosted-zone-config PrivateZone=true
aws route53 change-resource-record-sets \
--hosted-zone-id <hosted-zone-id> \
--change-batch file://phz-alias-record.json
phz-alias-record.json
{
"Changes": [{
"Action": "CREATE",
"ResourceRecordSet": {
"Name": "acm-acme-enroll.ap-northeast-1.api.aws.",
"Type": "A",
"AliasTarget": {
"HostedZoneId": "Z2E726K9Y6RL4W",
"DNSName": "<vpce-id>-<suffix>.acm-acme-enroll.ap-northeast-1.vpce.amazonaws.com",
"EvaluateTargetHealth": false
}
}
}]
}
AliasTarget の HostedZoneId は、エンドポイントの DNS エントリに表示されるホストゾーン ID です。DNSName には VPC-A のエンドポイントの DNS 名を入れます。
VPC-B の EC2 では、ホスト名が VPC-A のエンドポイントのプライベート IP に解決され、ディレクトリ URL が HTTP 200 を返しました。新しく作った EAB で certbot register も成功しています。
$ getent ahostsv4 acm-acme-enroll.ap-northeast-1.api.aws | head -1
10.20.1.158 STREAM acm-acme-enroll.ap-northeast-1.api.aws
$ certbot register --non-interactive --agree-tos --register-unsafely-without-email \
--server https://acm-acme-enroll.ap-northeast-1.api.aws/<directory-id>/directory \
--eab-kid <eab-kid> --eab-hmac-key <eab-hmac-key>
Account registered.
料金
料金は2026年10月8日に確認した値で、エンドポイントと PHZ は東京リージョンの単価です。
ACME 自体の料金
ACME 証明書は FQDN 1件あたり $1.00(最初の1,000件/月)で、発行時と自動更新のたびに課金されます。
関連費用
PrivateLink のインターフェースエンドポイントは、配置した AZ ごとに、プロビジョニングされている時間に対して課金されます。1時間に満たない分も1時間として数えられます。東京の時間単価は $0.014 で、データ処理は最初の 1 PB まで $0.01/GB です。
1か月を720時間として、時間課金だけを計算すると次のとおりです。
| 配置する AZ の数 | 1時間あたり | 1か月(720時間) |
|---|---|---|
| 1 | $0.014 | $10.08 |
| 2 | $0.028 | $20.16 |
| 3 | $0.042 | $30.24 |
マルチ AZ にすると AZ ごとに時間課金が発生するため、2 AZ では費用が2倍になります。エンドポイントの費用は発行の回数にかかわらず、配置している間は発生し続けます。
別 VPC から使うためのプライベートホストゾーン(PHZ)は、1つあたり月 $0.50(最初の25個まで。26個目以降は $0.10)です。日割りはなく、作成時と翌月以降の毎月1日に課金されます。プライベートホストゾーンへのクエリは無料です。作成から12時間以内に削除したホストゾーンは課金されません。実際のオンプレミスから使う場合は、PHZ の関連付けだけでは名前解決できず、オンプレミス側から問い合わせを受ける仕組み(Route 53 VPC Resolver のインバウンドエンドポイントなど)が別に必要になり、その費用も加わります。
まとめ
ACM の ACME 証明書発行を VPC のインターフェースエンドポイント経由で行えるようになり、IGW も NAT Gateway もない VPC の EC2 から証明書を発行できました。
今回の検証にかかった ACME 関連の費用は、計算すると1ドル強でした。この構成を継続して使う場合は、自動更新のたびに発生する証明書料金に加えて、エンドポイントを配置している間の時間課金(1 AZ で月約 $10、AZ ごとに増加)がかかります。オンプレミスから使う場合は、名前解決の仕組みの費用も別に必要です。
外部への接続経路を持てない環境でも PrivateLink 経由で ACME を利用できるようになりましたが、採用される場合は、ネットワークの実現性だけでなく、維持費についても事前に評価いただくことをおすすめします。







