一、前言
当网站接入到 Cloudflare 后,攻击者可通过 泛播IP 反查获得大量关联域名,扩大了域名在公网的暴露面。
这也导致各类恶意访问与攻击行为随之增多,常见目录扫描、接口探测、账号密码暴力破解等行为层出不穷。
Cloudflare 依靠其通用防护策略能够拦截绝大多数基础恶意流量,但面对伪装成正常请求的针对性登录爆破,仍缺乏精准甄别与拦截能力。即便引入 自定义规则,也难以实时适配攻击者不断变换的 IP 池与请求特征,防护效果存在滞后性。
而在服务端搭配 Nginx 访问规则虽可作为补充拦截手段,但其复杂的语法配置对不熟悉 Nginx 的用户而言门槛过高,难以在实际运维中有效落地。
针对上面提到的痛点,我们可以使用 Fail2Ban 工具进行入栈拦截,通过实时监控运行日志、识别异常访问并自动封禁攻击源的入栈请求。
同时搭配 Cloudflare IP访问规则在 CDN 层面同步拦截,在流量抵达源站前完成阻断。避免恶意请求穿透至服务器,从而有效减少服务器的性能损耗。
二、vaultwarden 安全部署
本文通过 Docker Compose 使用专用用户部署 Vaultwarden 为例,避免漏洞提权提高安全性。
2.1 创建专用用户以及数据目录
以下命令均需要在 root 权限下执行:
# 创建 vaultwarden 用户(禁止登录、无home目录)useradd -r -s /sbin/nologin -M vaultwarden
# 创建 vaultwarden 数据目录mkdir /opt/vaultwarden && cd /opt/vaultwarden
# 设置目录权限(仅 vaultwarden 用户可访问)chown -R vaultwarden:vaultwarden /opt/vaultwardenchmod -R 700 /opt/vaultwarden2.2 创建 docker 容器
创建 docker-compose.yml 配置文件:
cat << 'EOF' > /opt/vaultwarden/docker-compose.ymlservices: server: image: vaultwarden/server container_name: vaultwarden restart: always user: vaultwarden:vaultwarden # 使用 vaultwarden 用户运行容器 environment: - DOMAIN=https://vaultwarden.example.com # vaultwarden 访问域名 - SIGNUPS_ALLOWED=true # 是否允许注册:首次部署时必须设为 true 才能创建管理员账户,否则无法创建自己的账户。如果不开放使用可在注册完成后通过管理界面或修改 config.json 进行关闭。 - ORG_CREATION_USERS=false # 是否允许创建组织:个人使用建议设置为 false - ADMIN_TOKEN= # 使用 openssl rand -base64 32 生成,或根据官方指引生成 argon2 - WEBSOCKET_ENABLED=true # 启用 websocket 支持,关闭后部分插件以及 App 将无法使用 - ROCKET_PROXY_HEADER=CF-Connecting-IP # 设置解析 Cloudflare 请求头获取真实客户端 IP - TZ=Asia/Shanghai # 设置时区 - LOG_FILE=/data/vaultwarden.log # 设置日志文件存储位置 ports: - "127.0.0.1:10000:80" # 本地监听 10000 端口公网无法直接访问 volumes: - ./data:/data/ # 设置数据保存目录EOF修改 docker 配置后,创建容器并查看日志是否生成:
docker compose up -dcat /opt/vaultwarden/data/vaultwarden.log三、Cloudflare 配置
3.1 创建 Cloudflare API Token
- 进入 Cloudflare 控制台点击 管理账户 → 账户 API 令牌 → 创建令牌
- 设置令牌名称:例如
Vaultwarden Fail2ban API key - 设置权限策略:
- 强烈建议指定 API Token 生效的域名,不选则针对所有域名生效
- 选择
App Security下Firewall Services的读写权限
- 设置令牌过期时间:可根据实际需要设置令牌有效期
- 设置客户端 IP 地址过滤:建议设置
服务器IP提高安全性 - 点击
审核令牌创建令牌获取cfat_开头的 API Token
3.2 获取 Zone ID
- 进入 Cloudflare 控制台点击 域名 → 概览 → 选择域名
- 点击 概述 并下拉至最下方
- 显示的
区域 ID即为Zone ID
3.3 使用 API 测试权限配置正确
注意:尖括号
<、>为占位标识,填写时请务必删除,不要直接复制。
尝试封禁 IP 192.168.0.1
curl -s -X POST "https://api.cloudflare.com/client/v4/zones/<填写 Zone ID 即 区域 ID>/firewall/access_rules/rules" \ -H "Authorization: Bearer <填写 Cloudflare API Token>" \ -H "Content-Type: application/json" \ --data '{"mode":"block","configuration":{"target":"ip","value":"192.168.0.1"},"notes":"Fail2Ban Test>"}'尝试解封 IP 192.168.0.1
id=$(curl -s -G -X GET "https://api.cloudflare.com/client/v4/zones/<填写 Zone ID 即 区域 ID>/firewall/access_rules/rules" \ --data-urlencode "mode=block" \ --data-urlencode "configuration.target=ip" \ --data-urlencode "configuration.value=192.168.0.1" \ -H "Authorization: Bearer <填写 Cloudflare API Token>" \ -H "Content-Type: application/json" \
| awk -F"[,:}]" '{for(i=1;i<=NF;i++){if($i~/'id'\042/){print $(i+1)}}}' \ | tr -d ' "' \ | head -n 1)
if [ -z "$id" ]; then echo "未找到 IP 192.168.0.1 的封禁规则" exit 0fi
curl -s -X DELETE "https://api.cloudflare.com/client/v4/zones/<填写 Zone ID 即 区域 ID>/firewall/access_rules/rules/$id" \ -H "Authorization: Bearer <填写 Cloudflare API Token>" \ -H "Content-Type: application/json" \ --data '{"cascade": "none"}'四、Fail2Ban 配置
4.1 创建匹配规则
Fail2Ban 通过匹配规则的正则表达式来匹配日志文件中的记录,所以匹配规则的完整性是 Fail2Ban 拦截的基础。
通过软件源安装的 Fail2Ban 一般附带 vaultwarden 的匹配规则,但是面板等方式安装的可能没有附带。
如果存在没有附带的情况,可以使用以下命令获取或创建文件:
- 在线获取文件:
curl -o /etc/fail2ban/filter.d/vaultwarden.conf https://raw.githubusercontent.com/fail2ban/fail2ban/refs/heads/master/config/filter.d/vaultwarden.conf- 命令行创建文件:
cat << 'EOF' > /etc/fail2ban/filter.d/vaultwarden.conf# Fail2Ban filter for unsuccessful Vaultwarden authentication attempts# Logged in /var/log/vaultwarden.log# Author: LearningSpot
[Definition]
failregex = ^\s*(?:\[\]\s*)?\[vaultwarden::api::(?:identity|admin|core::two_factor::authenticator)?\]\[ERROR\] (?:Invalid admin token|Invalid TOTP code|Username or password is incorrect)[\.!](?:\s+(?!IP:)\S+)* IP: <ADDR>(?:\. Username: <F-USER>\S+</F-USER>)?ignoreregex =EOFTIPVaultwarden 在仓库 Wiki 中也提供了匹配规则,但校验范围较小,无法匹配管理员令牌错误、TOTP 错误等情况。因此建议使用该匹配规则的用户切换为覆盖范围更广的 Fail2ban 官方规则。
4.2 验证匹配规则
匹配规则创建完成后,建议使用以下命令测试,确保匹配规则可以生效:
fail2ban-regex /opt/vaultwarden/data/vaultwarden.log /etc/fail2ban/filter.d/vaultwarden.conf如果是宝塔面板安装的 Fail2Ban 可以使用以下命令测试:
/www/server/panel/pyenv/bin/fail2ban-regex /opt/vaultwarden/data/vaultwarden.log /etc/fail2ban/filter.d/vaultwarden.conf如果验证结果体现以下类似内容,则表示规则匹配正常:
# 该结果表示890行日志中有75行日志命中匹配规则Lines: 890 lines, 0 ignored, 75 matched, 815 missed4.3 配置 Action 执行脚本
Fail2Ban 一般附带 cloudflare.conf cloudflare-token.conf 两个 Action 配置,两者的核心区别如下:
cloudflare.conf基于 Cloudflare Global Key 进行操作,实行 Cloudflare 全局拦截规则。由于 Global Key 权限过大,存在严重安全隐患,Cloudflare 已不再推荐使用。cloudflare-token.conf基于 Cloudflare API Token 和 Zone ID 操作,仅在指定的 Zone 内进行拦截。遵循最小权限原则,安全性更高,是当前推荐使用的配置方式。、
本文以使用 cloudflare-token.conf 为例,只需取消以下两行的注释,并填入从 Cloudflare 获得的信息即可:
cfzone = <填写 Zone ID 即 区域 ID>cftoken = <填写 Cloudflare API Token>注意:尖括号
<、>为占位标识,填写时请务必删除,不要直接复制。
如果存在没有附带 cloudflare-token.conf 的情况,可以使用以下命令获取或创建文件:
- 在线获取文件:
curl -o /etc/fail2ban/action.d/cloudflare-token.conf https://raw.githubusercontent.com/fail2ban/fail2ban/refs/heads/master/config/action.d/cloudflare-token.conf- 命令行创建文件:
cat << 'EOF' > /etc/fail2ban/action.d/vaultwarden.local## Author: Logic-32## IMPORTANT## Please set jail.local's permission to 640 because it contains your CF API token.## This action depends on curl.## To get your Cloudflare API token: https://developers.cloudflare.com/api/tokens/create/## Cloudflare Firewall API: https://developers.cloudflare.com/firewall/api/cf-firewall-rules/endpoints/
[Definition]
# Option: actionstart# Notes.: command executed on demand at the first ban (or at the start of Fail2Ban if actionstart_on_demand is set to false).# Values: CMD#actionstart =
# Option: actionstop# Notes.: command executed at the stop of jail (or at the end of Fail2Ban)# Values: CMD#actionstop =
# Option: actioncheck# Notes.: command executed once before each actionban command# Values: CMD#actioncheck =
# Option: actionban# Notes.: command executed when banning an IP. Take care that the# command is executed with Fail2Ban user rights.# Tags: <ip> IP address# <failures> number of failures# <time> unix timestamp of the ban time# Values: CMDactionban = curl -s -X POST "<_cf_api_url>" \ <_cf_api_prms> \ --data '{"mode":"<cfmode>","configuration":{"target":"<cftarget>","value":"<ip>"},"notes":"<notes>"}'
# Option: actionunban# Notes.: command executed when unbanning an IP. Take care that the# command is executed with Fail2Ban user rights.# Tags: <ip> IP address# <failures> number of failures# <time> unix timestamp of the ban time# Values: CMD#actionunban = id=$(curl -s -G -X GET "<_cf_api_url>" \ --data-urlencode "mode=<cfmode>" --data-urlencode "notes=<notes>" --data-urlencode "configuration.target=<cftarget>" --data-urlencode "configuration.value=<ip>" \ <_cf_api_prms> \ | awk -F"[,:}]" '{for(i=1;i<=NF;i++){if($i~/'id'\042/){print $(i+1)}}}' \ | tr -d ' "' \ | head -n 1) if [ -z "$id" ]; then echo "<name>: id for <ip> cannot be found using target <cftarget>"; exit 0; fi; \ curl -s -X DELETE "<_cf_api_url>/$id" \ <_cf_api_prms> \ --data '{"cascade": "none"}'
_cf_api_url = https://api.cloudflare.com/client/v4/zones/<cfzone>/firewall/access_rules/rules_cf_api_prms = -H "Authorization: Bearer <cftoken>" -H "Content-Type: application/json"
[Init]
# Declare your Cloudflare Authorization Bearer Token in the [DEFAULT] section of your jail.local file.
# The Cloudflare <ZONE_ID> of the domain you want to manage.## cfzone =
# Your personal Cloudflare token. Ideally restricted to just have "Zone.Firewall Services" permissions.## cftoken =
# Target of the firewall rule. Default is "ip" (v4).#cftarget = ip
# The firewall mode Cloudflare should use. Default is "block" (deny access).# Consider also "js_challenge" or other "allowed_modes" if you want.#cfmode = block
# The message to include in the firewall IP banning rule.#notes = Fail2Ban <name>
[Init?family=inet6]cftarget = ip6EOF4.4 创建拦截规则
创建匹配规则后,还需配置对应的拦截规则。
当某个 IP 在指定时间内多次触发匹配规则时,Fail2Ban 将自动执行拦截规则对其进行封禁。
通过以下命令创建拦截规则:
cat << 'EOF' > /etc/fail2ban/jail.d/vaultwarden.local[vaultwarden]enabled = true # 启用 Vaultwarden 防爆破规则filter = vaultwarden # 日志过滤规则,须与/etc/fail2ban/filter.d/下的匹配规则文件名称一致logpath = /opt/vaultwarden/data/vaultwarden.log # Vaultwarden 日志路径backend = pyinotify # 监听文件maxretry = 5 # 最大失败次数findtime = 600 # 统计时长 600sbantime = 3660 # 封禁时长 3660saction = cloudflare-token # 触发 Action 动作调用 Cloudflare API 进行封禁和解封EOFNOTE由于已通过 Cloudflare 前置拦截,下述拦截规则未作配置,因其仅对请求到服务器的请求生效。
port = 80,443 # 指定监控与封禁的端口banaction = %(banaction_allports)s # 封禁所有端口(全局屏蔽)
4.5 拦截测试
# 重启 fail2bansystemctl restart fail2ban-client
# 手动测试封禁fail2ban-client set vaultwarden banip 1.2.3.4
# 检查本地封禁状态,同时可以访问 Cloudflare 查看封禁效果# 如果本地生效,但 Cloudflare 没有更新规则,请检测文件权限以及 API Token 权限以及 IP 授权范围fail2ban-client status vaultwarden
# 手动测试解除封禁fail2ban-client set vaultwarden unbanip 1.2.3.4五、使用 Cloudflare 自定义规则
使用 cloudflare-token.conf 进行封禁时,每封禁一个恶意访问的 IP 都将会生成一条 IP 封禁规则,对于追求配置简洁的用户来说难以接受。
因此我们可以通过 Cloudflare WAF 自定义规则 将所有封禁 IP 合并到一条规则中。
但该方法存在局限性:免费账户的自定义规则表达式上限为 4000个字符。
当 IP 地址每个段均为三位数(如 123.123.123.123)时最多可以存储 250 个 IP。
一旦字符数超限,那么新 IP 将无法被加入规则,进而导致封禁失效。
同时介于该规则通过文本内容进行匹配,因此不建议在本方案的自定义规则上进行额外的手动配置。
NOTE本方案与
cloudflare-token.conf可以同时设置,但是这是不必要的。
5.1 创建 Cloudflare 自定义规则
- 进入 Cloudflare 控制台点击 安全性 → 安全规则 → 创建规则 → 自定义规则
- 输入规则名称,例如
Vaultwarden Fail2ban Rules - 字段选择
IP 源地址 - 运算符选择
包含以下各项 - 值:填写一个需要封禁IP,如果暂时没有可输入
192.168.0.1 - 采取措施选择
阻止 - 执行顺序选择
第一个 - 状态选择
活动 - 记录 通过 API 调用保存 中的
zone_id、ruleset_id、rule_id
路径对应信息如下:/{zone_id}/rulesets/{ruleset_id}/rules/{rule_id} - 点击保存
5.2 创建 Cloudflare API Token
NOTE该方案所需的 Cloudflare API Token 权限与上文所需权限不一致!!!
- 进入 Cloudflare 控制台点击 管理账户 → 账户 API 令牌 → 创建令牌
- 设置令牌名称:例如
Vaultwarden Fail2ban Rules API key - 设置权限策略:
- 强烈建议指定 API Token 生效的域名,不选则针对所有域名生效
- 选择
Rules & Configuration下Account Rulesets的读写权限
- 设置令牌过期时间:可根据实际需要设置令牌有效期
- 设置客户端 IP 地址过滤:建议设置
服务器IP提高安全性 - 点击
审核令牌创建令牌获取cfat_开头的 API Token
5.3 配置 Action 执行脚本
TIP该 Action 脚本依赖
jq工具解析 JSON 数据,所以需要确保运行环境已安装该依赖。Terminal window apt-get install -y jq
使用以下命令获取或创建执行脚本:
- 在线获取执行脚本:
curl -o /etc/fail2ban/action.d/vaultwarden-cf-rules.local https://www.self4m.com/file/vaultwarden-cloudflare-fail2ban-r2-backup/vaultwarden-cf-rules.localchmod 640 /etc/fail2ban/action.d/vaultwarden-cf-rules.local- 文件创建执行脚本:
cat << "EOF" > /etc/fail2ban/action.d/vaultwarden-cf-rules.local## Author: self4m## Date: 2026-07-31## version: 1.1## 提示:## 请设置 vaultwarden-cf-rules.local 的权限为 640,因为它包含你的 CF API 令牌# chmod 640 /etc/fail2ban/action.d/vaultwarden-cf-rules.local
[Definition]
# 封禁操作:获取规则 → 提取IP列表 → 添加IP → 更新规则actionban = \ data=$(curl -s -X GET "https://api.cloudflare.com/client/v4/zones/<cfzone>/rulesets/<cfruleset_id>" \ -H "Authorization: Bearer <cftoken>") && \ expression=$(echo "$data" | jq -r --arg RULE_ID "<cfrule_id>" \ '.result.rules[] | select(.id == $RULE_ID) | .expression') && \ if [ -n "$expression" ]; then \ action=$(echo "$data" | jq -r --arg RULE_ID "<cfrule_id>" \ '.result.rules[] | select(.id == $RULE_ID) | .action') && \ description=$(echo "$data" | jq -r --arg RULE_ID "<cfrule_id>" \ '.result.rules[] | select(.id == $RULE_ID) | .description | tojson') && \ original_ip_segment=$(echo "$expression" | grep -oP 'ip\.src in \{[^}]+\}') && \ ip_list=$(echo "$expression" | grep -oP '(?<=ip\.src in \{)[^}]+(?=\})') && \ if ! echo " $ip_list " | grep -qF " <ip> "; then \ ip_list="${ip_list} <ip>" && \ new_ip_segment="ip.src in {${ip_list}}" && \ new_expression=$(echo "$expression" | sed "s|${original_ip_segment}|${new_ip_segment}|") && \ curl -s -X PATCH "https://api.cloudflare.com/client/v4/zones/<cfzone>/rulesets/<cfruleset_id>/rules/<cfrule_id>" \ -H 'Content-Type: application/json' \ -H "Authorization: Bearer <cftoken>" \ -d "{\"description\":${description},\"action\":\"${action}\",\"expression\":\"${new_expression}\"}" fi fi
# 解封操作:获取规则 → 提取IP列表 → 删除IP → 更新规则actionunban = \ data=$(curl -s -X GET "https://api.cloudflare.com/client/v4/zones/<cfzone>/rulesets/<cfruleset_id>" \ -H "Authorization: Bearer <cftoken>") && \ expression=$(echo "$data" | jq -r --arg RULE_ID "<cfrule_id>" \ '.result.rules[] | select(.id == $RULE_ID) | .expression') && \ if [ -n "$expression" ]; then \ action=$(echo "$data" | jq -r --arg RULE_ID "<cfrule_id>" \ '.result.rules[] | select(.id == $RULE_ID) | .action') && \ description=$(echo "$data" | jq -r --arg RULE_ID "<cfrule_id>" \ '.result.rules[] | select(.id == $RULE_ID) | .description | tojson') && \ original_ip_segment=$(echo "$expression" | grep -oP 'ip\.src in \{[^}]+\}') && \ ip_list=$(echo "$expression" | grep -oP '(?<=ip\.src in \{)[^}]+(?=\})') && \ if echo " ${ip_list} " | grep -qwF "<ip>"; then \ ip_list=$(echo "$ip_list" | tr ' ' '\n' | grep -vxF "<ip>" | tr '\n' ' ' | sed 's/^ *//;s/ *$//') && \ if [ -z "$ip_list" ]; then \ exit 0 fi new_ip_segment="ip.src in {${ip_list}}" && \ new_expression=$(echo "$expression" | sed "s|${original_ip_segment}|${new_ip_segment}|") && \ curl -s -X PATCH "https://api.cloudflare.com/client/v4/zones/<cfzone>/rulesets/<cfruleset_id>/rules/<cfrule_id>" \ -H 'Content-Type: application/json' \ -H "Authorization: Bearer <cftoken>" \ -d "{\"description\":${description},\"action\":\"${action}\",\"expression\":\"${new_expression}\"}" fi fi
[Init]
cfrule_id = <填写规则 ID>
cfruleset_id = <填写规则集 ID>
cfzone = <填写 Zone ID 即 区域 ID>
cftoken = <填写 Cloudflare API Token>
EOFchmod 640 /etc/fail2ban/action.d/vaultwarden-cf-rules.local注意:尖括号
<、>为占位标识,填写时请务必删除,不要直接复制。
5.4 修改拦截规则
由于配置了自定义执行脚本,所以需要在拦截规则中更新 action 的值,以匹配脚本的实际路径:
action = vaultwarden-cf-rules5.5 拦截测试
# 重启 fail2bansystemctl restart fail2ban-client
# 手动测试封禁fail2ban-client set vaultwarden banip 1.2.3.4
# 检查本地封禁状态,同时可以访问 Cloudflare 查看封禁效果# 如果本地生效,但 Cloudflare 没有更新规则,请检测文件权限以及 API Token 权限以及 IP 授权范围fail2ban-client status vaultwarden
# 手动测试解除封禁fail2ban-client set vaultwarden unbanip 1.2.3.4六、R2 定期备份
R2 是 Cloudflare 的兼容 S3 协议的对象存储,除用于静态资源分发外,也可以作为数据备份的存储。
6.1 创建 Cloudflare R2 存储桶
- 进入 Cloudflare 控制台点击 管理账户 → R2 对象存储 → 概述 → 创建存储桶
- 设置存储桶名称:例如
vaultwarden-backup - 位置选择:自动
- 默认存储类选择:标准
- 点击创建
IMPORTANT存储桶创建后是默认禁用公开访问的,为了数据的安全,请勿手动开启访问权限。
6.2 创建 Cloudflare API Token
NOTE该方案所需的 Cloudflare API Token 权限与上文所需权限不一致!!!
存储桶创建以后需要创建 API Token 用于脚本操作备份,根据最小权限原则建议创建一个独立的 API Token。
如果想要复用 API Token 额外添加下方所提及的权限即可。
- 进入 Cloudflare 控制台点击 存储和数据库 → 账户 API 令牌 → 创建令牌
- 设置令牌名称:例如
Vaultwarden Backup API key - 设置权限策略:
- 选择类型
R2 存储桶并选择vaultwarden-backup,不选则针对所有存储桶生效 - 选择
Developer Platform下Workers R2 Storage Bucket Item的读写权限
- 选择类型
- 设置令牌过期时间:可根据实际需要设置令牌有效期
- 设置客户端 IP 地址过滤:建议设置
服务器IP提高安全性 - 点击
审核令牌创建令牌获取访问密钥 ID秘密访问密钥S3 API
6.3 安装 AWS CLI
使用以下命令安装 AWS CLI:
apt-get install -y awscli6.4 创建备份脚本文件
因为 vaultwarden 使用本地数据库,为了避免直接备份导致数据差异,本脚本在备份过程中将会停止 vaultwarden 容器。容器停止后会立即打包所有数据文件,打包完成后会立即重启 vaultwarden 容器。
如果备份数据到 R2 失败,会将备份文件临时存储在 /tmp/vaultwarden_backup 目录下。
通过以下两种方式之一创建备份脚本:
- 在线获取脚本文件:
curl -o /opt/vaultwarden/vaultwarden_backup.sh https://www.self4m.com/file/vaultwarden-cloudflare-fail2ban-r2-backup/vaultwarden_backup.sh- 命令行创建脚本文件:
cat << "EOF" > /opt/vaultwarden/vaultwarden_backup.sh#!/bin/bashset -e
export AWS_ACCESS_KEY_ID="访问密钥 ID"export AWS_SECRET_ACCESS_KEY="秘密访问密钥"export ACCOUNT_ID="Cloudflare 账户 ID"
CONTAINER_NAME="vaultwarden" # 如果你的 vaultwarden 容器名称不同,需要修改为实际容器名称
BACKUP_DIR="/tmp/vaultwarden_backup"BUCKET_NAME="vaultwarden-backup"
DATE=$(date +%Y%m%d_%H%M%S)FILE_NAME="vaultwarden_${DATE}.tar.gz"
mkdir -p "$BACKUP_DIR"
echo "停止容器 $CONTAINER_NAME..."docker stop "$CONTAINER_NAME"
echo "备份数据到 $BACKUP_DIR/$FILE_NAME ..."tar -czf "${BACKUP_DIR}/${FILE_NAME}" -C /opt vaultwarden # 如果你的 vaultwarden 数据文件路径不同,需要修改为实际数据文件路径
echo "启动容器 $CONTAINER_NAME ..."docker start "$CONTAINER_NAME"
echo "上传到 R2 ..."if aws s3 cp "${BACKUP_DIR}/${FILE_NAME}" \ "s3://${BUCKET_NAME}/${FILE_NAME}" \ --endpoint-url "https://${ACCOUNT_ID}.r2.cloudflarestorage.com"; then rm -f "${BACKUP_DIR}/${FILE_NAME}" echo "备份完成: $FILE_NAME"else echo "上传失败,保留本地备份: ${BACKUP_DIR}/${FILE_NAME}"fi
find "$BACKUP_DIR" -type f -name "vaultwarden_*.tar.gz" -mtime +7 -exec rm -f {} \;EOF脚本文件创建后,在脚本中填写如下参数信息:
- AWS_ACCESS_KEY_ID:填写 访问密钥 ID
- AWS_SECRET_ACCESS_KEY:填写 秘密访问密钥
- ACCOUNT_ID:填写 S3 API 中的账户ID,https://<账户ID>.r2.cloudflarestorage.com/vaultwarden-backup
填写完成后设置 vaultwarden_backup.sh 的权限为 640,因为它包含你的 Access Key
chmod 640 /opt/vaultwarden/vaultwarden_backup.sh6.5 设置 crontab 定时任务
使用 crontab -e 添加定时任务,例如每天凌晨 1 点执行备份:
0 1 * * * /opt/vaultwarden/vaultwarden_backup.sh >> /var/log/r2-backup.log 2>&16.6 手动测试备份
bash /opt/vaultwarden/vaultwarden_backup.sh如果无法完成备份,请检查文件权限以及 API Token 权限以及 IP 授权范围
七、总结
通过以上的配置,我们已经完成了使用 Cloudflare + Fail2ban 对 Vaultwarden 进行安全加固,同时借助 R2 对象存储实现了数据的定期备份,为数据安全提供了基础保障。
但需要明确的是,无论防护措施做得多完善,数据安全的最终责任仍然掌握在自己手中。Vaultwarden 存储的是个人最敏感的密码信息,一旦数据丢失或服务遭入侵,恢复成本极高,甚至无法挽回。
因此,建议定期检查备份文件的完整性,验证恢复流程是否可用,同时妥善保管 API 密钥和各类凭证,避免将全部信任寄托在单一防护措施上。安全从来不是一劳永逸的结果,而是一个需要持续关注与不断维护的过程。
非商业用途可以使用,但必须注明出处;
若有改编需采用相同许可协议发布。