はじめに:
RADIUSは大変成熟したプロトコルで、AAAすなわちAuthentication(認証)、 Authorization(承認)、Accounting(アカウンティング)のために広く利用されています。

RADIUSは比較的シンプルなトランザクション型のプロトコルです。RASサーバ、FirePass、BIG-IPのようなクライアントがRADIUSリクエスト(例えばユーザ名+パスワードによる認証など)を出しRADIUSサーバからの応答を待ちます。
RADIUSクライアントとサーバ間の情報は属性(attribute)の形でやりとりされます。ユーザ名、ユーザパスワード、IPアドレス、ポート、セッション状態等は全て属性の一種です。属性は、テキスト、文字列、IPアドレス、整数、タイムスタンプのデータ型を採ることができます。可変長の属性もあれば、固定長のものもあります。

■ RADIUSプロトコルに特化したサポートをすることの利点

特定のプロトコルに特化しないUDP一般のロードバランシングには共通の課題があります。クライアントがいつも同じソースポートからリクエストを送ってきた場合、複数のサーバにパケットを分散することができないのです。この挙動はUDPプロファイルのデフォルトの動作となります。

このような状況で負荷分散を可能にするには、一般的には「データグラム・ロードバランシング」を行うか、即時にセッションタイムアウトを行う事が推奨されます。データグラム・ロードバランシングを行えば確かに個々のパケットは分散されますが、あるリクエストに対するサーバからの応答が返る前に新規のリクエストが入って来ると、設定によってはBIG-IPは新規のリスエストのソースポートを書き換えてからサーバに転送します。この結果アプリケーションがきちんと動かない場合には”immediate timeout”を設定することになります。あわせて、サーバからクライアントへの戻りのトラフィックを通すためのバーチャルサーバを追加する必要も生じるでしょう。

同一のIPアドレス/ポートから送られてくるRADIUSのトラフィックを負荷分散するには、データグラム・ロードバランシングまたはimmediate timeoutを用いればほぼ問題ありません。しかし、もしトランザクションあたり2パケット (リクエストに1パケット、応答に1パケット) より多いやり取りがある場合、BIG-IP LTMの更なる機能が必要となります。

該当するケースとしてRADIUSのチャレンジ/レスポンスによるハンドシェイクが挙げられます。下記のように4パケットがやりとりされます。

* Client ---- アクセス要求 ---> Server
* Client <-- アクセスチャレンジ --- Server
* Client --- アクセス要求----> Server
* Client <--- アクセス許可 ----- Server

この処理が成功するには、ひとつのトランザクションに属するパケットが全て同じサーバに振り分けられる必要があります。そのためには、カスタマイズされたL7パーシステンスが必要となります。

iRuleがそれを実現します。RADIUSプロトコルを理解できるiRuleを用いることで、BIG-IP LTMはクライアントから送られた属性に基づいて振り分け先のサーバを選定することができ、また、クライアント/サーバ双方いずれから送られた属性に基づいてセッションをパーシストすることができます。セッション管理をBIG-IPにオフロードできますので、サーバ側の設定をよりシンプルにできます。iRuleによりmd5の再計算、ユーザ名の変更レルムの検出等々を行う事ができますので、BIG-IPは更に高度な処理を行うこともできます。BIG-IP LTMはまた、RADIUSプロトコルにアプリケーションレベルでのセキュリティを追加することも可能です。改変されたトラフィックのrejectや、DoS攻撃などへの対策をiRuleにて行えます。

■ ソリューション
クライアントからのリクエストが常に同じIPアドレス/ポートから行われる場合は、データグラム・ロードバランシングまたはimmediate timeoutを用いれば、たいていは事足ります。
immediate timeoutを使用する場合、サーバからクライアントへのパケットを転送するために追加のバーチャルサーバおよび適切なSNAT(VIPと同じIPでクライアントに返すための)を設定します。
属性をもってUIE(Universal Inspection Engine)パーシステンスを行います。
immediate timeoutとクライアント/サーバそれぞれのために2つのバーチャルサーバを作成する場合は、セッションコマンドに"any"オプションを付けます。

タイトル:

Attributeを見てRADIUSサーバの負荷分散を行うiRule

メリット:

RADIUSサーバの負荷分散を行えるようになり、

  • システム全体として処理能力をあげられる
  • サービスを止めずに個々のRADIUSサーバのメンテナンスができる
  • レイヤ7でRADIUSの属性やレルムを見て転送先のRADIUSサーバを選択することができる

設定概要:
CLIENT_ACCEPTEDおよびSERVER_DATAイベントにてUDPパケットのペイロードをbinary scanし、RADIUS attributeとその値を抽出します。

 【iRule定義】 

1. 下記のサンプルiRuleはデコードおよび属性情報のロギングのみを行います。iRuleを用いてRADIUSをどのように取り扱えるかをつかむための良いサンプルです。特定の値を抽出して利用するので、目的により様々な用途に用いることができます。例えば、特定の属性やレルムの値を取得し振り分け先サーバの選定に用いる等です。

when RULE_INIT {
array set ::attr_code2name {
1      User-Name
2      User-Password
3      CHAP-Password
4      NAS-IP-Address
5      NAS-Port
6      Service-Type
7      Framed-Protocol
8      Framed-IP-Address
9      Framed-IP-Netmask
10      Framed-Routing
11      Filter-Id
12      Framed-MTU
13      Framed-Compression
14      Login-IP-Host
15      Login-Service
16      Login-TCP-Port
17      (unassigned)
18      Reply-Message
19      Callback-Number
20      Callback-Id
21      (unassigned)
22      Framed-Route
23      Framed-IPX-Network
24      State
25      Class
26      Vendor-Specific
27      Session-Timeout
28      Idle-Timeout
29      Termination-Action
30      Called-Station-Id
31      Calling-Station-Id
32      NAS-Identifier
33      Proxy-State
34      Login-LAT-Service
35      Login-LAT-Node
36      Login-LAT-Group
37      Framed-AppleTalk-Link
38      Framed-AppleTalk-Network
39      Framed-AppleTalk-Zone
60      CHAP-Challenge
61      NAS-Port-Type
62      Port-Limit
63      Login-LAT-Port
}
}
when CLIENT_ACCEPTED {
binary scan [UDP::payload] cH2SH32cc code ident len auth \
attr_code1 attr_len1
log local0. "code       = $code"
log local0. "ident      = $ident"
log local0. "len        = $len"
log local0. "auth       = $auth"
set index 22
while { $index < $len } {
set hsize [expr ( $attr_len1 - 2 ) * 2]
switch $attr_code1 {
11 -
1 {
binary scan [UDP::payload] @${index}a[expr $attr_len1 - 2]cc \
attr_value attr_code2 attr_len2
log local0. " $::attr_code2name($attr_code1) = $attr_value"
}
9 -
8 -
4 {
binary scan [UDP::payload] @${index}a4cc rawip \
attr_code2 attr_len2               
log local0. " $::attr_code2name($attr_code1) =\
[IP::addr $rawip mask 255.255.255.255]"
}
13 -
12 -
10 -
7 -
6 -
5 {
binary scan [UDP::payload] @${index}Icc attr_value \
attr_code2 attr_len2
log local0. " $::attr_code2name($attr_code1) = $attr_value"
}
default {
binary scan [UDP::payload] @${index}H${hsize}cc \
attr_value attr_code2 attr_len2
log local0. " $::attr_code2name($attr_code1) = $attr_value"
}
}
set index [ expr $index + $attr_len1 ]
set attr_len1 $attr_len2   
set attr_code1 $attr_code2
}
}
when SERVER_DATA {
binary scan [UDP::payload] cH2SH32cc code ident len auth \
attr_code1 attr_len1
log local0. "code       = $code"
log local0. "ident      = $ident"
log local0. "len        = $len"
log local0. "auth       = $auth"
set index 22
while { $index < $len } {
set hsize [expr ( $attr_len1 - 2 ) * 2]
switch $attr_code1 {
11 -
1 {
binary scan [UDP::payload] @${index}a[expr $attr_len1 - 2]cc \
attr_value attr_code2 attr_len2
log local0. " $::attr_code2name($attr_code1) = $attr_value"
}
9 -
8 -
4 {
binary scan [UDP::payload] @${index}a4cc rawip \
attr_code2 attr_len2               
log local0. " $::attr_code2name($attr_code1) =\
[IP::addr $rawip mask 255.255.255.255]"
}
13 -
12 -
10 -
7 -
6 -
5 {
binary scan [UDP::payload] @${index}Icc attr_value \
attr_code2 attr_len2
log local0. " $::attr_code2name($attr_code1) = $attr_value"
}
default {
binary scan [UDP::payload] @${index}H${hsize}cc \
attr_value attr_code2 attr_len2
log local0. " $::attr_code2name($attr_code1) = $attr_value"
}
}
set index [ expr $index + $attr_len1 ]
set attr_len1 $attr_len2   
set attr_code1 $attr_code2
}
}

2. 次のiRuleはUDPデータグラム・ロードバランシングが2要素認証でも機能するようにします。このiRule中のパーシステンスは”State”属性(value=24)に基づいています。iRuleがどれほどプロトコルの内部まで掘り下げることができるかの好例です。

when CLIENT_ACCEPTED {
binary scan [UDP::payload] ccSH32cc code ident len auth \
attr_code1 attr_len1
set index 22
while { $index < $len } {
set hsize [expr ( $attr_len1 - 2 ) * 2]
binary scan [UDP::payload] @${index}H${hsize}cc attr_value \
attr_code2 attr_len2
# If it is State(24) attribute...
if { $attr_code1 == 24 } {
persist uie $attr_value 30
return
}
set index [ expr $index + $attr_len1 ]
set attr_len1 $attr_len2   
set attr_code1 $attr_code2
}
}
when SERVER_DATA {
binary scan [UDP::payload] ccSH32cc code ident len auth \
attr_code1 attr_len1
# If it is Access-Challenge(11)...
if { $code == 11 } {
set index 22
while { $index < $len } {
set hsize [expr ( $attr_len1 - 2 ) * 2]
binary scan [UDP::payload] @${index}H${hsize}cc attr_value \
attr_code2 attr_len2
if { $attr_code1 == 24 } {
persist add uie $attr_value 30
return
}
set index [ expr $index + $attr_len1 ]
set attr_len1 $attr_len2   
set attr_code1 $attr_code2
}
}
}

 ※F5ネットワークスジャパンでは、サンプルコードについて検証を実施していますが、お客様の使用環境における動作を保証するものではありません。実際の使用にあたっては、必ず事前にテストを実施することを推奨します。