If you want to improve the security on your cluster and make it compliant with the CIS Benchmarks, here you can find a configuration to harden your kubernetes installation.
To apply the hardening configuration, create a file (eg. hardening.yaml
) and paste the content of the following code snippet into that.
The kubernetes version should be at least v1.23.6
to have all the most recent security features (eg. the new PodSecurity
admission plugin, etc).
N.B. Some of these configurations have just been added to kubespray, so ensure that you have the latest version to make it works properly. Also, ensure that other configurations doesn't override these.
hardening.yaml
:
# Hardening
---
## kube-apiserver
authorization_modes: ['Node', 'RBAC']
# AppArmor-based OS
# kube_apiserver_feature_gates: ['AppArmor=true']
kube_apiserver_request_timeout: 120s
kube_apiserver_service_account_lookup: true
# enable kubernetes audit
kubernetes_audit: true
audit_log_path: "/var/log/kube-apiserver-log.json"
audit_log_maxage: 30
audit_log_maxbackups: 10
audit_log_maxsize: 100
tls_min_version: VersionTLS12
tls_cipher_suites:
- TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
- TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
- TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305
# enable encryption at rest
kube_encrypt_secret_data: true
kube_encryption_resources: [secrets]
kube_encryption_algorithm: "secretbox"
kube_apiserver_enable_admission_plugins:
- EventRateLimit
- AlwaysPullImages
- ServiceAccount
- NamespaceLifecycle
- NodeRestriction
- LimitRanger
- ResourceQuota
- MutatingAdmissionWebhook
- ValidatingAdmissionWebhook
- PodNodeSelector
- PodSecurity
kube_apiserver_admission_control_config_file: true
# Creates config file for PodNodeSelector
# kube_apiserver_admission_plugins_needs_configuration: [PodNodeSelector]
# Define the default node selector, by default all the workloads will be scheduled on nodes
# with label network=srv1
# kube_apiserver_admission_plugins_podnodeselector_default_node_selector: "network=srv1"
# EventRateLimit plugin configuration
kube_apiserver_admission_event_rate_limits:
limit_1:
type: Namespace
qps: 50
burst: 100
cache_size: 2000
limit_2:
type: User
qps: 50
burst: 100
kube_profiling: false
# Remove anonymous access to cluster
remove_anonymous_access: true
## kube-controller-manager
kube_controller_manager_bind_address: 127.0.0.1
kube_controller_terminated_pod_gc_threshold: 50
# AppArmor-based OS
# kube_controller_feature_gates: ["RotateKubeletServerCertificate=true", "AppArmor=true"]
kube_controller_feature_gates: ["RotateKubeletServerCertificate=true"]
## kube-scheduler
kube_scheduler_bind_address: 127.0.0.1
# AppArmor-based OS
# kube_scheduler_feature_gates: ["AppArmor=true"]
## etcd
etcd_deployment_type: kubeadm
## kubelet
kubelet_authorization_mode_webhook: true
kubelet_authentication_token_webhook: true
kube_read_only_port: 0
kubelet_rotate_server_certificates: true
kubelet_protect_kernel_defaults: true
kubelet_event_record_qps: 1
kubelet_rotate_certificates: true
kubelet_streaming_connection_idle_timeout: "5m"
kubelet_make_iptables_util_chains: true
kubelet_feature_gates: ["RotateKubeletServerCertificate=true"]
kubelet_seccomp_default: true
kubelet_systemd_hardening: true
# In case you have multiple interfaces in your
# control plane nodes and you want to specify the right
# IP addresses, kubelet_secure_addresses allows you
# to specify the IP from which the kubelet
# will receive the packets.
kubelet_secure_addresses: "localhost link-local {{ kube_pods_subnet }} 192.168.10.110 192.168.10.111 192.168.10.112"
# additional configurations
kube_owner: root
kube_cert_group: root
# create a default Pod Security Configuration and deny running of insecure pods
# kube_system namespace is exempted by default
kube_pod_security_use_default: true
kube_pod_security_default_enforce: restricted
Let's take a deep look to the resultant kubernetes configuration:
- The
anonymous-auth
(onkube-apiserver
) is set totrue
by default. This is fine, because it is considered safe if you enableRBAC
for theauthorization-mode
. - The
enable-admission-plugins
includesPodSecurity
(for more details, please take a look here: https://kubernetes.io/docs/concepts/security/pod-security-admission/). Then, we set theEventRateLimit
plugin, providing additional configuration files (that are automatically created under the hood and mounted inside thekube-apiserver
container) to make it work. - The
encryption-provider-config
provide encryption at rest. This means that thekube-apiserver
encrypt data that is going to be stored before they reachetcd
. So the data is completely unreadable frometcd
(in case an attacker is able to exploit this). - The
rotateCertificates
inKubeletConfiguration
is set totrue
along withserverTLSBootstrap
. This could be used in alternative totlsCertFile
andtlsPrivateKeyFile
parameters. Additionally it automatically generates certificates by itself. By default the CSRs are approved automatically via kubelet-csr-approver. You can customize approval configuration by modifying Helm values viakubelet_csr_approver_values
. See https://kubernetes.io/docs/reference/access-authn-authz/kubelet-tls-bootstrapping/ for more information on the subject. - If you are installing kubernetes in an AppArmor-based OS (eg. Debian/Ubuntu) you can enable the
AppArmor
feature gate uncommenting the lines with the comment# AppArmor-based OS
on top. - The
kubelet_systemd_hardening
, both withkubelet_secure_addresses
setup a minimal firewall on the system. To better understand how these variables work, here's an explanatory image:
Once you have the file properly filled, you can run the Ansible command to start the installation:
ansible-playbook -v cluster.yml \
-i inventory.ini \
-b --become-user=root \
--private-key ~/.ssh/id_ecdsa \
-e "@vars.yaml" \
-e "@hardening.yaml"
N.B. The vars.yaml
contains our general cluster information (SANs, load balancer, dns, etc..) and hardening.yaml
is the file described above.