外网https访问群晖

一台笔记本装了黑群晖,主要用来下电影看,运行快一年了,很稳定。最近域名快到期不想续费了,换用另一个域名,过程记录下,方便自己后面查看。

⚠️外网访问的话注意做好安全防范。

DSM版本:DSM 6.1.7-15284 Update 3。
家里宽带有外网ip,会变。

联通动态外网ip,通过阿里云域名做ddns。

第一步:在阿里云上新建子账号,用来编程控制动态解析域名

鼠标放到右上角用户上,选择【访问控制】-【人员管理】-【用户】-【创建用户】,登陆名称和现实名称 输入 ddns,访问方式:编程访问。

生成的用户信息里,复制 AccessKey ID 和 AccessKey Secret,编写脚本用。

为 ddns 用户配置权限:
授权范围:云账号全部资源;
选择权限:系统策略下的 AliyunDNSFullAccess(管理云解析(DNS)的权限);

第二步:编写脚本

这一步的脚本是从网上找到的,忘了是哪位大神的了。。。

在 /volume1/home/scripts 下新建 ali_ddns.sh 文件,脚本内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
#!/bin/sh

set -e

ApiId="AccessKey Id"
ApiKey="AccessKey Secret"
Domain="yourdomain.com"
SubDomain="nas"

if [ -z "$ApiId" -o -z "$ApiKey" -o -z "$Domain" ]; then
echo "参数缺失"
exit 1
fi

Nonce=$(date -u "+%N") # 有bug?
Timestamp=$(date -u "+%Y-%m-%dT%H%%3A%M%%3A%SZ") # SB 阿里云, 什么鬼时间格式
Nonce=$Timestamp

urlencode() {
local raw="$1";
local len="${#raw}"
local encoded=""

for i in `seq 1 $len`; do
local j=$((i+1))
local c=$(echo $raw | cut -c$i-$i)

case $c in [a-zA-Z0-9.~_-]) ;;
*)
c=$(printf '%%%02X' "'$c") ;;
esac

encoded="$encoded$c"
done

echo $encoded
}

# $1 = query string
getSignature() {
local encodedQuery=$(urlencode $1)
local message="GET&%2F&$encodedQuery"
local sig=$(echo -n "$message" | openssl dgst -sha1 -hmac "$ApiKey&" -binary | openssl base64)
echo $(urlencode $sig)
}

sendRequest() {
local sig=$(getSignature $1)
local result=$(wget -qO- --no-check-certificate --content-on-error "https://alidns.aliyuncs.com?$1&Signature=$sig")
echo $result
}

getRecordId() {
echo "获取 $SubDomain.$Domain 的 IP..." >&2
local queryString="AccessKeyId=$ApiId&Action=DescribeSubDomainRecords&Format=JSON&SignatureMethod=HMAC-SHA1&SignatureNonce=$Nonce&SignatureVersion=1.0&SubDomain=$SubDomain.$Domain&Timestamp=$Timestamp&Type=A&Version=2015-01-09"
local result=$(sendRequest "$queryString")
local code=$(echo $result | sed 's/.*,"Code":"\([A-z]*\)",.*/\1/')
local recordId=$(echo $result | sed 's/.*,"RecordId":"\([0-9]*\)",.*/\1/')

if [ "$code" = "$result" ] && [ ! "$recordId" = "$result" ]; then
local ip=$(echo $result | sed 's/.*,"Value":"\([0-9\.]*\)",.*/\1/')

if [ "$ip" == "$NewIP" ]; then
echo "IP 无变化, 退出脚本..." >&2
echo "quit"
else
echo $recordId
fi
else
echo "null"
fi
}

# $1 = record ID, $2 = new IP
updateRecord() {
local queryString="AccessKeyId=$ApiId&Action=UpdateDomainRecord&DomainName=$Domain&Format=JSON&RR=$SubDomain&RecordId=$1&SignatureMethod=HMAC-SHA1&SignatureNonce=$Nonce&SignatureVersion=1.0&Timestamp=$Timestamp&Type=A&Value=$2&Version=2015-01-09"
local result=$(sendRequest $queryString)
local code=$(echo $result | sed 's/.*,"Code":"\([A-z]*\)",.*/\1/')

if [ "$code" = "$result" ]; then
echo "$SubDomain.$Domain 已指向 $NewIP." >&2
else
echo "更新失败." >&2
echo $result >&2
fi
}

# $1 = new IP
addRecord() {
local queryString="AccessKeyId=$ApiId&Action=AddDomainRecord&DomainName=$Domain&Format=JSON&RR=$SubDomain&SignatureMethod=HMAC-SHA1&SignatureNonce=$Nonce&SignatureVersion=1.0&Timestamp=$Timestamp&Type=A&Value=$1&Version=2015-01-09"
local result=$(sendRequest $queryString)
local code=$(echo $result | sed 's/.*,"Code":"\([A-z]*\)",.*/\1/')

if [ "$code" = "$result" ]; then
echo "$SubDomain.$Domain 已指向 $NewIP." >&2
else
echo "添加失败." >&2
echo $result >&2
fi
}

# Get new IP address
echo "获取当前 IP..."
NewIP=$(wget -qO- --no-check-certificate "http://members.3322.org/dyndns/getip")
echo "当前 IP 为 $NewIP."

# Get record ID of sub domain
recordId=$(getRecordId)

if [ ! "$recordId" = "quit" ]; then
if [ "$recordId" = "null" ]; then
echo "域名记录不存在, 添加 $SubDomain.$Domain$NewIP..."
addRecord $NewIP
else
echo "域名记录已存在, 更新 $SubDomain.$Domain$NewIP..."
updateRecord $recordId $NewIP
fi
fi
第三步:群晖里设置计划任务,每隔一小时执行一下 ali-ddns.sh

选择【控制面板】-【任务计划】-【新增】-【计划的任务】-【用户定义的脚本】:
【常规】和【计划】里根据自己需求填写和设置,我用的是每隔一小时执行一次。
【任务设置】:用户定义的脚本里填写脚本绝对路径:/volume1/homes/admin/scripts/ali_ddns.sh

配置好后运行一下,去阿里云查看下,已经添加了一条解析记录。

配置 Let’s Encrypt

大致流程步骤:通过计划任务一个月执行一次脚本,脚本会启动 docker 执行 acme.sh 通过 dns 模式 renew 证书,再通过脚本把证书复制到群晖的证书路径下。

注意:因为宽带默认屏蔽外网 80 端口,所以这里选择了 dns api integration 模式,该模式有限制,需要检查下 acme 是否支持域名服务商。详细见这里

第一步:配置 Docker 服务,申请证书

Docker 配置参考的这篇文章,在此基础上做了一些改动。

1、手动创建文件夹、启动脚本、配置文件

WX20210227-152302

auto-renew.sh 内容:

1
acme.sh --force --issue --dns dns_ali -d yourdomain.com -d *.yourdomain.com && acme.sh --force --installcert -d yourdomain.com -d *.yourdomain.com --certpath /acme.sh/pems/cert.pem --key-file /acme.sh/pems/privkey.pem --fullchain-file /acme.sh/pems/fullchain.pem --ca-file /acme.sh/pems/chain.pem

account.conf 内容:

1
2
3
4
5
6
7
8
export Ali_Key="AccessKey ID"
export Ali_Secret="AccessKey Secret"

AUTO_UPGRADE='1'
SAVED_Ali_Key='AccessKey ID'
SAVED_Ali_Secret='AccessKey Secret'
USER_PATH='/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin'

2、配置Docker服务
  • 注册标中搜索 acme.sh,选择 neilpang/acme.sh 并下载
  • 映像中启动容器

在高级设置中配置容器:
3400209572

卷:映射文件夹,选择 docker/acme,装载路径为 /acme.sh
2407762448

网络:勾选 使用与 Docker Host 相同的网络
3849262329

环境:Entrypoint 不变,添加命令 sh /acme.sh/auto-renew.sh。 sh 参数会把后面跟着的参数当成 shell 执行,这里是为了把证书导出到 docker/acme/pems 里,方便后面定时任务使用。

20210227154642

第二步:构建计划任务,每70天 renew 一下证书,并使用证书

在 /home/scripts 里创建脚本文件 renew_copy_certs.sh

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#!/bin/sh

current=`date +%s`
file_path="/volume1/docker/acme/pems/cert.pem"
last_modified=`stat -c "%Y" $file_path`

# 70 days (70*24*60*60)
if [ $(($current-$last_modified)) -lt 6048000 ]; then
exit 0;
fi

docker start neilpang-acme.sh

# 给予 acme 足够生成证书时间
sleep 5m

# 不同的系统对应的文件夹不一样,需要通过 ssh 查看下当前群晖自签名证书的路径
rsync -avzh /volume1/docker/acme/pems/ /usr/syno/etc/certificate/_archive/U17akK/
rsync -avzh /volume1/docker/acme/pems/ /usr/syno/etc/certificate/smbftpd/ftpd
rsync -avzh /volume1/docker/acme/pems/ /usr/syno/etc/certificate/system/default/

/usr/syno/etc/rc.sysv/nginx.sh reload

选择【控制面板】-【任务计划】-【新增】-【计划的任务】-【用户定义的脚本】:
【计划】群晖的计划任务只有每天、每年、每半年重复,所以只能选择每月重复执行。
【任务设置】:用户定义的脚本里填写脚本绝对路径:/volume1/homes/admin/scripts/renew_copy_certs.sh

手动执行一次,查看是否有问题,有问题的话检查下或者google找找解决方案。