デフォルト拒否から始めるクラスタ構築
目標
ネームスペース1つをデフォルト拒否の状態にしてから、必要な通り道だけを1つずつ開け直す順序を手に覚えさせ、コントロールプレーンの設定ファイルで何を確認すべきかを自分で書いてみます。
なぜ重要なのか
Kubernetesのデフォルト値は、すべて開いています。ポリシーが1つもないPodは、クラスター内のどのPodとも通信でき、ノードのメタデータアドレスにもそのまま到達します。そのため、セキュリティ設計の最初の動作は、何かを塞ぐルールを追加することではなく、すべてを塞いでおいて必要なものだけを元に戻すことです。この順序を逆にすると、「何を抜かしたのか」を永遠に知ることができません。設定ファイルの監査も同じ考え方です。実行中のプロセスを観察するより、マニフェストを1枚読むほうが速く、何が抜けているかを一覧にできます。
このラボ環境には実際のデータプレーンがないため、ポリシーが本当にトラフィックを遮断するかは確認できません。採点はオブジェクトのスペックが意図どおりに書かれているかを見ます。試験で求められるのも正確なスペックです。
ステップ
- ネームスペース
cks-netを作成し、ラベルtier=restrictedを付けてください。 cks-netにNetworkPolicydefault-deny-allを作成してください。podSelectorは空にし、policyTypesにはIngressとEgressの両方を書きますが、ingressとegressのルールは1つも置かないでください。cks-netにNetworkPolicyallow-dns-egressを作成してください。すべてのPodを対象にし、policyTypesはEgressのみとします。egressの宛先は、kubernetes.io/metadata.name=kube-systemラベルを持つネームスペースの中のk8s-app=kube-dnsラベルのPodで、ポートはUDP 53とTCP 53の2つです。cks-netにNetworkPolicyallow-frontend-to-apiを作成してください。対象はapp=apiのPod、ingressの許可元は同じネームスペースのapp=frontendのPod、ポートはTCP 8080です。cks-netにNetworkPolicydeny-node-metadataを作成してください。対象はすべてのPod、policyTypesはEgress、egressの宛先はipBlockで、cidr: 0.0.0.0/0にexcept: [169.254.169.254/32]を指定します。cks-netにkubernetes.io/tlsタイプのSecretshop-tlsを作成してください。tls.crtとtls.keyがどちらも空でないようにします。cks-netにIngressshopを作成してください。spec.tls[0]のhostsはshop.cks.local、secretNameはshop-tlsとし、spec.rules[0].hostもshop.cks.localにして、パス/をServiceapiのポート8080に振り向けます。/root/cks-cluster-setup/kube-apiserver.yamlに、CISの推奨を反映した静的Podマニフェストを書いてください。kind: Podで、コンテナ名はkube-apiserverとし、command配列には--anonymous-auth=false、--authorization-mode=Node,RBAC、--profiling=false、--audit-log-path=/var/log/kubernetes/audit.log、--enable-admission-plugins=NodeRestriction,PodSecurityを含めます。--insecure-portで始まるフラグは1つも含めないでください。
参考
kubectl create ns cks-netのあとにkubectl label ns cks-net tier=restrictedを実行します。- ポリシーのひな形は
kubectl create networkpolicyでは作れません。kubectl explain networkpolicy.spec.egressでフィールドを確認しながら、YAMLを直接書くほうが速いです。 - 自己署名証明書の例:
openssl req -x509 -newkey rsa:2048 -nodes -keyout tls.key -out tls.crt -days 365 -subj "/CN=shop.cks.local"を実行し、続けてkubectl create secret tls shop-tls --cert=tls.crt --key=tls.key -n cks-netを実行します。 - よくある間違い1: デフォルト拒否を適用したあとにDNS egressを忘れると、アプリケーションは「接続拒否」ではなく「名前が見つからない」というエラーで落ちます。
- よくある間違い2:
exceptの範囲がcidrの外にあると、APIサーバーがポリシーを拒否します。
隔離するネームスペースを作る
ネームスペースcks-netを作成し、ラベルtier=restrictedを付けてください。
ネームスペースを作成してラベルを付けます。kubectl create nsのあとにkubectl label nsを続けて実行しても、マニフェスト1枚で作成しても構いません。
ingressとegressをすべて遮断するデフォルトポリシー
cks-netにNetworkPolicydefault-deny-allを作成してください。podSelectorは空にし、policyTypesにはIngressとEgressの両方を書きますが、ingressとegressのルールは1つも置かないでください。
podSelector: {}はネームスペース内のすべてのPodを選択します。policyTypesに方向を書き、ingress/egressのルール一覧をまったく置かなければ、その方向はすべて拒否されます。
DNSだけを例外として開けるegress
cks-netにNetworkPolicyallow-dns-egressを作成してください。すべてのPodを対象にし、policyTypesはEgressのみとします。egressの宛先は、kubernetes.io/metadata.name=kube-systemラベルを持つネームスペースの中のk8s-app=kube-dnsラベルのPodで、ポートはUDP 53とTCP 53の2つです。
kube-dnsのPodはkube-systemネームスペースにあり、k8s-app=kube-dnsラベルが付いています。ネームスペースはkubernetes.io/metadata.nameラベルで選択できます。ポート53はUDPとTCPの両方が必要です。
必要なPodの組だけを開けるingress
cks-netにNetworkPolicyallow-frontend-to-apiを作成してください。対象はapp=apiのPod、ingressの許可元は同じネームスペースのapp=frontendのPod、ポートはTCP 8080です。
podSelectorで受け取る側を選び、ingress[].from[].podSelectorで送る側を選びます。ポートを書かないと、すべてのポートが開きます。
ノードのメタデータへ出る経路を切り取る
cks-netにNetworkPolicydeny-node-metadataを作成してください。対象はすべてのPod、policyTypesはEgress、egressの宛先はipBlockで、cidr: 0.0.0.0/0にexcept: [169.254.169.254/32]を指定します。
ipBlockはcidr1つにexceptの一覧を付けられます。exceptの範囲は必ずcidrの中に収まっている必要があります。
Ingress用のTLS Secret
cks-netにkubernetes.io/tlsタイプのSecretshop-tlsを作成してください。tls.crtとtls.keyがどちらも空でないようにします。
kubernetes.io/tlsタイプのSecretには、tls.crtとtls.keyの2つのキーが必ず必要です。opensslで自己署名証明書を作成しても、マニフェストにbase64の値を直接入れても構いません。
IngressにTLSを接続する
cks-netにIngressshopを作成してください。spec.tls[0]のhostsはshop.cks.local、secretNameはshop-tlsとし、spec.rules[0].hostもshop.cks.localにして、パス/をServiceapiのポート8080に振り向けます。
Ingressのspec.tlsは、hostsの一覧とsecretNameを組にして書きます。spec.rulesのホストとspec.tls[].hostsが食い違うと、証明書が付きません。
kube-apiserverマニフェストを監査する
/root/cks-cluster-setup/kube-apiserver.yamlに、CISの推奨を反映した静的Podマニフェストを書いてください。kind: Podで、コンテナ名はkube-apiserverとし、command配列には--anonymous-auth=false、--authorization-mode=Node,RBAC、--profiling=false、--audit-log-path=/var/log/kubernetes/audit.log、--enable-admission-plugins=NodeRestriction,PodSecurityを含めます。--insecure-portで始まるフラグは1つも含めないでください。
静的Podマニフェストはkind: Podで、フラグはspec.containers[0].command配列に入ります。CISは、匿名認証の遮断、Node+RBAC認可、プロファイリングの無効化、NodeRestrictionアドミッションを推奨しており、認証なしの平文ポートのフラグはそもそも存在してはいけません。