这份 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 防护?

  1. JSON 格式日志:传统的文本日志容易被恶意字符伪造或绕过。使用 format json 后,日志条目像这样:{"remote_ip":"1.2.3.4","status":401,"uri":"/admin"}Fail2Ban 的正则匹配这种结构化数据速度更快,且几乎零误报。
  2. 默认 403 屏蔽:直接通过 IP 访问(常见的扫描手段)会被直接拒绝。如果有人尝试暴力扫描你的网站路径,由于他没有正确的域名 Header,他会直接撞在第一个配置块上,从而被 Fail2Ban 快速标记并封禁。
  3. 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 列表:

  1. 安装插件版 Caddy(以 Debian/Ubuntu 为例):

使用 xcaddy 编译或从官网下载带 cloudflare-proxies 模块的版本

  1. 修改配置为动态获取:
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> 里。
    • 理由: 防止“点击劫持”攻击。
  • 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。