ポリシーが実際に塞ぐものを見る
このラボは本物のKubernetes上で動きます
VMの中でk3sが1台、実際に起動していて、NetworkPolicyを強制するコントローラーが動いています。そのため、ポリシーを付けると本当にブロックされます。採点ツールも、ファイルだけを見ずに、毎回実際に接続を試して確認します。
最初に起動するまでに、2分ほどかかります。
目標
許可リスト方式のネットワークポリシーを1枚ずつ積み重ね、その過程で、実務で最もよく起きる障害を1つ、自分で起こしてから直します。
なぜ重要なのか
Kubernetesの既定は、すべて許可です。どのネームスペースのどのPodでも、他のすべてのPodに届きます。侵入者がWebのPodを1つ押さえると、その場でデータベースが見えます。
ところが、ポリシーの動作の仕方は、直感と違います。Podにポリシーが1つも付いていなければすべて許可で、1つでも付いた瞬間に、そのPodは許可リスト方式に変わります。そのため、「ブロックするポリシー」を書くのではなく、「許可するポリシー」を書くことになり、この切り替えを知らないと、ポリシーを追加するほど、思わぬものが切れます。
最もよくある障害が、ステップ5です。egressをデフォルトでブロックした瞬間、DNSも一緒に切れます。CoreDNSへ出ていく53番ポートもegressだからです。症状は「名前が見つかりません」なので、誰も、今付けたネットワークポリシーを疑いません。
ステップ
用意されているもの: shopネームスペースのapi(app=api)とdb(app=db、nginx)のPod、そしてopsネームスペースがあります。なければステップ1で作成します。
- ポリシーが1つもないとき、どのPodからでも
dbに届くことを確認して、保存してください(保存先:/root/k8snp/baseline.txt)。レスポンスコード200が見える必要があります。 shopにdefault-denyポリシーを作成してください。podSelector: {}で、policyTypes: [Ingress]です。そして、これでブロックされることを記録してください(保存先:/root/k8snp/deny.txt)。allow-apiポリシーで、app=apiのPodだけがdbに届くようにしてください。結果を保存します(保存先:/root/k8snp/allow-pod.txt)。ポリシーは受信側(app=db)に付けます。allow-opsポリシーで、opsネームスペース全体を許可して、保存してください(保存先:/root/k8snp/namespace.txt)。ネームスペースは、kubernetes.io/metadata.nameラベルで選びます。shopにdeny-egress(policyTypes: [Egress]、空のセレクター)を付けて、何が起きるかを見てください。そして、障害報告書を書いてください(保存先:/root/k8snp/incident.md)。原因がDNSであること、53番ポート、CoreDNSがどのネームスペースにあるかが入っている必要があります。allow-dnsポリシーで、DNSだけを開けて復旧してください。結果を保存します(保存先:/root/k8snp/fixed.txt)。UDPとTCPの両方で、ポート53を開ける必要があります。allow-apiに、ポートの制限(80)を追加して、結果を保存してください(保存先:/root/k8snp/ports.txt)。policies=(ポリシーの数)とdns_fix_policy=allow-dnsの2行、そして、デフォルト許可から許可リストに切り替わる地点の説明を、/root/k8snp/report.mdに書いてください。
参考
- 接続テストは、
kubectl -n shop run t --rm -i --restart=Never --image=busybox:1.36 -- timeout 5 wget -q -O- http://<db의 IP>/で行います(プレースホルダーはdbのIPです)。Service名の代わりにPodのIPを使うと、DNSの問題とポリシーの問題を混ぜずに見られます。 kubernetes.io/metadata.nameラベルは、Kubernetesがすべてのネームスペースに自動で付けてくれます。自分で付ける必要はありません。- ステップ6で
toを空にすると、「どこへでも」になります。そうするとDNSは復旧しますが、残りのegressもすべて開いてしまい、ステップ5の意味がなくなります。kube-systemネームスペースに絞ってください。 - よくある間違い1: ポリシーを送信側に付けること。ingressルールは、常に受信側のPodを選びます。
- よくある間違い2: ステップ6でUDPだけを開けること。DNSの応答が512バイトを超えると、TCPに切り替わります。UDPだけを開けると、たまにだけ失敗するので、原因を探すのがずっと難しくなります。
- よくある間違い3:
allow-apiでportsを空にしておくこと。そうすると、apiはdbのすべてのポートに届きます。
既定はすべて許可である
ポリシーが1つもないとき、どのPodからでもdbに届くことを確認して、保存してください(保存先: /root/k8snp/baseline.txt)。レスポンスコード200が見える必要があります。
ポリシーを作る前に、今の状態を測っておいてください。何を変えたかを知るには、変える前を知る必要があります。
1つ付けるだけで、方式が変わる
shopにdefault-denyポリシーを作成してください。podSelector: {}で、policyTypes: [Ingress]です。そして、これでブロックされることを記録してください(保存先: /root/k8snp/deny.txt)。
podSelector: {}は、「このネームスペースのすべてのPod」という意味です。ルールを1つも指定しないと、「許可するものがない」になります。
受信側に付ける
allow-apiポリシーで、app=apiのPodだけがdbに届くようにしてください。結果を保存します(保存先: /root/k8snp/allow-pod.txt)。ポリシーは受信側(app=db)に付けます。
ポリシーのpodSelectorは保護されるPod(db)を選び、ingress.fromが許可する送信元(api)を選びます。方向を逆に書くと、何も起きません。
ネームスペース単位で許可する
allow-opsポリシーで、opsネームスペース全体を許可して、保存してください(保存先: /root/k8snp/namespace.txt)。ネームスペースは、kubernetes.io/metadata.nameラベルで選びます。
namespaceSelectorは、kubernetes.io/metadata.nameラベルで選びます。Kubernetesがすべてのネームスペースに自動で付けてくれるラベルです。
egressをブロックしたら、名前が解決できない
shopにdeny-egress(policyTypes: [Egress]、空のセレクター)を付けて、何が起きるかを見てください。そして、障害報告書を書いてください(保存先: /root/k8snp/incident.md)。原因がDNSであること、53番ポート、CoreDNSがどのネームスペースにあるかが入っている必要があります。
egressをデフォルトでブロックすると、CoreDNSへ出ていく53番も一緒にブロックされます。障害報告書に、原因・ポート・CoreDNSの場所を書いてください。
DNSだけを開けて復旧する
allow-dnsポリシーで、DNSだけを開けて復旧してください。結果を保存します(保存先: /root/k8snp/fixed.txt)。UDPとTCPの両方で、ポート53を開ける必要があります。
toにkube-systemネームスペースを選び、portsにUDP 53とTCP 53を両方入れます。
送信元を選ぶだけでは半分
allow-apiに、ポートの制限(80)を追加して、結果を保存してください(保存先: /root/k8snp/ports.txt)。
ingressの項目にportsを追加します。空にしておくと、その送信元は、対象Podのすべてのポートに届きます。
何を学んだか
policies=(ポリシーの数)とdns_fix_policy=allow-dnsの2行、そして、デフォルト許可から許可リストに切り替わる地点の説明を、/root/k8snp/report.mdに書いてください。
policies=とdns_fix_policy=の2行、そして、デフォルト許可が許可リストに切り替わる地点を説明してください。