Come mettere in sicurezza un cluster k3s su VPS
Scopri cosa lascia esposto k3s su un VPS: 6443, kubelet su 10250, kubeconfig, NodePort invisibili al firewall e pod privilegiati, con correzioni pratiche.
Cosa espone un cluster k3s a nodo singolo dal primo giorno
Un cluster k3s a nodo singolo su un VPS pubblico è esposto in cinque punti specifici il giorno dopo il completamento dell'installer a riga singola: il server API Kubernetes sulla porta TCP 6443, il kubelet sulla porta TCP 10250, il file kubeconfig presente sul disco, l'intervallo NodePort che il firewall non può rilevare e qualsiasi pod autorizzato a richiedere privileged o hostPath. Per ciascun punto esiste una correzione applicabile in pochi minuti. Questa guida presuppone che k3s sia già in esecuzione. In caso contrario, iniziare da installazione di k3s a nodo singolo su un VPS e tornare qui al termine.
Controllare cosa è in ascolto prima di modificare qualsiasi impostazione.
sudo ss -tulpn | grep -E '6443|10250|10256|8472'Un'installazione predefinita mostra 6443 (il server API), 10250 (il kubelet), 10256 (il controllo dello stato di kube-proxy) e 8472/udp (l'overlay flannel, che usa VXLAN, Virtual eXtensible LAN). Per impostazione predefinita, k3s si associa a 0.0.0.0, quindi ciascuno di questi servizi è esposto sull'indirizzo pubblico e non soltanto su loopback.
Perché la porta 6443 rappresenta l'intero cluster
Chiunque possa autenticarsi sulla porta 6443 con privilegi di amministratore può creare un pod, e un pod può ottenere privilegi root sull'host. La porta 6443 consente di accedere alla macchina.
Una porta 6443 esposta non comporta una compromissione immediata, perché Kubernetes non accetta password. Richiede un certificato client o un bearer token. Restano comunque valide due considerazioni.
Primo: il server API risponde ad alcune richieste anche senza credenziali. Il controllo degli accessi basato sui ruoli (RBAC) predefinito di Kubernetes associa il gruppo system:unauthenticated a un ruolo denominato system:public-info-viewer, che consente /version, /healthz, /livez e /readyz. Da un'altra macchina:
curl -sk https://YOUR_SERVER_IP:6443/versionIl comando restituisce la versione esatta di Kubernetes, che costituisce l'input per una ricerca di CVE (common vulnerabilities and exposures) e spiega perché uno scanner considera interessante il server. Tutti i percorsi successivi vengono rifiutati, e il rifiuto identifica il client:
forbidden: User "system:anonymous" cannot get path "/api"Secondo: ogni vulnerabilità del server API è raggiungibile da remoto finché la porta resta aperta. A quel punto applicare le patch non è più facoltativo.
La correzione più semplice consiste in una regola del firewall. In questo caso ufw richiede più di una riga, perché k3s instrada il traffico del cluster attraverso lo stesso kernel.
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 enableLe ultime due regole provengono direttamente dalla documentazione di k3s. 10.42.0.0/16 è la rete predefinita dei pod e 10.43.0.0/16 è la rete predefinita dei servizi. Senza queste regole, ufw blocca il traffico interno al cluster, quindi i pod perdono l'accesso al server API e agli altri pod. L'esempio di k3s consente l'accesso alla porta 6443 da qualsiasi origine; sostituire questo valore con il proprio indirizzo è la modifica più importante. Se non hai mai usato ufw, la guida di base al firewall ufw per un VPS descrive i criteri predefiniti da cui dipende questa configurazione.
La documentazione di k3s è esplicita sulla porta dell'overlay: "La porta VXLAN sui nodi non dovrebbe essere esposta a Internet, perché consente a chiunque di accedere alla rete del cluster." Un criterio predefinito di negazione del traffico in ingresso gestisce questo aspetto senza specificare la porta.
La correzione più solida consiste nel non raggiungere più il server API tramite l'indirizzo pubblico e nell'usare invece un indirizzo VPN o mesh. Il certificato del server deve includere l'indirizzo a cui ti connetti, quindi aggiungilo come SAN (subject alternative name) in /etc/rancher/k3s/config.yaml:
tls-san:
- 10.8.0.1
- k3s.example.com
secrets-encryption: truesudo systemctl restart k3s
sudo k3s secrets-encrypt statussecrets-encryption: true crittografa gli oggetti Secret nel datastore. La documentazione di k3s specifica che "La crittografia dei Secret non può essere abilitata su un server esistente senza riavviarlo"; inoltre, i Secret scritti prima della modifica mantengono il formato precedente finché non esegui sudo k3s secrets-encrypt reencrypt. È importante chiarire il vantaggio effettivo. Questa funzione protegge un file del datastore copiato da un backup. Non protegge da chi può comunicare con il server API, perché il server API decrittografa i Secret per chiunque abbia l'autorizzazione a leggerli. La stessa distinzione vale per qualsiasi archivio di Secret self-hosted. Per questo una verifica di hardening di Vaultwarden si concentra sul token di amministrazione e sul file di backup, non sulla crittografia in sé.
Perché il kubelet sulla porta 10250 è importante
Il kubelet è l'agente che avvia i container. La sua API sulla porta 10250 elenca i pod ed esegue comandi al loro interno. Un kubelet che accetta richieste anonime equivale a una shell remota su ogni workload della macchina.
Controlla il tuo:
curl -sk https://127.0.0.1:10250/pods | head -c 60Un k3s aggiornato risponde con Unauthorized, perché il kubelet chiede al server API di autenticare e autorizzare ogni client. Se invece restituisce un elenco di pod in formato JSON, l'accesso anonimo è attivo e chiunque possa raggiungere la porta 10250 può leggere i dati dei container ed eseguire comandi al loro interno.
Chiudi comunque la porta verso l'esterno. Su un nodo singolo, l'unico client del kubelet è il control plane sullo stesso host e il relativo traffico passa dall'interfaccia di loopback, che ufw accetta per impostazione predefinita. Bloccare la porta 10250 da Internet non comporta svantaggi. Se sei arrivato qui a causa di un kubectl top non funzionante o di un metrics-server che non diventa operativo, le cause sono raccolte in errori della porta 10250 del kubelet.
Il tuo kubeconfig è una credenziale di amministrazione del cluster
k3s scrive /etc/rancher/k3s/k3s.yaml con proprietario root e modalità 600. La documentazione indica questa conseguenza della modifica: "Il file kubeconfig appartiene a root e viene scritto con la modalità predefinita 600. Modificando la modalità in 644, gli altri utenti non privilegiati dell'host potranno leggerlo."
Interpretalo così: la modalità 644 rende ogni account locale un amministratore del cluster. Molte guide suggeriscono esattamente questa modifica, in genere tramite --write-kubeconfig-mode 644, affinché kubectl funzioni senza sudo. Funziona perché la credenziale di amministrazione viene resa disponibile a chiunque disponga di una shell.
Copia invece il file per un solo utente.
mkdir -p ~/.kube
sudo install -o "$USER" -g "$USER" -m 600 /etc/rancher/k3s/k3s.yaml ~/.kube/config
kubectl get nodesQuindi verifica che l'originale abbia ancora permessi restrittivi:
stat -c '%a %U:%G' /etc/rancher/k3s/k3s.yaml600 root:root è il risultato che devi ottenere. Quel file contiene un certificato client per un membro di system:masters, il gruppo che il server API considera sempre autorizzato; per questo le regole RBAC non vengono mai applicate a quel certificato. Kubernetes non dispone di una certificate revocation list, quindi una copia sottratta resta valida fino alla rotazione della certificate authority del cluster. Trattalo come una chiave privata SSH e limita il più possibile l'insieme degli account che possono accedervi, per lo stesso motivo per cui si applica il principio degli account utente con privilegi minimi su un VPS.
Il firewall non rileva il traffico NodePort
Un servizio type: NodePort apre una porta compresa tra 30000 e 32767 su ogni indirizzo posseduto dal nodo, incluso quello pubblico. Un servizio type: LoadBalancer va oltre in k3s: ServiceLB, il load balancer incluso, pianifica un pod ridotto per ogni servizio in kube-system, che riserva direttamente la porta del servizio sull'host.
kubectl -n kube-system get pods | grep svclb
sudo ss -tulpn | grep -E ':3[0-2][0-9]{3}'Ecco l'aspetto che sorprende molti utenti. Anche se si blocca la porta con ufw, questa continua a rispondere.
sudo ufw deny 30080/tcp
curl http://YOUR_SERVER_IP:30080La pagina continua a caricarsi a causa del percorso seguito dal pacchetto. kube-proxy scrive regole DNAT (destination network address translation) nella catena PREROUTING della tabella nat, e PREROUTING viene eseguita prima di qualsiasi decisione di filtraggio. La destinazione diventa l'indirizzo di un pod, che non è l'host; il kernel invia quindi il pacchetto nella catena FORWARD, senza mai passare per INPUT. Le regole di ufw risiedono in INPUT. Il pacchetto non le incontra. L'ordine dei jump è visibile qui:
sudo iptables -S PREROUTING -t nat | head
sudo iptables -S FORWARD | headKUBE-SERVICES si trova all'inizio di PREROUTING, mentre i jump di Kubernetes in FORWARD precedono le catene proprie di ufw. È lo stesso meccanismo che consente a Docker di pubblicare porte oltre ufw, e le soluzioni sono le stesse.
- Filtrare il traffico nel firewall di rete del provider. Viene eseguito prima della macchina e non dipende dal modo in cui il kernel instrada il traffico.
- Evitare NodePort e LoadBalancer. Lasciare i servizi su
ClusterIPe raggiungerli conkubectl port-forwardtramite la sessione SSH già disponibile. - Esporre un solo ingress sulle porte 80 e 443, senza esporre altro.
- Limitare l'intervallo con
service-node-port-rangeinkube-apiserver-arg, in modo che un NodePort creato accidentalmente ricada in un intervallo monitorato.
Vale comunque la pena eseguire ufw. Il firewall gestisce il traffico destinato direttamente all'host, cioè quello per SSH e per l'API server. Semplicemente, non controlla il traffico diretto ai pod; aspettarsi che lo faccia è il modo in cui un database finisce per essere raggiungibile.
Un pod con hostPath o privileged equivale a root sul VPS
I container sono normali processi eseguiti sul kernel, ma con una vista limitata del sistema. Diversi campi dei pod rimuovono questa limitazione.
securityContext.privileged: trueassegna al container tutte le capability Linux e l'accesso ai dispositivi dell'host.hostPathmonta una directory dell'host nel pod. Un pod che monta/in lettura e scrittura può aggiungere una chiave a/root/.ssh/authorized_keys.hostPID: trueinserisce il container nel namespace dei processi dell'host, dovensenterrispetto al PID 1 apre una shell sull'host.hostNetwork: trueinserisce il container nello stack di rete dell'host, consentendogli di associare le porte dell'host e raggiungere i servizi in ascolto sul loopback.
Quindi, la domanda «chi può creare pod qui?» equivale a «chi dispone dei privilegi root su questo VPS?». Qualsiasi ServiceAccount con create sui pod in un namespace qualsiasi equivale a root, a meno che un controllo non rifiuti prima il pod.
Questo controllo è l'ammissione Pod Security, integrata nell'API server. La configurazione rapida consiste in un'etichetta per namespace e non richiede un riavvio.
kubectl label namespace default \
pod-security.kubernetes.io/enforce=baseline \
pod-security.kubernetes.io/enforce-version=latest \
pod-security.kubernetes.io/warn=restrictedbaseline rifiuta tutti e quattro i campi precedenti. restricted applica inoltre l'uso di un utente non-root, di un profilo seccomp (secure computing mode), impedisce l'escalation dei privilegi e richiede di eliminare le capability fino a ALL; questo interrompe il funzionamento di molti chart pubblicati. Applicare baseline e generare soltanto avvisi per restricted consente di verificare cosa si interromperebbe prima di adottare questa configurazione.
Verifica che funzioni:
kubectl apply -f - <<'EOF'
apiVersion: v1
kind: Pod
metadata:
name: pstest
spec:
containers:
- name: app
image: busybox
command: ["sleep", "60"]
securityContext:
privileged: true
EOFL'API server rifiuta il pod e indica il campo rifiutato:
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)Per impostare un valore predefinito a livello di cluster invece di un'etichetta su ogni namespace, k3s documenta un file di configurazione dell'ammissione in /var/lib/rancher/k3s/server/psa.yaml:
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]Indica questo file all'API server in /etc/rancher/k3s/config.yaml, quindi riavvia k3s:
kube-apiserver-arg:
- 'admission-control-config-file=/var/lib/rancher/k3s/server/psa.yaml'L'esenzione kube-system è obbligatoria. I pod ServiceLB gestiti da k3s usano porte dell'host, che baseline vieta. Se si omette kube-system dall'elenco, questi pod vengono rifiutati la volta successiva in cui un componente li ricrea. Dopo una modifica all'ammissione, mantieni aperta una seconda sessione SSH quando riavvii k3s.
Già che ci sei, c'è un ulteriore vantaggio: k3s include un controller per le network policy e lo abilita per impostazione predefinita. Di conseguenza, gli oggetti NetworkPolicy diventano effettivi su questo cluster senza installare altro. Questo non vale per tutte le distribuzioni Kubernetes. È lo strumento da usare per impedire a un pod compromesso di raggiungere il resto della rete.
Rimuovere i componenti inclusi che non si utilizzano
L’installer installa una serie di componenti aggiuntivi. Ognuno aggiunge un listener e un ulteriore elemento da aggiornare. --disable accetta questi valori: coredns, servicelb, traefik, local-storage, metrics-server, runtimes.
Mantieni coredns. Nel cluster, nessun nome viene risolto senza di esso. Gli altri componenti sono opzionali. In /etc/rancher/k3s/config.yaml:
disable:
- traefik
- servicelb
disable-helm-controller: truesudo systemctl restart k3s
kubectl get pods -Ak3s elimina i componenti disabilitati, quindi i pod traefik e i pod svclb- scompaiono automaticamente. Prima di procedere, considera le conseguenze. Senza servicelb, ogni servizio type: LoadBalancer rimane per sempre in <pending>, perché nessun componente gli assegna un indirizzo. Senza traefik non è disponibile alcun controller Ingress, quindi gli oggetti Ingress non hanno alcun effetto. Disabilitali quando gestisci il traffico in un altro modo, ad esempio con un reverse proxy sull’host, e lasciali attivi quando li utilizzi. disable-helm-controller: true rimuove il controller che monitora le risorse HelmChart, un componente privilegiato che non utilizzi se esegui direttamente helm.
Arrestare il montaggio automatico del token ServiceAccount predefinito
Ogni pod riceve un token ServiceAccount in /var/run/secrets/kubernetes.io/serviceaccount/token, salvo diversa indicazione. Il ServiceAccount default non dispone di autorizzazioni RBAC, quindi il token da solo non consente di fare molto. Tuttavia, in un container compromesso fornisce a un attaccante una credenziale valida e un API server raggiungibile: è il primo passaggio nella maggior parte delle analisi sulle escalation dei privilegi nel cluster.
La guida al rafforzamento di k3s lo disabilita per ogni 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}'Verifica che la modifica sia stata applicata. Prima, attendi alcuni secondi affinché il pod venga avviato.
kubectl run t --image=busybox --restart=Never --command -- sleep 30
kubectl exec t -- ls /var/run/secrets/kubernetes.io/serviceaccountIl percorso non esiste più, quindi ls restituisce No such file or directory. Un workload che richiede realmente l'accesso all'API imposta automountServiceAccountToken: true nella propria specifica del pod; pertanto, l'accesso non viene bloccato in modo permanente. Questo è già un piccolo miglioramento. Il vantaggio principale consiste nel non assegnare a un workload un ServiceAccount con autorizzazioni effettive. Puoi verificare quali privilegi offre attualmente un token:
kubectl auth can-i --list --as=system:serviceaccount:default:defaultLimiti delle risorse, per impedire che un singolo pod abbatta il cluster
Su un singolo nodo, il control plane e i workload condividono lo stesso kernel e lo stesso pool di memoria. Un pod con una perdita di memoria non viene sempre terminato da solo. Il killer OOM (out of memory) del kernel sceglie la vittima in base a un punteggio che favorisce i processi di grandi dimensioni. k3s è un processo grande e persistente, quindi può scomparire il cluster invece del pod. A quel punto nulla riavvia i workload, perché il componente che li riavvia è proprio quello che è terminato.
Un LimitRange imposta i limiti per i pod che non ne definiscono:
apiVersion: v1
kind: LimitRange
metadata:
name: defaults
namespace: default
spec:
limits:
- type: Container
default:
cpu: 500m
memory: 512Mi
defaultRequest:
cpu: 50m
memory: 128MiUn ResourceQuota limita la quantità di risorse che l'intero namespace può richiedere:
apiVersion: v1
kind: ResourceQuota
metadata:
name: cap
namespace: default
spec:
hard:
limits.cpu: "3"
limits.memory: 3Gi
pods: "20"Riserva quindi spazio per k3s in /etc/rancher/k3s/config.yaml:
kubelet-arg:
- 'system-reserved=cpu=250m,memory=512Mi'La differenza è visibile in due punti. Un container terminato perché ha superato il proprio limite segnala Reason: OOMKilled in Last State in kubectl describe pod, mentre il resto del nodo continua a funzionare. Un nodo che ha esaurito completamente la memoria registra una riga Killed process in dmesg e di solito trascina con sé i nodi vicini. Il primo caso indica che il limite sta svolgendo il proprio compito. Il secondo è il problema che i limiti servono a prevenire.
Esegui una scansione dei manifest nella CI e nel cluster k3s secondo una pianificazione
La scansione serve in due punti, che rilevano problemi diversi. Installa prima Trivy. Il progetto pubblica un pacchetto Debian con ogni release e 0.74.0 era la versione corrente ad agosto 2026; quindi controlla la pagina delle release per verificare la disponibilità di una versione più recente prima di fissare questa versione nell’automazione.
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 --versionIl primo punto riguarda i manifest, prima che raggiungano il cluster.
trivy fs --scanners misconfig --severity HIGH,CRITICAL --exit-code 1 ./deploy--exit-code 1 interrompe il job di CI (continuous integration) quando rileva un problema. Ogni risultato indica il campo che causa il problema e la relativa gravità, quindi un privileged: true che non intendevi sottoporre al commit interrompe la build invece di raggiungere il server API. Un problema che hai deciso di accettare va inserito in un file .trivyignore, che mantiene questa decisione in git accanto al manifest che l’ha causato.
Il secondo punto riguarda il cluster in esecuzione, secondo una pianificazione.
trivy k8s --compliance=k8s-cis-1.23 --report summaryÈ importante essere chiari su quel comando. trivy k8s esegue il deployment di un pod collector sui nodi, che richiede l’accesso all’host per esaminare le impostazioni a livello di nodo. In un cluster in cui hai appena iniziato a rifiutare i pod privilegiati, è un aspetto da considerare, non da aggirare. trivy k8s --report summary --disable-node-collector salta il collector e, di conseguenza, perde anche i controlli a livello di nodo.
kube-bench verifica il lato host: permessi dei file e flag dei processi, che è l’ambito in cui si trova effettivamente la maggior parte del benchmark CIS (Center for Internet Security). Include un profilo per k3s. Usa il manifest Job upstream e modificalo.
curl -sfL -o kube-bench-job.yaml https://raw.githubusercontent.com/aquasecurity/kube-bench/main/job.yamlModifica il comando del container impostandolo su ["kube-bench", "--benchmark", "k3s-cis-1.7"] e sostituisci i mount /etc/kubernetes e /var/lib/etcd con /etc/rancher e /var/lib/rancher, perché è lì che k3s conserva i propri file. Poi:
kubectl apply -f kube-bench-job.yaml
kubectl logs -l app=kube-bench --tail=-1Nota la natura di quel Job: hostPID: true più mount di directory dell’host, cioè esattamente la struttura del pod che hai iniziato a rifiutare nella sezione precedente. Eseguilo nel namespace esentato kube-system, leggi l’output, quindi kubectl delete job kube-bench. Il fatto che uno scanner debba essere privilegiato non è un motivo per smettere di vietare i workload privilegiati.
Il contenuto delle immagini è una questione separata. trivy image ghcr.io/example/app:1.4 legge il database dei pacchetti all’interno di un’immagine ed elenca quelli che risultano vulnerabili, svolgendo lo stesso controllo di quando verifichi il server alla ricerca di CVE note.
Veniamo ora all’aspetto meno intuitivo dell’output degli scanner. Un cluster amatoriale con un solo nodo non supererà un lungo elenco di controlli CIS, e la maggior parte di questi risultati è corretta ma irrilevante per il tuo caso. Il benchmark è stato scritto per un cluster multi-nodo e multi-tenant: etcd su host separati, audit log inviati fuori dalla macchina, una certificate authority separata per kubelet e plugin di ammissione per un regime di conformità a cui non sei soggetto. k3s esegue deliberatamente il control plane come un singolo processo con un unico file di configurazione, quindi i controlli che verificano i permessi di un file manifest kube-scheduler non possono essere superati, perché quel file non esiste.
Leggi i risultati in questo ordine e fermati quando il valore dei controlli aggiuntivi si esaurisce: modalità e proprietà dei file in /etc/rancher e /var/lib/rancher; qualsiasi voce che menzioni l’accesso anonimo o non autenticato; qualsiasi voce che segnali un componente in ascolto su 0.0.0.0; infine, qualsiasi container eseguito come UID 0 senza una ragione valida. Il resto può attendere un secondo nodo o una seconda persona con accesso. Un report di cento righe che ignori vale meno di uno di cinque righe su cui intervieni.
Ordine di hardening di k3s per un nodo singolo
- Imposta su ufw la policy predefinita per le connessioni in ingresso su deny, consenti SSH, consenti 6443 dal tuo indirizzo e consenti le reti dei pod e dei servizi.
- Copia il kubeconfig nella home del tuo utente con modalità 600 e non impostare mai
--write-kubeconfig-mode 644. - Verifica che kubelet sulla porta 10250 rifiuti le richieste anonime e mantieni la porta non esposta a Internet.
- Disabilita i componenti inclusi che non utilizzi, quindi riavvia k3s.
- Applica ai namespace le label per l'ammissione Pod Security con
enforce=baselineewarn=restricted. - Disattiva il montaggio automatico predefinito del token del ServiceAccount.
- Aggiungi un LimitRange e un ResourceQuota e riserva CPU e memoria per k3s.
- Inserisci
trivy fs --scanners misconfignella CI ed esegui una scansione CIS ogni mese.
Il sistema host sottostante richiede la stessa attenzione di qualsiasi altro server, ma k3s introduce un aspetto specifico. È stato installato tramite uno script anziché tramite apt, quindi apt upgrade non lo aggiorna mai. Mantieni il sistema operativo aggiornato secondo una pianificazione separata, usando gli aggiornamenti automatici su Ubuntu, e aggiorna k3s intenzionalmente rieseguendo il relativo installer con il channel o la versione desiderata. Due percorsi di aggiornamento sulla stessa macchina si dimenticano facilmente; annota quindi quale componente segue ciascun percorso.
FAQ
È sicuro esporre il server API di k3s sulla porta 6443 a Internet?
Non è di per sé un accesso aperto, perché il server API richiede un certificato client o un token e rifiuta tutte le altre richieste con forbidden: User "system:anonymous". Restano due rischi. I client anonimi possono comunque leggere /version, che indica a uno scanner esattamente quale release di Kubernetes cercare. Inoltre, ogni futura vulnerabilità del server API diventa raggiungibile da remoto finché la porta resta aperta. In un cluster a nodo singolo, nessun dispositivo esterno ha bisogno della porta 6443, ad eccezione del tuo kubectl. Consenti quindi l'accesso dal tuo indirizzo con sudo ufw allow from YOUR_IP to any port 6443 proto tcp e lascia che la policy di rifiuto predefinita gestisca il resto.
Perché la regola ufw non blocca un servizio NodePort?
Perché il pacchetto non raggiunge la chain in cui si trova la regola. kube-proxy inserisce regole DNAT nella tabella nat, nella chain PREROUTING, che viene elaborata per prima e riscrive la destinazione con l'indirizzo di un pod. Il pacchetto viene quindi inoltrato invece di essere consegnato localmente, perciò attraversa FORWARD e salta INPUT, dove si trovano le regole di ufw. Verificalo con sudo iptables -S PREROUTING -t nat | head. Filtra i NodePort nel firewall di rete del provider oppure evita type: NodePort e raggiungi i servizi con kubectl port-forward.
Devo eseguire k3s con --write-kubeconfig-mode 644?
No. La documentazione di k3s descrive chiaramente l'effetto: "Changing the mode to 644 will allow it to be read by other unprivileged users on the host." Quel file contiene un certificato client per system:masters, quindi la modalità 644 trasformerebbe ogni account locale in un amministratore del cluster. Copialo invece per un solo utente con sudo install -o "$USER" -g "$USER" -m 600 /etc/rancher/k3s/k3s.yaml ~/.kube/config e lascia l'originale con 600 root:root.
Quale benchmark di kube-bench devo usare per k3s?
Usa k3s-cis-1.7. La documentazione di kube-bench afferma: "kube-bench includes benchmarks for Rancher K3S platform. To run this you will need to specify --benchmark k3s-cis-1.7 when you run the kube-bench command." Passalo esplicitamente, perché il rilevamento automatico presuppone una struttura kubeadm, mentre k3s mantiene i propri file in /etc/rancher e /var/lib/rancher. Sono previsti errori che non si applicano a un nodo singolo. Intervieni prima sui permessi dei file e sull'accesso anonimo.
Pod Security admission interromperà i componenti inclusi in k3s?
Sì, se la applichi in modalità enforce su kube-system. I pod ServiceLB creati da k3s per i servizi type: LoadBalancer richiedono porte sull'host, mentre baseline vieta le porte dell'host. Di conseguenza, questi pod vengono rifiutati quando vengono ricreati. Escludi kube-system nel file di configurazione dell'admission oppure applica Pod Security solo tramite label sui namespace che gestisci. Inizia con enforce=baseline e warn=restricted, così puoi verificare quali componenti restricted interromperebbe prima di attivarla.