整体步骤
- 利用certbot创建/续期证书
- 利用金山云API接口更新证书
环境准备
- Python≥3.7
- snap安装管理certbot以及插件
安装certbot以及插件
sudo snap install --classic certbot
sudo snap install certbot-dns-cloudflare
安装ksyun的SDK
pip install --upgrade kingsoftcloud-sdk-python
创建证书
sudo certbot certonly --key-type rsa --rsa-key-size 2048 --dns-cloudflare --dns-cloudflare-credentials /etc/letsencrypt/cloudflare.int --dns-cloudflare-propagation-seconds 60 -d huzi-baozi.com
主要是--dns-cloudflare 插件。
因为cloudflare代理、80端口被占、SLB没开80,所以用DNS验证的方式。
证书续期
sudo certbot renew
有三个钩子参数
--pre-hook:更新证书之前调用
--post-hook:更新证书之后调用
--deploy-hook:成功更新证书之后调用
配置文件位于
/etc/letsencrypt/renewal/huzi-baozi.com.conf
金山云更新证书
特别注意,来来回回调用都过不了,因为要把cert.pem和privkey.pem的换行都改为\n
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import os
import sys
import json
from ksyun.common import credential
from ksyun.common.profile.client_profile import ClientProfile
from ksyun.common.profile.http_profile import HttpProfile
from ksyun.common.exception.ksyun_sdk_exception import KsyunSDKException
# 导入 KCM 客户端和模型
from ksyun.client.kcm.v20160304 import client as kcm_client, models as kcm_models
# ================= 配置区域 =================
# 1. 金山云 API 凭证 (推荐使用环境变量)
# 如果环境变量不存在,将使用默认值,请替换为您的实际密钥
KSYUN_ACCESS_KEY = os.environ.get("KSYUN_SECRET_ID", "默认")
KSYUN_SECRET_KEY = os.environ.get("KSYUN_SECRET_KEY", "默认")
REGION = 'cn-beijing-6' # KCM 服务没有地域限制,但 SDK 需要指定一个
# 2. 目标证书 ID (请填入您 KCM 中需要更新的证书 ID)
# 脚本将直接覆盖这个 ID 下的证书内容
CERTIFICATE_ID = '你的证书ID'
# 3. Certbot 证书路径
# 请修改 DOMAIN_NAME 为您的域名目录名
DOMAIN_NAME = '你的域名'
CERT_PATH = f'/etc/letsencrypt/live/{DOMAIN_NAME}/cert.pem'
KEY_PATH = f'/etc/letsencrypt/live/{DOMAIN_NAME}/privkey.pem'
# ===========================================
def read_file_content(file_path):
"""读取文件内容"""
if not os.path.exists(file_path):
print(f"Error: 文件不存在: {file_path}")
sys.exit(1)
try:
with open(file_path, 'r') as f:
return f.read()
except Exception as e:
print(f"Error: 无法读取文件 {file_path}. 错误: {e}")
sys.exit(1)
def get_kcm_client():
"""初始化 KCM 客户端"""
if KSYUN_ACCESS_KEY == "KSYUN_SECRET_ID_HERE" or KSYUN_SECRET_KEY == "KSYUN_SECRET_KEY_HERE":
print("Error: 请在脚本中或环境变量中配置正确的 KSYUN_SECRET_ID 和 KSYUN_SECRET_KEY。")
sys.exit(1)
cred = credential.Credential(KSYUN_ACCESS_KEY, KSYUN_SECRET_KEY)
httpProfile = HttpProfile()
httpProfile.endpoint = "kcm.api.ksyun.com" # KCM API 端点
httpProfile.reqMethod = "POST"
httpProfile.reqTimeout = 60
httpProfile.scheme = "http" # 保持与官方案例一致
clientProfile = ClientProfile()
clientProfile.httpProfile = httpProfile
# KCMClient 使用 kcm_client.KcmClient
return kcm_client.KcmClient(cred, REGION, profile=clientProfile)
def modify_certificate_content(client, cert_id, cert_content, key_content):
"""调用 ModifyCertificate 接口直接更新 KCM 证书内容"""
print(f"正在更新 KCM 证书 ID: {cert_id} ...")
# 构造请求参数字典
params = {
"CertificateId": cert_id,
"PublicKey": cert_content.replace('\n', '\\n'),
"PrivateKey": key_content.replace('\n', '\\n')
}
try:
# 使用 call 方法执行 API 调用
resp = client.call("ModifyCertificate", params)
resp_json = json.loads(resp)
# KCM ModifyCertificate 接口返回为空,成功即表示更新完成
print(f"成功: KCM 证书 {cert_id} 内容已更新。所有关联的负载均衡或服务将自动使用新证书。")
except KsyunSDKException as err:
print(f"更新 KCM 证书失败: {err}")
# 如果报错,检查 KCM 接口文档确认参数名是否为 PublicKey 或 CertificateContent
sys.exit(1)
def main():
# 1. 读取本地最新证书
print(f"读取本地证书: {DOMAIN_NAME}")
cert_content = read_file_content(CERT_PATH)
key_content = read_file_content(KEY_PATH)
#print(cert_content )
# 2. 初始化客户端
client = get_kcm_client()
# 3. 执行更新
modify_certificate_content(client, CERTIFICATE_ID, cert_content, key_content)
if __name__ == "__main__":
main()
定时任务
可以写一个cron任务,certbot renew --deploy-hook来更新金山云证书,然后发送消息之类的。