📻

Raspberry Pi 4、Samba 4、FreeRADIUS、hostapdを使ってWPA2 Enterprise対応APを構築する

2022/06/21に公開

はじめに

Active Directory + WPA2 Enterpriseに関する検証が必要になったので、Raspberry Pi 4、Ubuntu 22.04 LTS、Samba 4、FreeRADIUS、hostapdを使って検証環境を構築しました。

結果から言えば、WPA2 Enterprise対応のアクセスポイントを構築でき、Android端末から接続、通信できました。

環境

ハードウェア的な環境は以下の通りです。

ソフトウェア的な環境は以下の通りです。

  • OS: Ubuntu 22.04 LTS
    • カーネル: 5.15.0-1011-raspi
  • Samba: 4.15.5
  • FreeRADIUS: 3.0.26
  • hostapd: 2.10
Ubuntuに関する詳細
$ cat /etc/os-release
PRETTY_NAME="Ubuntu 22.04 LTS"
NAME="Ubuntu"
VERSION_ID="22.04"
VERSION="22.04 LTS (Jammy Jellyfish)"
VERSION_CODENAME=jammy
ID=ubuntu
ID_LIKE=debian
HOME_URL="https://www.ubuntu.com/"
SUPPORT_URL="https://help.ubuntu.com/"
BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/"
PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy"
UBUNTU_CODENAME=jammy

$ uname -a
Linux dc1 5.15.0-1011-raspi #13-Ubuntu SMP PREEMPT Thu Jun 2 11:44:34 UTC 2022 aarch64 aarch64 aarch64 GNU/Linux
パッケージに関する詳細
$ dpkg -l | grep -i samba
ii  libwbclient0:arm64                2:4.15.5~dfsg-0ubuntu5                        arm64        Samba winbind client library
ii  python3-samba                     2:4.15.5~dfsg-0ubuntu5                        arm64        Python 3 bindings for Samba
ii  samba                             2:4.15.5~dfsg-0ubuntu5                        arm64        SMB/CIFS file, print, and login server for Unix
ii  samba-common                      2:4.15.5~dfsg-0ubuntu5                        all          common files used by both the Samba server and client
ii  samba-common-bin                  2:4.15.5~dfsg-0ubuntu5                        arm64        Samba common files used by both the server and the client
ii  samba-dsdb-modules:arm64          2:4.15.5~dfsg-0ubuntu5                        arm64        Samba Directory Services Database
ii  samba-libs:arm64                  2:4.15.5~dfsg-0ubuntu5                        arm64        Samba core libraries
ii  samba-vfs-modules:arm64           2:4.15.5~dfsg-0ubuntu5                        arm64        Samba Virtual FileSystem plugins

$ dpkg -l | grep -i freeradius
ii  freeradius                        3.0.26~dfsg~git20220223.1.00ed0241fa-0ubuntu3 arm64        high-performance and highly configurable RADIUS server
ii  freeradius-common                 3.0.26~dfsg~git20220223.1.00ed0241fa-0ubuntu3 all          FreeRADIUS common files
ii  freeradius-config                 3.0.26~dfsg~git20220223.1.00ed0241fa-0ubuntu3 arm64        FreeRADIUS default config files
ii  freeradius-krb5                   3.0.26~dfsg~git20220223.1.00ed0241fa-0ubuntu3 arm64        kerberos module for FreeRADIUS server
ii  freeradius-ldap                   3.0.26~dfsg~git20220223.1.00ed0241fa-0ubuntu3 arm64        LDAP module for FreeRADIUS server
ii  freeradius-utils                  3.0.26~dfsg~git20220223.1.00ed0241fa-0ubuntu3 arm64        FreeRADIUS client utilities
ii  libfreeradius3                    3.0.26~dfsg~git20220223.1.00ed0241fa-0ubuntu3 arm64        FreeRADIUS shared library

$ dpkg -l | grep -i hostapd
ii  hostapd                           2:2.10-6ubuntu1                               arm64        access point and authentication server for Wi-Fi and Ethernet

用語

WPA Enterprise、IEEE802.1X界隈はどうにも略語が分かりにくいので、以下に略語の一覧を載せておきます。

  • EAP: Extensive Authentication Protocol
  • EAPOL: EAP over LAN
  • MS-CHAP: Microsoft Challenge-Handshake Authentication Protocol
  • NAS: RADIUS界隈では Network Access Server
  • PAP: Password Authentication Protocol
  • PEAP: Protected Extensible Authentication Protocol
  • TLS: Transport Layer Security
  • WPA: Wi-Fi Protected Access

大まかな流れ

WPA2 Enterprise対応のアクセスポイントを構築し、動作確認を行う大まかな流れは以下の通りです。

  1. Sambaのセットアップ、ユーザの追加、動作確認
  2. FreeRADIUSのセットアップ、動作確認
  3. ブリッジの構成、hostapdのセットアップ
  4. Android端末による動作確認

Sambaのセットアップ

今回はユーザ認証をActive Directoryにて行うため、ドメインコントローラとしてSambaをセットアップします。

Sambaのインストール、ドメインコントローラの構築

Sambaのインストール、ドメインコントローラの構築までは、別に記事を書きましたのでそちらをご参照ください。

https://zenn.dev/yuyakato/articles/1186de8f2d675d

Sambaの設定変更

認証のためにSambaの設定を変更して、samba-ad-dcサービスを再起動します。

$ sudo cp /etc/samba/smb.conf /etc/samba/smb.conf.20220616
$ sudo vim /etc/samba/smb.conf
$ diff -u /etc/samba/smb.conf.20220616 /etc/samba/smb.conf
--- /etc/samba/smb.conf.20220616	2022-06-16 16:41:01.626125600 +0900
+++ /etc/samba/smb.conf	2022-06-16 16:42:07.109212715 +0900
@@ -6,6 +6,8 @@
 	server role = active directory domain controller
 	workgroup = AD
 	idmap_ldb:use rfc2307 = yes
+	ntlm auth = mschapv2-and-ntlmv2-only
+	log level = 0 auth_audit:2

 [sysvol]
 	path = /var/lib/samba/sysvol

$ sudo systemctl restart samba-ad-dc.service

ntlm authlog levelについての参考:

http://www.samba.gr.jp/project/translation/current/htmldocs/manpages/smb.conf.5.html

radiusユーザの作成

FreeRADIUSからSambaに接続し、認証するためのユーザradiusを作成します。パスワードはr@d1u$123としました。

$ sudo pdbedit --create radius
new password: r@d1u$123
retype new password: r@d1u$123
Unix username:        radius
...

$ sudo samba-tool user show radius
dn: CN=radius,CN=Users,DC=ad,DC=nayutaya,DC=jp
objectClass: top
objectClass: person
objectClass: organizationalPerson
objectClass: user
cn: radius
instanceType: 4
whenCreated: 20220616081135.0Z
uSNCreated: 4074
name: radius
objectGUID: af53d3b9-118a-449f-bdee-c41bfafd768b
badPwdCount: 0
codePage: 0
countryCode: 0
badPasswordTime: 0
lastLogoff: 0
lastLogon: 0
primaryGroupID: 513
objectSid: S-1-5-21-1197304507-3445897210-3495219254-1103
accountExpires: 9223372036854775807
logonCount: 0
sAMAccountName: radius
sAMAccountType: 805306368
objectCategory: CN=Person,CN=Schema,CN=Configuration,DC=ad,DC=nayutaya,DC=jp
userAccountControl: 512
whenChanged: 20220616081222.0Z
pwdLastSet: 132998407420000000
uSNChanged: 4076
distinguishedName: CN=radius,CN=Users,DC=ad,DC=nayutaya,DC=jp

winbindの動作確認

winbindが動作していることを確認します。

$ sudo wbinfo -t
checking the trust secret for domain AD via RPC calls succeeded

$ sudo wbinfo -u
AD\administrator
AD\guest
AD\krbtgt
AD\radius

FreeRADIUSのセットアップ

IEEE802.1Xによる認証を行うためにFreeRADIUSをセットアップします。

FreeRADIUSのインストール

Ubuntuパッケージを使ってFreeRADIUSをインストールします。
インストール後、自動的にfreeradiusサービスが起動しますが、動作確認のために停止しておきます。

$ sudo apt install freeradius freeradius-common freeradius-krb5 freeradius-ldap freeradius-utils
$ sudo systemctl stop freeradius.service
$ systemctl status freeradius.service
○ freeradius.service - FreeRADIUS multi-protocol policy server
     Loaded: loaded (/lib/systemd/system/freeradius.service; enabled; vendor preset: enabled)
     Active: inactive (dead) since Thu 2022-06-16 16:47:31 JST; 6s ago
       Docs: man:radiusd(8)
             man:radiusd.conf(5)
             http://wiki.freeradius.org/
             http://networkradius.com/doc/
    Process: 13917 ExecStart=/usr/sbin/freeradius -f $FREERADIUS_OPTIONS (code=exited, status=0/SUCCESS)
    Process: 13972 ExecReload=/usr/sbin/freeradius $FREERADIUS_OPTIONS -Cxm -lstdout (code=exited, status=0/SUCCESS)
    Process: 13973 ExecReload=/bin/kill -HUP $MAINPID (code=exited, status=0/SUCCESS)
   Main PID: 13917 (code=exited, status=0/SUCCESS)
     Status: "Processing requests"
        CPU: 3.031s
...

winbindにアクセスできるようにするため、freeradユーザをwinbindd_privグループに追加します。

$ sudo usermod -aG winbindd_priv freerad
$ id freerad
uid=114(freerad) gid=127(freerad) groups=127(freerad),42(shadow),125(winbindd_priv),126(ssl-cert)

FreeRADIUSをデバッグモードで起動できることを確認します。終了するにはCtrl+Cを押下します。

$ sudo freeradius -X
FreeRADIUS Version 3.0.26
Copyright (C) 1999-2021 The FreeRADIUS server project and contributors
...

FreeRADIUSの設定

FreeRADIUSの設定を変更します。見よう見まねなので、残念ながら詳しいことは分かりません。

LDAPサーバのアドレス、接続するためのDN、パスワードなどを設定します。ログインできるADユーザを制限する場合は、filterを変更します。
また今回、Samba側(LDAPSサーバ側)には妥当なサーバ証明書を設定していないため、require_cert = 'allow'としています。

$ sudo cp /etc/freeradius/3.0/mods-available/ldap /etc/freeradius/3.0/mods-available/ldap.20220616
$ sudo vim /etc/freeradius/3.0/mods-available/ldap
$ sudo ln -s ../mods-available/ldap /etc/freeradius/3.0/mods-enabled
$ sudo diff -u /etc/freeradius/3.0/mods-available/ldap.20220616 /etc/freeradius/3.0/mods-available/ldap
--- /etc/freeradius/3.0/mods-available/ldap.20220616	2022-06-16 17:05:50.138260949 +0900
+++ /etc/freeradius/3.0/mods-available/ldap	2022-06-16 17:33:47.596933738 +0900
@@ -16,7 +16,7 @@
 	#  - ldaps:// (LDAP over SSL)
 	#  - ldapi:// (LDAP over Unix socket)
 	#  - ldapc:// (Connectionless LDAP)
-	server = 'localhost'
+	server = 'ldaps://localhost'
 #	server = 'ldap.rrdns.example.org'
 #	server = 'ldap.rrdns.example.org'

@@ -25,12 +25,12 @@

 	#  Administrator account for searching and possibly modifying.
 	#  If using SASL + KRB5 these should be commented out.
-#	identity = 'cn=admin,dc=example,dc=org'
-#	password = mypass
+	identity = 'CN=radius,CN=Users,DC=ad,DC=nayutaya,DC=jp'
+	password = 'r@d1u$123'

 	#  Unless overridden in another section, the dn from which all
 	#  searches will start from.
-	base_dn = 'dc=example,dc=org'
+	base_dn = 'DC=ad,DC=nayutaya,DC=jp'

 	#
 	#  You can run the 'ldapsearch' command line tool using the
@@ -128,6 +128,7 @@
 	update {
 		control:Password-With-Header	+= 'userPassword'
 #		control:NT-Password		:= 'ntPassword'
+		reply:Reply-Message		:= 'distinguishedName'
 #		reply:Reply-Message		:= 'radiusReplyMessage'
 #		reply:Tunnel-Type		:= 'radiusTunnelType'
 #		reply:Tunnel-Medium-Type	:= 'radiusTunnelMediumType'
@@ -196,7 +197,7 @@
 		#  For Active Directory, you should use
 		#  "samaccountname=" instead of "uid="
 		#
-		filter = "(uid=%{%{Stripped-User-Name}:-%{User-Name}})"
+		filter = "(sAMAccountName=%{%{Stripped-User-Name}:-%{User-Name}})"

 		#  For Active Directory nested group, you should comment out the previous 'filter = ...'
 		#  and use the below. Where 'group' is the group you are querying for.
@@ -587,7 +588,7 @@
 		#  The default is libldap's default, which varies based
 		#  on the contents of ldap.conf.

-#		require_cert	= 'demand'
+		require_cert	= 'allow'

 		#
 		#  Minimum TLS version to accept. We STRONGLY recommend

認証方法などについて設定します。

$ sudo cp /etc/freeradius/3.0/sites-available/default /etc/freeradius/3.0/sites-available/default.20220616
$ sudo vim /etc/freeradius/3.0/sites-available/default
$ sudo diff -u /etc/freeradius/3.0/sites-available/default.20220616 /etc/freeradius/3.0/sites-available/default
--- /etc/freeradius/3.0/sites-available/default.20220616	2022-06-16 17:37:28.282088551 +0900
+++ /etc/freeradius/3.0/sites-available/default	2022-06-17 11:54:06.959487237 +0900
@@ -326,7 +326,7 @@
 	#
 	#  If you want to have a log of authentication requests,
 	#  un-comment the following line.
-#	auth_log
+	auth_log

 	#
 	#  The chap module will set 'Auth-Type := CHAP' if we are
@@ -345,7 +345,7 @@
 	#  If you have a Cisco SIP server authenticating against
 	#  FreeRADIUS, uncomment the following line, and the 'digest'
 	#  line in the 'authenticate' section.
-	digest
+#	digest

 	#
 	#  The WiMAX specification says that the Calling-Station-Id
@@ -436,6 +436,11 @@
 	#
 	#  The ldap module reads passwords from the LDAP database.
 	-ldap
+	if ((ok || updated) && User-Password) {
+		update control {
+			Auth-Type := ldap
+		}
+	}

 	#
 	#  Enforce daily limits on time spent logged in.
@@ -546,7 +551,7 @@
 	#  If you have a Cisco SIP server authenticating against
 	#  FreeRADIUS, uncomment the following line, and the 'digest'
 	#  line in the 'authorize' section.
-	digest
+#	digest

 	#
 	#  Pluggable Authentication Modules.
@@ -563,9 +568,9 @@
 	#  authentication server, and knows what to do with authentication.
 	#  LDAP servers do not.
 	#
-#	Auth-Type LDAP {
-#		ldap
-#	}
+	Auth-Type LDAP {
+		ldap
+	}

 	#
 	#  Allow EAP authentication.

MS-CHAPについて設定します。

$ sudo cp /etc/freeradius/3.0/mods-available/mschap /etc/freeradius/3.0/mods-available/mschap.20220616
$ sudo vim /etc/freeradius/3.0/mods-available/mschap
$ sudo diff -u /etc/freeradius/3.0/mods-available/mschap.20220616 /etc/freeradius/3.0/mods-available/mschap
--- /etc/freeradius/3.0/mods-available/mschap.20220616	2022-06-16 18:01:41.981739641 +0900
+++ /etc/freeradius/3.0/mods-available/mschap	2022-06-16 18:44:33.071578748 +0900
@@ -19,19 +19,19 @@
 	#  add MS-CHAP-MPPE-Keys for MS-CHAPv1 and
 	#  MS-MPPE-Recv-Key/MS-MPPE-Send-Key for MS-CHAPv2
 	#
-#	use_mppe = no
+	use_mppe = yes

 	#
 	#  If MPPE is enabled, require_encryption makes
 	#  encryption moderate
 	#
-#	require_encryption = yes
+	require_encryption = yes

 	#
 	#  require_strong always requires 128 bit key
 	#  encryption
 	#
-#	require_strong = yes
+	require_strong = yes

 	#
 	#  This module can perform authentication itself, OR
@@ -79,7 +79,7 @@
 	#
 	#  to the command-line parameters.
 	#
-#	ntlm_auth = "/path/to/ntlm_auth --request-nt-key --allow-mschapv2 --username=%{%{Stripped-User-Name}:-%{%{User-Name}:-None}} --challenge=%{%{mschap:Challenge}:-00} --nt-response=%{%{mschap:NT-Response}:-00}"
+	ntlm_auth = "/usr/bin/ntlm_auth --request-nt-key --allow-mschapv2 --username=%{%{Stripped-User-Name}:-%{%{User-Name}:-None}} --challenge=%{%{mschap:Challenge}:-00} --nt-response=%{%{mschap:NT-Response}:-00}"

 	#
 	#  The default is to wait 10 seconds for ntlm_auth to
@@ -114,6 +114,8 @@
 	#
 #	winbind_retry_with_normalised_username = no

+	with_ntdomain_hack = yes
+
 	#
 	#  Information for the winbind connection pool.  The configuration
 	#  items below are the same for all modules which use the new

radtestによる動作確認

設定ファイルを書き換えた後、FreeRADIUSをデバッグモードで起動します。

$ sudo freeradius -X

FreeRADIUSを起動した後、radtestコマンドを使って動作を確認します。PAP認証の具体例は以下の通りです。
Access-Acceptが認証成功を示します。

$ radtest -t pap radius 'r@d1u$123' localhost 0 testing123
Sent Access-Request Id 174 from 0.0.0.0:56093 to 127.0.0.1:1812 length 76
	User-Name = "radius"
	User-Password = "r@d1u$123"
	NAS-IP-Address = 192.168.1.214
	NAS-Port = 0
	Message-Authenticator = 0x00
	Cleartext-Password = "r@d1u$123"
Received Access-Accept Id 174 from 127.0.0.1:1812 to 127.0.0.1:56093 length 64
	Reply-Message = "CN=radius,CN=Users,DC=ad,DC=nayutaya,DC=jp"

MS-CHAP認証の具体例は以下の通りです。

$ radtest -t mschap radius 'r@d1u$123' localhost 0 testing123
Sent Access-Request Id 83 from 0.0.0.0:52151 to 127.0.0.1:1812 length 132
	User-Name = "radius"
	MS-CHAP-Password = "r@d1u$123"
	NAS-IP-Address = 192.168.1.214
	NAS-Port = 0
	Message-Authenticator = 0x00
	Cleartext-Password = "r@d1u$123"
	MS-CHAP-Challenge = 0xc23a8c69e1ba965a
	MS-CHAP-Response = 0x0001000000000000000000000000000000000000000000000000cebee7693e7229f5c6ec0eca1f3e0a8e82c266d446a03eb9
Received Access-Accept Id 83 from 127.0.0.1:1812 to 127.0.0.1:52151 length 128
	Reply-Message = "CN=radius,CN=Users,DC=ad,DC=nayutaya,DC=jp"
	MS-CHAP-MPPE-Keys = 0x00000000000000002a2f984c4466577c5b7009c5c8fd85e8
	MS-MPPE-Encryption-Policy = Encryption-Required
	MS-MPPE-Encryption-Types = 4

不正なユーザ名やパスワードを指定して、認証が失敗することも確認すると良いです。Access-Rejectが認証失敗を示します。

$ radtest -t pap radius wrong localhost 0 testing123
Sent Access-Request Id 242 from 0.0.0.0:51471 to 127.0.0.1:1812 length 76
	User-Name = "radius"
	User-Password = "wrong"
	NAS-IP-Address = 192.168.1.214
	NAS-Port = 0
	Message-Authenticator = 0x00
	Cleartext-Password = "wrong"
Received Access-Reject Id 242 from 127.0.0.1:1812 to 127.0.0.1:51471 length 64
	Reply-Message = "CN=radius,CN=Users,DC=ad,DC=nayutaya,DC=jp"
(0) -: Expected Access-Accept got Access-Reject

eapol_testによる動作確認

続いてeapol_testコマンドを使って動作を確認します。Ubuntuではeapoltestパッケージにeapol_testコマンドが含まれています。

$ sudo apt install eapoltest
$ eapol_test -v
eapol_test v2.10

認証方法、認証情報などを含む設定ファイルを作り、eapol_testコマンドを実行します。
以下の例では、EAP方式に「PEAP」、フェーズ2認証に「MS-CHAPv2」を指定しています。

$ vim peap-mschapv2.conf
$ cat peap-mschapv2.conf
network={
     ssid="TEST-SSID"
     key_mgmt=WPA-EAP
     eap=PEAP
     phase2="autheap=MSCHAPV2"
     identity="radius"
     password="r@d1u$123"
}

$ eapol_test -a 127.0.0.1 -c peap-mschapv2.conf -s testing123
...
State: DISCONNECTED -> COMPLETED
EAPOL: SUPP_PAE entering state AUTHENTICATED
EAPOL: SUPP_BE entering state RECEIVE
EAPOL: SUPP_BE entering state SUCCESS
EAPOL: SUPP_BE entering state IDLE
eapol_sm_cb: result=1
EAPOL: Successfully fetched key (len=32)
PMK from EAPOL - hexdump(len=32): 76 34 87 08 07 64 ba 3d 33 a2 a0 30 7c 9e 53 2a 6f 0e cd 6b b1 7e 58 60 fb 50 6c 4c 97 41 35 0e
No EAP-Key-Name received from server
WPA: Clear old PMK and PTK
EAP: deinitialize previously used EAP method (25, PEAP) at EAP deinit
ENGINE: engine deinit
MPPE keys OK: 1  mismatch: 0
SUCCESS

ここでは省略していますが、eapol_testコマンドの出力にはサーバ証明書の情報も含まれているため、サーバ証明書を確認するツールとしても使えます。

FreeRADIUSサービスの起動

動作確認の後、デバッグモードで起動したFreeRADIUSを終了し、サービス(デーモン)として起動します。

$ sudo systemctl start freeradius.service
$ systemctl status freeradius.service
● freeradius.service - FreeRADIUS multi-protocol policy server
     Loaded: loaded (/lib/systemd/system/freeradius.service; enabled; vendor preset: enabled)
     Active: active (running) since Fri 2022-06-17 11:57:30 JST; 3s ago
       Docs: man:radiusd(8)
             man:radiusd.conf(5)
             http://wiki.freeradius.org/
             http://networkradius.com/doc/
    Process: 105552 ExecStartPre=/usr/sbin/freeradius $FREERADIUS_OPTIONS -Cx -lstdout (code=exited, status=0/SUCCESS)
   Main PID: 105553 (freeradius)
     Status: "Processing requests"
      Tasks: 6 (limit: 4415)
     Memory: 79.3M (limit: 2.0G)
        CPU: 1.895s
     CGroup: /system.slice/freeradius.service
             └─105553 /usr/sbin/freeradius -f
...

FreeRADIUSのログは以下のコマンドで参照できます。

$ journalctl -u freeradius.service -f

hostapdのセットアップ

WPA2 Enterprise対応のアクセスポイントを構築するために、ブリッジを構成し、hostapdをセットアップします。

ブリッジの構成

ブリッジを構成する前の環境は以下の通りです。eth0が上位ネットワークで、Ethernet経由でルータ、そしてインターネットに接続されています。
また、固定のIPアドレスがDHCPで割り当てられています。

$ ip addr
...
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
    link/ether dc:a6:32:64:9c:b0 brd ff:ff:ff:ff:ff:ff
    inet 192.168.1.214/24 metric 100 brd 192.168.1.255 scope global dynamic eth0
       valid_lft 43344sec preferred_lft 43344sec
    inet6 fe80::dea6:32ff:fe64:9cb0/64 scope link
       valid_lft forever preferred_lft forever
3: wlan0: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN group default qlen 1000
    link/ether dc:a6:32:64:9c:b1 brd ff:ff:ff:ff:ff:ff

ネットワークインターフェースの設定は、netplanを用いて行われています。

$ cat /etc/netplan/50-cloud-init.yaml
...
network:
    ethernets:
        eth0:
            dhcp4: true
            optional: true
    version: 2

ブリッジを構成するための設定ファイルを作成します。要点は以下の通りです。

  • eth0wlan0のDHCPを無効化します。
  • ブリッジbr0を作成します
    • ブリッジにはメンバーとしてeth0のみを加えます。(wlan0hostapdが自動的に加えます)
    • ブリッジには固定のIPアドレスを設定します。
$ sudo vim /etc/netplan/99-config.yaml
$ cat /etc/netplan/99-config.yaml
network:
  version: 2
  ethernets:
    eth0:
      dhcp4: no
    wlan0:
      dhcp4: no
  bridges:
    br0:
      interfaces:
        - eth0
      dhcp4: no
      addresses:
        - 192.168.1.214/24
      routes:
        - to: default
          via: 192.168.1.1
      nameservers:
        addresses:
          - 127.0.0.1
        search:
          - ad.nayutaya.jp

netplan applyを実行してネットワーク構成を適用します。この際、ネットワークの構成が変わってSSH接続などが切れます。
そのため、Raspberry Piのローカルコンソールにアクセスできる状態で行うことをお勧めします。(失敗するとネットワーク経由でアクセスできなくなるため)

$ sudo netplan apply

ネットワーク構成の適用後(または再起動後)、eth0のIPアドレスがなくなり、br0にIPアドレスが設定されていることを確認します。

$ ip addr
...
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq master br0 state UP group default qlen 1000
    link/ether dc:a6:32:64:9c:b0 brd ff:ff:ff:ff:ff:ff
3: wlan0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc fq_codel state DOWN group default qlen 1000
    link/ether dc:a6:32:64:9c:b1 brd ff:ff:ff:ff:ff:ff
4: br0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
    link/ether 46:9b:75:00:c1:bb brd ff:ff:ff:ff:ff:ff
    inet 192.168.1.214/24 brd 192.168.1.255 scope global br0
       valid_lft forever preferred_lft forever
    inet6 fe80::449b:75ff:fe00:c1bb/64 scope link
       valid_lft forever preferred_lft forever

hostapdのインストール

hostapdサービスをインストールし、マスクを解除、有効化します。
また、無線LANクライアント(Supplicant)として動作しないようにするために、wpa_supplicantサービスを停止、無効化します。

$ sudo apt install hostapd
$ sudo systemctl unmask hostapd.service
$ sudo systemctl enable hostapd.service
$ sudo systemctl stop wpa_supplicant.service
$ sudo systemctl disable wpa_supplicant.service

WPA2 Personalによる動作確認

WPA2 Enterprise対応アクセスポイントを構築する前に、まずはシンプルなWPA2 Personal対応のアクセスポイントを作成します。
設定ファイルの例は以下の通りです。SSIDはEAP-TEST、パスワードはhogehogeとしています。

$ sudo vim /etc/hostapd/hostapd.conf
$ cat /etc/hostapd/hostapd.conf
country_code=JP
interface=wlan0
bridge=br0
ssid=EAP-TEST
hw_mode=g
channel=7
macaddr_acl=0
auth_algs=1
ignore_broadcast_ssid=0
wpa=2
wpa_passphrase=hogehoge
wpa_key_mgmt=WPA-PSK
wpa_pairwise=TKIP
rsn_pairwise=CCMP

$ sudo systemctl restart hostapd.service

hostapdサービスの再起動後、適当なWi-Fi対応端末からEAP-TESTに接続します。アクセスポイントに接続でき、インターネットと通信できれば成功です。
なお、hostapdサービスのログは以下のコマンドで参照できます。

$ journalctl -u hostapd.service -f

WPA2 Enterpriseによる動作確認

いよいよ本丸。WPA2 Enterprise対応に変更します。
設定の詳細などは「参考」に示したページをご参照ください。

$ cat /etc/hostapd/hostapd.conf
country_code=JP
interface=wlan0
bridge=br0
ssid=EAP-TEST
hw_mode=a
channel=36
macaddr_acl=0
auth_algs=1
ignore_broadcast_ssid=0
wpa=2
wpa_key_mgmt=WPA-EAP WPA-EAP-SHA256
wpa_pairwise=TKIP
rsn_pairwise=CCMP
rsn_preauth=1
ieee80211d=1
ieee80211h=1
ieee80211n=1
require_ht=1
ieee8021x=1
eapol_version=2
own_ip_addr=192.168.1.214
radius_client_addr=127.0.0.1
auth_server_addr=127.0.0.1
auth_server_port=1812
auth_server_shared_secret=testing123
acct_server_addr=127.0.0.1
acct_server_port=1813
acct_server_shared_secret=testing123

$ sudo systemctl restart hostapd.service

Android端末からの接続

構築したWPA2 Enterprise対応アクセスポイントに、Android 11の端末から接続してみます。設定は以下の通りです。

  • SSID: EAP-TEST
  • EAP方式: PEAP
  • フェーズ2認証: MSCHAPV2
  • CA証明書: 検証なし
  • ID: radius
  • 匿名ID: (指定なし)
  • パスワード: r@d1u$123

アクセスポイントに接続でき、インターネットに接続できれば成功です。

今回のFreeRADIUS環境では、自動生成されたデフォルトの証明書を使用しているため、「CA証明書: 検証なし」となっています。このオプションはWPA3への移行に伴い、Android 11の途中、Android 12以降では使用できません。
そのため「ある程度妥当な」証明書を設定する必要があります。(手動でルートCA証明書を登録すれば、システム内蔵のルートCA証明書で検証可能である必要はありません)
ちなみにこの証明書に関する検証が、この検証環境を構築した目的なのでした。

おわりに

記事中では特に述べていませんが、ハマりポイントが多々あり、検証環境の構築にそれなりの時間を要しました。
似たような環境を構築する方に、何かの参考になれば幸いです。

参考

https://www.raspberrypi.com/documentation/computers/configuration.html#setting-up-a-bridged-wireless-access-point

https://www.marbacka.net/blog/freeradius_eap-tls_wi-fi/

https://www.marbacka.net/blog/hostapd_access-point_bridge_enterprise-authentication/

https://qiita.com/f_nishio/items/74691eb900b554a12004

https://nanbu.marune205.net/2021/12/freeradius-wpa2-cert.html?m=1

https://qiita.com/bashaway/items/a9cf4a9168f325e65513

http://www.ne.jp/asahi/it/life/it/network/network_freeradius/freeradius.html

https://nwengblog.com/802-1x-sequence/

https://docs.microsoft.com/ja-jp/windows-server/networking/technologies/nps/nps-manage-cert-requirements

Discussion