Aws的EC2服务器用lego获得免费证书并更新到ACM

交代一下背景:

公司在AWS上用的是EKS+Fargate,还有一个跳板机,证书呢就不太想用aws收费的了,干脆用免费的证书好了。

那问题就来了,Let‘s encrypt的证书90天到期,已经手动更新过3次了,实在太麻烦了,能不能自动申请并更新到ACM呢?

答案是可以的,原理是:利用EC2可以携带IAM角色的原理,就可以无凭证更新免费证书到AWS Certificate Manager了

做法如下:

一、首先去ACM中拿到证书的ARN,要确保IAM权限不外泄,避免更新错证书

image-20250725144718592

二、生成一个IAM的policy,命名为AllowUpdateCertificate,只允许更新这两个特定的证书

具体策略内容如下:

 1{
 2	"Version": "2012-10-17",
 3	"Statement": [
 4		{
 5			"Effect": "Allow",
 6			"Action": [
 7				"acm:ImportCertificate"
 8			],
 9			"Resource": [
10				"arn:aws:acm:ap-southeast-1:111111111111:certificate/arn01",
11				"arn:aws:acm:ap-southeast-1:111111111111:certificate/arn02"
12			]
13		}
14	]
15}

image-20250725145030921

三、IAM生成一个role角色,命名为Ec2UpdateCertificateRole,把AllowUpdateCertificate策略给附上

image-20250725145132762

四、到EC2的实例详细信息中,操作–>安全–>修改IAM角色

image-20250725145419632

附上Ec2UpdateCertificateRole这个角色

image-20250725145533303

五、在EC2的机器上,装好AWS CLI,还有lego(申请Let’s encrypt证书的软件)

先写好获得证书的脚本 get_cert.sh,域名DNS解析是托管到cloudflare的,所以也直接用API TOKEN来处理

1#!/bin/bash
2
3CLOUDFLARE_DNS_API_TOKEN=######## /usr/local/bin/lego --path /usr/local/bin/certs --email zhangranrui@rendoumi.com --dns cloudflare --domains *.rendoumi.com --domains rendoumi.com renew --renew-hook="/usr/local/bin/update_aws_cert.sh"

参数--renew-hook如果有新证书,就调用后面的脚本。如果没有新证书,就无操作,避免无用功。

然后再写好 update_aws_cert.sh,这就是个千字文了,由gemini生成的:

由于lego生成的crt是证书+证书链,所以第一步要把证书部分给单独拆出来

 1#!/bin/bash
 2
 3cd /usr/local/bin/certs/certificates
 4
 5#首先把证书单独拆出来
 6awk '/-----BEGIN CERTIFICATE-----/,/-----END CERTIFICATE-----/ {print} /-----END CERTIFICATE-----/ {exit}' _.rendoumi.com.crt > _.rendoumi.com.only
 7
 8# --- 配置部分 ---
 9# 您要更新的现有 ACM 证书的 ARN
10# 示例:CERTIFICATE_ARN="arn:aws:acm:ap-southeast-1:123456789012:certificate/abcdefab-1234-5678-abcd-1234567890ab"
11CERTIFICATE_ARN="arn:aws:acm:ap-southeast-1:111111111111:certificate/11111111-1111-1111-1111-111111111111"
12
13# 新证书文件的路径(例如:/opt/certs/new_certificate.crt)
14# 确保这个路径在您的EC2实例上是正确的
15CERTIFICATE_BODY_PATH="_.rendoumi.com.only"
16
17# 新私钥文件的路径(例如:/opt/certs/private.key)
18# 确保这个路径在您的EC2实例上是正确的
19PRIVATE_KEY_PATH="_.rendoumi.com.key"
20
21# 证书链文件的路径(可选)
22# 如果没有证书链,请将此变量留空或注释掉
23# 示例:CERTIFICATE_CHAIN_PATH="/opt/certs/ca_bundle.crt"
24CERTIFICATE_CHAIN_PATH="_.rendoumi.com.issuer.crt" # 如果没有链,请将其留空 ""
25
26# ACM 证书所在的 AWS 区域
27# 这个区域应该与 CERTIFICATE_ARN 中的区域匹配
28# 示例:AWS_REGION="ap-southeast-1"
29AWS_REGION="ap-southeast-1"
30# --- 配置部分结束 ---
31
32echo "--- 开始使用实例角色重新导入 ACM 证书 ---"
33echo "证书 ARN: ${CERTIFICATE_ARN}"
34echo "区域: ${AWS_REGION}"
35
36# 检查文件是否存在
37if [ ! -f "${CERTIFICATE_BODY_PATH}" ]; then
38    echo "错误:证书文件 '${CERTIFICATE_BODY_PATH}' 不存在。"
39    exit 1
40fi
41
42if [ ! -f "${PRIVATE_KEY_PATH}" ]; then
43    echo "错误:私钥文件 '${PRIVATE_KEY_PATH}' 不存在。"
44    exit 1
45fi
46
47if [ -n "${CERTIFICATE_CHAIN_PATH}" ] && [ ! -f "${CERTIFICATE_CHAIN_PATH}" ]; then
48    echo "警告:证书链文件 '${CERTIFICATE_CHAIN_PATH}' 已指定但不存在。将不包含证书链进行导入。"
49    CERTIFICATE_CHAIN_PATH="" # 如果文件不存在,则清空路径
50fi
51
52# 构造 AWS CLI 命令。AWS CLI 会自动使用实例角色提供的凭证。
53IMPORT_CMD="aws acm import-certificate \
54  --certificate-arn ${CERTIFICATE_ARN} \
55  --certificate fileb://${CERTIFICATE_BODY_PATH} \
56  --private-key fileb://${PRIVATE_KEY_PATH} \
57  --region \"${AWS_REGION}\""
58
59# 如果有证书链,则添加到命令中
60if [ -n "${CERTIFICATE_CHAIN_PATH}" ]; then
61    IMPORT_CMD="${IMPORT_CMD} --certificate-chain fileb://${CERTIFICATE_CHAIN_PATH}"
62fi
63
64echo "正在执行命令:"
65echo "$IMPORT_CMD"
66echo "--------------------------"
67
68# 执行命令
69# 注意:这里我们不指定 --profile 或任何凭证,AWS CLI 会自动使用实例IAM角色。
70eval $IMPORT_CMD
71
72# 检查命令执行结果
73if [ $? -eq 0 ]; then
74    echo "--- 证书重新导入成功!---"
75    echo "ACM 证书 ${CERTIFICATE_ARN} 已更新。"
76else
77    echo "--- 证书重新导入失败!---"
78    echo "请检查错误消息,确保:"
79    echo "1. EC2 实例已正确附加了具有 acm:ImportCertificate 权限的 IAM 角色。"
80    echo "2. 证书、私钥和链文件路径正确且可读。"
81    echo "3. 证书/私钥/链的格式正确。"
82fi
83
84echo "--- 脚本执行完毕 ---"

看起来真的是又臭又长,废话连篇。注意:gemini 生成的代码有问题,aws acm import-certificate 的参数中,证书文件的前缀是fileb://,gemini给的是file://,调试了半天,最后去aws看了文档才发现。

如上操作,脚本放入到crontb中,没有用到任何aws access id和secret,就能正确更新证书,善莫大焉。


Redpanda到期后如何续License
用Docker部署kafka且不依赖zookeeper
comments powered by Disqus