ご挨拶
こんにちは、白"雪姫"です。
皆様、セキュリティ対策、効率よくは進んでますか?
前回の記事でACM証明書をエクスポートしてオンプレミスのnginxに設置するをやりましたが、あの記事の最後に書いた「自動更新されないため、有効期限の管理が必要」という課題。
これを解決するツールが AWS Workload Credentials Provider(WCP) です。
v3.0.0で安定してきたので実際に導入してみました。
前回のおさらい
前回の記事では、ACMからエクスポートした証明書をnginxに手動で配置しました。
ACMからエクスポートした証明書は、ACMの自動更新の恩恵を受けられません。有効期限前に手動でエクスポート→再配置が必要です。
この「手動でエクスポート→再配置」をどうにか自動化したい。そこで出てくるのがWCPです。
AWS Workload Credentials Provider とは
ACM証明書のエクスポートとローカルへの配置を自動化する軽量クライアントです。
| 項目 | 内容 |
|---|---|
| 機能 | ACM証明書の定期エクスポート+ファイル配置の自動化 |
| 対応OS | Linux (RPM/Deb), Windows, コンテナ |
| 対応Web | Apache / NGINX |
| 対象ホスト | EC2 および オンプレミス |
| 最新版 | v3.0.0 |
| 動作形態 | systemdサービスとして常駐 |
証明書の有効期限が近づくとACMが自動更新 → WCPが検知して再エクスポート → ファイルを差し替え → Webサーバをリロード、という流れを全自動でやってくれます。
今回の環境
| 項目 | 値 |
|---|---|
| 対象サーバ | AlmaLinux 9.7 (オンプレミス) |
| Webサーバ | nginx 1.20.1 |
| 証明書 |
*.lovelive-presents.com (ACM発行, us-east-1) |
| 有効期限 | 2026-12-15 |
| AWS CLI | 2.16.4 |
導入手順
1. IAMの準備(最小権限・構成タイプ③)
WCP用に最小権限のIAM構成を組みます。ルートキーの利用は絶対にやめましょう。
| リソース | 役割 | 権限 |
|---|---|---|
IAMユーザ wcp-cert-export-base
|
土台ユーザ |
sts:AssumeRole のみ |
IAMロール wcp-cert-export-role
|
実行ロール |
acm:ExportCertificate, acm:DescribeCertificate (対象ARN限定) |
信頼関係はロールが土台ユーザのみを信頼する形にします。
前回の記事から引き続きIAM Roles Anywhereを利用されている方は、そちらの認証基盤を流用するのもありです。
2. 対象証明書の確認
エクスポート可能な証明書である必要があります。
aws acm describe-certificate \
--region us-east-1 \
--certificate-arn "arn:aws:acm:us-east-1:<ACCOUNT_ID>:certificate/<CERT_ID>" \
--query 'Certificate.Options.CertificateTransparencyLoggingPreference'
list-certificatesでKeyTypesにエクスポート可能と表示されない場合は、describe-certificateで確認してください。エクスポート不可の場合は再発行が必要です。
3. WCPのビルドとインストール
v3.0.0はビルド済みバイナリが配布されていなかったため、サーバ上でRustビルドしました。
# Rustのインストール(sudo不要)
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
# ビルド(gcc, openssl-develは既存)
cargo build --release
インストールはconfig.tomlを作成してから実行します。
sudo ./install --config ~/wcp-config.toml --no-start
これにより以下が生成されます:
-
aws-wcpユーザ - systemd ユニット(acm, sm, token の3つ)
- sudoers 設定
- 証明書パスのdrop-in設定
今回はACM証明書の管理のみなので、sm(Secrets Manager)とtoken は disable にしました。
4. config.toml の設定
[acm]
certificate_arn = "arn:aws:acm:us-east-1:<ACCOUNT_ID>:certificate/<CERT_ID>"
role_arn = "arn:aws:iam::<ACCOUNT_ID>:role/wcp-cert-export-role"
region = "us-east-1"
certificate_path = "/etc/nginx/ssl/acm/fullchain.pem"
private_key_path = "/etc/nginx/ssl/acm/privkey.pem"
認証情報は ProtectHome=true 対策のため、/opt/aws/workload-credentials-provider/.aws/ に配置し、aws-wcp所有・パーミッション600としています。
5. 遭遇した問題:nginx reloadが失敗する
ここが今回一番ハマったポイントです。
問題
config.tomlの refresh_command に nginx -s reload を設定したところ、exit 1 で失敗。
原因
WCPの acm.service は PrivateDevices=yes でハードニングされています。
これにより実効的に NoNewPrivileges が強制ONとなり、setuidな sudo -n が動作しません。
no new privileges flag is set
systemctl showでは NoNewPrivileges=no と表示されるのですが、PrivateDevices=yes が暗黙的にONにしてしまう罠です。
解決策:権限分離アーキテクチャ
acm.serviceのサンドボックスを一切弱めず、reloadを別の特権ユニットに分離しました。
acm.service (aws-wcp, 完全サンドボックス)
└ 定期的に ACM から証明書をエクスポート
内容に変更があれば atomic rename で
/etc/nginx/ssl/acm/{fullchain.pem, privkey.pem} を更新
│ (ファイル変更を inotify 検知)
▼
wcp-nginx-reload.path → wcp-nginx-reload.service (root)
└ nginx -t && nginx -s reload
wcp-nginx-reload.path
[Unit]
Description=Watch ACM certificate for changes
[Path]
PathChanged=/etc/nginx/ssl/acm/fullchain.pem
[Install]
WantedBy=multi-user.target
wcp-nginx-reload.service
[Unit]
Description=Reload nginx after ACM certificate update
[Service]
Type=oneshot
ExecStartPre=/usr/sbin/nginx -t
ExecStart=/usr/sbin/nginx -s reload
これにより:
- acm.serviceのハードニング(ProtectSystem/ProtectHome/PrivateTmp/PrivateDevices/最小cap)は維持
- nginx reloadはroot権限で安全に実行
- config.tomlからは
refresh_commandを削除(デーモンは証明書をファイルに書くだけ)
acm.serviceで PrivateDevices=no / NoNewPrivileges=no に緩和する方法もありますが、セキュリティ後退のため不採用としました。
6. 動作確認
最終的な構成図
┌─────────────────────────────────────────────────┐
│ ACM (us-east-1) │
│ *.lovelive-presents.com / Export=ENABLED │
└──────────────────────┬──────────────────────────┘
│ (定期エクスポート)
▼
┌─────────────────────────────────────────────────┐
│ acm.service (aws-wcp, サンドボックス) │
│ AssumeRole → ExportCertificate │
│ → atomic rename で証明書ファイル更新 │
└──────────────────────┬──────────────────────────┘
│ (inotify: PathChanged)
▼
┌─────────────────────────────────────────────────┐
│ wcp-nginx-reload.path → .service (root) │
│ nginx -t && nginx -s reload │
└──────────────────────┬──────────────────────────┘
│
▼
┌─────────────────────────────────────────────────┐
│ nginx (TLS稼働) │
│ /etc/nginx/ssl/acm/{fullchain,privkey}.pem │
└─────────────────────────────────────────────────┘
確認コマンド
# acm.serviceの状態確認
sudo systemctl status acm.service
# → active / "Certificate refresh successful"
# path監視の状態確認
sudo systemctl status wcp-nginx-reload.path
# → active (fullchain.pem 監視中)
# reloadの手動テスト
sudo systemctl start wcp-nginx-reload.service
# → Result=success
# TLS証明書の確認
echo | openssl s_client -connect mail.lovelive-presents.com:443 \
-servername mail.lovelive-presents.com 2>/dev/null | \
openssl x509 -noout -issuer -dates
セキュリティ上の考慮
| 項目 | 対応 |
|---|---|
| 認証情報 | 専用IAMユーザ、最小権限、AssumeRoleで分離 |
| デーモンの権限 | aws-wcpユーザ、ProtectHome/PrivateDevices等で完全サンドボックス化 |
| 秘密鍵 | /opt配下、600、aws-wcp所有。ステージング用コピーはshredで削除済み |
| nginx reload | 権限分離ユニット(root)で実行。デーモン側のセキュリティ緩和なし |
| SSRF対策 | /var/run/awssmatoken + X-Aws-...-Token ヘッダ必須 |
まとめ
前回の記事で課題として残していた「ACMエクスポート証明書の手動更新問題」を、WCP v3.0.0で自動化できました。
ポイントは:
- WCPが証明書のエクスポートとファイル配置を自動でやってくれる
-
PrivateDevices=yes環境ではsudoが使えない罠がある → pathユニットで権限分離が正解 - セキュリティを緩めない設計が大事
これで証明書の有効期限を気にせず運用できるようになりました。
半年に一回の手動作業から解放されるのは大きいです。
では。