飞牛fnOS使用DNS验证方式,用acme自动签发SSL证书
飞牛fnOS在使用DDNS时,需要签发自己域名的SSL证书。但目前并没有该功能。
论坛里有方案:飞牛fnos使用acme.sh自动部署并自动更新ssl证书【脚本更新】 - 攻略分享 飞牛私有云论坛 fnOS,原作者为:https://blog.csdn.net/jummewu
原脚本只能签发RSA证书,因此做了点调整改动,使其能够签发ECC证书。
#!/bin/bash set -euo pipefail # ============================== # 🛠️ 配置区(请按需修改) # ============================== DOMAIN="domain.com" DNS_PROVIDER="dns_dp" # DNSPod DP_Id="123456" # ← 替换为你的 DNSPod ID DP_Key="1111111" # ← 替换为你的 DNSPod Token # 证书服务商:letsencrypt 或 zerossl CERT_SERVER="letsencrypt" # --- ZeroSSL 专用配置(当 CERT_SERVER=zerossl 时必填)--- # 获取 EAB 凭据:https://app.zerossl.com/developer # Email="admin@cnraft.com" # EAB_kid="xxxxx" # EAB_hmac_key="xxxxx" DNS_SLEEP=120 SSLS_DIR="/usr/trim/var/trim_connect/ssls" CONF_FILE="/usr/trim/etc/network_gateway_cert.conf" RELOAD_CMD="systemctl restart webdav.service smbftpd.service trim_nginx.service" # ============================== # 🧰 工具函数 # ============================== log() { echo -e "\033[1;34m[INFO]\033[0m $*"; } warn() { echo -e "\033[1;33m[WARN]\033[0m $*" >&2; } err() { echo -e "\033[1;31m[ERROR]\033[0m $*" >&2; exit 1; } check_cmd() { command -v "$1" &>/dev/null || err "Command '$1' not found." } install_jq() { if command -v jq &>/dev/null; then return; fi if command -v apt-get &>/dev/null; then apt-get update && apt-get install -y jq || err "apt install jq failed" elif command -v apk &>/dev/null; then apk add --no-cache jq || err "apk add jq failed" else err "Unsupported OS: cannot auto-install jq." fi } # ============================== # 🚀 主流程 # ============================== START_TT=$(date +%s%3N) # --- 检查依赖 --- check_cmd date psql openssl install_jq ACME_SH=$(command -v acme.sh 2>/dev/null || echo "/root/.acme.sh/acme.sh") [[ -x "$ACME_SH" ]] || err "acme.sh not found." export DP_Id DP_Key [[ -z "$DP_Id" ]] && err "DP_Id is empty!" [[ -z "$DP_Key" ]] && err "DP_Key is empty!" # --- 申请证书 --- log "Issuing ECC certificate for $DOMAIN and *.$DOMAIN via $CERT_SERVER..." case "$CERT_SERVER" in letsencrypt) "$ACME_SH" --force --log \ --issue \ --dns "$DNS_PROVIDER" --dnssleep "$DNS_SLEEP" \ -d "$DOMAIN" -d "*.$DOMAIN" \ --ecc --keylength ec-256 ;; zerossl) [[ -z "${Email:-}" || -z "${EAB_kid:-}" || -z "${EAB_hmac_key:-}" ]] && \ err "ZeroSSL requires Email, EAB_kid, EAB_hmac_key" export Email EAB_kid EAB_hmac_key "$ACME_SH" --register-account -m "$Email" --server zerossl \ --eab-kid "$EAB_kid" --eab-hmac-key "$EAB_hmac_key" || true "$ACME_SH" --force --log \ --issue \ --dns "$DNS_PROVIDER" --dnssleep "$DNS_SLEEP" \ -d "$DOMAIN" -d "*.$DOMAIN" \ --ecc --keylength ec-256 ;; *) err "Unsupported CERT_SERVER: $CERT_SERVER";; esac # --- 获取证书元数据(使用 Le_ 前缀字段)--- CERT_INFO_RAW="$("$ACME_SH" --info -d "$DOMAIN" --ecc)" Le_CertCreateTimeStr=$(echo "$CERT_INFO_RAW" | grep '^Le_CertCreateTimeStr=' | cut -d= -f2) Le_NextRenewTimeStr=$(echo "$CERT_INFO_RAW" | grep '^Le_NextRenewTimeStr=' | cut -d= -f2) if [ -z "$Le_CertCreateTimeStr" ] || [ -z "$Le_NextRenewTimeStr" ]; then err "Missing Le_CertCreateTimeStr or Le_NextRenewTimeStr in acme.sh output." fi # UTC+8 中国时区 CERT_CREATE_TT=$(date -d "${Le_CertCreateTimeStr} 8 hours" +%s%3N) CERT_CREATE=$(date -d "${Le_CertCreateTimeStr} 8 hours" +%s) CERT_RENEW_TT=$(date -d "${Le_NextRenewTimeStr} 1 month 8 hours" +%s%3N) CERT_RENEW=$(date -d "${Le_NextRenewTimeStr} 1 month 8 hours" +%s) # --- 创建目录 & 安装证书 --- CERT_DIR="${SSLS_DIR}/${DOMAIN}/${CERT_CREATE}" mkdir -p "$CERT_DIR" CERT_FILE="$CERT_DIR/${DOMAIN}.cer" KEY_FILE="$CERT_DIR/${DOMAIN}.key" FULLCHAIN_FILE="$CERT_DIR/fullchain.cer" CA_FILE="$CERT_DIR/issuer_certificate.crt" "$ACME_SH" --install-cert -d "$DOMAIN" --ecc \ --cert-file "$CERT_FILE" \ --key-file "$KEY_FILE" \ --fullchain-file "$FULLCHAIN_FILE" \ --ca-file "$CA_FILE" \ --reloadcmd "$RELOAD_CMD" chmod 755 "$KEY_FILE" "$CERT_FILE" "$FULLCHAIN_FILE" "$CA_FILE" # --- 提取证书信息 --- ISSUER=$(openssl x509 -in "$CERT_FILE" -noout -issuer | awk -F' = ' '{print $NF}' | sed 's/,.*$//' || echo "unknown") SIG_ALGO=$(openssl x509 -in "$CERT_FILE" -noout -text | awk '/Signature Algorithm:/{print $3; exit}' || echo "unknown") case "$SIG_ALGO" in *RSA*) ALGO_TYPE="RSA" ;; *ECDSA*|*ECC*) ALGO_TYPE="ECC" ;; *SM2*) ALGO_TYPE="SM2" ;; *) ALGO_TYPE="UNKNOWN" ;; esac # --- 更新 PostgreSQL 数据库 --- if psql -qtAX -U postgres -d trim_connect -c "SELECT 1 FROM cert WHERE domain = '$DOMAIN';" | grep -q 1; then psql -U postgres -d trim_connect -c " UPDATE cert SET valid_from = $CERT_CREATE_TT, valid_to = $CERT_RENEW_TT, encrypt_type = '$ALGO_TYPE', issued_by = '$ISSUER', last_renew_time = $START_TT, des = '由acme.sh自动生成的正式证书 ($CERT_SERVER)', private_key = '$KEY_FILE', certificate = '$CERT_FILE', issuer_certificate = '$CA_FILE', status = 'suc', updated_time = $START_TT WHERE domain = '$DOMAIN'; " >/dev/null else MAX_ID=$(psql -qtAX -U postgres -d trim_connect -c "SELECT COALESCE(MAX(id), 0) FROM cert;" 2>/dev/null) DOMAIN_ID=$((MAX_ID + 1)) psql -U postgres -d trim_connect -c " INSERT INTO cert ( id, domain, subject_alt_name, common_name, valid_from, valid_to, encrypt_type, issued_by, created_time, des, retry_count, error_msg, deploy_type, deploy_extra, private_key, certificate, issuer_certificate, status, last_renew_time, updated_time ) VALUES ( $DOMAIN_ID, '$DOMAIN', '*.$DOMAIN', '$DOMAIN', $CERT_CREATE_TT, $CERT_RENEW_TT, '$ALGO_TYPE', '$ISSUER', $START_TT, '由acme.sh自动生成的正式证书 ($CERT_SERVER)', 0, NULL, 'upload', NULL, '$KEY_FILE', '$CERT_FILE', '$CA_FILE', 'suc', $START_TT, $START_TT ); " >/dev/null fi # --- 更新 NGINX JSON 配置--- [[ -f "$CONF_FILE" ]] || { echo "[]" > "$CONF_FILE"; } NEW_ENTRY=$(jq -n \ --arg host "$DOMAIN" \ --arg cert "$FULLCHAIN_FILE" \ --arg key "$KEY_FILE" \ '{host: $host, cert: $cert, key: $key}' ) CURRENT=$(jq -c '.' "$CONF_FILE" 2>/dev/null || echo "[]") if echo "$CURRENT" | jq -e ".[] | select(.host == \"$DOMAIN\")" >/dev/null; then UPDATED=$(echo "$CURRENT" | jq --argjson new "$NEW_ENTRY" 'map(if .host == $new.host then $new else . end)') else UPDATED=$(echo "$CURRENT" | jq --argjson new "$NEW_ENTRY" '. += [$new]') fi cp "$CONF_FILE" "$CONF_FILE.$START_TT.bak" echo "$UPDATED" | jq -c '.' > "$CONF_FILE" if ! grep -F "$FULLCHAIN_FILE" "$CONF_FILE" >/dev/null; then err "NGINX config updated but cert path not found." fi # --- 重载服务 + 清理旧证书 --- $RELOAD_CMD || true find "${SSLS_DIR}/${DOMAIN}" -maxdepth 1 -type d -mtime +90 -exec rm -rf {} + 2>/dev/null || true find /usr/trim/etc/ -name "network_gateway_cert.conf.*.bak" -mtime +90 -delete 2>/dev/null || true # --- 生成摘要配置 --- CONF_SUMMARY="${SSLS_DIR}/${DOMAIN}/sslpath.conf" cat > "$CONF_SUMMARY" <<EOF DOMAIN=$DOMAIN CERT_CREATE_TT=$CERT_CREATE_TT CERT_RENEW_TT=$CERT_RENEW_TT ALGO_TYPE=$ALGO_TYPE CERT_ISSUED_BY=$ISSUER TT=$START_TT DOMAIN_SSL_DIR=$CERT_DIR CERT_SERVER=$CERT_SERVER STAGING=false EOF log "✅ Certificate deployed: $CERT_DIR" log "📌 Summary: $CONF_SUMMARY"
保存为deploy-cert.sh并给执行权限:
chmod +x deploy-cert.sh ./deploy-cert.sh
最后将其添加到计划任务中:
# 每月1日 2:30 执行 30 2 1 * * /path/to/deploy-cert.sh >> /var/log/acme-deploy.log 2>&1
浙公网安备 33010602011771号