Files
server-deploy/vaultwarden/deploy.sh
T

293 lines
12 KiB
Bash
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env bash
set -euo pipefail
# ============================================
# Vaultwarden 一键部署脚本
# 自动安装 Docker + Nginx + SSL + Vaultwarden
# ============================================
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
cd "$SCRIPT_DIR"
# ===== 加载公共基础函数 =====
BASE_DIR="$(cd "$SCRIPT_DIR/../base" 2>/dev/null && pwd)" || true
if [ -z "$BASE_DIR" ] || [ ! -f "$BASE_DIR/setup.sh" ]; then
echo "[ERROR] base/setup.sh 未找到" >&2
echo "请确保目录结构如下:" >&2
echo " /opt/base/setup.sh" >&2
echo " /opt/vaultwarden/deploy.sh (当前脚本)" >&2
exit 1
fi
source "$BASE_DIR/setup.sh"
# =============================================================
# Vaultwarden 专用函数
# =============================================================
GITHUB_REPO="dani-garcia/vaultwarden"
# 生成随机密码
generate_password() {
openssl rand -base64 32 | tr -d '/+=' | head -c 32
}
# 查询 GitHub 上的最新 release 版本号(不含前缀 v),失败返回非零
resolve_latest_version() {
local ver
ver=$(curl -sf --max-time 20 \
"https://api.github.com/repos/${GITHUB_REPO}/releases/latest" \
| grep -o '"tag_name"[[:space:]]*:[[:space:]]*"[^"]*"' \
| head -1 | sed 's/.*"\([^"]*\)"$/\1/' | sed 's/^v//') || true
[ -n "$ver" ] || return 1
echo "$ver"
}
# 把镜像固定到具体版本号。
# 必须同时更新 .env 和已导出的环境变量:init_env 里 `set -a; source .env` 已把旧值
# 导出到本进程,而 docker compose 对环境变量的优先级高于 .env 文件,只改文件不生效。
pin_image_version() {
local ref="$1"
if grep -q '^VAULTWARDEN_IMAGE=' .env; then
sed -i "s|^VAULTWARDEN_IMAGE=.*|VAULTWARDEN_IMAGE=${ref}|" .env
else
printf '\nVAULTWARDEN_IMAGE=%s\n' "$ref" >> .env
fi
export VAULTWARDEN_IMAGE="$ref"
}
# 确定本次部署使用的镜像版本。
# 不使用 latest 标签的原因:它只在 docker compose pull 时才重新解析,容器不重建就会
# 一直跑旧版本,且从配置上看不出实际运行的是哪一版。
setup_image_version() {
local current="${VAULTWARDEN_IMAGE:-}"
# 已固定到具体版本 → 尊重现有配置,升级交给 upgrade.sh
if [ -n "$current" ] && [[ "$current" != *":latest" ]]; then
log " 镜像: ${current}"
return 0
fi
# 已有实例却仍在用 latest:此时固定版本等同于一次升级,
# 而升级必须走 upgrade.sh(带冷备份、完整性校验、自动回滚),这里不擅自处理。
if docker compose ps -a -q vaultwarden 2>/dev/null | grep -q .; then
warn "检测到已存在的实例,且 .env 仍在使用 latest 标签"
warn "latest 不会自动更新,也看不出实际运行版本,建议固定到具体版本号:"
warn " bash upgrade.sh # 安全升级到最新版,并自动写回版本号"
warn "本次部署不改动镜像配置"
return 0
fi
log "正在查询 Vaultwarden 最新版本..."
local ver
if ! ver="$(resolve_latest_version)"; then
warn "无法从 GitHub 获取最新版本号(网络问题?),本次回退使用 latest 标签"
warn "部署完成后建议手动固定: VAULTWARDEN_IMAGE=vaultwarden/server:<版本号>"
[ -n "$current" ] || pin_image_version "vaultwarden/server:latest"
return 0
fi
pin_image_version "vaultwarden/server:${ver}"
log " 镜像: vaultwarden/server:${ver}(当前最新 release,已写入 .env"
}
init_env() {
step "初始化 Vaultwarden 配置"
if [ ! -f .env ]; then
if [ ! -f .env.example ]; then
error "缺少 .env.example 模板文件"
exit 1
fi
cp .env.example .env
# 自动生成 ADMIN_TOKEN
local admin_token
admin_token=$(generate_password)
sed -i "s/^ADMIN_TOKEN=$/ADMIN_TOKEN=${admin_token}/" .env
log "已自动生成 ADMIN_TOKEN"
log "已生成 .env 文件"
echo ""
warn "┌──────────────────────────────────────────────────────┐"
warn "│ 请编辑 .env 文件,至少修改以下配置: │"
warn "│ │"
warn "│ VAULTWARDEN_DOMAIN=vault.yourdomain.com │"
warn "│ CERTBOT_EMAIL=you@yourdomain.com │"
warn "│ │"
warn "│ ADMIN_TOKEN 已自动生成,请妥善保存 │"
warn "│ │"
warn "│ 编辑命令: vi $SCRIPT_DIR/.env │"
warn "│ 编辑完成后重新运行: bash deploy.sh │"
warn "└──────────────────────────────────────────────────────┘"
exit 0
fi
fix_crlf .env
set -a; source .env; set +a
local has_error=0
if [[ -z "${VAULTWARDEN_DOMAIN:-}" ]] || [[ "${VAULTWARDEN_DOMAIN}" == "vault.example.com" ]]; then
error "请在 .env 中将 VAULTWARDEN_DOMAIN 修改为你的实际域名"
has_error=1
fi
if [[ -z "${CERTBOT_EMAIL:-}" ]] || [[ "${CERTBOT_EMAIL}" == "admin@example.com" ]]; then
error "请在 .env 中将 CERTBOT_EMAIL 修改为你的实际邮箱"
has_error=1
fi
if [[ -z "${ADMIN_TOKEN:-}" ]]; then
error "ADMIN_TOKEN 未设置"
has_error=1
fi
[ "$has_error" -eq 1 ] && { error "请修改 .env 后重新运行"; exit 1; }
log "配置检查通过"
log " 域名: ${VAULTWARDEN_DOMAIN}"
log " 邮箱: ${CERTBOT_EMAIL}"
log " 注册: ${SIGNUPS_ALLOWED:-true}"
setup_image_version
}
create_dirs() {
step "创建数据目录"
local data_dir="${VAULTWARDEN_DATA_DIR:-/var/lib/vaultwarden}"
local backup_dir="${BACKUP_DIR:-/var/backups/vaultwarden}"
mkdir -p "$data_dir" "$backup_dir"
log "数据目录: $data_dir"
log "备份目录: $backup_dir"
}
# 确认容器真的跑在配置指定的镜像上,避免「配置改了但容器没重建」这类静默失败
verify_running_version() {
local cid actual expect="${VAULTWARDEN_IMAGE:-}"
cid=$(docker compose ps -a -q vaultwarden 2>/dev/null | head -1)
[ -n "$cid" ] || return 0
actual=$(docker inspect "$cid" --format '{{.Config.Image}}' 2>/dev/null || true)
if [ -n "$expect" ] && [ "$actual" != "$expect" ]; then
warn "容器实际镜像 (${actual}) 与配置 (${expect}) 不一致"
warn "请检查: docker compose up -d --force-recreate"
return 0
fi
log " 运行镜像: ${actual}"
}
start_services() {
step "启动 Vaultwarden 服务"
log "正在拉取镜像..."
docker compose pull
# --force-recreate:容器处于 stopped 状态时,up -d 只会把原容器重新 start,
# 不会因镜像或环境变量变更而重建,导致「配置改了但没生效」。
log "正在启动容器..."
docker compose up -d --force-recreate
local port="${VAULTWARDEN_PORT:-8080}"
log "等待 Vaultwarden 就绪..."
local max_wait=30
for _ in $(seq 1 "$max_wait"); do
if curl -sf "http://127.0.0.1:${port}/alive" &> /dev/null; then
log "Vaultwarden 启动成功!"
verify_running_version
return
fi
sleep 2
done
warn "Vaultwarden 可能仍在启动中,请稍后检查: docker compose logs -f"
}
show_info() {
set -a; source .env; set +a
echo ""
echo -e "${GREEN}╔══════════════════════════════════════════════════════════╗${NC}"
echo -e "${GREEN}║ Vaultwarden 部署完成! ║${NC}"
echo -e "${GREEN}╠══════════════════════════════════════════════════════════╣${NC}"
echo -e "${GREEN}${NC}"
echo -e "${GREEN}${NC} Web 访问: ${CYAN}https://${VAULTWARDEN_DOMAIN}${NC}"
echo -e "${GREEN}${NC} 管理后台: ${CYAN}https://${VAULTWARDEN_DOMAIN}/admin${NC}"
echo -e "${GREEN}${NC}"
echo -e "${GREEN}${NC} 数据目录: ${VAULTWARDEN_DATA_DIR:-/var/lib/vaultwarden}"
echo -e "${GREEN}${NC} 备份目录: ${BACKUP_DIR:-/var/backups/vaultwarden}"
echo -e "${GREEN}${NC}"
echo -e "${GREEN}${NC} ${YELLOW}管理员令牌保存在 .env 文件的 ADMIN_TOKEN 中${NC}"
echo -e "${GREEN}${NC}"
echo -e "${GREEN}${NC} 客户端下载:"
echo -e "${GREEN}${NC} 浏览器插件: ${CYAN}https://bitwarden.com/download/${NC}"
echo -e "${GREEN}${NC} 桌面客户端: ${CYAN}https://bitwarden.com/download/${NC}"
echo -e "${GREEN}${NC} 移动端: ${CYAN}https://bitwarden.com/download/${NC}"
echo -e "${GREEN}${NC}"
echo -e "${GREEN}${NC} ${RED}⚠ 客户端连接时,服务器 URL 填写:${NC}"
echo -e "${GREEN}${NC} ${CYAN} https://${VAULTWARDEN_DOMAIN}${NC}"
echo -e "${GREEN}${NC}"
if [[ "${SIGNUPS_ALLOWED:-true}" == "true" ]]; then
echo -e "${GREEN}${NC} ${YELLOW}⚠ 注册功能已开启,注册完账号后建议关闭:${NC}"
echo -e "${GREEN}${NC} ${YELLOW} 修改 .env 中 SIGNUPS_ALLOWED=false${NC}"
echo -e "${GREEN}${NC} ${YELLOW} 然后 docker compose up -d(不是 restart${NC}"
fi
echo -e "${GREEN}${NC}"
echo -e "${GREEN}╚══════════════════════════════════════════════════════════╝${NC}"
echo ""
echo "常用命令:"
echo " 查看日志: cd $SCRIPT_DIR && docker compose logs -f"
echo " 重启服务: cd $SCRIPT_DIR && docker compose restart"
echo " 停止服务: cd $SCRIPT_DIR && docker compose down"
echo " 备份数据: cd $SCRIPT_DIR && bash backup.sh"
echo ""
}
# =============================================================
# 主流程
# =============================================================
main() {
echo -e "${CYAN}"
echo " __ __ _ _"
echo " \\ \\ / /_ _ _ _| | |___ ____ _ _ __ ____| | ___ _ __"
echo " \\ \\ / / _\` | | | | | __\\ \\ /\\ / / _\` | '__/ _ | |/ _ \\ '_ \\"
echo " \\ V / (_| | |_| | | |_ \\ V V / (_| | | | (_| | __/ | | |"
echo " \\_/ \\__,_|\\__,_|_|\\__| \\_/\\_/ \\__,_|_| \\__,_|\\___|_| |_|"
echo -e "${NC}"
echo ""
check_root
load_base_env "$BASE_DIR"
# Step 1: 系统初始化
init_system
# Step 2: 安装 Docker
install_docker
# Step 3: 安装 Nginx
install_nginx
# Step 4: 初始化配置
init_env
# Step 5: 配置 Docker 镜像加速
configure_docker_mirrors
# Step 6: 创建数据目录
create_dirs
# Step 7: 配置防火墙
setup_firewall_base
# Step 8: 配置 SSL 证书
setup_ssl_cert "${VAULTWARDEN_DOMAIN}" "${CERTBOT_EMAIL}" "vaultwarden"
# Step 9: 部署 Nginx 反向代理
deploy_nginx_conf "$SCRIPT_DIR/nginx/vaultwarden.conf" "${VAULTWARDEN_DOMAIN}" "vaultwarden"
# Step 10: 启动服务
start_services
# 显示部署信息
show_info
log "===== Vaultwarden 部署完成 ====="
}
main "$@"