返回首页

Keycloak+Headscale:搭建开源零信任网络访问方案

前言:为什么需要零信任?

传统网络安全模型基于"内网可信、外网不可信"的边界思维,但随着远程办公、云原生架构和BYOD的普及,这种模型已经千疮百孔。零信任网络访问()的核心理念是"永不信任,始终验证"——无论用户身处公司内网还是咖啡厅WiFi,都必须经过身份验证和设备合规检查才能访问资源。

商业方案如 Zscaler ZPA 年费 $120-200/用户,Palo Alto Prisma Access 价格不透明但据估算也在类似区间,Cloudflare Access 相对便宜但仍需 $7/用户/月。对于中小团队或个人项目,这些成本可能难以承受。

本文将带你用 + 搭建一套完全开源的零信任方案,覆盖身份认证、网络隧道、策略控制三大核心能力。

一、付费工具定价对比

工具 定价模型 价格范围 核心优势 适用场景
Zscaler ZPA 按用户/年 $120-200/用户/年 全球150+ PoP节点,SASE集成 大型企业
Palo Alto Prisma Access 按用户计费 价格不公开(估算$150+/用户/年) 深度威胁检测,SASE全栈 合规要求高的企业
Cloudflare Access 按用户/月 $7/用户/月 CDN+安全一体化,低延迟 中小团队,应用
Cloudflare One 按用户/月 $7-20/用户/月 网关+接入+浏览器隔离 需要完整SASE的团队

关键差距:商业方案的核心价值在于全球边缘节点、内置威胁情报、与SASE/SSE平台的深度集成。开源方案需要自行搭建这些能力,但完全可控且零授权费用。

二、免费替代方案介绍

2.1 Keycloak —— 身份与访问管理(IAM)

Keycloak 是 Red Hat 主导的开源 IAM 解决方案,提供:

  • SAML 2.0 / OpenID Connect / OAuth 2.0 协议支持
  • 用户联邦(LDAP/AD集成)
  • 多因素认证(TOTP、WebAuthn/FIDO2)
  • 细粒度授权策略(基于属性的访问控制ABAC)
  • 单点登录(SSO)

: https://github.com/keycloak/keycloak (19k+ Stars)

2.2 Headscale —— 自托管控制服务器

Headscale 是 Tailscale 控制服务器的开源替代实现:

  • 基于 协议的Mesh
  • 自托管,数据不经过第三方服务器
  • 支持 DERP 中继服务器自建
  • 通过 ACL 策略控制网络访问
  • 轻量级,单二进制部署

GitHub: https://github.com/juanfont/headscale (22k+ Stars)

2.3 其他替代方案

  • freeIPA: 完整的身份管理方案(LDAP+Kerberos++CA),适合纯Linux环境
  • Pomerium: 开源零信任代理,支持OIDC,与Keycloak无缝集成
  • WireGuard + LDAP: 直接使用WireGuard,通过脚本集成LDAP认证

三、完整部署实战

3.1 环境准备

# 系统要求:Ubuntu 22.04 LTS / Debian 12
# 最低配置:2核4GB内存(Keycloak较吃内存),50GB磁盘

# 更新系统
sudo   && sudo apt  -y

# 安装必要工具
sudo apt install -y curl wget gnupg2 software-properties-common apt-transport-https ca-certificates

# 安装Docker(用于Keycloak)
curl -fsSL https://get.docker.com | sudo bash
sudo usermod -aG docker $USER

# 安装Docker Compose
sudo apt install -y docker-compose-plugin

3.2 部署 Keycloak

创建项目目录和配置:

mkdir -p /opt/keycloak && cd /opt/keycloak

cat > docker-compose.yml << 'EOF'
version: '3.8'
services:
  keycloak-db:
    : postgres:16-alpine
    container_name: keycloak-db
    restart: unless-stopped
    environment:
      POSTGRES_DB: keycloak
      POSTGRES_USER: keycloak
      POSTGRES_PASSWORD: ChangeMeStrongPass2024!
    volumes:
      - keycloak-db-:/var/lib/postgresql/data
    networks:
      - keycloak-net
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U keycloak"]
      interval: 10s
      timeout: 5s
      retries: 5

  keycloak:
    image: quay.io/keycloak/keycloak:24.0
    container_name: keycloak
    restart: unless-stopped
    depends_on:
      keycloak-db:
        condition: service_healthy
    environment:
      KC_DB: postgres
      KC_DB_URL: jdbc:postgresql://keycloak-db:5432/keycloak
      KC_DB_USERNAME: keycloak
      KC_DB_PASSWORD: ChangeMeStrongPass2024!
      KEYCLOAK_ADMIN: admin
      KEYCLOAK_ADMIN_PASSWORD: AdminPass2024!
      KC_HOSTNAME: auth.example.com
      KC_PROXY: 
      KC_HTTP_ENABLED: "true"
    command: start
    ports:
      - "8080:8080"
    networks:
      - keycloak-net

volumes:
  keycloak-db-data:

networks:
  keycloak-net:
    driver: bridge
EOF

# 启动Keycloak
docker compose up -d

# 等待启动完成
sleep 30
docker compose logs keycloak | tail -20

配置Keycloak领域(Realm)和客户端:

# 获取admin token
KC_TOKEN=$(curl -s -X POST "http://localhost:8080/realms/master/protocol/openid-connect/token" \
  -d "client_id=admin-" \
  -d "username=admin" \
  -d "password=AdminPass2024!" \
  -d "grant_type=password" | python3 -c "import sys,json; print(json.load(sys.stdin)['access_token'])")

# 创建ZTNA领域
curl -s -X POST "http://localhost:8080/admin/realms" \
  -H "Authorization: Bearer $KC_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "realm": "ztna",
    "enabled": true,
    "displayName": "ZTNA Zero Trust",
    "registrationAllowed": false,
    "loginWithEmailAllowed": true,
    "duplicateEmailsAllowed": false,
    "resetPasswordAllowed": true,
    "bruteForceProtected": true,
    "maxFailureWaitSeconds": 900,
    "permanentLockout": false
  }'

# 创建Headscale OIDC客户端
curl -s -X POST "http://localhost:8080/admin/realms/ztna/clients" \
  -H "Authorization: Bearer $KC_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "clientId": "headscale",
    "name": "Headscale VPN",
    "enabled": true,
    "protocol": "openid-connect",
    "publicClient": false,
    "secret": "headscale-oidc-secret-2024",
    "redirectUris": ["https://vpn.example.com/oidc/callback"],
    "webOrigins": ["https://vpn.example.com"],
    "standardFlowEnabled": true,
    "directAccessGrantsEnabled": true,
    "attributes": {
      "post.logout.redirect.uris": "https://vpn.example.com/"
    }
  }'

# 启用TOTP二因素认证(推荐)
curl -s -X PUT "http://localhost:8080/admin/realms/ztna/authentication/required-actions" \
  -H "Authorization: Bearer $KC_TOKEN" \
  -H "Content-Type: application/json" \
  -d '[
    {"alias":"CONFIGURE_TOTP","enabled":true,"priority":1},
    {"alias":"webauthn-register","enabled":true,"priority":2}
  ]'

3.3 部署 Headscale

# 创建Headscale目录
mkdir -p /opt/headscale && cd /opt/headscale

# 下载最新版Headscale
HS_VERSION="0.23.0"
wget "https://github.com/juanfont/headscale/releases/download/v${HS_VERSION}/headscale_${HS_VERSION}_linux_amd64"
chmod +x "headscale_${HS_VERSION}_linux_amd64"
sudo mv "headscale_${HS_VERSION}_linux_amd64" /usr/local/bin/headscale

# 创建数据目录
sudo mkdir -p /etc/headscale /var/lib/headscale

# 生成配置
headscale configdump > /etc/headscale/config.yaml

# 编辑核心配置
cat > /etc/headscale/config.yaml << 'EOF'
server_url: https://vpn.example.com
listen_addr: 0.0.0.0:8443
metrics_listen_addr: 127.0.0.1:9090

# DERP中继(自建)
derp:
  server:
    enabled: true
    region_id: 999
    region_code: "selfhosted"
    region_name: " DERP"
    stun_listen_addr: "0.0.0.0:3478"
  urls: []
  paths: []

# 数据库(SQLite足够中小规模)
db_type: sqlite3
db_path: /var/lib/headscale/db.sqlite

# OIDC配置(指向Keycloak)
oidc:
  issuer: https://auth.example.com/realms/ztna
  client_id: headscale
  client_secret: headscale-oidc-secret-2024
  scope: ["openid", "profile", "email"]
  allowed_domains: ["example.com"]
  strip_email_domain: true

# DNS配置
dns_config:
  override_local_dns: true
  nameservers:
    - 1.1.1.1
    - 8.8.8.8
  domains: []
  magic_dns: true

# IP分配
prefixes:
  : 100.64.0.0/10
  v6: fd7a:115c:a1e0::/48
EOF

# 创建systemd服务
sudo cat > /etc/systemd/system/headscale.service << 'EOF'
[Unit]
Description=Headscale control server
After=syslog.target
After=network.target

[Service]
Type=simple
User=root
ExecStart=/usr/local/bin/headscale serve
Restart=on-failure
RestartSec=5

[Install]
WantedBy=multi-user.target
EOF

sudo systemctl daemon-reload
sudo systemctl enable --now headscale
sudo systemctl status headscale

3.4 配置反向代理(

sudo apt install -y nginx certbot python3-certbot-nginx

cat > /etc/nginx/sites-available/ztna << 'EOF'
# Keycloak (auth.example.com)
server {
    listen 443 ssl http2;
    server_name auth.example.com;

    ssl_certificate /etc/letsencrypt/live/auth.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/auth.example.com/privkey.pem;

    location / {
        proxy_pass http://127.0.0.1:8080;
        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;
    }
}

# Headscale (vpn.example.com)
server {
    listen 443 ssl http2;
    server_name vpn.example.com;

    ssl_certificate /etc/letsencrypt/live/vpn.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/vpn.example.com/privkey.pem;

    location / {
        grpc_pass grpc://127.0.0.1:50443;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }

    location /web {
        proxy_pass http://127.0.0.1:8443;
        proxy_set_header Host $host;
    }
}
EOF

sudo ln -s /etc/nginx/sites-available/ztna /etc/nginx/sites-enabled/
sudo nginx -t && sudo systemctl reload nginx

3.5 客户端接入

# 安装Tailscale客户端(与Headscale兼容)
curl -fsSL https://tailscale.com/install.sh | sh

# 注册到自托管控制服务器
sudo tailscale up --login-server=https://vpn.example.com --accept-routes

# 在服务器端批准节点
headscale -n example nodes list
headscale -n example nodes approve --identifier <node-id>

# 配置ACL策略
cat > /etc/headscale/acl.hujson << 'EOF'
{
  "acls": [
    {
      "action": "accept",
      "src": ["group:developers"],
      "dst": ["tag:internal:*"]
    },
    {
      "action": "accept",
      "src": ["group:ops"],
      "dst": ["*:*"]
    }
  ],
  "groups": {
    "group:developers": ["[email protected]", "[email protected]"],
    "group:ops": ["[email protected]"]
  },
  "tagOwners": {
    "tag:internal": ["group:ops"]
  }
}
EOF

headscale -n example policy set /etc/headscale/acl.hujson

四、功能对比表

功能维度 Zscaler ZPA Cloudflare Access Keycloak+Headscale
身份认证 内置+SSO 内置+SSO Keycloak全功能
MFA支持 ✅ 内置 ✅ 内置 ✅ TOTP/WebAuthn
网络隧道 ✅ 专用 ✅ WARP ✅ WireGuard
全球PoP 150+ 300+ 需自建DERP
ACL策略 ✅ 细粒度 ✅ 细粒度 ✅ ACL文件
SASE集成 ✅ 全栈 ✅ 网关+DLP ❌ 需额外集成
日志审计 ✅ 详细 ✅ 详细 ✅ 需配置
设备合规检查 ✅ 内置 ✅ 内置 需自行实现
部署复杂度 中高
年成本(50用户) $6,000-10,000 $4,200 $0(服务器成本另计)

五、生产环境注意事项

  1. 高可用:Headscale 目前不支持集群模式,需通过备份+故障转移实现HA
  2. 证书管理:建议使用 certbot 自动续期 Let's Encrypt 证书
  3. 监控:Headscale 暴露 Prometheus metrics,配合 Grafana 监控节点状态
  4. 备份策略:定期备份 /var/lib/headscale/db.sqlite 和 Keycloak PostgreSQL 数据库
  5. 安全加固:Keycloak 生产环境务必启用 HTTPS、配置速率限制、定期更新

总结

Keycloak + Headscale 组合为中小团队提供了一套可行的零信任网络方案。虽然缺少商业方案的全球边缘网络和SASE集成能力,但核心的"身份验证+加密隧道+策略控制"三大支柱完全可以覆盖。对于预算有限但安全意识强的团队,这是目前最成熟的开源ZTNA方案。

常见问题

前言:为什么需要零信任?

>前言:为什么需要零信任?传统网络安全模型基于&quot;内网可信、外网不可信&quot;的边界思维,但随着远程办公、云原生架构和BYOD的普及,这种模型已经千疮百孔。零信任网络访问(ZTNA)的核心理念是&quot;永不信任,始终验证&quot;——无论用户身处公司内网还是咖啡厅WiFi,都必须经过身份验证和设备合规检查才能访问资源。 商业方案如 Zscaler ZPA 年费 $120-200/用户,Palo Alto Prisma Access 价格不透明但据估算也在类似区间,Cloudflare Access 相对便宜但仍需 $7/用户/月。对于中小团队或个人项目,这些成本可能难以承受。

评论