add: 七牛云分类 - XINFRA项目报告(架构/Ansible/部署CICD/安全认证/简历要点)
Deploy Docs / deploy (push) Successful in 11s

This commit is contained in:
2026-08-27 06:47:18 +00:00
parent b1a29548d2
commit 4dc3c45e89
7 changed files with 1897 additions and 0 deletions
+367
View File
@@ -0,0 +1,367 @@
# XINFRA Ansible 自动化报告
> 生成日期:2026-08-27 | 基于源码深度分析
---
## 一、整体结构
```
ansible/
├── README.md
├── gather-host-facts.yml # 目标主机事实采集
├── inventory.example.yml # MySQL 本地运行示例 inventory
├── group_vars/all.yml # 全局变量(SSH 密钥管理)
├── postgresql-deploy.yml # 单机/主从 PostgreSQL 交付入口
├── postgresql-rollback.yml # PostgreSQL 单机/主从回滚
├── files/
│ └── postgresql-xinfra@.service # PostgreSQL systemd 模板单元
├── tasks/
│ └── cleanup-controller-ssh-key.yml # 跨服务复用:清理控制端临时 SSH 私钥
├── templates/
│ └── postgresql-instance.conf.j2 # 单机/主从 PostgreSQL 配置模板
├── mysql/ # MySQL 自动化(独立子目录)
│ ├── deploy.yml / precheck.yml / rollback.yml / failover.yml / ...
│ ├── vars/ / tasks/ / templates/ / files/
└── postgresql_ha/ # PostgreSQL HA 集群
├── deploy.yml / preflight.yml / cleanup.yml / final_acceptance.yml
├── control-switchover.yml / control-restart.yml / control-acceptance.yml
├── templates/ / inventory.yml / galaxy.yml / README.md
```
**角色划分:**
- `mysql/` — 完整的 MySQL 原生交付生命周期
- `postgresql-deploy.yml` / `postgresql-rollback.yml` — 轻量级单机/主从 PostgreSQL
- `postgresql_ha/` — 生产级 PostgreSQL HA 集群(Patroni + etcd + HAProxy + Keepalived + pgBackRest)
- `tasks/` + `templates/` + `group_vars/` + `files/` — 跨服务共享资源
---
## 二、MySQL 自动化 (`mysql/`)
### 2.1 Playbook 清单
| Playbook | 职责 |
|----------|------|
| `deploy.yml` | 唯一部署入口:回调(precheck) → 参数校验 → MHA 制品安装 → 回调(install) → MySQL 二进制安装 → 回调(configure) → 账号配置 → 复制配置 → MHA 配置 → 回调(healthcheck) → TCP 端口检查 → 回调(register) |
| `precheck.yml` | 只读预检查:Python 脚本检查磁盘空间、内存、端口、制品可用性、目录冲突,输出结构化 JSON |
| `rollback.yml` | 回滚:停止 MHA Manager → 清理 SSH 授权 → 删除 MHA 工作目录 → 停止实例 → 删除配置/二进制/数据/运行时目录 |
| `failover.yml` | 两阶段切换:(1) 普通 primary_replica 主从切换(无 MHA)(2) MHA 模式切换(masterha_master_switch) |
| `decommission.yml` | 下线:校验参数 → 检查状态 → 停止 MHA Manager → 先停从库 → 再停主库(先设 read_only)→ 写下线标记 |
| `inspect.yml` | 实例巡检:检查 service 状态、配置、安装目录、数据目录、端口,输出 running/stopped/moved/deleted/unknown |
| `config-apply.yml` | 动态配置变更:写入 my.cnf 片段、重启实例(逐台从库后主库)、执行 SQL、轮换 root 密码 |
| `mha-check.yml` | MHA 验收:渲染配置 → 执行 masterha_check_repl → 安装/启动 Manager 服务 |
| `purge.yml` | 安全销毁:校验下线标记 → 确认服务停止 → 删除数据/程序/配置 → 验证删除 |
| `rejoin.yml` | 旧主重加入:备份旧数据 → Clone 接收 → 新 server_uuid → GTID 复制 → 校验一致性 |
| `restore.yml` | 恢复已下线实例:校验下线标记 → 启动实例 → 验证 UUID 和数据可读 |
### 2.2 公共变量 (`vars/delivery.yml`)
**核心变量:**
| 变量名 | 默认值 | 说明 |
|--------|--------|------|
| `mysql_instance` | `instance_name` | 实例名 |
| `mysql_version_value` | `8.0` | MySQL 版本 |
| `mysql_port_value` | `13306` | SQL 端口(池范围 13306-13999) |
| `mysql_gr_port_value` | SQL 端口+10000 | GR 组通信端口 |
| `mysql_data_disk` | `/data` | 数据盘挂载点 |
| `mysql_memory_mb_value` | `4096` | 内存配额(MB) |
| `mysql_storage_gb_value` | `50` | 存储配额(GB) |
| `ha_mode_value` | `none` | 高可用模式(none/mha) |
| `failover_mode_value` | `manual` | 切换模式(manual/automatic) |
**目录布局:**
```
{data_disk}/mysql-delivery/{instance}/
├── data/ # 数据目录
├── logs/ # 日志目录
├── logs/binlog/ # Binlog 目录
├── logs/redo/ # Redo 日志目录
└── tmp/ # 临时目录
/opt/mysql-delivery/{instance}/current → /opt/mysql/{version} (symlink)
/run/mysql-delivery-{instance}/ # 运行时目录
```
**账号体系(7 个内置账号):**
- `xinfra_repl` — 复制
- `xinfra_clone` — Clone
- `xinfra_mha` — MHA
- `dm_archery` — 审核
- `vault-dm` — Vault
- `xinfra_restore` — 恢复
- `xinfra_config` — 参数管理
**二进制制品目录(`mysql_binary_catalog`):**
- `8.0` — full_version=8.0.46,含 SHA256 校验和下载地址
- `8.4` — full_version=8.4.8,含 SHA256 校验和下载地址
### 2.3 Tasks 详情
#### `tasks/validate.yml` — 参数校验
- 拓扑合法性(standalone/primary_replica)
- MHA 模式要求 MySQL 8.0 + 至少 2 副本 + 主机数匹配
- 密码长度 >= 16、实例名格式、版本在 catalog 内
- 操作系统要求 Ubuntu 24.04+ x86_64
- 端口 13306-13999、内存 2-64G、存储 20-2000G
- 探测端口占用、检查可用内存和磁盘空间
#### `tasks/install.yml` — 安装流程
- 可选配置内部 APT 源
- 安装依赖包(aria2, ca-certificates, libaio1t64, libatomic1, libncurses6 等)
- 创建 `mysql` 系统用户
- 原子缓存并安装 MySQL generic binary(aria2 多线程下载、SHA256 校验、flock 并发锁)
- 通过 symlink 选择实例版本
- 安装 `xinfra-mysql-start` 启动器
- 创建实例目录结构
- 计算 `server_id`(端口+节点序号)、内存档位(max_connections 梯度表)、redo 容量
- 渲染 `mysql-instance.cnf.j2`
- 初始化数据目录(`mysqld --initialize-insecure`)
- 安装 systemd 模板单元并启动
#### `tasks/accounts.yml` — 账号配置
- 等待 socket 可用
- 初始化 root 密码(`SET SESSION sql_log_bin=0`,避免 GTID 污染)
- 安装 Clone 插件(MHA 模式)
- 创建远程 root、Vault、复制/Clone/MHA、恢复、配置、Archery 账号
- 使用临时文件存储凭据(`mktemp` + `chmod 600` + `trap`),避免命令行泄露
#### `tasks/replication.yml` — 复制配置
- MHA 模式下执行 MySQL Clone(从主库全量克隆,throttle=1 串行)
- Clone 后重新生成 server_uuid(删除 `auto.cnf` 重启)
- 配置 GTID 复制(`CHANGE REPLICATION SOURCE TO ... SOURCE_AUTO_POSITION=1`)
- 校验复制线程(IO/SQL Running: Yes)
- 校验 GTID 收敛(无 errant GTID、无 missing GTID)
#### `tasks/mha-artifacts.yml` — MHA 制品安装
- 下载 mha4mysql-node 和 mha4mysql-manager deb 包(SHA256 校验)
- 所有节点安装 node,最后一个副本节点安装 manager
- 校验 Perl 模块
#### `tasks/mha.yml` — MHA 配置
- 生成集群独立 SSH 密钥(ed25519)
- 授权 Manager 公钥到所有节点(`restrict` 前缀)
- 校验免密 SSH
#### `tasks/callback.yml` — 进度回调
- POST 请求到 `delivery_callback_url`,携带 Bearer token、stage、status、message、awx_job_id
### 2.4 Templates
#### `mysql-instance.cnf.j2` — MySQL 实例配置
关键配置段:
- **字符集:** utf8mb4 / utf8mb4_general_ci / +08:00 / lower_case_table_names=1
- **日志:** error.log、slow-query-log、long_query_time
- **Binlog:** ROW 格式、sync_binlog=1、max_binlog_size、expire_logs_seconds=604800
- **复制:** gtid-mode=ON、enforce-gtid-consistency=ON、relay-log、read-only/super-read-only(从库)
- **InnoDB:** buffer_pool=55% 内存、O_DIRECT、flush_log_at_trx_commit=1、io_capacity=2000
- **Redo:** innodb-redo-log-capacity(按内存档位:128M/256M/512M/1G)
- **Group Replication(mgr_3 拓扑):** plugin-load-add、group_name、local-address、group-seeds、single-primary-mode
#### `mysql-mha.cnf.j2` — MHA Manager 配置
- `[server default]`:user/password、manager_workdir、ssh_options(指定密钥和 known_hosts)、repl_user/password、ping_interval=5、report_script(自动模式)
- `[serverN]`:每个节点的 hostname、port、candidate_master、no_master
#### `mysql-mha-manager.service.j2` — MHA Manager systemd 单元
- ExecStartPre:`masterha_check_repl --conf=...`
- ExecStart:`masterha_manager --conf=... --remove_dead_master_conf`
- Restart=on-failure, RestartSec=10s, StartLimitBurst=5
#### `xinfra-mha-report.j2` — MHA 故障转移回调脚本
- 解析 `--orig_master_host`、`--new_master_host` 参数
- 构建 JSON payload,curl POST 到回调 URL,retry 5 次
### 2.5 Files
#### `mysql-delivery@.service` — systemd 模板单元
- Type=exec, User=mysql, Group=mysql
- ExecStart:`/usr/local/libexec/xinfra-mysql-start %i`
- Restart=on-failure, RestartSec=5s, TimeoutStartSec=120s
- LimitNOFILE=65535, OOMScoreAdjust=-200
#### `xinfra-mysql-start` — 启动器脚本
- 优先使用 `/opt/mysql-delivery/{instance}/current/bin/mysqld`
- 回退到 `/usr/sbin/mysqld`(兼容 APT 安装的存量实例)
---
## 三、PostgreSQL HA (`postgresql_ha/`)
### 3.1 HA 架构设计
**拓扑:3 节点共置**
```
┌─────────────────────────────────────────────────────────────┐
│ VIP (Keepalived) │
│ ┌───────────────┐ │
│ │ HAProxy:5432 │ │
│ │ (读写分离) │ │
│ └───────┬───────┘ │
│ ┌────────────────┼────────────────┐ │
│ ┌─────▼─────┐ ┌─────▼─────┐ ┌─────▼─────┐ │
│ │ Node 1 │ │ Node 2 │ │ Node 3 │ │
│ │ PG+Patroni│ │ PG+Patroni│ │ PG+Patroni│ │
│ │ etcd+pgBR │ │ etcd+pgBR │ │ etcd+pgBR │ │
│ │ HAProxy │ │ HAProxy │ │ HAProxy │ │
│ │ Keepalived│ │ Keepalived│ │ Keepalived│ │
│ └───────────┘ └───────────┘ └───────────┘ │
└─────────────────────────────────────────────────────────────┘
```
**服务依赖链:**
```
xinfra-pgha-etcd@{uuid}.service
→ xinfra-patroni.service
→ xinfra-postgres-exporter.service
xinfra-haproxy.service (独立)
keepalived.service (依赖 HAProxy 健康检查脚本)
```
**关键设计:**
- DCS:独立 etcd 3 节点集群(非系统 etcd)
- VIP:通过 Keepalived unicast 模式在 3 个 HAProxy 之间漂移
- HAProxy 通过 Patroni REST API `/primary` 健康检查路由到主库
- pgBackRest 双仓库:repo1 在第一个节点、repo2 在第二个节点
### 3.2 Playbook 清单
| Playbook | 职责 |
|----------|------|
| `preflight.yml` | 静态预检:校验操作身份哈希、平台密钥、CPU/内存/磁盘容量、端口空闲、Keepalived 接口/unicast peer/L2 可达性、VIP 未被占用、制品仓库可达性 |
| `deploy.yml` | 端到端部署(3 个 play):安装组件 → 配置 HAProxy/Keepalived → 创建 pgBackRest 备份 |
| `final_acceptance.yml` | 静态验收:Patroni 健康、集群成员(3 running + 1 primary)、etcd 健康、同步复制、HAProxy 端点、VIP 所有权、创建 Archery 账号 |
| `control-switchover.yml` | 计划切换:读取 Patroni 集群 → 解析 sync standby → patronictl switchover → 验证收敛 |
| `control-restart.yml` | 重启/滚动重启:单节点重启(leader 自动先切换)或滚动重启(serial:1) |
| `control-acceptance.yml` | 轻量验收:Patroni 健康、集群成员收敛、pg_isready、etcd 健康、HAProxy/Keepalived 状态 |
| `cleanup.yml` | 清理:停止服务 → 删除单元/配置/数据 → 验证端口和进程清除 |
### 3.3 关键变量
| 变量名 | 默认值 | 说明 |
|--------|--------|------|
| `postgresql_version` | `17` | PostgreSQL 版本 |
| `postgresql_port` | `15432` | PostgreSQL 端口 |
| `patroni_rest_port` | `8008` | Patroni REST API 端口 |
| `haproxy_service_port` | `5432` | HAProxy 监听端口 |
| `vip_address` | - | VIP 地址 |
| `vip_prefix_length` | - | VIP 前缀长度 |
| `vip_interface` | - | VIP 网络接口 |
| `synchronous_mode` | `true` | 同步复制开关 |
| `synchronous_mode_strict` | `true` | 严格同步复制 |
### 3.4 deploy.yml 核心流程
**Play 1(所有 postgresql 节点):**
1. 校验制品 URL 和密钥
2. 安装内部 APT 签名密钥和快照
3. 安装 PostgreSQL、Python 运行时包
4. 创建数据目录结构
5. 创建隔离的 Patroni venv(`python3 -m venv`,验证 system-site-packages=false)
6. 将白名单 OS Python 包复制到 venv
7. 从内部 PyPI 安装 patroni==3.2.2、psycopg[binary]==3.3.4、ydiff、cdiff
8. 验证 venv 完整性(`import patroni.ctl`、`patronictl --help`)
9. 下载并安装 etcd 静态制品
10. 创建 etcd systemd 单元并启动,等待 quorum
11. 安装 pgBackRest、postgres_exporter
12. 生成 pgBackRest SSH 密钥、授权所有节点、pin known_hosts
13. 渲染配置文件并启动 Patroni 和 exporter
**Play 2(安装 HAProxy/Keepalived):**
1. 安装 HAProxy 和 Keepalived
2. 渲染配置(验证语法)
3. 启动服务,等待 HAProxy 能连通 PostgreSQL primary
**Play 3(创建备份):**
1. 发现当前 primary
2. 创建 pgBackRest stanza
3. 在两个仓库各创建一次全量备份
### 3.5 Templates
#### `patroni.yml.j2` — Patroni 配置
- scope/namespace/name
- restapi:监听 `ansible_host:patroni_rest_port`
- etcd3:hosts 列表、http 协议
- bootstrap.dcs:ttl=30、loop_wait=10、retry_timeout=10、maximum_lag_on_failover=1048576
- synchronous_mode / synchronous_mode_strict
- PostgreSQL 参数:max_connections、shared_buffers=1GB、wal_level=replica、max_wal_senders=10、synchronous_commit=on、archive_mode=on(pgbackrest archive-push)
- initdb:UTF8、data-checksums
- pg_hba:peer 本地、scram-sha-256 远程
- create_replica_methods: basebackup (checkpoint: fast)
#### `haproxy.cfg.j2` — HAProxy 配置
- TCP 模式
- frontend:`0.0.0.0:{haproxy_service_port}`
- backend:`patroni_primary`,使用 `option httpchk GET /primary`(Patroni REST API 健康检查)
#### `keepalived.conf.j2` — Keepalived 配置
- VRRP 实例 `VI_PGHA`,state=BACKUP,nopreempt
- 单播模式(unicast_src_ip + unicast_peer)
- 健康检查脚本:`check-xinfra-haproxy.sh`
- auth_pass 由集群 UUID SHA1 派生
#### `pgbackrest.conf.j2` — pgBackRest 配置
- 双仓库:repo1 在第一个节点、repo2 在第二个节点
- retention-full=2
- stanza 名 = 集群名
---
## 四、通用共享资源
### 4.1 `group_vars/all.yml` — SSH 密钥管理
```yaml
xinfra_ssh_private_key_scope: "{{ task_id | ... }}_{{ job_id | ... }}"
xinfra_ssh_private_key_path: "/tmp/xinfra-ssh-{{ machine_id }}-{{ scope }}.pem"
xinfra_ssh_private_key_file: "{{ lookup('pipe', 'umask 077 && ... base64 ... mv ...') }}"
ansible_ssh_private_key_file: "{{ xinfra_ssh_private_key_file }}"
```
变量优先级:`xinfra_runtime_task_id` > `xinfra_runtime_inventory_id` > `manual`
### 4.2 `tasks/cleanup-controller-ssh-key.yml`
遍历 `ansible_play_hosts_all`,删除每个主机的临时 SSH 私钥文件。用于所有 playbook 的 post_tasks 段。
### 4.3 `templates/postgresql-instance.conf.j2` — 单机/主从 PostgreSQL 配置
- listen_addresses = '*'
- shared_buffers = 25% 内存、effective_cache_size = 70% 内存
- wal_level=replica、max_wal_senders、max_replication_slots、wal_keep_size=256MB
- primary/replica 条件分支:replica 时填写主库连接信息和 replication slot
### 4.4 `files/postgresql-xinfra@.service` — systemd 模板单元
- Type=notify, User=postgres, Group=postgres
- 通过环境变量 `POSTGRESQL_VERSION`、`POSTGRESQL_DATA_DIR`、`POSTGRESQL_CONFIG_FILE`、`POSTGRESQL_HBA_FILE` 启动 postgres
---
## 五、脚本工具链 (`scripts/`)
| 脚本 | 职责 |
|------|------|
| `build-publish-mha-packages.sh` | MHA deb 包修补与发布:修复 MySQL 8.x 版本解析正则、更新版本号和依赖、重新打包上传 |
| `publish-harbor.sh` | Docker 镜像构建推送:`docker buildx build --platform linux/amd64`,标签 git short hash |
| `publish-postgresql-python-runtime-closure.sh` | 构建不可变 APT 快照:收集运行时包、SHA256 校验、生成 Packages 索引、GPG 签名、上传 Nexus |
| `validate-postgresql-python-runtime-closure.sh` | 校验 APT 快照幂等性:两次独立演练对比 URI 和 SHA256 |
| `run-server-postgresql-local.sh` | 本地启动 Go 后端:从配置文件加载环境变量、自动从 K8s secret 获取 AWX 密码 |
| `setup-postgresql-awx.sh` | 单机 PostgreSQL AWX 环境初始化:创建 Project、Inventory、Credential Type、Job Template |
| `setup-postgresql-ha-e2e-awx.sh` | PGHA E2E AWX 工作流初始化:publish 模式创建全套资源、check 模式深度验证 |
| `tests/setup-postgresql-ha-e2e-awx-test.sh` | AWX 设置脚本的 mock 测试 |
| `ci/integration.sh` | CI 集成冒烟测试:启动后端、轮询端点、运行集成测试 |
---
## 六、设计亮点
1. **原子安装**:aria2 多线程下载 + SHA256 校验 + flock 并发锁,保证二进制安装的原子性和幂等性
2. **凭据安全**:临时文件存储(`mktemp` + `chmod 600` + `trap`),避免命令行泄露;SSH 密钥 ed25519 + `restrict` 前缀
3. **GTID 复制**:全流程 GTID 模式,Clone 后自动重新生成 server_uuid,校验 errant/missing GTID
4. **MHA 集成**:自动化 MHA 制品安装、配置、验收、故障转移回调
5. **PG HA 全栈**:Patroni + etcd + HAProxy + Keepalived + pgBackRest,VIP 漂移 + 读写分离 + 双仓库备份
6. **不可变 APT 快照**:内部 PyPI + APT 仓库,GPG 签名,幂等性校验
7. **AWX 集成**:完整的 Project/Inventory/Template/Workflow 自动化配置
+499
View File
@@ -0,0 +1,499 @@
# XINFRA 前后端架构报告
> 生成日期:2026-08-27 | 基于源码深度分析
---
## 一、后端架构(Go / Gin)
### 1.1 入口与启动
项目包含三个独立入口程序:
| 入口 | 路径 | 职责 |
|------|------|------|
| **主 API 服务** | `cmd/server/main.go` | HTTP API 服务,核心业务 |
| **Receptor Worker** | `cmd/receptor-worker/main.go` | 独立后台进程,Ansible Receptor 节点自动化供应 |
| **制品清单校验** | `cmd/artifact-manifest/main.go` | CLI 工具,离线制品清单签名校验 |
**主服务启动流程:**
```
config.Load() → vault.New(cfg) → database.Open(dsn) → database.AutoMigrate(db)
→ database.SeedDefaults(db, cfg) → cache.NewRedisClient(ctx, cfg)
→ router.New(deps) → r.Run(cfg.HTTPAddr)
```
- Vault 在 `VAULT_ENABLED=false` 时为 nil,不影响启动
- Redis 在 `REDIS_ENABLED=false` 时为 nil
- AutoMigrate 通过 `AUTO_MIGRATE` 环境变量控制
**Receptor Worker 启动流程:**
```
config.Load() → ValidateReceptorWorkerConfig() → database.Open() → database.AutoMigrate()
→ NewCredentialService() → NewHTTPAWXMeshClient() → ProbeCapabilities()
→ NewReceptorService() → Configure() → SetCredentialService() → SetBootstrapper() → Run(ctx)
```
- 使用 `signal.NotifyContext` 监听 SIGINT/SIGTERM 优雅退出
- 可通过 `RECEPTOR_PROVISIONING_CONCURRENCY` 控制并发
### 1.2 分层架构
```
┌─────────────────────────────────────────────────┐
│ cmd/ (入口) │
├─────────────────────────────────────────────────┤
│ internal/router (路由) │
├─────────────────────────────────────────────────┤
│ internal/handler (HTTP 处理) │
├─────────────────────────────────────────────────┤
│ internal/service (业务逻辑) │
├─────────────────────────────────────────────────┤
│ internal/model (数据模型) internal/database │
├─────────────────────────────────────────────────┤
│ internal/auth internal/sso internal/vault │
│ internal/cache internal/config │
│ internal/wayne internal/artifactmanifest │
└─────────────────────────────────────────────────┘
```
### 1.3 路由设计
**框架:** Gin
**顶层路由分组:**
| 路径前缀 | 说明 | 认证 |
|----------|------|------|
| `/swagger/*any` | Swagger 文档 | 无 |
| `/api/v1/ping` | 健康 ping | 无 |
| `/healthz` | 进程健康检查 | 无 |
| `/readyz` | 就绪检查(含 DB ping) | 无 |
| `/auth/.well-known/openid-configuration` | OIDC Discovery | 无 |
| `/auth/oauth/authorize` | OAuth2 授权端点 | 无 |
| `/auth/oauth/token` | OAuth2 Token 端点 | 无 |
| `/auth/oauth/jwks` | JWKS 端点 | 无 |
| `/auth/oauth/userinfo` | UserInfo 端点 | 无 |
| `/auth/internal/delivery/tasks/:id/events` | 交付任务事件回调 | Bearer Token |
| `/auth/internal/awx/jobs/events` | AWX Job 事件回调 | Bearer Token |
| `/auth/internal/delivery/mysql-clusters/:id/failover-events` | MySQL 故障切换事件回调 | Bearer Token |
| `/auth/api/v1` | **主 API 路由组** | 见下文 |
**`/auth/api/v1` 内部分组:**
**公开路由(无认证):**
- `GET /config` — 认证配置
- `POST /login` — 本地登录
- `GET /login/internal-sso` — SAML SSO 登录
- `GET/POST /logout` — 登出
- `GET /saml/metadata` — SAML SP 元数据
- `POST /saml/acs` — SAML ACS 断言消费
**受保护路由(AuthMiddleware)— 按业务域分组:**
| 业务域 | 主要端点 | 说明 |
|--------|----------|------|
| 用户 | `GET /users/me`, `PUT /users/me/phone`, `GET /users` | 用户信息管理 |
| 业务线 | CRUD + 成员/权限管理 + Wayne/Archery/SINA 映射 | 多租户核心 |
| Wayne 集成 | `GET /wayen/login`, `GET/PUT /wayen/credential` | Wayne SSO |
| 子系统授权 | Wayne 角色绑定 + Archery 权限组管理 | 细粒度授权 |
| 仪表盘 | `GET /dashboard/business-lines/:id` | 资源快照 |
| 容器服务 | summary / workloads / clusters | Wayne API 代理 |
| 交付系统 | MySQL/PostgreSQL 全生命周期 | 核心业务 |
| PostgreSQL HA | precheck/create/dispatch/config/control/cleanup | HA 集群管理 |
| Receptor | 节点 CRUD + 操作 + SSH 凭据 | 节点供应 |
| 机器资源 | overview/list/detail/create/delete/sync | SINA 集成 |
| 审计 | login / operations | 全链路审计 |
| 任务日志 | list / get / stream (SSE) | 任务追踪 |
**中间件:**
- `AuthMiddleware`:从 `Authorization: Bearer` header 或 `authserver_token` cookie 提取 JWT,调用 `auth.Parse` 校验,可选 token 吊销检查(Redis),将 claims 存入 gin.Context
- 回调路由使用独立的 `AWXWebhookToken` Bearer Token 认证
### 1.4 Handler 层
共 **22 个 Handler**,按业务域划分:
| Handler | 职责 |
|---------|------|
| `HealthHandler` | `/healthz`(进程存活)和 `/readyz`(DB ping) |
| `AuthHandler` | 本地登录、获取认证配置,SSO 启用时禁用本地登录 |
| `UserHandler` | 当前用户信息、更新手机号、用户列表 |
| `BusinessLineHandler` | 业务线 CRUD、成员管理、权限授予撤销、Wayne/Archery/SINA 映射 |
| `SAMLHandler` | SAML 元数据生成、登录重定向、登出(含 token 吊销)、ACS 断言消费 |
| `OAuthHandler` | 完整 OIDC Provider:Discovery、Authorize、Token、JWKS、UserInfo |
| `WayenHandler` | Wayne 平台 SSO 登录、凭据管理 |
| `WayneRoleBindingHandler` | Wayne 原生 API 代理:命名空间/应用的角色绑定/解绑 |
| `SubsystemAuthHandler` | 子系统授权管理:Archery 资源组/权限组、Wayne 命名空间角色 |
| `DeliveryHandler` | MySQL/PostgreSQL 交付全生命周期(30+ 端点) |
| `PostgreSQLDeliveryHandler` | PostgreSQL 独立交付任务创建 |
| `DeliveryCallbackHandler` | AWX 任务事件回调处理 |
| `PostgreSQLHAHandler` | PostgreSQL HA 全生命周期(15+ 端点) |
| `ExecutionProfileHandler` | AWX 执行配置管理 |
| `ReceptorHandler` | Receptor 节点管理 |
| `CredentialHandler` | SSH 凭据管理 |
| `ContainerServiceHandler` | 容器服务摘要(Wayne API 代理) |
| `MachineHandler` | 机器资源管理 |
| `ClouddmHandler` | CloudDM SSO 登录代理 |
| `ArcheryLoginHandler` | Archery SSO 登录代理 |
| `ArcherySyncHandler` | Archery 访问同步 |
| `AuditHandler` | 审计日志查询 |
| `DashboardHandler` | 业务线仪表盘快照 |
| `TaskLogHandler` | 任务日志查询和 SSE 流 |
### 1.5 Service 层
| Service | 职责 | 外部依赖 |
|---------|------|----------|
| `login/auth.go` | 本地登录(HS256 JWT)、SAML 登录、token 吊销 | DB、Redis、Config |
| `login/authorization.go` | RBAC 核心:平台角色、业务线角色/权限、用户授权 profile | DB |
| `login/wayen.go` | Wayen 平台 SSO 登录 | DB、Config |
| `delivery/delivery.go` | MySQL/PostgreSQL 交付任务调度、AWX Job、凭证管理、配额 | DB、Config、Audit、Notification、Archery、Vault |
| `delivery/postgresql_ha.go` | PostgreSQL HA 操作生命周期、AWX Workflow | DB、ExecutionProfileStore、AWXClient |
| `delivery/vault_database.go` | Vault Database Secrets Engine 集成 | Vault Client |
| `awx/awx.go` | AWX REST API 客户端 | AWX API |
| `awx/awx_execution.go` | AWX Job/Workflow 执行管理 | AWX API |
| `archery/archery.go` | Archery 平台集成 | DB、Config、Archery API |
| `audit/audit.go` | 审计日志写入(异步) | DB |
| `notification/notification.go` | 企业微信 Webhook 通知 | Config |
| `machine/machine.go` | 机器资源管理(SINA 同步、凭据、AWX Inventory) | DB、Config、SINA API |
| `receptor/receptor.go` | Receptor 节点生命周期管理 | DB、AWX Mesh |
| `receptor/credential.go` | SSH 凭据加密存储管理 | DB |
| `receptor/bootstrap.go` | Receptor 节点 SSH 引导安装 | SSH、CredentialService |
| `wayne/container_service.go` | Wayne API 代理(角色绑定、容器服务查询) | DB、Redis、Config、Wayne API |
### 1.6 Model 层
共 **40+ 张数据表**,核心模型分组:
**用户与权限:**
- `User` — ID, Username, DisplayName, Email, Phone, Source, ExternalID, Status, IsAdmin, LastLoginAt(软删除)
- `BusinessLine` — Name(唯一)
- `BusinessLineUser` — BusinessLineID, UserID, Role(owner/member), CreatedBy
- `PlatformRoleBinding` — UserID, Role(platform_admin)
- `BusinessLinePermission` — BusinessLineID, UserID, Permission
- `WayenCredential` — Email, Password, Enabled(软删除)
**交付系统:**
- `DeliveryTask` — ID(UUID), BusinessLineID, TaskKind(deploy/config_change/failover/switchover/decommission/restore/purge), Component(mysql/postgresql), Status(16 种状态)
- `ResourceQuota` / `ResourceReservation` — 配额与预留
- `DeploymentResult` / `DeploymentCredential` — 部署结果与凭证
- `ServiceConfig` — 服务配置台账
- `ExecutionJob` / `RollbackJob` — 执行与回滚
- `TaskEvent` — 任务事件流
**MySQL HA:**
- `MySQLCluster` — Name, Version, Topology, HAMode(none/mha), FailoverMode, VIP
- `MySQLInstance` — Hostname, HostIP, Port, Role(primary/replica), ServerID/UUID
- `MySQLReplicationChannel` / `MySQLInternalCredential` / `ServerIDAllocation`
**PostgreSQL HA:**
- `PostgreSQLCluster` — Name, ClusterUUID, VersionMajor, PatroniNamespace, PatroniScope
- `PostgreSQLInstance` — Hostname, Port, PatroniRESTPort, EtcdClientPort, Role
- `PostgreSQLHAOperation` — Status(14 种), AWXWorkflowJobID
- `PostgreSQLHAHostPlan` — MachineID, Hostname, Role, PostgreSQLRole, 资源需求
- `PostgreSQLHAAudit` / `PostgreSQLHAControlOperation` / `PostgreSQLHAJobLog`
**机器与 Receptor:**
- `MachineResource` — SinaID, Hostname, IntranetIP, Spec, CPUCores, Memory
- `MachineCredential` — AuthMethod(password/key), AWXInventoryID
- `ReceptorNode` — NodeIdentity, NodeType(hop/execution), ConnectionMode, AWXInstanceID
- `ReceptorProvisioningTask` — Operation, Status, CurrentStage, Attempt
- `SSHCredential` — KeyType, PrivateKeyCiphertext(PGP), EncryptedDataKey
**执行配置:**
- `ExecutionProfileRelease` — Name, Version, Status(draft/published/blocked), ManifestDigest
- `ExecutionBindingRelease` — ProfileReleaseID, Kind, AWX 对象 ID
- `TaskExecutionSnapshot` — 快照绑定
**Archery 集成:**
- `BusinessLineArcheryResourceGroup` / `BusinessLineArcheryPermissionGroup`
- `ArcheryAccessSyncTask` / `ArcheryInstanceBinding` / `ArcheryUserBinding`
### 1.7 认证机制
**JWT(`internal/auth/`):**
- `Claims` — HS256 访问令牌(UserID, Username, Email, DisplayName, IsAdmin)
- `OAuthCodeClaims` — HS256 OAuth 授权码
- `IDTokenClaims` — RS256 OIDC ID Token
- `PlatformTokenClaims` — RS256 子系统平台令牌
**SAML 2.0(`internal/sso/`):**
- 完整 SP 实现:AuthnRequest 构建、响应解码、加密断言解密(RSA-OAEP + AES-CBC/GCM)
- SP 元数据 XML 生成
**OAuth2/OIDC Provider(handler/oauth.go):**
- 完整 Authorization Code 流程
- 多 Client 支持:wayne、clouddm、archery
- OAuth Code 存储在 Redis(SetNX + TTL)
- Token 端点:access_token(HS256)+ id_token(RS256)
### 1.8 配置管理
`Config` 结构体包含 **200+ 个配置项**,全部通过环境变量加载,支持 `.env.local`、`.env.server`、`.env` 文件。
主要配置域:应用、数据库、Redis、SSO/JWT、SAML、Wayen、Wayne、OAuth/OIDC、CloudDM、AWX、PostgreSQL HA、MySQL、交付调度、Archery、Receptor、通知、Vault、SINA。
### 1.9 Vault 集成
- KV v2 客户端,支持 AppRole 认证和自动 token 续期
- Database Secrets Engine:MySQL/PostgreSQL 连接注册、Static Role 管理
- 请求重试:401/403 自动重新认证后重试一次
### 1.10 Wayne 集成
- API 代理层:角色绑定、命名空间管理、容器服务查询
- HMAC-SHA256 请求签名:`METHOD\nURI\nTIMESTAMP\nNONCE\nBODY_SHA256`
- Redis 缓存(TTL 可配置)
### 1.11 公共组件
- `response/` — 统一响应结构 `{code, message, data, details}`,业务错误码 10001-20002
- `artifactmanifest/` — PostgreSQL HA 离线制品清单解析和 Ed25519 签名校验
- `wayne/signature.go` — Wayne 服务间 HMAC-SHA256 签名
---
## 二、前端架构(Vue 3 / TypeScript)
### 2.1 技术栈
| 依赖 | 版本 |
|------|------|
| Vue | ^3.3.4 |
| Vue Router | ^4.2.4 |
| Pinia | ^2.1.4 |
| Element Plus | ^2.3.12 |
| Axios | ^1.5.0 |
| TypeScript | ^5.2.2 |
| Vite | ^6.4.3 |
| Vitest | ^3.2.4 |
自动导入插件:`unplugin-auto-import` + `unplugin-vue-components` 配合 `ElementPlusResolver`,实现 Element Plus 组件按需自动注册。
### 2.2 目录结构
```
src/
main.ts # 应用入口
App.vue # 根组件,含启动时 token 验证逻辑
router/index.ts # 路由定义 + 全局导航守卫
stores/auth.ts # 认证 Pinia store
stores/businessLine.ts # 业务线 Pinia store
api/ # 13 个 API 模块(含 .spec.ts 测试)
views/ # 9 个视图目录
components/ # 8 个公共组件
components/Layout/ # 3 个布局子组件
composables/ # 2 个组合式函数
types/api.ts # 全局类型定义
utils/ # 6 个工具模块
styles/ # 2 个样式文件
layouts/DefaultLayout.vue # 默认布局壳
```
### 2.3 路由设计
**路由模式:** `createWebHistory()` (HTML5 History)
**路由表(共 25+ 条):**
| 路径 | 组件 | 权限要求 |
|------|------|----------|
| `/login` | `views/auth/Login.vue` | `requiresAuth: false` |
| `/` | `layouts/DefaultLayout.vue` | `requiresAuth: true` |
| `/dashboard` | `views/dashboard/Index.vue` | 无 |
| `/subsystem` | `views/subsystem/Navigation.vue` | 无 |
| `/subsystem/detail/:name` | `views/subsystem/SubsystemDetail.vue` | 无 |
| `/subsystem/authorization` | `views/subsystem/Authorization.vue` | `capabilities: ['subsystem.wayne.manage', 'subsystem.archery.manage']` |
| `/business-line/manage` | `views/businessLine/Manage.vue` | `platformPermission: 'business_line.manage'` |
| `/business-line/assignment` | `views/businessLine/Assignment.vue` | `businessLineOwner: true` |
| `/audit/login` | `views/audit/LoginAudit.vue` | `capability: 'audit.login.view'` |
| `/audit/operation` | `views/audit/OpsAudit.vue` | `capability: 'audit.operation.view'` |
| `/resource/status` | `views/resource/StatusBoard.vue` | 无 |
| `/machine/management` | `views/resource/Management.vue` | `capability: 'machine.view'` |
| `/infrastructure/cluster` | `views/cluster/ClusterList.vue` | `businessLineMember: true` |
| `/service/catalog` | `views/service/Catalog.vue` | `capability: 'delivery.deploy'` |
| `/service/postgresql-ha` | `views/service/PostgreSQLHA.vue` | `capability: 'delivery.deploy'` |
| `/service/management` | `views/service/Management.vue` | `capability: 'delivery.view'` |
| `/service/recycle-bin` | `views/service/MySQLRecycleBin.vue` | `capability: 'delivery.view'` |
| `/task/log` | `views/task/TaskCenter.vue` | `capability: 'delivery.view'` |
| `/config` | `views/config/ConfigCenter.vue` | 无 |
| `/observable/monitoring` | `views/monitor/Monitor.vue` | 无 |
| `/observable/alert` | `views/monitor/Alert.vue` | 无 |
**路由守卫四级权限检查:**
1. `platformPermission` — 调用 `authStore.hasPlatformPermission()`
2. `businessLineMember` — 检查 `businessLineStore.current?.id` 是否存在
3. `businessLineOwner` — 检查 `businessLineStore.isCurrentAdmin`
4. `capability` / `capabilities` — 调用 `authStore.hasAnyBusinessLinePermission()`
### 2.4 状态管理
#### auth store (`stores/auth.ts`)
| 字段 | 类型 | 说明 |
|------|------|------|
| `token` | `ref<string \| null>` | JWT token,从 localStorage 初始化 |
| `user` | `ref<UserInfo \| null>` | 用户信息对象 |
| `platformRoles` | `computed` | `user.platform_roles` |
| `platformPermissions` | `computed` | `user.platform_permissions` |
| `businessLines` | `computed` | `user.business_lines` |
| `isAdmin` | `computed` | 是否拥有 `business_line.manage` 权限 |
**关键方法:**
- `hasPlatformPermission(permission)` — 平台级权限检查,admin 额外授予 3 个管理权限
- `isBusinessLineOwner(businessLineId)` — 业务线 owner 检查
- `hasBusinessLinePermission(businessLineId, permission)` — 业务线级权限检查,支持权限别名映射
- `hasAnyBusinessLinePermission(permission)` — 任一业务线权限检查
- `setAuth / refreshUser / clearAuth / isLoggedIn`
**权限别名映射(硬编码):**
- `subsystem.wayne.manage` → `subsystem.wayne.role.grant`, `subsystem.wayne.role.revoke`
- `machine.view` → `machine.read`, `machine.credential.read`
- `machine.manage` → `machine.create/update/delete/credential.manage/sync`
- `delivery.view` → `delivery.target.read`, `delivery.read`
- `delivery.deploy` → `delivery.create`
#### businessLine store (`stores/businessLine.ts`)
| 字段 | 类型 | 说明 |
|------|------|------|
| `businessLines` | `ref<BusinessLine[]>` | 所有业务线 |
| `currentBL` | `ref<BusinessLine \| null>` | 当前选中,从 localStorage 恢复 |
| `isCurrentAdmin` | `computed` | 当前 BL 的 role === 'owner' |
**方法:** `loadMine()` — 去重防并发(loadPromise 锁);`switchBL(id)` — 持久化到 localStorage key `xinfra-current-bl`
### 2.5 API 封装
**请求基础设施:**
- `api/request.ts` — 基于 Axios,`baseURL: '/api/v1'`,`timeout: 10000`,自动注入 Bearer token
- `api/authRequest.ts` — 基于原生 fetch 的轻量封装
- 响应拦截器:业务码映射中文提示、401 自动跳转、超时/网络异常处理
**统一响应类型:**
```typescript
ApiResponse<T> = { code: number, message: string, data: T, details?: Record<string, string[]> }
PaginatedData<T> = { total: number, items: T[] }
```
**业务错误码:** SUCCESS(0), LDAP_AUTH_FAILED(10001), INVALID_CREDENTIALS(10002), TOKEN_EXPIRED(10003), SSO_NOT_CONFIGURED(10004), SSO_GENERATE_FAILED(10005), TASK_CREATE_FAILED(20001), TASK_EXEC_TIMEOUT(20002)
**API 模块一览(13 个):**
| 模块 | 端点前缀 | 功能 |
|------|----------|------|
| auth | `/auth/api/v1/` | SSO 配置、登录、登出、用户信息 |
| dashboard | `/auth/api/v1/dashboard/` | 资源大盘快照 |
| machine | `/auth/api/v1/machines/` | 机器 CRUD、凭证、同步 |
| user | `/auth/api/v1/users` | 用户列表 |
| subsystem | 内嵌 mock + Wayne/Archery | 子系统列表、SSO 跳转 |
| subsystemAuth | `/auth/api/v1/subsystem-auth/` | 子系统赋权 |
| audit | `/auth/api/v1/audit/` | 登录/运维审计 |
| delivery | `/auth/api/v1/delivery/` + `/postgresql-ha/` | 服务交付全生命周期(30+ 方法) |
| receptor | `/auth/api/v1/infrastructure/` | Receptor 节点管理 |
| taskLog | `/auth/api/v1/task-logs` | 任务日志 + SSE 流 |
| containerService | `/auth/api/v1/container-services/` | 容器服务概览 |
| businessLine | `/auth/api/v1/business-lines/` | 业务线 CRUD + 成员权限 |
### 2.6 视图模块
| 目录 | 组件 | 功能 |
|------|------|------|
| auth/ | `Login.vue` | 登录页面 |
| dashboard/ | `Index.vue` | 资源大盘(容器/机器/MySQL/任务汇总) |
| subsystem/ | `Navigation.vue`, `SubsystemDetail.vue`, `Authorization.vue` | 子系统导航、详情、赋权 |
| businessLine/ | `Manage.vue`, `Assignment.vue` | 业务线管理、分配 |
| audit/ | `LoginAudit.vue`, `OpsAudit.vue` | 登录/运维审计 |
| resource/ | `StatusBoard.vue`, `Management.vue` | 资源状态看板、机器管理 |
| cluster/ | `ClusterList.vue`, `ReceptorNodes.vue` | 集群列表、Receptor 节点 |
| service/ | `Catalog.vue`, `Management.vue`, `PostgreSQLHA.vue`, `PostgreSQLHAControl.vue`, `PostgreSQLHAInstanceDetail.vue`, `MySQLInstanceDetail.vue`, `MySQLRecycleBin.vue` | 服务目录、管理、PG HA 全流程、MySQL 实例详情、回收站 |
| task/ | `TaskCenter.vue` | 任务日志中心 |
| config/ | `ConfigCenter.vue` | 配置中心 |
| monitor/ | `Monitor.vue`, `Alert.vue` | 监控看板、告警 |
### 2.7 组件体系
**布局组件:**
- `AppHeader.vue` — 顶部导航:logo + 环境标签 + BusinessLineSwitcher + 主题切换 + 用户信息
- `AppSidebar.vue` — 左侧导航:按分组(概览/业务交付/资源纳管/可观测性/统一入口/审计)动态显示/隐藏
- `AppMain.vue` — 内容区:`<router-view />`
- `DefaultLayout.vue` — CSS Grid 布局 `282px 1fr` / `70px 1fr`,移动端折叠侧边栏
**公共组件:**
- `BusinessLineSwitcher.vue` — 业务线下拉切换器
- `PhoneSetupDialog.vue` — 首次登录手机号补充弹窗
- `SubsystemCard.vue` — 子系统卡片(图标/标签/域名/SSO 状态)
- `AuditLogTable.vue` — 通用审计日志表格
- `ServiceConfigDialog.vue` — MySQL 服务配置弹窗(概览/编辑/变更任务轮询)
- `PostgreSQLHAConfigDialog.vue` — PostgreSQL HA Patroni 配置弹窗
### 2.8 组合式函数
| Composable | 功能 |
|------------|------|
| `useAuth` | 封装 login/logout/checkAuth,login 成功后自动加载业务线 |
| `useTheme` | 主题管理:light/dark 切换、系统主题监听、持久化到 localStorage |
### 2.9 工具函数
| 模块 | 功能 |
|------|------|
| `utils/auth.ts` | localStorage 读写 token/user、JWT 过期检查 |
| `utils/sso.ts` | SSO 跳转、sso_token 消费 |
| `utils/session.ts` | 401 处理、防并发重定向 |
| `utils/clipboard.ts` | 剪贴板写入(Clipboard API + fallback) |
| `utils/postgresqlHA.ts` | PG HA health_artifact 解析 |
| `utils/businessLineMock.ts` | 业务线 mock 数据 |
### 2.10 样式体系
**Design Token(CSS 变量):**
- 背景色 3 级、边框色 2 级、文字色 3 级
- 强调色 green `#00B42A`、状态色 warn/err/info
- 圆角 4 级、字体 mono/sans(IBM Plex)
- 浅色/深色双主题(`data-theme` 属性 + `.dark` class)
**全局样式:** 完整的表格/分页/日志流/工具栏/子系统卡片/环境标签样式
**响应式断点:** 1024px(平板)、768px(移动端)
### 2.11 构建配置
- **Vite:** `@` → `src/`,开发服务器 `127.0.0.1:5173`,代理 `/auth`、`/api`、`/swagger` 到后端
- **TypeScript:** ES2020 target,strict 模式,未使用变量/参数报错
- **测试:** jsdom 环境,v8 覆盖率(text + json-summary + lcov)
---
## 三、前后端交互模型
```
┌──────────────┐ /api/v1/* ┌──────────────┐
│ Vue 3 SPA │ ──────────────── → │ Gin HTTP │
│ (Vite) │ ← ───────────── ─ │ Server │
└──────────────┘ JSON Response └──────┬───────┘
│
┌─────────────────────┼─────────────────────┐
│ │ │
┌─────▼─────┐ ┌──────▼──────┐ ┌──────▼──────┐
│ MySQL │ │ Redis │ │ Vault │
│ (GORM) │ │ (缓存/会话) │ │ (密钥管理) │
└───────────┘ └─────────────┘ └─────────────┘
│
┌─────────────────────┼─────────────────────┐
│ │ │
┌─────▼─────┐ ┌──────▼──────┐ ┌──────▼──────┐
│ AWX │ │ Wayne │ │ Archery │
│ (Ansible) │ │ (K8s 管理) │ │ (SQL 审核) │
└───────────┘ └─────────────┘ └─────────────┘
```
**认证流程:**
1. 前端存储 JWT token 于 localStorage
2. 请求拦截器自动注入 `Authorization: Bearer <token>`
3. 后端 AuthMiddleware 校验 JWT,可选 Redis token 吊销检查
4. SSO 场景:SAML IDP → ACS → JWT 签发 → 前端消费 sso_token
5. OAuth 场景:前端 → `/auth/oauth/authorize` → Redis code → `/auth/oauth/token` → JWT
+345
View File
@@ -0,0 +1,345 @@
# XINFRA 部署与 CI/CD 报告
> 生成日期:2026-08-27 | 基于源码深度分析
---
## 一、Docker 部署 (`deploy/docker/`)
### 1.1 前端镜像 — `Dockerfile.frontend`
| 阶段 | 基础镜像 | 操作 |
|------|----------|------|
| 构建 | `node:22-alpine` | `npm ci` → `npm run build` → 产物 `/src/frontend/dist` |
| 运行 | `nginx:1.27-alpine` | 复制产物到 `/usr/share/nginx/html`,覆盖 nginx 默认配置 |
- 暴露端口:`80`
- 本地镜像标签:`xinfra-frontend:latest`(Makefile)
### 1.2 后端镜像 — `Dockerfile.server`
| 阶段 | 基础镜像 | 操作 |
|------|----------|------|
| 构建 | `golang:1.26-alpine` | Go 代理 `goproxy.cn`,编译 `authserver` 二进制 |
| 运行 | `alpine:3.22` | 放置于 `/usr/local/bin/authserver` |
- 暴露端口:`8083`
- 运行用户:`65532:65532`(非 root)
- 入口点:`/usr/local/bin/authserver`
- 本地镜像标签:`xinfra-server:latest`(Makefile)
### 1.3 Nginx 配置 — `nginx.frontend.conf`
**监听端口:** `80`
**反向代理规则:**
| 路径 | 目标 | 说明 |
|------|------|------|
| `/api/` | `http://xinfra-backend:8083` | API 代理 |
| `/auth/` | `http://xinfra-backend:8083` | 认证代理 |
| `/swagger/` | `http://xinfra-backend:8083` | Swagger 文档 |
| `/` | `try_files $uri $uri/ /index.html` | SPA fallback |
代理头:`Host`、`X-Real-IP`
### 1.4 本地开发数据库 — `docker-compose.mysql.yml`
**MySQL 服务:**
- 镜像:`mysql:8.0.46`
- 容器名:`authserver-mysql`
- 端口映射:`3000:3306`
- 环境变量:`MYSQL_ROOT_PASSWORD=root123456`, `MYSQL_DATABASE=authserver`, `MYSQL_USER=auth`, `MYSQL_PASSWORD=auth`, `TZ=Asia/Shanghai`
- 字符集:`utf8mb4` / `utf8mb4_unicode_ci`
- 认证插件:`mysql_native_password`
- 数据卷:`./data/mysql:/var/lib/mysql`
**Redis 服务:**
- 镜像:`redis:7.4-alpine`
- 容器名:`authserver-redis`
- 端口映射:`6379:6379`
- 持久化:`appendonly yes`
- 数据卷:`./data/redis:/data`
---
## 二、Kubernetes 部署 (`deploy/k8s/wayne-xinfra.yaml`)
单一 YAML 文件,包含以下资源,均标注 `wayne-app: xinfra`, `wayne-ns: xinfra`:
### 2.1 Secret: `xinfra-secret` (Opaque)
| 键 | 说明 |
|----|------|
| `MYSQL_DSN` | MySQL 连接字符串(集群内 Service DNS) |
| `JWT_SECRET` | 64 字节 hex 字符串 |
| `BOOTSTRAP_ADMIN_PASSWORD` | 初始管理员密码 |
| `OAUTH_WAYNE_CLIENT_SECRET` | Wayne OAuth 客户端密钥 |
| `OIDC_CLOUDDM_CLIENT_SECRET` | CloudDM OIDC 客户端密钥 |
| `AWX_TOKEN` / `AWX_USERNAME` / `AWX_PASSWORD` | AWX 凭据 |
| `AWX_WEBHOOK_TOKEN` | AWX Webhook Token |
| `NOTIFY_WECOM_WEBHOOK_URL` | 企业微信群机器人 Webhook |
| `DELIVERY_CREDENTIAL_SECRET` / `DELIVERY_SERVICE_TOKEN` | 交付系统凭据 |
| `REDIS_PASSWORD` | Redis 密码 |
### 2.2 Secret: `harbor-registry` (kubernetes.io/dockerconfigjson)
- Harbor 地址:`218.11.5.223:30006`
- 用户:`admin`
### 2.3 ConfigMap: `xinfra-configmap`
**关键配置项:**
| 域 | 配置 |
|----|------|
| 应用 | `APP_ENV=prod`, `HTTP_ADDR=:8083`, `AUTO_MIGRATE=true` |
| Redis | `REDIS_ADDR=xinfra-redis:6379`, `REDIS_DB=0` |
| SSO | `SSO_ENABLED=false` |
| 公网地址 | `PUBLIC_BASE_URL=http://218.11.5.223:32002` |
| JWT | `JWT_ISSUER=authserver`, `JWT_TTL_MINUTES=120` |
| SAML | IDP metadata URL、ACS URL、SP 证书路径 |
| OIDC | authorization/token/userinfo/jwks 端点 |
| Wayne | `WAYNE_API_BASE_URL=http://wayne-backend.demo.svc.cluster.local:8080` |
| Archery | `ARCHERY_API_BASE_URL=http://archery.archery.svc.cluster.local:9123` |
| AWX | `AWX_BASE_URL=http://awx-demo-service.awx.svc.cluster.local` |
| 交付调度 | `DELIVERY_SCHEDULER_ENABLED=true`, 限制参数: global=2, target=2, host_instance=4 |
| Vault | `VAULT_ENABLED=true`, `VAULT_ADDR=http://vault-active.vault.svc.cluster.local:8200` |
| PostgreSQL HA | pgbackrest、postgres_exporter、etcd 组件 URL、digest、模板 ID |
| Sina | `SINA_BASE_URL=https://sinai.qiniu.io:443`, 同步间隔 30 分钟 |
### 2.4 StatefulSet: `xinfra-redis`
- 镜像:`redis:7.4-alpine`
- 副本:1
- 持久化存储:2Gi, `storageClassName: local-path`
- 资源:requests 50m/128Mi, limits 500m/512Mi
- 探针:readiness/liveness 均通过 `redis-cli ping` 检测
### 2.5 Deployment: `xinfra-backend`
- 镜像:`218.11.5.223:30006/xinfra/xinfra-backend:latest`
- 镜像拉取策略:`Always`
- 副本:1,滚动更新(maxSurge 20%, maxUnavailable 1)
- 环境来源:`envFrom` — `xinfra-configmap`, `xinfra-secret`, `xinfra-vault-approle`
- 挂载卷:`xinfra-saml-certs` → `/authserver/backend/certs`(只读)
- 资源:requests 500m/3Gi, limits 1 CPU/3Gi
- 探针:readiness `/readyz`(10s 延迟), liveness `/healthz`(20s 延迟)
### 2.6 Deployment: `xinfra-frontend`
- 镜像:`218.11.5.223:30006/xinfra/xinfra-frontend:latest`
- 副本:1,滚动更新
- 资源:requests 50m/64Mi, limits 500m/256Mi
- 探针:readiness/liveness 均检测 `/`
### 2.7 Service
| Service | 类型 | 端口映射 |
|---------|------|----------|
| `xinfra-backend` | ClusterIP | 8083 |
| `xinfra-frontend` | **NodePort** | 80 → **32002** |
---
## 三、CI/CD (`.github/workflows/ci.yml`)
### 3.1 触发条件
- `push` 到 `main` 分支
- 所有 `pull_request`
### 3.2 全局配置
- **并发控制:** `ci-${{ github.workflow }}-${{ github.ref }}`,取消进行中的同组任务
- **权限:** `contents: read`
- **Go 缓存:** 自定义 GOPATH,GOMODCACHE, GOCACHE
- **S3 缓存:** 七牛 S3 兼容存储(`las-github-runner-dal`, `s3.us-north-1.qiniucs.com`)
### 3.3 Job 依赖图
```
prepare ──→ go-mod-cache ──→ backend-unit ──┐
├──→ coverage-upload
frontend-validation ────────────────────────┤
│
integration ────────────────────────────────┘
```
### 3.4 Job 详情
#### `prepare`
- Runner:`github-runner-ubuntu-24-04`
- 仅限仓库 `1024XEngineer/xinfra`
- 输出:`ci_go_mod_hash`(对 `go.mod`+`go.sum` 做双重 SHA256)
#### `go-mod-cache`
- 依赖:`prepare`
- 步骤:checkout → Go 环境准备 → `go mod download` → 保存到 S3
#### `backend-unit`
- 依赖:`prepare`, `go-mod-cache`
- 步骤:
1. Checkout + Go 环境准备
2. `go vet ./...`
3. `go test -race -covermode=atomic -coverprofile=coverage.out ./...`
4. (仅 main push)上传 coverage 到 S3
#### `frontend-validation`
- 无依赖(可并行)
- 步骤:
1. Checkout + Node.js setup(版本来自 `.nvmrc`,缓存 npm)
2. `npm ci`
3. `npm run test:coverage`
4. `npm run build`
5. (仅 main push)上传 lcov.info 到 S3
#### `integration`
- 依赖:`prepare`, `go-mod-cache`, `backend-unit`, `frontend-validation`
- **Services:** MySQL 8.0.46(端口 3306)
- 步骤:
1. Checkout + Go 环境准备
2. 运行 `scripts/ci/integration.sh`:
- 启动后端(`go run ./cmd/server`)
- 等待 `/readyz` 返回 `status: ok`(最多 30 次,每次 2s)
- 验证 `/healthz` 和 `/api/v1/ping`
- 运行集成测试
3. 环境变量:`APP_ENV=test`, `HTTP_ADDR=127.0.0.1:8080`, `AUTO_MIGRATE=true`, `SSO_ENABLED=false`
#### `coverage-upload`
- 依赖:`backend-unit`, `frontend-validation`, `integration`
- 仅在 `main` 分支 push 时运行
- 从 S3 下载前后端 coverage 文件
- 使用 `codecov/codecov-action@v7` 上传
### 3.5 Composite Actions
#### `ci-go-prep` (`.github/actions/ci-go-prep/action.yml`)
- `actions/setup-go@v6`,版本来自 `server/go.mod`
- 创建 Go 目录
- 从 S3 恢复 Go 模块缓存
#### `ci-s3-prep` (`.github/actions/ci-s3-prep/action.yml`)
- 安装 `rclone`(如果不存在)
### 3.6 CI 辅助脚本
| 脚本 | 功能 |
|------|------|
| `s3-cache-common.sh` | 封装 rclone 配置七牛 S3,提供 `s3_cache_get/put/upload_if_changed/upload_if_missing` 函数 |
| `save-go-cache.sh` | 将 Go 模块缓存打包为 `.tar.zst` 上传到 S3 |
| `restore-go-cache.sh` | 从 S3 下载并解压 Go 模块缓存 |
| `coverage-report-s3.sh` | 覆盖率报告的 S3 上传/下载工具 |
---
## 四、Makefile
| Target | 功能 |
|--------|------|
| `help` | 显示帮助信息(默认目标) |
| `build` | 构建前后端(`build-frontend` + `build-server`) |
| `build-frontend` | `npm install && npm run build`(带 nvm 切换) |
| `build-server` | `go build -o bin/xinfra ./cmd/server` |
| `publish-harbor [IMAGE_TAG=<tag>]` | 调用 `scripts/publish-harbor.sh` 构建并推送镜像到 Harbor |
| `run` | 并行启动前后端开发服务器 |
| `run-frontend` | 仅启动前端开发服务器 |
| `run-server` | 仅启动后端开发服务器 |
| `test` | 运行所有测试(`test-frontend` + `test-server`) |
| `test-frontend` | `npm run test` |
| `test-server` | `go test ./...` |
| `docker` | 构建 Docker 镜像(`docker-frontend` + `docker-server`) |
| `docker-frontend` | `docker build -t xinfra-frontend:latest -f deploy/docker/Dockerfile.frontend .` |
| `docker-server` | `docker build -t xinfra-server:latest -f deploy/docker/Dockerfile.server .` |
| `clean` | 清理 `frontend/dist`, `node_modules`, `server/bin`, `server/vendor` |
| `dev-up` | `docker compose -f server/docker-compose.mysql.yml up -d` |
| `dev-down` | `docker compose -f server/docker-compose.mysql.yml down` |
| `swagger` | `swag init -g internal/router/router.go -o ./docs` |
| `migrate-up` | `go run ./cmd/server migrate up` |
| `migrate-down` | `go run ./cmd/server migrate down` |
### publish-harbor.sh 脚本详情
- Harbor 地址:`218.11.5.223:30006`,项目:`xinfra`
- Tag 策略:默认取 `git rev-parse --short=12 HEAD`;工作区有改动时追加 `-dirty-<时间戳>`
- 构建命令:`docker buildx build --platform linux/amd64`,同时打 `tag` 和 `latest`
- 推送两个镜像:`xinfra/xinfra-backend` 和 `xinfra/xinfra-frontend`
---
## 五、本地开发配置 (`config/local/`)
### 5.1 PostgreSQL 交付 AWX 开发配置
- `AWX_BASE_URL=http://127.0.0.1:59123`
- AWX 对象:Project、Inventory、Job Template、Rollback Template
- 目标主机:`218.11.5.224`(SSH via `192.168.1.5`)
- Playbook:`postgresql-deploy.yml` / `postgresql-rollback.yml`
### 5.2 PostgreSQL HA E2E 测试配置
- `AWX_BASE_URL=http://218.11.5.223:31768`
- EE 镜像:`192.168.1.4:30006/xinfra/awx-ee`
- Workflow:`XINFRA PostgreSQL HA E2E Delivery v1`
- Container Group:CPU 250m/2, Memory 256Mi/2Gi
### 5.3 后端 PostgreSQL 交付运行时配置
- 调度器参数:`DELIVERY_SCHEDULER_ENABLED=true`, `DELIVERY_POLL_SECONDS=3`
- MySQL 模板:Config Apply, Decommission(保留 15 天), Restore, Purge
---
## 六、Nginx 配置汇总
### 6.1 生产 Docker 配置 (`deploy/docker/nginx.frontend.conf`)
简单 SPA 配置,监听 80 端口,反向代理 `/api/`、`/auth/`、`/swagger/` 到 `xinfra-backend:8083`。
### 6.2 本地开发网关 (`local/nginx.local.conf`)
**监听端口:** `8089`
**Upstream 后端:**
- `authserver_backend` → `127.0.0.1:8083`(keepalive 32)
- `authserver_frontend` → `127.0.0.1:5173`(Vite dev server)
- `wayne_backend` → `127.0.0.1:8080`
- `wayne_frontend` → `127.0.0.1:4200`
**路由规则:**
- `/healthz`, `/readyz` → authserver_backend
- `/auth/api/`, `/auth/oauth/`, `/auth/.well-known/` → authserver_backend
- `/wayne/` → wayne_frontend(rewrite 去掉 `/wayne/` 前缀)
- `/api/` → wayne_backend(携带 Authorization 和 X-Wayne-Emergency-Authorization 头)
- `/` → authserver_frontend(SPA 默认)
---
## 七、关键端口汇总
| 服务 | 端口 | 场景 |
|------|------|------|
| 前端 (nginx) | 80 | Docker/K8s |
| 后端 (authserver) | 8083 | 全场景 |
| MySQL | 3306 (映射到 3000) | 本地开发 |
| Redis | 6379 | 本地开发 / K8s |
| 本地网关 nginx | 8089 | 本地开发 |
| Vite dev server | 5173 | 本地开发 |
| Wayne 后端 | 8080 | 本地开发 |
| Wayne 前端 | 4200 | 本地开发 |
| K8s NodePort (前端) | 32002 | K8s 生产 |
| AWX API | 59123 | 本地开发 |
## 八、关键镜像汇总
| 镜像 | 版本 |
|------|------|
| `node` | `22-alpine` |
| `nginx` | `1.27-alpine` |
| `golang` | `1.26-alpine` |
| `alpine` | `3.22` |
| `mysql` | `8.0.46` |
| `redis` | `7.4-alpine` |
| `218.11.5.223:30006/xinfra/xinfra-backend` | `latest` (K8s) |
| `218.11.5.223:30006/xinfra/xinfra-frontend` | `latest` (K8s) |
| `192.168.1.4:30006/xinfra/awx-ee` | `sha256:d6fca88c...` |
+43
View File
@@ -0,0 +1,43 @@
# 七牛云
七牛云 XINFRA 项目相关笔记与文档。
---
## 项目概览
XINFRA 是一个生产级基础设施管理平台,涵盖前后端架构、Ansible 自动化、CI/CD 流水线、多协议认证网关、数据库高可用集群管理等核心能力。
| 报告 | 内容概要 |
|------|----------|
| [前后端架构](architecture.md) | Go/Gin 后端分层设计(22 Handler / 16 Service / 40+ Model)、Vue 3 前端架构(25+ 路由 / 13 API 模块 / Pinia Store)、前后端交互模型 |
| [Ansible 自动化](ansible-automation.md) | MySQL 全生命周期(11 Playbook)、PostgreSQL HA(Patroni+etcd+HAProxy+Keepalived+pgBackRest)、变量管理、Templates、脚本工具链 |
| [部署与 CI/CD](deployment-cicd.md) | Docker 多阶段构建、K8s/Wayne 清单(Secret/ConfigMap/StatefulSet/Deployment/Service)、GitHub Actions 流水线、Makefile、Nginx 配置 |
| [安全·认证·数据](security-auth-data.md) | 多协议认证(本地/SAML/OIDC)、JWT 机制、RBAC 权限模型、审计日志、数据库设计(40+ 表)、Vault 密钥管理、凭据加密 |
| [简历技术要点](resume-tech-points.md) | 基于项目源码实际实现提炼的核心技术亮点,每个要点可深入到函数级别展开 |
---
## 快速参考
### 关键端口
| 服务 | 端口 |
|------|------|
| 后端 (authserver) | 8083 |
| 前端 (nginx) | 80 |
| K8s NodePort | 32002 |
| 本地开发网关 | 8089 |
| Vite dev server | 5173 |
### 关键入口
| 组件 | 路径 |
|------|------|
| 后端主入口 | `server/cmd/server/main.go` |
| Receptor Worker | `server/cmd/receptor-worker/main.go` |
| 前端入口 | `frontend/src/main.ts` |
| 路由定义 | `frontend/src/router/index.ts` |
| 后端路由 | `server/internal/router/router.go` |
| CI 流水线 | `.github/workflows/ci.yml` |
| K8s 清单 | `deploy/k8s/wayne-xinfra.yaml` |
+51
View File
@@ -0,0 +1,51 @@
# XINFRA 简历技术要点
> 基于项目源码实际实现提炼,每个要点均可深入到函数级别展开
---
## 1. 设计并落地生产级 PostgreSQL HA 集群方案
基于 Patroni + etcd + HAProxy + Keepalived + pgBackRest 构建 3 节点共置高可用架构。通过 Keepalived unicast 模式实现 VIP 漂移,HAProxy 以 Patroni REST API `/primary` 健康检查实现读写分离路由;pgBackRest 双仓库(repo1/repo2 分布在不同节点)保障备份冗余。部署流水线经 preflight 静态预检(CPU/内存/磁盘/端口/L2 可达性/VIP 冲突检测)→ 3-Play 编排部署 → final_acceptance 验收(Patroni 健康/3 running + 1 primary/同步复制 2 streaming + 1 sync/etcd 3 members/HAPRoxy 端点/VIP 所有权)三阶段闭环,支持计划切换(patronictl switchover)、滚动重启、控制验收等运维操作。
---
## 2. 实现 Vault Database Secrets Engine 全链路集成
对接 HashiCorp Vault 的 AppRole 认证 + Database Secrets Engine,实现数据库凭据全生命周期自动化。通过 `RegisterMySQLDatabaseConnection` 注册 MySQL 连接(mysql-database-plugin),`RegisterDatabaseStaticRole` 登记静态角色并校验 `skip_static_role_import_rotation` 是否生效——在密码仍被 MHA/Archery 等运行链路依赖时,拒绝静默降级为轮换模式,确保凭据安全。内置 token 自动续期(TTL 前 1 分钟刷新)和 401/403 请求重试机制,消除 Vault token 过期导致的间歇性故障。
---
## 3. 构建多协议统一认证网关(SAML 2.0 + OAuth 2.0/OIDC + JWT)
从零实现 SAML 2.0 SP:AuthnRequest deflate+base64 编码 → HTTP-Redirect 绑定,SAMLResponse 解析支持加密断言解密(RSA-OAEP 密钥传输 + AES-128/192/256-CBC/GCM 数据加密),SP 元数据 XML 自动生成。同时实现完整 OAuth 2.0 Authorization Code 流程作为 OIDC Provider:OAuth Code 以 Redis SetNX+TTL 存储保证一次性消费,Token 端点按 client 差异化签发(Wayen/CloudDM: HS256 access_token,Archery: RS256 PlatformToken),JWKS 端点暴露 RSA 公钥。JWT 层设计四种 Claims 结构(访问令牌/OAuth Code/OIDC ID Token/平台令牌),支持 HS256/RS256 双算法,Redis 集中吊销。
---
## 4. 实现 Ansible MySQL 全生命周期自动化(11 个 Playbook)
覆盖 MySQL 从部署到销毁的完整链路:deploy(参数校验 → MHA 制品安装 → 原子二进制安装[flock 并发锁+SHA256 校验+aria2 多线程] → 账号配置[mktemp+chmod 600+trap 防泄露] → GTID 复制[Clone 全量克隆+server_uuid 重新生成+errant/missing GTID 校验] → MHA 配置 → 健康检查 → 注册回调)、precheck(Python 脚本结构化 JSON 输出)、failover(普通主从切换 + MHA masterha_master_switch 双模式)、config-apply(my.cnf 片段写入 + 逐台滚动重启 + SQL 执行 + root 密码轮换)、decommission(先停从库 → 主库设 read_only → 写下线标记)、rejoin(旧主 Clone 接收 + GTID 复制重建)、purge(校验下线标记 → 验证删除)。MHA 集成包含 deb 包修补(修复 MySQL 8.x 版本解析正则)、ed25519 集群独立 SSH 密钥、故障转移回调脚本。
---
## 5. 设计交付调度器状态机与幂等性保障
交付系统采用 16 状态状态机(DeliveryTask)+ 幂等键(IdempotencyKey)防重复提交。配额管理通过 ResourceQuota + ResourceReservation 实现业务线级资源预留与释放。PostgreSQL HA 操作引入 14 状态生命周期,支持 AWX Workflow Job 调度、preflight 预检、config 在线变更、control 操作(restart/rolling_restart/switchover)、cleanup 清理等完整运维操作。任务事件流通过 SSE(Server-Sent Events)实现实时进度推送,前端通过 `createTaskLogStream` 订阅。凭证揭示采用一次性消费模式(pending → revealed → expired),支持 JSON 和 XLSX 两种导出格式。
---
## 6. 实现 Receptor 节点自动化供应系统(独立 Worker 进程)
独立运行的 Receptor Worker 进程,以可配置并发度(RECEPTOR_PROVISIONING_CONCURRENCY)执行节点供应循环。多阶段管线:pending → prechecking → registering_awx → preparing_artifacts → installing → checking_remote → checking_awx → attaching_instance_group → succeeded,失败分支覆盖 precheck_failed/awx_failed/artifact_failed/install_failed/mesh_unreachable/health_check_failed。SSH 凭据采用 PGP + AES 双层加密(随机 DataKey AES 加密私钥,PGP 公钥加密 DataKey)。制品下载支持多架构(amd64/arm64),内置 SHA256 校验。下线流程独立编排:decommissioning → awx_disabled → instance_group_detached → remote_uninstalling → certificate_invalidated → awx_cleaning。
---
## 7. 构建离线制品清单签名校验体系
实现 `xinfra.artifact-manifest.v1/v2` 清单 schema 的完整校验链:Ed25519 签名验证 + SHA256 digest 计算(排除 digest 和 signature 字段)+ source 前缀白名单 + 可达性检查。支持两种清单模式:Evidence Summary(校验 sha256 digest)和嵌入 Ed25519 签名的 manifest。独立 CLI 工具(`cmd/artifact-manifest`)支持 `--manifest`、`--expected-digest`、`--key-id`、`--public-key`、`--allowed-source-prefix`、`--reachable-source` 参数,校验结果输出 JSON,非 StatusReady 则 exit 2,可集成到 CI/CD 流水线作为门禁。同时实现不可变 APT 快照构建脚本:收集运行时包 → SHA256 校验 → 生成 Packages 索引 → GPG 签名(InRelease + Release.gpg)→ 上传 Nexus,配合幂等性校验脚本确保两次独立演练的 URI 集合和 SHA256 摘要完全一致。
---
## 8. 实现 Wayne 服务间 HMAC-SHA256 签名与 RBAC 权限模型
服务间调用采用 HMAC-SHA256 签名方案:payload = `METHOD\nURI\nTIMESTAMP\nNONCE\nBODY_SHA256`,通过 `X-Wayne-Service`/`X-Wayne-Timestamp`/`X-Wayne-Nonce`/`X-Wayne-Signature` 四个 headers 传递,验签使用 `hmac.Equal` 常量时间比较防止时序攻击。RBAC 权限模型设计为三级架构:平台级(PlatformRoleBinding → platform_admin)、业务线级(BusinessLineUser owner/member + BusinessLinePermission 细粒度权限)、子系统级(Wayne Namespace Role Binding + Archery Resource/Permission Group)。前端实现四级路由守卫(platformPermission → businessLineMember → businessLineOwner → capability/capabilities),支持权限别名映射(如 `machine.view` → `machine.read` + `machine.credential.read`)。全链路审计日志异步写入,覆盖登录、业务线权限、子系统授权、服务交付四类操作。
+585
View File
@@ -0,0 +1,585 @@
# XINFRA 安全·认证·数据报告
> 生成日期:2026-08-27 | 基于源码深度分析
---
## 一、认证架构总览
XINFRA 实现了多协议统一认证网关,支持本地登录、SAML 2.0 SSO、OAuth 2.0/OIDC 三种认证方式,并通过 JWT 实现会话管理。
```
┌─────────────────────────────────────────────────────────────┐
│ 认证网关层 │
│ ┌──────────┐ ┌──────────────┐ ┌──────────────────────┐ │
│ │ 本地登录 │ │ SAML 2.0 SSO │ │ OAuth 2.0 / OIDC │ │
│ │ POST / │ │ GET /login/ │ │ /auth/oauth/ │ │
│ │ login │ │ internal-sso │ │ authorize/token/ │ │
│ └────┬─────┘ └──────┬───────┘ │ jwks/userinfo │ │
│ │ │ └──────────┬───────────┘ │
│ └───────────────┼─────────────────────┘ │
│ ▼ │
│ ┌─────────────────┐ │
│ │ JWT 签发/校验 │ │
│ │ (HS256/RS256) │ │
│ └────────┬────────┘ │
│ ▼ │
│ ┌─────────────────┐ │
│ │ AuthMiddleware │ │
│ │ (Bearer Token) │ │
│ └─────────────────┘ │
└─────────────────────────────────────────────────────────────┘
```
---
## 二、本地登录
**端点:** `POST /auth/api/v1/login`
**流程:**
1. 接收 `username` + `password`
2. 查询数据库用户记录
3. 验证密码哈希
4. 签发 HS256 JWT(Claims: UserID, Username, Email, DisplayName, IsAdmin)
5. 返回 token
**限制:** SSO 启用时(`SSO_ENABLED=true`)禁用本地登录
---
## 三、SAML 2.0 SSO
### 3.1 组件
| 组件 | 路径 | 职责 |
|------|------|------|
| `sso/login.go` | `internal/sso/` | SAML SP 核心实现 |
| `sso/metadata.go` | `internal/sso/` | SP 元数据 XML 生成 |
| `handler/saml.go` | `internal/handler/` | HTTP Handler |
### 3.2 登录流程
```
用户 → GET /auth/api/v1/login/internal-sso
↓
BuildLoginRedirect()
↓
从 IDP 元数据获取 SSO URL
↓
构建 AuthnRequest(deflate + base64 编码)
↓
HTTP-Redirect 绑定 URL → 重定向到 IDP
↓
用户在 IDP 完成认证
↓
IDP POST SAMLResponse 到 /auth/api/v1/saml/acs
↓
DecodeSAMLResponse()
↓
Base64 解码 → 提取 NameID 和属性
↓
支持加密断言解密(RSA-OAEP + AES-CBC/GCM)
↓
创建/更新用户 + 签发 JWT
↓
重定向回前端(携带 sso_token)
```
### 3.3 加密算法支持
- **密钥传输:** RSA-OAEP
- **数据加密:**
- AES-128-CBC / AES-192-CBC / AES-256-CBC
- AES-128-GCM / AES-192-GCM / AES-256-GCM
### 3.4 SP 元数据
**端点:** `GET /auth/api/v1/saml/metadata`
生成标准 SAML SP 元数据 XML,包含:
- EntityDescriptor
- SPSSODescriptor
- 签名/加密 KeyDescriptor
- AssertionConsumerService (ACS) URL
### 3.5 登出
**端点:** `GET/POST /auth/api/v1/logout`
- 清除本地 JWT(Redis token 吊销)
- 可选重定向到 SAML IDP 登出 URL
---
## 四、OAuth 2.0 / OIDC Provider
### 4.1 端点
| 端点 | 路径 | 说明 |
|------|------|------|
| Discovery | `/auth/.well-known/openid-configuration` | OIDC 发现文档 |
| Authorize | `/auth/oauth/authorize` | 授权端点 |
| Token | `/auth/oauth/token` | Token 端点 |
| JWKS | `/auth/oauth/jwks` | 公钥端点 |
| UserInfo | `/auth/oauth/userinfo` | 用户信息端点 |
### 4.2 支持的 Client
| Client ID | 用途 | Token 类型 |
|-----------|------|-----------|
| `wayne` | Wayne K8s 管理平台 | HS256 access_token |
| `clouddm` | CloudDM 数据库管理 | HS256 access_token |
| `archery` | Archery SQL 审核 | RS256 PlatformToken |
### 4.3 授权码流程
```
1. 前端重定向到 /auth/oauth/authorize?client_id=...&redirect_uri=...&scope=...&state=...&nonce=...
2. 用户认证后,后端生成 OAuth Code(HS256 签名的 Claims)
3. OAuth Code 存储在 Redis(SetNX + TTL)
4. 重定向回 redirect_uri?code=...&state=...
5. 前端用 code 换取 token(POST /auth/oauth/token)
6. 后端校验 code → Redis 消费(一次性)→ 签发 access_token + id_token
```
### 4.4 Token 签发
- **access_token:** HS256,包含 UserID, Username, Email, IsAdmin
- **id_token:** RS256,符合 OIDC 规范,包含 UserID, Username, Email, EmailVerified, Name, IsAdmin, Nonce
- **Archery 特殊处理:** 签发 RS256 PlatformToken 替代 HS256 access_token
### 4.5 Token 吊销
- 通过 Redis key 检查实现
- 登出时将 token 加入吊销列表
- AuthMiddleware 校验时可选检查 Redis 吊销状态
---
## 五、JWT 机制
### 5.1 Claims 结构
| 类型 | 算法 | 用途 | 包含字段 |
|------|------|------|----------|
| `Claims` | HS256 | 访问令牌 | UserID, Username, Email, DisplayName, IsAdmin |
| `OAuthCodeClaims` | HS256 | OAuth 授权码 | UserID, ClientID, RedirectURI, Scope, Nonce |
| `IDTokenClaims` | RS256 | OIDC ID Token | UserID, Username, Email, EmailVerified, Name, IsAdmin, Nonce |
| `PlatformTokenClaims` | RS256 | 子系统平台令牌 | UserID, Username, Email, DisplayName, IsAdmin |
### 5.2 密钥管理
- **HS256:** 通过 `JWT_SECRET` 环境变量配置
- **RS256:** RSA 私钥从文件加载(支持 PKCS1/PKCS8 格式)
- JWK 构建:从 RSA 公钥导出
- KeyID 生成:SHA256 前 8 字节 Base64
- 通过 JWKS 端点暴露公钥
### 5.3 配置
| 配置项 | 说明 |
|--------|------|
| `JWT_SECRET` | HS256 密钥 |
| `JWT_ISSUER` | 签发者(默认 `authserver`) |
| `JWT_TTL_MINUTES` | Token 有效期(默认 120 分钟) |
---
## 六、权限模型
### 6.1 RBAC 架构
```
┌─────────────────────────────────────────────────┐
│ 平台级权限 │
│ PlatformRoleBinding: UserID → Role │
│ (platform_admin) │
├─────────────────────────────────────────────────┤
│ 业务线级权限 │
│ BusinessLineUser: UserID → Role (owner/member) │
│ BusinessLinePermission: UserID → Permission │
├─────────────────────────────────────────────────┤
│ 子系统级权限 │
│ Wayne: Namespace Role Binding │
│ Archery: Resource Group / Permission Group │
└─────────────────────────────────────────────────┘
```
### 6.2 平台级角色
- `platform_admin` — 平台管理员,存储在 `PlatformRoleBinding` 表
- 系统用户 `xinfra-platform` 在初始化时自动创建并授予此角色
### 6.3 业务线级角色
- `owner` — 业务线管理员,拥有所有业务线权限
- `member` — 业务线成员,按 `BusinessLinePermission` 授予权限
### 6.4 权限清单
**平台级权限(`platformPermission`):**
- `business_line.manage` — 业务线管理
**业务线级权限(`capability`):**
| 权限 | 说明 |
|------|------|
| `subsystem.wayne.manage` | Wayne 子系统管理(含 `role.grant` + `role.revoke`) |
| `subsystem.wayne.read` | Wayne 子系统只读 |
| `subsystem.archery.manage` | Archery 子系统管理 |
| `subsystem.archery.read` | Archery 子系统只读 |
| `machine.view` / `machine.manage` | 机器资源查看/管理 |
| `delivery.view` / `delivery.deploy` / `delivery.credentials` | 交付系统查看/部署/凭证 |
| `execution_profile.manage` | 执行配置管理 |
| `audit.login.view` / `audit.operation.view` | 审计日志查看 |
### 6.5 前端权限检查
**四级检查(路由守卫):**
1. `platformPermission` — `authStore.hasPlatformPermission()`
2. `businessLineMember` — `businessLineStore.current?.id` 存在性
3. `businessLineOwner` — `businessLineStore.isCurrentAdmin`
4. `capability` / `capabilities` — `authStore.hasAnyBusinessLinePermission()`
**权限别名映射(前端硬编码):**
- `subsystem.wayne.manage` → `subsystem.wayne.role.grant`, `subsystem.wayne.role.revoke`
- `machine.view` → `machine.read`, `machine.credential.read`
- `machine.manage` → `machine.create/update/delete/credential.manage/sync`
- `delivery.view` → `delivery.target.read`, `delivery.read`
- `delivery.deploy` → `delivery.create`
- `delivery.credentials` → `delivery.credential.reveal`
- `execution_profile.manage` → `execution_profile.publish`
---
## 七、审计日志
### 7.1 数据模型
**`AuditLog` 表:**
| 字段 | 类型 | 说明 |
|------|------|------|
| `ActorUserID` | uint | 操作者 ID |
| `ActorUsername` | string | 操作者用户名 |
| `ClientIP` | string | 客户端 IP |
| `Action` | string | 操作类型 |
| `ResourceType` | string | 资源类型 |
| `ResourceID` | string | 资源 ID |
| `ScopeType` | string | 作用域类型 |
| `ScopeID` | string | 作用域 ID |
| `BusinessLineID` | uint | 业务线 ID |
| `Decision` | string | 决策结果 |
| `Reason` | string | 原因 |
| `Metadata` | JSON | 元数据 |
### 7.2 审计分类
| 分类 | 端点 | 内容 |
|------|------|------|
| 登录审计 | `GET /auth/api/v1/audit/login` | 登录/登出事件 |
| 业务线权限审计 | `GET /auth/api/v1/audit/operations?category=business_line_permission` | 权限授予/撤销 |
| 子系统授权审计 | `GET /auth/api/v1/audit/operations?category=subsystem_authorization` | 子系统角色绑定 |
| 服务交付审计 | `GET /auth/api/v1/audit/operations?category=service_delivery` | 交付任务操作 |
### 7.3 写入机制
- `AuditService` 异步写入(不阻塞业务请求)
- 支持分页查询
---
## 八、数据库设计
### 8.1 连接与迁移
- **数据库:** MySQL 8.0
- **ORM:** GORM(`gorm.io/driver/mysql`)
- **迁移:** `AUTO_MIGRATE=true` 时自动迁移 40+ 张表
- **初始化:** `SeedDefaults` 创建系统平台用户 `xinfra-platform`
### 8.2 核心数据模型分组
#### 用户与权限(7 张表)
- `User` — 用户主表(软删除)
- `WayenCredential` — Wayne 平台凭据(软删除)
- `BusinessLine` — 业务线
- `BusinessLineUser` — 业务线成员关系
- `PlatformRoleBinding` — 平台角色绑定
- `BusinessLinePermission` — 业务线权限
- `BusinessLineWayneNamespace` / `BusinessLineSinaOrganization` — 业务线映射
#### 交付系统(12+ 张表)
- `DeliveryTask` — 交付任务(16 种状态)
- `ResourceQuota` / `ResourceReservation` — 配额与预留
- `MySQLPrecheckLease` — 预检租约(唯一约束)
- `DeploymentResult` / `DeploymentCredential` — 部署结果与凭证
- `VaultDatabaseAccount` — Vault 数据库账号
- `ServiceConfig` — 服务配置台账
- `MySQLDynamicVariable` — MySQL 动态变量(12 个种子数据)
- `ExecutionJob` / `RollbackJob` — 执行与回滚
- `TaskEvent` — 任务事件
- `AWXRuntimeInventory` — AWX 运行时 Inventory
#### MySQL HA(5 张表)
- `MySQLCluster` — 集群(HAMode: none/mha)
- `MySQLInstance` — 实例(Role: primary/replica)
- `MySQLReplicationChannel` — 复制通道
- `MySQLInternalCredential` — 内部凭据(Vault 注册后清除)
- `ServerIDAllocation` — Server ID 分配
#### PostgreSQL HA(8 张表)
- `PostgreSQLCluster` — 集群
- `PostgreSQLInstance` — 实例
- `PostgreSQLResourceUsage` — 资源使用
- `PostgreSQLCloudDMReservation` — CloudDM 预留
- `PostgreSQLHAOperation` — HA 操作(14 种状态)
- `PostgreSQLHAHostPlan` — 主机计划
- `PostgreSQLHAAudit` — HA 审计
- `PostgreSQLHAControlOperation` / `PostgreSQLHAJobLog`
#### 机器与 Receptor(6 张表)
- `MachineResource` — 机器资源
- `MachineCredential` — 机器凭据
- `MachineSyncState` — 同步状态
- `ReceptorNode` — Receptor 节点
- `ReceptorProvisioningTask` / `ReceptorProvisioningEvent` — 供应任务
- `SSHCredential` — SSH 凭据(PGP 加密)
#### 执行配置(3 张表)
- `ExecutionProfileRelease` — 配置发布
- `ExecutionBindingRelease` — 绑定发布
- `TaskExecutionSnapshot` — 执行快照
#### Archery 集成(5 张表)
- `BusinessLineArcheryResourceGroup` / `BusinessLineArcheryPermissionGroup`
- `ArcheryAccessSyncTask` — 同步任务
- `ArcheryInstanceBinding` / `ArcheryUserBinding`
#### 审计(1 张表)
- `AuditLog` — 统一审计日志
### 8.3 迁移逻辑
`AutoMigrate` 包含复杂的索引迁移逻辑:
- RBAC 迁移:从 `users.is_admin` 导入 `platform_role_bindings`
- 业务线角色迁移:`permission=0` 映射为 `owner`
- PostgreSQL HA UUID 准备:将空字符串转为 NULL 以支持唯一索引
- MySQL/PostgreSQL 台账索引重建:活跃集群/实例的唯一约束
- SSH 凭据 schema 迁移
- MySQL 动态变量种子数据
---
## 九、缓存策略
### 9.1 Redis 使用
| 用途 | Key 模式 | TTL |
|------|----------|-----|
| OAuth Code 存储 | `oauth:code:{code}` | 可配置(`OAuthCodeTTL`) |
| Token 吊销 | `token:revoked:{token}` | JWT 剩余有效期 |
| Wayne 容器数据缓存 | 业务相关 | 可配置(`CacheTTL`) |
### 9.2 配置
| 配置项 | 说明 |
|--------|------|
| `REDIS_ENABLED` | 是否启用 Redis |
| `REDIS_ADDR` | Redis 地址 |
| `REDIS_PASSWORD` | Redis 密码 |
| `REDIS_DB` | 数据库编号 |
| `REDIS_CONNECT_TIMEOUT` / `REDIS_READ_TIMEOUT` / `REDIS_WRITE_TIMEOUT` | 超时配置 |
---
## 十、Vault 密钥管理
### 10.1 架构
```
┌─────────────────────────────────────────────────┐
│ Vault Server │
│ ┌──────────────┐ ┌──────────────────────────┐ │
│ │ KV v2 │ │ Database Secrets Engine │ │
│ │ (通用密钥) │ │ (数据库凭据自动化) │ │
│ └──────────────┘ └──────────────────────────┘ │
└─────────────────────────────────────────────────┘
▲ ▲
│ │
┌────┴────┐ ┌─────┴─────┐
│ AppRole │ │ Static │
│ Auth │ │ Role │
└─────────┘ └───────────┘
```
### 10.2 认证方式
- **AppRole 认证:** 通过 `role_id` + `secret_id` 获取 token
- **自动续期:** TTL 前 1 分钟自动刷新
- **请求重试:** 401/403 自动重新认证后重试一次
### 10.3 KV v2 操作
| 方法 | 说明 |
|------|------|
| `ReadKVV2(path, key)` | 读取密钥 |
| `WriteKVV2(path, data)` | 写入密钥 |
| `DeleteKVV2(path, key)` | 删除密钥 |
### 10.4 Database Secrets Engine
#### MySQL 连接注册
```go
RegisterMySQLDatabaseConnection(vault, connectionName, dsn)
```
- plugin_name: `mysql-database-plugin`
- 校验 `skip_static_role_import_rotation` 是否生效
#### Static Role 注册
```go
RegisterDatabaseStaticRole(vault, roleName, connectionName, username, rotationPeriod)
```
- 读取 `database/static-creds/<role>` 验证凭据可用性
- 支持 `skip_import_rotation` 避免立即轮换密码
#### 凭据读取
```go
ReadDatabaseStaticCredentials(vault, roleName)
```
- 读取 Static Role 当前凭据(只用于运行时,不持久化)
#### 清理
```go
DeleteMySQLDatabaseConnection(vault, connectionName)
DeleteDatabaseStaticRole(vault, roleName)
```
### 10.5 配置
| 配置项 | 说明 |
|--------|------|
| `VAULT_ENABLED` | 是否启用 Vault |
| `VAULT_ADDR` | Vault 地址 |
| `VAULT_NAMESPACE` | Vault Namespace |
| `VAULT_TOKEN` | Vault Token(备用) |
| `VAULT_KVV2_MOUNT_PATH` | KV v2 挂载路径 |
| `VAULT_APPROLE_ROLE_ID` / `VAULT_APPROLE_SECRET_ID` | AppRole 凭据 |
| `VAULT_DATABASE_MYSQL_CONNECTION_NAME` | MySQL 连接名 |
| `VAULT_DATABASE_STATIC_ROLE_PREFIX` | Static Role 前缀 |
| `VAULT_DATABASE_ROTATION_PERIOD` | 密码轮换周期(默认 87600h = 10 年) |
---
## 十一、凭据安全
### 11.1 SSH 凭据加密
**`SSHCredential` 模型:**
- `PrivateKeyCiphertext` — PGP 加密的私钥
- `EncryptedDataKey` — 加密的 DataKey
- `KnownHosts` — 已知主机
- `HostKeyPolicy` — 主机密钥策略
**加密方案:**
- 生成随机 DataKey
- 使用 DataKey 的 AES 密钥加密私钥
- 使用 PGP 公钥加密 DataKey
- 存储加密后的私钥和加密后的 DataKey
### 11.2 交付凭据
**`DeploymentCredential` 模型:**
- `Ciphertext` — 加密的凭据
- `Nonce` — 加密 nonce
- `Status` — 状态(pending/revealed/expired)
- `ViewedBy` — 查看者
**凭证揭示:**
- `POST /delivery/tasks/:id/credentials/reveal`
- 支持 JSON 和 XLSX 下载两种格式
- 一次性揭示,标记为 `revealed`
### 11.3 临时文件安全
Ansible playbook 中的凭据处理:
```yaml
# 创建临时文件
- tempfile:
state: file
suffix: .cred
register: cred_file
# 写入凭据
- copy:
content: "{{ password }}"
dest: "{{ cred_file.path }}"
mode: '0600'
# 使用后清理
- file:
path: "{{ cred_file.path }}"
state: absent
```
使用 `mktemp` + `chmod 600` + `trap` 模式,避免命令行泄露。
---
## 十二、Wayne 服务间签名
### 12.1 签名方案
**算法:** HMAC-SHA256
**签名 Payload:**
```
METHOD\nURI\nTIMESTAMP\nNONCE\nBODY_SHA256
```
**Headers:**
- `X-Wayne-Service` — 服务名
- `X-Wayne-Timestamp` — 时间戳
- `X-Wayne-Nonce` — 随机 nonce
- `X-Wayne-Signature` — HMAC-SHA256 签名
### 12.2 验签
- 常量时间比较(`hmac.Equal`),防止时序攻击
- 校验时间戳有效期
---
## 十三、安全配置汇总
### 13.1 传输安全
- K8s 内部通信使用 HTTP(Service DNS)
- 外部访问通过 NodePort 暴露
- SAML SP 证书路径可配置
### 13.2 认证安全
- JWT Token 有效期可配置(默认 120 分钟)
- Token 吊销通过 Redis 实现
- SAML 加密断言支持 RSA-OAEP + AES-CBC/GCM
- OAuth Code 一次性使用(Redis SetNX + 消费)
### 13.3 数据安全
- SSH 凭据 PGP + AES 加密
- 交付凭据 AES 加密 + 一次性揭示
- Vault 集成实现密钥自动化管理
- 数据库密码定期轮换(默认 10 年)
### 13.4 访问控制
- 三级 RBAC:平台级 → 业务线级 → 子系统级
- 路由级权限控制(路由守卫四级检查)
- API 级权限控制(AuthMiddleware + 业务逻辑校验)
- 全链路审计日志
+7
View File
@@ -102,3 +102,10 @@ nav:
- HeavyKeeper: algorithm/heavykeeper.md
- 二叉树的遍历: algorithm/binary-tree-traversal.md
- 队列: algorithm/queue.md
- 七牛云:
- qiniu-cloud/index.md
- 前后端架构: qiniu-cloud/architecture.md
- Ansible 自动化: qiniu-cloud/ansible-automation.md
- 部署与 CI/CD: qiniu-cloud/deployment-cicd.md
- 安全·认证·数据: qiniu-cloud/security-auth-data.md
- 简历技术要点: qiniu-cloud/resume-tech-points.md