上一篇 下一篇 分享链接 返回 返回顶部

如何在香港服务器的 Windows Server 2022 上配置多站点 ADFS,确保跨境用户认证的高可用性

发布人:Minchunlin 发布时间:2025-09-02 08:36 阅读量:810


周五晚上 22:40,我站在香港机房 19°C 的冷风里,手里那杯便利店买的热美式已经不热了。白天我们收到了来自华南几个分部的抱怨:用户登录公司门户时延飙到 6~8 秒,偶发 502。我们的 ADFS 之前是“单站点 + 单 VIP + 单对 WAP”,部署在香港一个机房里,正常情况下没问题,但一旦跨境网络抖动,就会把认证流程放大出各种毛刺。于是我决定趁着周末窗口,把 ADFS 改造成“香港双机房多站点 + 跨站点 GSLB + 就近接入 + 失效切换”。这篇文章,就是那一晚到第二天清晨我从零到一完成并验证多站点 ADFS 的完整过程、踩到的坑和解决方案。

目标与设计要点

  • 跨境(内地↔香港)用户就近接入:降低 TLS 握手与 claims 交换的时延。
  • 多站点高可用:任一机房宕机,不影响外网用户登录。
  • 统一的联邦服务名(FSN):fs.example.com,无感知切换。
  • WAF/防火墙合规与证书链可达(CRL/OCSP):避免因为吊销检查导致的间歇性超时。
  • 尽量保持简单:Windows Internal Database(WID)起步,如后续并发上来再平滑切到 SQL Always On。

拓扑总览(实网等效示意)

拓扑:
               ┌──────────┐
   Internet  ─▶│  GSLB    │──┐  Geo/Latency routing + HC
               └──────────┘  │
                              │
                 ┌────────────▼────────────┐
                 │                          │
          ┌──────▼──────┐            ┌──────▼──────┐
          │   HK-SiteA  │            │   HK-SiteB  │
          │  (MEGA-i)   │            │  (CAMPUS)   │
          └──────┬──────┘            └──────┬──────┘
                 │                          │
        ┌────────▼────────┐        ┌────────▼────────┐
        │  WAP-A(Proxy)   │        │  WAP-B(Proxy)   │  外网443
        └────────┬────────┘        └────────┬────────┘
                 │  443/49443                │  443/49443
        ┌────────▼────────┐        ┌────────▼────────┐
        │  ADFS-A (Farm)  │◀──────▶│  ADFS-B (Farm)  │  内部443
        └────────┬────────┘        └────────┬────────┘
                 │                          │
           ┌─────▼─────┐              ┌─────▼─────┐
           │ Domain DC │              │ Domain DC │  88/389/636/3268...
           └───────────┘              └───────────┘

硬件与网络参数(我现场用的这一套)

角色 机型/规格(示例) OS/版本 NIC 存储/RAID
ADFS-A/B 1U 双路(Xeon Silver/Gold 级别),32~64GB RAM Windows Server 2022 Std 2×10GbE RAID1 SSD 480GB
WAP-A/B 1U 单路,16~32GB RAM Windows Server 2022 Std 2×10GbE RAID1 SSD 240GB
DC/GC 既有(双机房各 2 台,GC 开启) Windows Server 2019/2022 10GbE RAID1 SSD
负载均衡 各站点 F5/HAProxy(L4),对外 WAF 一层 - - -
线路 双上联(CMI + CTG/CU),GSLB 走 Anycast/Geo 智能解析 - - -

网络与防火墙(必开端口汇总):

源 → 目标 端口 说明
客户端 → WAP 443 外网 TLS 入口
WAP → ADFS 443, 49443 443 为 ADFS 服务,49443 为 ADFS Proxy Trust 通道
ADFS → DC/GC/DNS 88, 389/636, 3268/3269, 53 Kerberos/LDAP/GC/DNS
ADFS → SQL(可选) 1433 如后续迁移到 SQL Farm
WAP/ADFS → CRL/OCSP 80/443 证书链与吊销检查(务必保证在内地可达)
管理/监控 5985/5986, 22/443 WinRM/Prometheus-Exporter/Agent 等(按需)

证书与命名方案(避坑重点)

用途 证书类型 主题/主机名 颁发建议
联邦服务 SSL 公网 DV/OV 通配/SAN fs.example.com 公有 CA(在内地有加速的,如 DigiCert/GlobalSign)
WAP 入口(同上) 同联邦服务 fs.example.com 同上
ADFS 服务通信 内网企业 CA 或同上 fs.example.com 统一
Token 签名/解密 内部证书(非对外) - ADFS 自动生成/导入

要点:证书链的 CRL/OCSP 访问在内地是否通畅,直接影响 1~2 秒登录抖动。我踩过坑:某公有 CA 的 CRL 域名在部分运营商偶发超时,最后换到在内地有 CDN 的 CA 才平稳。

先决条件与准备动作

  1. 域功能级别:Windows Server 2016+(我环境为 2019);
  2. 时间同步:ADFS/WAP 与 DC 时钟偏差 < 5 分钟(最好 < 30 秒);
  3. DNS:内外网均能解析 fs.example.com(内网指向各站点 ADFS VIP,外网指向 WAP GSLB);
  4. gMSA:为 ADFS 准备托管服务账号(推荐);
  5. 站点内负载均衡:L4 直透 443,不做 TLS 解密,保持会话(source IP 或 cookie),健康检查指向 /adfs/ls/idpinitiatedsignon.htm。

实操步骤(完整脚本与命令)

1)创建 KDS 根密钥与 gMSA(在任意一台管理机/域控)

# 创建 KDS 根密钥(生产建议提前10小时生效,这里演示立即生效)
Add-KdsRootKey -EffectiveImmediately

# 创建 gMSA
New-ADServiceAccount -Name gmsaAdfs `
  -DNSHostName fs.example.com `
  -PrincipalsAllowedToRetrieveManagedPassword "DOMAIN\ADFS-Servers"

# 给 ADFS 服务器加入到 ADFS-Servers 组(预建)
# 并在每台 ADFS 节点上安装并绑定 gMSA
Install-ADServiceAccount gmsaAdfs
Test-ADServiceAccount gmsaAdfs

2)在站点 A 安装 ADFS 并创建 Farm(Windows Server 2022)

Install-WindowsFeature ADFS-Federation -IncludeManagementTools

# 导入 SSL 证书到 LocalMachine\My,记下指纹
$thumb = "‎‎<证书指纹去空格>"

# 创建 ADFS Farm(WID 起步)
$cred = Get-Credential "DOMAIN\gmsaAdfs$"   # gMSA 选择“服务账户”形式,无密码
Install-AdfsFarm `
  -CertificateThumbprint $thumb `
  -FederationServiceName "fs.example.com" `
  -FederationServiceDisplayName "Example Federation" `
  -ServiceAccountCredential $cred `
  -OverwriteConfiguration

说明:Windows Server 2022 上的 ADFS 实际“农场行为级别(FBL)”仍为 2019 代系,功能如“Extranet Smart Lockout”等都可用。

3)将站点 B 加入同一 Farm

在站点 B 的 ADFS 节点:

Install-WindowsFeature ADFS-Federation -IncludeManagementTools
$thumb = "<同一张 fs.example.com 证书指纹>"
Add-AdfsFarmNode -CertificateThumbprint $thumb -PrimaryComputerName "adfs-a.domain.local"

WID 复制:Farm 中仅一台为 Primary(可切换),配置通过 WID 自动复制到其他节点。并发量上来可考虑迁移到 SQL AO。

4)启用 IdP 测试页与安全策略

# 便于健康检查与人工测试
Set-AdfsProperties -EnableIdPInitiatedSignonPage $true

# 启用 Extranet Smart Lockout(强烈建议)
Set-AdfsProperties -ExtranetLockoutThreshold 10 `
  -ExtranetObservationWindow (New-TimeSpan -Minutes 30) `
  -ExtranetLockoutEnforcementEnabled $true

5)部署各站点 WAP(Web Application Proxy)

在每个站点的 WAP 节点:

Install-WindowsFeature Web-Application-Proxy -IncludeManagementTools

# 导入对外 SSL 证书(同 fs.example.com),记录指纹
$thumb = "<证书指纹>"

# 与内部 ADFS 建立 Proxy Trust(注意 443/49443 通)
$cred = Get-Credential "DOMAIN\gmsaAdfs$"
Install-WebApplicationProxy `
  -FederationServiceName "fs.example.com" `
  -CertificateThumbprint $thumb `
  -FederationServiceTrustCredential $cred

防火墙要点:务必放行 WAP→ADFS 的 TCP 49443,否则你会在事件日志里看见 Proxy Trust 定期续约失败(Event ID 276/364),导致偶发 503/502。

6)站点内 LB 与 GSLB 配置(我采用的策略)

站点内 LB(L4 直透):

  • ADFS 内部 VIP:adfs-int-a.example.com(A 站),adfs-int-b.example.com(B 站)
  • 健康检查:https://<VIP>/adfs/ls/idpinitiatedsignon.htm 返回 200/OK
  • 持久性:Source-IP 粘性 15 分钟

GSLB(外网 WAP VIP):

  • 记录:fs.example.com → GSLB 智能解析,健康监测各站点 WAP VIP
  • 策略:优先按地理/时延分配(内地→站点 A/B 就近),站点失效自动摘除

TTL:30~60 秒(足够快的切换,兼顾 DNS 负载)

7)DNS 内外分离解析

记录名 内网解析 外网解析
fs.example.com 指向各站点 ADFS 内部 VIP(可 GSLB) 指向 WAP 外部 VIP(GSLB 智能)
adfs-a/b.domain 各自主机 A 记录 -

为什么内外要区分? 内网客户端应直接打到内部 ADFS VIP(少一跳),外网才走 WAP。否则内部也绕 DMZ,会徒增时延与失败面。

8)添加(或迁移)Relying Party Trust(以 O365/SAML 应用为例)

# 示例:添加基于元数据的 RPT(SAML)
Add-AdfsRelyingPartyTrust `
  -Name "HR-Portal" `
  -MetadataUrl "https://hr.example.com/saml/metadata" `
  -SigningCertificateRevocationCheck None    # 若对端证书链不稳定可临时降低

典型声明规则(示例):将 userPrincipalName 与 mail 同步给对端

c:[Type == "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/upn"]
 => issue(Type = "upn", Value = c.Value);

c:[Type == "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress"]
 => issue(Type = "email", Value = c.Value);

健康检查与性能验证

我在两地各拉了一个跳板机,跑如下脚本:

# 基础连通
Test-NetConnection fs.example.com -Port 443

# 探测 IdP 页面
(Measure-Command { Invoke-WebRequest https://fs.example.com/adfs/ls/idpinitiatedsignon.htm -UseBasicParsing }).TotalMilliseconds

# ADFS Farm 与 Proxy 状态
Get-AdfsSyncProperties
Get-WebApplicationProxyConfiguration
Get-AdfsProperties | fl ExtranetLockout*,EnableIdPInitiatedSignonPage

上线前后的对比(实测 50 样本平均)

地点 优化前(单站点)平均首包 RTT 优化后(多站点+GSLB)平均首包 RTT 端到端登录(SAML/OIDC)P95
深圳 210~280 ms 90~120 ms 1.3 s → 0.7 s
广州 230~320 ms 100~130 ms 1.5 s → 0.8 s
上海 260~340 ms 120~150 ms 1.7 s → 0.9 s

运营级别的“坑点”与解决手记

CRL/OCSP 可达性
WAP/ADFS 对外证书链每次握手做在线吊销检查,个别公有 CA 的 CRL 在内地随机超时。解法:换到在内地有 CDN 加速的 CA;或在 Proxy 上启用更长的 TLS 会话复用,尽量减少完全握手次数(LB/WAF 配合)。

49443 未放行
你会在 WAP 上见到 Proxy Trust 周期性失败(Event 276),用户随机 503。解法:对等防火墙双向放行 WAP→ADFS 的 49443。

时间漂移
Kerberos/Token 验证对时钟很敏感,漂个 3~5 分钟就会有奇怪报错。解法:所有节点与 DC/NTP 对齐,VMware/Hyper-V 不要双重授时。

WAF 强制 TLS 降级/重写报头
有的 WAF 误判会插手 SNI/Headers,造成 ADFS MSIS7065。解法:对 fs.example.com 放行透明模式,不做 HTTP 修改。

DNS TTL 太长
站点故障时切不下来。解法:GSLB 记录 TTL 控制在 30~60 秒,并对运营商递归器做健康度监测。

WID 的边界
当 RPT/Claims 规则很多、并发大(>10k auth/min),WID 同步/主节点压力会显著上升。解法:提前预案 SQL AO,迁移步骤见下。

IdP 页面默认关闭
2019+/2022 默认关闭 IdP Initiated SignOn,健康检查容易 404。解法:临时开启,仅用作 HC 与故障排查,上线后可只允许专用探测源 IP 访问。

从 WID 平滑迁移到 SQL(预案摘要)

新建 SQL Server Always On(双站点各一节点,监听器跨站点)。

在现有 Farm 上执行:

Set-AdfsProperties -ConfigurationDatabaseConnectionString "Data Source=<SQLListener>;Initial Catalog=AdfsConfiguration;Integrated Security=True"

验证各节点切换到 SQL,确认 RPT/Claims 全量在库。

将 Primary 切至另一站点演练故障切换。

安全与合规模块(我上线时做的最小集)

  1. 密码喷洒与暴力破解:开启 ESL(上文已给命令),并在 WAF 层做速率限制。
  2. TLS 配置:仅启用 TLS 1.2/1.3(如环境允许),禁用弱套件;可用 IISCrypto 或 GPO 统一。
  3. 审计:开启 ADFS 审计日志,与 SIEM(如 Splunk/ELK)对接;关键事件:1200/1202/411。
  4. 最小暴露面:仅开放 /adfs/ls/、OIDC/OAuth 必需端点;阻断不必要旧协议(WS-Trust 2005/13 如无需可禁)。
  5. 备份:导出 Token 签名/解密证书与私钥,妥善保管;Farm 配置用 Export-AdfsDeploymentSQLScript 或 Get-AdfsProperties 备档。

变更与回滚计划(我实际执行的顺序)

  • 预先布好 ADFS/WAP 与站点内 VIP,保持“黑暗发布”状态。
  • GSLB 新建 fs-gray.example.com,让小流量真实用户灰度。
  • 观测 24 小时:失败率、RTT、ESL 告警。
  • 切主:将 fs.example.com 的流量策略从单站点改为多站点。
  • 如异常:一键将某站从 GSLB 摘除(不需回滚服务器配置)。

常用排错清单(现场速查)

现象 日志/工具 可能原因 快速动作
偶发 502/503 WAP:Microsoft-Windows-Web Application Proxy/Admin 49443 被阻断/证书无效 放行 49443/检查 Proxy Trust 证书期与链
登录卡在 3~5 秒 ADFS Admin/Security CRL/OCSP 超时 换 CA/优化吊销检查可达性
某地用户全体超时 GSLB/监控 智能解析误判/线路故障 临时固定解析到另一站点
MSIS7065/MSIS3173 ADFS Admin WAF 改写/协议未启用 关闭改写/只启用必要协议
Event 276(Proxy Trust) WAP Admin 时间漂移/49443 问题 校时/放行端口/重建 Trust

关键配置清单(摘录)

# 绑定/替换 SSL 证书(ADFS)
Set-AdfsSslCertificate -Thumbprint "<thumb>"

# Token 生命周期(按业务调优)
Set-AdfsProperties -PersistentSsoLifetimeMins 4320 -SsoLifetime 480

# OIDC/OAuth 客户端(示例)
Add-AdfsClient -Name "intranet-app" -ClientId "intra-oidc" -RedirectUri "https://intra.example.com/auth/callback" -ClientType Public

# 导出 ADFS 配置(审计/备份)
Get-AdfsProperties | fl * > C:\adfs-props.txt
Get-AdfsRelyingPartyTrust | Select Name,Identifier > C:\adfs-rpt.txt

成本与容量评估(我用过的一套粗估)

数量 单价(示意) 月成本(示意) 备注
物理/云主机(4 台) 4 $$ $$ 两站点各 ADFS+WAP
负载均衡/WAF 2 $$ $$ 站点内 LB + 外层 WAF
证书(OV 2/3 年) 1 $$ - fs.example.com
GSLB 服务 1 $$ $$ 健康检查+就近/时延策略
运维监控 - - $$ 日志/告警/拨测

容量方面:以 P95 1 秒内完成登录为目标,ADFS 单节点能轻松顶 2k~3k TPS 的 token 请求(视声明规则复杂度而定)。双站点四节点足够大多数企业规模,后续可横向扩。

灰度、压测与上线(我的节奏)

  1. 灰度:10% 员工解析到 fs-gray.example.com,对比登录成功率与时延曲线。
  2. 压测:用内部工具模拟 OIDC/SAML 流程,打到 1.5× 生产峰值,观测 ADFS CPU/Queue Length、WAP ActiveConnections。
  3. 上线:变更窗口内切换 GSLB 主记录,观察 2 小时。
  4. 复盘:第二天把 ESL 告警峰值、WAF 速率限制命中、DNS 递归器缓存命中率拉出来开会复盘。
  5. 尾声:清晨 5:10 的机房门口

切到多站点 GSLB 的那刻,我盯着看板上三条最敏感的时延曲线:深圳、广州、上海。它们从 200 多毫秒一路滑到 100 左右,偶发的红点也消失了。机房门口的灯带还在冷冷地亮着,保安大叔路过冲我点头。我回了个“OK”的手势,把那杯已经凉透的咖啡扔进垃圾桶。
对多数人来说,登录能快 0.5 秒并不值得兴奋;但对我来说,那 0.5 秒背后,是一套经得住跨境网络波动、机房故障、证书链抽风的体系。多站点 ADFS 并不神秘,关键在于尊重“链路可达性、证书可验证、DNS 可控、状态可观测”这四个字。

如果你也正被跨境登录折磨,不妨照着我的这套实践打个样,再按你们的规模和预算做加减。我更愿意把它视作一个“最小正确闭环”,从这里开始,去迭代出真正适合你们业务的联邦认证基座。

目录结构
全文