本記事は、実際に稼働しているWEBサーバーにOracle CloudのWeb Application Firewall(以下「WAF」という)を設定した一連の手順について記載したものです。
こんにちは!k.takeiです
今回はすでに稼働しているWebサーバーに対してWAFを導入しました!
その際に悩んだところや工夫したところを中心にまとめています。
実現したかったのは、下記の2点です。
・稼働しているWebサーバーへのアクセスをWAF経由のみにしたい。
・アクセスさせるドメインは変更したくない。
WAFを経由してWebサーバーにアクセスさせる
WAFはWebアプリケーションを防御するためのものです。
例えば、SQLインジェクション、OSコマンドインジェクション、クロスサイトスクリプティングのような攻撃からWEBサーバーを守る用途で使用されます。
WAFポリシーの作成については「こちらの記事」で紹介してますのでご参照してください。
WAFポリシーの作成後、DNS管理から対象のWebサーバーで使用しているドメインを選択し、遷移後の画面でレコードを選択します。
今回はCNAMEではなく、ALIASというレコードを使用します。
理由は、Zone APEXだとCNAMEが設定できないからです。
今回稼働しているWebサーバーが使用しているドメインは、Zone APEX(wwwなどのサブドメインを含まないドメイン名)です。
そして、Zone APEXの場合はNSレコードの設定が必須となっています。
ただ、CNAMEは他のレコードと共存できないので、プロトコルエラーになってしまいます。
そのため、ALIASレコードを使用しています。
※OracleCloudのDNSレコードに関するドキュメントはこちらを参照して下さい。
今回設定する値を例に説明をします。
設定したいURLは「onigiri.fun」です。
「onigiri.fun」はZone APEXなので、NSレコードが必ず設定されています。
この状態でCNAMEを登録しようとすると、下記の画像の通りエラーが出てしまい設定することができません。
これを解決するためにALIASレコードを下記の手順で設定していきます。
ネットワーキング > DNS管理 > ゾーン > 対象のドメイン名を選択します。
レコードの追加をクリックすると項目の入力画面になります。
入力項目は下記の通りです。
・レコード型:レコードの型を選択する項目
今回はALIASを指定します。
・名前:ドメインの前に設定する名前を入力する項目
今回はそのまま使用したいので、空欄のままにします。
※赤枠の箇所にドメイン名が入っています。
・TTL:レコードのTTLを設定する項目
今回はAレコードに合わせて300秒に設定しました。
・RDATA:入力方式を基本と拡張のどちらかから選択する項目
今回は基本を選択します。
・DOMAIN:別名を入力する項目
今回は作成したWAFのCNAMEターゲットをここに入力します。
送信が完了し、変更の公開を押すと反映されます。
AレコードのRDATA項目が下記のようになっていれば問題ないです。
これでWAFを経由させる作業は完了しました!
onigiri.funへのアクセスは、WAFを通してのみ行われるようになります。
WebサーバーへのアクセスをWAF経由のみに設定する
次はWebサーバーへのアクセスを制御します。
WAFを経由していないものに関しては、拒否するように設定していきます。
現状はドメインを使用してアクセスされるもの(http://onigiri.funなど)は、先ほどの設定でWAFを経由するようになっていますが、IPを直接指定してアクセスされるもの(http://150.136.63.233など)はWAFを経由せずにアクセス出来てしまう状態です。
Webサーバーへのアクセスを指定したIPからの通信だけ許可する設定する方法ですが、今回はネットワーク・セキュリティ・グループ(以下「NSG」という)で行います。
ネットワーキング > 仮想クラウド・ネットワークから対象のVCNを選択し、左下のネットワーク・セキュリティ・グループを押下すると設定画面が出てきます。
その後に、ネットワーク・セキュリティ・グループの作成ボタンを押下します。
入力項目は下記の通りです。
・名前:NSGの名前を設定
任意の名前を設定します。
・コンパートメントに作成:作成するコンパートメントを選択
コンパートメントを選択します。
・セキュリティ・ルールの追加:セキュリティ・ルールを追加します。
今回はこの画面では設定しません。
※理由は後述します。セキュリティ・ルールについては公式ドキュメントを参照してください。
NSGを作成し、セキュリティ・ルールにWAFのCIDERを追加していくのですが、合計で108個作成する必要があります。
※CIDER範囲が記載されているドキュメントはこちらを参照してください。
先ほどコンソール画面から設定しなかった理由は追加するルールの多さです。
手で設定するとなると、かなり大変な上にミスをしそうだったので、今回はCLIを使用してルールを追加していきます。
※CLIの始め方はこちらを参照してください。
・現在のNSGのルールを取得するコマンド
oci network nsg rules list --all --output table --nsg-id <NSGのOCID>
〈--output table〉をつけない場合、JSON形式でデータが取得できます。
ただ、上記のコマンドではカラムが多すぎるので、欲しいものだけに絞ります。
oci network nsg rules list --all --output table --query "data[*].{\"説明\":description,\"プロトコル\":protocol,\"IPアドレス\":source,\"ポート\":\"tcp-options\"}" --nsg-id <NSG_ID> > ./NSG_rule.txt
上記のコードでは、description、protocol、source、tcp-optionsの項目に絞ってデータを取得し、NSG_rule.txtというファイルに出力をしています。
NSGのルールを追加するコマンドは下記です。
oci network nsg rules add --from-json file://〈読み込むJSONファイルを指定〉
作成するJSONファイルの形式は下記のコマンドで取得できます。
oci network nsg rules add --generate-full-command-json-input
取得したJSON形式に説明のコメントを記載したものが下記です。
各項目の説明はこちらをご覧ください。
{
"nsgId": "string",
"securityRules": [
{
"description": "string",
"destination": "string",
"destinationType": "string",
"direction": "string",
"icmpOptions": {
"code": 0,
"type": 0
},
"isStateless": true,
"protocol": "string",
"source": "string",
"sourceType": "string",
"tcpOptions": {
"destinationPortRange": {
"max": 0,
"min": 0
},
"sourcePortRange": {
"max": 0,
"min": 0
}
},
"udpOptions": {
"destinationPortRange": {
"max": 0,
"min": 0
},
"sourcePortRange": {
"max": 0,
"min": 0
}
}
},
{
"description": "string",
"destination": "string",
"destinationType": "string",
"direction": "string",
"icmpOptions": {
"code": 0,
"type": 0
},
"isStateless": true,
"protocol": "string",
"source": "string",
"sourceType": "string",
"tcpOptions": {
"destinationPortRange": {
"max": 0,
"min": 0
},
"sourcePortRange": {
"max": 0,
"min": 0
}
},
"udpOptions": {
"destinationPortRange": {
"max": 0,
"min": 0
},
"sourcePortRange": {
"max": 0,
"min": 0
}
}
}
]
}
今回追加するINGRESSルールに必要なもののみに絞ると下記の形になりました。
{
"nsgId": "xxx",
"securityRules": [
{
"description": "WAFのCIDR範囲",
"direction": "INGRESS",
"isStateless": "false",
"protocol": "6",
"source": "192.29.60.0/23",
"tcpOptions": {
"destinationPortRange": {
"max": "443",
"min": "443"
}
}
}
]
}
セキュリティ・ルール追加のコマンドの読み込むファイルの部分に上記のJSONファイルが格納されているパスとファイル名を指定すると、OCIDが「xxx」のNSGに下記の画像のルールを追加していることになります。
あとは、追加したいルールをJSON形式で記述していくだけなのですが、いくつか注意する点がいくつかあります。
- CLIを使用してルールを追加する際は1度に25件が上限。
- コンソールでは宛先ポート範囲で80,443という記述をしてもよかったが、CLIでは出来ないので分けて作成する必要がある。(21‐22のような範囲指定は可能)
- セキュリティ・ルールは120個が上限。
WAFのCIDERが108個あるので、JSONファイルは5つに分ける必要があります。
また、ポート指定を80と443両方していすると上限を超えて登録できないため、どちらかに絞る必要がありました。
なので、WAFでHTTPからHTTPSへのリダイレクトを行い、WebサーバーのNSGは443のみを許可させることにしました。
下記はイメージです。※実際にはWebサーバーにssh接続する用のものも許可していますが省いています。
WAFのリダイレクト設定は、対象のWAF詳細画面の左下にある、設定から行えます。
編集ボタンを押下して、赤枠の項目を設定します。
※SSL証明書に関してはこちらを参照してください。
あとはWAFに必要な保護ルールを設定したら完了です。
※WAFの保護ルールについてはこちらを参照してください。
動作確認
設定が完了したので、動作確認をしていきます。
確認するのは下記の内容です。
・onigiri.funにアクセスした場合、WAFを経由すること。
・http://onigiri.funにアクセスした場合、https://onigiri.funでWebサーバーにアクセスされること。
・WebサーバーのIPアドレスを指定してアクセスした場合、拒否されること。
まず、WAFを経由してアクセス出来ているかに関しては、下記の手順で確認できます。
- Webサーバーにアクセスする。
- 開発者ツールを開く。
- ページを更新し、Headerを確認する。
serverがZENEDGEとなっていればWAFを経由してアクセスしている証拠です。
次に、http://onigiri.funでアクセスしてみます。
するとhttps://としてアクセスされました。
最後にIPアドレスでWebサーバーにアクセスしてみます。
パブリックIPの150136.63.233を指定すると接続できません。
これでWAFを経由しないとWebサーバーへアクセス出来なくなっていることを確認できました!
まとめ
WAFの設定をしてみましたが、一番辛いのはWAFのCIDERをNSGのセキュリティ・ルールに追加する部分でした。
WAFを触ってみた個人的な感想ですが、
・Webアプリケーションの防御を開発者のスキルに依存することなく行える。
・セキュリティに割く開発コストや期間を短くできる。
・新たな脆弱性が出たときに、簡単に対応できる。
※これはプラットフォームにもよります。
・運用者のセキュリティの知識が浅くても設定自体は簡単に行える。
というのが、メリットだと感じました。
そして作っている最中に、CLIを使用しても手で書くと労力も精度もコンソール画面から登録するのと大差がないと気づいたので、JSONファイルを作ってくれるスクリプトを作成することにしました。。。
作成したはコードはGitHubで公開してあるので、興味のある方はこちらからご覧ください。
README.mdに使い方に関しての簡単な説明は記載しております。
※Pythonで作成しています(バージョンは3.8.10)。
少量であればJSONファイルをそのまま作成した方が早いのですが、今回みたいに大量に追加しなければいけない場合は、スクリプトを作成するのも1つの手段だと思いますので、興味がある方はチャレンジしてみてください!
この記事が少しでも皆様のお役に立てれば幸いです。
最後まで読んで頂きありがとうございました!