飞牛fnOS使用DNS验证方式,用acme自动签发SSL证书

飞牛fnOS在使用DDNS时,需要签发自己域名的SSL证书。但目前并没有该功能。

论坛里有方案:飞牛fnos使用acme.sh自动部署并自动更新ssl证书【脚本更新】 - 攻略分享 飞牛私有云论坛 fnOS,原作者为:https://blog.csdn.net/jummewu

原脚本只能签发RSA证书,因此做了点调整改动,使其能够签发ECC证书。在fnOS 1.1.8版本下测试通过。

#!/bin/bash
set -euo pipefail

# ==============================
# 🛠️ 配置区
# ==============================
DOMAIN="yourdomain.com"
DNS_PROVIDER="dns_dp"
DP_Id="123456" # ← 替换为你的真实 ID
DP_Key="1111111111111" # ← 替换为你的真实 Key

# 证书服务商:letsencrypt,letsencrypt_test,zerossl,sslcom,google,googletest,actalis.这里暂时使用letsencrypt_test进行签发,调试没有问题了可以切换成letsencrypt
CERT_SERVER="letsencrypt_test"

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; }

# ==============================
# 🚀 主流程
# ==============================
START_TT=$(date +%s%3N)

# --- 1. 检查依赖 ---
command -v psql &>/dev/null || err "psql not found."
command -v jq &>/dev/null || err "jq not found."
ACME_SH=$(command -v acme.sh 2>/dev/null || echo "/root/.acme.sh/acme.sh")

export DP_Id DP_Key

# --- 2. 申请证书 ---
log "Issuing certificate via: $CERT_SERVER for $DOMAIN..."
"$ACME_SH" --force --issue --dns "$DNS_PROVIDER" --dnssleep "$DNS_SLEEP" \
--server "$CERT_SERVER" \
-d "$DOMAIN" -d "*.$DOMAIN" \
--ecc --keylength ec-256

# --- 3. 获取元数据并转换时间戳 ---
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)

# 转换毫秒级时间戳 (UTC+8)
CERT_CREATE_TT=$(date -d "${Le_CertCreateTimeStr} 8 hours" +%s%3N)
CERT_CREATE_SEC=$(date -d "${Le_CertCreateTimeStr} 8 hours" +%s)
CERT_RENEW_TT=$(date -d "${Le_NextRenewTimeStr} 1 month 8 hours" +%s%3N)

# --- 4. 准备物理路径并安装 ---
CERT_DIR="${SSLS_DIR}/${DOMAIN}/${CERT_CREATE_SEC}"
mkdir -p "$CERT_DIR"

KEY_FILE="$CERT_DIR/${DOMAIN}.key"
CERT_FILE="$CERT_DIR/${DOMAIN}.crt"
FULLCHAIN_FILE="$CERT_DIR/fullchain.crt"
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"

# 确保证书文件对 Nginx 可读
chmod 644 "$KEY_FILE" "$CERT_FILE" "$FULLCHAIN_FILE" "$CA_FILE"

# --- 5. 提取并转义 SQL 变量 ---
ISSUER_RAW=$(openssl x509 -in "$CERT_FILE" -noout -issuer | sed 's/.*CN = //;s/,.*//')
[[ "$CERT_SERVER" == "letsencrypt_test" ]] && ISSUER_RAW="Let's Encrypt Test (Fake)"
# 处理单引号转义,防止 SQL 报错
ISSUER_SQL=$(echo "$ISSUER_RAW" | sed "s/'/''/g")

ALGO_TYPE="ECDSA"
SAN_VAL="${DOMAIN},*.${DOMAIN}"
DES_SQL=$(echo "acme.sh自动生成的证书 (Server: $CERT_SERVER)" | sed "s/'/''/g")

# --- 6. 数据库同步 (19个字段全覆盖) ---
log "Syncing to PostgreSQL (is_default=1)..."

EXISTS=$(psql -qtAX -U postgres -d trim_connect -c "SELECT count(*) FROM cert WHERE domain = '$DOMAIN';")

if [ "$EXISTS" -gt 0 ]; then
psql -U postgres -d trim_connect <<EOF || err "SQL UPDATE failed"
UPDATE cert SET
san = '$SAN_VAL',
valid_from = $CERT_CREATE_TT,
valid_to = $CERT_RENEW_TT,
encrypt_type = '$ALGO_TYPE',
issued_by = '$ISSUER_SQL',
last_renew_time = $START_TT,
des = '$DES_SQL',
is_default = 1,
platform = '',
source = 'system',
cert_url = '',
private_key = '$KEY_FILE',
certificate = '$CERT_FILE',
issuer_certificate = '$CA_FILE',
status = 'suc',
updated_time = $START_TT
WHERE domain = '$DOMAIN';
EOF
else
MAX_ID=$(psql -qtAX -U postgres -d trim_connect -c "SELECT COALESCE(MAX(id), 0) FROM cert;")
NEW_ID=$((MAX_ID + 1))
psql -U postgres -d trim_connect <<EOF || err "SQL INSERT failed"
INSERT INTO cert (
id, domain, san, valid_from, valid_to,
encrypt_type, issued_by, last_renew_time, des,
is_default, platform, source, cert_url,
private_key, certificate, issuer_certificate,
status, created_time, updated_time
) VALUES (
$NEW_ID, '$DOMAIN', '$SAN_VAL', $CERT_CREATE_TT, $CERT_RENEW_TT,
'$ALGO_TYPE', '$ISSUER_SQL', $START_TT, '$DES_SQL',
1, '', 'system', '',
'$KEY_FILE', '$CERT_FILE', '$CA_FILE',
'suc', $START_TT, $START_TT
);
EOF
fi

# --- 7. 更新 Nginx 配置文件并强制修正权限 ---
log "Updating Nginx Gateway Config 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}'
)

# 使用临时变量存储 JSON,避免直接操作导致文件损坏
UPDATED_JSON=$(jq --argjson new "$NEW_ENTRY" '
if . == null or . == [] then [$new]
elif map(.host == $new.host) | any then map(if .host == $new.host then $new else . end)
else . + [$new] end' "$CONF_FILE")

# 使用 cat 重定向以保留原文件 inode/属性,并显式设置权限
echo "$UPDATED_JSON" > "$CONF_FILE"
chmod 644 "$CONF_FILE"

# --- 8. 重载服务 ---

log "Reloading services..."
$RELOAD_CMD || warn "Some services failed to restart."

# 清理超过 90 天的旧证书文件夹
find "${SSLS_DIR}/${DOMAIN}" -maxdepth 1 -type d -mtime +90 -exec rm -rf {} + 2>/dev/null || true

log "✅ All Done! Certificate for $DOMAIN is active with is_default=1."

 

保存为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

 

posted @ 2025-12-12 02:28  Mrcoool  阅读(353)  评论(0)    收藏  举报