这份 Caddyfile 模板专为高安全性和 Fail2Ban 精准对接而设计。它包含了全局 JSON 日志(Fail2Ban 识别需要)、自动阻止 IP 直接访问、以及针对 Proxmox 的特殊配置。
🛡️ 堡垒级 Caddyfile 模版
{
# --- 全局设置 ---
email [email protected] # 用于自动申请证书
# 强制开启 JSON 格式日志,方便 Fail2Ban 极其精准地抓取攻击 IP
log {
output file /var/log/caddy/access.log
format json
}
# 性能优化:开启 HTTP/3 (默认开启,显式声明以确保支持)
servers {
protocol {
experimental_http3
}
}
}
# --- 安全 Snippets (可复用的代码块) ---
(security_headers) {
header {
# 隐藏 Caddy 版本信息
-Server
# 防点击劫持
X-Frame-Options "SAMEORIGIN"
# 强制 HTTPS
Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
# 禁用嗅探
X-Content-Type-Options "nosniff"
# 跨域策略
Referrer-Policy "strict-origin-when-cross-origin"
}
}
# --- 1. 默认屏蔽:阻止通过 IP 直接访问 ---
# 所有的扫描器和脚本通常直接扫描 IP 端口,这个块会让它们直接吃闭门羹
:80, :443 {
respond "Access Denied" 403 {
close
}
}
# --- 2. Proxmox PVE 反向代理 ---
pve.yourdomain.com {
import security_headers
# 将流量转发到本地 8006
reverse_proxy https://127.0.0.1:8006 {
# 关键:由于 PVE 默认使用自签名证书,Caddy 必须忽略验证才能后端连接
transport http {
tls_insecure_skip_verify
}
}
# 为 PVE 单独记一份日志,方便排查
log {
output file /var/log/caddy/pve_access.log
format json
}
}
# --- 3. 示例:其他受保护的服务 (如 Docker 容器) ---
service.yourdomain.com {
import security_headers
# 这里可以增加基础身份验证 (Basic Auth) 作为 Fail2Ban 的触发点
basic_auth {
# 使用 'caddy hash-password' 生成加密后的密码
admin $2a$14$xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
}
reverse_proxy localhost:8080
}🔍 为什么这个配置能增强 Fail2Ban 防护?
- JSON 格式日志:传统的文本日志容易被恶意字符伪造或绕过。使用
format json后,日志条目像这样:{"remote_ip":"1.2.3.4","status":401,"uri":"/admin"}Fail2Ban 的正则匹配这种结构化数据速度更快,且几乎零误报。 - 默认 403 屏蔽:直接通过 IP 访问(常见的扫描手段)会被直接拒绝。如果有人尝试暴力扫描你的网站路径,由于他没有正确的域名 Header,他会直接撞在第一个配置块上,从而被 Fail2Ban 快速标记并封禁。
- Basic Auth 诱饵:在
service.yourdomain.com中加入basic_auth。任何没有经过授权的访问都会返回401状态码,这会直接触发我们在 Fail2Ban 脚本中写的caddy-auth过滤器。
🚀 进阶建议
- 路径混淆:如果你觉得登录页面太明显,可以在 Caddy 中使用
handle_path做一个前缀,比如pve.yourdomain.com/secret_gate/才能访问。 - 配合 Cloudflare:如果你使用了 Cloudflare,请在 Caddyfile 中加入
trusted_proxies配置,否则 Caddy 记录到的remote_ip将全是 Cloudflare 的节点 IP,导致 Fail2Ban 封禁了 CDN 节点(这就尴尬了)。 - 一键测试:修改完配置后,别忘了运行
caddy validate检查语法,然后systemctl reload caddy生效。
如果使用cloudflare的CDN/DNS代理,可以使用下面建议的:
如果你使用了 Cloudflare (CF) 作为 CDN 或 DNS 代理(即那个“小黄云”是开启状态),那么必须将 trusted_proxies 整合到配置中。
为什么必须整合?
如果没有这个配置,Caddy 记录到的访客 IP 统统都会变成 Cloudflare 的服务器 IP。
- 后果: 如果有人攻击你,Fail2Ban 识别到的是 CF 的 IP。一旦封禁,你可能直接把整个 Cloudflare 的某个网段给封了,导致所有通过 CF 访问的用户(包括你自己)都打不开网站。
- 解决: 告诉 Caddy 哪些 IP 是 Cloudflare 的,Caddy 就会自动提取
X-Forwarded-For头部里的真实访客 IP。
🛠️ 整合后的完整 Caddyfile 模版
直接将这段配置替换或合并到你之前的 Caddyfile 中即可:
{
email [email protected]
log {
output file /var/log/caddy/access.log
format json
}
# --- 整合 Cloudflare 信任代理 ---
servers {
trusted_proxies static {
# Cloudflare IPv4 地址段 (截至2026年常用段)
173.245.48.0/20 103.21.244.0/22 103.22.200.0/22 103.31.4.0/22 141.101.64.0/18 108.162.192.0/18 190.93.240.0/20 188.114.96.0/20 197.234.240.0/22 198.41.128.0/17 162.158.0.0/15 104.16.0.0/13 104.24.0.0/14 172.64.0.0/13 131.0.72.0/22
# Cloudflare IPv6 地址段
2400:cb00::/32 2606:4700::/32 2803:f800::/32 2405:b500::/32 2405:8100::/32 2a06:98c0::/29 2c0f:f248::/32
}
}
}
# 之前的安全 Snippet
(security_headers) {
header {
-Server
X-Frame-Options "SAMEORIGIN"
Strict-Transport-Security "max-age=31536000"
X-Content-Type-Options "nosniff"
}
}
# 默认阻止 IP 访问
:80, :443 {
respond "Access Denied" 403 {
close
}
}
# PVE 反代示例
pve.yourdomain.com {
import security_headers
reverse_proxy https://127.0.0.1:8006 {
transport http {
tls_insecure_skip_verify
}
}
log {
output file /var/log/caddy/pve_access.log
format json
}
}💡 自动更新的小技巧
Cloudflare 的 IP 段偶尔会变动。如果你不追求极致的静态性能,可以安装 Caddy 的 cloudflare-proxies 插件,这样它可以自动获取最新的 IP 列表:
- 安装插件版 Caddy(以 Debian/Ubuntu 为例):
使用 xcaddy 编译或从官网下载带 cloudflare-proxies 模块的版本
- 修改配置为动态获取:
servers {
trusted_proxies cloudflare
}验证是否生效
配置完成后,你可以尝试访问一下网站,然后查看日志:
tail -f /var/log/caddy/pve_access.log
如果 JSON 里的 remote_ip 是你的手机 5G IP 或你家里的宽带 IP,而不是 172.x.x.x 或 104.x.x.x 开头的地址,说明配置成功。这时 Fail2Ban 就能精准地“指哪打哪”了。
到这一步,你的安全链路(Fail2Ban + 端口混淆 + Caddy 真实 IP + JSON 日志)已经非常稳固了。
为了你的服务器安全,了解它们的作用以及是否会导致“副作用”非常重要:
1. Caddy 会报 Warning 吗?
不会。 Caddy 的哲学是“配置即所得”。如果你不写,它就保持简洁。
唯一可能引发警告(Warning)的情况是你配置了 HSTS (Strict-Transport-Security) 但没有使用合法的 HTTPS 证书,或者在某些特定的自动 HTTPS 环境下参数冲突。但在你目前的配置下,它是完全安静的。
2. 这些 Header 的真实作用(建议保留的原因)
虽然不是必须,但这几行代码能让你的服务器从“裸奔”变成“武装”:
-Server:- 作用: 从响应中移除
Server: Caddy标志。 - 理由: 攻击者通常会搜索特定的服务器版本漏洞。隐藏它是“防御性隐身”的第一步。
- 作用: 从响应中移除
X-Frame-Options "SAMEORIGIN":- 作用: 防止你的 PVE 面板被别人嵌套在他们网站的
<iframe>里。 - 理由: 防止“点击劫持”攻击。
- 作用: 防止你的 PVE 面板被别人嵌套在他们网站的
Strict-Transport-Security (HSTS):- 作用: 告诉浏览器:“以后访问我这个域名,必须直接用 HTTPS,不许试探 HTTP。”
- 理由: 极大提高安全性,是获取 SSL Labs A+ 评分的必备项。
X-Content-Type-Options "nosniff":- 作用: 禁止浏览器通过“猜”来改变文件的 MIME 类型。
- 理由: 防止攻击者通过伪装脚本文件进行跨站脚本攻击 (XSS)。
3. 可能存在的“副作用”
如果你发现以下情况,可能需要调整这些 Header:
- PVE 无法在其他网页中嵌入: 如果你打算把 PVE 控制台嵌入到你自己的仪表盘(如 Dashy 或 Homepage)中,
X-Frame-Options "SAMEORIGIN"可能会导致加载失败。此时需要将其改为ALLOW-FROM或者直接删除。 - HSTS 锁定: HSTS 一旦开启,如果你以后想关掉 HTTPS 回退到纯 HTTP(虽然不建议),浏览器会因为之前的缓存而拒绝访问。
⚠️ 真正需要注意的“隐形警告”
在你的 Caddy 配置中,唯一一个虽然不会报错、但属于“不安全操作”的是:
tls_insecure_skip_verify
这是为了连接 PVE 的自签名证书所必须的,但 Caddy 在启动时会在日志里打印一条 Warning,提醒你后端连接没有经过验证。这是正常的,可以忽略。
建议
保留它们。 它们是现代 Web 安全的标准实践。如果你追求配置文件的极致精简,可以只保留 Strict-Transport-Security 和 -Server。