{"id":695,"date":"2022-01-15T22:14:44","date_gmt":"2022-01-15T20:14:44","guid":{"rendered":"https:\/\/blog.chloesoe.ch\/?p=695"},"modified":"2022-01-15T22:20:23","modified_gmt":"2022-01-15T20:20:23","slug":"cis-benchmarks-k8s","status":"publish","type":"post","link":"https:\/\/blog.chloesoe.ch\/?p=695","title":{"rendered":"CIS Benchmarks K8s"},"content":{"rendered":"<h2>Overview CIS Benchmarks on K8s<\/h2>\n<p>From CKS there is a chapter about CIS Benchmarks with kube-bench.<\/p>\n<p>Benchmarks are coming from <a href=\"https:\/\/www.cisecurity.org\/benchmark\/kubernetes\/\">https:\/\/www.cisecurity.org\/benchmark\/kubernetes\/<\/a>. You have to go to &quot;Download Latest CIS Benchmark&quot; and register, then you'll get a link an can download all relevant benchmarks, for this how-to we used Fixes are based on &quot;CIS Kubernetes V1.20 Benchmark - v1.0.0&quot; from 2021-05-19.<\/p>\n<p>This documents what changes are needed, to fix all FAILs.<\/p>\n<hr \/>\n<h2>Get Benchmarks<\/h2>\n<p>First check  <a href=\"https:\/\/github.com\/aquasecurity\/kube-bench\/blob\/main\/docs\/platforms.md\">https:\/\/github.com\/aquasecurity\/kube-bench\/blob\/main\/docs\/platforms.md<\/a> and take a note which benchmark version you want to check against.<\/p>\n<p>For my cluster running with K8s version 1.23.1 we use <code>cis-1.20<\/code> version.<\/p>\n<pre><code>wget -O kube-bench-control-plane.yaml https:\/\/raw.githubusercontent.com\/aquasecurity\/kube-bench\/main\/job-\nmaster.yaml\nwget -O kube-bench-node.yaml https:\/\/raw.githubusercontent.com\/aquasecurity\/kube-bench\/main\/job-node.yaml<\/code><\/pre>\n<p>Now you could change the startup command in the job yamls, to add the benchmark you want to use. You could also define <code>--version<\/code> instead of <code>--benchmark<\/code>, but it should be done automatically:<\/p>\n<pre><code>      containers:\n        - name: kube-bench\n          image: aquasec\/kube-bench:latest\n          command: [&quot;kube-bench&quot;, &quot;run&quot;, &quot;--benchmark&quot;, &quot;cis-1.20&quot;, &quot;--targets&quot;, &quot;master&quot;]<\/code><\/pre>\n<p>Now create the jobs:<\/p>\n<pre><code>kubectl create -f kube-bench-control-plane.yaml\nkubectl create -f kube-bench-node.yaml\n\nkubectl get pods\n\nkubectl logs kube-bench-master-&lt;RAND&gt; &gt; bench-master.log\nkubectl logs kube-bench-node-&lt;RAND&gt; &gt; bench-worker.log<\/code><\/pre>\n<h3>Control Plane<\/h3>\n<p>Get the output for master:<\/p>\n<pre><code>$ grep FAIL bench-master.log \n[FAIL] 1.1.12 Ensure that the etcd data directory ownership is set to etcd:etcd (Automated)\n[FAIL] 1.2.5 Ensure that the --kubelet-certificate-authority argument is set as appropriate (Automated)\n[FAIL] 1.2.15 Ensure that the admission control plugin PodSecurityPolicy is set (Automated)\n[FAIL] 1.2.18 Ensure that the --insecure-port argument is set to 0 (Automated)\n[FAIL] 1.2.20 Ensure that the --profiling argument is set to false (Automated)\n[FAIL] 1.2.21 Ensure that the --audit-log-path argument is set (Automated)\n[FAIL] 1.2.22 Ensure that the --audit-log-maxage argument is set to 30 or as appropriate (Automated)\n[FAIL] 1.2.23 Ensure that the --audit-log-maxbackup argument is set to 10 or as appropriate (Automated)\n[FAIL] 1.2.24 Ensure that the --audit-log-maxsize argument is set to 100 or as appropriate (Automated)\n[FAIL] 1.3.2 Ensure that the --profiling argument is set to false (Automated)\n[FAIL] 1.4.1 Ensure that the --profiling argument is set to false (Automated)<\/code><\/pre>\n<h3>Worker Nodes<\/h3>\n<p>Get the output of the worker:<\/p>\n<pre><code>$ grep FAIL bench-worker.log \n[FAIL] 4.2.6 Ensure that the --protect-kernel-defaults argument is set to true (Automated)<\/code><\/pre>\n<hr \/>\n<h2>Fixing Control Plane<\/h2>\n<p>Below documented the failed controls. <\/p>\n<p>Fixes are based on &quot;CIS Kubernetes V1.20 Benchmark&quot; v1.0.0 - 2021-05-19.<\/p>\n<h3>Summary<\/h3>\n<p>After fixing, we have three open controls, which cannot be fixed right now:<\/p>\n<pre><code>[FAIL] 1.2.5 Ensure that the --kubelet-certificate-authority argument is set as appropriate (Automated)\n[FAIL] 1.2.15 Ensure that the admission control plugin PodSecurityPolicy is set (Automated)\n[FAIL] 1.2.21 Ensure that the --audit-log-path argument is set (Automated)<\/code><\/pre>\n<p>Reason see below in their respective sections.<\/p>\n<h3>1.1.12<\/h3>\n<p>By default there is no etcd user.<br \/>\nFirst add user (from <a href=\"https:\/\/devopscube.com\/setup-etcd-cluster-linux\/\">https:\/\/devopscube.com\/setup-etcd-cluster-linux\/<\/a>)<\/p>\n<pre><code>groupadd -f -g 1501 etcd\nuseradd -c &quot;etcd user&quot; -d \/var\/lib\/etcd -s \/bin\/false -g etcd -u 1501 etcd\nchown etcd:etcd \/var\/lib\/etcd\/<\/code><\/pre>\n<h3>1.2.5<\/h3>\n<blockquote>\n<p>1.2.5 Follow the Kubernetes documentation and setup the TLS connection between the apiserver and kubelets. Then, edit the API server pod specification file <code>\/etc\/kubernetes\/manifests\/kube-apiserver.yaml<\/code> on the master node and set the<br \/>\n<code>--kubelet-certificate-authority<\/code> parameter to the path to the cert file for the  certificate authority.<br \/>\n<code>--kubelet-certificate-authority=&lt;ca-string&gt;<\/code><\/p>\n<\/blockquote>\n<p>The obvious solution to add <code>- --kubelet-certificate-authority=\/etc\/kubernetes\/pki\/etcd\/ca.crt <code> to <\/code>\/etc\/kubernetes\/manifests\/kube-apiserver.yaml<\/code> does not really work. After rerunning the kube-bench job, the master pod will have theses logs:<\/p>\n<pre><code>$ kubectl logs kube-bench-master-jhnp9 \nError from server: Get &quot;https:\/\/213.167.224.157:10250\/containerLogs\/default\/kube-bench-master-jhnp9\/kube-bench&quot;: x509: cannot validate certificate for 213.167.224.157 because it doesn&#039;t contain any IP SANs<\/code><\/pre>\n<p>According to <a href=\"https:\/\/stackoverflow.com\/q\/63994701\/7311363\">https:\/\/stackoverflow.com\/q\/63994701\/7311363<\/a> &quot;you first need to make sure you got <a href=\"https:\/\/kubernetes.io\/docs\/reference\/command-line-tools-reference\/kubelet-authentication-authorization\/#kubelet-authentication\">Kubelet authentication<\/a> and <a href=\"https:\/\/kubernetes.io\/docs\/reference\/command-line-tools-reference\/kubelet-authentication-authorization\/#kubelet-authorization\">Kubelet authorization<\/a> enabled. After that you can follow the Kubernetes documentation and setup the <a href=\"https:\/\/kubernetes.io\/docs\/concepts\/architecture\/control-plane-node-communication\/#apiserver-to-kubelet\">TLS connection between the apiserver and kubelet<\/a>&quot;<\/p>\n<p>Sounds interesting, to be revisited once all FAILs are fixed.<\/p>\n<p>Set it back to the default. <\/p>\n<h3>1.2.15<\/h3>\n<p><em>deprecated soon<\/em><\/p>\n<p><a href=\"https:\/\/kubernetes.io\/blog\/2021\/04\/06\/podsecuritypolicy-deprecation-past-present-and-future\/\">https:\/\/kubernetes.io\/blog\/2021\/04\/06\/podsecuritypolicy-deprecation-past-present-and-future\/<\/a><\/p>\n<blockquote>\n<p>1.2.15 Follow the documentation and create Pod Security Policy objects as per your environment.<br \/>\nThen, edit the API server pod specification file \/etc\/kubernetes\/manifests\/kube-apiserver.yaml on the master node and set the --enable-admission-plugins parameter to a value that includes PodSecurityPolicy:<br \/>\n<code><code>--enable-admission-plugins=...,PodSecurityPolicy,...<\/code><\/code><br \/>\nThen restart the API Server.<\/p>\n<\/blockquote>\n<p>Check <code>ps -ef | grep kube-apiserver<\/code> and ensure <code>PodSecurityPolicy<\/code> is included in <code>--enable-admission-plugins<\/code>.<\/p>\n<p>If not add it to <code>\/etc\/kubernetes\/manifests\/kube-apiserver.yaml<\/code><\/p>\n<pre><code>spec:\n  containers:\n  - command:\n    - kube-apiserver\n    ...\n    - --enable-admission-plugins=NodeRestriction,PodSecurityPolicy<\/code><\/pre>\n<p>After setting that, the pod for the our CIS benchmark job cannot be started:<\/p>\n<pre><code>  Type     Reason        Age                From            Message\n  ----     ------        ----               ----            -------\n  Warning  FailedCreate  27s (x3 over 57s)  job-controller  Error creating: pods &quot;kube-bench-master-&quot; is forbidden: PodSecurityPolicy: no providers available to validate pod request<\/code><\/pre>\n<p>checking for PodSecurityPolicies:<\/p>\n<pre><code>$ kubectl get podsecuritypolicies.policy \nWarning: policy\/v1beta1 PodSecurityPolicy is deprecated in v1.21+, unavailable in v1.25+\nNo resources found<\/code><\/pre>\n<h3>1.2.18<\/h3>\n<blockquote>\n<p>1.2.18 Edit the API server pod specification file \/etc\/kubernetes\/manifests\/kube-apiserver.yaml on the master node and set the below parameter.<br \/>\n<code>--insecure-port=0<\/code><\/p>\n<\/blockquote>\n<h4>check<\/h4>\n<pre><code>ps -ef | grep kube-apiserver<\/code><\/pre>\n<p>Verify that the <code>--insecure-port<\/code> argument is set to 0 .<\/p>\n<h4>fix<\/h4>\n<p>If not add it to <code>\/etc\/kubernetes\/manifests\/kube-apiserver.yaml<\/code><\/p>\n<pre><code>spec:\n  containers:\n  - command:\n    - kube-apiserver\n    ...\n    - --insecure-port=0<\/code><\/pre>\n<h3>1.2.21 - 1.2.25 profiling and auditlog<\/h3>\n<p>Audit log is important to log security-relevant actions.<\/p>\n<p>Edit <code>\/etc\/kubernetes\/manifests\/kube-apiserver.yaml<\/code><\/p>\n<pre><code>spec:\n  containers:\n  - command:\n    - kube-apiserver\n...\n    - --audit-log-path=\/var\/log\/apiserver\/audit.log\n    - --audit-log-maxage=30\n    - --audit-log-maxbackup=10\n    - --audit-log-maxsize=100<\/code><\/pre>\n<p><strong>WARNING:<\/strong><br \/>\nIf you set audit-log-path, kubelet cannot start and will show error like:<\/p>\n<pre><code>Jan 15 19:43:28 server.example.com kubelet[13157]: E0115 19:43:28.784974   13157 kubelet.go:2422] &quot;Error getting node&quot; err=&quot;node \\&quot;server.example.com\\&quot; not found&quot;\nJan 15 19:43:28 server.example.com kubelet[13157]: E0115 19:43:28.886336   13157 kubelet.go:2422] &quot;Error getting node&quot; err=&quot;node \\&quot;server.example.com\\&quot; not found&quot;<\/code><\/pre>\n<h3>1.2.20, 1.3.2, 1.4.1 Disable Profiling<\/h3>\n<p>Disable profiling to reduce the potential attack surface. Profiling is used or the identification of specific performance bottlenecks and if we aren't active troubleshooting, we can disable it.<\/p>\n<p>We have to do this for:<\/p>\n<ul>\n<li><code>\/etc\/kubernetes\/manifests\/kube-apiserver.yaml<\/code> (1.2.20)<\/li>\n<li><code>\/etc\/kubernetes\/manifests\/kube-controller-manager.yaml<\/code> (1.3.2)<\/li>\n<li><code>\/etc\/kubernetes\/manifests\/kube-scheduler.yaml<\/code> (1.4.1)<\/li>\n<\/ul>\n<pre><code>spec:\n  containers:\n  - command:\n    - kube-&lt;SYSTEMPOD&gt;\n    ....\n    - --profiling=false<\/code><\/pre>\n<hr \/>\n<h2>Fixing Worker Node<\/h2>\n<p>There is only one FAIL in the default configuration.<br \/>\nOn my Cluster I have a control plane who is also working as worker node, therefore it must also be added there.<\/p>\n<h3>4.2.6<\/h3>\n<blockquote>\n<p>4.2.6 If using a Kubelet config file, edit the file to set rotectKernelDefaults: true.<br \/>\nIf using command line arguments, edit the kubelet service file <code>\/etc\/systemd\/system\/kubelet.service.d\/10-kubeadm.conf<\/code> on each worker node and set the below parameter in KUBELET_SYSTEM_PODS_ARGS variable.<br \/>\n<code>--protect-kernel-defaults=true<\/code><br \/>\nBased on your system, restart the kubelet service. For example:<br \/>\n<code>systemctl daemon-reload<\/code><br \/>\n<code>systemctl restart kubelet.service<\/code><\/p>\n<\/blockquote>\n<p>According to CIS Benchmarks we should add <code>--protect-kernel-defaults=true<\/code> to <code>KUBELET_SYSTEM_PODS_ARGS<\/code>. This environment variable does not exist. We create it and edit the ExecStart command:<\/p>\n<ol>\n<li>Edit <code>\/etc\/systemd\/system\/kubelet.service.d\/10-kubeadm.conf<\/code><\/li>\n<li>Add\n<pre><code>Environment=\"KUBELET_SYSTEM_PODS_ARGS=--protect-kernel-defaults=true\"<\/code><\/pre>\n<\/li>\n<li>Add <code>$KUBELET_SYSTEM_PODS_ARGS<\/code> to the <code>ExecStart<\/code>, have it like that:\n<pre><code>ExecStart=\/usr\/bin\/kubelet $KUBELET_KUBECONFIG_ARGS $KUBELET_CONFIG_ARGS $KUBELET_KUBEADM_ARGS $KUBELET_EXTRA_ARGS $KUBELET_SYSTEM_PODS_ARGS<\/code><\/pre>\n<\/li>\n<li><code>sudo systemctl daemon-reload<\/code><\/li>\n<li><code>sudo systemctl restart kubelet.service<\/code><\/li>\n<\/ol>\n<p>Now CIS Benchmarks should show everything ok for the nodes.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Overview CIS Benchmarks on K8s From CKS there is a chapter about CIS Benchmarks with kube-bench. Benchmarks are coming from https:\/\/www.cisecurity.org\/benchmark\/kubernetes\/. You have to go to &quot;Download Latest CIS Benchmark&quot; and register, then you&#8217;ll get a link an can download all relevant benchmarks, for this how-to we used Fixes are based on &quot;CIS Kubernetes V1.20&#8230; <a href=\"https:\/\/blog.chloesoe.ch\/?p=695\" class=\"more-link\">Continue reading <span class=\"screen-reader-text\">CIS Benchmarks K8s<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[20],"tags":[],"class_list":["post-695","post","type-post","status-publish","format-standard","hentry","category-k8s"],"_links":{"self":[{"href":"https:\/\/blog.chloesoe.ch\/index.php?rest_route=\/wp\/v2\/posts\/695","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/blog.chloesoe.ch\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blog.chloesoe.ch\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blog.chloesoe.ch\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.chloesoe.ch\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=695"}],"version-history":[{"count":4,"href":"https:\/\/blog.chloesoe.ch\/index.php?rest_route=\/wp\/v2\/posts\/695\/revisions"}],"predecessor-version":[{"id":699,"href":"https:\/\/blog.chloesoe.ch\/index.php?rest_route=\/wp\/v2\/posts\/695\/revisions\/699"}],"wp:attachment":[{"href":"https:\/\/blog.chloesoe.ch\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=695"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.chloesoe.ch\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=695"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.chloesoe.ch\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=695"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}