DNSを止めたファイアウォールとアクセスを広げた許可ルール
目標
ポリシーの方向、ネームスペースセレクター、DNSへの依存、許可の和集合、明示的な拒否を、実際のCiliumで区別します。
なぜ重要なのか
セキュリティ変更のあとにtimeoutが発生したからといって、サーバーから再起動すると、原因を消してしまうことがあります。IPの対照群と名前によるリクエストを比較して依存関係を見つけ、正常なリクエストと拒否されるリクエストをあわせて確認します。ポリシーの数ではなく、実際のアクセス範囲を検証します。比較対象は最後まで残すので、全体の採点も現在の状態を再度確認します。
VM内の個人用k3sだけを使ってください。新しい環境はCilium 1.20.1とk3s 1.35.8+k3s1を使い、起動に数分かかることがあります。ステップの準備だけで、現在のステップが解決されるわけではありません。ポリシーの伝播が終わる前は、観測結果が一時的に異なることがあるので、観測後に再度採点してください。セッションが終了すると作業ファイルは消えるので、必要ならあらかじめエクスポートしてください。
ステップ
- ツールのinventoryコマンドで、11個のPodのnamespace・name・uid・ip・identityを、/root/cca-policy/inventory.jsonに保存してください。cca-blue/clientとcca-red/clientのroleラベルは同じですが、namespaceラベルとidentityは異なります。出力の実際の値を読んで比較してください。Podが再作成された場合は、記録も更新する必要があります。
- /root/cca-policy/native.yamlに、cca-blueのnative-only NetworkPolicyを作成して適用してください。podSelector.matchLabelsはtarget=native、policyTypesは[Ingress]、ingressの1つのfromの1つには、podSelector.matchLabels.role=clientだけを置きます。nativeは、青のclientだけが200で、青のstrangerと赤のclientはtimeoutである必要があります。ポリシーなしのbaselineは、3つともすべて200である必要があります。
- /root/cca-policy/ingress.yamlに、cca-blueのblue-ingress CiliumNetworkPolicyを作成して適用してください。endpointSelector.matchLabels.target=ingress、ingressの1つのfromEndpointsの1つには、matchLabels.role=clientだけを置きます。青のclientだけが200で、strangerと赤のclientはtimeoutである必要があります。nativeポリシーは削除しないでください。
- /root/cca-policy/dns-blocked.yamlに、cca-blueのdns-blocked CNPを作成して適用してください。endpointSelector.matchLabels.client-mode=blockedです。egressの1つに、toEndpoints.matchLabels={role: api, target: egress}とtoPorts.ports=[{port: "8080", protocol: TCP}]だけを置きます。DNSの許可は入れません。dns-blockedクライアントの、egressサーバーへのIPでのリクエストは200で、Service名でのリクエストはtimeoutである必要があります。
- /root/cca-policy/dns-open.yamlに、cca-blueのdns-open CNPを作成して適用してください。client-mode=openを選択します。最初のegress項目は、前のステップのAPI宛先とTCP 8080のままです。2つ目の項目のtoEndpoints.matchLabelsは、k8s:io.kubernetes.pod.namespace=kube-systemとk8s:k8s-app=kube-dnsで、toPorts.portsは、文字列53のUDPとTCPの2つの項目です。dns-openは、IPでのアクセスも名前でのアクセスも、どちらも200である必要があります。dns-blockedの制限はそのままにします。
- 初期のblue-union CNPは残します。/root/cca-policy/union.yamlに、cca-blueのred-union NetworkPolicyを作成して適用してください。target=unionを選択し、policyTypes=[Ingress]とします。ingressの1つのfromの1つに、namespaceSelector.matchLabels.team=cca-redとpodSelector.matchLabels.role=clientを一緒に入れます。青のclientと赤のclientは200で、strangerはtimeoutである必要があります。
- 初期のblue-deny CNPとred-deny NetworkPolicyは残します。/root/cca-policy/deny.yamlに、cca-blueのdeny-red CNPを作成して適用してください。target=denyを選択し、ingressDenyの1つのfromEndpointsの1つに、matchLabelsのk8s:io.kubernetes.pod.namespace=cca-redとk8s:role=clientを一緒に入れます。青のclientは200で、strangerと赤のclientはtimeoutである必要があります。
- ツールのreportコマンドの結果を、/root/cca-policy/report.jsonに保存してください。赤のclientがbaseline・union・denyに送った3つのリクエストは、それぞれ200・200・timeoutである必要があります。出力から、現在のPodのUID/IP/identity、ポリシーのUID/specのハッシュ、リクエストのsource port、同じノード・時刻のHubble FORWARDED/DROPPEDフローを突き合わせてください。ポリシーやPodを変更した場合は、レポートを作り直してください。最後の全体採点では、前のステップもすべて有効な状態である必要があります。
参考
- 観測ツール: python3 /opt/fixtures/cca-policy/runtime.pyのobserveコマンドで、ケース名を指定します。ケース名は、baseline、native、ingress、dns-blocked、dns-open、union、denyです。
- アイデンティティ: kubectl get cep -A、ポリシー: kubectl get cnp,netpol -A -o yaml
- Hubble: hubble observe --server 127.0.0.1:4245 --namespace cca-blue --last 50
- 課題ファイルは、JSONまたは単一のYAMLオブジェクトとして保存できます。値だけでなく、リストのまとまりも確認してください。
- HTTP 000は、サーバーの応答コードではありません。応答を受け取れなかったときは、実際の終了コードとフローも確認します。
2つのチームの実際のPodとセキュリティアイデンティティを記録する
ツールのinventoryコマンドで、11個のPodのnamespace・name・uid・ip・identityを、/root/cca-policy/inventory.jsonに保存してください。cca-blue/clientとcca-red/clientのroleラベルは同じですが、namespaceラベルとidentityは異なります。出力の実際の値を読んで比較してください。Podが再作成された場合は、記録も更新する必要があります。
名前が同じであることと、同じエンドポイントであることは、別のことです。UIDとセキュリティラベルの集合をあわせて読んでみてください。
標準のNetworkPolicyもラベルを選択する
/root/cca-policy/native.yamlに、cca-blueのnative-only NetworkPolicyを作成して適用してください。podSelector.matchLabelsはtarget=native、policyTypesは[Ingress]、ingressの1つのfromの1つには、podSelector.matchLabels.role=clientだけを置きます。nativeは、青のclientだけが200で、青のstrangerと赤のclientはtimeoutである必要があります。ポリシーなしのbaselineは、3つともすべて200である必要があります。
ポリシーの対象セレクターと、入ってくる相手のセレクターを、取り違えて読まないでください。peerにnamespaceSelectorがない場合の範囲を確認してください。
CNPでも2つのチームの境界を確認する
/root/cca-policy/ingress.yamlに、cca-blueのblue-ingress CiliumNetworkPolicyを作成して適用してください。endpointSelector.matchLabels.target=ingress、ingressの1つのfromEndpointsの1つには、matchLabels.role=clientだけを置きます。青のclientだけが200で、strangerと赤のclientはtimeoutである必要があります。nativeポリシーは削除しないでください。
CNPという名前だけで、すべてのネームスペースが自動的につながるわけではありません。同じroleの別のチームも、対照用のリクエストとして残してください。
APIは生かしたまま、名前解決だけが塞がれた障害を再現する
/root/cca-policy/dns-blocked.yamlに、cca-blueのdns-blocked CNPを作成して適用してください。endpointSelector.matchLabels.client-mode=blockedです。egressの1つに、toEndpoints.matchLabels={role: api, target: egress}とtoPorts.ports=[{port: "8080", protocol: TCP}]だけを置きます。DNSの許可は入れません。dns-blockedクライアントの、egressサーバーへのIPでのリクエストは200で、Service名でのリクエストはtimeoutである必要があります。
サーバーのegress Podは、ポート8080の対照群です。DNSが抜けたからといって、IPへの直接の経路までブロックされるなら、セレクターや方向も確認する必要があります。
別の比較用クライアントで、DNSだけを追加して復旧する
/root/cca-policy/dns-open.yamlに、cca-blueのdns-open CNPを作成して適用してください。client-mode=openを選択します。最初のegress項目は、前のステップのAPI宛先とTCP 8080のままです。2つ目の項目のtoEndpoints.matchLabelsは、k8s:io.kubernetes.pod.namespace=kube-systemとk8s:k8s-app=kube-dnsで、toPorts.portsは、文字列53のUDPとTCPの2つの項目です。dns-openは、IPでのアクセスも名前でのアクセスも、どちらも200である必要があります。dns-blockedの制限はそのままにします。
すべての外部接続を開ける代わりに、名前解決のエンドポイントとポートだけを許可します。復旧用クライアントのセレクターを確認してください。
許可を追加すると、別のチームに扉が開く
初期のblue-union CNPは残します。/root/cca-policy/union.yamlに、cca-blueのred-union NetworkPolicyを作成して適用してください。target=unionを選択し、policyTypes=[Ingress]とします。ingressの1つのfromの1つに、namespaceSelector.matchLabels.team=cca-redとpodSelector.matchLabels.role=clientを一緒に入れます。青のclientと赤のclientは200で、strangerはtimeoutである必要があります。
2つの条件は、同じpeerの中にまとめますが、既存のCNPと新しいNPの許可は、別々の経路として合算されます。
明示的な拒否を、許可ポリシーより優先させる
初期のblue-deny CNPとred-deny NetworkPolicyは残します。/root/cca-policy/deny.yamlに、cca-blueのdeny-red CNPを作成して適用してください。target=denyを選択し、ingressDenyの1つのfromEndpointsの1つに、matchLabelsのk8s:io.kubernetes.pod.namespace=cca-redとk8s:role=clientを一緒に入れます。青のclientは200で、strangerと赤のclientはtimeoutである必要があります。
許可ポリシーを削除して赤チームをブロックすると、明示的なdenyの優先順位の比較が失われます。初期の許可2つは残してください。
現在のポリシーと、同じリクエストのフローでインシデントを報告する
ツールのreportコマンドの結果を、/root/cca-policy/report.jsonに保存してください。赤のclientがbaseline・union・denyに送った3つのリクエストは、それぞれ200・200・timeoutである必要があります。出力から、現在のPodのUID/IP/identity、ポリシーのUID/specのハッシュ、リクエストのsource port、同じノード・時刻のHubble FORWARDED/DROPPEDフローを突き合わせてください。ポリシーやPodを変更した場合は、レポートを作り直してください。最後の全体採点では、前のステップもすべて有効な状態である必要があります。
DROPPEDの行を適当に貼り付けないでください。TCPの送信元ポートと両側のエンドポイントを合わせ、HTTPの結果とパケット転送の観察を区別してください。