Cisco WLC と FreeRADIUS を利用した EAP-TLS認証

## はじめに
このエントリはCiscoWLCとFreeRADIUSを利用した、EAP-TLS認証の手順について記載します。

## 更新履歴
2020/1/29 パラメータの表記を一部修正しました。

## 対象機器および環境

* Cisco Virtual Wireless LAN Controller (Ver.8.2.x)
* CentOS7(7.6.1810)
* OpenSSL(1.0.2k)
* FreeRADIUS(3.0.13)

## このエントリを読むとできること

* OpenSSLで構築したルートCAと中間CAを利用して
* サーバ証明書とクライアント証明書を発行して
* WLCとFreeRADIUSを利用した
* EAP-TLS認証ができるようになる

はず。

## 関連するエントリ
[OpenSSLでプライベート認証局の構築（ルートCA、中間CA）](https://qiita.com/haruca_tech/items/ac5ece9618a613f37ce5)
[Cisco WLCにSSIDを作成する（802.1X/PSK） コマンド編＋よくつかうコマンド](https://qiita.com/haruca_tech/items/088dda5d2110cf0962a8)
[CentOS7で各種ネットワークサービスを基本設定したメモ（dhcp/radius/proxy/tftp/syslog）](https://qiita.com/haruca_tech/items/2bf96ae84ebb5c84ba7d)


## 前提条件

サーバ証明書はルートCAから発行
クライアント証明書は中間CAから発行
という構成にしています。

クライアントの増減のたびにルートCAのお世話になるのはどうも違うと思います。中間CAにクライアント証明書の発行の権限を持たせるイメージです。

## 手順

## Cisco WLCの設定
WLCでは以下で作成したような802.1X認証用のSSID作成などが必要です。
[Cisco WLCにSSIDを作成する（802.1X/PSK） コマンド編＋よくつかうコマンド](https://qiita.com/haruca_tech/items/088dda5d2110cf0962a8)
[Cisco WLCにSSIDを作成する（802.1X/PSK）](https://qiita.com/haruca_tech/items/0128e00b792e1ac6ab4b)

## チェーン証明書の作成

FreeRADIUSでの証明書配置や、WindowsクライアントのためのPKCS#12作成時に利用するための、チェーン証明書（ルートCA＋中間CA）を先に作成しておく。

```
cd /opt/pki/
openssl x509 -in RootCA/RootCA_crt.pem -out chainCA_crt.pem
openssl x509 -in InterCA/InterCA_crt.pem >> chainCA_crt.pem
```



## FreeRADIUSサーバ
`VMnetserv01` というホスト名に対して発行しています。

### サーバ証明書発行
RootCAの秘密鍵のパスワードは、 **[前回構築](https://qiita.com/haruca_tech/items/ac5ece9618a613f37ce5)** した文字列をそのまま利用しています。

```
(VMnetserv01というサーバに対して発行するので、新規ディレクトリを作成する）
mkdir -p /opt/pki/Server/VMnetserv01
cd /opt/pki/Server/VMnetserv01

(パスワード無し秘密鍵とCSRを同時に発行しています)
openssl req  -new \
 -out VMnetserv01_csr.pem \
 -newkey rsa:2048 \
 -nodes \
 -keyout VMnetserv01_key.pem \
 -subj "/C=JP/ST=Tokyo/O=EXAMPLE/CN=VMnetserv01.example.net"

(RootCAで署名します)
cd /opt/pki/RootCA
openssl ca -config ../configs/openssl_sign.cnf -batch -extensions v3_server \
 -out ../Server/VMnetserv01/VMnetserv01_crt.pem \
 -in  ../Server/VMnetserv01/VMnetserv01_csr.pem \
 -cert RootCA_crt.pem \
 -keyfile RootCA_key.pem \
 -passin pass:rootcaprivkeypass

(余計な文字列を取り除きます)
cd /opt/pki/Server/VMnetserv01
openssl x509 -in VMnetserv01_crt.pem -out VMnetserv01_crt.pem
```

### FreeRADIUS設定

```
(証明書を収容するディレクトリを新規に作成します)
mkdir -p /etc/raddb/p2certs

(DHファイルの作成)
openssl dhparam -5 -out /etc/raddb/p2certs/dh 1024

(各証明書を配置していきます)
cp /opt/pki/Server/VMnetserv01/VMnetserv01_key.pem /etc/raddb/p2certs
cp /opt/pki/Server/VMnetserv01/VMnetserv01_crt.pem /etc/raddb/p2certs
cp /opt/pki/chainCA_crt.pem /etc/raddb/p2certs
```

　
証明書ディレクトリの設定を変更

```:/etc/raddb/radiusd.conf
-----8<-----snip-----8<-----
certdir = ${confdir}/p2certs
cadir   = ${confdir}/p2certs
-----8<-----snip-----8<-----
```

　
eapセクション中の default_eap_type が md5となっていても、tlsとなっていても、デフォルトのEAPが通らない場合はNAKレスポンス中にステーション側のEAP方式を返すらしいので、RADIUSサーバ側で適切なEAP方式を試行するっぽい。

```:/etc/raddb/mods-available/eap
-----8<-----snip-----8<-----
eap {
        #default_eap_type = md5
        default_eap_type = tls

        tls-config tls-common {
                #private_key_password = whatever
                #private_key_file = ${certdir}/server.pem
                #certificate_file = ${certdir}/server.pem
                #ca_file = ${cadir}/ca.pem
                private_key_file = ${certdir}/VMnetserv01_key.pem
                certificate_file = ${certdir}/VMnetserv01_crt.pem
                ca_file = ${cadir}/chainCA_crt.pem
	}
}
-----8<-----snip-----8<-----
```


## クライアント

### クライアント証明書発行
InterCAの秘密鍵のパスワードは、 **[前回構築](https://qiita.com/haruca_tech/items/ac5ece9618a613f37ce5)** した文字列をそのまま利用しています。

```
(Client01というクライアントに対して発行するので、新規ディレクトリを作成する）
mkdir -p /opt/pki/Client/Client01
cd /opt/pki/Client/Client01

(パスワード無し秘密鍵とCSRを同時に発行しています)
openssl req -new \
 -out Client01_csr.pem \
 -newkey rsa:2048 \
 -nodes \
 -keyout Client01_key.pem \
 -subj "/C=JP/ST=Tokyo/O=EXAMPLE/CN=Client01.example.net"

(InterCAで署名します)
cd /opt/pki/InterCA
openssl ca -config ../configs/openssl_sign.cnf  -batch -extensions v3_client \
 -out ../Client/Client01/Client01_crt.pem \
 -in  ../Client/Client01/Client01_csr.pem \
 -cert InterCA_crt.pem \
 -keyfile InterCA_key.pem \
 -passin pass:intercaprivkeypass

(余計な文字列を取り除きます)
cd /opt/pki/Client/Client01
openssl x509 -in Client01_crt.pem -out Client01_crt.pem

(Windowsで利用するため、PKCS#12：秘密鍵と証明書をまとめたファイルを作成します)
(-passoutでエクスポート用パスワード "exportpass" を与えています）
openssl pkcs12 -export \
 -inkey Client01_key.pem \
 -in Client01_crt.pem \
 -certfile ../../chainCA_crt.pem \
 -out Client01_crt.p12 \
 -passout pass:exportpass
```

### Windowsクライアント設定

作成したp12ファイルをWindowsクライアントへ転送する。
（画面ではWinSCPを利用）
![image.png](https://qiita-image-store.s3.amazonaws.com/0/334782/2754a937-8770-595a-9f3b-95f957d3db0c.png)
　

転送したp12ファイルをダブルクリック
![image.png](https://qiita-image-store.s3.amazonaws.com/0/334782/ae742876-f560-1de2-2a80-64b4a81b9982.png)
　

そのまま「次へ」をクリック
![image.png](https://qiita-image-store.s3.amazonaws.com/0/334782/343f4656-8840-bf26-06b8-8db737cc6657.png)

　
p12エクスポート時に設定した-passout の文字列を入力する。
（先ほどの例では "exportpass" という文字列）
![image.png](https://qiita-image-store.s3.amazonaws.com/0/334782/4e36a7da-1874-b2e0-6053-9aa6960f3d59.png)

　
そのまま「次へ」をクリック
![image.png](https://qiita-image-store.s3.amazonaws.com/0/334782/a49ce507-1e2b-4080-31df-1b3ef9945db5.png)

　
そのまま「完了」をクリック
![image.png](https://qiita-image-store.s3.amazonaws.com/0/334782/74f93db4-f925-a916-7b96-ade27999455c.png)

　
問題なくインポートできれば、以下のメッセージが表示される。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/334782/3fecfeef-9374-8f97-cdf0-948f2ae57ece.png)

　
Ctrl+Rキーで「mmc」と入力して「OK」をクリック
![image.png](https://qiita-image-store.s3.amazonaws.com/0/334782/1c54513c-ab7f-6eb8-abe0-c1ed5851cb06.png)

　
「ファイル」→「スナップインの追加と削除」をクリック
![image.png](https://qiita-image-store.s3.amazonaws.com/0/334782/9b724fc1-69a0-bfec-d3ec-c3354e4eb1d3.png)

　
最下段の「証明書」をダブルクリックして「コンピュータアカウント」を選択し、「次へ」をクリック
![image.png](https://qiita-image-store.s3.amazonaws.com/0/334782/4dae9244-b4f3-cadd-4862-6fb3db775d7a.png)

　
「ローカルコンピュータ」を選択し「完了」をクリック
![image.png](https://qiita-image-store.s3.amazonaws.com/0/334782/9b96ae1d-66d0-d64b-f98f-23754ceb163b.png)

　
選択されたスナップインに 証明書（ローカルコンピュータ）があることを確認して「OK」をクリック
![image.png](https://qiita-image-store.s3.amazonaws.com/0/334782/7d5aa609-b633-0674-13a3-af76647ee570.png)

　
左側メニューの「証明書」→「個人」→「証明書」をクリックし、作成したクライアント証明書が存在するか確認。
証明書をダブルクリックして、「証明書パス」のタブをクリック
証明書パスの画面で「×」印がついておらず、証明書の状態が「この証明書は問題ありません」となっているれば問題なく導入されている。
「OK」をクリックして、閉じる。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/334782/78cbbb21-37de-2044-dd25-b2890bce6f42.png)

　
Ctrl+Rをクリックして、 `control.exe /name Microsoft.NetworkAndSharingCenter` と入力して、「OK」をクリック
![image.png](https://qiita-image-store.s3.amazonaws.com/0/334782/f2e75da5-aae5-eda6-430b-2bdcf5ed4e1d.png)

　
中央付近の「新しい接続またはネットワークのセットアップ」をクリック
![image.png](https://qiita-image-store.s3.amazonaws.com/0/334782/0fb52965-679a-c940-526c-b6918a666e2f.png)

　
「ワイヤレスネットワークに手動で接続します」を選択して「次へ」をクリック
![image.png](https://qiita-image-store.s3.amazonaws.com/0/334782/cbe71684-99b8-521a-d5d0-8f0185981f7b.png)

　
「ネットワーク名」にSSIDを入力（例ではIntraGeneral）
自動的に開始するかどうかはお好みで。
SSIDをステルス化していれば「ネットワークがブロードキャストを行っていない～～」にチェックを入れ「次へ」をクリック
![image.png](https://qiita-image-store.s3.amazonaws.com/0/334782/fbf39cfe-12ae-28df-bcba-0dbca8b01461.png)

　
「接続の設定を変更します」をクリック
![image.png](https://qiita-image-store.s3.amazonaws.com/0/334782/7956c028-f296-d537-dd71-960fe50ad896.png)

　
表示される画面で「セキュリティ」タブをクリック
ネットワークの認証方法の選択を「Microsoft: スマートカードまたはその他の証明書」を選択し、隣の「設定」をクリック
![image.png](https://qiita-image-store.s3.amazonaws.com/0/334782/397857e0-fc96-40ed-570d-1893d1b392a6.png)

信頼されたルート証明機関にルートCAのチェックを入れる（例ではexample Root CA）
「新しいサーバまたは信頼された証明機関を～～」にチェックを入れ「OK」をクリック
![image.png](https://qiita-image-store.s3.amazonaws.com/0/334782/bf8f5637-bb16-22da-ce84-da6461996fc7.png)

　
もとの画面に戻るので、「詳細設定」をクリックすると、下記の画面が表示される。
「802.1Xの設定」タブで、認証モードを指定するにチェックを入れ、プルダウンメニュから「コンピュータの認証」を選択し、「OK」をクリック
![image.png](https://qiita-image-store.s3.amazonaws.com/0/334782/b6e9ab62-2d6f-29c7-347b-6ad9e5ce25fc.png)


その後、ほかの設定画面も「OK」「閉じる」ですべて閉じる。
ここまででWindowsの無線設定が完了しました。


## 動作確認
最初にRADIUSサーバをデバッグモードで起動させてみて、問題なく認証されるかを確認する。

```
（radiusサービスが停止していることを確認する）
# systemctl status radiusd
● radiusd.service - FreeRADIUS high performance RADIUS server.
   Loaded: loaded (/usr/lib/systemd/system/radiusd.service; enabled; vendor preset: disabled)
   Active: inactive (dead) since 木 2019-01-03 19:38:23 JST; 3h 39min ago
 Main PID: 6132 (code=exited, status=0/SUCCESS)

 1月 03 19:38:23 VMnetserv01.example.net systemd[1]: Stopping FreeRADIUS high performance RADIUS server....
 1月 03 19:38:23 VMnetserv01.example.net systemd[1]: Stopped FreeRADIUS high performance RADIUS server..
Warning: Journal has been rotated since unit was started. Log output is incomplete or unavailable.
#

（radiusサーバをデバックモードで起動しておく）
# radiusd -X
（ばばばー、って表示されるあ、そのまま流しておく）
```

　
タスクトレイから先ほど作成したSSIDを選択し、接続する。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/334782/e2591082-a6ce-0e78-1fb6-e1f6b98b0a71.png)

デバッグモードで起動したradiusのログの最後のほうに、
`Login OK: [host/Client01.example.net] (なんちゃら～～)`
と表示されていれば、認証できている。

　
認証後は、設定したSSIDに接続されているはず
![image.png](https://qiita-image-store.s3.amazonaws.com/0/334782/b25c5a0c-f604-24f5-0b2a-73dc4a6fab9a.png)


　
認証成功時のradiusログは以下のようなもの

```
(20) Received Access-Request Id 53 from 10.254.10.201:50312 to 10.254.10.251:1812 length 302
(20)   User-Name = "host/Client01.example.net"
(20)   Chargeable-User-Identity = 0xc9
(20)   Location-Capable = Civic-Location
(20)   Calling-Station-Id = "40-e2-30-af-55-79"
(20)   Called-Station-Id = "d4-a0-2a-11-d5-a0:IntraGeneral"
(20)   NAS-Port = 1
(20)   Cisco-AVPair = "audit-session-id=c90afe0a00000078b3192e5c"
(20)   Acct-Session-Id = "5c2e19b3/40:e2:30:af:55:79/168"
(20)   NAS-IP-Address = 10.254.10.201
(20)   NAS-Identifier = "VMwlc01"
(20)   Airespace-Wlan-Id = 1
(20)   Service-Type = Framed-User
(20)   Framed-MTU = 1300
(20)   NAS-Port-Type = Wireless-802.11
(20)   Tunnel-Type:0 = VLAN
(20)   Tunnel-Medium-Type:0 = IEEE-802
(20)   Tunnel-Private-Group-Id:0 = "4001"
(20)   EAP-Message = 0x020700060d00
(20)   State = 0x47ff5d6b42f8509cdd59458a7ab664e0
(20)   Message-Authenticator = 0x4e520e371412faf3d280e4d28edc2686
(20) session-state: No cached attributes
(20) # Executing section authorize from file /etc/raddb/sites-enabled/default
(20)   authorize {
(20)     policy filter_username {
(20)       if (&User-Name) {
(20)       if (&User-Name)  -> TRUE
(20)       if (&User-Name)  {
(20)         if (&User-Name =~ / /) {
(20)         if (&User-Name =~ / /)  -> FALSE
(20)         if (&User-Name =~ /@[^@]*@/ ) {
(20)         if (&User-Name =~ /@[^@]*@/ )  -> FALSE
(20)         if (&User-Name =~ /\.\./ ) {
(20)         if (&User-Name =~ /\.\./ )  -> FALSE
(20)         if ((&User-Name =~ /@/) && (&User-Name !~ /@(.+)\.(.+)$/))  {
(20)         if ((&User-Name =~ /@/) && (&User-Name !~ /@(.+)\.(.+)$/))   -> FALSE
(20)         if (&User-Name =~ /\.$/)  {
(20)         if (&User-Name =~ /\.$/)   -> FALSE
(20)         if (&User-Name =~ /@\./)  {
(20)         if (&User-Name =~ /@\./)   -> FALSE
(20)       } # if (&User-Name)  = notfound
(20)     } # policy filter_username = notfound
(20)     [preprocess] = ok
(20)     [chap] = noop
(20)     [mschap] = noop
(20)     [digest] = noop
(20) suffix: Checking for suffix after "@"
(20) suffix: No '@' in User-Name = "host/Client01.example.net", looking up realm NULL
(20) suffix: No such realm "NULL"
(20)     [suffix] = noop
(20) eap: Peer sent EAP Response (code 2) ID 7 length 6
(20) eap: No EAP Start, assuming it's an on-going EAP conversation
(20)     [eap] = updated
(20) files: users: Matched entry DEFAULT at line 1
(20) files: users: Matched entry DEFAULT at line 1
(20) files: users: Matched entry DEFAULT at line 1
(20)     [files] = ok
(20)     [expiration] = noop
(20)     [logintime] = noop
(20)     [pap] = noop
(20)   } # authorize = updated
(20) Found Auth-Type = eap
(20) # Executing group from file /etc/raddb/sites-enabled/default
(20)   authenticate {
(20) eap: Expiring EAP session with state 0x47ff5d6b42f8509c
(20) eap: Finished EAP session with state 0x47ff5d6b42f8509c
(20) eap: Previous EAP request found for state 0x47ff5d6b42f8509c, released from the list
(20) eap: Peer sent packet with method EAP TLS (13)
(20) eap: Calling submodule eap_tls to process data
(20) eap_tls: Continuing EAP-TLS
(20) eap_tls: Peer ACKed our handshake fragment.  handshake is finished
(20) eap_tls: [eaptls verify] = success
(20) eap_tls: [eaptls process] = success
(20) eap: Sending EAP Success (code 3) ID 7 length 4
(20) eap: Freeing handler
(20)     [eap] = ok
(20)   } # authenticate = ok
(20) # Executing section post-auth from file /etc/raddb/sites-enabled/default
(20)   post-auth {
(20)     update {
(20)       No attributes updated
(20)     } # update = noop
(20)     [exec] = noop
(20)     policy remove_reply_message_if_eap {
(20)       if (&reply:EAP-Message && &reply:Reply-Message) {
(20)       if (&reply:EAP-Message && &reply:Reply-Message)  -> FALSE
(20)       else {
(20)         [noop] = noop
(20)       } # else = noop
(20)     } # policy remove_reply_message_if_eap = noop
(20)   } # post-auth = noop
(20) Login OK: [host/Client01.example.net] (from client example.net port 1 cli 40-e2-30-af-55-79)
(20) Sent Access-Accept Id 53 from 10.254.10.251:1812 to 10.254.10.201:50312 length 0
(20)   Tunnel-Type = VLAN
(20)   Tunnel-Medium-Type = IEEE-802
(20)   Tunnel-Private-Group-Id = "1"
(20)   MS-MPPE-Recv-Key = 0x2fffb3dd4351fa83c019ab3eaa5451386a3deb0c2cebbda985094dd7818f0e13
(20)   MS-MPPE-Send-Key = 0x1ed298b804eecfe8303731cc5a186df7bd56b678170d6f475b77acbca86cc57e
(20)   EAP-Message = 0x03070004
(20)   Message-Authenticator = 0x00000000000000000000000000000000
(20)   User-Name = "host/Client01.example.net"
(20) Finished request
```


ここまでできたら、radiusサーバを `ctrl+C` で停止させる。

```
# systemctl enable radiusd
# systemctl start radiusd
# systemctl status radiusd
● radiusd.service - FreeRADIUS high performance RADIUS server.
   Loaded: loaded (/usr/lib/systemd/system/radiusd.service; enabled; vendor preset: disabled)
   Active: active (running) since 木 2019-01-03 23:48:58 JST; 11s ago
  Process: 28658 ExecStart=/usr/sbin/radiusd -d /etc/raddb (code=exited, status=0/SUCCESS)
  Process: 28654 ExecStartPre=/usr/sbin/radiusd -C (code=exited, status=0/SUCCESS)
  Process: 28653 ExecStartPre=/bin/chown -R radiusd.radiusd /var/run/radiusd (code=exited, status=0/SUCCESS)
 Main PID: 28661 (radiusd)
   CGroup: /system.slice/radiusd.service
           mq28661 /usr/sbin/radiusd -d /etc/raddb

 1月 03 23:48:58 VMnetserv01.example.net systemd[1]: Starting FreeRADIUS high performance RADIUS server....
 1月 03 23:48:58 VMnetserv01.example.net systemd[1]: Started FreeRADIUS high performance RADIUS server..
```

WLCからは以下のように見えます

```
(Cisco Controller) >show client summary ssid ip username


Number of Clients................................ 3

MAC Address       AP Name          Status        SSID                 IP Address                       Username
----------------- ---------------- ------------- -------------------- -------------------------------- ------------------

40:e2:30:af:55:79 APbranch01-01F01  Associated   IntraGeneral         10.101.19.1                       host/Client01.example.net
50:dc:e7:b3:bf:34 APhq01-01F01      Associated   IntraStrict          10.1.19.2                         N/A
ac:63:be:92:9d:f9 APhq01-01F01      Associated   IntraStrict          10.1.19.5                         N/A

(Cisco Controller) >
```


## さいごに

### 感想
証明書失効時の動作検証などできていないため、後日整理したいと思います。

### おまけ

チェーン証明書、サーバ証明書、クライアント証明は、以下の一連のテキストコピペで発行できます。

**[前回作成のルートCAと中間CA](https://qiita.com/haruca_tech/items/ac5ece9618a613f37ce5#%E3%81%8A%E3%81%BE%E3%81%91)** のあとに発行すると、その環境のままサーバ証明書とクライアント証明書が発行できます。


```
cd /opt/pki/
openssl x509 -in RootCA/RootCA_crt.pem -out chainCA_crt.pem
openssl x509 -in InterCA/InterCA_crt.pem >> chainCA_crt.pem

mkdir -p /opt/pki/Server/VMnetserv01
cd /opt/pki/Server/VMnetserv01
openssl req  -new -out VMnetserv01_csr.pem -newkey rsa:2048 -nodes -keyout VMnetserv01_key.pem -subj "/C=JP/ST=Tokyo/O=EXAMPLE/CN=VMnetserv01.example.net"
cd /opt/pki/RootCA
openssl ca -config ../configs/openssl_sign.cnf -out ../Server/VMnetserv01/VMnetserv01_crt.pem -in ../Server/VMnetserv01/VMnetserv01_csr.pem -cert RootCA_crt.pem -keyfile RootCA_key.pem -passin pass:rootcaprivkeypass -batch -extensions v3_server
cd /opt/pki/Server/VMnetserv01
openssl x509 -in VMnetserv01_crt.pem -out VMnetserv01_crt.pem

mkdir -p /opt/pki/Client/Client01
cd /opt/pki/Client/Client01
openssl req -new -out Client01_csr.pem -newkey rsa:2048 -nodes -keyout Client01_key.pem -subj "/C=JP/ST=Tokyo/O=EXAMPLE/CN=Client01.example.net"
cd /opt/pki/InterCA
openssl ca -config ../configs/openssl_sign.cnf -out ../Client/Client01/Client01_crt.pem -in ../Client/Client01/Client01_csr.pem -cert InterCA_crt.pem -keyfile InterCA_key.pem -passin pass:intercaprivkeypass -batch -extensions v3_client
cd /opt/pki/Client/Client01
openssl x509 -in Client01_crt.pem -out Client01_crt.pem
openssl pkcs12 -export -inkey Client01_key.pem -in Client01_crt.pem -certfile ../../chainCA_crt.pem -out Client01_crt.p12 -passout pass:exportpass

```