How to Secure Your Ubuntu Kubernetes Cluster: A Step‑by‑Step Hardening Guide
This comprehensive guide explains how to harden an Ubuntu‑based Kubernetes cluster with external IPs by applying system hardening, firewall rules, TLS encryption, Calico network policies, RBAC permissions, audit logging, and verification steps to achieve a multi‑layered security posture.
Overview
When deploying a Kubernetes cluster on Ubuntu with an external IP, multi‑layer security measures are required. This guide covers system hardening, firewall configuration, TLS communication, network policies, RBAC, audit logging and verification steps.
1. Basic System Hardening
Install and configure NTP for time sync, disable swap, install Docker or containerd, and set required kernel parameters for Kubernetes networking.
sudo apt update
sudo apt install ntpdate ntp
sudo ntpdate ntp1.aliyun.com
# Disable swap
sudo swapoff --all
# edit /etc/fstab to comment swap line
# Docker installation
sudo apt-get update
sudo apt-get install docker.io
# Containerd installation
sudo apt-get update
sudo apt-get install containerd
# Kernel parameters
cat >> /etc/sysctl.d/kubernetes.conf <<EOF
net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1
net.ipv4.ip_forward = 1
EOF
sysctl -p2. Firewall Rules (UFW)
Enable UFW and allow only required ports such as SSH (22), API server (6443), kubelet (10250, 10255), DNS (53) and etcd (2379/2380) from trusted IP ranges.
sudo apt update
sudo apt install ufw
sudo ufw enable
# Example rules
sudo ufw allow from 192.168.1.0/24 to any port 22 proto tcp
sudo ufw allow from 10.0.0.0/24 to any port 6443 proto tcp
sudo ufw allow from <master-node-IP> to any port 2379 proto tcp
sudo ufw allow from 10.0.0.0/24 to any port 10250 proto tcp
sudo ufw allow from 10.0.0.0/24 to any port 53 proto tcp
sudo ufw allow from 10.0.0.0/24 to any port 53 proto udp
sudo ufw default deny incoming
sudo ufw reload
sudo ufw status numbered3. TLS Communication
Initialize the control plane with kubeadm init enabling TLS and adding the external IP as a SAN. Set token validity to 24 h and renew certificates when needed.
sudo kubeadm init \
--apiserver-advertise-address=<internal-IP> \
--apiserver-cert-extra-sans=<external-IP> \
--pod-network-cidr=10.244.0.0/16
sudo kubeadm token create --validity 24h --print-join-command
sudo kubeadm certs check-expiration
sudo kubeadm certs renew all
systemctl restart kubelet
kubectl delete pod -n kube-system -l k8s-app=kube-apiserver
kubectl delete pod -n kube-system -l k8s-app=kube-controller-manager
kubectl delete pod -n kube-system -l k8s-app=kube-scheduler4. Network Policy (Calico)
Deploy Calico as the CNI plugin and apply a global deny policy followed by namespace‑specific exceptions such as DNS.
# Download and apply Calico
curl https://docs.projectcalico.org/manifests/calico.yaml -O
kubectl apply -f calico.yaml
# Global deny policy (global-network-policy.yaml)
apiVersion: projectcalico.org/v3
kind: GlobalNetworkPolicy
metadata:
name: default-deny
spec:
selector: all()
types:
- Ingress
- Egress
kubectl apply -f global-network-policy.yaml
# DNS allow policy (dns-policy.yaml)
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-dns
namespace: default
spec:
podSelector: {}
policyTypes:
- Ingress
ingress:
- from:
- namespaceSelector:
matchLabels:
name: kube-system
- podSelector:
matchLabels:
k8s-app: kube-dns
ports:
- protocol: TCP
port: 53
- protocol: UDP
port: 535. RBAC and Auditing
Create minimal‑privilege Roles and RoleBindings for developers, restrict the default ServiceAccount, and enable API‑server audit logging.
# Example Role (dev-namespace-role.yaml)
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
namespace: dev-namespace
name: dev-namespace-role
rules:
- apiGroups: [""]
resources: ["pods","services"]
verbs: ["get","list","create","update","delete"]
- apiGroups: ["apps"]
resources: ["deployments"]
verbs: ["get","list","create","update","delete"]
- apiGroups: [""]
resources: ["configmaps"]
verbs: ["get","list"]
# Bind the role
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: dev-namespace-binding
namespace: dev-namespace
subjects:
- kind: User
name: dev-team
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: Role
name: dev-namespace-role
apiGroup: rbac.authorization.k8s.io
# Read‑only pod role for default SA
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
namespace: default
name: pod-reader-role
rules:
- apiGroups: [""]
resources: ["pods"]
verbs: ["get","watch","list"]6. Verification
After hardening, verify firewall rules with ufw status, TLS with kubectl cluster-info, network policies by testing pod‑to‑pod connectivity, RBAC by attempting privileged actions with different users, and audit logs in /var/log/kubernetes/audit.log.
Conclusion
Applying layered security—system hardening, firewall, TLS, network policies, RBAC and continuous audit—creates a resilient Ubuntu‑based Kubernetes deployment. Regular updates, monitoring and a security‑first culture are essential for long‑term protection.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Raymond Ops
Linux ops automation, cloud-native, Kubernetes, SRE, DevOps, Python, Golang and related tech discussions.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
