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

Technical Article
Attributeを見てRADIUSサーバの負荷分散を行うiRule
はじめに:
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は更に
ichiro
クライアントから送られた属性に基づいて振り分け先のサーバを選定することができ、また、クライア
ント/サーバ双方いずれから送られた属性に基づいてセッションをパーシストすることができます。セッ
ション管理を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
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]"
}
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 \
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
return
}
set index [ expr $index + $attr_len1 ]
set attr_len1 $attr_len2
set attr_code1 $attr_code2
}
}
}
※ F5ネットワークスジャパンでは、サンプルコードについて検証を実施していますが、お客様の使用環境における動作を保証
するものではありません。実際の使用にあたっては、必ず事前にテストを実施することを推奨します。
Document Created: 2014.6.23