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

{% hint style="success" %}
对应的[官方页面地址](https://github.com/dani-garcia/vaultwarden/wiki/Running-a-private-vaultwarden-instance-with-Let%27s-Encrypt-certs)
{% endhint %}

假设您希望运行一个只能从本地网络访问的 Vaultwarden 实例，但您又希望此实例启用由一个被广泛接受的 CA 而不是你自己的[私有 CA](/other-information/private-ca-and-self-signed-certs-that-work-with-chrome.md) 来签署的 HTTPS（以避免将专用 CA 证书加载到所有设备中的麻烦）。

本文将演示如何使用 [Caddy](https://caddyserver.com/) 网页服务器创建这样的设置，Caddy 内置了对诸多 DNS 提供程序的 ACME 支持。我们将通过 ACME [DNS 验证方式](https://letsencrypt.org/docs/challenge-types/#dns-01-challenge)获取 Let's Encrypt 证书来配置 Caddy -- 在这里使用普通的 HTTP 验证方式的话会有问题，因为它依赖于 Let's Encrypt 服务器能够访问到您的内部网页服务器。

{% hint style="danger" %}
本文涵盖了更通用的 DNS 验证设置，但许多用户可能会发现使用 Docker Compose 来集成 Caddy 和 Vaultwarden 是最简单的。具体的例子请参见[使用 Docker Compose](/container-image-usage/using-docker-compose.md#caddy-with-dns-challenge)。
{% endhint %}

涵盖了两个 DNS 提供程序：

* [Duck DNS](https://www.duckdns.org/) -- 为你提供一个 `duckdns.org` 下的子域名（例如 `my-bwrs.duckdns.org`）。如果您没有自己的域名，此选项是最简单的。
* [Cloudflare](https://www.cloudflare.com/) -- 这可以让您把您的 Vaultwarden 实例放在您拥有或控制的域名下。请注意，Cloudflare 可以只作为一个 DNS 提供程序使用（即不使用 Cloudflare 最著名的代理功能）。如果您目前没有自己的域名，您也许可以在 [Freenom](https://www.freenom.com/) 获得一个免费的域名。

当然也可以使用其他的网络服务器、[ACME 客户端](https://letsencrypt.org/docs/client-options/)和 DNS 提供程序的组合来创建类似的设置，但您必须解决细节上的差异。

## 获取自定义 Caddy 构建 <a href="#getting-a-custom-caddy-build" id="getting-a-custom-caddy-build"></a>

Caddy 默认情况下没有内置 DNS 验证挑战支持，因为大多数人不使用这种验证挑战方式，并且它需要为每个 DNS 提供程序进行自定义实现。

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

如果您喜欢从源代码构建，可以使用 [`xcaddy`](https://caddyserver.com/docs/build#xcaddy)。例如，要创建一个包含 Cloudflare 和 Duck DNS 支持的构建：

```sh
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 设置 <a href="#duck-dns-setup" id="duck-dns-setup"></a>

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

在 Caddy 可执行文件所在的同一目录中创建一个名为 `Caddyfile`（大写 C，无文件扩展名）的文件，其中包含以下内容，并将 `localhost:` 端口替换为 Vaultwarden 在其 `ROCKET_PORT=` 指令中使用的端口（Vaultwarden 的默认 Rocket\_port 为 8001）：

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

创建一个名为 `caddy.env` 的文件，内容如下（替换相应的值）：

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

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

```sh
caddy run --envfile caddy.env
```

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

运行命令以启动 `vaultwarden`：

```sh
export ROCKET_PORT=8001

./vaultwarden
```

{% hint style="info" %}
在设置 Caddy 之前，Vaultwarden 是否已运行并不重要。
{% endhint %}

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

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

```sh
caddy start --envfile caddy.env
```

**重要提示：**&#x5982;有必要，在某些路由器（例如 FritzBox）或 DNS 解析器（例如 unbound）中，由于 DNS 重新绑定保护，必须为域名（例如 `my-vw.example.com`）设置例外。

## Cloudflare 设置 <a href="#cloudflare-setup" id="cloudflare-setup"></a>

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

<figure><img src="https://i.imgur.com/BBvy4Yj.png" alt=""><figcaption></figcaption></figure>

创建一个用于 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 列表看起来应该像这样：

<figure><img src="https://i.imgur.com/FoOv9Ww.png" alt=""><figcaption></figcaption></figure>

创建一个名为 `Caddyfile` 的文件，内容如下：

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

创建一个名为 `caddy.env` 的文件，内容如下（替换相应的值）：

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

运行命令以启动 `caddy`：

```sh
caddy run --envfile caddy.env
```

运行命令以启动 `vaultwarden`：

```sh
export ROCKET_PORT=8080

./vaultwarden
```

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

**重要提示：**&#x5982;有必要，在某些路由器（例如 FritzBox）中，由于 DNS 重新绑定保护，必须为域名（例如 `vw.example.com`）设置例外。

## 使用 `lego` CLI 获取证书 <a href="#getting-certs-using-the-lego-cli" id="getting-certs-using-the-lego-cli"></a>

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

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

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`。这将在你的证书即将到期时更新它。

{% hint style="info" %}
`lego` 默认请求 ECC/ECDSA 证书。如果您使用 Vaultwarden 中内置的 [Rocket HTTPS 服务器](/reverse-proxy/https/enabling-https.md#via-rocket)，您需要请求 RSA 证书。在上面的 `lego` 命令中，添加选项 `--key-type rsa2048`。
{% endhint %}

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

* `/usr/local/lego/.lego/certificates/my-vw.duckdns.org.crt` （证书）
* `/usr/local/lego/.lego/certificates/my-vw.duckdns.org.key` （私钥）

## 故障排除 <a href="#troubleshooting" id="troubleshooting"></a>

### DNS 问题 <a href="#dns-issues" id="dns-issues"></a>

如果您的子域名出现 DNS 解析错误（例如，`DNS_PROBE_FINISHED_NXDOMAIN` 或 `ERR_NAME_NOT_RESOLVED`），可能是您的 DNS 解析器阻止了解析，有以下原因：

1. 出于安全原因，它会阻止动态 DNS 服务。
2. 为防止 [DNS 重新绑定](https://en.wikipedia.org/wiki/DNS_rebinding)攻击，或出于其他一些原因，它会阻止域名解析到私有 (RFC 1918) IP 地址。

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

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

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

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

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

### Vaultwarden 登录问题 <a href="#vaultwarden-login-issues" id="vaultwarden-login-issues"></a>

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

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

## 参考 <a href="#references" id="references"></a>

### DNS 验证挑战 <a href="#dns-challenge" id="dns-challenge"></a>

* <https://caddy.community/t/how-to-use-dns-provider-modules-in-caddy-2/8148>
* <https://community.letsencrypt.org/t/dns-providers-who-easily-integrate-with-lets-encrypt-dns-validation/86438>

### Caddy Cloudflare 组件 <a href="#caddy-cloudflare-module" id="caddy-cloudflare-module"></a>

* <https://github.com/caddy-dns/cloudflare>
* <https://go-acme.github.io/lego/dns/cloudflare/>

### Caddy Duck DNS 组件 <a href="#caddy-duck-dns-module" id="caddy-duck-dns-module"></a>

* <https://github.com/caddy-dns/duckdns>
* <https://go-acme.github.io/lego/dns/duckdns/>


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://rs.ppgg.in/reverse-proxy/https/running-a-private-vaultwarden-instance-with-lets-encrypt-certs.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
