ACM の ACME 証明書発行が PrivateLink に対応したので、IGW も NAT Gateway もない VPC で試してみた

ACM の ACME 証明書発行が PrivateLink に対応したので、IGW も NAT Gateway もない VPC で試してみた

AWS Certificate Manager(ACM)の ACME 証明書発行が AWS PrivateLink に対応しました。IGW も NAT Gateway もない VPC の EC2 から certbot で証明書を発行し、Nginx で HTTPS を提供できました。別 VPC からの名前解決も確認しています。
2026.10.08

はじめに

2026年10月6日、ACM の ACME 証明書発行を VPC のインターフェースエンドポイント経由で行えるようになりました。

https://aws.amazon.com/jp/about-aws/whats-new/2026/10/AWS-Certificate-Manager-ACME-Privatelink/

今回は、インターネットゲートウェイ(IGW)と NAT Gateway のない VPC に EC2 を用意しました。ディレクトリ URL のホスト名はそのままで、プライベート DNS によってインターフェースエンドポイントのプライベート IP に解決されます。この EC2 から PrivateLink 経由で certbot により証明書を発行し、Nginx の HTTPS に使えるかを試しました。

あわせて、オンプレミスの環境にみたてた別 VPC を用意し、そこからの到達性(名前解決、ディレクトリ取得、アカウント登録)も確認しました。

ACM の ACME をパブリックなエンドポイントで使う手順は7月に紹介済みのため、この記事では省きます。

https://dev.classmethod.jp/articles/acm-acme-endpoint-certbot-ec2-nginx-apache/

検証の構成

東京リージョンに VPC(10.20.0.0/16、以下 VPC-A)とプライベートサブネットを1つ作りました。構成は次のとおりです。

VPC-A(IGW・NAT Gateway なし)のプライベートサブネットにある EC2 が、インターフェースエンドポイント(acm-acme-enroll)の TCP 443 に接続し、PrivateLink 経由で ACM の ACME エンドポイントに到達し、S3 ゲートウェイエンドポイント経由でパッケージを取得する構成図。作業端末は EC2 Instance Connect Endpoint 経由で SSH 接続する。

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 サービスとは異なります。

https://docs.aws.amazon.com/acm/latest/userguide/acm-acme.html

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 が非対応であることが書かれています。

https://docs.aws.amazon.com/acm/latest/userguide/acm-acme-privatelink.html

エンドポイントはプライベート 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 の EC2 がプライベートホストゾーンで名前解決し、VPC ピアリング経由で VPC-A のインターフェースエンドポイント(10.20.1.158)の TCP 443 に接続する構成図。エンドポイントから PrivateLink で ACM の ACME エンドポイントに到達する。

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件/月)で、発行時と自動更新のたびに課金されます。

https://aws.amazon.com/certificate-manager/pricing/

関連費用

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倍になります。エンドポイントの費用は発行の回数にかかわらず、配置している間は発生し続けます。

https://aws.amazon.com/privatelink/pricing/

別 VPC から使うためのプライベートホストゾーン(PHZ)は、1つあたり月 $0.50(最初の25個まで。26個目以降は $0.10)です。日割りはなく、作成時と翌月以降の毎月1日に課金されます。プライベートホストゾーンへのクエリは無料です。作成から12時間以内に削除したホストゾーンは課金されません。実際のオンプレミスから使う場合は、PHZ の関連付けだけでは名前解決できず、オンプレミス側から問い合わせを受ける仕組み(Route 53 VPC Resolver のインバウンドエンドポイントなど)が別に必要になり、その費用も加わります。

https://aws.amazon.com/route53/pricing/

まとめ

ACM の ACME 証明書発行を VPC のインターフェースエンドポイント経由で行えるようになり、IGW も NAT Gateway もない VPC の EC2 から証明書を発行できました。

今回の検証にかかった ACME 関連の費用は、計算すると1ドル強でした。この構成を継続して使う場合は、自動更新のたびに発生する証明書料金に加えて、エンドポイントを配置している間の時間課金(1 AZ で月約 $10、AZ ごとに増加)がかかります。オンプレミスから使う場合は、名前解決の仕組みの費用も別に必要です。

外部への接続経路を持てない環境でも PrivateLink 経由で ACME を利用できるようになりましたが、採用される場合は、ネットワークの実現性だけでなく、維持費についても事前に評価いただくことをおすすめします。

この記事をシェアする

AWSのお困り事はクラスメソッドへ

関連記事