tinyauth:Go 认证中间件,环境变量搞定反向代理 SSO
自托管玩家大概都经历过这个场景:你有十几个自托管应用(Nextcloud、Gitea、Jellyfin、Home Assistant……),每个都有自己的登录页面。你想统一管理访问权限,但一看 Authentik——Docker Compose 五个容器、PostgreSQL 数据库、Django 管理后台,光配置就花半天。
tinyauth 的定位是"你能找到的最小的认证授权服务器"。一个 Go 静态二进制,只用环境变量配置,SQLite 存储,Traefik/Caddy/Nginx/Envoy 四种反向代理通吃。15 个月内从零到 7,239 Star,说明自托管社区对"极简认证"有真实需求。
安装
# docker-compose.yml — 最小可运行配置
services:
traefik:
image: traefik:v3.6
command: --api.insecure=true --providers.docker
ports:
- "80:80"
volumes:
- /var/run/docker.sock:/var/run/docker.sock
whoami:
image: traefik/whoami:latest
labels:
traefik.enable: true
traefik.http.routers.whoami.rule: Host(`whoami.example.com`)
traefik.http.routers.whoami.middlewares: tinyauth
tinyauth:
image: ghcr.io/steveiliop56/tinyauth:v5
environment:
- TINYAUTH_APPURL=https://tinyauth.example.com
- TINYAUTH_AUTH_USERS=user:$$2a$$10$$UdLYoJ5lgPsC0RKqYH/jMua7zIn0g9kPqWmhYayJYLaZQ/FTmH2/u
volumes:
- ./data:/data
labels:
traefik.enable: true
traefik.http.routers.tinyauth.rule: Host(`tinyauth.example.com`)
traefik.http.middlewares.tinyauth.forwardauth.address: http://tinyauth:3000/api/auth/traefik
三个服务,全部在 Docker 里跑。用户访问 whoami.example.com 时,Traefik 先把请求转发给 tinyauth 做认证,认证通过才放行。不通过则 302 重定向到 tinyauth 登录页。
认证方式
本地用户
# 密码用 bcrypt 哈希
TINYAUTH_AUTH_USERS=user:$2a$10$xxx,admin:$2a$10$yyy
# 或者用文件
TINYAUTH_AUTH_USERSFILE=/data/users.txt
OAuth(Google/GitHub/GitLab/任意 OIDC)
TINYAUTH_OAUTH_PROVIDERS_google_CLIENTID=xxx
TINYAUTH_OAUTH_PROVIDERS_google_CLIENTSECRET=yyy
TINYAUTH_OAUTH_WHITELIST=gmail.com # 限制邮箱域名
用户点击"Sign in with Google",完成 OAuth 流程后自动创建会话。OAUTH_WHITELIST 确保只有 @gmail.com 的用户能登录。
LDAP
TINYAUTH_LDAP_ADDRESS=ldaps://ldap.example.com:636
TINYAUTH_LDAP_BINDDN=cn=admin,dc=example,dc=com
TINYAUTH_LDAP_BINDPASSWORD=secret
TINYAUTH_LDAP_BASEDN=ou=users,dc=example,dc=com
TINYAUTH_LDAP_SEARCHFILTER=(uid=%s)
支持 mTLS 客户端证书认证:
TINYAUTH_LDAP_AUTHCERT=/certs/ldap-client.crt
TINYAUTH_LDAP_AUTHKEY=/certs/ldap-client.key
TOTP 二步验证
本地用户支持 TOTP 2FA(基于 pquerna/otp 库)。登录时先验证密码,再要求输入 TOTP 验证码。QR 码通过 qrterminal 在终端显示。
OIDC Provider
tinyauth 不仅是 OAuth 客户端,它自己就是一个 OIDC Provider——可以让其他应用通过 OIDC 协议向 tinyauth 认证:
# 自动生成 RSA 2048 密钥对(或指定路径)
TINYAUTH_OIDC_PRIVATEKEYPATH=/data/oidc_key
TINYAUTH_OIDC_PUBLICKEYPATH=/data/oidc_key.pub
# 注册 OIDC 客户端
TINYAUTH_OIDC_CLIENTS_myapp_CLIENTID=myapp
TINYAUTH_OIDC_CLIENTS_myapp_CLIENTSECRET=secret
OIDC 端点:
POST /api/oidc/authorize # 授权端点
POST /api/oidc/token # Token 端点
GET /api/oidc/userinfo # 用户信息端点
GET /.well-known/openid-configuration # 发现文档
支持 Authorization Code Flow + PKCE + Refresh Token。JWT 用 RS256 签名。
访问控制
tinyauth 的访问控制是按应用粒度的,支持两种配置方式。
方式一:环境变量
# 应用 "git" 只有 alice 和 bob 能访问
TINYAUTH_APPS_git_CONFIG_DOMAIN=git.example.com
TINYAUTH_APPS_git_USERS_ALLOW=alice,bob
TINYAUTH_APPS_git_OAUTH_GROUPS=developers
TINYAUTH_APPS_git_IP_ALLOW=10.0.0.0/8
方式二:Docker Labels
jellyfin:
image: jellyfin/jellyfin
labels:
tinyauth.http.middlewares.tinyauth.forwardauth.address: http://tinyauth:3000/api/auth/traefik
tinyauth.users.allow: "alice,bob"
tinyauth.oauth.groups: "media-users"
ACL 字段
| 字段 | 说明 |
|---|---|
| Users.Allow/Block | 用户名过滤(支持 glob) |
| OAuth.Whitelist | 邮箱域名白名单 |
| OAuth.Groups | 要求的 OAuth 分组 |
| LDAP.Groups | 要求的 LDAP 分组 |
| IP.Allow/Block/Bypass | IP/CIDR 过滤 |
| Path.Allow/Block | 正则路径过滤 |
| Response.Headers | 自定义响应头 |
| Response.BasicAuth | 注入上游 Basic Auth |
IP.Bypass 特别实用——内网 IP 可以跳过认证直接访问,外部访问则需要登录。
Forward Auth 原理
tinyauth 的核心机制是 Forward Auth(转发认证)。不是所有请求都经过 tinyauth,而是反向代理在每个请求前先问 tinyauth:"这个用户能不能访问?"
浏览器 → Traefik → tinyauth/api/auth/traefik
↓ ↓
检查 Cookie 验证会话
↓ ↓
已认证 → 放行 未认证 → 302 到登录页
↓
转发到后端应用
四种代理适配:
| 代理 | 认证方式 | 读取的 Header |
|---|---|---|
| Traefik | ForwardAuth | X-Forwarded-Host/URI/Proto |
| Caddy | ForwardAuth | 同 Traefik |
| Envoy | ExtAuthz | X-Forwarded-Proto + query path |
| Nginx | AuthRequest | X-Original-URL |
认证通过后,tinyauth 设置这些 Header 传给后端应用:
Remote-User: alice
Remote-Name: Alice Smith
Remote-Email: alice@example.com
Remote-Groups: developers,admins
后端应用可以读取这些 Header 获取用户信息,不需要自己实现认证。
Go 实现的技术亮点
Gin 框架 + 中间件链
tinyauth 使用 Gin 作为 HTTP 框架,中间件链清晰:
请求 → zerolog 中间件(日志)→ context 中间件(会话验证)→ 路由处理器
context_middleware.go 是认证的核心:
- 检查会话 Cookie(默认 24h 有效)
- 如果 TOTP 待验证,限制访问
- OAuth 用户验证邮箱域名
- LDAP 用户获取分组信息(缓存 900s)
sqlc + golang-migrate
SQL 层面使用 sqlc 生成类型安全的 Go 代码:
-- sql/session_queries.sql
-- name: GetSession :one
SELECT * FROM sessions WHERE uuid = ? AND expiry > strftime('%s','now');
// 自动生成
func (q *Queries) GetSession(ctx context.Context, uuid string) (Session, error)
数据库迁移用 golang-migrate,SQLite schema 管理规范化。
pure Go SQLite
// go.mod
modernc.org/sqlite v1.48.2
纯 Go 实现,不需要 CGO。CGO_ENABLED=0 编译出的静态二进制可以在任何 Linux 上运行,不依赖 glibc。SQLite 存储会话、OIDC codes/tokens、用户信息——所有数据都在一个文件里。
Docker SDK 读取 Labels
tinyauth 使用 Docker SDK(docker/docker)读取同一 Docker 网络上的容器 Labels:
// 自动发现带 tinyauth.* labels 的容器
// 不需要手动配置每个应用的域名
这让访问控制的配置贴近应用本身——在应用的 docker-compose.yml 里写 labels 就行。
总结
tinyauth 用极简的方式解决了自托管场景的统一认证问题:
- 一个静态二进制,零外部依赖
- 环境变量 + Docker Labels 双配置
- 本地/OAuth/LDAP/TOTP/OIDC 全支持
- 四种反代通吃,Forward Auth 标准机制
如果你也被十几个自托管应用的登录页折磨过,tinyauth 值得一试。
GitHub:https://github.com/steveiliop56/tinyauth