こんにちは、コンテナソリューション事業部の髙井です。
AzureのApplication Gateway V2では、フロントエンドIPをプライベートIPのみの構成とすることが仕様上できません。
しかし、そうはいってもユースケースとしてはプライベートのみに制限したい場合が依然としてあると思います。
そこで、Azure Policyを利用して「パブリックIPを受け口にするリスナーを作成させない」という方針で実質的にプライベートIPのみの構成を強制しましょう。
また、今回の記事の前提となる各サービスの概要については、
で紹介しているので、参考にしていただければ幸いです。
techblog.ap-com.co.jp techblog.ap-com.co.jp
パブリックIPをリスナーに使用させないカスタムポリシー
まずは、ポリシー定義の全体を示します。
{ "mode": "All", "policyRule": { "if": { "allOf": [ { "field": "type", "equals": "Microsoft.Network/applicationGateways" }, { "count": { "field": "Microsoft.Network/applicationGateways/httpListeners[*]", "where": { "field": "Microsoft.Network/applicationGateways/httpListeners[*].frontendIPConfiguration.id", "notEquals": "[concat(field('id'),'/frontEndIPConfigurations/appGwPrivateFrontendIp')]" } }, "greater": 0 } ] }, "then": { "effect": "deny" } } }
Azure Policyで配列プロパティを条件に引っ掛けるのは少し複雑です。
配列プロパティについては公式ドキュメントに以下のページがありますが、わりと分量が多いので、今回のポリシーを理解するのに必要な部分だけを以降で説明します。
複雑な配列の条件にはcount
を使う
Azure Policyでは、基本的にオブジェクトや配列をequals
の中身などにそのままの形で書けません。
したがって、以下のようにcount
で条件に一致する配列要素の個数をカウントするようにします。
{ "count": { "field": "Microsoft.Network/aplicationGateways/httpListeners[*]", "where": { "field": "Microsoft.Network/applicationGateways/httpListeners[*].frontendIPConfiguration.id", "notEquals": "[concat(field('id'),'/frontEndIPConfigurations/appGwPrivateFrontendIp')]" } }, "greater": 0 }
where
の中身が「どんな条件のものをカウントするか」に相当しています。
今回の例では、「プライベートIPを指定していないこと(notEquals
)」が条件です(違反件数をカウント)。
1個でも違反するリスナーがあればNGのため、カウント結果に対する閾値は0
より大きいという条件で"greater": 0
としています。
配列の各要素を返す[*]
指定子
ここで、field
の中身に注目しましょう。
httpListers
ではなく、httpListeners[*]
となっています。なにが違うかというと、[*]
が付いていない場合は「配列全体」が返ります。
しかし、先ほど述べたようにAzure Policyでは、equals
などに直接オブジェクトや配列を書いて指定することが出来ません。
つまり、以下のように「配列全体として一致するか」といった書き方ができないので、[*]
なしの配列プロパティは使い道がほぼありません。
"field": "Microsoft.Web/sites/config/ipSecurityRestrictions" "equals": [{ "ipAddress": "Any", "action": "Deny", "priority": 2147483647, "name": "Deny all", "description": "Deny all access" }]
一方で、[*]
が付いている場合には、各要素が含まれるコレクションが戻り値です。
これによって、冒頭で述べた「各要素のうち、条件を満たすもの」といった表現内容に繋がります。
もっとシンプルに書く
汎用性があるのは、いま紹介したfield
とwhere
を使用する書き方です。
しかし、今回の例ではもう少しシンプルに書くことができます。
{ "not": { "field": "Microsoft.Network/applicationGateways/httpListeners[*].frontendIPConfiguration.id", "equals": "[concat(field('id'),'/frontEndIPConfigurations/appGwPrivateFrontendIp')]" } }
実は、このような書き方をすると、httpListeners
配列に対して、いわゆるJavaScriptのArray.every()
やPythonのall()
と同じような挙動を示します。
つまり、httpListeners
配列それぞれの要素がequals
の条件を満たすかを検査し、すべての要素が条件を満たす場合のみ全体としてtrue
が返ります。
したがって、1つでもプライベートIPを指定しないリスナーがあるとfalse
が返りますので、not
と合わせて全体としてはtrue
となり条件を満たします。
文章で説明すると分かりにくいですが、数式で表すとシンプルです(ド・モルガンの法則)。
「どれかひとつでも違反」(否定要素の和集合)は、「どれも違反してない」を満たさない(肯定要素の積集合の否定)と同じことです。
ド・モルガンの法則についての補足
数式だと逆にわかりにくいという方のためにもう少し噛み砕きます。
中学数学で見慣れた記号を使ってド・モルガンの法則を書くと以下です。
ただ、だとかであらわされた要素の個数は配列なので不定です。
こんなふうに全ての要素をいちいち列挙するのはキリがないですし、数も不定のため表現としては不適です。
そこで大型演算子により一般化したのが最初に紹介した数式になります。
より詳しくは、Wikipediaを参考にしてみてください。
おわりに
Azure Policyでのカスタムポリシー作成で、わりと詰まりやすいのが今回ご紹介したリソースの配列プロパティで条件分岐させるところだと思います。
公式ドキュメントに説明されている内容ではありますが、Azure Policyは作成してから最初に評価が開始されるまでは時間がかかってサクッと試すのが地味に大変だったりするので、具体例に沿って説明するのがよいと思い本記事を作成しました。
カスタムポリシーは奥が深いので、いい題材があればまた記事を書こうと思います。