This hardening guide is intended to be used for RKE clusters and associated with specific versions of the CIS Kubernetes Benchmark, Kubernetes, and Rancher:

This document provides prescriptive guidance for hardening a RKE cluster to be used for installing Rancher v2.5.4 with Kubernetes v1.18 or provisioning a RKE cluster with Kubernetes v1.18 to be used within Rancher v2.5.4. It outlines the configurations required to address Kubernetes benchmark controls from the Center for Information Security (CIS).

For more detail about evaluating a hardened cluster against the official CIS benchmark, refer to the CIS 1.6 Benchmark - Self-Assessment Guide - Rancher v2.5.4.

Known Issues

  • Rancher exec shell and view logs for pods are not functional in a CIS 1.6 hardened setup when only public IP is provided when registering custom nodes. This functionality requires a private IP to be provided when registering the custom nodes.
  • When setting the default_pod_security_policy_template_id: to restricted Rancher creates RoleBindings and ClusterRoleBindings on the default service accounts. The CIS 1.6 5.1.5 check requires the default service accounts have no roles or cluster roles bound to it apart from the defaults. In addition the default service accounts should be configured such that it does not provide a service account token and does not have any explicit rights assignments.

  • Migration Rancher from 2.4 to 2.5. Addons were removed in HG 2.5, and therefore namespaces on migration may be not created on the downstream clusters. Pod may fail to run because of missing namesapce like ingress-nginx, cattlae-system.

Configure Kernel Runtime Parameters

The following sysctl configuration is recommended for all nodes type in the cluster. Set the following parameters in /etc/sysctl.d/90-kubelet.conf:

A user account and group for the etcd service is required to be setup before installing RKE. The uid and gid for the etcd user will be used in the RKE config.yml to set the proper permissions for files and directories during installation time.

create etcd user and group

To create the etcd group run the following console commands.

The commands below use 52034 for uid and gid are for example purposes. Any valid unused uid or gid could also be used in lieu of 52034.

  1. groupadd --gid 52034 etcd
  2. useradd --comment "etcd service account" --uid 52034 --gid 52034 etcd

Update the RKE config.yml with the uid and gid of the etcd user:

  1. services:
  2. etcd:
  3. gid: 52034
  4. uid: 52034

Set automountServiceAccountToken to false for default service accounts

Kubernetes provides a default service account which is used by cluster workloads where no specific service account is assigned to the pod. Where access to the Kubernetes API from a pod is required, a specific service account should be created for that pod, and rights granted to that service account. The default service account should be configured such that it does not provide a service account token and does not have any explicit rights assignments.

For each namespace including default and kube-system on a standard RKE install the default service account must include this value:

Save the following yaml to a file called account_update.yaml

  1. apiVersion: v1
  2. kind: ServiceAccount
  3. metadata:
  4. name: default
  5. automountServiceAccountToken: false

Create a bash script file called account_update.sh. Be sure to chmod +x account_update.sh so the script has execute permissions.

  1. #!/bin/bash -e
  2. for namespace in $(kubectl get namespaces -A -o json | jq -r '.items[].metadata.name'); do
  3. kubectl patch serviceaccount default -n ${namespace} -p "$(cat account_update.yaml)"
  4. done

Ensure that all Namespaces have Network Policies defined

Network Policies are namespace scoped. When a network policy is introduced to a given namespace, all traffic not allowed by the policy is denied. However, if there are no network policies in a namespace all traffic will be allowed into and out of the pods in that namespace. To enforce network policies, a CNI (container network interface) plugin must be enabled. This guide uses canal to provide the policy enforcement. Additional information about CNI providers can be found

Once a CNI provider is enabled on a cluster a default network policy can be applied. For reference purposes a permissive example is provide below. If you want to allow all traffic to all pods in a namespace (even if policies are added that cause some pods to be treated as “isolated”), you can create a policy that explicitly allows all traffic in that namespace. Save the following yaml as default-allow-all.yaml. Additional documentation about network policies can be found on the Kubernetes site.

This NetworkPolicy is not recommended for production use

Create a bash script file called apply_networkPolicy_to_all_ns.sh. Be sure to chmod +x apply_networkPolicy_to_all_ns.sh so the script has execute permissions.

  1. #!/bin/bash -e
  2. for namespace in $(kubectl get namespaces -A -o json | jq -r '.items[].metadata.name'); do
  3. kubectl apply -f default-allow-all.yaml -n ${namespace}
  4. done

Execute this script to apply the default-allow-all.yaml the permissive NetworkPolicy to all namespaces.

The reference cluster.yml is used by the RKE CLI that provides the configuration needed to achieve a hardened install of Rancher Kubernetes Engine (RKE). Install is provided with additional details about the configuration items. This reference cluster.yml does not include the required nodes directive which will vary depending on your environment. Documentation for node configuration can be found here: https://rancher.com/docs/rke/latest/en/config-options/nodes

  1. # If you intend to deploy Kubernetes in an air-gapped environment,
  2. # please consult the documentation on how to configure custom RKE images.
  3. # https://rancher.com/docs/rke/latest/en/installation/
  4. # the nodes directive is required and will vary depending on your environment
  5. # documentation for node configuration can be found here:
  6. # https://rancher.com/docs/rke/latest/en/config-options/nodes
  7. nodes: []
  8. services:
  9. etcd:
  10. image: ""
  11. extra_args: {}
  12. extra_binds: []
  13. extra_env: []
  14. win_extra_args: {}
  15. win_extra_binds: []
  16. win_extra_env: []
  17. external_urls: []
  18. ca_cert: ""
  19. cert: ""
  20. key: ""
  21. path: ""
  22. uid: 52034
  23. gid: 52034
  24. snapshot: false
  25. retention: ""
  26. creation: ""
  27. backup_config: null
  28. kube-api:
  29. image: ""
  30. extra_args: {}
  31. extra_binds: []
  32. extra_env: []
  33. win_extra_args: {}
  34. win_extra_binds: []
  35. win_extra_env: []
  36. service_cluster_ip_range: ""
  37. service_node_port_range: ""
  38. pod_security_policy: true
  39. always_pull_images: false
  40. secrets_encryption_config:
  41. enabled: true
  42. custom_config: null
  43. audit_log:
  44. enabled: true
  45. configuration: null
  46. admission_configuration: null
  47. event_rate_limit:
  48. enabled: true
  49. configuration: null
  50. kube-controller:
  51. image: ""
  52. extra_args:
  53. feature-gates: RotateKubeletServerCertificate=true
  54. extra_binds: []
  55. extra_env: []
  56. win_extra_args: {}
  57. win_extra_binds: []
  58. win_extra_env: []
  59. cluster_cidr: ""
  60. service_cluster_ip_range: ""
  61. scheduler:
  62. image: ""
  63. extra_args: {}
  64. extra_binds: []
  65. win_extra_args: {}
  66. win_extra_binds: []
  67. win_extra_env: []
  68. kubelet:
  69. image: ""
  70. extra_args:
  71. feature-gates: RotateKubeletServerCertificate=true
  72. protect-kernel-defaults: "true"
  73. 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,TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,TLS_RSA_WITH_AES_256_GCM_SHA384,TLS_RSA_WITH_AES_128_GCM_SHA256
  74. extra_binds: []
  75. extra_env: []
  76. win_extra_binds: []
  77. win_extra_env: []
  78. cluster_domain: cluster.local
  79. infra_container_image: ""
  80. cluster_dns_server: ""
  81. fail_swap_on: false
  82. generate_serving_certificate: true
  83. kubeproxy:
  84. image: ""
  85. extra_args: {}
  86. extra_binds: []
  87. extra_env: []
  88. win_extra_args: {}
  89. win_extra_binds: []
  90. win_extra_env: []
  91. network:
  92. plugin: ""
  93. options: {}
  94. mtu: 0
  95. node_selector: {}
  96. update_strategy: null
  97. authentication:
  98. strategy: ""
  99. sans: []
  100. webhook: null
  101. addons: |
  102. apiVersion: policy/v1beta1
  103. kind: PodSecurityPolicy
  104. metadata:
  105. name: restricted
  106. spec:
  107. requiredDropCapabilities:
  108. - NET_RAW
  109. privileged: false
  110. allowPrivilegeEscalation: false
  111. defaultAllowPrivilegeEscalation: false
  112. fsGroup:
  113. rule: RunAsAny
  114. runAsUser:
  115. rule: MustRunAsNonRoot
  116. seLinux:
  117. rule: RunAsAny
  118. supplementalGroups:
  119. rule: RunAsAny
  120. volumes:
  121. - emptyDir
  122. - secret
  123. - persistentVolumeClaim
  124. - downwardAPI
  125. - configMap
  126. - projected
  127. ---
  128. apiVersion: rbac.authorization.k8s.io/v1
  129. kind: ClusterRole
  130. metadata:
  131. name: psp:restricted
  132. rules:
  133. - apiGroups:
  134. - extensions
  135. resourceNames:
  136. - restricted
  137. resources:
  138. - podsecuritypolicies
  139. verbs:
  140. - use
  141. ---
  142. apiVersion: rbac.authorization.k8s.io/v1
  143. kind: ClusterRoleBinding
  144. metadata:
  145. name: psp:restricted
  146. roleRef:
  147. apiGroup: rbac.authorization.k8s.io
  148. kind: ClusterRole
  149. name: psp:restricted
  150. subjects:
  151. - apiGroup: rbac.authorization.k8s.io
  152. kind: Group
  153. name: system:serviceaccounts
  154. - apiGroup: rbac.authorization.k8s.io
  155. kind: Group
  156. name: system:authenticated
  157. ---
  158. apiVersion: networking.k8s.io/v1
  159. kind: NetworkPolicy
  160. metadata:
  161. name: default-allow-all
  162. spec:
  163. podSelector: {}
  164. ingress:
  165. - {}
  166. egress:
  167. - {}
  168. policyTypes:
  169. - Ingress
  170. - Egress
  171. ---
  172. apiVersion: v1
  173. kind: ServiceAccount
  174. metadata:
  175. name: default
  176. automountServiceAccountToken: false
  177. addons_include: []
  178. system_images:
  179. etcd: ""
  180. nginx_proxy: ""
  181. cert_downloader: ""
  182. kubernetes_services_sidecar: ""
  183. kubedns: ""
  184. dnsmasq: ""
  185. kubedns_autoscaler: ""
  186. coredns: ""
  187. coredns_autoscaler: ""
  188. nodelocal: ""
  189. kubernetes: ""
  190. flannel: ""
  191. flannel_cni: ""
  192. calico_node: ""
  193. calico_cni: ""
  194. calico_controllers: ""
  195. calico_ctl: ""
  196. calico_flexvol: ""
  197. canal_node: ""
  198. canal_cni: ""
  199. canal_controllers: ""
  200. canal_flannel: ""
  201. canal_flexvol: ""
  202. weave_node: ""
  203. weave_cni: ""
  204. pod_infra_container: ""
  205. ingress: ""
  206. ingress_backend: ""
  207. metrics_server: ""
  208. windows_pod_infra_container: ""
  209. ssh_key_path: ""
  210. ssh_cert_path: ""
  211. ssh_agent_auth: false
  212. authorization:
  213. mode: ""
  214. options: {}
  215. ignore_docker_version: false
  216. kubernetes_version: v1.18.12-rancher1-1
  217. private_registries: []
  218. ingress:
  219. provider: ""
  220. options: {}
  221. node_selector: {}
  222. extra_args: {}
  223. dns_policy: ""
  224. extra_envs: []
  225. extra_volumes: []
  226. extra_volume_mounts: []
  227. update_strategy: null
  228. http_port: 0
  229. https_port: 0
  230. network_mode: ""
  231. cluster_name:
  232. cloud_provider:
  233. name: ""
  234. prefix_path: ""
  235. win_prefix_path: ""
  236. addon_job_timeout: 0
  237. bastion_host:
  238. address: ""
  239. port: ""
  240. user: ""
  241. ssh_key: ""
  242. ssh_key_path: ""
  243. ssh_cert: ""
  244. ssh_cert_path: ""
  245. monitoring:
  246. provider: ""
  247. options: {}
  248. node_selector: {}
  249. update_strategy: null
  250. replicas: null
  251. restore:
  252. restore: false
  253. snapshot_name: ""
  254. dns: null
  255. upgrade_strategy:
  256. max_unavailable_worker: ""
  257. max_unavailable_controlplane: ""
  258. drain: null
  259. node_drain_input: null

Reference Hardened RKE Template configuration

The reference RKE Template provides the configuration needed to achieve a hardened install of Kubenetes. RKE Templates are used to provision Kubernetes and define Rancher settings. Follow the Rancher documentaion for additional installation and RKE Template details.

The reference cloud-config is generally used in cloud infrastructure environments to allow for configuration management of compute instances. The reference config configures Ubuntu operating system level settings needed before installing kubernetes.

  1. #cloud-config
  2. apt:
  3. sources:
  4. docker.list:
  5. source: deb [arch=amd64] http://download.docker.com/linux/ubuntu $RELEASE stable
  6. keyid: 9DC858229FC7DD38854AE2D88D81803C0EBFCD88
  7. system_info:
  8. default_user:
  9. groups:
  10. - docker
  11. write_files:
  12. - path: "/etc/apt/preferences.d/docker"
  13. owner: root:root
  14. permissions: '0600'
  15. content: |
  16. Package: docker-ce
  17. Pin: version 5:19*
  18. Pin-Priority: 800
  19. - path: "/etc/sysctl.d/90-kubelet.conf"
  20. owner: root:root
  21. permissions: '0644'
  22. content: |
  23. vm.overcommit_memory=1
  24. vm.panic_on_oom=0
  25. kernel.panic=10
  26. kernel.panic_on_oops=1
  27. kernel.keys.root_maxbytes=25000000
  28. package_update: true
  29. packages:
  30. - docker-ce
  31. - docker-ce-cli
  32. - containerd.io
  33. runcmd:
  34. - sysctl -p /etc/sysctl.d/90-kubelet.conf
  35. - groupadd --gid 52034 etcd