# OPS 统一管理方案 - Headscale 组网实施方案 > **任务编号**: 4448 > **版本**: v2.0 > **最后更新**: 2025-12-18 > **文档状态**: 详细设计 --- ## 目录 1. [项目背景与目标](#1-项目背景与目标) 2. [技术方案概述](#2-技术方案概述) 3. [网络架构设计](#3-网络架构设计) 4. [基础设施规划](#4-基础设施规划) 5. [Headscale 服务端部署](#5-headscale-服务端部署) 6. [客户端接入方案](#6-客户端接入方案) 7. [访问控制与安全策略](#7-访问控制与安全策略) 8. [DNS 与服务发现](#8-dns-与服务发现) 9. [监控与告警](#9-监控与告警) 10. [运维管理规范](#10-运维管理规范) 11. [故障恢复与灾备](#11-故障恢复与灾备) 12. [实施计划与里程碑](#12-实施计划与里程碑) 13. [风险评估与应对](#13-风险评估与应对) 14. [附录](#14-附录) --- ## 1. 项目背景与目标 ### 1.1 项目背景 随着业务发展,运维团队面临以下挑战: - **多云多地域分布**: 服务器分布在阿里云、腾讯云、AWS 等多个云平台,以及多个物理机房 - **网络隔离复杂**: 不同环境(生产、测试、开发)之间网络隔离管理复杂 - **VPN 管理困难**: 传统 VPN 方案(OpenVPN、IPSec)配置复杂、维护成本高 - **安全访问需求**: 需要安全、便捷地访问内部服务,同时满足合规要求 - **运维效率低下**: 跨网络运维操作繁琐,无统一入口 ### 1.2 项目目标 | 目标维度 | 具体目标 | 验收标准 | |---------|---------|---------| | 网络互通 | 实现所有节点 P2P 直连 | 任意两节点延迟 < 50ms(同区域)| | 安全性 | 零信任网络架构 | 所有通信加密,基于身份认证 | | 易用性 | 一键接入内网 | 客户端安装配置 < 5分钟 | | 可扩展 | 支持快速扩容 | 新节点接入 < 10分钟 | | 高可用 | 控制平面高可用 | SLA 99.9% | ### 1.3 适用范围 - 生产环境所有服务器 - 测试/预发布环境服务器 - 运维/开发人员工作设备 - CI/CD 构建节点 - 数据库、缓存等基础设施 --- ## 2. 技术方案概述 ### 2.1 为什么选择 Headscale | 方案 | 优点 | 缺点 | 适用场景 | |------|-----|------|---------| | **Headscale** | 开源自托管、WireGuard 内核、P2P 直连、轻量级 | 生态相对较新 | 自主可控要求高 | | Tailscale | 完善的商业支持 | 数据过境国外、成本高 | 小团队快速起步 | | OpenVPN | 成熟稳定 | 配置复杂、性能较差 | 传统企业 | | ZeroTier | 易于使用 | 免费版限制多 | 小规模使用 | **选择 Headscale 的核心理由**: 1. **数据主权**: 所有协调数据存储在自己的服务器上 2. **成本可控**: 完全开源,无订阅费用 3. **WireGuard 优势**: 现代密码学、低延迟、高性能 4. **Mesh 网络**: 节点间直接通信,无需中心转发 5. **兼容 Tailscale 客户端**: 可使用成熟的 Tailscale 客户端 ### 2.2 技术架构图 ``` ┌─────────────────────────────────────────────────────────┐ │ Internet │ └──────────────────────────┬──────────────────────────────┘ │ ┌──────────────────────────┴──────────────────────────────┐ │ │ ┌─────────▼─────────┐ ┌────────────────▼────────────────┐ │ Headscale HA │ │ DERP Relay Servers │ │ Control Plane │ │ (Beijing/Shanghai/HK) │ │ │ │ │ │ ┌───────────────┐ │ │ ┌─────────┐ ┌─────────┐ │ │ │ Headscale │ │ │ │ DERP-BJ │ │ DERP-SH │ │ │ │ Primary │ │ │ └─────────┘ └─────────┘ │ │ └───────────────┘ │ │ ┌─────────┐ │ │ ┌───────────────┐ │ │ │ DERP-HK │ │ │ │ PostgreSQL │ │ │ └─────────┘ │ │ │ (HA) │ │ └─────────────────────────────────┘ │ └───────────────┘ │ └─────────┬─────────┘ │ Coordination │ ┌─────────────────────┼─────────────────────┬─────────────────────┐ │ │ │ │ ▼ ▼ ▼ ▼ ┌───────────────┐ ┌───────────────┐ ┌───────────────┐ ┌───────────────┐ │ Production │ │ Staging │ │ Development │ │ Operator │ │ Servers │ │ Servers │ │ Servers │ │ Devices │ │ │ │ │ │ │ │ │ │ ┌───────────┐ │ │ ┌───────────┐ │ │ ┌───────────┐ │ │ ┌───────────┐ │ │ │ Tailscale │ │◄───►│ │ Tailscale │ │◄───►│ │ Tailscale │ │◄───►│ │ Tailscale │ │ │ │ Agent │ │ P2P │ │ Agent │ │ P2P │ │ Agent │ │ P2P │ │ Client │ │ │ └───────────┘ │ │ └───────────┘ │ │ └───────────┘ │ │ └───────────┘ │ └───────────────┘ └───────────────┘ └───────────────┘ └───────────────┘ 100.64.1.x 100.64.2.x 100.64.3.x 100.64.10.x ``` ### 2.3 核心组件说明 | 组件 | 功能 | 部署位置 | 高可用策略 | |------|-----|---------|-----------| | Headscale Server | 协调服务、密钥分发、ACL 管理 | 云主机 | 主备 + PostgreSQL HA | | DERP Relay | NAT 穿透失败时的中继服务 | 多地域部署 | 多节点冗余 | | Tailscale Client | 客户端 Agent | 所有节点 | 开机自启 | | Admin UI | Web 管理界面 | 与 Headscale 同机 | - | --- ## 3. 网络架构设计 ### 3.1 IP 地址规划 采用 CGNAT 地址段 `100.64.0.0/10`,按环境和用途划分: ``` 100.64.0.0/10 (总地址空间: 4,194,304 个地址) │ ├── 100.64.0.0/16 - 保留地址段 (管理用途) │ ├── 100.64.0.0/24 - Headscale 控制平面 │ ├── 100.64.1.0/24 - DERP 中继服务器 │ └── 100.64.2.0/24 - 监控基础设施 │ ├── 100.65.0.0/16 - 生产环境 (Production) │ ├── 100.65.1.0/24 - Web 服务器组 │ ├── 100.65.2.0/24 - API 服务器组 │ ├── 100.65.3.0/24 - 数据库服务器组 │ ├── 100.65.4.0/24 - 缓存服务器组 │ ├── 100.65.5.0/24 - 消息队列服务器组 │ ├── 100.65.10.0/24 - Kubernetes Master │ ├── 100.65.11.0/23 - Kubernetes Worker │ └── 100.65.100.0/24 - 生产环境堡垒机 │ ├── 100.66.0.0/16 - 预发布环境 (Staging) │ ├── 100.66.1.0/24 - 应用服务器 │ ├── 100.66.2.0/24 - 数据库服务器 │ └── 100.66.10.0/24 - Kubernetes 集群 │ ├── 100.67.0.0/16 - 测试环境 (Testing) │ ├── 100.67.1.0/24 - 应用服务器 │ ├── 100.67.2.0/24 - 数据库服务器 │ └── 100.67.100.0/24 - CI/CD 构建节点 │ ├── 100.68.0.0/16 - 开发环境 (Development) │ ├── 100.68.1.0/24 - 开发服务器 │ └── 100.68.2.0/24 - 开发数据库 │ ├── 100.70.0.0/16 - 运维人员设备 (Operators) │ ├── 100.70.1.0/24 - 高级运维 │ ├── 100.70.2.0/24 - 普通运维 │ └── 100.70.10.0/24 - 值班人员 │ ├── 100.71.0.0/16 - 开发人员设备 (Developers) │ ├── 100.71.1.0/24 - 后端开发 │ ├── 100.71.2.0/24 - 前端开发 │ └── 100.71.3.0/24 - 移动开发 │ └── 100.80.0.0/16 - 外部合作伙伴 (Partners) └── 100.80.1.0/24 - 第三方供应商 ``` ### 3.2 命名空间设计 Headscale 使用 User (原 Namespace) 进行逻辑隔离: | User 名称 | 用途 | IP 段 | 管理员 | |-----------|-----|-------|--------| | `infra` | 基础设施服务 | 100.64.0.0/16 | ops-admin | | `prod` | 生产环境服务器 | 100.65.0.0/16 | ops-admin | | `staging` | 预发布环境 | 100.66.0.0/16 | ops-admin | | `testing` | 测试环境 | 100.67.0.0/16 | qa-admin | | `dev` | 开发环境 | 100.68.0.0/16 | dev-admin | | `ops-team` | 运维人员设备 | 100.70.0.0/16 | ops-admin | | `dev-team` | 开发人员设备 | 100.71.0.0/16 | dev-admin | | `partners` | 外部合作伙伴 | 100.80.0.0/16 | ops-admin | ### 3.3 节点命名规范 ``` <环境>-<角色>-<区域>-<序号> 示例: - prod-web-bj-001 生产环境北京Web服务器#1 - prod-db-sh-001 生产环境上海数据库#1 - staging-api-bj-001 预发布环境北京API服务器#1 - ops-laptop-zhangsan 运维人员张三的笔记本 ``` ### 3.4 DERP 中继网络 部署自建 DERP 服务器以确保 NAT 穿透失败时的可靠中继: | 节点 | 区域 | 公网 IP | 端口 | 备注 | |------|-----|---------|-----|------| | derp-bj-01 | 北京 | x.x.x.x | 443/3478 | 阿里云主节点 | | derp-sh-01 | 上海 | x.x.x.x | 443/3478 | 腾讯云备节点 | | derp-hk-01 | 香港 | x.x.x.x | 443/3478 | AWS 海外节点 | | derp-sg-01 | 新加坡 | x.x.x.x | 443/3478 | 东南亚节点 | --- ## 4. 基础设施规划 ### 4.1 服务器资源规划 #### 4.1.1 Headscale 控制平面 | 组件 | 配置 | 数量 | 说明 | |------|-----|------|-----| | Headscale Primary | 4C8G 100GB SSD | 1 | 主控制节点 | | Headscale Standby | 4C8G 100GB SSD | 1 | 热备节点 | | PostgreSQL Primary | 4C16G 500GB SSD | 1 | 数据库主节点 | | PostgreSQL Replica | 4C16G 500GB SSD | 1 | 数据库从节点 | | Admin UI | 2C4G 50GB SSD | 1 | 管理界面 | #### 4.1.2 DERP 中继服务器 | 区域 | 配置 | 带宽 | 数量 | |------|-----|------|------| | 北京 | 2C4G 50GB | 100Mbps | 1 | | 上海 | 2C4G 50GB | 100Mbps | 1 | | 香港 | 2C4G 50GB | 100Mbps | 1 | | 新加坡 | 2C4G 50GB | 100Mbps | 1 | ### 4.2 网络要求 #### 4.2.1 Headscale 服务器端口 | 端口 | 协议 | 用途 | 来源 | |-----|------|-----|------| | 443 | TCP | HTTPS API & gRPC | 所有客户端 | | 80 | TCP | HTTP 重定向 | 所有客户端 | | 50443 | TCP | 管理 API (可选) | 管理网络 | #### 4.2.2 DERP 服务器端口 | 端口 | 协议 | 用途 | 来源 | |-----|------|-----|------| | 443 | TCP | HTTPS DERP | 所有客户端 | | 3478 | UDP | STUN | 所有客户端 | | 80 | TCP | HTTP 重定向 | 所有客户端 | #### 4.2.3 Tailscale 客户端端口 | 端口 | 协议 | 用途 | 方向 | |-----|------|-----|------| | 41641 | UDP | WireGuard 直连 | 入站/出站 | | 443 | TCP | DERP 中继 | 出站 | | 3478 | UDP | STUN | 出站 | ### 4.3 域名与证书规划 | 域名 | 用途 | 证书类型 | |------|-----|---------| | hs.ops.company.com | Headscale API | Let's Encrypt 通配符 | | admin.hs.ops.company.com | 管理界面 | Let's Encrypt | | derp-bj.ops.company.com | 北京 DERP | Let's Encrypt | | derp-sh.ops.company.com | 上海 DERP | Let's Encrypt | | derp-hk.ops.company.com | 香港 DERP | Let's Encrypt | --- ## 5. Headscale 服务端部署 ### 5.1 系统环境准备 ```bash # 操作系统: Ubuntu 22.04 LTS / Rocky Linux 9 # 时区设置 timedatectl set-timezone Asia/Shanghai # 更新系统 apt update && apt upgrade -y # 安装必要工具 apt install -y curl wget vim htop net-tools jq unzip # 关闭 swap (容器化部署时) swapoff -a sed -i '/swap/d' /etc/fstab # 设置内核参数 cat >> /etc/sysctl.conf << EOF net.ipv4.ip_forward = 1 net.ipv6.conf.all.forwarding = 1 net.core.rmem_max = 2500000 net.core.wmem_max = 2500000 EOF sysctl -p # 设置文件描述符限制 cat >> /etc/security/limits.conf << EOF * soft nofile 65535 * hard nofile 65535 root soft nofile 65535 root hard nofile 65535 EOF ``` ### 5.2 PostgreSQL 高可用部署 #### 5.2.1 PostgreSQL 主节点安装 ```bash # 安装 PostgreSQL 15 apt install -y postgresql-15 postgresql-contrib-15 # 配置 PostgreSQL cat > /etc/postgresql/15/main/postgresql.conf << 'EOF' listen_addresses = '*' port = 5432 max_connections = 200 shared_buffers = 4GB effective_cache_size = 12GB maintenance_work_mem = 1GB checkpoint_completion_target = 0.9 wal_buffers = 16MB default_statistics_target = 100 random_page_cost = 1.1 effective_io_concurrency = 200 work_mem = 10MB min_wal_size = 1GB max_wal_size = 4GB max_worker_processes = 4 max_parallel_workers_per_gather = 2 max_parallel_workers = 4 max_parallel_maintenance_workers = 2 # 复制配置 wal_level = replica max_wal_senders = 5 wal_keep_size = 1GB hot_standby = on EOF # 配置访问控制 cat > /etc/postgresql/15/main/pg_hba.conf << 'EOF' local all postgres peer local all all peer host all all 127.0.0.1/32 scram-sha-256 host all all ::1/128 scram-sha-256 host replication replicator /32 scram-sha-256 host headscale headscale /32 scram-sha-256 host headscale headscale /32 scram-sha-256 EOF # 创建数据库和用户 sudo -u postgres psql << 'EOF' CREATE USER headscale WITH PASSWORD 'your_secure_password_here'; CREATE DATABASE headscale OWNER headscale; GRANT ALL PRIVILEGES ON DATABASE headscale TO headscale; CREATE USER replicator WITH REPLICATION PASSWORD 'replicator_password'; EOF systemctl restart postgresql systemctl enable postgresql ``` #### 5.2.2 PostgreSQL 从节点配置 ```bash # 停止 PostgreSQL systemctl stop postgresql # 清空数据目录 rm -rf /var/lib/postgresql/15/main/* # 从主节点复制数据 sudo -u postgres pg_basebackup -h -U replicator -p 5432 \ -D /var/lib/postgresql/15/main -Fp -Xs -P -R # 启动从节点 systemctl start postgresql ``` ### 5.3 Headscale 安装与配置 #### 5.3.1 二进制安装 ```bash # 下载最新版本 (以 0.23.0 为例) HEADSCALE_VERSION="0.23.0" wget -O /tmp/headscale.deb \ "https://github.com/juanfont/headscale/releases/download/v${HEADSCALE_VERSION}/headscale_${HEADSCALE_VERSION}_linux_amd64.deb" # 安装 dpkg -i /tmp/headscale.deb # 或使用 Docker docker pull headscale/headscale:0.23.0 ``` #### 5.3.2 Headscale 配置文件 ```yaml # /etc/headscale/config.yaml --- server_url: https://hs.ops.company.com:443 listen_addr: 0.0.0.0:443 metrics_listen_addr: 127.0.0.1:9090 grpc_listen_addr: 0.0.0.0:50443 grpc_allow_insecure: false # 私有密钥路径 private_key_path: /var/lib/headscale/private.key noise: private_key_path: /var/lib/headscale/noise_private.key # IP 地址前缀 prefixes: v4: 100.64.0.0/10 v6: fd7a:115c:a1e0::/48 allocation: sequential # 数据库配置 (PostgreSQL) database: type: postgres postgres: host: port: 5432 name: headscale user: headscale pass: your_secure_password_here max_open_conns: 100 max_idle_conns: 10 conn_max_idle_time_secs: 3600 ssl: disable # 生产环境建议启用 require # DERP 配置 derp: server: enabled: false # 使用独立 DERP 服务器 region_id: 999 region_code: "headscale" region_name: "Headscale Embedded DERP" stun_listen_addr: "0.0.0.0:3478" urls: - https://hs.ops.company.com/derp.json paths: [] auto_update_enabled: true update_frequency: 24h # 禁用默认 Tailscale DERP disable_check_updates: true ephemeral_node_inactivity_timeout: 30m # 节点更新检查 node_update_check_interval: 10s # DNS 配置 dns: magic_dns: true base_domain: ts.company.local nameservers: global: - 10.0.0.1 # 内部 DNS - 223.5.5.5 # 阿里 DNS (备用) search_domains: - company.local extra_records: - name: "grafana.ts.company.local" type: "A" value: "100.64.0.10" - name: "prometheus.ts.company.local" type: "A" value: "100.64.0.11" # Unix socket 配置 unix_socket: /var/run/headscale/headscale.sock unix_socket_permission: "0770" # TLS 配置 (使用反向代理时可设为空) tls_cert_path: "" tls_key_path: "" # 日志配置 log: format: json level: info # ACL 策略 policy: mode: file path: /etc/headscale/acl.json # OIDC 配置 (可选) oidc: only_start_if_oidc_is_available: true issuer: "https://sso.company.com/realms/ops" client_id: "headscale" client_secret: "your_oidc_client_secret" scope: ["openid", "profile", "email"] extra_params: domain_hint: company.com strip_email_domain: true allowed_users: [] allowed_groups: - "/ops-team" - "/dev-team" ``` #### 5.3.3 创建 systemd 服务 ```ini # /etc/systemd/system/headscale.service [Unit] Description=headscale coordination server Documentation=https://github.com/juanfont/headscale After=network-online.target postgresql.service Wants=network-online.target Requires=postgresql.service [Service] User=headscale Group=headscale Type=simple Restart=always RestartSec=5 ExecStart=/usr/bin/headscale serve Environment="GIN_MODE=release" # 资源限制 LimitNOFILE=65535 LimitNPROC=65535 # 安全加固 NoNewPrivileges=true PrivateTmp=true ProtectSystem=strict ProtectHome=true ReadWritePaths=/var/lib/headscale /var/run/headscale [Install] WantedBy=multi-user.target ``` #### 5.3.4 启动服务 ```bash # 创建用户和目录 useradd -r -s /bin/false headscale mkdir -p /var/lib/headscale /var/run/headscale /etc/headscale chown -R headscale:headscale /var/lib/headscale /var/run/headscale # 启动服务 systemctl daemon-reload systemctl enable headscale systemctl start headscale # 验证服务状态 systemctl status headscale headscale version ``` ### 5.4 DERP 中继服务器部署 #### 5.4.1 DERP 服务器配置 ```bash # 安装 Go (如果需要编译) wget https://go.dev/dl/go1.21.5.linux-amd64.tar.gz tar -C /usr/local -xzf go1.21.5.linux-amd64.tar.gz export PATH=$PATH:/usr/local/go/bin # 安装 derper go install tailscale.com/cmd/derper@latest # 或使用 Docker docker pull ghcr.io/tailscale/derper:latest ``` #### 5.4.2 DERP Docker Compose 部署 ```yaml # /opt/derper/docker-compose.yml version: '3.8' services: derper: image: ghcr.io/tailscale/derper:latest container_name: derper restart: always ports: - "443:443" - "80:80" - "3478:3478/udp" volumes: - ./certs:/etc/derper/certs:ro - ./config:/etc/derper/config:ro command: - --hostname=derp-bj.ops.company.com - --certmode=manual - --certdir=/etc/derper/certs - --stun - --stun-port=3478 - --verify-clients=true - --verify-client-url=https://hs.ops.company.com/verify environment: - DERP_VERIFY_CLIENTS=true logging: driver: "json-file" options: max-size: "100m" max-file: "3" ``` #### 5.4.3 DERP Map 配置 在 Headscale 服务器上配置 DERP Map: ```json // /etc/headscale/derp.json { "Regions": { "900": { "RegionID": 900, "RegionCode": "bj", "RegionName": "Beijing", "Avoid": false, "Nodes": [ { "Name": "bj1", "RegionID": 900, "HostName": "derp-bj.ops.company.com", "DERPPort": 443, "STUNPort": 3478, "InsecureForTests": false } ] }, "901": { "RegionID": 901, "RegionCode": "sh", "RegionName": "Shanghai", "Avoid": false, "Nodes": [ { "Name": "sh1", "RegionID": 901, "HostName": "derp-sh.ops.company.com", "DERPPort": 443, "STUNPort": 3478, "InsecureForTests": false } ] }, "902": { "RegionID": 902, "RegionCode": "hk", "RegionName": "Hong Kong", "Avoid": false, "Nodes": [ { "Name": "hk1", "RegionID": 902, "HostName": "derp-hk.ops.company.com", "DERPPort": 443, "STUNPort": 3478, "InsecureForTests": false } ] } } } ``` ### 5.5 Nginx 反向代理配置 ```nginx # /etc/nginx/sites-available/headscale upstream headscale { server 127.0.0.1:8080; keepalive 32; } server { listen 80; server_name hs.ops.company.com; return 301 https://$server_name$request_uri; } server { listen 443 ssl http2; server_name hs.ops.company.com; # SSL 配置 ssl_certificate /etc/letsencrypt/live/hs.ops.company.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/hs.ops.company.com/privkey.pem; ssl_protocols TLSv1.2 TLSv1.3; ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384; ssl_prefer_server_ciphers off; ssl_session_cache shared:SSL:10m; ssl_session_timeout 1d; ssl_session_tickets off; ssl_stapling on; ssl_stapling_verify on; # 安全头 add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always; add_header X-Frame-Options DENY always; add_header X-Content-Type-Options nosniff always; location / { proxy_pass http://headscale; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_buffering off; proxy_read_timeout 86400s; proxy_send_timeout 86400s; } # gRPC 支持 location /headscale.v1.HeadscaleService/ { grpc_pass grpc://127.0.0.1:50443; grpc_set_header Host $host; grpc_set_header X-Real-IP $remote_addr; } # 健康检查 location /health { proxy_pass http://headscale/health; access_log off; } # Metrics (仅内网访问) location /metrics { allow 10.0.0.0/8; allow 172.16.0.0/12; allow 192.168.0.0/16; allow 100.64.0.0/10; deny all; proxy_pass http://127.0.0.1:9090/metrics; } } ``` ### 5.6 管理界面部署 (Headscale-UI) ```yaml # /opt/headscale-ui/docker-compose.yml version: '3.8' services: headscale-ui: image: ghcr.io/gurucomputing/headscale-ui:latest container_name: headscale-ui restart: always ports: - "127.0.0.1:8081:80" environment: - HS_SERVER=https://hs.ops.company.com ``` --- ## 6. 客户端接入方案 ### 6.1 Linux 服务器接入 #### 6.1.1 安装 Tailscale 客户端 ```bash # Ubuntu/Debian curl -fsSL https://tailscale.com/install.sh | sh # RHEL/CentOS curl -fsSL https://tailscale.com/install.sh | sh # 或手动安装 # Ubuntu/Debian curl -fsSL https://pkgs.tailscale.com/stable/ubuntu/jammy.noarmor.gpg | sudo tee /usr/share/keyrings/tailscale-archive-keyring.gpg >/dev/null curl -fsSL https://pkgs.tailscale.com/stable/ubuntu/jammy.tailscale-keyring.list | sudo tee /etc/apt/sources.list.d/tailscale.list apt update && apt install -y tailscale ``` #### 6.1.2 连接到 Headscale ```bash # 使用预认证密钥 (推荐用于服务器) tailscale up \ --login-server https://hs.ops.company.com \ --authkey tskey-preauth-xxxxxxxxxxxxx \ --hostname prod-web-bj-001 \ --advertise-tags tag:prod,tag:web \ --accept-routes \ --accept-dns # 交互式登录 (用于开发机器) tailscale up \ --login-server https://hs.ops.company.com \ --hostname ops-laptop-zhangsan # 验证连接 tailscale status tailscale ip ``` #### 6.1.3 自动化安装脚本 ```bash #!/bin/bash # /opt/scripts/setup-tailscale.sh set -euo pipefail # 配置变量 HEADSCALE_URL="${HEADSCALE_URL:-https://hs.ops.company.com}" AUTH_KEY="${AUTH_KEY:-}" HOSTNAME="${HOSTNAME:-$(hostname -s)}" TAGS="${TAGS:-}" ACCEPT_ROUTES="${ACCEPT_ROUTES:-true}" # 日志函数 log() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" } # 检查是否已安装 if command -v tailscale &> /dev/null; then log "Tailscale 已安装,版本: $(tailscale version)" else log "正在安装 Tailscale..." curl -fsSL https://tailscale.com/install.sh | sh fi # 构建 tailscale up 命令 UP_CMD="tailscale up --login-server ${HEADSCALE_URL}" if [ -n "$AUTH_KEY" ]; then UP_CMD="$UP_CMD --authkey $AUTH_KEY" fi if [ -n "$HOSTNAME" ]; then UP_CMD="$UP_CMD --hostname $HOSTNAME" fi if [ -n "$TAGS" ]; then UP_CMD="$UP_CMD --advertise-tags $TAGS" fi if [ "$ACCEPT_ROUTES" = "true" ]; then UP_CMD="$UP_CMD --accept-routes --accept-dns" fi # 执行连接 log "正在连接到 Headscale..." eval $UP_CMD # 验证连接 sleep 5 if tailscale status | grep -q "100."; then log "连接成功! IP: $(tailscale ip -4)" else log "连接失败,请检查配置" exit 1 fi ``` ### 6.2 macOS/Windows 客户端接入 #### 6.2.1 macOS ```bash # 使用 Homebrew 安装 brew install tailscale # 启动并连接 sudo tailscaled & tailscale up --login-server https://hs.ops.company.com # 或使用官方客户端 # 下载: https://tailscale.com/download/mac # 安装后在设置中修改 Login Server ``` #### 6.2.2 Windows ```powershell # 使用 Winget 安装 winget install tailscale.tailscale # 使用 Chocolatey 安装 choco install tailscale # 连接命令 (PowerShell 管理员) tailscale up --login-server https://hs.ops.company.com ``` ### 6.3 移动设备接入 1. 从 App Store / Google Play 下载 Tailscale 官方客户端 2. 打开 App,点击设置图标 3. 选择 "Custom coordination server" 4. 输入: `https://hs.ops.company.com` 5. 点击 "Log in" 完成认证 ### 6.4 预认证密钥管理 ```bash # 创建可重用的预认证密钥 (用于自动化部署) headscale preauthkeys create \ --user prod \ --reusable \ --expiration 720h \ --tags tag:prod,tag:automated # 创建一次性预认证密钥 headscale preauthkeys create \ --user ops-team \ --expiration 24h # 查看所有预认证密钥 headscale preauthkeys list --user prod # 使密钥失效 headscale preauthkeys expire --user prod ``` ### 6.5 Ansible 自动化部署 ```yaml # roles/tailscale/tasks/main.yml --- - name: Install Tailscale shell: curl -fsSL https://tailscale.com/install.sh | sh args: creates: /usr/bin/tailscale - name: Start tailscaled service systemd: name: tailscaled state: started enabled: yes - name: Check if already connected command: tailscale status register: ts_status ignore_errors: yes changed_when: false - name: Connect to Headscale command: > tailscale up --login-server {{ headscale_url }} --authkey {{ headscale_authkey }} --hostname {{ inventory_hostname }} --advertise-tags {{ tailscale_tags | join(',') }} --accept-routes --accept-dns when: ts_status.rc != 0 - name: Verify connection command: tailscale ip -4 register: ts_ip changed_when: false - name: Display Tailscale IP debug: msg: "Tailscale IP: {{ ts_ip.stdout }}" ``` --- ## 7. 访问控制与安全策略 ### 7.1 ACL 策略设计原则 1. **最小权限原则**: 只授予完成工作所需的最小权限 2. **分层隔离**: 生产/测试/开发环境严格隔离 3. **基于角色**: 运维/开发不同角色不同权限 4. **审计可追溯**: 所有访问可记录和追溯 ### 7.2 详细 ACL 配置 ```json // /etc/headscale/acl.json { "groups": { "group:ops-admin": ["user:zhangsan", "user:lisi"], "group:ops-member": ["user:wangwu", "user:zhaoliu"], "group:dev-senior": ["user:dev01", "user:dev02"], "group:dev-junior": ["user:dev03", "user:dev04"], "group:qa": ["user:qa01", "user:qa02"], "group:dba": ["user:dba01"] }, "tagOwners": { "tag:prod": ["group:ops-admin"], "tag:staging": ["group:ops-admin", "group:ops-member"], "tag:testing": ["group:ops-admin", "group:qa"], "tag:dev": ["group:ops-admin", "group:dev-senior"], "tag:web": ["group:ops-admin"], "tag:api": ["group:ops-admin"], "tag:db": ["group:ops-admin", "group:dba"], "tag:cache": ["group:ops-admin"], "tag:mq": ["group:ops-admin"], "tag:k8s": ["group:ops-admin"], "tag:monitoring": ["group:ops-admin"], "tag:bastion": ["group:ops-admin"] }, "hosts": { "prod-bastion": "100.65.100.1", "staging-bastion": "100.66.100.1", "monitoring-server": "100.64.0.10", "jenkins-master": "100.67.100.1" }, "acls": [ // ===== 基础设施规则 ===== // 所有节点可以访问 DNS { "action": "accept", "src": ["*"], "dst": ["100.64.0.1:53"] }, // 所有节点可以访问监控系统 { "action": "accept", "src": ["*"], "dst": ["tag:monitoring:9090,9093,3000"] }, // ===== 运维管理员规则 ===== // 运维管理员可以访问所有环境的所有服务 { "action": "accept", "src": ["group:ops-admin"], "dst": ["*:*"] }, // ===== 普通运维规则 ===== // 普通运维可以访问非生产环境 { "action": "accept", "src": ["group:ops-member"], "dst": ["tag:staging:*", "tag:testing:*", "tag:dev:*"] }, // 普通运维只能通过堡垒机访问生产环境 { "action": "accept", "src": ["group:ops-member"], "dst": ["tag:bastion:22"] }, // ===== DBA 规则 ===== // DBA 可以访问所有数据库 { "action": "accept", "src": ["group:dba"], "dst": ["tag:db:3306,5432,6379,27017"] }, // DBA 可以访问堡垒机 { "action": "accept", "src": ["group:dba"], "dst": ["tag:bastion:22"] }, // ===== 高级开发规则 ===== // 高级开发可以访问开发、测试和预发布环境 { "action": "accept", "src": ["group:dev-senior"], "dst": ["tag:staging:*", "tag:testing:*", "tag:dev:*"] }, // ===== 初级开发规则 ===== // 初级开发只能访问开发环境 { "action": "accept", "src": ["group:dev-junior"], "dst": ["tag:dev:*"] }, // ===== QA 规则 ===== // QA 可以访问测试和预发布环境 { "action": "accept", "src": ["group:qa"], "dst": ["tag:testing:*", "tag:staging:80,443,8080"] }, // ===== 服务间通信规则 ===== // 生产环境 Web 服务器可以访问 API 服务器 { "action": "accept", "src": ["tag:web"], "dst": ["tag:api:8080,8443"] }, // API 服务器可以访问数据库和缓存 { "action": "accept", "src": ["tag:api"], "dst": ["tag:db:3306,5432", "tag:cache:6379", "tag:mq:5672,15672"] }, // Kubernetes 集群内部通信 { "action": "accept", "src": ["tag:k8s"], "dst": ["tag:k8s:*"] }, // ===== CI/CD 规则 ===== // Jenkins 可以访问测试环境进行部署 { "action": "accept", "src": ["jenkins-master"], "dst": ["tag:testing:22,80,443,8080"] }, // ===== 默认拒绝规则 (隐含) ===== ], // SSH 规则 (控制 Tailscale SSH) "ssh": [ { "action": "accept", "src": ["group:ops-admin"], "dst": ["*"], "users": ["root", "ubuntu", "centos"] }, { "action": "accept", "src": ["group:ops-member"], "dst": ["tag:staging", "tag:testing", "tag:dev"], "users": ["ubuntu", "centos"] } ], // 测试规则 (用于调试) "tests": [ { "src": "user:zhangsan", "accept": ["tag:prod:22", "tag:db:3306"] }, { "src": "user:dev01", "accept": ["tag:dev:*"], "deny": ["tag:prod:*"] } ] } ``` ### 7.3 标签管理 ```bash # 为节点添加标签 headscale nodes tag -i -t "tag:prod,tag:web" # 查看节点标签 headscale nodes list # 批量更新标签 (通过 API) curl -X POST https://hs.ops.company.com/api/v1/machine//tags \ -H "Authorization: Bearer " \ -H "Content-Type: application/json" \ -d '{"tags": ["tag:prod", "tag:web", "tag:bj"]}' ``` ### 7.4 安全加固措施 #### 7.4.1 Headscale 服务器加固 ```bash # 1. 防火墙配置 ufw default deny incoming ufw default allow outgoing ufw allow from 10.0.0.0/8 to any port 22 # SSH 仅允许内网 ufw allow 80/tcp # HTTP 重定向 ufw allow 443/tcp # HTTPS ufw allow 50443/tcp # gRPC (如需要) ufw enable # 2. fail2ban 配置 apt install -y fail2ban cat > /etc/fail2ban/jail.local << 'EOF' [sshd] enabled = true port = ssh filter = sshd logpath = /var/log/auth.log maxretry = 3 bantime = 3600 findtime = 600 [headscale] enabled = true port = 443 filter = headscale logpath = /var/log/headscale/headscale.log maxretry = 5 bantime = 3600 findtime = 600 EOF # 3. 禁用密码登录 sed -i 's/^#*PasswordAuthentication.*/PasswordAuthentication no/' /etc/ssh/sshd_config systemctl restart sshd # 4. 定期更新 apt update && apt upgrade -y ``` #### 7.4.2 客户端安全配置 ```bash # 限制 Tailscale 网络接口的路由 tailscale up \ --shields-up \ # 默认拒绝入站连接 --accept-routes=false \ # 不接受其他节点的路由广播 --advertise-routes="" \ # 不广播本地路由 --exit-node="" # 不使用出口节点 ``` --- ## 8. DNS 与服务发现 ### 8.1 MagicDNS 配置 Headscale 内置的 MagicDNS 提供自动的服务发现能力: ```yaml # config.yaml DNS 部分 dns: magic_dns: true base_domain: ts.company.local nameservers: global: - 10.0.0.1 # 公司内部 DNS - 223.5.5.5 # 阿里 DNS restricted: internal.company.com: - 10.0.0.1 aws.internal: - 169.254.169.253 search_domains: - ts.company.local - company.local extra_records: - name: "grafana" type: "A" value: "100.64.0.10" - name: "prometheus" type: "A" value: "100.64.0.11" - name: "jenkins" type: "A" value: "100.67.100.1" - name: "gitlab" type: "CNAME" value: "prod-gitlab-bj-001" ``` ### 8.2 DNS 解析规则 启用 MagicDNS 后,域名解析规则如下: | 域名格式 | 解析目标 | 示例 | |---------|---------|------| | `` | 直接解析 | `prod-web-bj-001` → `100.65.1.1` | | `.` | 带命名空间 | `prod-web-bj-001.prod` | | `.` | 完整域名 | `prod-web-bj-001.ts.company.local` | | 自定义记录 | extra_records | `grafana` → `100.64.0.10` | ### 8.3 Split DNS 配置 针对特定域名使用特定 DNS 服务器: ```yaml dns: nameservers: restricted: # AWS 内部域名使用 AWS DNS "compute.internal": - 169.254.169.253 "ec2.internal": - 169.254.169.253 # 阿里云内部域名 "alibaba-inc.com": - 100.100.2.136 - 100.100.2.138 # 公司内部域名 "company.internal": - 10.0.0.1 - 10.0.0.2 ``` ### 8.4 服务发现集成 #### 8.4.1 与 Consul 集成 ```hcl # consul-config.hcl services { id = "web-prod-001" name = "web" tags = ["prod", "tailscale"] port = 80 checks = [ { http = "http://prod-web-bj-001.ts.company.local/health" interval = "10s" timeout = "2s" } ] } ``` #### 8.4.2 与 Kubernetes CoreDNS 集成 ```yaml # coredns-configmap.yaml apiVersion: v1 kind: ConfigMap metadata: name: coredns namespace: kube-system data: Corefile: | .:53 { errors health kubernetes cluster.local in-addr.arpa ip6.arpa { pods insecure fallthrough in-addr.arpa ip6.arpa } # 转发 Tailscale 域名到 MagicDNS forward ts.company.local 100.100.100.100 { policy sequential } forward . /etc/resolv.conf cache 30 loop reload loadbalance } ``` --- ## 9. 监控与告警 ### 9.1 监控架构 ``` ┌─────────────────────────────────────────────────────────────────┐ │ Grafana Dashboard │ │ (hs-monitor.ops.company.com) │ └──────────────────────────────┬──────────────────────────────────┘ │ ┌─────────────┴─────────────┐ │ Prometheus │ │ (100.64.0.11:9090) │ └─────────────┬─────────────┘ │ ┌───────────────┬───────┴───────┬───────────────┐ │ │ │ │ ▼ ▼ ▼ ▼ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ Headscale │ │ DERP │ │ Tailscale │ │ System │ │ Metrics │ │ Metrics │ │ Metrics │ │ Metrics │ │ :9090 │ │ :8080 │ │ (via API) │ │ (node_exp) │ └─────────────┘ └─────────────┘ └─────────────┘ └─────────────┘ ``` ### 9.2 Prometheus 配置 ```yaml # /etc/prometheus/prometheus.yml global: scrape_interval: 15s evaluation_interval: 15s alerting: alertmanagers: - static_configs: - targets: - alertmanager:9093 rule_files: - "/etc/prometheus/rules/*.yml" scrape_configs: # Headscale 指标 - job_name: 'headscale' static_configs: - targets: ['100.64.0.1:9090'] metrics_path: /metrics relabel_configs: - source_labels: [__address__] target_label: instance replacement: headscale-primary # DERP 服务器指标 - job_name: 'derp' static_configs: - targets: - 'derp-bj.ops.company.com:8080' - 'derp-sh.ops.company.com:8080' - 'derp-hk.ops.company.com:8080' # PostgreSQL 指标 - job_name: 'postgresql' static_configs: - targets: ['100.64.0.2:9187'] # 所有 Tailscale 节点 (使用服务发现) - job_name: 'tailscale-nodes' file_sd_configs: - files: - '/etc/prometheus/tailscale_nodes.json' refresh_interval: 5m ``` ### 9.3 关键监控指标 #### 9.3.1 Headscale 指标 | 指标名称 | 类型 | 说明 | 告警阈值 | |---------|-----|------|---------| | `headscale_connected_nodes` | Gauge | 已连接节点数 | < 预期节点数 * 0.9 | | `headscale_api_requests_total` | Counter | API 请求总数 | - | | `headscale_api_request_duration_seconds` | Histogram | API 响应时间 | P99 > 1s | | `headscale_db_query_duration_seconds` | Histogram | 数据库查询时间 | P99 > 500ms | #### 9.3.2 DERP 指标 | 指标名称 | 类型 | 说明 | 告警阈值 | |---------|-----|------|---------| | `derp_connections` | Gauge | 当前连接数 | > 10000 | | `derp_bytes_sent_total` | Counter | 发送字节数 | 突增 > 200% | | `derp_bytes_received_total` | Counter | 接收字节数 | 突增 > 200% | | `derp_home_connections` | Gauge | Home 连接数 | - | #### 9.3.3 节点健康指标 | 指标名称 | 类型 | 说明 | 告警阈值 | |---------|-----|------|---------| | `tailscale_up` | Gauge | 节点在线状态 | = 0 | | `tailscale_derp_latency_seconds` | Gauge | DERP 延迟 | > 200ms | | `tailscale_peer_count` | Gauge | 对等节点数 | = 0 | ### 9.4 告警规则配置 ```yaml # /etc/prometheus/rules/headscale.yml groups: - name: headscale interval: 30s rules: # Headscale 服务不可用 - alert: HeadscaleDown expr: up{job="headscale"} == 0 for: 1m labels: severity: critical annotations: summary: "Headscale 控制平面不可用" description: "Headscale 服务已离线超过1分钟" # 节点大量离线 - alert: TailscaleNodesMassOffline expr: | (count(tailscale_up == 0) / count(tailscale_up)) > 0.1 for: 5m labels: severity: warning annotations: summary: "超过10%的节点离线" description: "{{ $value | humanizePercentage }} 的节点当前离线" # API 响应慢 - alert: HeadscaleAPILatencyHigh expr: | histogram_quantile(0.99, rate(headscale_api_request_duration_seconds_bucket[5m])) > 1 for: 10m labels: severity: warning annotations: summary: "Headscale API 响应延迟过高" description: "API P99 延迟: {{ $value | humanizeDuration }}" # 数据库连接问题 - alert: HeadscaleDatabaseConnectionIssues expr: | rate(headscale_db_errors_total[5m]) > 0.1 for: 5m labels: severity: critical annotations: summary: "Headscale 数据库连接异常" description: "数据库错误率: {{ $value }}/s" - name: derp rules: # DERP 服务不可用 - alert: DERPServerDown expr: up{job="derp"} == 0 for: 2m labels: severity: critical annotations: summary: "DERP 中继服务器不可用" description: "{{ $labels.instance }} DERP 服务已离线" # DERP 连接数过高 - alert: DERPConnectionsHigh expr: derp_connections > 8000 for: 10m labels: severity: warning annotations: summary: "DERP 连接数接近上限" description: "{{ $labels.instance }} 当前连接数: {{ $value }}" - name: nodes rules: # 单个节点离线 - alert: TailscaleNodeDown expr: tailscale_up == 0 for: 5m labels: severity: warning annotations: summary: "Tailscale 节点离线" description: "节点 {{ $labels.hostname }} 已离线超过5分钟" # 生产环境节点离线 (更严格) - alert: ProductionNodeDown expr: tailscale_up{env="prod"} == 0 for: 2m labels: severity: critical annotations: summary: "生产环境节点离线" description: "生产节点 {{ $labels.hostname }} 已离线" # 节点无法建立直连 - alert: TailscaleNoPeerConnection expr: tailscale_peer_count == 0 and tailscale_up == 1 for: 10m labels: severity: warning annotations: summary: "节点无法建立 P2P 连接" description: "节点 {{ $labels.hostname }} 无法与其他节点建立直接连接" ``` ### 9.5 Grafana 仪表板 创建以下仪表板: 1. **Headscale Overview** - 总节点数、在线节点数、离线节点数 - API 请求 QPS 和延迟 - 数据库连接状态 2. **DERP Network** - 各 DERP 服务器连接数 - 流量统计 (发送/接收) - 区域分布 3. **Node Health** - 节点在线状态矩阵 - 各节点延迟热力图 - 节点流量统计 4. **ACL Audit** - 访问拒绝事件 - 规则命中统计 - 异常访问模式 --- ## 10. 运维管理规范 ### 10.1 日常运维操作 #### 10.1.1 用户管理 ```bash # 创建用户 (命名空间) headscale users create prod headscale users create staging headscale users create dev # 查看用户列表 headscale users list # 删除用户 (谨慎操作) headscale users destroy dev ``` #### 10.1.2 节点管理 ```bash # 列出所有节点 headscale nodes list # 列出特定用户的节点 headscale nodes list --user prod # 查看节点详情 headscale nodes list --identifier prod-web-bj-001 # 删除节点 headscale nodes delete --identifier # 重命名节点 headscale nodes rename --identifier --name new-hostname # 移动节点到其他用户 headscale nodes move --identifier --user staging # 设置节点过期时间 headscale nodes expire --identifier ``` #### 10.1.3 路由管理 ```bash # 查看所有路由 headscale routes list # 启用路由 headscale routes enable --route # 禁用路由 headscale routes disable --route # 删除路由 headscale routes delete --route ``` #### 10.1.4 API Key 管理 ```bash # 创建 API Key headscale apikeys create --expiration 90d # 列出 API Keys headscale apikeys list # 使 API Key 过期 headscale apikeys expire --prefix ``` ### 10.2 运维脚本工具 #### 10.2.1 节点健康检查脚本 ```bash #!/bin/bash # /opt/scripts/check-tailscale-health.sh HEADSCALE_URL="https://hs.ops.company.com" API_KEY="your_api_key" ALERT_WEBHOOK="https://webhook.ops.company.com/alert" # 获取所有节点 nodes=$(curl -s -H "Authorization: Bearer $API_KEY" \ "${HEADSCALE_URL}/api/v1/machine" | jq -r '.machines[]') # 检查离线节点 offline_nodes=$(echo "$nodes" | jq -r 'select(.online == false) | .givenName') if [ -n "$offline_nodes" ]; then # 发送告警 curl -X POST "$ALERT_WEBHOOK" \ -H "Content-Type: application/json" \ -d "{\"text\": \"[Tailscale] 以下节点离线:\\n$offline_nodes\"}" fi # 检查即将过期的节点 expiring_nodes=$(echo "$nodes" | jq -r \ 'select(.expiry != "0001-01-01T00:00:00Z") | select((.expiry | fromdateiso8601) < (now + 604800)) | .givenName + " (expires: " + .expiry + ")"') if [ -n "$expiring_nodes" ]; then curl -X POST "$ALERT_WEBHOOK" \ -H "Content-Type: application/json" \ -d "{\"text\": \"[Tailscale] 以下节点即将过期:\\n$expiring_nodes\"}" fi ``` #### 10.2.2 批量节点管理脚本 ```python #!/usr/bin/env python3 # /opt/scripts/headscale-manager.py import requests import argparse import json from datetime import datetime, timedelta class HeadscaleManager: def __init__(self, url, api_key): self.url = url.rstrip('/') self.headers = { 'Authorization': f'Bearer {api_key}', 'Content-Type': 'application/json' } def get_nodes(self, user=None): """获取节点列表""" params = {} if user: params['user'] = user resp = requests.get( f'{self.url}/api/v1/machine', headers=self.headers, params=params ) return resp.json().get('machines', []) def get_offline_nodes(self, threshold_hours=1): """获取离线节点""" nodes = self.get_nodes() offline = [] threshold = datetime.utcnow() - timedelta(hours=threshold_hours) for node in nodes: if not node.get('online', False): last_seen = datetime.fromisoformat( node['lastSeen'].replace('Z', '+00:00') ) if last_seen < threshold.replace(tzinfo=last_seen.tzinfo): offline.append(node) return offline def bulk_tag_nodes(self, node_ids, tags): """批量设置节点标签""" results = [] for node_id in node_ids: resp = requests.post( f'{self.url}/api/v1/machine/{node_id}/tags', headers=self.headers, json={'tags': tags} ) results.append({ 'node_id': node_id, 'success': resp.status_code == 200 }) return results def cleanup_expired_nodes(self, dry_run=True): """清理过期节点""" nodes = self.get_nodes() expired = [] for node in nodes: expiry = node.get('expiry') if expiry and expiry != '0001-01-01T00:00:00Z': expiry_dt = datetime.fromisoformat(expiry.replace('Z', '+00:00')) if expiry_dt < datetime.utcnow().replace(tzinfo=expiry_dt.tzinfo): expired.append(node) if not dry_run: for node in expired: requests.delete( f'{self.url}/api/v1/machine/{node["id"]}', headers=self.headers ) return expired if __name__ == '__main__': parser = argparse.ArgumentParser(description='Headscale 管理工具') parser.add_argument('--url', required=True, help='Headscale URL') parser.add_argument('--api-key', required=True, help='API Key') parser.add_argument('action', choices=['list', 'offline', 'cleanup']) parser.add_argument('--user', help='过滤用户') parser.add_argument('--dry-run', action='store_true', help='试运行模式') args = parser.parse_args() manager = HeadscaleManager(args.url, args.api_key) if args.action == 'list': nodes = manager.get_nodes(args.user) print(json.dumps(nodes, indent=2)) elif args.action == 'offline': offline = manager.get_offline_nodes() print(f"离线节点数: {len(offline)}") for node in offline: print(f" - {node['givenName']} (last seen: {node['lastSeen']})") elif args.action == 'cleanup': expired = manager.cleanup_expired_nodes(dry_run=args.dry_run) print(f"过期节点数: {len(expired)}") for node in expired: print(f" - {node['givenName']} (expired: {node['expiry']})") ``` ### 10.3 日志管理 ```bash # Headscale 日志位置 /var/log/headscale/headscale.log # 日志轮转配置 cat > /etc/logrotate.d/headscale << 'EOF' /var/log/headscale/*.log { daily rotate 30 compress delaycompress missingok notifempty create 0640 headscale headscale sharedscripts postrotate systemctl reload headscale > /dev/null 2>&1 || true endscript } EOF # 结构化日志查询 (JSON 格式) cat /var/log/headscale/headscale.log | jq 'select(.level == "error")' ``` ### 10.4 备份与恢复 #### 10.4.1 数据库备份 ```bash #!/bin/bash # /opt/scripts/backup-headscale.sh BACKUP_DIR="/backup/headscale" DATE=$(date +%Y%m%d_%H%M%S) RETENTION_DAYS=30 # PostgreSQL 备份 pg_dump -h localhost -U headscale -d headscale -F c \ -f "${BACKUP_DIR}/headscale_${DATE}.dump" # 配置文件备份 tar -czf "${BACKUP_DIR}/config_${DATE}.tar.gz" \ /etc/headscale/config.yaml \ /etc/headscale/acl.json \ /etc/headscale/derp.json \ /var/lib/headscale/private.key \ /var/lib/headscale/noise_private.key # 清理旧备份 find "${BACKUP_DIR}" -type f -mtime +${RETENTION_DAYS} -delete # 上传到 S3 (可选) aws s3 sync "${BACKUP_DIR}/" s3://backup-bucket/headscale/ ``` #### 10.4.2 数据恢复 ```bash #!/bin/bash # /opt/scripts/restore-headscale.sh BACKUP_FILE=$1 # 停止服务 systemctl stop headscale # 恢复数据库 pg_restore -h localhost -U headscale -d headscale -c "${BACKUP_FILE}" # 恢复配置 tar -xzf "${BACKUP_FILE%.dump}_config.tar.gz" -C / # 重启服务 systemctl start headscale # 验证 headscale nodes list ``` ### 10.5 版本升级流程 ```bash #!/bin/bash # /opt/scripts/upgrade-headscale.sh NEW_VERSION=$1 BACKUP_DIR="/backup/headscale/upgrade" echo "开始升级 Headscale 到版本 ${NEW_VERSION}" # 1. 备份当前版本 echo "备份当前配置和数据..." ./backup-headscale.sh # 2. 下载新版本 echo "下载新版本..." wget -O /tmp/headscale_new.deb \ "https://github.com/juanfont/headscale/releases/download/v${NEW_VERSION}/headscale_${NEW_VERSION}_linux_amd64.deb" # 3. 停止服务 echo "停止 Headscale 服务..." systemctl stop headscale # 4. 安装新版本 echo "安装新版本..." dpkg -i /tmp/headscale_new.deb # 5. 数据库迁移 (如果需要) echo "执行数据库迁移..." headscale serve --config /etc/headscale/config.yaml --migrate-only # 6. 启动服务 echo "启动服务..." systemctl start headscale # 7. 验证 echo "验证升级..." sleep 5 headscale version headscale nodes list | head -5 echo "升级完成!" ``` --- ## 11. 故障恢复与灾备 ### 11.1 故障场景与恢复方案 #### 11.1.1 Headscale 主节点故障 **影响范围**: - 新节点无法加入网络 - 无法更新 ACL 策略 - 已连接节点正常通信 (P2P 直连) **恢复步骤**: ```bash # 1. 确认主节点故障 systemctl status headscale curl -s https://hs.ops.company.com/health # 2. 切换到备用节点 # 在备用节点上修改 DNS 或负载均衡器配置 # 3. 如果是数据库问题,切换到从库 # 修改 config.yaml 中的数据库连接 # 4. 重启服务 systemctl restart headscale # 5. 验证服务恢复 headscale nodes list ``` #### 11.1.2 PostgreSQL 数据库故障 **恢复步骤**: ```bash # 1. 如果主库故障,提升从库 # 在从库执行 sudo -u postgres pg_ctl promote -D /var/lib/postgresql/15/main # 2. 更新 Headscale 配置指向新主库 sed -i 's/old_primary_ip/new_primary_ip/' /etc/headscale/config.yaml # 3. 重启 Headscale systemctl restart headscale # 4. 重建从库 # 使用 pg_basebackup 从新主库同步 ``` #### 11.1.3 DERP 中继服务器故障 **影响范围**: - 无法 NAT 穿透的节点将失去连接 - 可直连的节点不受影响 **恢复步骤**: ```bash # 1. 检查 DERP 服务状态 systemctl status derper curl -s https://derp-bj.ops.company.com/derp/probe # 2. 如果无法恢复,从 DERP Map 中移除该节点 # 编辑 /etc/headscale/derp.json,移除故障节点 # 3. 等待客户端自动切换到其他 DERP # 或手动强制刷新 tailscale netcheck ``` #### 11.1.4 完全灾难恢复 ```bash # 1. 准备新服务器 # 2. 从备份恢复数据库 pg_restore -h localhost -U headscale -d headscale /backup/latest.dump # 3. 恢复配置文件 tar -xzf /backup/config_latest.tar.gz -C / # 4. 安装 Headscale dpkg -i headscale_latest.deb # 5. 启动服务 systemctl start headscale # 6. 更新 DNS 指向新服务器 # 7. 验证所有节点重新连接 watch 'headscale nodes list | grep -c Online' ``` ### 11.2 RTO 和 RPO 目标 | 场景 | RTO (恢复时间目标) | RPO (数据恢复点目标) | |------|-------------------|---------------------| | Headscale 单点故障 | < 5 分钟 | 0 (热备接管) | | 数据库故障 | < 15 分钟 | < 1 分钟 (同步复制) | | DERP 故障 | 自动切换 | N/A | | 完全灾难 | < 2 小时 | < 24 小时 | ### 11.3 定期演练 建议每季度进行一次故障演练: 1. **演练内容**: - 主备切换 - 数据库故障转移 - 从备份恢复 - ACL 策略回滚 2. **演练记录**: - 演练时间和参与人员 - 实际恢复时间 - 发现的问题和改进措施 --- ## 12. 实施计划与里程碑 ### 12.1 实施阶段 #### 第一阶段:基础设施准备 | 任务 | 负责人 | 前置条件 | 交付物 | |------|--------|---------|--------| | 服务器资源申请 | 运维 | 预算审批 | 服务器清单 | | 域名和证书准备 | 运维 | 域名购买 | SSL 证书 | | PostgreSQL 高可用部署 | DBA | 服务器就绪 | 数据库集群 | | 网络规划确认 | 网络组 | - | IP 规划文档 | #### 第二阶段:核心服务部署 | 任务 | 负责人 | 前置条件 | 交付物 | |------|--------|---------|--------| | Headscale 主节点部署 | 运维 | PostgreSQL 就绪 | 服务运行 | | Headscale 备节点配置 | 运维 | 主节点就绪 | 主备切换测试 | | DERP 中继服务器部署 | 运维 | 服务器就绪 | 多区域 DERP | | ACL 策略配置 | 安全组 | 服务运行 | ACL 文件 | | 监控告警部署 | 运维 | 服务运行 | Grafana 仪表板 | #### 第三阶段:节点接入 | 任务 | 负责人 | 前置条件 | 交付物 | |------|--------|---------|--------| | 测试环境接入 | 运维 | 服务就绪 | 测试节点在线 | | 预发布环境接入 | 运维 | 测试通过 | 预发布节点在线 | | 生产环境接入 (批次1) | 运维 | 预发布验证 | 首批生产节点 | | 生产环境接入 (批次2-N) | 运维 | 批次1成功 | 全部生产节点 | | 运维人员设备接入 | 运维 | 生产稳定 | 运维设备在线 | | 开发人员设备接入 | 开发组长 | 运维验证 | 开发设备在线 | #### 第四阶段:验收与交接 | 任务 | 负责人 | 前置条件 | 交付物 | |------|--------|---------|--------| | 功能验收测试 | QA | 全部接入 | 验收报告 | | 性能压力测试 | 性能组 | 功能验收 | 性能报告 | | 故障演练 | 运维 | 验收通过 | 演练记录 | | 文档交付 | 运维 | 演练通过 | 运维手册 | | 培训交接 | 运维 | 文档完成 | 培训记录 | ### 12.2 里程碑 ``` ┌─────────────────────────────────────────────────────────────────────────────┐ │ 实施时间线 │ ├─────────────────────────────────────────────────────────────────────────────┤ │ │ │ M1: 基础设施就绪 │ │ ├── PostgreSQL HA 部署完成 │ │ ├── 域名/证书准备完成 │ │ └── 网络规划确认 │ │ │ │ M2: 核心服务上线 │ │ ├── Headscale 主备节点运行 │ │ ├── DERP 多区域部署 │ │ ├── 监控告警就绪 │ │ └── ACL 策略配置完成 │ │ │ │ M3: 测试验证完成 │ │ ├── 测试环境全部接入 │ │ ├── 预发布环境接入 │ │ └── 功能验收通过 │ │ │ │ M4: 生产环境迁移完成 │ │ ├── 生产服务器全部接入 │ │ ├── 旧 VPN 方案下线 │ │ └── 运维设备接入 │ │ │ │ M5: 项目验收 │ │ ├── 故障演练通过 │ │ ├── 培训交接完成 │ │ └── 项目正式结项 │ │ │ └─────────────────────────────────────────────────────────────────────────────┘ ``` ### 12.3 验收标准 | 验收项 | 验收标准 | 验收方法 | |--------|---------|---------| | 网络连通性 | 任意两节点可互通 | ping/traceroute 测试 | | 连接延迟 | 同区域 P2P < 10ms | Tailscale ping | | 服务可用性 | 99.9% 可用率 | 监控数据 | | ACL 生效 | 策略符合设计 | 安全扫描 | | 故障恢复 | RTO < 目标时间 | 故障演练 | | 性能指标 | 支持 1000+ 节点 | 压力测试 | --- ## 13. 风险评估与应对 ### 13.1 风险矩阵 | 风险项 | 可能性 | 影响 | 风险等级 | 应对措施 | |--------|-------|-----|---------|---------| | Headscale 版本不稳定 | 中 | 高 | 高 | 充分测试,制定回滚方案 | | 网络穿透失败率高 | 中 | 中 | 中 | 部署多区域 DERP | | 密钥泄露 | 低 | 极高 | 高 | 密钥管理,定期轮换 | | 性能瓶颈 | 中 | 中 | 中 | 监控预警,容量规划 | | 运维人员技能不足 | 中 | 中 | 中 | 培训,文档完善 | | 与现有系统冲突 | 低 | 中 | 低 | 充分测试,分批上线 | ### 13.2 回滚方案 #### 13.2.1 服务端回滚 ```bash # 1. 停止新版本服务 systemctl stop headscale # 2. 恢复旧版本 dpkg -i /backup/headscale_old.deb # 3. 恢复配置 cp /backup/config_old.yaml /etc/headscale/config.yaml # 4. 如需回滚数据库 pg_restore -h localhost -U headscale -d headscale -c /backup/db_old.dump # 5. 重启服务 systemctl start headscale ``` #### 13.2.2 客户端回滚 ```bash # 断开 Headscale 连接 tailscale down # 恢复原有 VPN 配置 # (根据原有 VPN 方案操作) ``` ### 13.3 应急联系人 | 角色 | 姓名 | 联系方式 | 职责 | |------|-----|---------|-----| | 项目负责人 | xxx | 138xxxxxxxx | 决策、协调 | | 技术负责人 | xxx | 139xxxxxxxx | 技术方案 | | 运维负责人 | xxx | 137xxxxxxxx | 部署实施 | | DBA | xxx | 136xxxxxxxx | 数据库运维 | | 安全负责人 | xxx | 135xxxxxxxx | 安全评审 | --- ## 14. 附录 ### 14.1 术语表 | 术语 | 解释 | |------|-----| | Headscale | Tailscale 的开源自托管控制服务器 | | Tailscale | 基于 WireGuard 的零配置 VPN 方案 | | WireGuard | 现代化的 VPN 协议 | | DERP | Designated Encrypted Relay for Packets,加密中继协议 | | MagicDNS | Tailscale 的自动 DNS 服务 | | ACL | Access Control List,访问控制列表 | | PreAuth Key | 预认证密钥,用于无交互接入 | | Mesh Network | 网状网络,节点间可直接通信 | | NAT Traversal | NAT 穿透技术 | | STUN | Session Traversal Utilities for NAT | ### 14.2 参考文档 - [Headscale 官方文档](https://headscale.net/) - [Tailscale 官方文档](https://tailscale.com/docs/) - [WireGuard 官方网站](https://www.wireguard.com/) - [Headscale GitHub](https://github.com/juanfont/headscale) ### 14.3 常用命令速查 ```bash # Headscale 服务管理 systemctl start|stop|restart|status headscale # 用户管理 headscale users list headscale users create headscale users destroy # 节点管理 headscale nodes list headscale nodes delete -i headscale nodes expire -i headscale nodes rename -i -n headscale nodes tag -i -t # 预认证密钥 headscale preauthkeys create --user --expiration 24h headscale preauthkeys list --user # 路由管理 headscale routes list headscale routes enable -r # API Key headscale apikeys create --expiration 90d headscale apikeys list # Tailscale 客户端 tailscale up --login-server tailscale down tailscale status tailscale ip tailscale ping tailscale netcheck ``` ### 14.4 配置模板 配置模板文件位于: - `/opt/templates/headscale/config.yaml.tmpl` - `/opt/templates/headscale/acl.json.tmpl` - `/opt/templates/derp/docker-compose.yml.tmpl` ### 14.5 变更记录 | 版本 | 日期 | 变更内容 | 变更人 | |------|-----|---------|--------| | v1.0 | 2025-12-15 | 初稿 | xxx | | v2.0 | 2025-12-18 | 详细设计完善 | AI Assistant | --- > **文档维护说明**: 本文档应随着项目进展持续更新,每次重大变更需记录在变更记录中。