3201 字
16 分钟
用 Docker 自建 2FAuth,打造属于你的二步验证(2FA)云端保险库
2025-11-06

保姆级教程:用 Docker 自建 2FAuth,打造属于你的二步验证(2FA)云端保险库#

你是否也有这样的“安全焦虑”?#

我越来越依赖二步验证(2FA/TOTP)来保护我的重要账户。像 Google Authenticator 这样的 App 虽然方便,但却存在一个致命的“单点故障”:一旦手机丢失或损坏,存储在其中的所有 2FA 密钥将面临永久丢失的风险,找回账户的过程苦不堪言。

为了彻底解决这个问题,今天我将向大家介绍如何使用 Docker 部署一个开源的 2FA 管理工具——2FAuth。它将成为你所有二步验证密钥的云端保险库,让你从此高枕无忧。

项目地址: https://github.com/Bubka/2FAuth

通过本教程,你将获得:

  • 一个完全由你掌控的 2FA 密钥备份中心。
  • 一个美观易用的 Web 界面,随时随地获取动态口令。
  • 手机丢失后,从容恢复所有账户的能力。

准备好了吗?让我们开始吧!

准备工作#

在开始之前,请确保你已经拥有:

  1. 一台云服务器(VPS)或任何可以运行 Docker 的主机。
  2. 安装好了 Docker 和 Docker Compose。
  3. (强烈推荐)一个域名,用于配置 HTTPS 安全访问。

第一步:创建项目目录与配置文件#

首先,通过 SSH 登录你的服务器,创建一个专属的项目文件夹。

Terminal window
# 创建一个存放所有 Docker 项目的目录(如果还没有的话)
mkdir -p /data/docker
# 创建 2fauth 项目目录并进入
cd /data/docker
mkdir 2fauth
cd 2fauth

接下来,我们需要创建一个 docker-compose.yml 文件,这是部署的核心。

Terminal window
nano docker-compose.yml

将下面这份经过优化和详细注释的配置完整地粘贴进去。这份配置预设了安全选项、邮件通知等常用功能,你只需要修改少量个人信息即可。

# --- 在这里粘贴你的 docker-compose.yml 配置 ---
# 使用 Docker Compose 的文件格式版本,'3.7' 是一个稳定且常用的版本。
version: '3.7'
# 'services' 关键字,下面定义了我们要运行的一个或多个容器服务。
services:
# '2fauth' 是我们为这个服务起的名字,可以自定义。
2fauth:
# 指定要使用的 Docker 镜像。'2fauth/2fauth:latest' 表示使用官方最新的镜像。
image: 2fauth/2fauth:latest
# 给运行的容器起一个固定的名字,方便我们用 `docker logs 2fauth` 等命令管理它。
container_name: 2fauth
# 设置容器的重启策略。'unless-stopped' 意味着除非你手动停止它,否则容器总会自动重启(比如 Docker 服务重启或容器意外崩溃时)。
restart: unless-stopped
# 'volumes' 定义了数据持久化的方式,这是 Docker 最重要的部分之一。
volumes:
# 将宿主机当前目录下的 'data' 文件夹,映射到容器内部的 '/2fauth' 文件夹。
# 这样做可以保证容器删除重建后,所有数据(用户、密钥、配置)都得以保留。
# 备份这个 'data' 文件夹就等于备份了整个应用。
- ./data:/2fauth
# 'ports' 定义了端口映射规则。
ports:
# 将宿主机的 8082 端口映射到容器内部的 8000 端口。
# 格式是 '宿主机端口:容器端口'。
# 这意味着我们通过访问 `http://宿主机IP:8082` 来访问容器里的服务。
- "8082:8000"
# 'environment' 用来设置容器内部的环境变量,这是配置应用行为的主要方式。
environment:
#### 基础与个性化设置 ####
# 应用名称,会显示在网页标题和邮件中。
- APP_NAME=我的2FA安全令牌
# 应用的公开访问URL。必须与你的实际访问地址完全一致,否则CSS/JS等资源可能加载失败。
# 部署到公网并使用HTTPS反代后,务必改成 "https://你的域名"。
- APP_URL=http://192.168.5.206:8082
# 应用的加密密钥,必须是32个字符的随机字符串。用于加密数据库中的敏感信息。
# !!极其重要!!一旦设定,绝不要更改,否则所有已存数据将无法解密!
- APP_KEY=0bdc8a4744cd8a9f8857f621710d4e05
# 设置容器的时区,以确保动态口令(TOTP)的时间计算正确。
- TZ=Asia/Shanghai
# 网站所有者的邮箱,可能会显示在应用的某些地方。
- SITE_OWNER=your_email@gmail.com
#### 安全强化设置 ####
# 开启内容安全策略(CSP),一个重要的浏览器安全功能,能有效防止跨站脚本(XSS)攻击。
- CONTENT_SECURITY_POLICY=true
# 登录节流(防暴力破解)。限制在1分钟内,同一用户最多允许登录失败的次数。
- LOGIN_THROTTLE=5
# 登录认证日志的保留天数。超过这个天数的旧日志会被自动删除。
- AUTHENTICATION_LOG_RETENTION=365
#### 邮件通知设置 (SMTP) ####
# 邮件发送驱动。'smtp' 表示通过标准的SMTP服务器发送邮件。
- MAIL_MAILER=smtp
# 你的SMTP服务器地址。
- MAIL_HOST=smtp.gmail.com # 替换成你的 SMTP 服务器地址
# 你的SMTP服务器端口。
- MAIL_PORT=465
# 邮件发送的加密方式。465端口通常使用 'ssl',587端口通常使用 'tls'。
- MAIL_ENCRYPTION=ssl
# 用于登录SMTP服务器的邮箱账户名。
- MAIL_USERNAME=your_email@gmail.com # 你的邮箱账户
# 用于登录SMTP服务器的密码。注意:对于Gmail/QQ邮箱等,这里应填写“应用专用密码”。
- MAIL_PASSWORD=your_app_password # 你的邮箱密码或应用专用密码
# 邮件中显示的发件人名称。'${APP_NAME}' 表示自动使用上面设置的应用名称。
- MAIL_FROM_NAME=${APP_NAME}
# 邮件中显示的发件人地址。
- MAIL_FROM_ADDRESS=your_email@gmail.com # 发件人地址,通常和用户名一致
#### 其他保持默认即可的优化设置 ####
# 应用运行环境,'local' 或 'production'。'production' 模式下出错不会显示详细调试信息,更安全。
- APP_ENV=local
# 是否开启调试模式的总开关,'false' 是生产环境的最佳实践。
- APP_DEBUG=false
# 日志记录方式,'daily' 表示每天一个日志文件,自动轮换,防止单个文件过大。
- LOG_CHANNEL=daily
# 日志记录级别,'notice' 是一个很好的平衡点,既不会太啰嗦也不会漏掉重要信息。
- LOG_LEVEL=notice
# 数据库连接类型,'sqlite' 是一个轻量级的单文件数据库,无需额外服务。
- DB_CONNECTION=sqlite
# SQLite数据库文件在容器内的路径。我们已通过 volume 映射,无需关心此路径。
- DB_DATABASE="/srv/database/database.sqlite"
# 缓存驱动,'file' 表示使用文件系统来存储缓存。
- CACHE_DRIVER=file
# Session(用户登录状态)驱动,'file' 表示使用文件来存储Session。
- SESSION_DRIVER=file
# 设置信任的代理服务器。'*' 表示信任所有代理,这在使用反向代理时非常重要,能让应用获取到用户的真实IP地址。
- TRUSTED_PROXIES=*

在粘贴后,请务必修改以下几项:

  1. APP_URL: 极其重要!http://192.168.5.206:8082 修改为你的服务器公网 IP 和端口,或者你计划使用的域名(推荐)。例如:https://2fa.yourdomain.com
  2. APP_KEY: 这是一个 32 位的加密密钥。请在服务器上执行 openssl rand -hex 16 命令生成一个,并替换掉默认值。此密钥一旦设定,切勿更改!
  3. SITE_OWNER, MAIL_USERNAME, MAIL_PASSWORD, MAIL_FROM_ADDRESS: 替换成你自己的 SMTP 邮箱信息,用于接收安全通知和重置密码。

第二步:修复文件权限并启动服务#

Docker 容器内的程序通常以非 root 用户运行,为了避免因权限问题导致容器无限重启,我们需要提前创建数据目录并赋予正确的权限。

Terminal window
# 创建数据目录
mkdir data
# 将 data 目录的所有者更改为容器内部用户 ID (1000)
sudo chown -R 1000:1000 data

现在,万事俱备,启动 2FAuth 服务!

Terminal window
docker-compose up -d

等待几十秒,Docker 会自动拉取镜像并启动容器。你可以使用 docker ps 命令查看容器状态,如果 STATUS 显示为 Up ...,恭喜你,服务已经成功运行了!

第三步:配置反向代理(安全必备)#

直接通过 IP 和端口访问你的 2FA 服务是极不安全的。我们必须使用 Nginx 等工具进行反向代理,并配置免费的 SSL 证书,实现 HTTPS 加密访问。

这里以 宝塔面板 为例:

  1. 添加站点:在宝塔面板中,添加一个站点,域名填写你为 2FAuth 规划的域名(例如 2fa.yourdomain.com)。
  2. 申请SSL证书:进入站点设置,申请 Let’s Encrypt 证书,并开启强制HTTPS
  3. 设置反向代理
    • 在“反向代理”选项卡中,添加一个新的代理。
    • 目标URL 填写:http://127.0.0.1:8082 (这里的 8082 是你在 docker-compose.yml 中设置的宿主机端口)。
    • 提交保存。

现在,你应该可以通过 https://2fa.yourdomain.com 安全地访问你的 2FAuth 服务了。

反向代理配置文件示例:

location / {
# 代理到你的 2FAuth 容器所监听的 本机IP:端口
proxy_pass http://127.0.0.1:8082;
# --- WebSocket 支持 (关键!) ---
# 允许协议从 HTTP 升级到 WebSocket
proxy_set_header Upgrade $http_upgrade;
# 设置 Connection 头,完成 WebSocket 握手
proxy_set_header Connection "upgrade";
# --- 传递真实信息给后端 ---
# 传递真实的 Host 头,让后端应用知道用户访问的是哪个域名
proxy_set_header Host $host;
# 传递用户的真实 IP 地址
proxy_set_header X-Real-IP $remote_addr;
# 传递完整的代理链 IP
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# 传递协议信息 (http or https)
proxy_set_header X-Forwarded-Proto $scheme;
}

第四步:开始使用你的 2FA 保险库#

  1. 注册账户:通过你的域名访问 2FAuth,点击 Register 创建你的主账户。
  2. 添加密钥:登录后,点击 Add new OTP。你可以通过上传二维码截图手动输入密钥字符串的方式,将你在其他网站的二步验证密钥添加到 2FAuth 中。
  3. 数据迁移:如果你想从 Google Authenticator 迁移,可以在 Google Authenticator 中选择“转移账户”->“导出”,然后将生成的二维码截图,再到 2FAuth 中上传这张截图来添加。

强烈建议:配置自动备份#

数据是无价的。建议使用服务器的计划任务功能(如 cron 或宝塔面板的计划任务),每日自动打包备份你的 ./data 文件夹。

宝塔面板 Shell 脚本示例:

Terminal window
# 备份文件存放路径
BACKUP_DIR="/www/backup/2fauth"
# 2FAuth 的数据源路径
SOURCE_DIR="/data/docker/2fauth/data"
# 带日期的文件名
BACKUP_FILE="$BACKUP_DIR/2fauth_backup_$(date +%Y%m%d).tar.gz"
# 创建备份目录
mkdir -p $BACKUP_DIR
# 打包并压缩
tar -zcvf $BACKUP_FILE -C $(dirname $SOURCE_DIR) $(basename $SOURCE_DIR)
# 删除7天前的旧备份
find $BACKUP_DIR -name "2fauth_backup_*.tar.gz" -mtime +7 -exec rm {} \;

总结#

通过以上步骤,你就拥有了一个安全、可靠、私有的二步验证密钥备份中心。从今往后,你可以放心地在任何新服务上开启 2FA,因为你知道,即使手机不在身边,你也永远不会失去对账户的控制权。


补充#

“通过域名访问,禁止 IP 访问”

“默认 docker 的应用都是可以 ip+端口访问,而且不需要在防火墙开放端口的” 也完全正确。这背后的原因我们之前也探讨过:Docker 会直接操作 iptables,它的网络优先级高于很多系统防火墙(如 ufw),所以即使 ufw 没放行,Docker 映射的端口也可能已经暴露在公网上了。

解决方案:修改 Docker 端口绑定地址#

这是最简单、最有效、最推荐的方法。它的原理是,在做端口映射时,我们不让 Docker 监听所有网络接口(0.0.0.0),而是只让它监听本机的回环地址(127.0.0.1localhost

  • 127.0.0.1 是一个特殊的 IP 地址,它只代表“这台机器自己”。
  • 任何来自外部网络(局域网或公网)的请求,都无法访问到这个地址。
  • 但是,本机上运行的其他程序(比如你安装在同一台机器上的宝塔面板/Nginx)可以访问它。

操作步骤:

  1. 打开你的 docker-compose.yml 文件。
  2. 找到 ports 部分。
  3. 将端口映射的格式从 "宿主机端口:容器端口" 修改为 "127.0.0.1:宿主机端口:容器端口"

以 2FAuth 为例:

修改前:

ports:
- "8082:8000"

修改后:

ports:
- "127.0.0.1:8082:8000"
  1. 保存文件后,执行 docker-compose up -d 重启容器。

效果:

  • 用 IP 访问:现在你再尝试用 http://你的公网IP:8082 或者 http://你的内网IP:8082 访问,会直接收到 ERR_CONNECTION_REFUSED(连接被拒绝)。因为 Docker 根本就没有在你的公网/内网网卡上监听 8082 端口。目标达成!
  • 通过域名访问
    • 你的反向代理(宝塔 Nginx)和 Docker 容器在同一台机器上。
    • 当用户访问 https://2fa.yourdomain.com 时,请求先到达 Nginx。
    • Nginx 会把请求转发给 http://127.0.0.1:8082
    • 因为 Nginx 是“本机程序”,所以它可以成功访问到 Docker 监听的 127.0.0.1 地址。
    • 访问完全正常!

这个方法釜底抽薪,从根源上关闭了 IP 直接访问的通道,同时保证了反向代理的正常工作。

用 Docker 自建 2FAuth,打造属于你的二步验证(2FA)云端保险库
https://blog.wlens.top/posts/保姆级教程用-docker-自建-2fauth打造属于你的二步验证2fa云端保险库/
作者
Lao Wang
发布于
2025-11-06
许可协议
CC BY-NC-SA 4.0