はじめに
このページでは、BIG-IPで、Virtual Server(HTTP or HTTPS)へのアクセスにBASIC認証を仕掛けるための方法について記載します。BASIC認証は、セキュリティ的にどうだろうかと思わなくもありませんが、調べてみると、まだまだ需要はあるようです。少なくとも通信経路は必ず暗号化しましょう。
BASIC認証は、Apacheやnginxならばconfファイルで簡単にできることですが、BIG-IPではiRuleが必要です。また、ユーザ名とパスワードの保管のために、Data Group Listも使います。
参考情報
- DevCentral: HTTP Basic Access Authentication iRule Style
- DevCentral: iRules 101 - #17 – Mapping Protocol Fields with the Binary Scan Command
- DevCentral: Advanced iRules: Getting Started with iRules Procedures
- DevCentral: iRules Wiki: HTTP::username
- DevCentral: iRules Wiki: HTTP::password
環境
このページに記載のiRuleはv12.1.2で動作を確認してます。
iRule
動作
このiRuleは、以下のような動作を行います。わざわざ箇条書きにしていますが、ごく普通のBASIC認証です。
- BIG-IPのVirtual Serverの、特定のディレクトリ配下(ここでは/mgmt/)へのアクセス時に、BASIC認証を行う
- BIG-IPには、認証に必要なユーザ名とパスワード(パスワードをsha256でハッシュした後の文字列)をあらかじめData Group Listとして登録しておく
- BIG-IPは、/mgmt/で開始するURLへのアクセス時に対して、BASIC認証の情報(ユーザ名とパスワード)が含まれていない場合、401 Unauthorizedを応答する
- BASIC認証の情報がBIG-IPのData Group Listに登録されているユーザ名とパスワードに一致する場合は、Poolへ通信を振り分ける
本ページのサンプルでは、認証用のユーザ名とパスワードは「ユーザ名:admin、パスワード:admin」としています。
コード
proc basic_auth {} {
binary scan [ sha256 [HTTP::password]] H* password
if { [class lookup [HTTP::username] authorized_users] ne $password } {
log local0. "User [HTTP::username], has been denied access to virtual server [virtual name]."
HTTP::respond 401 WWW-Authenticate "Basic realm=\"Secured Area\""
}
}
when HTTP_REQUEST {
if {[HTTP::uri] starts_with "/mgmt/"} {
call basic_auth
}
}
Data Group List
Data Group list名"authorized_users"を作成して、ユーザ名と、パスワード”admin"をsha256でハッシュ化した値を保存します。
GUI画面(Configuration Utility)では、このような画面になります。
複数のユーザを登録する場合は、このData Group Listにレコードを追加します。
パスワードのSHA256ハッシュ変換は、BIG-IPのbashを使う場合、以下のようなコマンドで可能です。
$ echo -n "admin" | sha256sum
8c6976e5b5410415bde908bd4dee15dfb167a9c873fc4bb8a81f6f2ab448a918 -
Virtual Server
このiRuleをVirtual Serverに適用します。これについては特に書くべきことはありません。
動作確認
まず、通常のページ(/index.html)にアクセスした場合。
$ curl -i http://203.0.113.1/index.html
HTTP/1.1 200 OK
Content-Type: text/html
Accept-Ranges: bytes
Content-Length: 23
Server: lighttpd/1.4.45
This is a test server.
認証なく、問題なくアクセスができています。
次に、BASIC認証が必要なディレクトリ配下(/mgmt/index.html)に、認証なしでアクセスした場合。
$ curl -i http://203.0.113.1/mgmt/index.html
HTTP/1.0 401 Unauthorized
WWW-Authenticate: Basic realm="Secured Area"
Server: BigIP
Connection: Keep-Alive
Content-Length: 0
BIG-IPが応答して、401を応答してます。
次は認証用のユーザ名とパスワードを入れますが、間違ったパスワードでアクセスした場合。
$ curl -i -u admin:admin2 http://203.0.113.1/mgmt/index.html
HTTP/1.0 401 Unauthorized
WWW-Authenticate: Basic realm="Secured Area"
Server: BigIP
Connection: Keep-Alive
Content-Length: 0
依然、401が応答し続けます。ちなみに上記2回のアクセスは、BIG-IPの/var/log/ltmに、次のような感じで記録されます。
info tmm[17378]: Rule /Common/Sample_Basic_Auth <HTTP_REQUEST>: User , has been denied access to virtual server /Common/vs_public.
info tmm[17378]: Rule /Common/Sample_Basic_Auth <HTTP_REQUEST>: User admin, has been denied access to virtual server /Common/vs_public.
Sample_Basic_AuthはiRuleの名前で、vs_publicはVirtual Serverの名前です。
最後に、正しいユーザ名とパスワードでアクセスした場合。
$ curl -i -u admin:admin http://203.0.113.1/mgmt/index.html
HTTP/1.1 200 OK
Content-Type: text/html
Accept-Ranges: bytes
Content-Length: 32
Server: lighttpd/1.4.45
This page needs authentication.
BIG-IPでの認証が通り、サーバからのレスポンスが得られました。
解説
procについて
procは、tclでサブルーチン(関数)を作るコマンドです。tclではサブルーチンのことを「プロシジャ」と呼ぶそうなので、ここではプロシジャと呼ぶことにします。
プロシジャは次のような文法となります。
proc プロシジャ名 {引数1 引数2 ...} {
プロシジャ内の処理...
# 特定の値を返したい場合はreturnを使う
}
# プロシジャを実行する際はcallを使う
call プロシジャ名 引数1 引数2 ...
一般的なプログラミング言語の関数と同様に、引数や戻り値が利用できますが、今回使用しているプロシジャでは不要なので、使用していません。returnを使わない場合、プロシジャの終了時、プロシジャ内で最後に実行したコマンドの戻り値が、プロシジャの戻り値になります。
引数が0個の時、プロシジャの定義時に"proc プロシジャ名 {} {"と、引数に空のリスト**{}**をつけるべきなのか気になりましたが、このページ(wiki.tcl.tk)を参考にして、空のリストをつけることにしました。ただし、DevCentralのページには、引数のないプロシジャ(単に {}を書かないだけ)のサンプルコードが普通にあるので、少なくともiRuleでは空リストは書かなくとも良さそうです。
procの面白いのは、これがBIG-IPに実装されたのは、案外最近(v11.4以降)ということです。参照情報にあげたprocの解説ページには**"Ladies and gentlemen, procs are now supported in iRules!"**とデカデカと書かれていて、それまでどれだけ待ち望まれていたのかと思います。プログラミング言語では、サブルーチンを作れるのは当然のことのように思いますが、iRuleにそれを実装するのは、簡単なことではなかったようです。
procで作成したプロシジャは、同一iRule内からだけでなく、外部のiRuleからもcallすることができます。procについては、いずれ別ページを作成したいと思います。
[HTTP::username] と [HTTP::password]について
これらのコマンドにより、HTTP Basic認証のユーザ名とパスワードを取得できます。詳細はDevCentralを参照してください。
HTTP Basic認証では、これらの情報はクライアントからサーバへの通信時、BASE64でエンコードされていますが、[HTTP::username] と [HTTP::password]はデコードした値を返すので、デコード処理を作成する必要はありません。
##パスワードをハッシュ化した値で保存する意味
Data Group Listにはパスワードをハッシュ化した値を保存してますが、そもそも、Basic認証ではパスワードは単にBase64エンコードされた文字列に過ぎず、通信経路での傍受については、ハッシュ化は何の意味もありません。通信経路での安全性は、HTTPS化で対応すべきと思います。
Apacheやnginxでは、パスワード情報は一般的にハッシュ化してるので、このiRuleでもハッシュ化するようにしてます。
「パスワード情報(Apacheで言うところの.htpasswd)が漏洩したらどうするんだ」という点で、ハッシュ化する意味は確かにあるのですが、そもそもこのData Group Listが保存されるのは、bigipの設定ファイルであるbigip.confであり、このファイルが外部に漏れたら、Basic認証のパスワードどころの騒ぎではないような気もします。が、それでもやらないよりはマシかと思います。
Data Group Listを外部ファイル参照(external)にするようなケースでは、ハッシュする価値はよりあるのではないでしょうか。
DevCentralにあるサンプルコードではmd5でハッシュしていましたが、時代の流れと思ってsha256でハッシュ化することにしました。これについては、サンプルコードの"md5"を"sha256"に置き換えただけです。
免責事項
本ページの内容に誤り等があり、参考にされた方がなんらかの損害を被った場合、一切の責任は負いません。