2.使用 Let's Encrypt 证书运行私有 Vaultwarden 实例

假设您希望运行一个只能从本地网络访问的 Vaultwarden 实例,但您又希望此实例启用由一个被广泛接受的 CA 而不是你自己的私有 CA 来签署的 HTTPS(以避免将专用 CA 证书加载到所有设备中的麻烦)。

本文将演示如何使用 Caddy Web 服务器创建这样的设置,Caddy 内置了对诸多 DNS 提供商的 ACME 支持。我们将通过 ACME DNS 验证方式获取 Let's Encrypt 证书来配置 Caddy -- 在这里使用通常的 HTTP 验证方式的话会有问题,因为它依赖于 Let's Encrypt 服务器能够访问到您的内部 Web 服务器。

本文涵盖了更通用的 DNS 验证设置,但许多用户可能会发现使用 Docker Compose 来集成 Caddy 和 Vaultwarden 是最简单的。具体的例子请参见使用 Docker Compose

涵盖了两个 DNS 提供商:

  • Duck DNS -- 为你提供一个 duckdns.org 下的子域名(例如 my-bwrs.duckdns.org)。如果您没有自己的域名,此选项是最简单的。

  • Cloudflare -- 这可以让您把您的 Vaultwarden 实例放在您拥有或控制的域名下。请注意,Cloudflare 可以只作为一个 DNS 提供商使用(即不使用 Cloudflare 最著名的代理功能)。如果您目前没有自己的域名,您也许可以在 Freenom 获得一个免费的域名。

当然也可以使用其他的网络服务器、ACME 客户端和 DNS 提供商的组合来创建类似的设置,但您必须解决细节上的差异。

获取自定义 Caddy 构建

由于大多数人不使用 DNS 验证方式,为每个 DNS 提供商自定义实现,因此 Caddy 默认没有内置此验证方式的支持。

最简单的方式是通过 https://caddyserver.com/download 获取带有 DNS 验证模块的 Caddy 版本。选择您的平台,选中 github.com/caddy-dns/cloudflare(用于 Cloudflare)和/或 github.com/caddy-dns/duckdns(用于 Duck DNS),然后点击下载。

如果您喜欢从源代码构建,可以使用 xcaddy。例如,要创建一个包含 Cloudflare 和 Duck DNS 支持的构建:

xcaddy build --with github.com/caddy-dns/cloudflare --with github.com/caddy-dns/duckdns

caddy 二进制 移动到 /usr/local/bin/caddy 或其他合适的目录中。使文件可执行。(可选)运行语句 sudo setcap cap_net_bind_service=+ep /usr/local/bin/caddy 以允许 caddy 在特权端口 (< 1024) 上监听,而无须以 root 身份运行。

Duck DNS 设置

如果您还没有账户,请在 https://www.duckdns.org/ 创建一个。给您的 Vaultwarden 实例创建一个子域名(例如,my-vw.duckdns.org),将其 IP 地址设置为您的 Vaultwarden 主机的私有 IP(例如,192.168.1.100)。记下您的账户的 token 值(UUID 格式的字符串)。Caddy 将需要此 token 来完成 DNS 验证。

在 caddy 可执行文件所在的同一目录中创建一个名为 Caddyfile(大写 C,无文件扩展名)的文件,其中包含以下内容,并将 localhost: 端口替换为 Vaultwarden 在其 ROCKET_PORT= 指令中使用的端口(Vaultwarden 的默认 Rocket_port 为 8001):

{$DOMAIN}:443 {
    tls {
        dns duckdns {$DUCKDNS_TOKEN}
    }
    reverse_proxy localhost:8001
}

创建一个名为 caddy.env 的文件,内容如下(替换相应的值):

DOMAIN=my-vw.duckdns.org
DUCKDNS_TOKEN=00112233-4455-6677-8899-aabbccddeeff

切换到 caddy 所在目录然后运行以下命令以首次启动 caddy

caddy run --envfile caddy.env

Duck DNS 域名(例如 my-vw.duckns.org)的 caddy 首次启动需要几秒钟的时间来解决 DNS 挑战和获取 HTTPS 证书。Caddy 通常将它们存储在 /root/.local/share/caddy 中,以及 caddy 的配置会自动保存到 /root/.config/caddy

运行命令以启动 vaultwarden

export ROCKET_PORT=8001

./vaultwarden

在设置 Caddy 之前,Vaultwarden 是否已运行并不重要。

您现在应该可以通过 https://my-vw.duckdns.org 访问到您的 Vaultwarden 实例了。如果没有,请检查 caddy 的输出。

您可以使用 [STRG]-[C] 来停止 caddy。接下来通过以下命令在后台启动 caddy:

caddy start --envfile caddy.env

重要提示:如有必要,在某些路由器(例如 FritzBox)或 DNS 解析器(例如 unbound)中,由于 DNS 重新绑定保护,必须为域名(例如 my-vw.example.com)设置例外。

Cloudflare 设置

如果您还没有账户,请在 https://www.cloudflare.com/ 创建一个;您还需要到您的域名注册商那里将名称服务器设置为 Cloudflare 分配给您的值。为您的 Vaultwarden 实例创建一个子域名(例如,vw.example.com),将其 IP 地址设置为您的 Vaultwarden 主机的私有 IP(例如,192.168.1.100)。例如:

创建一个用于 DNS 验证的 API token(更多背景知识,请参阅 https://github.com/libdns/cloudflare/blob/master/README.md):

  1. 点击右上角的个人图标并导航到 My Profile,然后选择 API Tokens 选项卡。

  2. 点击 Create Token 按钮,然后点击Edit zone DNS 右边的 Use template

  3. 编辑 Token name 字段(如果您希望使用更具描述性的名称)。

  4. Permissions 下应出现一个权限:Zone / DNS / Edit。添加其他权限:Zone / Zone / Read

  5. Zone Resources 下,设置 Include / Specific zone / example.com(使用您自己的域名替换 example.com)。

  6. TTL 下,为您的 tokan 设置一个变为非活动状态的 End Date。您也可以在以后设置。

  7. 创建 token 并复制 token 值。

您的 token 列表看起来应该像这样:

创建一个名为 Caddyfile 的文件,内容如下:

{$DOMAIN}:443 {
    tls {
        dns cloudflare {$CLOUDFLARE_API_TOKEN}
    }
    reverse_proxy localhost:8080
}

创建一个名为 caddy.env 的文件,内容如下(替换相应的值):

DOMAIN=vw.example.com
CLOUDFLARE_API_TOKEN=<your-api-token>

运行命令以启动 caddy

caddy run --envfile caddy.env

运行命令以启动 vaultwarden

export ROCKET_PORT=8080

./vaultwarden

您现在应该可以通过 https://vw.example.com 访问到您的实例了。

重要提示:如有必要,在某些路由器(例如 FritzBox)中,由于 DNS 重新绑定保护,必须为域名(例如 vw.example.com)设置例外。

使用 lego CLI 获取证书

在上面的 DuckDNS 例子中,Caddy 使用 lego 库通过 DNS 验证获取证书。lego 也有一个 CLI,你可以直接使用它来获取证书,例如,如果你想使用 Caddy 以外的反向代理。 (注意:这个例子使用 lego,但也有其他独立的 ACME 客户端支持 DNS 验证方式(参阅 DNS 验证部分)。

下面是一个如何做到这一点的例子。

  1. https://github.com/go-acme/lego/releases 下载预建的 lego 二进制文件到您的系统中。将其解压到某个目录,比如 /usr/local/lego

  2. 从那个目录中,运行 DUCKDNS_TOKEN=<token> ./lego -a --dns duckdns -d my-vm.duckdns.org -m me@example.com run(用合适的值替换令牌、域名和电子邮件地址)。这将使您在 Let's Encrypt 注册,并为您的域名获取一个证书。

  3. 设置一个每周的 cron 作业来运行 DUCKDNS_TOKEN=<token> ./lego --dns duckdns -d my-vw.duckdns.org -m me@example.com renew。这将在你的证书即将到期时更新它。

lego 默认请求 ECC/ECDSA 证书。如果您使用 Vaultwarden 中内置的 Rocket HTTPS 服务器,您需要请求 RSA 证书。在上面的 lego 命令中,添加选项 --key-type rsa2048

在这个例子中,您需要用生成的输出来配置您的反向代理:

  • /usr/local/lego/.lego/certificates/my-vw.duckdns.org.crt (证书)

  • /usr/local/lego/.lego/certificates/my-vw.duckdns.org.key (私钥)

故障排除

DNS 问题

如果您的子域名出现 DNS 解析错误(例如,DNS_PROBE_FINISHED_NXDOMAINERR_NAME_NOT_RESOLVED),可能是您的 DNS 解析器阻止了解析,有以下原因:

  1. 出于安全原因,它会阻止动态 DNS 服务。

  2. 为防止 DNS 重新绑定攻击,或出于其他一些原因,它会阻止域名解析到私有 (RFC 1918) IP 地址。

无论哪种情况,您都可以尝试使用其他 DNS 解析器,例如 Google 的 8.8.8.8 或 Cloudflare 的 1.1.1.1。对于第二种情况,如果您在 dnsmasq 或 Unbound 等本地 DNS 服务器后面运行,则可以将其配置为完全禁用 DNS 重新绑定保护,或允许某些域名返回私有地址。关于 Unbound,您可以通过将以下指令添加到其配置文件中来实现(使用您自己的 Duck DNS 域名替换该域名):

private-domain: "my-vw.duckdns.org"

然后通过 unbound-control reloadsystemctl restart unbound 重新启动 unbound 以使其加载新配置。

此外,请确保关闭您之前为 Vaultwarden 设置的 HTTPS 设置,特别是通过 Rocket TLS 使用您自己的(自签名)证书的私有 CA,因为这会干扰您新的 Let's Encrypt 受保护的域名。只需在 Vaultwarden 的环境文件中注释掉(# 符号)ROCKET_TLS 指令即可:

# ROCKET_TLS={certs="./cert.pem",key="./privkey.pem"}

Vaultwarden 登录问题

更改域名后不要忘记更新 Vaultwarden 的环境文件:

DOMAIN=https://my-vw.duckdns.org

参考

DNS 验证

Caddy Cloudflare 组件

Caddy Duck DNS 组件

最后更新于