📚 DDNS-GO 状态监控完整部署教程(Webhook + Caddy 静态服务版)

本教程旨在通过 Webhook 接收 DDNS-GO 的更新请求,将状态写入 JSON 文件,再利用 Caddy 提供静态文件服务(经 Traefik 反向代理实现 HTTPS),最终由 Homepage 的 Custom API Widget 进行定时轮询展示。整个流程实现了从 IP 变更检测到可视化监控的闭环。


🏗️ 一、整体架构与原理

在开始之前,请先理解本方案的数据流转逻辑,这有助于排查后续可能出现的问题:

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
DDNS-GO (IP 变化时触发)

▼ POST 请求 (https://webhook.your-domain.com/hooks/ddns-update)
Traefik 反向代理

▼ 转发到 Webhook 容器 (9000 端口)
Webhook 容器 (adnanh/webhook)

▼ 验证签名后,执行脚本,写入 JSON 文件
容器内路径: /deploy/api/ddns/ddns-status.json

▼ 映射到宿主机
宿主机路径: ~/docker/caddy/data/www/api/ddns/ddns-status.json

▼ 映射到 Caddy 容器
Caddy 容器路径: /opt/www/api/ddns/ddns-status.json

▼ Caddy 提供 HTTP 服务
http://api.your-domain.com/ddns/ddns-status.json

▼ Traefik 反向代理为 HTTPS
https://api.your-domain.com/ddns/ddns-status.json

▼ 定时轮询 (60秒)
Homepage Custom API Widget

▼ 显示在仪表盘
IPv4/IPv6 地址、状态、域名等信息

数据流详解:

  1. **触发层 (DDNS-GO)**:当公网 IP 发生变化时,DDNS-GO 向指定 Webhook 地址发送 POST 请求。
  2. **接收层 (Webhook)**:容器接收请求,验证签名后执行 Shell 脚本,提取参数并生成 ddns-status.json
  3. **存储层 (Caddy)**:利用 Caddy 的静态文件服务功能,将 JSON 文件暴露为 Web 接口。
  4. **展示层 (Homepage)**:仪表盘通过 Custom API 组件定时请求该 JSON 接口,解析并渲染数据。

🛠️ 二、环境准备与前置条件

在部署前,请确保您的服务器环境已满足以下基础条件:

类别 必备组件 状态要求
基础环境 Docker & Docker Compose 已安装并可正常运行
核心服务 Traefik 已部署,且配置了 webhook.your-domain.comapi.your-domain.com 的路由规则
依赖服务 DDNS-GO 已部署,建议使用 host 网络模式以准确获取 IP
展示服务 Homepage 已部署,用于最终可视化展示
网络配置 域名解析 webhook.your-domain.comapi.your-domain.com 已解析至服务器公网 IP
防火墙 端口开放 确保 80/TCP 和 443/TCP 端口处于开放状态

📂 三、项目目录结构规划

统一的目录结构是维护复杂 Docker 项目的关键。建议在宿主机创建以下目录层级:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
~/docker/
├── webhook/ # Webhook 服务根目录
│ ├── docker-compose.yml
│ ├── data/
│ │ ├── webhook/ # 存放 hooks.json
│ │ ├── scripts/ # 存放脚本文件
│ │ │ └── ddns/
│ │ │ └── ddns-update.sh
│ │ └── logs/ # 日志目录
├── caddy/ # Caddy 服务根目录
│ ├── docker-compose.yml
│ └── data/
│ ├── conf/ # Caddyfile 配置
│ │ └── Caddyfile
│ ├── data/ # Caddy 证书与缓存
│ └── www/ # 静态文件根目录
│ └── api/
│ └── ddns/
│ └── ddns-status.json # 状态文件将生成于此

⚙️ 四、核心组件配置详解

本章节包含所有关键配置文件的编写,请严格按照路径创建并修改。

4.1 Webhook 处理脚本 (ddns-update.sh)

文件路径~/docker/webhook/data/scripts/ddns/ddns-update.sh

功能说明:该脚本负责接收 DDNS-GO 传来的参数,并将其格式化为 JSON 文件。脚本中包含了参数验证和日志记录功能。

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
#!/bin/sh
# ============================================================
# DDNS-GO Webhook 更新脚本
# 功能:接收 7 个参数,生成 JSON 状态文件
# 写入路径:/deploy/api/ddns/ddns-status.json
#
# 参数说明(由 Webhook 从 JSON 请求体中提取):
# $1 = ipv4_addr - 当前 IPv4 地址
# $2 = ipv4_result - IPv4 更新结果
# $3 = ipv4_domains - IPv4 关联的域名
# $4 = ipv6_addr - 当前 IPv6 地址
# $5 = ipv6_result - IPv6 更新结果
# $6 = ipv6_domains - IPv6 关联的域名
# $7 = timestamp - 触发时间戳
# ============================================================

# 接收参数
IPV4_ADDR="$1"
IPV4_RESULT="$2"
IPV4_DOMAINS="$3"
IPV6_ADDR="$4"
IPV6_RESULT="$5"
IPV6_DOMAINS="$6"
TIMESTAMP="$7"

# 参数验证:如果 IPv4 和 IPv6 地址都为空,则退出并报错
if [ -z "$IPV4_ADDR" ] && [ -z "$IPV6_ADDR" ]; then
echo "Error: No IP address provided" >&2
exit 1
fi

# 输出参数到标准输出(用于调试)
echo "ipv4_addr=$IPV4_ADDR"
echo "ipv4_result=$IPV4_RESULT"
echo "ipv4_domains=$IPV4_DOMAINS"
echo "ipv6_addr=$IPV6_ADDR"
echo "ipv6_result=$IPV6_RESULT"
echo "ipv6_domains=$IPV6_DOMAINS"
echo "timestamp=$TIMESTAMP"

# 写入 JSON 文件到指定目录
# 注意:路径 /deploy/api/ddns 是容器内的挂载点,映射到宿主机的 Caddy 静态目录
cat > /deploy/api/ddns/ddns-status.json << EOF
{
"ipv4_addr": "$IPV4_ADDR",
"ipv4_result": "$IPV4_RESULT",
"ipv4_domains": "$IPV4_DOMAINS",
"ipv6_addr": "$IPV6_ADDR",
"ipv6_result": "$IPV6_RESULT",
"ipv6_domains": "$IPV6_DOMAINS",
"timestamp": "$TIMESTAMP"
}
EOF

# 记录系统日志
logger "DDNS updated: ipv4_addr=$IPV4_ADDR"

💡 权限设置
脚本创建后,必须赋予执行权限,否则 Webhook 容器无法运行它。

1
chmod +x ~/docker/webhook/data/scripts/ddns/ddns-update.sh

4.2 Webhook 配置文件 (hooks.json)

文件路径~/docker/webhook/data/webhook/hooks.json

配置要点

  • id: 对应访问 URL 的路径后缀。
  • trigger-rule: 安全机制,通过 Header 中的签名密钥验证请求合法性。
  • pass-arguments-to-command: 定义了从 HTTP 请求体中提取哪些字段传递给脚本。
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
[
{
"id": "ddns-update",
"execute-command": "/scripts/ddns/ddns-update.sh",
"command-working-directory": "/scripts/ddns",
"response-message": "DDNS updated.",
"pass-arguments-to-command": [
{ "source": "payload", "name": "ipv4_addr" },
{ "source": "payload", "name": "ipv4_result" },
{ "source": "payload", "name": "ipv4_domains" },
{ "source": "payload", "name": "ipv6_addr" },
{ "source": "payload", "name": "ipv6_result" },
{ "source": "payload", "name": "ipv6_domains" },
{ "source": "payload", "name": "timestamp" }
],
"trigger-rule": {
"match": {
"type": "value",
"value": "your-secret-key", // 🔑 请替换为你生成的强密钥
"parameter": {
"source": "header",
"name": "X-DDNS-Signature"
}
}
}
}
]

🔐 密钥生成建议
请在终端执行 openssl rand -base64 32 生成一个高强度的随机密钥,并替换配置中的 your-secret-key。该密钥需同时配置在 DDNS-GO 的 Webhook 设置中。


4.3 Caddy 配置文件 (Caddyfile)

文件路径~/docker/caddy/data/conf/Caddyfile

原理说明:Caddy 在容器内监听 80 端口提供 HTTP 服务,实际对外由 Traefik 提供 HTTPS 加密。此配置指定了静态文件的根目录。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 服务域名:api.your-domain.com
# 监听端口:80 (由 Traefik 处理 TLS)
# 静态文件根目录:/opt/www/api
http://api.your-domain.com:80 {
root * /opt/www/api

# 可选:导入默认设置(如果存在)
# import default_setting

# 日志配置
log {
output file /var/log/caddy/api_access.log {
roll_size 10mb
roll_keep 5
}
format console
}
}

4.4 Docker Compose 编排

1. Webhook 服务编排
文件路径~/docker/webhook/docker-compose.yml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
version: '3'
services:
webhook:
# image: adnanh/webhook:latest # 可直接使用官方镜像
image: my-webhook:alpine # 若使用自定义镜像(含 jq/git 等工具)
container_name: webhook
volumes:
- ./data/webhook:/etc/webhook
- ./data/scripts:/scripts:rw
- ../caddy/data/www/api/ddns:/deploy/api/ddns # 共享存储卷
command: -hooks /etc/webhook/hooks.json -port 9000 -verbose
restart: unless-stopped
networks:
- dual-stack-bridge
labels:
- "traefik.enable=true"
- "traefik.http.routers.webhook.rule=Host(`webhook.your-domain.com`)"
- "traefik.http.routers.webhook.tls=true"
- "traefik.http.routers.webhook.tls.certresolver=myresolver"
- "traefik.http.services.webhook.loadbalancer.server.port=9000"

networks:
dual-stack-bridge:
external: true

2. Caddy 服务编排
文件路径~/docker/caddy/docker-compose.yml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
version: '3'
services:
caddy:
image: caddy:alpine
container_name: caddy
ports:
- "80:80"
- "443:443"
volumes:
- ./data/www/api:/opt/www/api
- ./data/data:/data
- ./data/conf/Caddyfile:/etc/caddy/Caddyfile:ro
restart: unless-stopped
networks:
- dual-stack-bridge

networks:
dual-stack-bridge:
external: true

🖥️ 五、客户端与仪表盘配置

5.1 DDNS-GO 端配置

  1. 登录 DDNS-GO 管理界面(默认端口 9876)。
  2. 找到 Webhook 配置模块。
  3. URL: https://webhook.your-domain.com/hooks/ddns-update
  4. 请求头: X-DDNS-Signature: your-secret-key (需与 hooks.json 中一致)
  5. 请求体 (JSON): 复制以下内容,注意使用 DDNS-GO 的模板变量:
1
2
3
4
5
6
7
8
9
{
"ipv4_addr": "#{ipv4Addr}",
"ipv4_result": "#{ipv4Result}",
"ipv4_domains": "#{ipv4Domains}",
"ipv6_addr": "#{ipv6Addr}",
"ipv6_result": "#{ipv6Result}",
"ipv6_domains": "#{ipv6Domains}",
"timestamp": "#{timestamp}"
}

5.2 Homepage 仪表盘配置

services.yaml 中添加以下卡片配置,即可在首页看到实时状态。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
- 动态DNS监控:
- DDNS-GO 状态:
icon: /icons/ddns-go.png
href: http://your-ddns-go-ip:9876/
description: "公网 IP 变更实时监控"
siteMonitor: http://your-ddns-go-ip:9876/
widget:
type: customapi
url: https://api.your-domain.com/ddns/ddns-status.json
refreshInterval: 60000
mappings:
- field: ipv4_addr
label: IPv4 地址
- field: ipv4_result
label: IPv4 状态
- field: ipv4_domains
label: IPv4 域名
- field: ipv6_addr
label: IPv6 地址
- field: ipv6_result
label: IPv6 状态
- field: ipv6_domains
label: IPv6 域名

🛡️ 六、安全建议与故障排查

安全加固

  • 密钥管理:定期更换 Webhook 的签名密钥,防止恶意伪造请求。
  • 网络隔离:在防火墙上限制 webhook.your-domain.com 的访问源,仅允许 DDNS-GO 所在 IP 访问。
  • 只读挂载:Caddyfile 配置文件建议以只读(:ro)方式挂载进容器。

常见问题排查表

故障现象 可能原因 解决方案
Webhook 返回 401/403 签名密钥不匹配 核对 hooks.json 中的 value 与 DDNS-GO 请求头中的密钥是否一致
JSON 文件未生成 路径映射错误 检查 Webhook 容器内的 /deploy/api/ddns 是否正确映射到 Caddy 的静态目录
Homepage 显示空白 跨域或 URL 错误 浏览器直接访问 https://api.your-domain.com/ddns/ddns-status.json 查看是否能获取数据
Caddy 404 错误 文件权限 确认宿主机目录权限为 755,文件可被 Caddy 进程读取
无法触发更新 网络连通性 确保服务器能解析 webhook.your-domain.com 且能访问 Traefik 端口

🎉 七、总结

通过以上步骤,您已成功构建了一套高可用的 DDNS 状态监控系统。该方案利用 Webhook 实现了事件驱动,利用 Caddy 实现了极简的静态 API 服务,最终通过 Homepage 实现了可视化展示。整个流程不仅解决了 DDNS 状态不可视的问题,也为后续扩展其他服务监控提供了标准范例。