雑な hinananoha

やさしいせかいをさがして三千里

OpenLDAP+freeRADIUS連携でWindowsでも利用可能な無線LAN認証(WPA2/WPA3-EAP(PEAP, MS-CHAPv2))

TL;DR

この記事は whywaita advent calendar 5日目(遅刻)の記事です。
adventar.org

投稿プレビューに出ていた今日の日付

大遅刻です。遅刻してごめんなさい!!!!

whywaitaと言えばインフラ*1というわけでインフラの話を書きます*2

LDAPRADIUSを連携させてWPA2/WPA3-EAPを実現する方法は世の中に多数記事がありますが、世に出回っている方法ではWindows端末に対して安定的にWPA2/WPA3-EAPを提供出来ないとされています。
その原因は、Windowsではユーザ名/パスワード方式を使う場合は標準でPEAP(MS-CHAPv2)を用いるのですが、LDAPで通常作成されるPOSIX Accountの情報ではMS-CHAPv2に必要なNTPasswordを得られないためです。
従来(2016年頃)までは、それらを有機的に結合される「smbldap-tools」というパッケージがあったのですが、現在は更新されておらず使うことが出来ません。

そのため、この記事は、「出来る限り少ない労力で」OpenLDAP+freeRADIUS連携でWindowsでも利用可能な無線LAN認証環境を構築する方法を解説します。

構築手順

なお、以下の手順はUbuntu Server 24.04 で構築した際の手順です。他のディストリビューションは適宜読み替えてください。

  1. パッケージのインストール(OpenLDAP/freeRADIUS/samba)
  2. LDAPの構築
  3. EAP用の証明書発行
  4. freeRADIUSの設定
  5. sambaスキーマOpenLDAPへの登録
  6. sambaの設定(samba-ad-dc)
  7. LDAP Account Managerのインストール
  8. LDAP Account Managerの設定

パッケージのインストール

~$ sudo apt install slapd ldap-utils samba freeradius freeradius-ldap

インストール中にLDAPサーバの管理者パスワードの設定が求められるので、設定すること。

LDAPの構築

ドメインの再設定

まずは現在の設定を slapcat コマンドで確認する。

~$ sudo slapcat
dn: dc=nodomain
objectClass: top
objectClass: dcObject
objectClass: organization
o: nodomain
dc: nodomain
structuralObjectClass: organization
entryUUID: 3e1d49fe-6890-1040-913d-db5a588913ed
creatorsName: cn=admin,dc=nodomain
createTimestamp: 20251208144511Z
entryCSN: 20251208144511.116760Z#000000#000#000000
modifiersName: cn=admin,dc=nodomain
modifyTimestamp: 20251208144511Z

最初はdn が dc=nodomain で登録されているので、適切なドメイン名に変更する。

~$ sudo dpkg-reconfigure slapd

以下は、ドメインを hinananoha.example.com、Organization Nameを hinananoha にした後の slapcat である。

~$ sudo slapcat
dn: dc=hinananoha,dc=example,dc=com
objectClass: top
objectClass: dcObject
objectClass: organization
o: hinananoha
dc: hinananoha
structuralObjectClass: organization
entryUUID: fe0adefa-6a18-1040-9bc6-1b28b1cf31c6
creatorsName: cn=admin,dc=hinananoha,dc=example,dc=com
createTimestamp: 20251210133635Z
entryCSN: 20251210133635.836632Z#000000#000#000000
modifiersName: cn=admin,dc=hinananoha,dc=example,dc=com
modifyTimestamp: 20251210133635Z
user/group organizationUnitの作成

ユーザとグループを格納するouも作成する。
base.ldif ファイルを以下の様に作成する。

dn: ou=people,dc=hinananoha,dc=example,dc=com
objectClass: organizationalUnit
ou: people

dn: ou=groups,dc=hinananoha,dc=example,dc=com
objectClass: organizationalUnit
ou: groups

このファイルでouを作成する。

~$ sudo ldapadd -x -D cn=admin,dc=hinananoha,dc=example,dc=com -W -f base.ldif
Enter LDAP Password:
adding new entry "ou=people,dc=hinananoha,dc=example,dc=com"

adding new entry "ou=groups,dc=hinananoha,dc=example,dc=com"

以下が初期設定が完了した後の slapcat の結果である。

~$ sudo slapcat
dn: dc=hinananoha,dc=example,dc=com
objectClass: top
objectClass: dcObject
objectClass: organization
o: hinananoha
dc: hinananoha
structuralObjectClass: organization
entryUUID: fe0adefa-6a18-1040-9bc6-1b28b1cf31c6
creatorsName: cn=admin,dc=hinananoha,dc=example,dc=com
createTimestamp: 20251210133635Z
entryCSN: 20251210133635.836632Z#000000#000#000000
modifiersName: cn=admin,dc=hinananoha,dc=example,dc=com
modifyTimestamp: 20251210133635Z

dn: ou=people,dc=hinananoha,dc=example,dc=com
objectClass: organizationalUnit
ou: people
structuralObjectClass: organizationalUnit
entryUUID: 0a528b6e-6a1d-1040-918c-5d35bc622ca0
creatorsName: cn=admin,dc=hinananoha,dc=example,dc=com
createTimestamp: 20251210140534Z
entryCSN: 20251210140534.425941Z#000000#000#000000
modifiersName: cn=admin,dc=hinananoha,dc=example,dc=com
modifyTimestamp: 20251210140534Z

dn: ou=groups,dc=hinananoha,dc=example,dc=com
objectClass: organizationalUnit
ou: groups
structuralObjectClass: organizationalUnit
entryUUID: 0a671728-6a1d-1040-918d-5d35bc622ca0
creatorsName: cn=admin,dc=hinananoha,dc=example,dc=com
createTimestamp: 20251210140534Z
entryCSN: 20251210140534.560608Z#000000#000#000000
modifiersName: cn=admin,dc=hinananoha,dc=example,dc=com
modifyTimestamp: 20251210140534Z

EAP用の証明書発行

EAP用にサーバ証明書が必要なため、作成する。
作成方法は任意ですが、独自証明書を作成する場合はCA証明書も作成する必要あり。
これについては、本記事の主題ではないため、ここでは割愛。
easy-rsa などを使えば楽に作れます。
このあたりとか見れば良いと思います。
https://docs.nifcloud.com/network/guide/cert_easy-rsa_linux.htm

なお、サーバ証明書を作る際、秘密鍵にはパスワードをかけないようにして下さい。

ここでは、CA証明書を ca.crt、サーバ証明書を server.crt、サーバ秘密鍵を server.key として作成したものとします。

freeRADIUSの設定

freeRADIUSの設定を以下実施する。

RADIUS Clientの設定

RADIUSに接続するクライアント(Wi-Fi AP)のアドレス(帯)とsecretを /etc/freeradius/3.0/clients.conf に設定する。

--- clients.conf.orig 
+++ clients.conf 
@@ -132,7 +132,7 @@
        #  The default secret below is only for testing, and should
        #  not be used in any real environment.
        #
-       secret = testing123
+       secret = insertHereInUseSecret

        #
        #  The global configuration "security.require_message_authenticator"
@@ -284,7 +284,7 @@
 # IPv6 Client
 client localhost_ipv6 {
        ipv6addr        = ::1
-       secret          = testing123
+       secret          = insertHereInSecret
 }

 # All IPv6 Site-local clients
@@ -303,10 +303,10 @@
 #  When a client request comes in, the BEST match is chosen.
 #  i.e. The entry from the smallest possible network.
 #
-#client private-network-1 {
-#      ipaddr          = 192.0.2.0/24
-#      secret          = testing123-1
-#}
+client private-network-1 {
+       ipaddr          = 192.168.1.0/24
+       secret          = insertHereInSecret
+}

 #client private-network-2 {
 #      ipaddr          = 198.51.100.0/24
LDAP-RADIUS連携の設定

LDAP-RADIUS連携の設定を /etc/freeradius/3.0/dictionary と /etc/freeradius/3.0/mods-available/ldap に入れる。

  • dictionary
--- dictionary.orig 
+++ dictionary 
@@ -47,3 +47,4 @@
 #ATTRIBUTE     My-Local-String         3000    string
 #ATTRIBUTE     My-Local-IPAddr         3001    ipaddr
 #ATTRIBUTE     My-Local-Integer        3002    integer
+VALUE  Auth-Type       LDAP    5
  • mods-available/ldap
--- ldap.orig
+++ ldap  
@@ -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=admin,dc=hinananoha,dc=example,dc=com'
+       password = admin_password_for_ldap

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

        #
        #  You can run the 'ldapsearch' command line tool using the
@@ -127,7 +127,7 @@
        #  attribute ref.
        update {
                control:Password-With-Header    += 'userPassword'
-#              control:NT-Password             := 'ntPassword'
+               control:NT-Password             := 'sambaNTPassword'
 #              reply:Reply-Message             := 'radiusReplyMessage'
 #              reply:Tunnel-Type               := 'radiusTunnelType'
 #              reply:Tunnel-Medium-Type        := 'radiusTunnelMediumType'

これらの設定が終わった後、ldapの設定ファイルをmods-enabledに追加し、有効化する

~$ sudo ln -s /etc/freeradius/3.0/mods-available/ldap /etc/freeradius/3.0/mods-enabled/ldap
EAPの設定

EAPの種類と証明書の設定などをする。
先立って、作成したCA証明書・サーバ証明書・サーバ秘密鍵を /etc/freeradius/3.0/certs フォルダに格納しておく。
格納後に、chown で3つのファイルのowner/groupをどちらも freerad にしておくこと。

/etc/freeradius/3.0/certs# sudo chown freerad:freerad ca.crt
/etc/freeradius/3.0/certs# sudo chown freerad:freerad server.{crt,key}

その上で、 /etc/freeradius/3.0/mods-available/eap を以下の様に修正する

--- eap.orig
+++ eap 
@@ -24,7 +24,7 @@
        #  then that EAP type takes precedence over the
        #  default type configured here.
        #
-       default_eap_type = md5
+       default_eap_type = peap

        #  A list is maintained to correlate EAP-Response
        #  packets with EAP-Request packets.  After a
@@ -197,8 +197,8 @@
        #  authenticate via EAP-TLS!  This is likely not what you want.
        #
        tls-config tls-common {
-               private_key_password = whatever
-               private_key_file = /etc/ssl/private/ssl-cert-snakeoil.key
+               #private_key_password = whatever
+               private_key_file = ${certdir}/server.key

                #  If Private key & Certificate are located in
                #  the same file, then private_key_file &
@@ -234,7 +234,7 @@
                #  give advice which will work everywhere.  Instead,
                #  we give general guidelines.
                #
-               certificate_file = /etc/ssl/certs/ssl-cert-snakeoil.pem
+               certificate_file = ${certdir}/server.crt

                #  Trusted Root CA list
                #
@@ -247,7 +247,7 @@
                #  In that case, this CA file should contain
                #  *one* CA certificate.
                #
-               ca_file = /etc/ssl/certs/ca-certificates.crt
+               ca_file = ${certdir}/ca.crt

                #
                #  Directory where multiple CAs are stored.  Both
@@ -690,7 +690,7 @@
                        #  You should also delete all of the files
                        #  in the directory when the server starts.
                        #
-               #       tmpdir = /tmp/radiusd
+                       tmpdir = /var/run/freeradius/tmp

                        #  The command used to verify the client cert.
                        #  We recommend using the OpenSSL command-line
@@ -705,7 +705,7 @@
                        #  deleted by the server when the command
                        #  returns.
                        #
-               #       client = "/path/to/openssl verify -CApath ${..ca_path} %{TLS-Client-Cert-Filename}"
+                       client = "/path/to/openssl verify -CApath ${..ca_path} %{TLS-Client-Cert-Filename}"
                }

                #  OCSP Configuration
RADIUSサーバの設定

RADIUSサーバ(ホスティング)の設定として、/etc/freeradius/3.0/sites-available/default と /etc/freeradius/3.0/sites-available/inner-tunnel の設定を変更する。

  • default
--- default.orig     
+++ default     
@@ -290,7 +290,7 @@
        #
        #  See policy.d/filter for the definition of the filter_username policy.
        #
-       filter_username
+#      filter_username

        #
        #  Some broken equipment sends passwords with embedded zeros.
@@ -309,7 +309,7 @@
        #
        #  It takes care of processing the 'raddb/mods-config/preprocess/hints'
        #  and the 'raddb/mods-config/preprocess/huntgroups' files.
-       preprocess
+#      preprocess

        #  If you intend to use CUI and you require that the Operator-Name
        #  be set for CUI generation and you want to generate CUI also
@@ -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 dpsk module implements dynamic PSK.
@@ -393,7 +393,7 @@

        #
        # Look for realms in user@domain format
-       suffix
+#      suffix
 #      ntdomain

        #
@@ -432,14 +432,14 @@
        #
        #  Read the 'users' file.  In v3, this is located in
        #  raddb/mods-config/files/authorize
-       files
+#      files

        #
        #  Look in an SQL database.  The schema of the database
        #  is meant to mirror the "users" file.
        #
        #  See "Authorization Queries" in mods-available/sql
-       -sql
+#      -sql

        #
        #  If you are using /etc/smbpasswd, and are also doing
@@ -449,7 +449,7 @@

        #
        #  The ldap module reads passwords from the LDAP database.
-       -ldap
+       ldap

        #
        #  If you're using Active Directory and PAP, then uncomment
@@ -468,8 +468,8 @@
 #      daily

        #
-       expiration
-       logintime
+#      expiration
+#      logintime

        #
        #  If no other module has claimed responsibility for
@@ -574,7 +574,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.
@@ -594,9 +594,9 @@
        #  However, it is necessary for Active Directory, because
        #  Active Directory won't give the passwords to FreeRADIUS.
        #
-#      Auth-Type LDAP {
-#              ldap
-#      }
+       Auth-Type LDAP {
+               ldap
+       }

        #
        #  Allow EAP authentication.
@@ -712,7 +712,7 @@
        #  Log traffic to an SQL database.
        #
        #  See "Accounting queries" in mods-available/sql
-       -sql
+#      -sql

        #
        #  If you receive stop packets with zero session length,
@@ -868,13 +868,13 @@
        #  After authenticating the user, do another SQL query.
        #
        #  See "Authentication Logging Queries" in mods-available/sql
-       -sql
+#      -sql

        #
        #  Un-comment the following if you want to modify the user's object
        #  in LDAP after a successful login.
        #
-#      ldap
+       ldap

        # For Exec-Program and Exec-Program-Wait
        exec
  • inner-tunnel
--- inner-tunnel.orig 
+++ inner-tunnel    
@@ -31,7 +31,7 @@
 #  on fixing the inner tunnel configuration.  DO NOTHING ELSE.
 #
 listen {
-       ipaddr = 127.0.0.1
+       ipaddr = *
        port = 18120
        type = auth
 }
@@ -53,7 +53,7 @@
        #
        #  See policy.d/filter for the definition of the filter_username policy.
        #
-       filter_username
+#      filter_username

        #
        #  Do checks on outer / inner User-Name, so that users
@@ -101,7 +101,7 @@
        #  accounting logs *not* sent to the other server.  This makes
        #  it difficult to bill people for their network activity.
        #
-       suffix
+#      suffix
 #      ntdomain

        #
@@ -112,9 +112,9 @@
        #  If you want the inner tunnel request to be proxied, delete
        #  the next few lines.
        #
-       update control {
-               &Proxy-To-Realm := LOCAL
-       }
+#      update control {
+#              &Proxy-To-Realm := LOCAL
+#      }

        #
        #  This module takes care of EAP-MSCHAPv2 authentication.
@@ -134,14 +134,14 @@

        #
        #  Read the 'users' file
-       files
+#      files

        #
        #  Look in an SQL database.  The schema of the database
        #  is meant to mirror the "users" file.
        #
        #  See "Authorization Queries" in `mods-config/sql/main/$driver/queries.conf`
-       -sql
+#      -sql

        #
        #  If you are using /etc/smbpasswd, and are also doing
@@ -151,14 +151,14 @@

        #
        #  The ldap module reads passwords from the LDAP database.
-       -ldap
+       ldap

        #
        #  Enforce daily limits on time spent logged in.
 #      daily

-       expiration
-       logintime
+#      expiration
+#      logintime

        #
        #  If no other module has claimed responsibility for
@@ -268,9 +268,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.
@@ -334,14 +334,14 @@
        #  After authenticating the user, do another SQL query.
        #
        #  See "Authentication Logging Queries" in `mods-config/sql/main/$driver/queries.conf`
-       -sql
+#      -sql

        #
        #  Un-comment the following if you have set
        #  'edir = yes' in the ldap module sub-section of
        #  the 'modules' section.
        #
-#      ldap
+       ldap


        #
EAP用tmpファイルの自動作成

EAPの設定に記載した tmpdir = /var/run/freeradius/tmp をfreeRADIUSの起動時に自動で生成されるように、freeRADIUSのsystemdのUnitファイルのoverrideの設定を追加する。

~$ sudo mkdir -p /etc/systemd/system/freeradius.service.d
~$ sudoedit /etc/systemd/system/freeradius.service.d/override.conf

Unitファイルのoverrideに以下の様に記述

[Service]
User=freerad
Group=freerad
RuntimeDirectory=freeradius freeradius/tmp
RuntimeDirectoryPreserve=yes
freeRADIUSの再起動

freeRADIUSの設定は完了のため、再起動を実施する。

~$ sudo systemctl daemon-reload
~$ sudo systemctl restart freeradius.service

sambaスキーマOpenLDAPへの登録

LDAP内にNTPasswordを格納するためのsambaオブジェクトクラスを追加するために、sambaのスキーマOpenLDAPへ登録する。
sambaスキーマはsambaパッケージのdocの中に入っているため、sambaパッケージがインストールされていれば自動で使える。

~$ cd /usr/share/doc/samba/examples/LDAP/
/usr/share/doc/samba/examples/LDAP$ sudo ldapadd -Y EXTERNAL -H ldapi:/// -f samba.ldif
SASL/EXTERNAL authentication started
SASL username: gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth
SASL SSF: 0
adding new entry "cn=samba,cn=schema,cn=config"

正常に登録出来ていると、以下のとおりになります。

$ sudo ldapsearch -LLLQY EXTERNAL -H ldapi:/// -b cn=schema,cn=config "(objectClass=olcSchemaConfig)" dn
dn: cn=schema,cn=config

dn: cn={0}core,cn=schema,cn=config

dn: cn={1}cosine,cn=schema,cn=config

dn: cn={2}nis,cn=schema,cn=config

dn: cn={3}inetorgperson,cn=schema,cn=config

dn: cn={4}samba,cn=schema,cn=config

sambaの設定(samba-ad-dc)

LDAP Account ManagerにSamba AD/DCがあると「勘違い」させるための設定をする。
/etc/samba/smb.confを以下のように修正する。(workgroupのところは好きな値を入力)

--- /etc/samba/smb.conf.orig    
+++ /etc/samba/smb.conf 
@@ -26,7 +26,7 @@
 ## Browsing/Identification ###

 # Change this to the workgroup/NT-domain name your Samba server will part of
-   workgroup = WORKGROUP
+   workgroup = HINANANOHA

 # server string is the equivalent of the NT Description field
    server string = %h server (Samba, Ubuntu)
@@ -167,6 +167,17 @@
 # public shares, not just authenticated ones
    usershare allow guests = yes

+#======================= LDAP Configuration =====================
+
+load printers = no
+passdb backend = ldapsam:ldap://localhost
+ldap suffix = dc=hinananoha,dc=example,dc=com
+ldap user suffix = ou=people
+ldap group suffix = ou=groups
+ldap admin dn = cn=admin,dc=hinananoha,dc=example,dc=com
+ldap passwd sync = yes
+ldap ssl = off
+
 #======================= Share Definitions =======================

 # Un-comment the following (and tweak the other settings below to suit)
@@ -216,23 +227,23 @@
 ;   create mask = 0600
 ;   directory mask = 0700

-[printers]
-   comment = All Printers
-   browseable = no
-   path = /var/tmp
-   printable = yes
-   guest ok = no
-   read only = yes
-   create mask = 0700
+;[printers]
+;   comment = All Printers
+;   browseable = no
+;   path = /var/tmp
+;   printable = yes
+;   guest ok = no
+;   read only = yes
+;   create mask = 0700

 # Windows clients look for this share name as a source of downloadable
 # printer drivers
-[print$]
-   comment = Printer Drivers
-   path = /var/lib/samba/printers
-   browseable = yes
-   read only = yes
-   guest ok = no
+;[print$]
+;   comment = Printer Drivers
+;   path = /var/lib/samba/printers
+;   browseable = yes
+;   read only = yes
+;   guest ok = no
 # Uncomment to allow remote administration of Windows print drivers.
 # You may need to replace 'lpadmin' with the name of the group your
 # admin users are members of

その後、LDAPの管理パスワードと同じパスワードをsambaの管理パスワードとして登録。

~$ sudo smbpasswd -W
Setting stored password for "cn=admin,dc=hinananoha,dc=example,dc=com" in secrets.tdb
New SMB password:
Retype new SMB password:

設定を反映させるために、sambaを再起動します。

~$ sudo systemctl restart smbd

ここまでの設定が正常に出来ていれば、slapcatをすると、LDAPにsambaドメイン情報が追加されているはずです。

~$ sudo slapcat
dn: dc=hinananoha,dc=example,dc=com
objectClass: top
objectClass: dcObject
objectClass: organization
o: hinananoha
dc: hinananoha
structuralObjectClass: organization
entryUUID: fe0adefa-6a18-1040-9bc6-1b28b1cf31c6
creatorsName: cn=admin,dc=hinananoha,dc=example,dc=com
createTimestamp: 20251210133635Z
entryCSN: 20251210133635.836632Z#000000#000#000000
modifiersName: cn=admin,dc=hinananoha,dc=example,dc=com
modifyTimestamp: 20251210133635Z

dn: ou=people,dc=hinananoha,dc=example,dc=com
objectClass: organizationalUnit
ou: people
structuralObjectClass: organizationalUnit
entryUUID: 0a528b6e-6a1d-1040-918c-5d35bc622ca0
creatorsName: cn=admin,dc=hinananoha,dc=example,dc=com
createTimestamp: 20251210140534Z
entryCSN: 20251210140534.425941Z#000000#000#000000
modifiersName: cn=admin,dc=hinananoha,dc=example,dc=com
modifyTimestamp: 20251210140534Z

dn: ou=groups,dc=hinananoha,dc=example,dc=com
objectClass: organizationalUnit
ou: groups
structuralObjectClass: organizationalUnit
entryUUID: 0a671728-6a1d-1040-918d-5d35bc622ca0
creatorsName: cn=admin,dc=hinananoha,dc=example,dc=com
createTimestamp: 20251210140534Z
entryCSN: 20251210140534.560608Z#000000#000#000000
modifiersName: cn=admin,dc=hinananoha,dc=example,dc=com
modifyTimestamp: 20251210140534Z

dn: sambaDomainName=DEV-PROX,dc=hinananoha,dc=example,dc=com
sambaDomainName: DEV-PROX
sambaSID: S-1-5-21-1595148958-61504898-393552916
sambaAlgorithmicRidBase: 1000
objectClass: sambaDomain
sambaNextUserRid: 1000
structuralObjectClass: sambaDomain
entryUUID: f12d05a4-73f1-1040-9162-8b1d10ac8f32
creatorsName: cn=admin,dc=hinananoha,dc=example,dc=com
createTimestamp: 20251223022215Z
sambaMinPwdLength: 5
sambaPwdHistoryLength: 0
sambaLogonToChgPwd: 0
sambaMaxPwdAge: -1
sambaMinPwdAge: 0
sambaLockoutDuration: 30
sambaLockoutObservationWindow: 30
sambaLockoutThreshold: 0
sambaForceLogoff: -1
sambaRefuseMachinePwdChange: 0
entryCSN: 20251223022215.831180Z#000000#000#000000
modifiersName: cn=admin,dc=hinananoha,dc=example,dc=com
modifyTimestamp: 20251223022215Z

以上でサーバとしての設定は完了です。

LDAP Account Managerのインストール

LDAP Account Managerをインストールする

~$ sudo apt update
~$ sudo apt install ldap-account-manager

もし必要であれば、アクセス許可範囲の変更などをApacheの /etc/apache2/conf-enabled/ldap-account-manager.conf から実施する。

LDAP Account Managerの設定

Webブラウザを開いて、LDAP Account Managerを開く。標準の設定の場合は、 http://<サーバのIPアドレス>/lam/

LDAP情報の登録

開いたら、右上の「LAM Configuration」と書かれたリンクをクリックした上で、「Edit server profiles」をクリック。パスワードは標準では「lam」。
表示された「General settings」にLDAPサーバの情報を入力する。
入力項目は以下のとおり(※但し、LDAP Account Managerのバージョンによって項目の位置が少し変わるので注意)

  • Server settings
    • Server address: (もしLDAPサーバとLDAP Account Managerのホストが違う場合は「localhost」を当該ホストのIPアドレス/FQDNに変更)
    • List of valid users: cn=admin,dc=hinananoha,dc=example,dc=com (LDAP管理者アカウントの場所を設定)
  • Language settings (※任意)
    • Default language: 日本語(日本)
    • Time zone: Asia/Tokyo
  • Profile password: 必要に応じて変更する
入力後の状態(パスワード欄は未入力)
LDAP Account Managerで管理する項目の設定

続いて、「Account types」タブを選択して、LDAP Account Managerで管理する項目を設定する。
まず、「Available account types」の「Samba domains」の緑色の+ボタンをクリックし、Samba domainsをLDAP Account Managerで有効にする。
その上で、「Active account types」を以下の様に修正する

  • Users
    • LDAP suffix: ou=people,dc=hinananoha,dc=example,dc=com
  • Groups
    • LDAP suffix: ou=groups,dc=hinananoha,dc=example,dc=com
  • Samba domains
    • LDAP suffix: dc=hinananoha,dc=example,dc=com
    • List attributes: #sambaDomainName;#sambaSID
入力後の状態
LDAP Accout Managerでsambaオブジェクトクラスを有効にする設定

続いて、「Modules」タブを選択して、sambaオブジェクトクラスを参照可能な状態にする。
下の画像で赤枠で囲われている、「Users」の「Samba 3 (sambaSamAccount) 」の緑色の + ボタンをクリックした後、同様に赤枠で囲われている「Samba domains」の2つの項目の緑色の+ボタンをクリックする。

Modules 設定画面

ここまで設定したら、左下の「Save」ボタンをクリックし、設定を完了する。

動作確認

ドメイン情報の表示確認

LDAP Account Managerのログイン画面に戻ってくるので、ログインを実施する。パスワードはLDAPの管理パスワード(先程のプロファイル管理パスワードではないので注意)。
ログイン後、右上の「Accounts」にマウスオーバーして「Samba domains」をクリックした時にドメイン情報が表示されていればOK。

user識別情報の変更

標準ではユーザアカウントをuid= ではなく cn= で作ろうとするので修正する。
右上の「Tools」にマウスオーバーして「Profile editor」をクリックする。
その後、「Manage existing profiles」から「Users」の横の編集ボタン(画像赤枠)をクリックする。

General settingsの「RDN identifier」を「cn」から「uid」に変更する。

変更したら下までスクロールして「Save」をクリックして保存する。

ユーザアカウントの作成

無線LAN認証で用いるアカウントを作成する。
アカウントの作成方法はいくつかあるが、ここではLinuxのログインにも利用可能な形で作成する。

まず、右上の「Accounts」にマウスオーバーして「Groups」をクリックする。
グループの表示画面になるので、「New group」をクリックする。

グループは以下の情報で作成する

  • Group name: username(uid)に付ける予定のものと同じものを入力
  • GID number: 任意の数字を設定。よく使われるのは 10000 以降。今後作成する場合はこのIDをインクリメントする。

入力が終わったら「Save」をクリックしてグループの作成を終了する。

右上の「Accounts」にマウスオーバーして「Users」をクリックする。
ユーザの表示画面になるので、「New user」をクリックする。
右上の「RDN identifier」が「uid」になっていることを必ず確認する。

ユーザは以下の情報で作成する。

  • Personal
    • First name/Last name: 適当な値を入力(Last nameの方が必須)
    • 他の項目:好きなように入力
  • Unix
    • username: 先程「Group name」に入力したものと同じ物を入力。なお、標準で「First nameの頭文字+Last name」が入力されている。
    • UID number: 先程「GID number」で入力したものと同じものを入力
    • Primary group: 先程作成したグループを選択(1つ目のアカウントの場合は1つしかグループが無いため既に選ばれているはず)
  • Samba 3
    • 「Add Samba 3 extension」というボタンが表示されている(下図)ので、ボタンをクリックする。
    • 表示された項目は特に変更する必要はない

ここまでを実施した後(特に「Samba 3」の項目は忘れずに実施する)「Set password」ボタンをクリックする。
下に「Unix」と「Samba 3」にチェックが付いていることを確認して、パスワードを設定する。

パスワードの設定まで完了したら、左上の「Save」ボタンをクリックしてユーザの作成を終了する。

LDAPユーザアカウントの確認

ここまで完了したところで、slapcatコマンドを実行して LDAP の登録情報を確認し、自分のユーザのところに「sambaNTPassword」が記載されていれば動作確認完了。

dn: uid=hinananoha,ou=people,dc=hinananoha,dc=example,dc=com
objectClass: sambaSamAccount
objectClass: posixAccount
objectClass: inetOrgPerson
objectClass: organizationalPerson
objectClass: person
sambaDomainName: DEV-PROX
displayName: hinananoha
sambaAcctFlags: [XU         ]
sambaHomeDrive: U:
sambaKickoffTime: 1893466373
sambaPrimaryGroupSID: S-1-5-21-1595148958-61504898-393552916-513
sambaNTPassword: XXXXXXXXXXX[MASKED]XXXXXXXXXXXX
sambaPwdLastSet: 1766458826
sambaSID: S-1-5-21-1595148958-61504898-393552916-21000
loginShell: /bin/bash
homeDirectory: /home/hinananoha
gecos: $firstname hinananoha
uid: hinananoha
cn: hinananoha
uidNumber: 10000
gidNumber: 10000
userPassword:: XXXXXXXXXXX[MASKED]XXXXXXXXXXXX
sn: hinananoha
structuralObjectClass: inetOrgPerson
entryUUID: 58226f7e-73f7-1040-9164-8b1d10ac8f32
creatorsName: cn=admin,dc=hinananoha,dc=example,dc=com
createTimestamp: 20251223030055Z
entryCSN: 20251223030055.725008Z#000000#000#000000
modifiersName: cn=admin,dc=hinananoha,dc=example,dc=com
modifyTimestamp: 20251223030055Z

あとは、Wi-Fi APでRADIUSの設定を実施すれば、Windowsからは特に何も気にすることなくWPA2/WPA3-EAPが利用可能になります。

解説

ここからは、なぜこれらの手順が必要で、その上で何故この設定で実現可能なのかを簡単に解説します。

WindowsでのWPA2/WPA3-EAPの(非明示な)仕様

Windows/Android/Linux/iOSに関係無く、現代のクライアント端末では通常、WPA2/WPA3-EAP認証は利用可能となっています。
EAP認証には様々な種類があり、証明書認証/コンピュータ認証/ユーザ・パスワード認証などがありますが、ここではユーザ・パスワード認証(接続時にユーザ名とパスワードを聞かれるもの)について説明します。

ユーザ・パスワード認証タイプのEAP方式には主にPEAPEAP-TTLSがあります。
PEAPEAP-TTLSもどちらも「認証をするときの暗号化トンネリング方式」を指しており、実際にはその中で更に認証方式(EAP Method)があります。
PEAPの場合はユーザ・パスワード認証が使えるのはMS-CHAPv2、EAP-TTLSの場合はPAP, CHAP, MS-CHAP(非推奨), MS-CHAPv2などがあります。
問題はこの認証方式で、Windowsは「仕様上は」何れの認証方法も*3使うことが出来ます。しかし、非明示な仕様として、(少なくともOpenLDAP+freeRADIUSで構築されたRADIUSサーバに対しては)WindowsEAP認証を行う場合、普通にUI上からWi-Fi接続を選択して接続した場合、PEAP/EAP-TTLSの何れの場合も、MS-CHAPv2での認証に失敗した場合「認証に失敗した」として、それ以外の認証方法(PAP/CHAP)を試行することなく接続を終了します。また、なぜか設定から認証方式をPAP/CHAPなどを選択しても「まず」MS-CHAPv2を試行し、認証に失敗した場合PAP/CHAPを実施することなく接続を終了します(意☆味☆不☆明)*4

もしかしたらこれを解決する設定方法があるのかもしれませんが、少なくとも一般のユーザがストレスなく接続する方法はなさそうです。何らかの理由で謎の意味不明な仕様がなくなったとしても、割と面倒な設定を経ないと使えないためです。

というわけで、WindowsからWPA2/WPA3-EAPを使う場合は、事実上認証方式はPEAP(MS-CHAPv2)か、EAP-TTLS(MS-CHAPv2)に限られる訳です。

MS-CHAPv2認証に必要なもの

ここで、OpenLDAP+freeRADIUSの構成の話に戻ります。
何れのEAP方式においても、MS-CHAPv2で認証を行う場合に必要なものがあります。それがNTPasswordです。これは、OpenLDAPのユーザアカウントが通常有しているハッシュ化されたuserPasswordからは生成できません*5。そのため、freeRADIUSは「平文のパスワードから」NTPasswordを生成するのですが、現在、普通にOpenLDAPを設定すると、平文のパスワードはOpenLDAPに保存されませんし、当然LDAPは平文のPasswordは持つべきではありません*6

ちなみに、OpenLDAPと連携せず、freeRADIUS単体でPEAP(MS-CHAPv2) などを実現するのは簡単です。freeRADIUSで読み取れるアカウント情報を記載したファイルからNTPasswordを生成出来るためです。

商業サービスはどうやって実現しているの?

さて、商業サービスはどうやって実現しているか。答えは当然、Active Directoryです。当然、MS製品の認証をするわけなので、MS製品のIdPを使うのが自然ですね。
ところが、ADの構築は(したことがある方はご存じの通り)かな~り面倒臭いです。少なくともたかがWi-Fiの認証のためにやるのは無駄骨もいいところです。
Windows Serverで実現する方法は勿論、LinuxでもSambaのSamba AD/DCを使えばADは動くのですが、これをフルで構築するのはかなり面倒です。
さて、本当にWi-Fiの認証のために、フルスペックのAD/DCは必要なんでしょうか。

OpenLDAPに「最低限」必要な項目

さて、OpenLDAP+freeRADIUS連携でMS-CHAPv2認証を通すのに最低限必要な項目は何か。
それは、sambaSamAccoutオブジェクトクラスのsambaNTPasswordのみです。

さて、OpenLDAPにおいてそのsambaSamAccountオブジェクトクラスで必須とされているディレクティブは何か。それは、この記事の導入方法で出てきたここに答えが書いてあります。

~$ cd /usr/share/doc/samba/examples/LDAP/
/usr/share/doc/samba/examples/LDAP$ sudo ldapadd -Y EXTERNAL -H ldapi:/// -f samba.ldif
SASL/EXTERNAL authentication started
SASL username: gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth
SASL SSF: 0
adding new entry "cn=samba,cn=schema,cn=config"

ここで登場したsamba.ldifを開いて見ると、こんなことが書かれています。

olcObjectClasses: {0}( 1.3.6.1.4.1.7165.2.2.6 NAME 'sambaSamAccount' DESC 'Sam
 ba 3.0 Auxilary SAM Account' SUP top AUXILIARY MUST ( uid $ sambaSID ) MAY (
 cn $ sambaLMPassword $ sambaNTPassword $ sambaPwdLastSet $ sambaLogonTime $ s
 ambaLogoffTime $ sambaKickoffTime $ sambaPwdCanChange $ sambaPwdMustChange $
 sambaAcctFlags $ displayName $ sambaHomePath $ sambaHomeDrive $ sambaLogonScr
 ipt $ sambaProfilePath $ description $ sambaUserWorkstations $ sambaPrimaryGr
 oupSID $ sambaDomainName $ sambaMungedDial $ sambaBadPasswordCount $ sambaBad
 PasswordTime $ sambaPasswordHistory $ sambaLogonHours ) )

MUST ( uid $ sambaSID ) MAY (...と書いてあるので、オブジェクトクラスとしてはsambaSIDだけあればOKです。
ということは、この記事の内容を実現するのに最低限必要なのは、sambaSIDとsambaNTPasswordだけです。

そのため、ここまで丁寧に記事を読んだ諸兄の中で、アカウントを作る際に毎度毎度LDIFファイルを作る事が苦でない変態優秀な方や、LDIFファイルを自動で生成してアカウントを作るスクリプトを組むことに快感を覚える方などは、実は「sambaの設定(samba-ad-dc)」以降は、任意の手段でNTPasswordさえ生成できれば、もう少し楽(?)な方法で実現が可能です。

smbldap-tools の死

さらに優秀な諸兄や、長年ネットワークに触れている方には、「そういうのを良い感じにしてくれるsmbldap-toolsってのがあったでしょ」と言われることでしょう。はい、確かにあります。ただ……。
github.com
ご覧の通り、2020年を最後に更新が止まっており、今日のLinux環境では動作しません。

実は私は以前(2017年頃)同様の必要性に駆られた際に調査した時、ちょうどsmbldap-toolsが最新版に更新され、Got Kotonakiしたことがありました。それから8年。残念ながら更新は5年前にストップし、現在では動かない物となってしまいました……。

今回LAMを使った理由

前述の通り、NTPasswordを生成する方法さえあれば、その後のSamba AD/DCがいるように見せる必要はありませんでした。では、なぜそのような無駄なあがきをしたか。それは、「再現性がある形で簡単に」できるようにしたためです。
実は「OpenLDAPに「最低限」必要な項目」に書いた内容は、2017年の調査時に、smbldap-toolsの導入後に知りまして、最終的にはLDIFファイルを自動で生成して登録するシェルスクリプトを組みました。しかし、そのシェルスクリプトはいずこに……。
今回同じ事をやってもまた同様の事態になるだろう、ということで、「よくあるLDAP管理ツールで」「フルスペックADを組まずに」「MS-CHAPv2認証が通る構成を作る」という目的で、今回の記事を書きました。

もちろん、ここまで丁寧に呼んでくださった諸兄は、前記の仕様を用いて「発展的な手段」を用いるのも良いかと思います。是非、試してみてください。

さいごに - という名の記事を完走した感想

この記事は、whywaita Advent Calendar 10周年記念 Party のLTで話した内容を記事にしたものです。

――が、当初の予定を大幅に上回って内容がみっちみちみちになってしまい、大遅刻しました。whyくん、ほんとうに申し訳ない。

実はこの記事の内容に相当する内容は、前述の2017年頃に構築した際にブログに投稿していました。
しかし、そのブログは当時自宅鯖で運用していたWordPress上のもので、今はどこにも現存しません*7
そして、改めて今年構築することになった時調べようとしても、情報が全く出てこない。これは、記事を書いて後生に残すべきだ、と思い、執筆するに至りました。このブログが消えないことを祈っています。

書くのは滅茶苦茶大変でした。久々にこの文量の技術ブログ記事を書きました。そして、何より、まだ現在、冬コミの原稿が仕上がっていないのが一番ピンチです。無事、私は冬コミの新刊が出来るのでしょうか。

その結果は、是非 2025年12月31日に、東京ビッグサイトで開催されるコミックマーケット107 2日目 2日目南h-18ab 「嬉野ネットワークサービス」にてご確認下さい(露骨な宣伝)

おまけ

Zabbix Serverのスクリーンショット

このクソ忙しいタイミングでサーバのうちの1台が、RAIDを構成するディスクの1台が死んだって言ってきました*8
ディスク故障は、忙しい時にやってきます。RAID/バックアップ/冗長化は大事です、という話でオチを付けたいと思いマス。

*1:諸説あります

*2:年々雑になる導入

*3:MS-CHAPはもう使えなかったかもしれない

*4:本当に意味不明。昔試したときに本当に訳が分からなくてモニタをぶん殴った記憶がある

*5:言うまでも無く、ハッシュ化されたものから作ったハッシュで、元のパスワードに対して認証が通るのは訳がわからないから、当然です

*6:この記事を書くに当たって他の記事を調べると、平文のPasswordをLDAPに持たせるように指示しているものがあり、恐怖に震えました

*7:一応Web Archiveは残ってました。今回の構築の際にだいぶ参考になった。過去の私、ありがとう。https://web.archive.org/web/20160516110234/http://blog.estel-freesia.moe/2016/05/156/

*8:iLOを見たらPredictive failureなので「もうすぐ死ぬかも」なのでまだ比較的マシだけど、数日中には対応します