diff --git a/docs/qiniu-cloud/ansible-automation.md b/docs/qiniu-cloud/ansible-automation.md new file mode 100644 index 0000000..97f3e6e --- /dev/null +++ b/docs/qiniu-cloud/ansible-automation.md @@ -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 自动化配置 diff --git a/docs/qiniu-cloud/architecture.md b/docs/qiniu-cloud/architecture.md new file mode 100644 index 0000000..74bac1a --- /dev/null +++ b/docs/qiniu-cloud/architecture.md @@ -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` | JWT token,从 localStorage 初始化 | +| `user` | `ref` | 用户信息对象 | +| `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` | 所有业务线 | +| `currentBL` | `ref` | 当前选中,从 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 = { code: number, message: string, data: T, details?: Record } +PaginatedData = { 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` — 内容区:`` +- `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 ` +3. 后端 AuthMiddleware 校验 JWT,可选 Redis token 吊销检查 +4. SSO 场景:SAML IDP → ACS → JWT 签发 → 前端消费 sso_token +5. OAuth 场景:前端 → `/auth/oauth/authorize` → Redis code → `/auth/oauth/token` → JWT diff --git a/docs/qiniu-cloud/deployment-cicd.md b/docs/qiniu-cloud/deployment-cicd.md new file mode 100644 index 0000000..070d0c9 --- /dev/null +++ b/docs/qiniu-cloud/deployment-cicd.md @@ -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=]` | 调用 `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...` | diff --git a/docs/qiniu-cloud/index.md b/docs/qiniu-cloud/index.md new file mode 100644 index 0000000..c3b0245 --- /dev/null +++ b/docs/qiniu-cloud/index.md @@ -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` | diff --git a/docs/qiniu-cloud/resume-tech-points.md b/docs/qiniu-cloud/resume-tech-points.md new file mode 100644 index 0000000..ebbdf3c --- /dev/null +++ b/docs/qiniu-cloud/resume-tech-points.md @@ -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`)。全链路审计日志异步写入,覆盖登录、业务线权限、子系统授权、服务交付四类操作。 diff --git a/docs/qiniu-cloud/security-auth-data.md b/docs/qiniu-cloud/security-auth-data.md new file mode 100644 index 0000000..2fdcce6 --- /dev/null +++ b/docs/qiniu-cloud/security-auth-data.md @@ -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/` 验证凭据可用性 +- 支持 `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 + 业务逻辑校验) +- 全链路审计日志 diff --git a/mkdocs.yml b/mkdocs.yml index 37cf9e9..3b8a1ea 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -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