- 8.0 from Ubuntu archive, 8.4 from MySQL official APT repo - 5.6/5.7 not supported for now; enforce one MySQL series per host - Sync Go version whitelist with the playbook package map
AuthServer
最小 Gin + MySQL 权限服务实践。
当前能力
- Gin HTTP 服务
- MySQL / GORM 连接
- 核心表 AutoMigrate
- 默认环境和身份源初始化
- 健康检查
- 本地登录接口骨架
- LDAP / 内部 SSO provider 预留
- 权限判断接口骨架
- 管理员用户、用户组、角色、权限、授权绑定 API
- 业务线、namespace、cluster 管理 API
- Rancher 同步预览接口
- 审计日志落库
- Vue 管理后台
启动
cp .env.example .env
go mod tidy
go run ./cmd/server
本地 Docker MySQL 8.0.46:
docker compose -f docker-compose.mysql.yml up -d
这个 MySQL 会映射到本机 3000 端口,.env.example 默认 DSN 已按这个端口配置:
MYSQL_DSN=auth:auth@tcp(127.0.0.1:3000)/authserver?charset=utf8mb4&parseTime=True&loc=Local
连接测试:
mysql -h127.0.0.1 -P3000 -uauth -pauth authserver
前端开发服务:
cd web
npm install
npm run dev
默认访问:
http://127.0.0.1:5173
Vite 会把 /api 代理到后端 http://127.0.0.1:8080。
如果 MySQL 还没有创建库和账号,可以用 root 执行:
mysql -uroot -p < docs/mysql-init.sql
如果你使用的是 Docker Desktop、OrbStack、Colima 或容器里的 MySQL,应用访问 MySQL 时来源 IP 可能不是 localhost,而是类似 192.168.65.1。这时 MySQL 账号需要允许远程来源,docs/mysql-init.sql 已经包含 'auth'@'%'。
默认启用 SSO:
SSO_ENABLED=true
如果只是本地开发测试,可以关闭 SSO,前端会显示用户名登录框。后端只校验用户名非空;用户不存在时会自动创建本地用户,第一位用户会自动成为管理员:
MYSQL_DSN='auth:auth@tcp(127.0.0.1:3306)/authserver?charset=utf8mb4&parseTime=True&loc=Local' \
JWT_SECRET='change-this-secret' \
SSO_ENABLED=false \
go run ./cmd/server
发版
发布脚本会在本地完成:
- 构建后端和前端 Docker 镜像
- 读取本地
.env.server,生成并应用线上ConfigMap/Secret - 导出
authserver-images-<version>.tar - 上传到服务器
- 导入 RKE2 使用的
containerd - 更新
authservernamespace 下的 deployment 并等待 rollout 完成
默认目标与你当前线上环境一致:
- 主机:
root@218.11.5.223 - 私钥:
/Users/mac/dev/xengineer-cs2.pem - 远端目录:
/authserver - containerd socket:
/run/k3s/containerd/containerd.sock
发布脚本默认把仓库根目录的 .env.server 当作服务器配置源;脚本会在发版时重新生成并应用 authserver-config 和 authserver-secret。本地开发用的 .env 不会被默认发布。
脚本默认不会拦截测试环境配置。如果你要对正式环境启用更严格的保护,可以显式打开:
STRICT_DEPLOY_ENV=true bash scripts/release.sh 1.0.5
开启后会拦截几类明显错误:
APP_ENV != prodMYSQL_DSN指向127.0.0.1或localhostPUBLIC_BASE_URL、SAML_ENTITY_ID、SAML_ACS_URL、WAYEN_LOGIN_URL、WAYEN_TARGET_URL指向127.0.0.1或localhost
执行方式:
bash scripts/release.sh 1.0.5
如果需要覆盖默认值,可以传环境变量:
ENV_FILE=.env.server \
DEPLOY_HOST=218.11.5.223 \
DEPLOY_KEY=/path/to/key.pem \
REMOTE_DIR=/authserver \
bash scripts/release.sh 1.0.5
如果只是想在服务器上重启已部署的服务,可以执行:
bash scripts/restart-authserver.sh
也支持只重启单个 deployment:
bash scripts/restart-authserver.sh backend
bash scripts/restart-authserver.sh nginx
接口
| 方法 | 路径 | 说明 |
|---|---|---|
GET |
/healthz |
服务健康检查 |
GET |
/readyz |
数据库连接检查 |
GET |
/auth/api/v1/config |
前端登录模式配置,返回 sso_enabled |
POST |
/auth/api/v1/login |
本地开发测试登录,仅 SSO_ENABLED=false 时可用,只需用户名 |
POST |
/api/v1/login/ldap |
LDAP 登录预留 |
GET |
/api/v1/login/:provider |
SSO 跳转预留 |
GET |
/api/v1/login/:provider/callback |
SSO 回调预留 |
POST |
/api/v1/authz/check |
权限判断 |
GET |
/api/v1/users/me |
当前用户 |
GET |
/api/v1/wayen/login |
根据 token 邮箱登录 Wayen 并跳转 |
GET |
/api/v1/saml/metadata |
SAML SP metadata |
GET |
/api/v1/login/internal-sso |
发起 SAML SSO 登录 |
POST |
/api/v1/saml/acs |
SAML ACS 回调,当前仅调试打印 |
GET |
/auth/.well-known/openid-configuration |
OIDC Discovery 配置 |
GET |
/auth/oauth/authorize |
OAuth2 Authorization Code 授权入口,供 Wayne 使用 |
POST |
/auth/oauth/token |
OAuth2 code 换 access token |
GET |
/auth/oauth/userinfo |
OAuth2 bearer token 查询当前用户 |
GET |
/auth/oauth/jwks |
OIDC JWKS 公钥 |
SAML Metadata
服务会根据 .env 和 certs/sp.crt 动态生成 SP metadata,不需要手工维护 certs/spmeta。
发起 SSO 登录时会实时请求 .env 里的 SAML_IDP_METADATA_URL,不再读取本地 certs/idpmeta。
本地后端如果运行在 8083,建议配置:
HTTP_ADDR=:8083
PUBLIC_BASE_URL=http://localhost:8083
SAML_ENTITY_ID=http://localhost:8083/api/v1/saml/metadata
SAML_ACS_URL=http://localhost:8083/api/v1/saml/acs
SAML_IDP_METADATA_URL=http://sso-internal.dev.qiniu.io/saml2/meta
SAML_SP_CERT_FILE=certs/sp.crt
SAML_SP_KEY_FILE=certs/sp.key
给 IdP 配置的 SP metadata 地址:
http://localhost:8083/api/v1/saml/metadata
调试 SSO 登录入口:
http://localhost:8083/api/v1/login/internal-sso
ACS 会处理 IdP 回调:
POST /api/v1/saml/acs
收到 IdP 回调后,会把 SAMLResponse 解码;如果 Assertion 被加密,会用 SAML_SP_KEY_FILE 对应私钥解密,并在后端控制台打印解密后的摘要和 Assertion:
- Response ID
- InResponseTo
- Issuer
- Destination
- NameID
- Attributes
- 解密后的 Assertion XML
当前 SAML 用户落库规则:
eduPersonPrincipalName同时作为users.username和users.email。NameID写入users.external_id。- 首次登录会自动创建
source=saml的用户。 - 不再创建
identity_providers/user_identities表记录。 - 登录成功后签发 JWT,写入
authserver_tokenHttpOnly cookie,并带着 token 跳回前端。
当前阶段还没有做:
- SAML 签名验证
Wayne OAuth2 对接
方案 1 下 AuthServer 作为 OAuth2 Provider,Wayne 作为 OAuth2 Client。Wayne 登录时不再校验 AuthServer 里的 Wayne 密码,也不需要 AuthServer 存储 Wayne 资源组信息;AuthServer 只负责 SAML 登录、签发 token、返回 name/email/display,Wayne 收到 userinfo 后会在 Wayne 自己数据库里创建或更新本地用户,Wayne 的资源组和权限仍由 Wayne 自己维护。
AuthServer 端配置:
OAUTH_WAYNE_CLIENT_ID=wayne
OAUTH_WAYNE_CLIENT_SECRET=change-this-wayne-client-secret
OAUTH_WAYNE_REDIRECT_URI=http://127.0.0.1:8080/login/oauth2/oauth2
WAYEN_OAUTH_REF=/portal/namespace/1/app
OAUTH_CODE_TTL_SECONDS=120
AuthServer 暴露给 Wayne 的 OAuth2 地址:
GET /auth/oauth/authorize
POST /auth/oauth/token
GET /auth/oauth/userinfo
OIDC Discovery 里的 endpoint 默认由 OIDC_ISSUER 拼接,也可以按 endpoint 单独覆盖。浏览器需要访问 OIDC_AUTHORIZATION_ENDPOINT,后端系统通常访问 OIDC_TOKEN_ENDPOINT、OIDC_USERINFO_ENDPOINT 和 OIDC_JWKS_URI。
OIDC_ISSUER=http://auth.example.com/auth
OIDC_AUTHORIZATION_ENDPOINT=http://auth.example.com/auth/oauth/authorize
OIDC_TOKEN_ENDPOINT=http://auth-internal.example.com/auth/oauth/token
OIDC_USERINFO_ENDPOINT=http://auth-internal.example.com/auth/oauth/userinfo
OIDC_JWKS_URI=http://auth-internal.example.com/auth/oauth/jwks
CLOUDDM_TARGET_URL=http://authserver-nginx/internal/clouddm
CLOUDDM_TARGET_URL 用于 AuthServer 后端请求 CloudDM /requestJumpUrl。在 k8s 内建议指向 AuthServer nginx 的内部代理路径,由 nginx 转发到 CloudDM Service,并把 Host 固定成 CloudDM 公网入口,确保 CloudDM 生成浏览器可访问的 callback。
Wayne app.conf 示例:
[auth.oauth2]
enabled = true
redirect_url = http://127.0.0.1:8080
client_id = wayne
client_secret = change-this-wayne-client-secret
auth_url = http://218.11.5.223/auth/oauth/authorize
token_url = http://218.11.5.223/auth/oauth/token
api_url = http://218.11.5.223/auth/oauth/userinfo
api_mapping = name:name,email:email,display:display
scopes = profile,email
Wayne 会把回调地址拼成:
{redirect_url}/login/oauth2/oauth2
因此 OAUTH_WAYNE_REDIRECT_URI 必须和 Wayne 实际回调地址完全一致。浏览器访问 Wayne OAuth 登录入口后,如果 AuthServer 还没有登录态,会先跳内部 SAML;SAML 成功后再回到 OAuth authorize,签发 code 给 Wayne。
WAYEN_OAUTH_REF 是 AuthServer 发起 Wayne 登录时写入 Wayne next 参数的登录完成页,默认 /portal/namespace/1/app,对应 Wayne DemoNamespaceId = 1 的默认 namespace。不要配置成 oauth 或 /oauth,否则 Wayne 回调会把它当成前端路由跳到 /oauth。
Wayne 授权代理接口
AuthServer 的 Wayne 授权代理接口要求调用方传目标 Wayne username。后端使用配置里的 Wayne 超级管理员账号登录 Wayne 原生 API,拿到 Wayne token 后存入数据库,后续代理请求都带 Authorization: Bearer <wayne_token>。token 过期或 Wayne 返回 401 时会重新调用 Wayne 登录接口获取 token 并重试一次。
对外接口:
GET /auth/api/v1/wayne/namespaces
GET /auth/api/v1/wayne/groups
GET /auth/api/v1/wayne/users/:username/roles
GET /auth/api/v1/wayne/namespaces/:namespaceid/operator-permissions
GET /auth/api/v1/wayne/apps/:appid/operator-permissions
PUT /auth/api/v1/wayne/namespaces/:namespaceid/roles
DELETE /auth/api/v1/wayne/namespaces/:namespaceid/roles
PUT /auth/api/v1/wayne/apps/:appid/roles
DELETE /auth/api/v1/wayne/apps/:appid/roles
示例:
PUT /auth/api/v1/wayne/namespaces/1/roles
Authorization: Bearer <authserver_token>
Content-Type: application/json
{
"username": "target@example.com",
"groupIds": [10, 11],
"replace": false,
"requestId": "req-001",
"reason": "grant namespace access"
}
AuthServer 转发到 Wayne 原生 API 时会先按 username 查询 Wayne 用户,拿到 Wayne user.id 后再查询目标 namespace/app 下的用户角色绑定记录:
GET /api/v1/users?name=target@example.com
GET /api/v1/namespaces/1/users?userId=<wayne_user_id>
如果绑定记录存在,会调用原生更新接口;不存在则调用原生创建接口:
POST /api/v1/namespaces/1/users
PUT /api/v1/namespaces/1/users/<namespace_user_id>
删除角色时会先查询绑定记录,再调用:
DELETE /api/v1/namespaces/1/users/<namespace_user_id>
相关配置:
WAYNE_API_BASE_URL=http://wayne-backend.demo.svc.cluster.local:8080
WAYNE_ADMIN_USERNAME=admin
WAYNE_ADMIN_PASSWORD=<wayne-admin-password>
WAYNE_TOKEN_TTL_MINUTES=1440
WAYNE_API_BASE_URL 未配置时会兼容读取旧的 WAYNE_INTERNAL_API_BASE_URL。Wayne admin token 会写入 wayne_tokens 表,服务重启后优先复用未过期 token。
子系统赋权接口
子系统赋权接口是平台业务层接口,前端应优先调用这一组,而不是直接调用低层 /wayne/* 代理。当前只实现 Wayne,CloudDM 先返回未启用占位。
权限规则:
- 平台管理员可以操作任意业务线。
- 非平台管理员必须是当前业务线管理员,也就是
business_line_users.permission = 0。 - Wayne 写操作前还会查询 Wayne
operator-permissions,确认当前登录用户在目标 namespace 下具备创建/更新/删除用户角色的权限。 - 用户首次加入业务线时,如果该业务线绑定了 Wayne namespace,会自动给该用户初始化 Wayne namespace
访客角色。
接口列表:
GET /auth/api/v1/subsystem-auth/systems
GET /auth/api/v1/subsystem-auth/wayne/roles
GET /auth/api/v1/subsystem-auth/wayne/business-lines/:id/namespaces
GET /auth/api/v1/subsystem-auth/wayne/users/:username/roles
PUT /auth/api/v1/subsystem-auth/wayne/business-lines/:id/namespaces/:namespaceid/users/:username/roles
DELETE /auth/api/v1/subsystem-auth/wayne/business-lines/:id/namespaces/:namespaceid/users/:username/roles
POST /auth/api/v1/subsystem-auth/wayne/business-lines/:id/users/:userid/init
Wayne 授权示例:
PUT /auth/api/v1/subsystem-auth/wayne/business-lines/1/namespaces/3/users/eastsales@qiniu.com/roles
Authorization: Bearer <authserver_token>
Content-Type: application/json
{
"groupIds": [2],
"replace": true,
"requestId": "req-001",
"reason": "业务线授权"
}
Wayne 解绑示例:
DELETE /auth/api/v1/subsystem-auth/wayne/business-lines/1/namespaces/3/users/eastsales@qiniu.com/roles
Authorization: Bearer <authserver_token>
Content-Type: application/json
{
"groupIds": [2],
"requestId": "req-002",
"reason": "回收业务线授权"
}
管理员可查看当前 SAML metadata 配置:
GET /api/v1/admin/saml/metadata/config
管理员接口
管理员接口需要 Authorization: Bearer <token>,并且 token 里的用户必须是 is_admin=true。
| 方法 | 路径 | 说明 |
|---|---|---|
GET |
/api/v1/admin/users |
用户列表 |
POST |
/api/v1/admin/users |
创建用户,可带本地密码 |
PUT |
/api/v1/admin/users/:id |
更新用户 |
DELETE |
/api/v1/admin/users/:id |
删除用户 |
GET |
/api/v1/admin/groups |
用户组列表 |
POST |
/api/v1/admin/groups |
创建用户组 |
POST |
/api/v1/admin/groups/:id/members |
用户加入组 |
DELETE |
/api/v1/admin/groups/:id/members/:userID |
用户移出组 |
GET |
/api/v1/admin/roles |
角色列表 |
POST |
/api/v1/admin/roles |
创建角色 |
PUT |
/api/v1/admin/roles/:id/permissions |
设置角色权限 |
GET |
/api/v1/admin/permissions |
权限点列表 |
POST |
/api/v1/admin/permissions |
创建权限点 |
GET |
/api/v1/admin/business-lines |
业务线列表 |
POST |
/api/v1/admin/business-lines |
创建业务线 |
GET |
/api/v1/admin/namespaces |
namespace 列表 |
POST |
/api/v1/admin/namespaces |
创建 namespace |
GET |
/api/v1/admin/environments |
环境列表 |
GET |
/api/v1/admin/clusters |
集群列表 |
POST |
/api/v1/admin/clusters |
创建集群 |
GET |
/api/v1/admin/wayen/credentials |
Wayen 凭据列表,不返回密码 |
POST |
/api/v1/admin/wayen/credentials |
按邮箱保存 Wayen 密码 |
DELETE |
/api/v1/admin/wayen/credentials/:id |
删除 Wayen 凭据 |
GET |
/api/v1/admin/role-bindings |
授权绑定列表 |
POST |
/api/v1/admin/role-bindings |
创建授权绑定 |
DELETE |
/api/v1/admin/role-bindings/:id |
删除授权绑定 |
POST |
/api/v1/admin/rancher/sync |
Rancher 同步预览 |
GET |
/api/v1/admin/rancher/sync-status |
Rancher 同步状态 |
GET |
/api/v1/admin/saml/metadata/config |
SAML metadata 配置 |
Rancher 对齐流程
当前 Rancher 同步接口是 dry-run,不会真实调用 Rancher API。推荐先按下面流程把本系统数据配齐:
- 创建环境和集群:
rke2-dev、rke2-test、rke2-prod。 - 创建业务线:例如
pay。 - 创建 namespace:例如
pay,归属支付业务线。 - 创建权限点:例如
deployment:deploy、deployment:rollback。 - 创建角色:例如
prod-deployer。 - 给角色绑定权限点。
- 创建用户组:例如
pay-prod-deployer。 - 把 SSO/LDAP 用户加入用户组。
- 创建授权绑定:
group + role + cluster + namespace。 - 调用
/api/v1/admin/rancher/sync查看将要同步到 Rancher 的映射。
核心授权绑定应该长这样:
{
"subject_type": "group",
"subject_id": 1,
"role_id": 1,
"scope_type": "cluster_namespace",
"cluster_id": 3,
"namespace_id": 1
}
这表示某个用户组在 prod 集群的 pay namespace 下拥有指定角色。