ASUS 路由器联动更新 Synology 群晖证书

淮城一只猫 奇技淫巧
阅读量 0 评论量 0

前言

最近折腾群晖 Nas 部署的时候结合路由器遇到一些问题,就写下记录,方便以后踩坑回头再看。这次遇到的问题是自动化更新 SSL 证书,由于家中路由器性能过好承担一些任务,就包括了 SSL 证书更新,群晖那边共用路由器同一个域名,但系统需要 SSL 自定义域名证书,所以本文就是根据自身情况尽节约互联网资源来更新 SSL 证书。

环境

宽带:联通 200M ,支持 IPv6

主路由器:华硕路由器 RT-AX86U 信息如下:

  • KoolCenter 梅林官改:3004.388.8_2
  • 安装 DDNS-Go 用于自动更新 IP
  • 安装 Let's Encrypt 插件用于自动更新域名证书
  • 安装 魔法猫咪 用于畅通网络

群晖 NasSynology DS220+ 信息如下:

  • DSM 版本:DSM 7.2.2-72806 Update 1
  • RAID 类别:RAID0
  • 安装 Docker

光猫修改

宽带师傅安装宽带的时候,默认是路由模式,但这样对宽带速度牺牲很大,主要光猫设备性能不咋的,所以要从路由模式修改成桥接,然后用路由器拨号就行,正好尝试下是否有公网地址。

我家的光猫设备类型GPON/4+1(C 系统) 首先需要知道管理员超级密码,光猫后面账号是默认密码,并没什么用,根据我的型号直接进入光猫网关:

光猫后台:http://192.168.1.1/cu.html 
开启Telnet:http://192.168.1.1/telnet?enable=1&key=光猫MAC
关闭Telnet:http://192.168.1.1/telnet?enable=0&key=光猫MAC

直接开启 Telnet 后在终端输入:telnet 192.168.1.1 ,账号为 admin 密码是 Fh@你家光猫的MAC地址后六位

进来之后输入命令:

/var # load_cli factory
cli main init.....argc=2 /r/n
consoleFd = 3
----cli_main_begin 475 3
########cli sip init ok########
cannot   open   (fh-bin)confile
cl_run_memfile_config  unlocked success!

Config\factorydir# show admin_name 
Success! admin_name=CUAdmin
Config\factorydir# show admin_pwd
Success! admin_pwd=CUAdminPs6nAyME

这里面的密码还会随时变,所以需要进入的时候建议从头再查看,然后进入基本配置选项中的网络连接修改成桥接模式即可,这里就不再详细描述,这类教程网上一大把。

AX86U 路由器设置

设置好光猫后在路由器设置拨号账号,输入宽带和密码即可,然后在 IPv6 设置选择 Native 选项即可获取 IPv6 地址。去无线网络 - 专业设置关闭 无线传输公平性通用 Beamforming 选项可以解决家里智能设备离线的问题。去软件中心安装 Let's Encrypt 插件配置好相关配置后,在 USB 相关应用 新建一个 FTP 账号,这个账号用于给群晖自动更新证书用的,这里注意是专门新建一个文件夹,然后文件夹只授权 Read 权限,不需要给他读写权限以免账号被黑。

然后登录路由器生成 RSA 密钥对用于路由器免密登录群晖来执行脚本操作:

# 大多数环境下生成密钥是ssh-keygen,但在华硕路由器命令是dropbearkey
dropbearkey -t rsa -f id_rsa

生成好的密钥 id_rsa 文件放在 ~/.ssh 文件内,然后将 id_rsa.pub 内容复制到记录下来,到后面会用到。

路由器安装的是 Let's Encrypt 插件实际上是 acme.sh 实现的,这里就不关注细节,我找了下插件的位置,在路由器的 /koolshare/scripts/ 位置下面的 acme_config.sh ,查阅代码在更新证书任何情况下会走 install_cert 方法,所以在 install_cert 方法结束后执行更新证书到 FTP 公开地方然后再执行群晖的脚本即可:

install_cert(){
	# ... 更多内容就不展示了
  # 在最后添加下面代码
	# 复制证书并且执行群晖脚本
	cp /koolshare/acme/你的域名/你的域名.cer /mnt/WDDisk/iNas/cert.pem
	cp /koolshare/acme/你的域名/你的域名.key /mnt/WDDisk/iNas/key.pem
	cp /koolshare/acme/你的域名/fullchain.cer /mnt/WDDisk/iNas/fullchain.pem
	# 此处是群晖 ssh 免密登录执行脚本
	ssh -i ~/.ssh/id_rsa root@192.168.50.2 -p2233 '/root/scripts/my_script.sh'
}

Synology 群晖设置

群晖要打开控制面板 - 终端机和 SNMP - 启用 SSH,然后修改端口号,建议修改成千位端口号,打开终端登录群晖的 ssh

ssh 你的用户名@群晖ip -p 端口号
# 登录 root 账号
sudo -i
cd /root/.ssh
# 如果提示文件不存在那就新建 .ssh 文件夹
mkdir .ssh
cd .ssh
vi authorized_keys
# 将上面路由器复制的 id_ras.pub 文件内容复制进去
chmod 755 ~
chmod 600 ~/.ssh/authorized_keys

vim /etc/ssh/sshd_config
# 编辑 sshd_config 将前面 # 删除
PubkeyAuthentication yes
AuthorizedKeysFile .ssh/authorized_keys

然后在群晖 控制面板 -> 终端机和 SNMP 关闭再开启 SSH,登录路由器输入:

ssh -i ~/.ssh/id_rsa root@192.168.50.2 -p2233

是否直接能登录群晖,如果提示权限不足请检查下是否设置 authorized_keys 权限为 600 。登录成功回到群晖的 ssh 终端在 /root 新建一个文件:

mkdir scripts
vim my_script.sh

脚本内容编写如下:

#!/bin/bash

# 定义FTP相关变量
FTP_SERVER="路由器 ftp 地址包括端口号:192.168.50.1"
FTP_USER="路由器 ftp 账户名"
FTP_PASSWORD="路由器 ftp 密码"
FTP_DIR="路由器 ftp 目录,例如我的是:/WDDisk/iNas/"

# 定义本地相关变量
LOCAL_DIR="."

# 定义证书相关变量
CERT_FILE="cert.pem"
KEY_FILE="key.pem"
PRIVKEY_FILE="privkey.pem"
FULLCHAIN_FILE="fullchain.pem"

# 定义目标目录相关变量
CERT_DIRS=(
    "/volume1/docker/AList/ssl/"
    "/usr/syno/etc/certificate/system/default/"
    "/usr/syno/etc/certificate/_archive/$(cat /usr/syno/etc/certificate/_archive/DEFAULT | tr -d '\n')/"
)

# 定义日志文件路径
LOG_FILE="$HOME/scripts/script_log.txt"

# 日志记录函数
log_message() {
    local level=$1
    local message=$2
    local line_number=${3:-"N/A"}
    local timestamp=$(date +"%Y-%m-%d %H:%M:%S")
    echo "[$timestamp] [LINE:$line_number] [$level] $message" >> "$LOG_FILE"
}

# 错误处理函数
handle_error() {
    local message=$1
    local exit_code=${2:-1}
    log_message "ERROR" "$message" "${BASH_LINENO[0]}"
    exit "$exit_code"
}

# 检查脚本运行环境
check_environment() {
    command -v wget >/dev/null 2>&1 || handle_error "wget 命令未安装,请安装后重试。" 2
    command -v openssl >/dev/null 2>&1 || handle_error "openssl 命令未安装,请安装后重试。" 2
    command -v systemctl >/dev/null 2>&1 || handle_error "systemctl 命令未安装,请检查环境。" 2
}

# 下载证书文件函数
download_file() {
    local file=$1
    log_message "INFO" "开始下载文件 $file ..." "${BASH_LINENO[0]}"
    wget -t 3 -P "$LOCAL_DIR" "ftp://$FTP_USER:$FTP_PASSWORD@$FTP_SERVER$FTP_DIR$file"
    if [ $? -ne 0 ]; then
        handle_error "文件 $file 下载失败,请检查网络连接或FTP服务器设置。" 3
    fi
}

# 转换密钥格式
convert_key_format() {
    log_message "INFO" "开始转换密钥格式..." "${BASH_LINENO[0]}"
    openssl pkcs8 -topk8 -inform PEM -in "$LOCAL_DIR/$KEY_FILE" -outform PEM -nocrypt -out "$LOCAL_DIR/$PRIVKEY_FILE"
    if [ ! -s "$LOCAL_DIR/$PRIVKEY_FILE" ]; then
        handle_error "密钥格式转换失败,请检查 openssl 配置及相关文件。" 4
    fi
}

# 复制文件到目标目录
copy_and_check() {
    local source_file=$1
    local target_dir=$2
    log_message "INFO" "复制文件 $source_file 到目录 $target_dir ..." "${BASH_LINENO[0]}"
    cp -f "$source_file" "$target_dir"
    if [ $? -ne 0 ]; then
        handle_error "文件 $source_file 复制到 $target_dir 失败,请检查权限或磁盘空间。" 5
    fi
}

# 重启服务
restart_service() {
    local service=$1
    log_message "INFO" "重启服务 $service ..." "${BASH_LINENO[0]}"
    systemctl reload "$service"
    if [ $? -ne 0 ]; then
        handle_error "$service 服务重载失败,请检查配置。" 6
    fi
    systemctl restart "$service"
    if [ $? -ne 0 ]; then
        handle_error "$service 服务重启失败,请检查配置。" 7
    fi
}

# 清理临时文件
cleanup_files() {
    local files=("$@")
    log_message "INFO" "开始清理临时文件..." "${BASH_LINENO[0]}"
    for file in "${files[@]}"; do
        rm -f "$file"
    done
    log_message "INFO" "临时文件清理完成。" "${BASH_LINENO[0]}"
}

# 主程序
main() {
    check_environment

    # 下载文件
    download_file "$CERT_FILE"
    download_file "$KEY_FILE"
    download_file "$FULLCHAIN_FILE"

    # 转换密钥格式
    convert_key_format

    # 复制文件到目标目录
    for cert_dir in "${CERT_DIRS[@]}"; do
        copy_and_check "$LOCAL_DIR/$CERT_FILE" "$cert_dir"
        copy_and_check "$LOCAL_DIR/$PRIVKEY_FILE" "$cert_dir"
        copy_and_check "$LOCAL_DIR/$FULLCHAIN_FILE" "$cert_dir"
    done

    # 重启服务
    restart_service "nginx"

    # 清理临时文件
    cleanup_files "$LOCAL_DIR/$CERT_FILE" "$LOCAL_DIR/$KEY_FILE" "$LOCAL_DIR/$PRIVKEY_FILE" "$LOCAL_DIR/$FULLCHAIN_FILE"

    log_message "INFO" "脚本执行完成!" "${BASH_LINENO[0]}"
}

# 执行主程序
main "$@"

编写好的脚本再授权:chmod +x ./my_script.sh,脚本内容主要内容就是从路由器的 FTP 读取 pem 文件然后下载群晖,如果不确定 FTP 是否能用可以用命令测试下:curl ftp://账号:密码@192.168.50.1/WDDisk/iNas/cert.pem 如果有返回内容那就没问题的,群晖下载证书后,还需要将原先的 私钥 转换成 PKCS#8 的格式密钥,然后再将密钥分别复制到几个地方,除了群晖自带的 ssl 默认证书,还涉及到 AListssl 证书。如果路由器执行定时脚本就会关联执行到群晖的脚本,就不需要操心手动更新这么多证书了。

结束

脚本为什么不用 curl 是因为群晖的 curl 不支付 ftp 协议:

curl -u "iNas:123456" ftp://192.168.50.1/WDDisk/iNas/key.pem
curl: (1) Protocol "ftp" not supported or disabled in libcurl

所以没办法改用 wget 来下载证书。

另外值得注意是 控制面板 - 安全性 - 证书 要先上传自己域名的证书,作为初始化,方便后续脚本自动更新证书。

喵~