PostgreSQL をバックエンドとした FreeRADIUS を構築する

# 概要
ある装置を利用する際、必要な権限が付与されているか確認するために認証が行われる。一台だったら、あるいは数台ならそこまで大きな問題にはならない。でも、それが数十台の機器で、何人ものユーザがいて、しかもそのユーザが頻繁に入れ替わるのなら？ 一台ごとに設定することは現実的でなくなる。共通パスワードにすることでメンテナンス問題は回避できるが、「誰が触ったかわからない」「流出してもすぐに対応できない」といった問題が残る。

というわけで認証をまとめて行う認証サーバの需要が発生する。 

## 認証方式
Network 機器の認証方式としては

* RADIUS
* TACACS+
* LDAP
* Diameter

をよく見かける。この資料では、RADIUS の Open Source 実装である FreeRADIUSを使用した構築を行う。

認証以外にも認可を精密に制御するとなると、TACACS+が候補に挙がる。Open Source 実装としては tac_plus があるが、2015年1月を最後に更新が止まっており、Ubuntu では 18.04から、CentOS でも 7からはそれぞれのパッケージ管理システムから外されている。いくらOSSとはいえ、メンテナンスが止まっている実装は使いたくない。そもそも自分でビルドして行うことになる。プロプライエタリの実装として Cisco も出していたが、そっちも販売が終わっている。

Diameter は 3GPP な世界でよく使われている。追加属性を容易に定義できるので、RADIUSよりも使い勝手が良いらしい。ただ、ピュアな Network 機器としてはあまり見かけない。OSS 実装として FreeDiameter があるが、CentOS系がターゲットとして開発されており、Ubuntu だとビルドが困難な印象。私が以前試した限りではビルドできなかった。
LDAP はもともとサーバ系で使われていたが、近年 Network機器でも対応されるようになってきた。つまり、古い奴だと対応していない。

# 構築
## 前提となる環境
| 主なソフト | Version | 備考 |
|:-:|:-:|:-:|
| Ubuntu  | 20.04  | 検証時最新安定版|
| FreeRADIUS  | 3.0.20  | aptから入手 |
| PostgreSQL  | 12.6  | aptから入手 |

PostgreSQL は Optional であり、FreeRADIUS単体で動作する。この場合、ユーザアカウントはconfファイルに書いていくことになるが、ユーザ数が増えると管理が煩雑になる。それを見越して Database と連携させた。

FreeRADIUS の設定は基本的にコマンドラインで頑張ることになるが、GUIをサポートさせた拡張パッケージ、daloRADIUS も存在する。これを利用するには Database 連携が必須となる。daloRADIUS, FreeRADIUS で使用する Database で対応しているものは事実上 MySQL のみ。公式サイトには PostgreSQL も含まれていた。有償サポートで捻じ込めば対応してもらえるのかもだが、かなりハードルは高そう。

## パッケージのインストール
全て APTから落とせる。野良ファイルは不要。

```shell
sudo apt install freeradius freeradius-utils freeradius-postgresql 
sudo apt install postgresql
```

## モジュールの有効化
PostgreSQLに限らず、DBと連携させる際は`mods-enabled`へ`SQL`モジュールを登録する必要がある。

```shell
sudo ln -s ../mods-available/sql /etc/freeradius/3.0/mods-enabled/sql
```
## PostgreSQLの用意
### FreeRADIUS用アカウント & DB作成
```shell
sudo -u postgres createuser radius --createdb --pwprompt
```
ここでは新しいユーザとして`radius`を作成。FreeRADIUSからのアクセス用としてパスワードも合わせて設定。

```shell
createdb -U radius radius
sudo cp /etc/freeradius/3.0/mods-config/sql/main/postgresql/schema.sql .
sudo chown 自アカウント ./schema.sql
psql radius -u radius < ./schema.sql
```
FreeRADIUSで必要となるスキーマファイルはすでに用意されている。ただ、アクセス権の都合で直接は流し込み出来ない。そのため、`schema.sql`を自分の操作できるディレクトリにコピーし、適切な権限を与えた上で流し込みを実施する。
ただ、このあたりの設定はPostgreSQLのセキュリティの壁に阻まれて結構面倒くさい。実際にはpostgres権限でschema.sqlを流し込み、ALTER TABLEでradiusユーザにオーナ変更した。あるべき姿が分かったら別途更新。



## RADIUS の Config修正
### sites-available/default と inner-tunnel
コメントアウト(`# SQL`)や-されたところ（`-SQL`）をすべて有効化（`SQL`）に変更する。これを有効にすることで、`mods-enabled`にシンボリックリンクを張った`SQL`モジュールが参照されるようになる。

```shell
sudo vi /etc/freeradius/3.0/sites-available/default
sudo vi /etc/freeradius/3.0/sites-available/inner-tunnel
```

### mods-available/sql
実際、どこのPostgreSQLと接続するかを設定する。

```shell
sudo vi /etc/freeradius/3.0/mods-available/sql
```

| 変数| 設定値 | 備考 |
|:-:|:-:|:-:|
| dialect  | postgresql | |
| driver  | rlm_sql_postgresql | |
| server | localhost |  |
| port | 5432 |  |
| login | radius |  |
| password | radpass |  |
| radius_db | radius |  |
| read_clients | yes |  |
| client_table | nas |  |

`driver`の設定値サンプルがConfig内にいくつかあった。postgresqlはサンプルになかったが、設定できた。

# 動作テスト
## デバッグモードでの実行
```shell
sudo systemctl stop freeradius
sudo freeradius -X
(snip)
Ready to process requests ← これがでれば成功
```
`freeradius -X`はデバッグモードでの実行。UbuntuではFreeRADIUSが自動起動されているため、明示的に止めてあげる。`Ready to process requests`が出ていれば正しく動作している。

```shell
sudo systemctl restart freeradius
```
デバッグモードを`Ctrl+C`で一度停止し、再実行。

## サンプルアカウントの登録
```shell
psql -U radius radius
radius=# \dt
             List of relations
 Schema |     Name      | Type  |  Owner
--------+---------------+-------+----------
 public | nas           | table | radius
 public | radacct       | table | radius
 public | radcheck      | table | radius
 public | radgroupcheck | table | radius
 public | radgroupreply | table | radius
 public | radpostauth   | table | radius
 public | radreply      | table | radius
 public | radusergroup  | table | radius
(8 rows)

radius=# \d radcheck
                                    Table "public.radcheck"
  Column   |         Type         | Collation | Nullable |               Default
-----------+----------------------+-----------+----------+--------------------------------------
 id        | integer              |           | not null | nextval('radcheck_id_seq'::regclass)
 username  | text                 |           | not null | ''::text
 attribute | text                 |           | not null | ''::text
 op        | character varying(2) |           | not null | '=='::character varying
 value     | text                 |           | not null | ''::text
Indexes:
    "radcheck_pkey" PRIMARY KEY, btree (id)
    "radcheck_username" btree (username, attribute)

radius=# INSERT INTO radcheck VALUES (1, 'shakapon', 'Cleartext-Password', ':=', '12345');
INSERT 0 1
radius=# select * from radcheck;
 id | username |     attribute      | op | value
----+----------+--------------------+----+-------
    1 | shakapon | Cleartext-Password | := | 12345
(1 rows)

radius=# \q
```

### 認証テスト
```shell
sudo radtest shakapon 12345 localhost 10 testing123
Sent Access-Request Id 210 from 0.0.0.0:53286 to 127.0.0.1:1812 length 74
        User-Name = "shakapon"
        User-Password = "12345"
        NAS-IP-Address = 127.0.1.1
        NAS-Port = 10
        Message-Authenticator = 0x00
        Cleartext-Password = "12345"
Received Access-Accept Id 114 from 127.0.0.1:1812 to 127.0.0.1:46494 length 20
```

動作テストが終わったら`Ctrl+C`で止めた後、プロセスを再起動すること（startでも良いと思うけど、検証ではrestartとした）。

```shell
sudo systemctl restart freeradius
```


## Database上に作られるTableの意味
MySQL/PostgreSQLとも、作られるテーブルに差分なし（多分）。インストール用のschema.sqlを見る限り差分なかった。

| テーブル名| 設定内容 | 備考 |
|:-:|:-:|:-:|
|nas|RADIUSクライアントを定義|NASアドレスとshared keyもここで定義 |
|radacct | アカウンティングログ | 利用開始/終了の時間とか|
|radcheck |認証するユーザとパスワード | 検証では平文を使用|
|radgroupcheck | グループごとのradcheck | 多分、NAS側から投げられたパケットの属性と思われる|
|radgroupreply | リプライに設定する設定値をグループごとに設定| |
|radpostauth | 認証結果ログ | |
|radreply | リプライに設定する設定値をグループごとに設定| |
|radusergroup | ユーザとGroupを紐づけ | 1ユーザを複数グループに紐づけられるかは未調査|

### NASテーブル

| 列名| タイプ | 意味| サンプル |
|:-:|:-:|:-:|:-:|
| id          | integer | ID        | 1 | 
| nasname     | text    | 接続元NASアドレス          | 192.168.1.0/24 |
| shortname   | text    | NASアドレス名称          | HOME-LAB |
| type        | text    | NASの種類          | other | 
| ports       | integer | Port番号          |          |
| secret      | text    | 共通鍵          | Shakapon |
| server      | text    | 調査中          |          |
| community   | text    | 調査中          |          |
| description | text    | デスクリプション          |          |

各列名の意味は`/etc/freeradius/3.0/clients.conf`と共通。typeとかはデフォルト`other`だが、ほかにも`cisco`とか`juniper`とかが入る。ここでアドレスごとにVSAが決まってくれているのなら、色々と楽かもしれない。`server`や`community`は調査中。PPPoEとかの認証でも使えるため、より大規模な利用目的があるのかな？

他のテーブルは、見ればまぁなんとか。

## Network機器からの認証
### Network機器検証用追加データ入力
認証の検証を行うNetwork機器として今回はVyOSを採用した。ただし検証時点で下記をケアする必要がある。

> If you want to have admin users to authenticate via RADIUS it is essential to sent the Cisco-AV-Pair shell:priv-lvl=15 attribute. Without the attribute you will only get regular, non privilegued, system users.
> https://docs.vyos.io/en/latest/configuration/system/login.html?highlight=login

とのことで、VSAとして`Cisco-AVPair=shell:priv-lvl=15`を設定する必要がある。これをやらないと```show configuration```すらできなかった。

この紐づけを行うには`radreply`テーブルか、`radgroupreply`テーブルで属性を定義する必要がある。`radreply`テーブルはユーザごと、`radgroupreply`はグループごと。ラボなのでそう増えることもないが、将来を見越して`radgroupreply`を使用する。

* ユーザ`Shakapon`はグループ`HomeLAB`に所属する。
* グループ`HomeLAB`は属性値`Cisco-AVPair='shell:priv-lvl=15'`を持つ。
という感じ。Operatorとして`:=`とする必要があった。


```sql
INSERT INTO NAS(id, nasname, shortname,type,secret) values(1, '192.168.1.0/24', 'HomeLAB','other','Shakapon')
INSERT INTO radusergroup values(1, 'shakapon' ,'HomeLAB' ,0);
INSERT INTO radgroupreply values(1, 'HomeLAB', 'Cisco-AVPair',':=','shell:priv-lvl=15');
```
NASテーブルをいじっても即時反映はされない（多分）。FreeRADIUSの再起動が必要だった。

### VyOSの設定
```shell
# 帯域外監視設定
set vrf name Mgmt
set vrf name Mgmt table 100
set interface ethernet eth0 address 192.168.1.249/24
set interface ethernet eth0 description "Out-Of-Band Management"
set interface ethernet eth0 vrf Mgmt
set protocols static route 0.0.0.0/0 next-hop 192.168.1.1 vrf Mgmt 

# 認証設定
set system login radius server 192.168.1.250 key Shakapon
set system login radius vrf Mgmt
```
気が付いたらVyOSで帯域外監視ができるようになっていた。せっかくなので使う。Inbound監視はできるだけ避けたい。