SSD Nodes Learn 🎉 VPS $5.50/月〜
ガイド Matt Connor著者 Matt Connor ・更新日 2026-08-21

VPSのシングルノードk3sを安全にする方法

初期状態のk3sでは、TCP 6443と10250、kubeconfigの権限、NodePort、特権podが公開リスクになります。各設定を数分で確認し、堅牢化する手順を解説します。

シングルノード k3s クラスターを初日に公開する箇所

パブリック VPS 上のシングルノード k3s クラスターでは、1 行のインストーラーが完了した翌日までに、5 つの箇所が具体的な攻撃対象になります。Kubernetes API server の TCP 6443、kubelet の TCP 10250、ディスク上に保存された kubeconfig ファイル、ファイアウォールでは検出できない NodePort の範囲、そして privileged または hostPath の要求を許可された pod です。それぞれ数分で対策できます。このガイドでは、k3s がすでに稼働していることを前提とします。まだ稼働していない場合は、まず VPS へのシングルノード k3s インストールを行い、その後に戻ってください。

変更する前に、待ち受けているポートを確認します。

sudo ss -tulpn | grep -E '6443|10250|10256|8472'

デフォルトのインストールでは、6443(API server)、10250(kubelet)、10256(kube-proxy のヘルスチェック)、8472/udp(VXLAN(virtual extensible LAN)を使用する flannel オーバーレイ)が表示されます。k3s はデフォルトで 0.0.0.0 にバインドするため、これらはすべて loopback だけでなくパブリックアドレス上でも待ち受けます。

ポート 6443 がクラスター全体への入口になる理由

ポート 6443 に管理者権限で認証できるものは、すべて Pod を作成できます。そして Pod はホスト上で root になれます。ポート 6443 はマシンへの入口です。

6443 を公開しても、直ちに侵害されるわけではありません。Kubernetes はパスワードを受け付けないためです。クライアント証明書または bearer token が必要です。ただし、次の2点は変わりません。

1つ目は、API server が資格情報なしで一部のリクエストに応答することです。Kubernetes のデフォルト RBAC(role-based access control)は、グループ system:unauthenticatedsystem:public-info-viewer という名前の Role にバインドします。この Role には、/version/healthz/livez/readyz が許可されています。別のマシンから次を実行します。

curl -sk https://YOUR_SERVER_IP:6443/version

これにより、正確な Kubernetes バージョンが返されます。これは CVE(common vulnerabilities and exposures)検索の入力になり、スキャナーがそのマシンに関心を持つ理由になります。これらのパス以外は拒否され、拒否理由には対象が明示されます。

forbidden: User "system:anonymous" cannot get path "/api"

2つ目は、API server のすべてのバグが、そのポートを開いている間はリモートから到達可能になることです。この時点で、パッチ適用は任意ではありません。

低コストの対策は、firewall ルールです。ここでは k3s が同じ kernel を通してクラスターのトラフィックをルーティングするため、ufw には1行より多くの設定が必要です。

sudo ufw default deny incoming
sudo ufw allow OpenSSH
sudo ufw allow from 203.0.113.10 to any port 6443 proto tcp
sudo ufw allow from 10.42.0.0/16 to any
sudo ufw allow from 10.43.0.0/16 to any
sudo ufw enable

最後の2つのルールは、k3s のドキュメントにそのまま記載されています。10.42.0.0/16 はデフォルトの Pod ネットワークで、10.43.0.0/16 はデフォルトの Service ネットワークです。これらがないと ufw がクラスター内部のトラフィックを破棄するため、Pod は API server と相互に通信できなくなります。k3s の例では、どこからでも 6443 への接続を許可しています。これを自分のアドレスに置き換えることが、行う価値のある変更です。ufw が初めての場合は、VPS 向け ufw firewall の基本で、この設定が前提とするデフォルトポリシーを説明しています。

k3s のドキュメントは、overlay ポートについて明確に警告しています。「ノード上の VXLAN ポートをインターネットに公開してはなりません。公開すると、誰でもクラスターのネットワークにアクセスできるようになります」。受信トラフィックに対するデフォルト拒否ポリシーを設定すれば、ポートを個別に指定しなくても対処できます。

より強力な対策は、公開アドレス経由で API に接続するのをやめ、代わりに VPN または mesh アドレスを使用することです。server certificate には接続先のアドレスを記載する必要があるため、/etc/rancher/k3s/config.yaml の SAN(subject alternative name)として追加します。

tls-san:
  - 10.8.0.1
  - k3s.example.com
secrets-encryption: true
sudo systemctl restart k3s
sudo k3s secrets-encrypt status

secrets-encryption: true は datastore 内の Secret オブジェクトを暗号化します。k3s のドキュメントには、「Secrets-encryption は server を再起動せずに既存の server で有効化できない」と記載されています。変更前に書き込まれた Secret は、sudo k3s secrets-encrypt reencrypt を実行するまで以前の形式のままです。この対策で得られる効果を明確に理解してください。バックアップからコピーされた datastore ファイルは保護できます。しかし、API server と通信できる相手には効果がありません。API server は、読み取りを許可された相手に対して Secret を復号するためです。同じ区別は、あらゆる self-hosted secret store にも当てはまります。そのため、Vaultwarden の hardening 手順では、暗号化そのものではなく admin token とバックアップファイルを重点的に扱います。

ポート 10250 の kubelet が重要な理由

kubelet はコンテナを起動するエージェントです。ポート 10250 の API では、Pod の一覧取得やコンテナ内でのコマンド実行ができます。匿名リクエストを受け付ける kubelet は、そのマシン上のすべてのワークロードに対するリモートシェルになります。

次のコマンドで確認します。

curl -sk https://127.0.0.1:10250/pods | head -c 60

現在の k3s は Unauthorized を返します。kubelet がすべての呼び出し元について、API サーバーに認証と認可を要求するためです。代わりに JSON 形式の Pod 一覧が返る場合は、匿名アクセスが有効です。ポート 10250 に到達できるユーザーは、コンテナ内のデータを読み取り、コマンドを実行できます。

いずれの場合も、外部からのアクセスを閉じてください。単一ノードでは、kubelet のクライアントは同じホスト上の control plane だけです。この通信は loopback インターフェース経由で入り、ufw はデフォルトで loopback からの通信を許可します。インターネットからの 10250 へのアクセスを拒否しても、問題はありません。壊れた kubectl top や、正常に起動しない metrics-server の問題からこのページに来た場合は、原因を kubelet ポート 10250 のエラー にまとめています。

kubeconfig はクラスタ管理者の認証情報です

k3s は /etc/rancher/k3s/k3s.yaml を root が所有し、モード 600 で作成します。ドキュメントには、モードを変更した場合の結果が次のように記載されています。「kubeconfig ファイルは root が所有し、デフォルトのモード 600 で書き込まれます。モードを 644 に変更すると、ホスト上の他の非特権ユーザーも読み取れるようになります。」

これは、モード 644 にすると、ローカルのすべてのアカウントがクラスタ管理者になるという意味です。多くの手順では、通常 --write-kubeconfig-mode 644 としてまさにこの変更を推奨しており、sudo なしで kubectl を実行できるようにします。これは、シェルにアクセスできる全員に管理者認証情報を渡すことで機能します。

代わりに、ファイルを特定のユーザー1人だけにコピーします。

mkdir -p ~/.kube
sudo install -o "$USER" -g "$USER" -m 600 /etc/rancher/k3s/k3s.yaml ~/.kube/config
kubectl get nodes

次に、元のファイルの権限が維持されていることを確認します。

stat -c '%a %U:%G' /etc/rancher/k3s/k3s.yaml

求めている結果は 600 root:root です。このファイルには system:masters のメンバー用クライアント証明書が含まれています。system:masters は、API サーバーが無条件に許可するグループです。そのため、この証明書に対しては RBAC ルールが参照されません。Kubernetes には証明書失効リストがないため、漏えいしたコピーはクラスタの認証局をローテーションするまで有効です。SSH の秘密鍵と同じように扱い、このファイルにアクセスできるアカウントを少数に限定してください。これは VPS の最小権限ユーザーアカウントと同じ考え方です。

NodePort の通信がファイアウォールで検出されない

A type: NodePort Service は、ノードが保持するすべてのアドレスで、30000〜32767 の範囲にポートを開きます。パブリックアドレスも対象です。type: LoadBalancer Service は k3s でさらに動作します。組み込みのロードバランサーである ServiceLB が、kube-system に Service ごとの小さな Pod を 1 つずつスケジュールし、Service のポートをホスト上で直接確保します。

kubectl -n kube-system get pods | grep svclb
sudo ss -tulpn | grep -E ':3[0-2][0-9]{3}'

ここで、多くの人が驚く動作が起きます。ufw でそのポートをブロックしても、応答が続きます。

sudo ufw deny 30080/tcp
curl http://YOUR_SERVER_IP:30080

パケットの通過経路が原因で、ページは引き続き読み込まれます。kube-proxy は nat テーブルの PREROUTING チェーンに DNAT(宛先ネットワークアドレス変換)ルールを書き込みます。PREROUTING は、フィルタリングの判定より前に実行されます。宛先は Pod のアドレスに変わります。このアドレスはホストではないため、カーネルはパケットを FORWARD チェーンへ送り、INPUT を通しません。ufw のルールは INPUT にあります。そのため、パケットは ufw のルールに到達しません。ジャンプの順序は次のように確認できます。

sudo iptables -S PREROUTING -t nat | head
sudo iptables -S FORWARD | head

KUBE-SERVICES は PREROUTING の先頭にあり、FORWARD にある Kubernetes のジャンプは ufw 独自のチェーンより上にあります。これは Docker が ufw を経由せずにポートを公開する場合と同じ仕組みです。対処方法も同じです。

  • プロバイダーのネットワークファイアウォールでフィルタリングします。これはマシンの前段で動作するため、カーネルがどのようにルーティングするかに左右されません。
  • NodePort と LoadBalancer を使用しません。Service を ClusterIP に残し、既存の SSH セッション経由で kubectl port-forward を使ってアクセスします。
  • 80 と 443 で 1 つの Ingress だけを公開し、それ以外は公開しません。
  • kube-apiserver-argservice-node-port-range で範囲を狭めます。これにより、誤って NodePort を使用しても、監視対象の範囲に収まります。

ufw は引き続き実行する価値があります。ホスト自体を宛先とする通信を制御するため、SSH と API server が対象になります。ただし、Pod の通信は制御しません。それを制御できると考えると、データベースが到達可能な状態になります。

hostPath または privileged を使用する Pod は VPS 上で root と同等です

コンテナは、カーネル上で動作する通常のプロセスですが、カーネルの一部しか参照できません。複数の Pod フィールドを使用すると、この制限が解除されます。

  • securityContext.privileged: true は、コンテナにすべての Linux capability とホストデバイスへのアクセスを与えます。
  • hostPath は、ホストのディレクトリを Pod にマウントします。/ を read-write でマウントする Pod は、/root/.ssh/authorized_keys に鍵を追記できます。
  • hostPID: true は、コンテナをホストのプロセス namespace に配置します。この namespace で nsenter を PID 1 に対して実行すると、ホストの shell が開きます。
  • hostNetwork: true は、コンテナをホストの network stack に配置します。これにより、ホストのポートで待ち受け、loopback に bind されたサービスへ接続できます。

したがって、「ここで誰が Pod を作成できるか」は「この VPS で誰が root か」と同じ問題です。任意の namespace の Pod に対して create を持つ ServiceAccount は、Pod が事前に拒否されない限り root と同等です。

この拒否を行うのが、API server に組み込まれた Pod Security admission です。簡単な方法は、namespace ごとに label を設定することです。再起動は必要ありません。

kubectl label namespace default \
  pod-security.kubernetes.io/enforce=baseline \
  pod-security.kubernetes.io/enforce-version=latest \
  pod-security.kubernetes.io/warn=restricted

baseline は、上記の4つのフィールドをすべて拒否します。restricted はさらに、root 以外のユーザー、seccomp (secure computing mode) profile、権限昇格の禁止、capability の ALL への削除を要求します。そのため、多くの公開 chart は動作しなくなります。baseline を enforcing にし、restricted については warning のみにすると、適用を確定する前に何が壊れるかを確認できます。

動作を確認します。

kubectl apply -f - <<'EOF'
apiVersion: v1
kind: Pod
metadata:
  name: pstest
spec:
  containers:
  - name: app
    image: busybox
    command: ["sleep", "60"]
    securityContext:
      privileged: true
EOF

API server はこれを拒否し、拒否したフィールドを示します。

Error from server (Forbidden): error when creating "STDIN": pods "pstest" is forbidden: violates PodSecurity "baseline:latest": privileged (container "app" must not set securityContext.privileged=true)

各 namespace に label を設定する代わりに、cluster 全体のデフォルトを設定する場合、k3s は /var/lib/rancher/k3s/server/psa.yaml に admission configuration file を置く方法を説明しています。

apiVersion: apiserver.config.k8s.io/v1
kind: AdmissionConfiguration
plugins:
- name: PodSecurity
  configuration:
    apiVersion: pod-security.admission.config.k8s.io/v1beta1
    kind: PodSecurityConfiguration
    defaults:
      enforce: "baseline"
      enforce-version: "latest"
      warn: "restricted"
      warn-version: "latest"
    exemptions:
      namespaces: [kube-system]

/etc/rancher/k3s/config.yaml で API server にこのファイルを指定し、k3s を再起動します。

kube-apiserver-arg:
  - 'admission-control-config-file=/var/lib/rancher/k3s/server/psa.yaml'

kube-system の除外は必須です。k3s 自身の ServiceLB Pod は host port を使用しますが、baseline はそれを禁止します。そのため、kube-system をリストから外すと、次回それらの Pod が再作成された際に拒否されます。admission の変更後に k3s を再起動する場合は、SSH セッションをもう1つ開いたままにしてください。

ここで、もう1つ確認しておきます。k3s には network policy controller が同梱され、デフォルトで有効になっています。そのため、この cluster では NetworkPolicy オブジェクトが追加のインストールなしで有効になります。これはすべての Kubernetes distribution に当てはまるわけではありません。侵害された Pod から他の領域への接続を止めるための機能です。

使用しない同梱コンポーネントを削除する

インストーラーは複数のアドオンをデプロイします。各アドオンはリスナーを追加し、パッチ対象も1つ増やします。--disable には次の値を指定できます: corednsservicelbtraefiklocal-storagemetrics-serverruntimes

coredns は残してください。これがないと、クラスター内で名前を解決できません。それ以外は選択項目です。/etc/rancher/k3s/config.yaml では次のように指定します。

disable:
  - traefik
  - servicelb
disable-helm-controller: true
sudo systemctl restart k3s
kubectl get pods -A

k3s は無効にしたコンポーネントを削除するため、traefik pod と svclb- pod は自動的に消えます。先に影響を確認してください。servicelb を削除すると、すべての type: LoadBalancer service は割り当て先がないため、常に <pending> のままになります。traefik を削除すると ingress controller がなくなるため、Ingress object は何も実行しません。ホスト上の reverse proxy など、別の方法でトラフィックを処理する場合に無効化してください。使用する場合はそのままにします。disable-helm-controller: trueHelmChart resource を監視する controller を削除します。helm を自分で実行している場合、この特権コンポーネントは使用していません。

デフォルトの ServiceAccount トークンの自動マウントを停止する

特に指定しない限り、すべての Pod には /var/run/secrets/kubernetes.io/serviceaccount/token で ServiceAccount トークンが設定されます。default ServiceAccount には RBAC 権限がないため、トークンだけでは大きなことはできません。ただし、侵害されたコンテナ内の攻撃者に有効な認証情報と到達可能な API server を与えることになります。これは、多くのクラスタ権限昇格の解説で最初に行われる手順です。

k3s のハードニングガイドでは、namespace 単位でこれを無効にします。

kubectl patch serviceaccount --namespace default default --patch '{"automountServiceAccountToken": false}'
kubectl patch serviceaccount --namespace kube-node-lease default --patch '{"automountServiceAccountToken": false}'
kubectl patch serviceaccount --namespace kube-public default --patch '{"automountServiceAccountToken": false}'

設定が反映されたことを確認します。まず Pod が起動するまで数秒待ちます。

kubectl run t --image=busybox --restart=Never --command -- sleep 30
kubectl exec t -- ls /var/run/secrets/kubernetes.io/serviceaccount

パスが存在しないため、lsNo such file or directory を出力します。API へのアクセスが本当に必要なワークロードは、自身の Pod spec で automountServiceAccountToken: true を設定します。そのため、必要なアクセスが恒久的に禁止されるわけではありません。これは単独でも小さな改善です。より重要なのは、実際の権限を持つ ServiceAccount をワークロードに割り当てないことです。現在のトークンで何ができるかは、次のように確認できます。

kubectl auth can-i --list --as=system:serviceaccount:default:default

リソース制限により、1 つの Pod がクラスター全体を停止させないようにする

単一ノードでは、コントロールプレーンとワークロードが同じカーネルとメモリプールを共有します。メモリリークを起こした Pod が、必ず単独で終了するとは限りません。カーネルの OOM(out of memory)killer は、大きなプロセスを優先するスコアで終了対象を選びます。k3s は大きく、長時間稼働するプロセスであるため、Pod ではなくクラスターが停止することがあります。ワークロードを再起動するプロセス自体が停止しているため、その後にワークロードを再起動するものはありません。

LimitRange は、リソース制限を指定していない Pod に制限を設定します。

apiVersion: v1
kind: LimitRange
metadata:
  name: defaults
  namespace: default
spec:
  limits:
  - type: Container
    default:
      cpu: 500m
      memory: 512Mi
    defaultRequest:
      cpu: 50m
      memory: 128Mi

ResourceQuota は、namespace 全体が要求できるリソース量に上限を設定します。

apiVersion: v1
kind: ResourceQuota
metadata:
  name: cap
  namespace: default
spec:
  hard:
    limits.cpu: "3"
    limits.memory: 3Gi
    pods: "20"

次に、k3s 自体のための余裕を /etc/rancher/k3s/config.yaml に確保します。

kubelet-arg:
  - 'system-reserved=cpu=250m,memory=512Mi'

この違いは 2 か所に現れます。コンテナが自身の制限を超えて終了した場合、kubectl describe podLast StateReason: OOMKilled と記録され、ノード上の他の処理は継続します。ノード全体のメモリが枯渇した場合は、dmesgKilled process の行が残り、通常は同じノード上の他の処理も停止します。前者は、設定した制限が機能した結果です。後者を防ぐために、リソース制限を設定します。

CI と k3s クラスターのマニフェストを定期的にスキャンする

スキャンは 2 か所で行います。検出できる問題はそれぞれ異なります。まず Trivy をインストールします。プロジェクトは各リリースで Debian パッケージを提供しています。2026 年 8 月時点では 0.74.0 が最新だったため、自動化にバージョンを固定する前に releases ページで新しいバージョンを確認してください。

sudo apt-get install -y wget
wget -q https://github.com/aquasecurity/trivy/releases/download/v0.74.0/trivy_0.74.0_Linux-64bit.deb
sudo dpkg -i trivy_0.74.0_Linux-64bit.deb
trivy --version

1 つ目は、クラスターに適用する前のマニフェストです。

trivy fs --scanners misconfig --severity HIGH,CRITICAL --exit-code 1 ./deploy

--exit-code 1 は検出結果があると CI(継続的インテグレーション)ジョブを失敗させます。各結果には、問題のあるフィールドと重大度が示されます。そのため、意図せずコミットした privileged: true は API サーバーに到達する前にビルドを失敗させます。受け入れると判断した検出結果は .trivyignore ファイルに記録します。この判断を、原因となったマニフェストのそばで git に保持できます。

2 つ目は、稼働中のクラスターです。こちらは定期的にスキャンします。

trivy k8s --compliance=k8s-cis-1.23 --report summary

このコマンドについては、注意すべき点があります。trivy k8s は、ノードレベルの設定を調べるためにホストアクセスを必要とするノードコレクター Pod をデプロイします。特権 Pod の拒否を始めたばかりのクラスターでは、この点を認識し、回避策で済ませないことが重要です。trivy k8s --report summary --disable-node-collector を指定するとコレクターがスキップされ、ノードレベルのチェックも失われます。

kube-bench はホスト側を対象にします。ファイル権限やプロセスフラグなどを確認します。CIS(Center for Internet Security)ベンチマークの大部分はこの領域にあります。k3s 用のプロファイルも提供されています。upstream の Job マニフェストを取得して調整します。

curl -sfL -o kube-bench-job.yaml https://raw.githubusercontent.com/aquasecurity/kube-bench/main/job.yaml

コンテナコマンドを ["kube-bench", "--benchmark", "k3s-cis-1.7"] に変更し、/etc/kubernetes/var/lib/etcd のマウントを /etc/rancher/var/lib/rancher に置き換えます。k3s はそこにファイルを保持するためです。続いて、次を実行します。

kubectl apply -f kube-bench-job.yaml
kubectl logs -l app=kube-bench --tail=-1

この Job の構成を確認してください。hostPID: true とホストディレクトリのマウントです。これは、1 つ前のセクションで拒否し始めた Pod の形そのものです。例外にした kube-system namespace で実行し、出力を確認してから kubectl delete job kube-bench します。スキャナーに特権が必要だからといって、特権ワークロードの禁止をやめる理由にはなりません。

イメージの内容は別の問題です。trivy image ghcr.io/example/app:1.4 はイメージ内のパッケージデータベースを読み取り、既知の脆弱性があるパッケージを一覧表示します。これは サーバーに既知の CVE がないか確認する場合と同じ処理です。

ここで、スキャナーの出力について率直に説明します。単一ノードのホビークラスターでは、多数の CIS コントロールに失敗します。その多くは判定として正しい一方で、あなたの環境には関係ありません。ベンチマークは、複数ノードおよび複数テナントのクラスターを前提に作成されています。etcd を別ホストで実行すること、監査ログをマシン外へ送信すること、kubelet 用の認証局を分離すること、対象外のコンプライアンス体制向けに admission plugin を有効にすることなどが前提です。k3s は意図的に、コントロールプレーンを 1 つの設定ファイルを使う単一プロセスとして実行します。そのため、kube-scheduler マニフェストファイルの権限を確認するコントロールは合格できません。そのようなファイルが存在しないためです。

次の順序で失敗を確認し、価値がなくなった時点で止めます。/etc/rancher/var/lib/rancher 配下のファイルモードおよび所有者、anonymous または unauthenticated access に言及する項目、0.0.0.0 にバインドされたコンポーネントを報告する項目、そして特別な理由なく UID 0 で実行されているコンテナを確認します。残りは、2 台目のノードまたはアクセス権を持つ 2 人目の担当者ができてからで構いません。無視する 100 行のレポートは、対応する 5 行のレポートより価値がありません。

単一ノードの k3s を強化する手順

  1. ufw のデフォルト受信ポリシーを deny に設定し、SSH を許可します。自分のアドレスからの 6443 番ポートへの接続と、Pod ネットワークおよび Service ネットワークも許可します。
  2. kubeconfig を mode 600 で自分のユーザー用にコピーし、--write-kubeconfig-mode 644 は絶対に設定しません。
  3. 10250 番ポートの kubelet が匿名リクエストを拒否することを確認し、このポートをインターネットから到達可能にしないでください。
  4. 使用しない組み込みコンポーネントを無効にしてから、k3s を再起動します。
  5. enforce=baselinewarn=restricted を使用して、Pod Security admission 用のラベルを namespace に設定します。
  6. デフォルトの ServiceAccount トークンの自動マウントを無効にします。
  7. LimitRange と ResourceQuota を追加し、k3s 用に CPU とメモリを確保します。
  8. trivy fs --scanners misconfig を CI に組み込み、毎月 CIS スキャンを実行します。

基盤となるホストにも、他のサーバーと同じ保守が必要です。k3s では、この点に注意が必要です。k3s は apt ではなくスクリプトでインストールされるため、apt upgrade の対象になりません。Ubuntu で unattended upgrades を有効にする などを利用し、OS は独自のスケジュールでパッチを適用してください。k3s は、必要な channel またはバージョンを指定してインストーラーを再実行し、計画的にアップグレードします。1 台のマシンに 2 つの更新経路があると忘れやすいため、どのコンポーネントがどの経路に従うかを記録してください。

FAQ

k3s API server をポート 6443 でインターネットに公開しても安全ですか?

API server は client certificate または token を要求し、それ以外の要求を forbidden: User "system:anonymous" で拒否するため、公開しただけで無防備になるわけではありません。ただし、2 つのリスクが残ります。匿名の呼び出し元でも /version を読み取れるため、scanner に Kubernetes のリリースを特定され、調査対象にされます。また、API server に将来脆弱性が見つかった場合、ポートが開いている間はリモートから到達可能です。single-node cluster で 6443 が必要なのは、自分の kubectl 以外にはありません。そのため、sudo ufw allow from YOUR_IP to any port 6443 proto tcp で自分のアドレスからのアクセスだけを許可し、残りは default deny policy に任せます。

ufw のルールで NodePort service をブロックできないのはなぜですか?

パケットが、ルールを配置した chain に到達しないためです。kube-proxy は nat table の PREROUTING chain に DNAT rule を配置します。この chain が先に実行され、宛先が pod address に書き換えられます。その後、パケットはローカルに配信されずに転送されるため、FORWARD を通過し、ufw のルールがある INPUT を通りません。sudo iptables -S PREROUTING -t nat | head で確認できます。NodePort は provider の network firewall でフィルタリングするか、type: NodePort を避けて kubectl port-forward で service にアクセスしてください。

k3s を --write-kubeconfig-mode 644 で実行すべきですか?

いいえ。k3s のドキュメントには、その動作が明記されています。「mode を 644 に変更すると、host 上の他の unprivileged user が読み取れるようになります。」この file には system:masters の client certificate が含まれているため、mode 644 にすると、すべての local account が cluster administrator になります。代わりに sudo install -o "$USER" -g "$USER" -m 600 /etc/rancher/k3s/k3s.yaml ~/.kube/config で 1 人の user にコピーし、元の file は 600 root:root のままにしてください。

k3s ではどの kube-bench benchmark を使用すべきですか?

k3s-cis-1.7 を使用してください。kube-bench のドキュメントには、次のように記載されています。「kube-bench には Rancher K3S platform 用の benchmark が含まれています。実行するには、kube-bench command の実行時に --benchmark k3s-cis-1.7 を指定する必要があります。」auto-detection は kubeadm layout を前提としますが、k3s は file を /etc/rancher/var/lib/rancher の下に保持するため、明示的に指定してください。single node には該当しない failure も発生します。まず file permission と anonymous access に対応してください。

Pod Security admission によって、k3s に組み込まれた component は停止しますか?

kube-system に対して enforce すると停止します。k3s が type: LoadBalancer service 用に作成する ServiceLB pod は host 上の port を確保します。しかし baseline は host port を禁止するため、これらの pod は次回再作成されたときに拒否されます。admission configuration file で kube-system を除外するか、自分が管理する namespace に label を付けて Pod Security を適用してください。まず enforce=baselinewarn=restricted を使用し、適用前に restricted によって何が影響を受けるか確認してください。