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 一樣無腦直連

搞定收工