Talos Linux hardening, the secure way
You picked Talos because it has no SSH, no shell and no package manager, so there is nothing left to harden. Then a port scan of your node listed 50000 and 6443, open to the whole internet, answering anyone who asks.
The short answer
Talos hardens the kernel and Kubernetes for you. You still own the edges: set the ingress firewall to block and allow the Talos API, etcd, kubelet and trustd only from the subnets that need them, keep workload isolation on, give people short-lived talosconfig certificates with the smallest role, and keep pods away from the Talos API.
On this page
What goes wrong
Talos removes most of what a Linux hardening guide is about. There is no SSH daemon, no shell, no package manager, and the root filesystem is read-only. The kernel boots with the Kernel Self Protection Project (KSPP) settings. Kubernetes starts with RBAC, audit logging, secrets encryption and Pod Security Admission already on.
What Talos does not decide for you is who can reach the node. Every Talos
machine serves its management API, apid, on TCP 50000. Control plane nodes
also serve the Kubernetes API on 6443, etcd on 2379 and 2380, and trustd on
50001. Every node serves the kubelet on 10250. The host firewall that could
limit this is off by default: its default action is accept.
On a cloud VM with a public address, that means the whole internet can open a
TLS connection to apid and the kubelet. They still need a client
certificate, so this is not an open door. It is an open window: every future
bug in the TLS stack, the gRPC server or the authorization code is reachable
by anyone, and scanners find the ports within minutes.
The second edge is the client credential. talosctl gen config writes a
talosconfig with the os:admin role and a certificate valid for a year. It
reads files, resets nodes and wipes disks. It ends up in a laptop's home
directory, in CI variables and in chat messages, and nobody can revoke one
copy without rotating the Talos CA.
The third edge is Kubernetes itself. A pod can be given access to the Talos API, and a workload can be allowed to share the host's namespaces. Both undo the isolation Talos was built for.
What the docs say
Talos Linux is designed to be secure by default.
Source: Sidero Labs docs, Talos Default Hardening and CIS Compliance
The first document configures the default action for ingress traffic, which can be either
acceptorblock, with the default beingaccept.
Source: Sidero Labs docs, Ingress Firewall
Limit access to your talosconfig, since it contains the credentials required to communicate with the cluster.
Source: Sidero Labs docs, Talos Security Checklist
"Secure by default" covers the kernel and the Kubernetes flags. It does not
cover the network: the firewall starts in accept, and the control plane
example on the firewall page itself leaves "apid and Kubernetes API are wide
open" to 0.0.0.0/0. The checklist says to limit the talosconfig, but
talosctl gen config still hands you an admin certificate that lives for a
year.
The secure configuration
Two patches: one for every node, one added on control plane nodes. The
example uses 10.10.0.0/16 for the private network the nodes share and
192.0.2.0/28 for the hosts that administer the cluster (a bastion, a VPN
or a tailnet range). Replace both with yours.
# talos-hardening-all.yaml
# Applied to every node (talosctl gen config --config-patch @talos-hardening-all.yaml).
apiVersion: v1alpha1
kind: NetworkDefaultActionConfig
ingress: block # default is accept; block drops what no rule allows
---
apiVersion: v1alpha1
kind: NetworkRuleConfig
name: apid-ingress # Talos API: admin hosts, plus nodes (control plane proxies to workers)
portSelector:
ports:
- 50000
protocol: tcp
ingress:
- subnet: 192.0.2.0/28
- subnet: 10.10.0.0/16
---
apiVersion: v1alpha1
kind: NetworkRuleConfig
name: kubelet-ingress # kubelet API: only from inside the cluster network
portSelector:
ports:
- 10250
protocol: tcp
ingress:
- subnet: 10.10.0.0/16
---
apiVersion: v1alpha1
kind: NetworkRuleConfig
name: cni-vxlan # Flannel default; Cilium uses 8472
portSelector:
ports:
- 4789
protocol: udp
ingress:
- subnet: 10.10.0.0/16
---
apiVersion: v1alpha1
kind: SecurityProfileConfig
workloadIsolation: true # containerd, kubelet and pods run in a sandbox namespace away from machined
---
apiVersion: v1alpha1
kind: VolumeConfig
name: EPHEMERAL
mount:
secure: true # nosuid, nodev on /var# talos-hardening-controlplane.yaml
# Added on control plane nodes (--config-patch-control-plane @talos-hardening-controlplane.yaml).
apiVersion: v1alpha1
kind: NetworkRuleConfig
name: kubernetes-api-ingress # 6443: admin hosts and nodes, never 0.0.0.0/0
portSelector:
ports:
- 6443
protocol: tcp
ingress:
- subnet: 192.0.2.0/28
- subnet: 10.10.0.0/16
---
apiVersion: v1alpha1
kind: NetworkRuleConfig
name: trustd-ingress # certificate issuance for joining nodes
portSelector:
ports:
- 50001
protocol: tcp
ingress:
- subnet: 10.10.0.0/16
---
apiVersion: v1alpha1
kind: NetworkRuleConfig
name: etcd-ingress # etcd client and peer ports: control plane nodes only
portSelector:
ports:
- 2379-2380
protocol: tcp
ingress:
- subnet: 10.10.1.11/32
- subnet: 10.10.1.12/32
- subnet: 10.10.1.13/32A config generated by Talos 1.14 already contains the last two documents
(workloadIsolation: true, and secure: true for EPHEMERAL). Keep them in
the patch anyway: clusters generated by older versions and upgraded do not
have them.
Apply firewall changes in try mode first. Talos reverts them after one
minute unless you apply again, so a wrong subnet does not lock you out:
talosctl -n 10.10.1.11 patch machineconfig --mode=try \
-p @talos-hardening-all.yaml -p @talos-hardening-controlplane.yaml
# Test access from an admin host within the minute, then apply for real:
talosctl -n 10.10.1.11 patch machineconfig \
-p @talos-hardening-all.yaml -p @talos-hardening-controlplane.yamlGive people their own short-lived, least-privilege client certificates instead of copies of the admin talosconfig:
# A reader certificate that expires tomorrow. os:reader lists files but cannot read them.
talosctl -n 10.10.1.11 config new talosconfig-reader --roles os:reader --crt-ttl 24h
# Operators who reboot nodes and take etcd snapshots, for one working day.
talosctl -n 10.10.1.11 config new talosconfig-operator --roles os:operator --crt-ttl 8hKeep the admin talosconfig offline, encrypted, and out of CI (see
Talos machine configs in git with age).
Do not add a KubeTalosAPIAccessConfig document (or the deprecated
.machine.features.kubernetesTalosAPIAccess it replaces) unless a controller
needs it; without it, Talos hands no Talos API credentials to pods through
a ServiceAccount. If one does, allow one role
in one namespace, for example os:etcd:backup in etcd-backup.
Prove it
Run on two Talos 1.14.1 VMs (QEMU, booted from the Image Factory ISO): a control plane at 10.10.1.2 and a worker at 10.10.1.3. The etcd rule used 10.10.1.2/32, the lab's only control plane node, instead of the example addresses. Ports were probed from the lab host with two source addresses: 192.0.2.5, inside the admin range, and 203.0.113.9, outside every range.
1. Before, in try mode, after the revert, and for real (control plane, ports 50000 Talos API, 10250 kubelet, 6443 Kubernetes API):
before any patch:
from 203.0.113.9 50000:open 10250:open 6443:open
from 192.0.2.5 50000:open 10250:open 6443:open
--mode=try, 10 s in:
from 203.0.113.9 50000:closed 10250:closed 6443:closed
try mode, 85 s in (reverted):
from 203.0.113.9 50000:open 10250:open 6443:open
applied for real:
from 203.0.113.9 50000:closed 10250:closed 6443:closed
from 192.0.2.5 50000:open 10250:closed 6443:openOn the worker, from the admin address: 50000 open, 10250 closed. Both nodes
stayed Ready, a pod on the control plane reached a pod on the worker
(200), and kubectl logs for a pod on the worker worked, so the kubelet
and VXLAN rules let the cluster itself through.
2. The firewall is in block mode:
talosctl -n 10.10.1.2 get nftableschain ingress -o yamlspec:
type: filter
hook: input
priority: NfTablesChainPriority(-140)
policy: drop
rules:
- matchIIfName: {interfaceName: [lo, siderolink, kubespan], operator: ==}
verdict: accept
- matchConntrackState: {states: [established, related]}
verdict: accept
...(Trimmed and folded.) Further down, one accept rule per NetworkRuleConfig,
for example port 4789 from 10.10.0.0/16.
3. The kernel booted with the KSPP arguments:
$ talosctl -n 10.10.1.2 read /proc/cmdline
talos.platform=metal console=tty0 slab_nomerge pti=on consoleblank=0 printk.devkmsg=on selinux=1 module.sig_enforce=1 proc_mem.force_override=never console=ttyS0
$ talosctl -n 10.10.1.2 get securitystate -o yaml
spec:
secureBoot: false
selinuxState: enabled, permissive
bootedWithUKI: true
moduleSignatureEnforced: trueThese are Talos defaults, not set by any field in the patches.
4. /var is mounted nosuid,nodev:
$ talosctl -n 10.10.1.3 read /proc/mounts | awk '$2=="/var"'
/dev/vda4 /var xfs rw,seclabel,nosuid,nodev,relatime,inode64,logbufs=8,logbsize=32k,prjquota 0 05. Short-lived, least-privilege certificates:
$ talosctl -n 10.10.1.2 config new talosconfig-reader --roles os:reader --crt-ttl 24h
$ talosctl -n 10.10.1.2 config new talosconfig-operator --roles os:operator --crt-ttl 8h
reader cert: subject=O = os:reader notBefore=Sep 25 11:27:56 2026 GMT notAfter=Sep 26 11:27:56 2026 GMT
operator cert: subject=O = os:operator notBefore=Sep 25 11:27:56 2026 GMT notAfter=Sep 25 19:27:56 2026 GMT
$ talosctl --talosconfig talosconfig-reader -n 10.10.1.2 read /proc/cmdline
error reading: rpc error: code = PermissionDenied desc = not authorized
$ talosctl --talosconfig talosconfig-reader -n 10.10.1.2 reboot
"10.10.1.2": rpc error: code = PermissionDenied desc = not authorized
$ talosctl --talosconfig talosconfig-reader -n 10.10.1.2 ls /var/lib
.
cni
$ talosctl --talosconfig talosconfig-operator -n 10.10.1.2 read /proc/cmdline
error reading: rpc error: code = PermissionDenied desc = not authorized
$ talosctl --talosconfig talosconfig-operator -n 10.10.1.2 etcd snapshot /tmp/o.db
snapshot info: hash 92f9ae25, revision 1029, total keys 353, total size 1552384
$ talosctl --talosconfig talosconfig-reader config info
Roles: os:reader
Certificate expires: 23 hours from now (2026-09-26)Mistakes people make
Believing "no SSH" means "no attack surface"
The Talos API is the replacement for SSH, and it listens on every interface. It needs a client certificate, but the TLS handshake, the gRPC server and the parser in front of it are reachable by anyone until the firewall is on.
Copying the firewall example as written
The control plane example on the Talos firewall page allows 50000 and 6443
from 0.0.0.0/0 and ::/0. It is a starting point for a lab. On a public
cloud, restrict both to your admin range and the node network.
Blocking VXLAN and wondering where the pods went
In block mode the firewall also drops encapsulated pod traffic between nodes. Allow the UDP port your CNI uses: 4789 for Flannel and Calico, 8472 for Cilium's VXLAN. Pod-to-pod traffic inside the pod and service subnets is allowed for native routing.
Handing out the admin talosconfig
One admin file, copied five times, is five credentials you cannot revoke one
by one. Issue each person a certificate with a role and a TTL. The CA
rotation guide is the documented way to revoke a leaked talosconfig, so
when someone leaves with an admin file, rotate the Talos API CA.
Upgrading and assuming the new defaults came along
workloadIsolation is on for clusters generated by Talos 1.14. Clusters
upgraded from older versions do not get the document, so it stays off until
you add it. See Talos upgrades that keep your hardening.
Checklist
NetworkDefaultActionConfigsetsingress: blockon every node.- Port 50000 is allowed only from the admin range and the node network.
- Port 6443 is allowed only from the admin range, the node network and your load balancer.
- Ports 2379 and 2380 are allowed only from control plane node addresses.
- Ports 10250 and 50001 are allowed only from the node network.
- The CNI's VXLAN port is allowed from the node network.
- Firewall changes are applied with
--mode=tryfirst. SecurityProfileConfighasworkloadIsolation: true.talosctl get cmdlineshowsslab_nomerge,pti=onandmodule.sig_enforce=1.- Each person has a named talosconfig with the smallest role and a TTL of hours.
- The admin talosconfig is stored encrypted and offline.
- No
KubeTalosAPIAccessConfigexists, or it allows one role in one namespace.
Talos took away the shell, the packages and the SSH keys. The firewall default and the admin certificate are the two things it left on your desk.
H2-CSPE
Learn it on a live range
Immutable OS and cluster hardening, in Secure Platform Engineering: a real host in your browser, and every objective checked on the machine.
Start freeThe Secure Way
More on nodes and clusters
Talos Linux, Kubernetes API hardening, service account tokens, RBAC and the cloud underneath.
All nodes and clusters guides