802.1x 無線網路驗證 FreeRADIUS + Microsoft AD

首先感恩廖大讚歎廖大(chianan_liao)的分享私人筆記才有這篇騙吃騙喝的文章

現況

  • 有一台該死的Microsoft AD(Active Directory) 但我離不開她 (其實用 samba 的偽 AD 也可以)
  • 我的無線網路環境有一堆人要用,不想用單一密碼,我需要 802.1x 做無線網路驗證,讓每個人打自己的 AD 帳密連線

架構

本文就是要弄出右上角這台 RADIUS Server

  • 網域: alexw.net
  • 網域簡寫: ALEXW
  • 原本就有的 AD Server
    host: ads.alexw.net
    ip: 192.168.1.2
  • 本文要建立的 FreeRADIUS + samba
    host: rad.alexw.net
    ip: 192.168.1.3

本文測試環境: Debian 10 / Windows Server 2019

前置作業

裝好一台 debian,設置 hosts 對應

debian 會設置 127.0.1.1 對應本機,這個註解掉改為 host ip

/etc/hosts

127.0.0.1   localhost
# 127.0.1.1 rad.alexw.net  rad
192.168.1.3    rad.alexw.net rad

設置 DNS server (設為兼任 dns 的 AD server)

/etc/resolv.conf

nameserver 192.168.1.2
domain alexw.net
search alexw.net

安裝套件

安裝 freeradius 和 samba 等相關套件

apt install freeradius samba-common winbind krb5-config libpam-winbind libnss-winbind -y

設定 krb5

/etc/krb5.conf

[libdefaults]
    dns_lookup_realm = false
    dns_lookup_kdc = true
    default_realm = ALEXW.NET
    
[realms]
    ALEXW.NET = {
        kdc = ads.alexw.net
        admin_server = ads.alexw.net
    }
​
[domain_realm]
    .alexw.net = ALEXW.NET
     alexw.net = ALEXW.NET

設定 samba

/etc/samba/smb.conf

* * security = ADS  這行的 ADS 不是 ads.alexw.net 的 server name 而是真的要打 “ADS” * *

[global]
    security = ADS
    workgroup = ALEXW
    ntlm auth = Yes
    realm = ALEXW.NET
    client NTLMv2 auth = YES
    log file = /var/log/samba/log.%m
    max log size = 1000
    logging = file
    log level = 1
    password server = ads.alexw.net
    winbind use default domain = true
    winbind offline logon = false
    template homedir = /home/%U
    template shell = /bin/bash
    idmap config * : backend = tdb
    idmap config * : range = 10000-20000

/etc/nsswitch.conf

passwd:         compat winbind
group:            compat winbind
shadow:         compat winbind
gshadow:       files
​
hosts:             files dns
networks:      files
​
protocols:      db files
services:         db files
ethers:            db files
rpc:                  db files
​
netgroup:       nis

加入網域

把這台機器加入 AD 網域,當個快樂的 AD 成員(使用 administrator 帳號,理論上非 administrator 也可以)

net ads join -U administrator

下面這錯誤可以忽略 這是動態更新 DNS 失敗(因為伺服器都設置 static dns 不做 dynamic)

Enter administrator's password:
Using short domain name -- ALEXW
Joined 'RAD' to dns domain 'alexw.net'
DNS Update for rad.alexw.net failed: ERROR_DNS_UPDATE_FAILED
DNS update failed: NT_STATUS_UNSUCCESSFUL

備註:以後如果機器撤掉要退網域則使用 (現在不要打這行指令啦啊啊啊)

## 這是退網域用的指令
## net ads leave -U administrator

重啟 winbind

systemctl restart winbind

測試是否能讀取 AD 使用者和群組的資料

wbinfo -u
wbinfo -g

測試帳號登入

ntlm_auth --username={AD_USER_ACCOUNT} --password={AD_USER_PASSWORD}

正確會出現

NT_STATUS_OK: The operation completed successfully. (0x0)

設定FreeRADIUS

把 freerad 帳號加入 winbindd_priv 群組

usermod -a -G winbindd_priv freerad

重啟 winbind

systemctl restart winbind

編輯 FreeRADIUS 設定

/etc/freeradius/3.0/radiusd.conf 不需更改

編輯用戶端設定

/etc/freeradius/3.0/clients.conf

secret 後面接的是 radius 用的 secret key (自行設定)

client localhost {
    ipaddr = 127.0.0.1
    secret   = AAA@AAA
}
​
client localhost_ipv6 {
    ipv6addr = ::1
    secret   = AAA@AAA
}
​
client private-network-1 {
    ipaddr  = 192.168.0.0/16
    secret  = AAA@AAA
}

修改 mschap

/etc/freeradius/3.0/mods-available/mschap

mschap {
    use_mppe=yes
    require_encryption = yes
    require_strong = yes
    with_ntdomain_hack = yes
​
    winbind_username = "%{mschap:User-Name}"
    winbind_domain = "ALEXW"
​
   ntlm_auth = "/usr/bin/ntlm_auth --allow-mschapv2 --request-nt-key --username=%{%{Stripped-User-Name}:-%{%{User-Name}:-None}} --challenge=%{%{mschap:Challenge}:-00} --nt-response=%{%{mschap:NT-Response}:-00}"
​
}

修改 /etc/freeradius/3.0/mods-available/ntlm_auth

修改裡面的 path 和 domain

exec ntlm_auth {
    wait = yes
    program = "/usr/bin/ntlm_auth --request-nt-key --domain=ALEXW --username=%{mschap:User-Name} --password=%{User-Password}"
}

測試 RADIUS 連線

停止服務並改用啟動偵錯模式 freeradius -X

systemctl stop freeradius
freeradius -X

然後用另一個 console 測試連線測試本地端 (本地開 18120,如果是遠端則是 1812)

radtest -t mschap {USER} "{USER_PASSWORD}" localhost:18120 0 AAA@AAA

成功會得到這樣的訊息

Sent Access-Request Id 12 from 0.0.0.0:57999 to 127.0.0.1:18120 length 132
        User-Name = "{USER}"
        MS-CHAP-Password = "{USER_PASSWORD}"
        NAS-IP-Address = 127.0.0.1
        NAS-Port = 0
        Message-Authenticator = 0x00
        Cleartext-Password = "{USER_PASSWORD}"
        MS-CHAP-Challenge = 0x3a6a904d7a1c7d7b
        MS-CHAP-Response = 0x0001000000000000000000000000000000000000000000000000beb439542bf97174619a4b7a7360141633ba32b8719a5de4
Received Access-Accept Id 12 from 127.0.0.1:18120 to 127.0.0.1:57999 length 84
        MS-CHAP-MPPE-Keys = 0x00000000000000004e09b29052bcb917ed0d2bc195ce801a
        MS-MPPE-Encryption-Policy = Encryption-Required
        MS-MPPE-Encryption-Types = 4

測試成功後 ctrl-c 終止 freeradius -X 程序 啟用並設置每次開機啟動服務

systemctl start freeradius
systemctl enable freeradius

現在連線可以使用

Android 手機連線時選擇 PEAP / MSCHAPV2 / 不驗證

iOS 則無腦直連

以上就可以算完工了,不過對於 android 11 更新後會發現不能選不驗證,他一定要做驗證才能連線

只好繼續往下做

加入憑證

這邊我們採用免費的 Let’s encrypt 的憑證來使用

let’s encrypt 是發行憑證的單位,但是我們會用第三方套件去申請和更新憑證,本文採用 certbot 這個套件來處理

記得以前都是用 apt 直裝,不過這次發現 certbot 是建議使用 snap 套件管理來安裝,那就來試試看)

那就先來安裝 snap

apt install snapd -y

安裝完之後要更新 snap core

snap install core
snap refresh core

使用 snap 安裝 certbot

snap install --classic certbot

取得憑證 (需公用 ip 正反解 + 80 port 防火牆暢通)

/snap/bin/certbot certonly --standalone

完成後金鑰會存放在 /etc/letsencrypt/live/{your_domain}

測試自動更新

/snap/bin/certbot renew --dry-run

在 free radius 目錄內建立 let’s encrypt 資料夾 將金鑰檔案複製過去並設置權限

mkdir -p /etc/freeradius/3.0/certs/letsencrypt
cp /etc/letsencrypt/live/rad.alexw.net/privkey.pem /etc/freeradius/3.0/certs/letsencrypt
cp /etc/letsencrypt/live/rad.alexw.net/fullchain.pem /etc/freeradius/3.0/certs/letsencrypt
chown freerad:freerad -R /etc/freeradius/3.0/certs/letsencrypt

修改 /etc/freeradius/3.0/mods-enabled/eap

# private_key_file = /etc/ssl/private/ssl-cert-snakeoil.key 
private_key_file = /etc/freeradius/3.0/certs/letsencrypt/privkey.pem

# certificate_file = /etc/ssl/certs/ssl-cert-snakeoil.pem
certificate_file = /etc/freeradius/3.0/certs/letsencrypt/fullchain.pem

重啟 freeradius

systemctl restart freeradius

android 手機連線的時候,驗證部分選使用系統憑證應該就可以通了

iOS 一樣無腦直連

搞定收工

AD GPO 隱藏討人厭的搜尋方塊和沒用的摳他那(cortana)

Windows 搜尋功能自古以來按下 win-key 直接打字就有,不過麻瓜們都不知道,所以在最近這幾版的 Windows 10 改成預設就是個大搜尋框,超礙事的,直接拿掉怕麻瓜們不方便,留個放大鏡圖示好了,至於 Cortana 就根本是沒用的東西,直接拿掉。

老樣子,打開 gpmc.msc 編輯你要改的 GPO 找到 使用者設定 / 喜好設定 / Windows 設定 / 登錄
記得這邊不要用”電腦設定”,要用”使用者設定

新增登錄
搜尋方塊是
HKEY_CURRENT_USER\SOFTWARE\Microsoft\Windows\CurrentVersion\Search\SearchboxTaskbarMode
值設定為 1 ( 0隱藏 / 1圖示 / 2 整個方框 )

Cortana 圖示是
HKEY_CURRENT_USER\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Advanced\ShowCortanaButton
設定值為 0 ( 0隱藏 / 1 顯示)

這樣乾淨多了(菸~

在 AD 環境中 Firefox 的群組原則管理

要在 AD 環境中透過群組原則 (GPO) 來管理 Firefox 可以從 https://github.com/mozilla/policy-templates/releases 下載群組原則樣板 (ex: policy_templates_v2.4.zip)。

解開來裡面找到兩個檔案

  • nwindows\firefox.admx 這是群組原則樣板檔
  • windows\zh-TW\firefox.adml 這是樣板的中文語言檔

admx 檔和之前做 chorme / new edge 的 adm 檔操作方式不同,
admx 檔是放在本機,然後操作群組原則的時候會自動抓來用,但是你換到沒有admx 的電腦操作就看不到哩,但是群組原則效果還在。
adm 檔則是掛進去到處都看得到。

firefox.admx 複製到本機(要執行 gpmc.msc 的機器)的 C:\Windows\PolicyDefinitions\ 底下(此範例以 windows 10 為例,如果是 windows 版本較舊,像是 windows7, server 2012 則資料夾可能不同)

firefox.adml 語言檔則放在 C:\Windows\PolicyDefinitions\zh-TW\ 底下。
以上兩個檔案 (addx/adml) 放好了就完成環境準備了,這時候執行 gpmc.msc 開或是新開你要的群組原則來編輯,就可以看到在系統管理範本內有 firefox 的選項了,admx 檔案會直接出現,不會像 adm 檔出現在傳統系統管理範本的分類。

自動安裝擴充元件

本範例以 uBlock Origin 為例,先到擴充套件的官網,找到網址的識別名稱,這邊是 “ublock-origin” 先把這字串記起來,下個步驟會用到。

打開群組原則編輯器,找到 電腦設定\原則\系統管理範本\Firefox\擴充套件\要安裝的擴充套件 啟用他,然後按下 “顯示…” 的按鈕,編輯要安裝的檔案來源列表。

注意! 這邊的值要直接可以找到要安裝的 xpi 檔,如果你去複製官網的按鈕連結,你會得到的是 https://addons.mozilla.org/firefox/downloads/file/3663488/ublock_origin-1.30.6-an+fx.xpi

這其實不 OK! 因為這路徑有限定版本,以後官網更新不就會裝到舊版….

所以這路徑不能直接複製官網按鈕,不過還好官網有作對應,我們只要用 https://addons.mozilla.org/firefox/downloads/latest/<擴充元件識別名稱>/latest.xpi 就可以直接對應到最新版本,範例如下:

擴充套件網頁 https://addons.mozilla.org/zh-TW/firefox/addon/ublock-origin/
擴充套件最新版本檔案 https://addons.mozilla.org/firefox/downloads/latest/ublock-origin/latest.xpi

我們就直接把下面這行貼到值裡面,按下確定就好了。

關閉新通知

打開群組原則編輯器,找到 電腦設定\原則\系統管理範本\Firefox\權限\通知\封鎖傳送通知的新請求 把它啟用就好了。

讀者可能會想,上面不是有個封鎖的網站,為何不打開來填個萬用字元* 就好了嗎?
這其實不行,以目前版本 2.4 版官網說明 有提到

> Because these are origins, not domains, entries with unique ports must be specified separately. This explicitly means that it is not possible to add wildcards.

所以是不能用萬用字元的。

範例就這兩個,其他就不贅述了,祝各位躺的愉快。

在 AD 環境中 Google Chrome 的管理

要在 AD 環境中使用群組原則管理 Chrome 要先到 官網 下載 Chrome 政策範本 policy_templates.zip

解壓縮後找到 windows\adm\zh-TW\chrome.adm

再打開 群組原則編輯器 (gpmc.msc) 到 系統管理範本 新增剛解壓縮出來的 chrome.adm 這個範本檔
就可以讓群組原則處理 Chrome 的設定

範例一 : 幫網域上的 Chrome 都裝上 uBlock Origin

這個擋廣告軟體 這超好用 也可以擋掉 youtube 剛開始播放時的廣告
首先先去 uBlock Origin 的官網 記住網址的後面代號

然後再群組原則管理裡面 對你想要改的組織設一個群組原則然後編輯它

找到 系統管理範本 / 傳統系統管理範本(ADM) / Google / Google Chrome / 擴充功能 / 設定強制安裝的應用程式和擴充功能清單

啟用它並由下方選項顯示編輯清單,裡面的值就打上上面複製來的 擴充功能 id (例如 cjpalhdlnbpafiamejdnhcphjbkeiagm )

按下確定後 所有符合此群組原則的電腦就會自動裝上你指定的擴充功能了

範例二 : 讓所有電腦的桌面通知都關閉

系統管理範本 / 傳統系統管理範本 / Google / Google Chrome / 內容設定 / 預設通知設定
把它停用

以後就不會莫名其妙亂跳通知了

批次清除 AD 上使用者的主資料夾

依序測試一下
然後最後一步驟批次下去

查詢 AD 上所有使用者 列出 cn / 登入帳號 / 說明 / 主資料夾

dsquery * domainroot -filter "(&(objectClass=person)(objectClass=user)(!objectClass=computer)(cn=*))" -attr cn sAMAccountName description homeDirectory -uco -limit 0 

測試是否能查到帳號: kerker

dsquery * domainroot -filter "(&(objectClass=person)(objectClass=user)(!objectClass=computer)(sAMAccountName=kerker))" -attr cn sAMAccountName description homeDirectory -uco -limit 0

測試清除 kerker 的主資料夾

dsquery * domainroot -filter "(&(objectClass=person)(objectClass=user)(!objectClass=computer)(sAMAccountName=kerker))"| DSMod user -hmdir "" -hmdrv ""

清除所有主資料夾含有 ftp 的帳號的主資料夾(真饒舌)

dsquery * domainroot -filter "(&(objectClass=person)(objectClass=user)(!objectClass=computer)(homedirectory=*ftp*))" -limit 0 | DSMod user -hmdir "" -hmdrv ""

搞定收工