# AutoSSO 部署指南

本文档介绍如何将 AutoSSO 部署到阿里云 ECS 服务器上。适用于运维人员和开发者，覆盖从编译到上线的完整流程。

---

## 目录

- [一、项目简介](#一项目简介)
- [二、前置条件](#二前置条件)
- [三、飞书应用配置](#三飞书应用配置)
- [四、阿里云跨账号 RAM 角色链配置](#四阿里云跨账号-ram-角色链配置)
- [五、部署文件清单](#五部署文件清单)
- [六、编译项目](#六编译项目)
- [七、准备配置文件](#七准备配置文件)
- [八、飞书审批表单设计规范](#八飞书审批表单设计规范)
- [九、初始化数据库](#九初始化数据库)
- [十、上传到服务器](#十上传到服务器)
- [十一、在服务器上运行](#十一在服务器上运行)
- [十二、安全加固（生产环境推荐）](#十二安全加固生产环境推荐)
- [十三、使用 Pulumi 自动化部署（可选）](#十三使用-pulumi-自动化部署可选)
- [十四、常见问题排查](#十四常见问题排查)
- [附录：配置文件详解](#附录配置文件详解)

---

## 一、项目简介

AutoSSO 是一个多云 IAM SSO 权限自动化管理系统。它对接飞书审批流程，自动完成以下工作：

1. **监听审批** — 轮询飞书获取新的权限申请审批
2. **分配权限** — 审批通过后自动在云平台（AWS / GCP / Azure / 阿里云）分配 SSO 权限
3. **到期监控** — 自动检测过期权限并发送提醒通知
4. **自动回收** — 宽限期过后自动移除过期权限

---

## 二、前置条件

### 2.1 本地开发机

| 依赖 | 版本要求 | 用途 |
|------|---------|------|
| Go | 1.23+ | 编译项目 |
| SSH 密钥 | - | 连接远程服务器 |

### 2.2 远程服务器

| 依赖 | 版本要求 | 用途 |
|------|---------|------|
| Linux (x86_64) | 阿里云 Linux / Ubuntu / CentOS | 运行 AutoSSO |
| MySQL | 5.7+ | 存储审批状态数据 |
| SSH 服务 | - | 远程管理 |

> **网络要求**：ECS 需位于 VPC 内，**无需**开放公网入方向端口（如 80/443），程序仅通过出方向访问阿里云 API 和飞书 API。建议仅开放 SSH (22) 端口给特定的办公网 IP。

> **数据库推荐**：推荐使用阿里云 **RDS MySQL** 或同 VPC 内的自建 MySQL。确保 ECS 安全组（出方向）允许访问数据库端口（默认 3306），并将 ECS **内网 IP** 加入 RDS 白名单。

### 2.3 云平台凭证

根据你要启用的云平台，准备对应凭证：

- **阿里云**：ECS RAM Role（推荐，部署在 ECS 上免 AK）或 Access Key
- **飞书**：App ID + App Secret + 审批流程 Code + 群聊 ID

---

## 三、飞书应用配置

在部署 AutoSSO 之前，需要先在飞书开放平台创建应用并获取必要的凭证。

### 3.1 创建飞书应用

1. 登录 [飞书开放平台](https://open.feishu.cn/)。
2. 点击 "创建应用"，选择 "**企业自建应用**"。
3. 填写应用名称（例如：AutoSSO）、描述等信息，点击创建。

### 3.2 获取应用凭证

在应用详情页的 "基础信息" → "凭证与基础信息" 中，获取以下信息：

- **App ID** (`app_id`)
- **App Secret** (`app_secret`)

### 3.3 开启机器人能力

1. 在左侧菜单选择 "添加应用能力" → "机器人"。
2. 点击 "添加机器人"。

### 3.4 配置 API 权限

在 "开发配置" → "权限管理" 中，开通以下 **必要权限**：

| 权限范围 | 权限 Scope | 说明 |
|---------|-----------|------|
| 审批 | `approval:approval:readonly` | 访问审批应用（读取审批数据） |
| 审批 | `approval:approval` | 管理审批（获取审批详情） |
| 通讯录 | `contact:user.email:readonly` | 根据邮箱查找用户 |
| 通讯录 | `contact:user.id:readonly` | 获取用户 ID |
| 消息 | `im:message:send_as_bot` | 以机器人身份发送通知消息 |

> **注意**：添加权限后，需要**发布应用版本**并在飞书管理后台**审核通过**后才能生效。

### 3.5 发布应用

1. 在飞书开放平台 → 你的应用 → 版本管理与发布。
2. 创建一个新版本，填写版本描述。
3. 提交审核，等待企业管理员审核通过。

### 3.6 获取审批定义编码 (Approval Code)

1. 在飞书管理后台或通过 API 创建一个审批定义。
2. 获取该审批定义的 Code，这将作为配置中的 `approval_code`。
3. Approval Code 通常在审批管理后台的 URL 中或创建时获取（格式如 `202402010001`）。

### 3.7 获取群组 ID (Chat ID)

1. 创建一个用于接收通知的飞书群组。
2. 将刚才创建的机器人添加到群组中。
3. 可以通过 API 或在网页版飞书 URL 中获取 `chat_id`（格式如 `oc_1234567890abcdef`）。

### 3.8 填写配置

在 `config.yaml` 中填写飞书凭证：

```yaml
feishu:
  app_id: "你的AppID"           # 或使用环境变量: ${FEISHU_APP_ID}
  app_secret: "你的AppSecret"   # 或使用环境变量: ${FEISHU_APP_SECRET}
  approval_code: "你的ApprovalCode"
  chat_id: "你的ChatID"
  base_url: "https://open.feishu.cn"
```

或者使用环境变量避免在配置文件中暴露敏感信息：

```bash
export FEISHU_APP_ID="cli_a1b2c3d4e5"
export FEISHU_APP_SECRET="your_app_secret_here"
```

---

## 四、阿里云跨账号 RAM 角色链配置

这是**推荐的生产环境部署方案**。适用于将 AutoSSO 部署在管理账号的阿里云 ECS 实例上，去管理另一个或多个生产账号的 CloudSSO 资源。

### 4.1 架构说明

此模式利用 ECS 绑定的**实例角色 (Instance Role)** 作为初始身份（无需在代码中配置 AK/SK），通过该角色去**扮演 (Assume)** 目标生产账号的角色。SDK 会自动从 ECS 元数据服务器获取临时凭证并自动轮转，安全性最高。

```
ECS (管理账号) → AutoSSO-Management-Role (实例角色) → AutoSSO-Production-Role (生产账号角色) → 操作 CloudSSO
```

**假设**：
- **管理账号 UID**: `11111111`（程序运行在此账号的 ECS 上）
- **生产账号 UID**: `22222222`（目标 CloudSSO 资源在此账号）

### 4.2 步骤 1：在【管理账号】创建 ECS 实例角色

1. 登录**管理账号** (`11111111`) 的 RAM 控制台。
2. 创建角色 `AutoSSO-Management-Role`。
3. **可信实体类型**选择 "阿里云服务"，受信服务选择 "**云服务器 (ECS)**"。
   - 这意味着 ECS 实例可以扮演此角色。
4. 创建自定义权限策略，允许扮演生产账号的角色：

```json
{
    "Version": "1",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": "sts:AssumeRole",
            "Resource": "acs:ram::22222222:role/AutoSSO-Production-Role"
        }
    ]
}
```

5. 将该策略授权给 `AutoSSO-Management-Role`。

### 4.3 步骤 2：将角色绑定到 ECS 实例

1. 在 ECS 控制台，找到部署 AutoSSO 的实例。
2. 在实例详情页或操作菜单中，选择 "**授予/收回 RAM 角色**"。
3. 选择刚才创建的 `AutoSSO-Management-Role` 并绑定。

### 4.4 步骤 3：在【生产账号】创建被扮演的角色

1. 登录**生产账号** (`22222222`) 的 RAM 控制台。
2. 创建角色 `AutoSSO-Production-Role`。
3. **可信实体类型**选择 "**阿里云账号**"。
   - 填写**管理账号**的 UID (`11111111`) 作为受信云账号。
   - 信任策略会自动生成，允许管理账号的用户/角色扮演它。
4. 授予该角色 CloudSSO 相关权限。建议创建自定义权限策略 `AutoSSO-CloudSSO-Policy`：

```json
{
    "Version": "1",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "cloudsso:ListUsers",
                "cloudsso:ListAccessConfigurations",
                "cloudsso:CreateAccessAssignment",
                "cloudsso:DeleteAccessAssignment"
            ],
            "Resource": "*"
        }
    ]
}
```

> **安全提示**：`Resource` 字段可以进一步限制为特定的 Directory ARN 以增强安全性。

5. 获取该角色的 ARN，例如：`acs:ram::22222222:role/AutoSSO-Production-Role`。

### 4.5 步骤 4：配置 AutoSSO

在 `config.yaml` 中使用 `ram_role_arn` 模式：

```yaml
providers:
  alibaba:
    # 认证方式选择 ram_role_arn (跨账号扮演)
    auth_type: "ram_role_arn"
    
    # 【留空】不要填写 AK/SK，SDK 会自动检测 ECS 环境并使用实例角色
    # access_key_id: ""
    # access_key_secret: ""
    
    # 【必需】ECS 实例角色的名称 (作为 Source Credential)
    role_name: "AutoSSO-Management-Role"
    
    # 【必需】生产账号的目标角色 ARN (作为 Target Role)
    role_arn: "acs:ram::22222222:role/AutoSSO-Production-Role"
    
    role_session_name: "autosso-ecs-session"
    directory_id: "生产账号的DirectoryID"
    endpoint: "cloudsso.cn-shanghai.aliyuncs.com"
    
    accounts:
      - id: "22222222"
        name: "生产账号"
        type: "prod"
```

> **关键点**：`access_key_id` 和 `access_key_secret` **留空或注释掉**，程序检测到为空会自动使用 ECS 实例角色获取临时凭证。

### 4.6 获取 Cloud SSO 信息

1. 进入目标账号（通常是生产账号或资源目录管理账号）的 Cloud SSO 控制台。
2. 获取 **目录 ID** (`directory_id`)。
3. 确认 Cloud SSO 已开通（否则会报 `InvalidServiceStatus: Cloud SSO is not activated`）。

---

## 五、部署文件清单

`ssh/` 目录包含了部署所需的全部文件：

```
ssh/
├── autosso                  # 已编译的 Linux x86_64 可执行文件
├── 001_create_tables.sql    # 数据库初始化脚本（建表）
├── config.yaml              # 生产环境配置文件（需按实际修改）
├── upload.sh                # 一键上传脚本
├── ip.txt                   # 记录服务器 SSH 连接命令
├── jsonfor.json             # 运行日志样例（供排查参考）
├── error.txt                # 错误日志样例（供排查参考）
└── key.pem                  # SSH 私钥（不在仓库中，需自行准备）
```

---

## 六、编译项目

如果 `ssh/autosso` 已经是最新编译的二进制文件，可以跳过此步。

### 6.1 前置条件

- **Go 版本**：项目 `go.mod` 要求 Go 1.23.0+，请确认本地 Go 版本满足要求
- 所有依赖均为纯 Go 实现（MySQL 驱动 `go-sql-driver/mysql` 无 CGO 依赖），编译无需安装额外 C 库

```bash
# 确认 Go 版本
go version
# 预期输出：go version go1.23.x ...

# 下载依赖（首次编译或 go.mod 有变更时执行）
go mod download
```

### 6.2 交叉编译（macOS/Windows → Linux）

**生产构建（推荐）**：带 `-ldflags` 优化，去掉调试信息，减小文件体积

```bash
# 在项目根目录执行（macOS / Linux）
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags="-s -w" -o ssh/autosso ./cmd/autosso/

# Windows PowerShell
$env:CGO_ENABLED="0"; $env:GOOS="linux"; $env:GOARCH="amd64"; go build -ldflags="-s -w" -o ssh/autosso ./cmd/autosso/
```

**开发构建**：保留调试信息，方便排查问题

```bash
# 开发编译（不带 ldflags，文件较大但包含调试符号）
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o ssh/autosso ./cmd/autosso/

# 或者本地直接运行（不编译二进制，仅用于开发调试）
go run ./cmd/autosso/ -config ./config/config.yaml
```

**ARM64 架构**：如果目标服务器是 ARM 架构（如阿里云 g8y/c8y 实例），改用 `GOARCH=arm64`

```bash
CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build -ldflags="-s -w" -o ssh/autosso ./cmd/autosso/
```

参数说明：
- `CGO_ENABLED=0`：禁用 CGO，确保纯静态编译，生成的二进制无外部库依赖
- `GOOS=linux GOARCH=amd64`：编译为 Linux x86_64 可执行文件（阿里云 ECS 默认架构）
- `-ldflags="-s -w"`：`-s` 去掉符号表、`-w` 去掉 DWARF 调试信息，通常可减小约 30% 文件体积
- `-o ssh/autosso`：输出到 `ssh/` 目录，方便后续 `upload.sh` 直接上传

### 6.3 验证编译结果

```bash
# 检查文件类型
file ssh/autosso
# 预期输出：ELF 64-bit LSB executable, x86-64 ...（amd64）
# 或：ELF 64-bit LSB executable, ARM aarch64 ...（arm64）

# 查看文件大小（生产构建通常 20-40 MB）
ls -lh ssh/autosso
```

### 6.4 代码质量检查（可选）

项目配置了 `.golangci.yml` 代码检查规则，编译前可运行 lint 检查：

```bash
# 需要先安装 golangci-lint：https://golangci-lint.run/usage/install/
golangci-lint run

# 运行单元测试
go test ./...
```
---

## 七、准备配置文件

### 7.1 复制并编辑配置

```bash
# 如果还没有 config.yaml，从模板复制
cp config/config.example.yaml ssh/config.yaml
```

### 7.2 必须修改的配置项

编辑 `ssh/config.yaml`，至少修改以下内容：

```yaml
# 飞书配置 —— 改成你的飞书应用凭证
feishu:
  app_id: "你的飞书App ID"
  app_secret: "你的飞书App Secret"
  approval_code: "你的审批流程Code"
  chat_id: "通知群聊ID"

# 数据库配置 —— 改成你的 MySQL 连接信息
database:
  host: "127.0.0.1"          # 数据库地址（本机则用 127.0.0.1）
  port: 3306
  user: "root"               # 数据库用户名
  password: "你的数据库密码"   # 数据库密码
  database: "security"        # 数据库名

# 阿里云配置 —— 改成你的阿里云信息
providers:
  alibaba:
    auth_type: "ecs_ram_role"               # ECS 部署推荐用这个
    role_name: "AutoSSO-Management-Role"    # ECS 绑定的 RAM 角色名
    directory_id: "你的云SSO目录ID"
    endpoint: "cloudsso.cn-shanghai.aliyuncs.com"
    accounts:
      - id: "你的阿里云账号ID"
        name: "账号名称"
        type: "dev"
```

### 7.3 权限映射配置

如果飞书审批表单中的角色名称和阿里云 CloudSSO 中的访问配置名称不一致，需要配置映射：

```yaml
permission_mappings:
  - feishu_name: "只读权限 (ReadOnly)"        # 飞书表单中展示的名称
    real_name: "AliyunCloudSSOReadOnlyAccess"  # 阿里云 CloudSSO 中的真实名称
  - feishu_name: "DBA 运维权限"
    real_name: "DBA-Custom-Policy"
```

> 如果表单选项值和云端名称完全一致，可以不配置映射。

---

## 八、飞书审批表单设计规范

为了使 AutoSSO 正确解析审批数据，飞书审批表单的控件名称和类型必须符合以下规范。程序通过**关键词匹配**来识别字段，因此请确保字段名称包含指定的关键词。

### 8.1 必需字段

| 字段用途 | 建议控件类型 | 字段名称必须包含的关键词（任选其一） | 选项/值填写规范 |
| :--- | :--- | :--- | :--- |
| **申请人邮箱** | 单行文本 (Input) | `申请人邮箱`、`Applicant Email`、`邮箱`、`email` | 用户填写有效邮箱地址 |
| **申请类型** | 单选 (Radio) | `申请类型`、`Application Type` | 选项必须包含 `SSO` 或 `生产账号申请` 字样 |
| **账号选择** | 单选/下拉 (Select) | **阿里云**: `生产账号选择`、`Alibaba Account`、`alibaba cloud` | 选项值格式: `账号别名 (#账号ID)` |
| **角色权限** | 多选 (Checkbox) | **阿里云**: `选择生产 SSO Role`、`Alibaba SSO Role`、`alibaba role` | 选项值必须是云端真实角色名称 |
| **申请时间** | 日期区间 (DateInterval) | （名称不限，控件类型必须是日期区间） | 系统自动读取开始/结束时间 |
| **申请说明** | 多行文本 (Textarea) | （无限制） | 仅做记录，不参与逻辑处理 |

### 8.2 账号选项格式（重要）

账号选项**必须**使用以下格式，系统会自动提取 `#` 后的数字作为 Account ID：

```
账号别名 (#账号ID)
```

**示例**：
- `Production-DB (#22222222)`
- `Staging-App (#33333333)`
- `交易核心库 (#123456)`

### 8.3 角色选项与权限映射

飞书表单中的角色选项需与 `config.yaml` 中的 `permission_mappings` 一致：

- 如果表单选项值和阿里云 CloudSSO 中的访问配置名称**完全一致**，无需额外配置
- 如果**不一致**，需要在 `permission_mappings` 中配置映射关系（见§七.3）

### 8.4 配置示例图解

```
┌─────────────────────────────────────────────────────────────┐
│                  云平台权限申请                                │
├─────────────────────────────────────────────────────────────┤
│ 1. 申请人邮箱 *                                               │
│    ┌────────────────────────────────────────────┐            │
│    │ user@example.com                           │            │
│    └────────────────────────────────────────────┘            │
│                                                              │
│ 2. 申请类型 *                                                 │
│    ○ 阿里云生产账号申请 (SSO)                                   │
│    ○ 其他申请                                                 │
│                                                              │
│ 3. 生产账号选择 *                                              │
│    ┌────────────────────────────────────────────┐            │
│    │ ▼ Production-DB (#22222222)                │            │
│    └────────────────────────────────────────────┘            │
│                                                              │
│ 4. 选择生产 SSO Role *                                        │
│    ☑ AliyunCloudSSOFullAccess                                │
│    ☐ DBaaS-Prod-ViewOnly-Role                                │
│    ☐ Admin-Role                                              │
│                                                              │
│ 5. 使用时间 *                                                 │
│    ┌───────────┐    ┌───────────┐                           │
│    │ 2026-02-05│ 至 │ 2026-02-12│                           │
│    └───────────┘    └───────────┘                           │
│                                                              │
│ 6. 申请说明                                                   │
│    ┌────────────────────────────────────────────┐            │
│    │ 需要访问生产环境排查问题                       │            │
│    └────────────────────────────────────────────┘            │
│                                                              │
│                        [提交]                                 │
└─────────────────────────────────────────────────────────────┘
```

### 8.5 自定义字段映射（高级）

如果你的表单使用了非标准字段名称，可以在 `config.yaml` 中配置自定义映射来覆盖默认关键词：

```yaml
feishu:
  app_id: "${FEISHU_APP_ID}"
  app_secret: "${FEISHU_APP_SECRET}"
  approval_code: "your-approval-code"
  
  # 自定义字段映射配置
  field_mappings:
    # 邮箱字段关键词
    email:
      - "applicant email"
      - "employee email"
      - "user email"

    # 申请类型关键词
    app_type:
      - "application type"
      - "request type"

    # 账号字段关键词（按云平台）
    account:
      alibaba: ["alibaba account", "alibaba cloud", "aliyun"]
      default: ["production account"]

    # 权限字段关键词（按云平台）
    permission:
      alibaba: ["alibaba role", "aliyun sso"]
      default: ["sso role", "permission"]

    # 时间字段关键词
    start_time: ["start time", "begin time", "effective from"]
    end_time: ["end time", "expire time", "valid until"]

    # 描述字段关键词
    description: ["description", "reason", "justification"]

    # 云平台检测关键词
    cloud_detection:
      alibaba: ["alibaba", "aliyun"]

    # SSO 类型检测关键词
    sso_type_keywords:
      - "sso"
      - "production account"
      - "temporary access"

    # 默认云平台（未检测到时使用）
    default_cloud_type: "Alibaba"

    # 自定义日期格式
    date_formats:
      - "2006-01-02T15:04:05Z07:00"
      - "2006-01-02 15:04:05"
      - "2006-01-02"
```

### 8.6 支持的日期格式

系统默认支持以下日期格式：

| 格式 | 示例 |
|-----|------|
| RFC3339 (推荐) | `2026-02-05T15:00:00+08:00` |
| RFC3339 UTC | `2026-02-05T15:00:00Z` |
| 日期时间 | `2026-02-05 15:04:05` |
| 仅日期 | `2026-02-05` |
| 斜杠格式 | `2026/02/05` |
| 美式格式 | `02/05/2026` |

如需支持其他格式，可通过 `date_formats` 配置添加。

### 8.7 表单故障排查

| 问题 | 可能原因 | 解决方案 |
|-----|---------|---------|
| 无法识别邮箱 | 字段名不含 email 关键词 | 配置 `field_mappings.email` |
| 账号 ID 为空 | 选项格式不正确 | 使用 `Name (#ID)` 格式 |
| 云平台识别错误 | 字段名含多个云平台关键词 | 配置 `cloud_detection` 或 `default_cloud_type` |
| 日期解析失败 | 日期格式不支持 | 添加 `date_formats` 配置 |
| 角色权限未匹配 | 表单选项与 CloudSSO 名称不一致 | 配置 `permission_mappings` |

> **调试技巧**：启用 `log_level: "debug"` 可查看详细的字段解析日志。

---

## 九、初始化数据库

### 9.1 创建数据库

```bash
# SSH 登录服务器后
mysql -u root -p -e "CREATE DATABASE IF NOT EXISTS security CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;"
```

### 9.2 执行建表脚本

```bash
mysql -u root -p security < /root/autosso/001_create_tables.sql
```

这个脚本会创建 4 张表：

| 表名 | 用途 | 替代的旧文件 |
|------|------|-------------|
| `approval_instances` | 存储飞书审批实例 | instancesid.txt |
| `pending_removals` | 存储待移除的权限记录 | wait_delete_up.txt |
| `processed_instances` | 记录已处理的审批实例 | deleted_permission.txt |
| `ntgper` | 非标准权限集（兼容旧数据） | - |

> **注意**：程序启动时也会自动检查并创建所需的表结构，但建议首次部署时手动执行建表脚本以确认数据库连接正常。

### 9.3 验证建表成功

```bash
mysql -u root -p security -e "SHOW TABLES;"
```

预期输出：
```
+--------------------+
| Tables_in_security |
+--------------------+
| approval_instances |
| ntgper             |
| pending_removals   |
| processed_instances|
+--------------------+
```

---

## 十、上传到服务器

### 10.1 准备 SSH 密钥

将你的 SSH 私钥文件放到 `ssh/` 目录下，命名为 `key.pem`：

```bash
cp /你的密钥路径/xxx.pem ssh/key.pem
chmod 600 ssh/key.pem
```

### 10.2 使用 upload.sh 一键上传

```bash
# 基本用法：上传默认文件（autosso + 001_create_tables.sql）
./ssh/upload.sh 你的服务器IP

# 上传指定文件
./ssh/upload.sh 你的服务器IP autosso 001_create_tables.sql config.yaml

# 自定义远程目录
REMOTE_DIR=/opt/autosso ./ssh/upload.sh 你的服务器IP config.yaml
```

**upload.sh 做了什么：**
1. 通过 SSH 在远程服务器创建目标目录（默认 `/root/autosso`）
2. 通过 SCP 将文件上传到该目录

**环境变量**：

| 变量 | 默认值 | 说明 |
|------|--------|------|
| `KEY_PATH` | `./ssh/key.pem` | SSH 私钥路径 |
| `REMOTE_USER` | `root` | SSH 登录用户 |
| `REMOTE_DIR` | `/root/autosso` | 远程部署目录 |

### 10.3 手动上传（如果不用脚本）

```bash
# 创建远程目录
ssh -i ssh/key.pem root@服务器IP "mkdir -p /root/autosso"

# 上传文件
scp -i ssh/key.pem ssh/autosso ssh/001_create_tables.sql ssh/config.yaml root@服务器IP:/root/autosso/
```

---

## 十一、在服务器上运行

### 11.1 SSH 登录服务器

```bash
ssh -i ssh/key.pem root@你的服务器IP
```

### 11.2 给程序加执行权限

```bash
chmod +x /root/autosso/autosso
```

### 11.3 前台运行（测试用）

```bash
cd /root/autosso
./autosso -config ./config.yaml
```

看到 `AutoSSO is running, press Ctrl+C to stop` 说明启动成功。按 `Ctrl+C` 可优雅停止。

### 11.4 后台运行（生产用）

```bash
cd /root/autosso
nohup ./autosso -config ./config.yaml > autosso.log 2>&1 &

# 记录 PID
echo $! > autosso.pid
```

### 11.5 配置 Systemd 服务（推荐）

创建服务文件：

```bash
cat > /etc/systemd/system/autosso.service << 'EOF'
[Unit]
Description=AutoSSO Permission Management Service
After=network.target mysql.service

[Service]
Type=simple
User=root
WorkingDirectory=/root/autosso
ExecStart=/root/autosso/autosso -config /root/autosso/config.yaml
Restart=on-failure
RestartSec=10
StandardOutput=append:/root/autosso/autosso.log
StandardError=append:/root/autosso/autosso.log

[Install]
WantedBy=multi-user.target
EOF
```

启用并启动服务：

```bash
systemctl daemon-reload
systemctl enable autosso      # 开机自启
systemctl start autosso       # 立即启动
systemctl status autosso      # 查看状态
```

常用操作：

```bash
systemctl stop autosso        # 停止（发送 SIGTERM，程序会优雅关闭）
systemctl restart autosso     # 重启
journalctl -u autosso -f     # 查看实时日志
```

> **提示**：如需更安全的 Systemd 配置（专用用户、分离错误日志、资源限制），请参考下一节「安全加固」。

---

## 十二、安全加固（生产环境推荐）

对于生产环境，建议按照以下步骤进行安全加固，遵循**最小权限原则**。

### 12.1 创建专用服务用户

避免使用 `root` 运行应用程序：

```bash
# 创建 autosso 用户，指定无登录 shell
useradd -r -s /bin/false autosso
```

### 12.2 规范目录结构与权限

```bash
# 创建标准目录
mkdir -p /opt/autosso/config
mkdir -p /opt/autosso/logs

# 移动文件到标准目录
cp /root/autosso/autosso /opt/autosso/
cp /root/autosso/config.yaml /opt/autosso/config/

# 设置文件归属和权限
chown -R autosso:autosso /opt/autosso
chmod 750 /opt/autosso
chmod +x /opt/autosso/autosso
```

### 12.3 验证 ECS RAM 角色环境

在 ECS 上执行以下命令，确认实例能否通过元数据获取角色凭证：

```bash
curl http://100.100.100.200/latest/meta-data/ram/security-credentials/
```

如果输出你绑定的角色名称（如 `AutoSSO-Management-Role`），说明环境正常。

### 12.4 增强版 Systemd 配置

使用专用用户运行，分离错误日志，增加资源限制：

```bash
cat > /etc/systemd/system/autosso.service << 'EOF'
[Unit]
Description=AutoSSO Service
Documentation=https://github.com/your-repo/autosso
After=network.target mysqld.service

[Service]
Type=simple
# 使用专用用户运行
User=autosso
Group=autosso
WorkingDirectory=/opt/autosso

# 启动命令
ExecStart=/opt/autosso/autosso -config /opt/autosso/config/config.yaml

# 环境变量（用于覆盖配置文件中的敏感信息）
# Environment="DB_PASSWORD=xxxx"
# Environment="FEISHU_APP_SECRET=xxxx"

# 重启策略: 失败自动重启
Restart=on-failure
RestartSec=10s

# 资源限制
LimitNOFILE=65535

# 日志输出（标准输出和错误输出分离）
StandardOutput=append:/opt/autosso/logs/autosso.log
StandardError=append:/opt/autosso/logs/autosso_error.log

[Install]
WantedBy=multi-user.target
EOF
```

启动服务：

```bash
systemctl daemon-reload
systemctl enable --now autosso
systemctl status autosso
```

### 12.5 配置日志轮转 (Logrotate)

防止日志文件无限增长占满磁盘：

```bash
cat > /etc/logrotate.d/autosso << 'EOF'
/opt/autosso/logs/*.log {
    daily
    rotate 7
    compress
    delaycompress
    missingok
    notifempty
    copytruncate
    create 0640 autosso autosso
}
EOF
```

参数说明：
- `daily`：每天轮转一次
- `rotate 7`：保留最近 7 天的日志
- `compress`：压缩旧日志文件
- `copytruncate`：截断原文件，不影响程序写入
- `create 0640 autosso autosso`：新日志文件的权限和归属

---

## 十三、使用 Pulumi 自动化部署（可选）

项目提供了 Pulumi 脚本，可以一键完成"创建 ECS + 安装 MySQL + 部署应用"的全流程。

### 13.1 安装依赖

```bash
# macOS
brew install pulumi/tap/pulumi

# 或通用安装
curl -fsSL https://get.pulumi.com | sh
```

### 13.2 使用交互式向导

```bash
cd deploy/pulumi
./setup.sh
```

向导会引导你完成：
1. 选择部署模式（新建 ECS / 已有服务器 IP / 已有实例 ID）
2. 配置 SSH 密钥路径
3. 是否自动安装 MySQL
4. 执行部署

### 13.3 三种部署模式

**模式 A：部署到已有服务器（通过 IP）**

```bash
pulumi config set deployHost 你的服务器IP
pulumi config set deployUser root
pulumi config set sshKey ~/.ssh/id_rsa
pulumi up
```

**模式 B：部署到已有服务器（通过实例 ID）**

```bash
pulumi config set alicloud:region cn-hangzhou
pulumi config set deployInstanceId i-xxxxxxxxxxxxx
pulumi config set sshKey ~/.ssh/id_rsa
pulumi up
```

**模式 C：全新创建基础设施**

```bash
pulumi config set alicloud:region cn-hangzhou
pulumi config set zoneId cn-hangzhou-i
pulumi config set instanceType ecs.c7.large
pulumi config set sshPublicKey ~/.ssh/id_rsa.pub
pulumi config set sshKey ~/.ssh/id_rsa
pulumi config set --secret dbPassword "你的数据库密码"
pulumi up
```

Pulumi 会自动创建 VPC、VSwitch、安全组、ECS 实例，安装 MySQL，执行建表脚本，上传并启动应用。

---

## 十四、常见问题排查

### 14.1 程序启动失败

**检查配置文件**：
```bash
cat /root/autosso/config.yaml
```
确认 YAML 格式正确，所有必填字段已填写。

**检查数据库连接**：
```bash
mysql -u root -p -h 127.0.0.1 -e "SELECT 1"
```

**检查环境变量**（如果配置中使用了 `${VAR}` 形式）：
```bash
env | grep -E "(FEISHU|DB|ALIBABA)"
```

### 14.2 阿里云 Cloud SSO 未激活

如果日志中出现 `Cloud SSO is not activated`：

```json
{"level":"error","message":"[Alibaba] Failed to get user ID",
 "error":"InvalidServiceStatus: Cloud SSO is not activated."}
```

**解决办法**：
1. 登录阿里云控制台 → 云 SSO → 开通服务
2. 确认 `config.yaml` 中的 `directory_id` 和 `endpoint` 正确
3. 确认 ECS 实例绑定的 RAM 角色有 CloudSSO 相关权限

### 14.3 权限配置未找到

如果日志中出现 `access configuration not found: ViewOnly-Role`：

**解决办法**：
1. 登录阿里云 CloudSSO 控制台，确认存在名为 `ViewOnly-Role` 的访问配置
2. 检查 `config.yaml` 中的 `permission_mappings` 映射是否正确
3. 确保飞书表单中的选项名称与映射配置一致

### 14.4 阿里云认证失败

| 错误信息 | 可能原因 | 解决方案 |
|---------|---------|---------|
| `Failed to create Alibaba client` | ECS 未绑定 RAM 角色 | 检查 ECS 实例是否绑定了正确的 RAM 角色 |
| `InvalidAccessKeyId` | AK/SK 错误 | 检查环境变量 `ALIBABA_ACCESS_KEY_ID` |
| `no cloud providers configured` | 所有云平台账号列表为空 | 至少在一个 provider 下配置有效的 `accounts` |

### 14.5 飞书 API 报错

| 错误信息 | 可能原因 | 解决方案 |
|---------|---------|---------|
| `invalid app_id or app_secret` | 飞书凭证错误 | 检查 `FEISHU_APP_ID` 和 `FEISHU_APP_SECRET` |
| 连接超时 | ECS 无法访问外网 | 检查 DNS 解析、NAT 网关配置 |
| 权限不足 | API 权限未开通 | 参考§三.4 开通所有必要的 API 权限 |

### 14.6 上传脚本报错

**"Error: key not found"**：
```bash
# 确认密钥文件存在
ls -la ssh/key.pem
# 或指定自定义路径
KEY_PATH=/path/to/your/key.pem ./ssh/upload.sh 服务器IP
```

**"Error: missing \<ip\>"**：
```bash
# 需要提供服务器 IP
./ssh/upload.sh 47.93.xxx.xxx
```

### 14.7 查看运行日志

```bash
# 查看最新日志
tail -100 /root/autosso/autosso.log

# 实时跟踪日志
tail -f /root/autosso/autosso.log

# 只看错误
grep '"level":"error"' /root/autosso/autosso.log

# 如果使用了安全加固方案，日志在 /opt/autosso/logs/
tail -f /opt/autosso/logs/autosso.log
tail -f /opt/autosso/logs/autosso_error.log
```

### 14.8 优雅停止程序

```bash
# 方法 1：Systemd（推荐）
systemctl stop autosso

# 方法 2：发送 SIGTERM
kill -TERM $(cat /root/autosso/autosso.pid)

# 方法 3：前台运行时按 Ctrl+C
```

程序收到信号后会等待所有正在进行的操作完成（最长 30 秒），然后关闭数据库连接和 HTTP 客户端。

---

## 附录：配置文件详解

### 运行时参数

```yaml
runtime:
  poll_interval: "10s"           # 轮询飞书审批的间隔（建议 5-30s）
  expire_check_interval: "1h"    # 检查权限过期的间隔
  grace_period_days: 3           # 权限过期后，自动回收前的宽限天数
  http_timeout: "10s"            # HTTP 请求超时
  max_retries: 3                 # API 调用失败重试次数
  log_level: "info"              # 日志级别：debug / info / warn / error
  skip_historical_approvals: true # true=只处理启动后新审批，false=处理近24h审批
  whitelist_emails:              # 豁免自动回收的邮箱（如安全团队）
    - "admin@example.com"
```

### 认证方式说明

| auth_type | 适用场景 | 说明 |
|-----------|---------|------|
| `ecs_ram_role` | 部署在阿里云 ECS 上 | **推荐**。无需管理 AK，通过实例 RAM 角色获取临时凭证 |
| `ram_role_arn` | 跨账号访问 | 适用于 ECS 在管理账号、CloudSSO 在生产账号的场景。需配置 `role_arn` 和 `role_name` |
| `oidc_role_arn` | 部署在 Kubernetes 上 | 使用 K8s ServiceAccount Token 换取阿里云凭证。需配置 `role_arn`、`oidc_provider_arn`、`oidc_token_file` |
| `access_key` | 部署在非阿里云环境 | 需配置 `access_key_id` 和 `access_key_secret`。**仅限测试**，不推荐用于生产 |

#### OIDC 认证配置示例（K8s 部署场景）

```yaml
providers:
  alibaba:
    auth_type: "oidc_role_arn"
    role_arn: "acs:ram::xxx:role/AutoSSORole"
    oidc_provider_arn: "acs:ram::xxx:oidc-provider/k8s"
    oidc_token_file: "/var/run/secrets/tokens/alibaba-token"
    directory_id: "d-xxxxx"
    endpoint: "cloudsso.cn-shanghai.aliyuncs.com"
    accounts:
      - id: "目标账号ID"
        name: "prod"
        type: "prod"
```

### 环境变量覆盖

以下配置项支持通过环境变量覆盖，适合在 Systemd、Docker 或 CI/CD 中使用：

| 配置项 | 环境变量 | 说明 |
|-------|---------|------|
| `feishu.app_id` | `FEISHU_APP_ID` | 飞书 App ID |
| `feishu.app_secret` | `FEISHU_APP_SECRET` | 飞书 App Secret |
| `database.password` | `DB_PASSWORD` | 数据库密码 |

在 `config.yaml` 中使用 `${VAR}` 语法引用环境变量：

```yaml
feishu:
  app_id: "${FEISHU_APP_ID}"
  app_secret: "${FEISHU_APP_SECRET}"
database:
  password: "${DB_PASSWORD}"
```

### 数据库连接池参数

```yaml
database:
  max_open_conns: 10    # 最大打开连接数（一般 10 足够）
  max_idle_conns: 5     # 最大空闲连接数
  max_lifetime: "1h"    # 连接最大存活时间（防止连接泄漏）
```

### 完整配置参考

```yaml
# ========================
# 1. 飞书 (Feishu/Lark) 配置
# ========================
feishu:
  app_id: "${FEISHU_APP_ID}"
  app_secret: "${FEISHU_APP_SECRET}"
  approval_code: "202402010001"
  chat_id: "oc_1234567890abcdef"
  base_url: "https://open.feishu.cn"

# ========================
# 2. 数据库 (Database) 配置
# ========================
database:
  host: "127.0.0.1"
  port: 3306
  user: "root"
  password: "${DB_PASSWORD}"
  database: "security"
  max_open_conns: 10
  max_idle_conns: 5
  max_lifetime: "1h"

# ========================
# 3. 云平台 Provider 配置
# ========================
providers:
  alibaba:
    auth_type: "ram_role_arn"
    role_name: "AutoSSO-Management-Role"
    role_arn: "acs:ram::22222222:role/AutoSSO-Production-Role"
    directory_id: "d-00j28xxxx"
    endpoint: "cloudsso.cn-shanghai.aliyuncs.com"
    accounts:
      - id: "22222222"
        name: "Production-DB"
        type: "prod"

# ========================
# 4. 运行时 (Runtime) 配置
# ========================
runtime:
  poll_interval: "10s"
  expire_check_interval: "5m"
  grace_period_days: 0
  http_timeout: "30s"
  log_level: "debug"
  whitelist_emails:
    - "admin@example.com"
    - "sre@example.com"

# ========================
# 5. 权限映射 (Permission Mappings)
# ========================
permission_mappings:
  - feishu_name: "阿里云全量管理 (FullAccess)"
    real_name: "AliyunCloudSSOFullAccess"
  - feishu_name: "DBA 运维权限 (Custom)"
    real_name: "DBA-Custom-Policy"
```

---

> **提示**：首次部署建议先用 `log_level: "debug"` 运行，确认一切正常后再改为 `"info"`。
