0%

(填坑) Keybase Pages + Cloudflare 证书过期问题

用 Keybase Pages + Cloudflare 建软件仓库已经有一段时间了,之所以没写博客是因为当时太懒,
后来艾老师写了 利用 Keybase 搭建自己的軟件源
所以没有必要再水一篇。

所以有什么坑没填呢?

其实就是用 Cloudflare CDN 导致 Keybase Pages 签的 Letsencrypt 证书过期的问题,大概是因为 Cloudflare开了 Proxy 之后 DNS 记录就不再直接指向 kbp.keybaseapi.com ,而 Keybase Pages 使用的是 HTTP 认证的方式来签发 Letsencrypt 证书。 也就是说使用 Cloudflare Proxy 导致证书无法成功 renew 。

如何解决呢?

其实只要定期把 DNS 记录改为直连,等待 Keybase 完成证书 renew 之后再将 DNS 记录改回 Proxy 即可。但人工操作实在是有点麻烦,而且容易忘记这件事情,那就用 Cloudflare API 吧:

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
#!/bin/bash

DOMAIN_ZONE=""
AUTH_EMAIL=""
AUTH_KEY=""
CLOUDFLARE_API_URL="https://api.cloudflare.com/client/v4"

NAMES=("example.example.com")

declare -A RECORD_IDS

TTL=300

get_ids_by_name() {
for name in "${NAMES[@]}"; do
RECORD_IDS[$name]="$(
curl -X GET "$CLOUDFLARE_API_URL/zones/$DOMAIN_ZONE/dns_records?type=CNAME&name=$name" \
-H "X-Auth-Email: $AUTH_EMAIL" \
-H "X-Auth-Key: $AUTH_KEY" \
-H "Content-Type: application/json" |
jq ".result[].id" | sed 's,",,g'
)"
done
}

set_proxy_false() {
for name in "${NAMES[@]}"; do
curl -X PUT "$CLOUDFLARE_API_URL/zones/$DOMAIN_ZONE/dns_records/${RECORD_IDS[$name]}" \
-H "X-Auth-Email: $AUTH_EMAIL" \
-H "X-Auth-Key: $AUTH_KEY" \
-H "Content-Type: application/json" \
--data "$(
cat <<-EOF
{
"type": "CNAME",
"name": "$name",
"content": "kbp.keybaseapi.com",
"ttl": 1,
"proxied": false
}
EOF
)"
done
}

set_proxy_true() {
for name in "${NAMES[@]}"; do
curl -X PUT "$CLOUDFLARE_API_URL/zones/$DOMAIN_ZONE/dns_records/${RECORD_IDS[$name]}" \
-H "X-Auth-Email: $AUTH_EMAIL" \
-H "X-Auth-Key: $AUTH_KEY" \
-H "Content-Type: application/json" \
--data "$(
cat <<-EOF
{
"type": "CNAME",
"name": "$name",
"content": "kbp.keybaseapi.com",
"ttl": 1,
"proxied": true
}
EOF
)"
done
}

check_record_cnames() {
for name in "${NAMES[@]}"; do
if [ -z "$(dig +short CNAME "$name")" ]; then
return 1
fi
done
}

check_http_status() {
for name in "${NAMES[@]}"; do
if [ "$(curl -w "%{http_code}" "https://$name" -s -o /dev/null)" -ne "200" ]; then
return 1
fi
done
}

get_ids_by_name

set_proxy_false

sleep "$TTL" #Wait for DNS TTL to expire

while ! check_record_cnames; do
sleep "$TTL" #Wait for DNS TTL to expire
done

while ! check_http_status; do
sleep "$TTL" #Wait for new certificates
done

set_proxy_true

上述脚本就起名叫 keybase_cert_refresher.sh 吧,
缩进使用 TAB, 运行需要有 dig jq curl

  • AUTH_EMAIL 认证邮箱, 可在 Profile/API Tokens 查看
  • AUTH_KEY 认证 key, 可在 Profile/API Tokens 查看
  • DOMAIN_ZONE Zone ID, 每个由CF管理的顶级域名都有一个,获取方式
  • NAMES 你给 Keybase Pages 使用的域名

然后找台不太会关机的设备,比如你的 VPS , 设置计划任务:

1
0 3 15 */2 * keybase_cert_refresher.sh

关于 add2repo, rmfromrepo 最近又做了些改进,加入了 Telegram Bot 支持


关于 Cloudflare API 的一个大胆的想法

土法 DDNS 😝