[RADIUS]RADIUSプロトコルに関してゆるく丸くまとめてみた。

# はじめに

**Hello, World!!**

「時刻に左右されない便利な挨拶はないものか」と冒頭の書き出しから丸い表現を突き詰めた結果、IT屋の初心者がよく見るような文章になりました。

はじめましての方ははじめまして。
筆者がTalendというETLツールの技術者になってから、約3年半が経ちました。
さて、そんな筆者ですが、過去には通信系プロトコルのRADIUS(ラディウス)およびRADIUSパケットを処理するサーバーの構築・保守等を行っておりました。

今回は、筆者の持っているRADIUSの知識の再確認、および社内の同志たちへの知識の共有も兼ねて、(需要があるかはわかりませんが)**RADIUSプロトコル、および簡単なRADIUSサーバーの構築手順**についてまとめていこうと思います。

# 本記事の概要

本記事では、以下の内容を記載します。

* [RADIUSプロトコルについて](#radiusプロトコルについて)
* [RADIUSサーバーの構築](#radiusサーバーの構築)
* [疎通確認](#疎通確認)

**では早速やっていきます！！**

# RADIUSプロトコルについて

**RADIUS** (ラディウス) は、「Remote Authentication Dial In User Service」の略称です。
「円の半径」の意味を持つ英単語でもありますが、名称の由来は上記の通りです。

:::note info

ちなみに、RADIUSの後継として、DIAMETER (ダイアメーター) というプロトコルがあります。こちらは、「RADIUS (=円の半径) の後継プロトコル」であることから「円の直径」を意味する名前が付けられました。

:::

このRADIUSプロトコルですが、正式名称に"Dial"を含むことからもわかるとおり、もともとはダイヤルアップ接続のためのプロトコルとして開発、使用されていた経緯があります。
現在は拡張され、ネットワーク上のユーザ認証プロトコルの1つとして使用されています。
詳細はRFC参照。

https://datatracker.ietf.org/doc/html/rfc2865

https://datatracker.ietf.org/doc/html/rfc2866

## AAAモデル

RADIUSを語るうえで外せない概念。日本の有名な音楽グループ、ではなく

* [**Authentication**](#authentication) : 認証
* [**Authorization**](#authorization) : 認可
* [**Accounting**](#accounting) : アカウンティング

の頭文字をとったものです。

### Authentication

「認証」と訳されます。
サービスを利用とするユーザーをユーザー名等で判別し、パスワード等を用いて利用を許可または拒否するプロセスを指します。
要するに、「ログイン」です。

### Authorization

「認可」と訳されます。
上記の [Authentication](#authentication) が完了した後、そのユーザーに対して、「何をすることを許可するのか」というプロセスを指します。
Linux系OSのパーミッションや、AWSのIAMロールに触れたことのある方はイメージが付きやすいかもしれません。

### Accounting

そのまま「アカウンティング」と表現されたり、あるいは「課金」と訳されます。
たとえば、「ユーザーがいつログインしたか」「ユーザーがどのコマンドを実行したか」等、そのユーザーの行動を記録するプロセスを指します。
「行動履歴」とでも表現するのがわかりやすいと思います。

RADIUSプロトコルの場合、「認証」と「認可」の境界、あるいは認可のプロセスで何をするか、あいまいな部分はあるものの、

1. クライアント側がRADIUSサーバーに対して認証を要求
1. RADIUSサーバー側で認証要求を検証し、許可または拒否の応答を送信
1. (認証許可の場合) クライアント側がRADIUSサーバーに対してサービス利用を要求
1. RADIUSサーバー側でクライアントのサービス利用の記録を開始

と、「認証」(→「認可」)→「アカウンティング」の流れで処理を行うため、一般的にはRADIUSプロトコルもこの**AAAモデル**に含まれると解釈されています。

# RADIUSサーバーの構築

さて、座学のつぎはいよいよRADIUSサーバーの構築です。
AWS上にEC2インスタンスを立て、そこにRADIUSサーバーを構築していこうと思います。

:::note info
インスタンスのスペック
OS : Red Hat Enterprise Linux release 9.4 (Plow)
インスタンスタイプ : t2.micro
:::

今回は、このインスタンスにオープンソースのRADIUSサーバーである**FreeRADIUS**を導入します。
FreeRADIUSの詳細は以下のリンク先参照。

https://www.freeradius.org/

## FreeRADIUSの導入

FreeRADIUSはdnfコマンドで導入できます。
以下のコマンドを実行します。

```
# dnf install -y freeradius
```

これで、FreeRADIUSの導入ができました。

## 証明書関係の準備

導入が完了したところで、いったん以下のコマンドを実行し、FreeRADIUSの起動を試してみます。

```
# radiusd -X
```

実行してみると、以下のようなエラーとなり、起動に失敗します。

> Unable to check file "/etc/raddb/certs/server.pem": No such file or directory
/etc/raddb/mods-enabled/eap[183]: Failed parsing configuration item "private_key_file"
rlm_eap_tls: Failed initializing SSL context
rlm_eap (EAP): Failed to initialise rlm_eap_tls
/etc/raddb/mods-enabled/eap[14]: Instantiation failed for module "eap"

どうやらファイルが不足しているようです。
実際に/etc/raddb/certs/の配下を確認すると、たしかに当該のファイルがありません。

```
# ls /etc/raddb/certs/
bootstrap  ca.cnf  client.cnf  inner-server.cnf  Makefile  passwords.mk  README  server.cnf  xpextensions
```

ということで、証明書周りの用意を行います。
本来であれば正規の証明書を用意すべきなのでしょうが、今回の記事の本筋からは外れるため、正規の証明書作成は省略します。
今回は、配置されているMakefileを利用して作成を行います。
以下のコマンドを実行し、pemファイルの作成とパーミッションの変更を行います。

```
# cd /etc/raddb/certs/
# make all
# chmod 775 dh server.pem
```

## FreeRADIUSの起動
証明書の準備ができたところで、再度以下のコマンドを実行し、デバッグモードで起動してみます。
※設定は後からでも変更できるため、いったん後回しにします。

```
# radiusd -X
```

コンソールにいろいろと出力されますが、最後が

```Ready to process requests```

となっていれば、起動成功です。
Ctrl+Cを押下し、デバッグモードを終了してください。

## FreeRADIUSサーバーの設定

こちらはいったん後回しにして、先に下記の**radclient**を一度実行します。

# 疎通確認

最後に疎通確認を行います。
疎通確認では**radclient**を利用します。

## radclientの導入

radclientは**freeradius-utils**に含まれているため、以下のコマンドを実行します。

```
# dnf install -y freeradius-utils
```

実行後、radclientが導入できているはずなので、コマンドを実行します。

```
# radclient
radclient: Insufficient arguments
Usage: radclient [options] server[:port] <command> [<secret>]
  <command>              One of auth, acct, status, coa, disconnect or auto.
  -4                     Use IPv4 address of server
  -6                     Use IPv6 address of server.
  -b                     Mandate checks for Blast RADIUS (this is not set by default).
  -c <count>             Send each packet 'count' times.
  -d <raddb>             Set user dictionary directory (defaults to /etc/raddb).
  -D <dictdir>           Set main dictionary directory (defaults to /usr/share/freeradius).
  -f <file>[:<file>]     Read packets from file, not stdin.
                         If a second file is provided, it will be used to verify responses
  -F                     Print the file name, packet number and reply code.
  -h                     Print usage help information.
  -n <num>               Send N requests/s
  -p <num>               Send 'num' packets from a file in parallel.
  -q                     Do not print anything out.
  -r <retries>           If timeout, retry sending the packet 'retries' times.
  -s                     Print out summary information of auth results.
  -S <file>              read secret from file, not command line.
  -t <timeout>           Wait 'timeout' seconds before retrying (may be a floating point number).
  -v                     Show program version information.
  -x                     Debugging mode.
  -P <proto>             Use proto (tcp or udp) for transport.
```

問題なく導入できていれば、Usageが表示されます。

## radclientの書式

詳細はUsage参照。
今回は以下の書式で記載します。

```
$ radclient -x -r 1 -f [ファイルパス] [送信先ホスト名orIPアドレス]:[送信先ポート番号] [パケット種別] [シークレットキー]
```

* -x : デバッグモードで実行
* -r 1 : RADIUSサーバーから応答が返ってこなかった場合のリトライ回数を設定 (1回目も回数に含まれるので、実質リトライを行わない)
* -f [ファイルパス] : RADIUSサーバーに送信するアトリビュートファイルを指定
* [送信先ホスト名orIPアドレス]:[送信先ポート番号] : パケット送信先を指定
* [パケット種別] : 今回は"**auth**" (認証要求) と"**acct**" (アカウンティング要求) の2つを使用
* [シークレットキー] : RADIUSサーバーと共有しているシークレットキーを設定

:::note info
パケット送信先のポート番号についてもRFCで定義されており、一般的には
* auth : 1812 (システムによっては 1645)
* acct : 1813 (システムによっては 1646)
* disconnect : 3799

に対して送信します。
パケット送信先はRADIUSサーバー側の設定に依存するため、必要に応じて読み替えてください。
:::

## 認証要求パケットの送信

それでは、認証要求パケットを送信します。

### アトリビュートファイルの用意

"radclientの書式"の章で記載した書式に沿って、コマンドを用意します。

```
$ radclient -x -r 1 -f auth.attr localhost:1812 auth secret_key
```

ここで必要になるのが、RADIUSサーバーに送信する情報を記述した**アトリビュートファイル**です。アトリビュートファイルは
```
属性名 = 値
```

の形式で記述します。属性名の型、指定できる文字数等はRFCで定義されているため、詳細はそちらをご覧ください。
今回は認証要求の疎通確認なので、本当に最小限の

* User-Name
* User-Password

のみを記載します。

```auth.attr
User-Name = otani
User-Password = passwd
```

ファイルが用意出来たら、実際に送信します。

```
$ radclient -x -r 1 -f auth.attr localhost:1812 auth secret_key
Sent Access-Request Id 93 from 0.0.0.0:54798 to 127.0.0.1:1812 length 63
        User-Name = "otani"
        User-Password = "passwd"
        Cleartext-Password = "passwd"
(0) No reply from server for ID 93 socket 3
```

**応答が返ってきませんでした。**

### (後回しにした) FreeRADIUSサーバーの設定

ここで、先ほど後回しにした設定ファイルを見てみようと思います。

#### clients.conf

まずは、**clients.conf**を確認します。パスは、```/etc/raddb/clients.conf```です。
このファイルですが、RADIUSパケットの送信元IPアドレス (クライアントIPアドレス) とシークレットキーの組を指定します。
FreeRADIUSは、RADIUSパケットを受信した際にクライアントIPアドレスとシークレットキーのチェックを行い、その内容がclients.confの記述と合致する場合、クライアントに対して応答パケットを返します。

```/etc/raddb/clients.conf
client localhost {
    ipaddr = 127.0.0.1
    secret = testing123
}
```
:::note info
実際のファイルにはコメント行が大量にありますが、除外しています。
:::

localhostからの認証要求は受け付けてくれるものの、シークレットキーの内容が違うために応答が返されなかったようです。ひとまず正しいシークレットキーをセットして、再度パケットを送信してみます。

#### users

というわけで早速送信。
```
$ radclient -x -r 1 -f auth.attr localhost:1812 auth testing123
Sent Access-Request Id 116 from 0.0.0.0:36213 to 127.0.0.1:1812 length 63
        User-Name = "otani"
        User-Password = "passwd"
        Cleartext-Password = "passwd"
Received Access-Reject Id 116 from 127.0.0.1:1812 to 127.0.0.1:36213 length 38
        Message-Authenticator = 0x4588eefc79fd9b88b8bbc35d3b7018f0
(0) auth.attr: Expected Access-Accept got Access-Reject
```

応答は返ってきたものの、認証拒否が返ってきています。
要するに「**"otani"さんですか、知らない子ですねぇ**」と言われているわけです。
RADIUSサーバーで認証を許可してもらうには、RADIUSサーバーにユーザーを事前に紹介しておく必要があります。
ここで、RADIUSユーザーを登録しておく設定ファイルである**users**の編集を行います。パスは```/etc/raddb/users```です。

このファイルの末尾に

```
#########################################################
# You should add test accounts to the TOP of this file! #
# See the example user "bob" above.                     #
#########################################################
```
と記載があるので、言われたとおりファイルの先頭に、以下の内容を追記し、保存します。

```/etc/raddb/users
otani  Cleartext-Password := "passwd"
       Reply-Message := "Hello, %{User-Name}"
```
:::note info
Cleartext-Password := "passwd"
の記載は、そのユーザーのパスワードを指定します。

Reply-Message := "Hello, %{User-Name}"
の行は、認証許可応答に付与する属性名と値のペアを指定します。
RFCに記載された属性名であれば設定できますが、今回はファイルに記述されたサンプルをそのまま利用します。
:::

**これで、(本当の本当に) 認証要求を通すための事前準備が整いました。**

### 認証要求パケットの送信

ここまでの手順を実施したら、再度RADIUSサーバーに対して認証要求を送信します。

```
$ radclient -x -r 1 -f auth.attr localhost:1812 auth testing123
Sent Access-Request Id 31 from 0.0.0.0:49939 to 127.0.0.1:1812 length 63
        User-Name = "otani"
        User-Password = "passwd"
        Cleartext-Password = "passwd"
Received Access-Accept Id 31 from 127.0.0.1:1812 to 127.0.0.1:49939 length 52
        Message-Authenticator = 0x4772d9c8b4f15e1fd4e39917619d44fd
        Reply-Message = "Hello, otani"
```

**RADIUSサーバーから認証許可応答が返されました。**

## アカウンティング要求パケットの送信

次に、アカウンティング要求パケットを送信します。

### アトリビュートファイルの用意

認証要求を投げる場合同様、アトリビュートファイルを用意します。
アカウンティング要求の場合、User-PAsswordの項目は不要ですが、代わりに**Acct-Status-Type**という属性の記載が必要です。

```acct.attr
User-Name = otani
*Acct-Status-Type = 1
```
:::note info
Acct-Status-Typeは、いうなればパケットの種類の指定です。
* 1 : Start
* 2 : Stop
* 3 : Interim-Update
* 7 : Accounting-On
* 8 : Accounting-Off

となります。詳細はRFC参照。

:::

### アカウンティング要求パケットの送信

アトリビュートファイルの準備ができたら、アカウンティング要求パケットを送信します。アカウンティング応答の属性値ペアについてもRADIUSサーバー側で設定ができますが、今回は省略します。

```
$ radclient -x -r 1 -f acct.attr localhost:1813 acct testing123
Sent Accounting-Request Id 199 from 0.0.0.0:46762 to 127.0.0.1:1813 length 33
        User-Name = "otani"
        Acct-Status-Type = Start
Received Accounting-Response Id 199 from 127.0.0.1:1813 to 127.0.0.1:46762 length 20
```

アカウンティング応答が返されました。アカウンティング応答には、認証要求のような許可/拒否の概念はないため、応答が返ってくればOKです。

**ここまでできれば、今回やりたかったことは完遂です。お疲れ様でした。**

# まとめ

今回はRADIUSプロトコルについてまとめてみました。技術が日々進化し、昨日まで有用だったものがいきなり廃れたりするようなこともある中、ダイアルインの時代からあるようなプロトコルがいまだ現役であるのはなんだか不思議な感じがします。

現在は毛色が違う業務についているため、私自身がRADIUSに触る機会はそう多くありませんが、この記事が、今日もどこかでRADIUSサーバーの開発や運用保守をしている誰かの役に立てばいいなと思います。

以上。