從 Windows 2000 開始,使用 AD 已超過 20 年,但我對其運作仍屬一知半解。

今天來探索一個我很想知道答案的冷門問題:若企業 AD Forest 包含多個子網域,Domain A 有台 IIS 伺服器 Web X,使用者使用另一個子網域 Domain B 的 AD 帳號登入,網站伺服器 X 要驗證 Domain B 帳號密碼需要連線 Domain B 的 DC 嗎?若是,會用到哪些協定?防火牆需開放哪些 Port?

有三種可能:

  1. Web X 透過 Domain A DC 間接完成驗證
  2. Web X 自行連上 Domain B DC 進行驗證
  3. 客戶端與 Domain B DC 完成驗證

依直覺猜測,第二種做法讓網路傳輸關聯複雜化,設計系統時應會避免,而第三種應該就是 Kerboros 的做法。但猜測終究是猜測,架個 VM 環境實測觀察是追尋真相最好的方法。

對門外漢來說,從無到有架出 AD Forest 肯定是件大工程,花時間是一回事,不知從何下手才是大問題。我決定抄捷徑,找到在 Azure 建兩台 VM 架設出網域跟子網域的自動化範本:Create new Active Directory forest with optional subdomain,大約半小時可以建好兩台 VM 組出 contoso.com 及 child.contoson.com 的子網域架構,實踐了傻瓜式 AD 環境架設。
(一台能跑 DC 的 Windows VM,每小時成本頂多新台幣十幾塊,測完刪除資源即不再計費,若有 MSDN 訂閱,每個月 150 USD 額度可以應付)

自動化範本會建出兩個網域及兩台 DC:

網域:contoso.com / DC:rootdc1 (10.0.0.4)
 └─子網域:child.contoso.com / DC: childc3 (10.0.0.6)

為了讓情境更擬真,我又加了三台 VM,多建立一個子網域 web.contoso.com,其下包含一台 DC、一台 Web 及一個 Client。

網域:contoso.com / DC:rootdc1 (10.0.0.4)
 ├─子網域:child.contoso.com / DC: childc3 (10.0.0.6)
 └─子網域:web.contoso.com / DC: webdc5 (10.0.0.7)
   ├─網站:testweb.web.contoso.com (10.0.0.5)
   └─客戶端:client.web.contoso.com (10.0.0.8)
   

網域從屬關係:

我使用的測試方式是從 client 10.0.0.8 開啟 Chrome 連上 testweb 10.0.0.5 80 Port,網頁與客戶端都屬於 web.contoso.com 網域,但使用 child.contoso.com 的 AD 帳號登入。IIS 整合式 Windows 驗證有 NTLM 及 Kerberos 兩種方式,二者驗證程序與涉及對象截然不同,故我用 http://10.0.0.5http://testweb.web.contoso.com 兩種不同 URL 各測一次,預期前者會走 NTLM,後者會走 Kerberos。(延伸閱讀:關於 IIS 整合式 Windows 驗證的冷知識Windows 驗證歷程觀察與 Kerberos/NTLM 判別)

下圖是使用 IP 連網站走 NTLM 登入時 IIS 主機的傳輸狀況:(註:使用 Wireshark 抓封包,套用 dst net 10.0.0 and src net 10.0.0 and not port 80 篩選器鎖定 Intranet 傳輸)

  我共試了兩次,第一次故意輸入錯誤密碼,第二次才成功登入。從封包可以看到兩次 RPC_NETLOGON 往返,對象是 web.contoso.com 的 DC 10.0.0.7,使用的 API 是 NetrLogonSamLogonEx。由此推測,IIS 是透過自己 web 子網域 DC (10.0.0.7) 驗證 child 子網域的帳號密碼,本身不需要跟 child DC (10.0.0.6) 打交道。

改使用 http://testweb.web.contoso.com 連上 IIS 時符合啟用 Kerberos 的條件,Client 端會跟 child KDC (10.0.0.6) 交涉取得 Service Ticket 交給 IIS,Service Ticket 真偽靠金鑰及加密機制識別,理論上不需送給 DC 驗證:


圖片來源

整個登入過程 IIS (10.0.0.5) 的封包記錄異常乾淨,只有 HTTP 傳輸:

Client (10.0.0.8) 的封包比較精彩,將範圍限縮到 TCP Port 88 Kerberos 協定傳輸可看到 Client 對 child 網域 DC (10.0.0.6) 發出 AS 及 TGS 請求,接著對根網域 DC (10.0.0.4)、web 網域 DC (10.0.0.7) 也有發出 TGS 請求:

根網域 DC (10.0.0.4) 與 web 網域 DC (10.0.0.7) 回覆的 TGS-REP 長這樣,應是為了準備產生 AP-REQ 需要的資訊:

最後從 HTTP 傳輸中可觀察到 AP-REQ 及 AP-REP,印證上面的 Kerberos 驗證流程:

結論:

回答最早的問題「當使用其他子網域帳號登入 IIS 時,IIS 需要跟該子網域的主機直接溝通嗎?」

由實驗結果得到的答案是 - 不用。

IIS 預設使用 Negotiate 提供者,將與瀏覽器協商,先嘗試使用 Kerberos,若條件不符則改用 NTLM。使用 Kerberos 時,由客戶端連絡其他子網域 DC 及自家子網域 DC 取得 Service,過程 IIS 完全不需接觸 DC;若走 NTLM,IIS 會使用 Netlogon RPC 連上 自家 DC* 呼叫 NetrLogonSamLogonEx 驗證其他網域的帳號,過程也不會接觸其他子網域的 DC。

同場加映:這次的測試環境多個網域位於同一網段 (10.0.0.0/24),若客戶端因路由或防火牆無法接觸其他子網域 DC,結果會有不同嗎?會,依理論推測,瀏覽器會因無法使用 Kerberos 將改走 NTLM。我在防火牆設定阻擋對 Child DC 10.0.0.6 88 Port 傳輸來驗證這點:

得證。

最後附上這次新學會的 Azure CLI 好用技巧,用指令一次關閉資源群組所有 VM,比起在網頁介面點來點去方便多了:
Azure 小常識:關閉 VM 有 stop 及 deallocate 兩種選擇,前者仍會佔用硬體資源會繼續依小時計費,後者才會真正停止計費(但磁碟空間要繼續計算)但公用 IP 也會歸還,下次啟動時 IP 會換。若測好確定不再需要了,可將整個資源群組刪除,VM、IP、磁碟殺光,避免白白浪費錢。

az vm deallocate --ids $(az vm list --resource-group ad-forest-lab --query "[].id" -o tsv)


Comments

# by djlin

Typo “ 接著對根網域 DC (10.0.0.4)、web 網域 DC (10.0.0.57) 也有發出 TGS 請求” 10.0.0.57 -> 10.0.0.7

# by Jeffrey

to djlin, 謝謝,已修正。

Post a comment