Repository: aquasecurity/kube-bench Branch: main Commit: e4f7b7de225b Files: 344 Total size: 3.5 MB Directory structure: gitextract_nozolvbr/ ├── .github/ │ ├── ISSUE_TEMPLATE/ │ │ ├── bug_report.md │ │ └── config.yml │ ├── dependabot.yml │ └── workflows/ │ ├── build.yml │ ├── mkdocs-deploy.yaml │ ├── publish.yml │ └── release.yml ├── .gitignore ├── .golangci.yaml ├── .goreleaser.yml ├── .yamllint.yaml ├── CONTRIBUTING.md ├── Dockerfile ├── Dockerfile.fips.ubi ├── Dockerfile.ubi ├── LICENSE ├── NOTICE ├── OWNERS ├── README.md ├── cfg/ │ ├── ack-1.0/ │ │ ├── config.yaml │ │ ├── controlplane.yaml │ │ ├── etcd.yaml │ │ ├── managedservices.yaml │ │ ├── master.yaml │ │ ├── node.yaml │ │ └── policies.yaml │ ├── aks-1.0/ │ │ ├── config.yaml │ │ ├── controlplane.yaml │ │ ├── managedservices.yaml │ │ ├── master.yaml │ │ ├── node.yaml │ │ └── policies.yaml │ ├── aks-1.7/ │ │ ├── config.yaml │ │ ├── controlplane.yaml │ │ ├── managedservices.yaml │ │ ├── master.yaml │ │ ├── node.yaml │ │ └── policies.yaml │ ├── aks-1.8/ │ │ ├── config.yaml │ │ ├── controlplane.yaml │ │ ├── managedservices.yaml │ │ ├── master.yaml │ │ ├── node.yaml │ │ └── policies.yaml │ ├── cis-1.10/ │ │ ├── config.yaml │ │ ├── controlplane.yaml │ │ ├── etcd.yaml │ │ ├── master.yaml │ │ ├── node.yaml │ │ └── policies.yaml │ ├── cis-1.11/ │ │ ├── config.yaml │ │ ├── controlplane.yaml │ │ ├── etcd.yaml │ │ ├── master.yaml │ │ ├── node.yaml │ │ └── policies.yaml │ ├── cis-1.12/ │ │ ├── config.yaml │ │ ├── controlplane.yaml │ │ ├── etcd.yaml │ │ ├── master.yaml │ │ ├── node.yaml │ │ └── policies.yaml │ ├── cis-1.20/ │ │ ├── config.yaml │ │ ├── controlplane.yaml │ │ ├── etcd.yaml │ │ ├── master.yaml │ │ ├── node.yaml │ │ └── policies.yaml │ ├── cis-1.23/ │ │ ├── config.yaml │ │ ├── controlplane.yaml │ │ ├── etcd.yaml │ │ ├── master.yaml │ │ ├── node.yaml │ │ └── policies.yaml │ ├── cis-1.24/ │ │ ├── config.yaml │ │ ├── controlplane.yaml │ │ ├── etcd.yaml │ │ ├── master.yaml │ │ ├── node.yaml │ │ └── policies.yaml │ ├── cis-1.24-microk8s/ │ │ ├── config.yaml │ │ ├── controlplane.yaml │ │ ├── etcd.yaml │ │ ├── master.yaml │ │ ├── node.yaml │ │ └── policies.yaml │ ├── cis-1.5/ │ │ ├── config.yaml │ │ ├── controlplane.yaml │ │ ├── etcd.yaml │ │ ├── master.yaml │ │ ├── node.yaml │ │ └── policies.yaml │ ├── cis-1.6/ │ │ ├── config.yaml │ │ ├── controlplane.yaml │ │ ├── etcd.yaml │ │ ├── master.yaml │ │ ├── node.yaml │ │ └── policies.yaml │ ├── cis-1.6-k3s/ │ │ ├── config.yaml │ │ ├── controlplane.yaml │ │ ├── etcd.yaml │ │ ├── master.yaml │ │ ├── node.yaml │ │ └── policies.yaml │ ├── cis-1.7/ │ │ ├── config.yaml │ │ ├── controlplane.yaml │ │ ├── etcd.yaml │ │ ├── master.yaml │ │ ├── node.yaml │ │ └── policies.yaml │ ├── cis-1.8/ │ │ ├── config.yaml │ │ ├── controlplane.yaml │ │ ├── etcd.yaml │ │ ├── master.yaml │ │ ├── node.yaml │ │ └── policies.yaml │ ├── cis-1.9/ │ │ ├── config.yaml │ │ ├── controlplane.yaml │ │ ├── etcd.yaml │ │ ├── master.yaml │ │ ├── node.yaml │ │ └── policies.yaml │ ├── config.yaml │ ├── eks-1.0.1/ │ │ ├── config.yaml │ │ ├── controlplane.yaml │ │ ├── managedservices.yaml │ │ ├── master.yaml │ │ ├── node.yaml │ │ └── policies.yaml │ ├── eks-1.1.0/ │ │ ├── config.yaml │ │ ├── controlplane.yaml │ │ ├── managedservices.yaml │ │ ├── master.yaml │ │ ├── node.yaml │ │ └── policies.yaml │ ├── eks-1.2.0/ │ │ ├── config.yaml │ │ ├── controlplane.yaml │ │ ├── managedservices.yaml │ │ ├── master.yaml │ │ ├── node.yaml │ │ └── policies.yaml │ ├── eks-1.5.0/ │ │ ├── config.yaml │ │ ├── controlplane.yaml │ │ ├── managedservices.yaml │ │ ├── master.yaml │ │ ├── node.yaml │ │ └── policies.yaml │ ├── eks-1.7.0/ │ │ ├── config.yaml │ │ ├── controlplane.yaml │ │ ├── managedservices.yaml │ │ ├── master.yaml │ │ ├── node.yaml │ │ └── policies.yaml │ ├── eks-1.8.0/ │ │ ├── config.yaml │ │ ├── controlplane.yaml │ │ ├── managedservices.yaml │ │ ├── master.yaml │ │ ├── node.yaml │ │ └── policies.yaml │ ├── eks-stig-kubernetes-v1r6/ │ │ ├── config.yaml │ │ ├── controlplane.yaml │ │ ├── managedservices.yaml │ │ ├── master.yaml │ │ ├── node.yaml │ │ └── policies.yaml │ ├── gke-1.0/ │ │ ├── config.yaml │ │ ├── controlplane.yaml │ │ ├── etcd.yaml │ │ ├── managedservices.yaml │ │ ├── master.yaml │ │ ├── node.yaml │ │ └── policies.yaml │ ├── gke-1.2.0/ │ │ ├── config.yaml │ │ ├── controlplane.yaml │ │ ├── managedservices.yaml │ │ ├── master.yaml │ │ ├── node.yaml │ │ └── policies.yaml │ ├── gke-1.6.0/ │ │ ├── config.yaml │ │ ├── controlplane.yaml │ │ ├── managedservices.yaml │ │ ├── master.yaml │ │ ├── node.yaml │ │ └── policies.yaml │ ├── gke-1.8.0/ │ │ ├── config.yaml │ │ ├── controlplane.yaml │ │ ├── managedservices.yaml │ │ ├── master.yaml │ │ ├── node.yaml │ │ └── policies.yaml │ ├── k3s-cis-1.23/ │ │ ├── config.yaml │ │ ├── controlplane.yaml │ │ ├── etcd.yaml │ │ ├── master.yaml │ │ ├── node.yaml │ │ └── policies.yaml │ ├── k3s-cis-1.24/ │ │ ├── config.yaml │ │ ├── controlplane.yaml │ │ ├── etcd.yaml │ │ ├── master.yaml │ │ ├── node.yaml │ │ └── policies.yaml │ ├── k3s-cis-1.7/ │ │ ├── config.yaml │ │ ├── controlplane.yaml │ │ ├── etcd.yaml │ │ ├── master.yaml │ │ ├── node.yaml │ │ └── policies.yaml │ ├── k3s-cis-1.8/ │ │ ├── config.yaml │ │ ├── controlplane.yaml │ │ ├── etcd.yaml │ │ ├── master.yaml │ │ ├── node.yaml │ │ └── policies.yaml │ ├── rh-0.7/ │ │ ├── config.yaml │ │ ├── master.yaml │ │ └── node.yaml │ ├── rh-1.0/ │ │ ├── config.yaml │ │ ├── controlplane.yaml │ │ ├── etcd.yaml │ │ ├── master.yaml │ │ ├── node.yaml │ │ └── policies.yaml │ ├── rh-1.4/ │ │ ├── config.yaml │ │ ├── controlplane.yaml │ │ ├── etcd.yaml │ │ ├── master.yaml │ │ ├── node.yaml │ │ └── policies.yaml │ ├── rh-1.8/ │ │ ├── config.yaml │ │ ├── controlplane.yaml │ │ ├── etcd.yaml │ │ ├── master.yaml │ │ ├── node.yaml │ │ └── policies.yaml │ ├── rke-cis-1.23/ │ │ ├── config.yaml │ │ ├── controlplane.yaml │ │ ├── etcd.yaml │ │ ├── master.yaml │ │ ├── node.yaml │ │ └── policies.yaml │ ├── rke-cis-1.24/ │ │ ├── config.yaml │ │ ├── controlplane.yaml │ │ ├── etcd.yaml │ │ ├── master.yaml │ │ ├── node.yaml │ │ └── policies.yaml │ ├── rke-cis-1.7/ │ │ ├── config.yaml │ │ ├── controlplane.yaml │ │ ├── etcd.yaml │ │ ├── master.yaml │ │ ├── node.yaml │ │ └── policies.yaml │ ├── rke2-cis-1.23/ │ │ ├── config.yaml │ │ ├── controlplane.yaml │ │ ├── etcd.yaml │ │ ├── master.yaml │ │ ├── node.yaml │ │ └── policies.yaml │ ├── rke2-cis-1.24/ │ │ ├── config.yaml │ │ ├── controlplane.yaml │ │ ├── etcd.yaml │ │ ├── master.yaml │ │ ├── node.yaml │ │ └── policies.yaml │ ├── rke2-cis-1.7/ │ │ ├── config.yaml │ │ ├── controlplane.yaml │ │ ├── etcd.yaml │ │ ├── master.yaml │ │ ├── node.yaml │ │ └── policies.yaml │ ├── rke2-cis-1.8/ │ │ ├── config.yaml │ │ ├── controlplane.yaml │ │ ├── etcd.yaml │ │ ├── master.yaml │ │ ├── node.yaml │ │ └── policies.yaml │ └── tkgi-1.2.53/ │ ├── config.yaml │ ├── controlplane.yaml │ ├── etcd.yaml │ ├── master.yaml │ ├── node.yaml │ └── policies.yaml ├── check/ │ ├── check.go │ ├── check_test.go │ ├── controls.go │ ├── controls_test.go │ ├── data │ ├── test.go │ └── test_test.go ├── cmd/ │ ├── common.go │ ├── common_test.go │ ├── database.go │ ├── kubernetes_version.go │ ├── kubernetes_version_test.go │ ├── root.go │ ├── run.go │ ├── run_test.go │ ├── securityHub.go │ ├── testdata/ │ │ ├── controlsCollection.json │ │ ├── passedControlsCollection.json │ │ ├── result.json │ │ └── result_no_totals.json │ ├── util.go │ ├── util_test.go │ └── version.go ├── codecov.yml ├── docs/ │ ├── architecture.md │ ├── asff.md │ ├── controls.md │ ├── flags-and-commands.md │ ├── index.md │ ├── installation.md │ ├── platforms.md │ └── running.md ├── entrypoint.sh ├── fipsonly.go ├── go.mod ├── go.sum ├── hack/ │ ├── debug.yaml │ ├── kind-stig.test.yaml │ ├── kind-stig.yaml │ ├── kind.yaml │ └── node_only.yaml ├── helper_scripts/ │ └── check_files_owner_in_dir.sh ├── hooks/ │ └── build ├── integration/ │ └── testdata/ │ ├── Expected_output.data │ └── Expected_output_stig.data ├── internal/ │ └── findings/ │ ├── doc.go │ └── publisher.go ├── job-ack.yaml ├── job-aks.yaml ├── job-eks-asff.yaml ├── job-eks-stig.yaml ├── job-eks.yaml ├── job-gke.yaml ├── job-iks.yaml ├── job-master.yaml ├── job-node.yaml ├── job-tkgi.yaml ├── job.yaml ├── main.go ├── makefile └── mkdocs.yml ================================================ FILE CONTENTS ================================================ ================================================ FILE: .github/ISSUE_TEMPLATE/bug_report.md ================================================ --- name: Bug report about: Tell us about a problem you are experiencing --- **Overview** [A clear and concise description of what the bug is] **How did you run kube-bench?** [Please specify exactly how you ran kube-bench, including details of command parameters and/or job file that you used to run it] **What happened?** [Please include output from the report to illustrate the problem. If possible please supply logs generated with the `-v 3` parameter.] **What did you expect to happen:** [Please describe what you expected to happen differently.] **Environment** [What is your version of kube-bench? (run `kube-bench version`)] [What is your version of Kubernetes? (run `kubectl version` or `oc version` on OpenShift.)] **Running processes** [Please include the output from running `ps -eaf | grep kube` on the affected node. This will allow us to check what Kubernetes processes are running, and how this compares to what kube-bench detected.] **Configuration files** [If kube-bench is reporting an issue related to the settings defined in a config file, please attach the file, or include an extract showing the settings that are being detected incorrectly.] **Anything else you would like to add:** [Miscellaneous information that will assist in solving the issue.] ================================================ FILE: .github/ISSUE_TEMPLATE/config.yml ================================================ --- blank_issues_enabled: false contact_links: - name: Feature request url: https://github.com/aquasecurity/kube-bench/discussions/new?category_id=19113743 about: Share ideas for new features - name: Ask a question url: https://github.com/aquasecurity/kube-bench/discussions/new?category_id=19113742 about: Ask questions and discuss with other community members ================================================ FILE: .github/dependabot.yml ================================================ --- version: 2 updates: - package-ecosystem: gomod directory: / schedule: interval: weekly - package-ecosystem: github-actions directory: / schedule: interval: weekly - package-ecosystem: docker directory: / schedule: interval: weekly ================================================ FILE: .github/workflows/build.yml ================================================ --- name: Build on: push: branches: - main paths-ignore: - "*.md" - "LICENSE" - "NOTICE" pull_request: paths-ignore: - "*.md" - "LICENSE" - "NOTICE" env: KIND_VERSION: "v0.11.1" KIND_IMAGE: "kindest/node:v1.21.1@sha256:69860bda5563ac81e3c0057d654b5253219618a22ec3a346306239bba8cfa1a6" jobs: lint: name: Lint runs-on: ubuntu-latest steps: - name: Checkout code uses: actions/checkout@v6 - name: Setup Go uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6.3.0 with: go-version-file: go.mod - name: yaml-lint uses: ibiqlik/action-yamllint@v3 - name: Setup golangci-lint uses: golangci/golangci-lint-action@v8 with: version: v2.5.0 args: --verbose --timeout 2m unit: name: Unit tests runs-on: ubuntu-latest steps: - name: Checkout code uses: actions/checkout@v6 - name: Setup Go uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6.3.0 with: go-version-file: go.mod - name: Run unit tests run: make tests - name: Upload code coverage uses: codecov/codecov-action@v5 with: file: ./coverage.txt e2e: name: E2e tests runs-on: ubuntu-latest steps: - name: Checkout code uses: actions/checkout@v6 - name: Setup Go uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6.3.0 with: go-version-file: go.mod - name: Setup Kubernetes cluster (KIND) uses: engineerd/setup-kind@v0.6.2 with: version: ${{ env.KIND_VERSION }} image: ${{ env.KIND_IMAGE }} name: kube-bench - name: Test connection to Kubernetes cluster run: | kubectl cluster-info kubectl describe node - name: Run integration tests run: | make integration-test - name: Compare output with expected output uses: GuillaumeFalourd/diff-action@v1 with: first_file_path: ./test.data second_file_path: integration/testdata/Expected_output.data expected_result: PASSED release: name: Release snapshot runs-on: ubuntu-latest needs: [e2e, unit] steps: - name: Checkout code uses: actions/checkout@v6 with: fetch-depth: 0 - name: Setup Go uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6.3.0 with: go-version-file: go.mod - name: Dry-run release snapshot uses: goreleaser/goreleaser-action@v7 with: distribution: goreleaser version: v1.7.0 args: release --snapshot --skip-publish --rm-dist ================================================ FILE: .github/workflows/mkdocs-deploy.yaml ================================================ --- # This is a manually triggered workflow to build and publish the MkDocs from the # main branch to GitHub pages at https://aquasecurity.github.io/kube-bench. name: Deploy documentation on: workflow_dispatch: inputs: version: description: Version to be deployed required: true jobs: deploy: name: Deploy documentation runs-on: ubuntu-latest steps: - name: Checkout main uses: actions/checkout@v6 with: fetch-depth: 0 persist-credentials: true - uses: actions/setup-python@v6 with: python-version: 3.x - run: | pip install git+https://${GH_TOKEN}@github.com/squidfunk/mkdocs-material-insiders.git pip install mike pip install mkdocs-macros-plugin env: # Note: It is not the same as ${{ secrets.GITHUB_TOKEN }} ! GH_TOKEN: ${{ secrets.MKDOCS_AQUA_BOT }} - run: | git config user.name "aqua-bot" git config user.email "aqua-bot@users.noreply.github.com" - run: | mike deploy --push --update-aliases ${{ github.event.inputs.version }} latest ================================================ FILE: .github/workflows/publish.yml ================================================ --- name: Publish on: workflow_dispatch: push: tags: - "v*" env: ALIAS: aquasecurity DOCKERHUB_ALIAS: aquasec REP: kube-bench jobs: publish: name: Publish runs-on: ubuntu-latest steps: - name: Check Out Repo uses: actions/checkout@v6 - name: Set up QEMU uses: docker/setup-qemu-action@v4 - name: Set up Docker Buildx id: buildx uses: docker/setup-buildx-action@v4 - name: Cache Docker layers uses: actions/cache@v5 with: path: /tmp/.buildx-cache key: ${{ runner.os }}-buildxarch-${{ github.sha }} restore-keys: | ${{ runner.os }}-buildxarch- - name: Login to Docker Hub uses: docker/login-action@v4 with: username: ${{ secrets.DOCKERHUB_USER }} password: ${{ secrets.DOCKERHUB_TOKEN }} - name: Login to ECR uses: docker/login-action@v4 with: registry: public.ecr.aws username: ${{ secrets.ECR_ACCESS_KEY_ID }} password: ${{ secrets.ECR_SECRET_ACCESS_KEY }} - name: Get version id: get_version uses: crazy-max/ghaction-docker-meta@v5 with: images: ${{ env.REP }} tag-semver: | {{version}} - name: Extract variables from makefile (kubectl) id: extract_vars run: | echo "KUBECTL_VERSION=$(grep -oP '^KUBECTL_VERSION\s*\?=\s*\K.*' makefile)" >> $GITHUB_ENV - name: Build and push - Docker/ECR id: docker_build uses: docker/build-push-action@v7 with: context: . platforms: linux/amd64,linux/arm64,linux/ppc64le,linux/s390x builder: ${{ steps.buildx.outputs.name }} push: true build-args: | KUBEBENCH_VERSION=${{ steps.get_version.outputs.version }} KUBECTL_VERSION=${{ env.KUBECTL_VERSION }} tags: | ${{ env.DOCKERHUB_ALIAS }}/${{ env.REP }}:${{ steps.get_version.outputs.version }} public.ecr.aws/${{ env.ALIAS }}/${{ env.REP }}:${{ steps.get_version.outputs.version }} ${{ env.DOCKERHUB_ALIAS }}/${{ env.REP }}:latest public.ecr.aws/${{ env.ALIAS }}/${{ env.REP }}:latest cache-from: type=local,src=/tmp/.buildx-cache/release cache-to: type=local,mode=max,dest=/tmp/.buildx-cache/release - name: Build and push ubi image - Docker/ECR id: docker_build_ubi uses: docker/build-push-action@v7 with: context: . platforms: linux/amd64,linux/arm64,linux/ppc64le,linux/s390x builder: ${{ steps.buildx.outputs.name }} push: true file: Dockerfile.ubi build-args: | KUBEBENCH_VERSION=${{ steps.get_version.outputs.version }} KUBECTL_VERSION=${{ env.KUBECTL_VERSION }} tags: | ${{ env.DOCKERHUB_ALIAS }}/${{ env.REP }}:${{ steps.get_version.outputs.version }}-ubi public.ecr.aws/${{ env.ALIAS }}/${{ env.REP }}:${{ steps.get_version.outputs.version }}-ubi cache-from: type=local,src=/tmp/.buildx-cache/release cache-to: type=local,mode=max,dest=/tmp/.buildx-cache/release - name: Image digest run: echo ${{ steps.docker_build.outputs.digest }} - name: Build and push fips ubi image - Docker/ECR id: docker_build_fips_ubi uses: docker/build-push-action@v7 with: context: . platforms: linux/amd64,linux/arm64,linux/ppc64le,linux/s390x builder: ${{ steps.buildx.outputs.name }} push: true file: Dockerfile.fips.ubi build-args: | KUBEBENCH_VERSION=${{ steps.get_version.outputs.version }} KUBECTL_VERSION=${{ env.KUBECTL_VERSION }} tags: | ${{ env.DOCKERHUB_ALIAS }}/${{ env.REP }}:${{ steps.get_version.outputs.version }}-ubi-fips public.ecr.aws/${{ env.ALIAS }}/${{ env.REP }}:${{ steps.get_version.outputs.version }}-ubi-fips cache-from: type=local,src=/tmp/.buildx-cache/release cache-to: type=local,mode=max,dest=/tmp/.buildx-cache/release - name: Image digest run: echo ${{ steps.docker_build.outputs.digest }} ================================================ FILE: .github/workflows/release.yml ================================================ --- name: Release on: push: tags: - "v*" env: KIND_VERSION: "v0.11.1" KIND_IMAGE: "kindest/node:v1.21.1@sha256:69860bda5563ac81e3c0057d654b5253219618a22ec3a346306239bba8cfa1a6" jobs: release: name: Release runs-on: ubuntu-latest steps: - name: Checkout code uses: actions/checkout@v6 with: fetch-depth: 0 - name: Setup Go uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6.3.0 with: go-version-file: go.mod - name: Run unit tests run: make tests - name: Setup Kubernetes cluster (KIND) uses: engineerd/setup-kind@v0.6.2 with: version: ${{ env.KIND_VERSION }} image: ${{ env.KIND_IMAGE }} name: kube-bench - name: Test connection to Kubernetes cluster run: | kubectl cluster-info kubectl describe node - name: Run integration tests run: | make integration-test - name: Compare output with expected output uses: GuillaumeFalourd/diff-action@v1 with: first_file_path: ./test.data second_file_path: integration/testdata/Expected_output.data expected_result: PASSED - name: Release uses: goreleaser/goreleaser-action@v7 with: distribution: goreleaser version: v1.7.0 args: release --rm-dist env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} ================================================ FILE: .gitignore ================================================ kube-bench *.swp vendor dist .vscode/ hack/kind.test.yaml coverage.txt .idea/ # Directory junk file .DS_Store thumbs.db /kubeconfig.kube-bench /test.data *.iml ================================================ FILE: .golangci.yaml ================================================ version: "2" linters: default: none enable: - gocyclo - govet - misspell exclusions: generated: lax presets: - comments - common-false-positives - legacy - std-error-handling paths: - third_party$ - builtin$ - examples$ formatters: enable: - gofmt - goimports exclusions: generated: lax paths: - third_party$ - builtin$ - examples$ ================================================ FILE: .goreleaser.yml ================================================ --- project_name: kube-bench env: - GO111MODULE=on - CGO_ENABLED=0 - KUBEBENCH_CFG=/etc/kube-bench/cfg builds: - main: . binary: kube-bench tags: - osusergo - netgo - static_build goos: - linux - darwin goarch: - amd64 - arm - arm64 - ppc64le - s390x goarm: - 6 - 7 ldflags: - "-s" - "-w" - "-extldflags '-static'" - "-X github.com/aquasecurity/kube-bench/cmd.KubeBenchVersion={{.Version}}" - "-X github.com/aquasecurity/kube-bench/cmd.cfgDir={{.Env.KUBEBENCH_CFG}}" # Archive customization archives: - id: default format: tar.gz name_template: '{{ .Binary }}_{{.Version}}_{{ .Os }}_{{ .Arch }}{{ if .Arm }}v{{.Arm }}{{ end }}' files: - "cfg/**/*" - "cfg/config.yaml" nfpms: - vendor: Aqua Security description: "The Kubernetes Bench for Security is a Go application that checks whether Kubernetes is deployed according to security best practices" maintainer: Yoav Rotem license: Apache-2.0 homepage: https://github.com/aquasecurity/kube-bench file_name_template: '{{ .Binary }}_{{.Version}}_{{ .Os }}_{{ .Arch }}{{ if .Arm }}v{{.Arm }}{{ end }}' contents: - src: "cfg/**/*" dst: "/etc/kube-bench/cfg" - src: "cfg/config.yaml" dst: "/etc/kube-bench/cfg/config.yaml" formats: - deb - rpm changelog: sort: asc filters: exclude: - '^docs' - '^test' - '^release' ================================================ FILE: .yamllint.yaml ================================================ --- extends: default rules: line-length: disable truthy: disable ================================================ FILE: CONTRIBUTING.md ================================================ Thank you for taking an interest in contributing to kube-bench ! ## Contributing, bug reporting, openning issues and starting discussions ### Issues - Feel free to open an issue for any reason as long as you make it clear if the issue is about a bug/feature/question/comment. - Please spend some time giving due diligence to the issue tracker. Your issue might be a duplicate. If it is, please add your comment to the existing issue. - Remember, users might be searching for your issue in the future. So please give it a meaningful title to help others. - The issue should clearly explain the reason for opening the proposal if you have any, along with any relevant technical information. - For questions and bug reports, please include the following information: - version of kube-bench you are running (from kube-bench version) along with the command line options you are using. - version of Kubernetes you are running (from kubectl version or oc version for Openshift). - Verbose log output, by setting the `-v 3` command line option. ### Bugs If you think you have found a bug please follow the instructions below. - Open a [new bug](https://github.com/aquasecurity/kube-bench/issues/new?assignees=&labels=&template=bug_report.md) if a duplicate doesn't already exist. - Make sure to give as much information as possible in the following questions - Overview - How did you run kube-bench? - What happened? - What did you expect to happen - Environment - Running processes - Configuration files - Anything else you would like to add - Set `-v 3` command line option and save the log output. Please paste this into your issue. ### Features We also use the GitHub discussions to track feature requests. If you have an idea to make kube-bench even more awesome follow the steps below. - Open a [new discussion](https://github.com/aquasecurity/kube-bench/discussions/new?category_id=19113743) if a duplicate doesn't already exist. - Remember users might be searching for your discussion in the future, so please give it a meaningful title to helps others. - Clearly define the use case, using concrete examples. For example, I type `this` and kube-bench does `that`. - If you would like to include a technical design for your feature please feel free to do so. ### Questions We also use the GitHub discussions to Q&A. - Open a [new discussion](https://github.com/aquasecurity/kube-bench/discussions/new) if a duplicate doesn't already exist. - Remember users might be searching for your discussion in the future, so please give it a meaningful title to helps others. ### Pull Requests We welcome pull requests! - Every Pull Request should have an associated Issue, unless you are fixing a trivial documentation issue. - We will not accept changes to LICENSE, NOTICE or CONTRIBUTING from outside the Aqua Security team. Please raise an Issue if you believe there is a problem with any of these files. - Your PR is more likely to be accepted if it focuses on just one change. - Describe what the PR does. There's no convention enforced, but please try to be concise and descriptive. Treat the PR description as a commit message. Titles that start with "fix"/"add"/"improve"/"remove" are good examples. - Please add the associated Issue in the PR description. - Please include a comment with the results before and after your change. - There's no need to add or tag reviewers. - If a reviewer commented on your code or asked for changes, please remember to mark the discussion as resolved after you address it. PRs with unresolved issues should not be merged (even if the comment is unclear or requires no action from your side). - Please include a comment with the results before and after your change. - Your PR is more likely to be accepted if it includes tests (We have not historically been very strict about tests, but we would like to improve this!). - You're welcome to submit a draft PR if you would like early feedback on an idea or an approach. - Happy coding! ## Testing locally with kind Our makefile contains targets to test your current version of kube-bench inside a [Kind](https://kind.sigs.k8s.io/) cluster. This can be very handy if you don't want to run a real Kubernetes cluster for development purposes. First, you'll need to create the cluster using `make kind-test-cluster` this will create a new cluster if it cannot be found on your machine. By default, the cluster is named `kube-bench` but you can change the name by using the environment variable `KIND_PROFILE`. *If kind cannot be found on your system the target will try to install it using `go get`* Next, you'll have to build the kube-bench docker image using `make build-docker`, then we will be able to push the docker image to the cluster using `make kind-push`. Finally, we can use the `make kind-run` target to run the current version of kube-bench in the cluster and follow the logs of pods created. (Ctrl+C to exit) Every time you want to test a change, you'll need to rebuild the docker image and push it to cluster before running it again. ( `make build-docker kind-push kind-run` ) To run the STIG tests locally execute the following: `make build-docker kind-push kind-run-stig` ================================================ FILE: Dockerfile ================================================ FROM golang:1.26.1 AS build WORKDIR /go/src/github.com/aquasecurity/kube-bench/ COPY makefile makefile COPY go.mod go.sum ./ COPY main.go . COPY check/ check/ COPY cmd/ cmd/ COPY internal/ internal/ ARG KUBEBENCH_VERSION RUN make build && cp kube-bench /go/bin/kube-bench # Add kubectl to run policies checks ARG KUBECTL_VERSION TARGETARCH RUN wget -O /usr/local/bin/kubectl "https://dl.k8s.io/release/v${KUBECTL_VERSION}/bin/linux/${TARGETARCH}/kubectl" RUN wget -O kubectl.sha256 "https://dl.k8s.io/release/v${KUBECTL_VERSION}/bin/linux/${TARGETARCH}/kubectl.sha256" # Verify kubectl sha256sum RUN /bin/bash -c 'echo "$( kube-bench is a tool that checks whether Kubernetes is deployed securely by running the checks documented in the [CIS Kubernetes Benchmark](https://www.cisecurity.org/benchmark/kubernetes/). Tests are configured with YAML files, making this tool easy to update as test specifications evolve. ![Kubernetes Bench for Security](/docs/images/output.png "Kubernetes Bench for Security") ## CIS Scanning as part of Trivy and the Trivy Operator [Trivy](https://github.com/aquasecurity/trivy), the all in one cloud native security scanner, can be deployed as a [Kubernetes Operator](https://github.com/aquasecurity/trivy-operator) inside a cluster. Both, the [Trivy CLI](https://github.com/aquasecurity/trivy), and the [Trivy Operator](https://github.com/aquasecurity/trivy-operator) support CIS Kubernetes Benchmark scanning among several other features. ## Quick start There are multiple ways to run kube-bench. You can run kube-bench inside a pod, but it will need access to the host's PID namespace in order to check the running processes, as well as access to some directories on the host where config files and other files are stored. The supplied `job.yaml` [file](job.yaml) can be applied to run the tests as a job. For example: ```bash $ kubectl apply -f job.yaml job.batch/kube-bench created $ kubectl get pods NAME READY STATUS RESTARTS AGE kube-bench-j76s9 0/1 ContainerCreating 0 3s # Wait for a few seconds for the job to complete $ kubectl get pods NAME READY STATUS RESTARTS AGE kube-bench-j76s9 0/1 Completed 0 11s # The results are held in the pod's logs kubectl logs kube-bench-j76s9 [INFO] 1 Master Node Security Configuration [INFO] 1.1 API Server ... ``` For more information and different ways to run kube-bench see [documentation](docs/running.md) ### Please Note 1. kube-bench implements the [CIS Kubernetes Benchmark](https://www.cisecurity.org/benchmark/kubernetes/) as closely as possible. Please raise issues here if kube-bench is not correctly implementing the test as described in the Benchmark. To report issues in the Benchmark itself (for example, tests that you believe are inappropriate), please join the [CIS community](https://cisecurity.org). 1. There is not a one-to-one mapping between releases of Kubernetes and releases of the CIS benchmark. See [CIS Kubernetes Benchmark support](docs/platforms.md#cis-kubernetes-benchmark-support) to see which releases of Kubernetes are covered by different releases of the benchmark. By default, kube-bench will determine the test set to run based on the Kubernetes version running on the machine. - see the following documentation on [Running kube-bench](docs/running.md#running-kube-bench) for more details. ## Contributing Kindly read [Contributing](CONTRIBUTING.md) before contributing. We welcome PRs and issue reports. ## Roadmap Going forward we plan to release updates to kube-bench to add support for new releases of the CIS Benchmark. Note that these are not released as frequently as Kubernetes releases. ================================================ FILE: cfg/ack-1.0/config.yaml ================================================ --- ## Version-specific settings that override the values in cfg/config.yaml ================================================ FILE: cfg/ack-1.0/controlplane.yaml ================================================ --- controls: version: "ack-1.0" id: 3 text: "Control Plane Configuration" type: "controlplane" groups: - id: 3.1 text: "Authentication and Authorization" checks: - id: 3.1.1 text: "Revoke client certificate when possible leakage (Manual)" type: "manual" remediation: | Kubernetes provides the option to use client certificates for user authentication. ACK issues kubeconfig with its client certificates as the user credentials for connecing to target cluster. User should revoke his/her issued kubeconfig when possible leakage. scored: false - id: 3.2 text: "Logging" checks: - id: 3.2.1 text: "Ensure that a minimal audit policy is created (Manual)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--audit-policy-file" remediation: | Create an audit policy file for your cluster. scored: false - id: 3.2.2 text: "Ensure that the audit policy covers key security concerns (Manual)" type: "manual" remediation: | Consider modification of the audit policy in use on the cluster to include these items, at a minimum. scored: false ================================================ FILE: cfg/ack-1.0/etcd.yaml ================================================ --- controls: version: "ack-1.0" id: 2 text: "Etcd Node Configuration" type: "etcd" groups: - id: 2 text: "Etcd Node Configuration Files" checks: - id: 2.1 text: "Ensure that the --cert-file and --key-file arguments are set as appropriate (Automated)" audit: "/bin/ps -ef | /bin/grep $etcdbin | /bin/grep -v grep" tests: bin_op: and test_items: - flag: "--cert-file" env: "ETCD_CERT_FILE" - flag: "--key-file" env: "ETCD_KEY_FILE" remediation: | Follow the etcd service documentation and configure TLS encryption. Then, edit the etcd pod specification file /etc/kubernetes/manifests/etcd.yaml on the master node and set the below parameters. --cert-file= --key-file= scored: true - id: 2.2 text: "Ensure that the --client-cert-auth argument is set to true (Automated)" audit: "/bin/ps -ef | /bin/grep $etcdbin | /bin/grep -v grep" tests: test_items: - flag: "--client-cert-auth" env: "ETCD_CLIENT_CERT_AUTH" compare: op: eq value: true remediation: | Edit the etcd pod specification file $etcdconf on the master node and set the below parameter. --client-cert-auth="true" scored: true - id: 2.3 text: "Ensure that the --auto-tls argument is not set to true (Automated)" audit: "/bin/ps -ef | /bin/grep $etcdbin | /bin/grep -v grep" tests: bin_op: or test_items: - flag: "--auto-tls" env: "ETCD_AUTO_TLS" set: false - flag: "--auto-tls" env: "ETCD_AUTO_TLS" compare: op: eq value: false remediation: | Edit the etcd pod specification file $etcdconf on the master node and either remove the --auto-tls parameter or set it to false. --auto-tls=false scored: true - id: 2.4 text: "Ensure that the --peer-cert-file and --peer-key-file arguments are set as appropriate (Automated)" audit: "/bin/ps -ef | /bin/grep $etcdbin | /bin/grep -v grep" tests: bin_op: and test_items: - flag: "--peer-cert-file" env: "ETCD_PEER_CERT_FILE" - flag: "--peer-key-file" env: "ETCD_PEER_KEY_FILE" remediation: | Follow the etcd service documentation and configure peer TLS encryption as appropriate for your etcd cluster. Then, edit the etcd pod specification file $etcdconf on the master node and set the below parameters. --peer-client-file= --peer-key-file= scored: true - id: 2.5 text: "Ensure that the --peer-client-cert-auth argument is set to true (Automated)" audit: "/bin/ps -ef | /bin/grep $etcdbin | /bin/grep -v grep" tests: test_items: - flag: "--peer-client-cert-auth" env: "ETCD_PEER_CLIENT_CERT_AUTH" compare: op: eq value: true remediation: | Edit the etcd pod specification file $etcdconf on the master node and set the below parameter. --peer-client-cert-auth=true scored: true - id: 2.6 text: "Ensure that the --peer-auto-tls argument is not set to true (Automated)" audit: "/bin/ps -ef | /bin/grep $etcdbin | /bin/grep -v grep" tests: bin_op: or test_items: - flag: "--peer-auto-tls" env: "ETCD_PEER_AUTO_TLS" set: false - flag: "--peer-auto-tls" env: "ETCD_PEER_AUTO_TLS" compare: op: eq value: false remediation: | Edit the etcd pod specification file $etcdconf on the master node and either remove the --peer-auto-tls parameter or set it to false. --peer-auto-tls=false scored: true - id: 2.7 text: "Ensure that a unique Certificate Authority is used for etcd (Manual)" audit: "/bin/ps -ef | /bin/grep $etcdbin | /bin/grep -v grep" tests: test_items: - flag: "--trusted-ca-file" env: "ETCD_TRUSTED_CA_FILE" remediation: | [Manual test] Follow the etcd documentation and create a dedicated certificate authority setup for the etcd service. Then, edit the etcd pod specification file $etcdconf on the master node and set the below parameter. --trusted-ca-file= scored: false ================================================ FILE: cfg/ack-1.0/managedservices.yaml ================================================ --- controls: version: "ack-1.0" id: 6 text: "Managed Services" type: "managedservices" groups: - id: 6.1 text: "Image Registry and Image Scanning" checks: - id: 6.1.1 text: "Ensure Image Vulnerability Scanning using ACR image scanning or a third party provider (Manual)" type: "manual" remediation: | Ensure Image Vulnerability Scanning using ACR image scanning or a third party provider by follow the ACR document: https://www.alibabacloud.com/help/doc-detail/160146.htm scored: false - id: 6.1.2 text: "Minimize user access to ACR (Manual)" type: "manual" remediation: | Minimize user access to ACR by follow the ACR document to setup network access control: https://www.alibabacloud.com/help/doc-detail/142179.htm And follow the ACR document to setup Resource Access Management (RAM) policies for ACR: https://www.alibabacloud.com/help/doc-detail/144229.htm scored: false - id: 6.1.3 text: "Minimize cluster access to read-only for ACR (Manual)" type: "manual" remediation: Minimize cluster access to read-only for ACR scored: false - id: 6.1.4 text: "Minimize Container Registries to only those approved (Manual)" type: "manual" remediation: Minimize Container Registries to only those approved scored: false - id: 6.2 text: "Key Management Service (KMS)" checks: - id: 6.2.1 text: "Ensure Kubernetes Secrets are encrypted using keys managed in KMS (Manual)" type: "manual" remediation: | Ensure Kubernetes Secrets are encrypted using keys managed in KMS by follow The ACK document: https://www.alibabacloud.com/help/zh/doc-detail/177372.htm scored: false - id: 6.3 text: "Cluster Networking" checks: - id: 6.3.1 text: "Restrict Access to the Control Plane Endpoint (Manual)" type: "manual" remediation: Restrict Access to the Control Plane Endpoint scored: false - id: 6.3.2 text: "Ensure clusters are created with Private Endpoint Enabled and Public Access Disabled (Manual)" type: "manual" remediation: Ensure clusters are created with Private Endpoint Enabled and Public Access Disabled scored: false - id: 6.3.3 text: "Ensure clusters are created with Private Nodes (Manual)" type: "manual" remediation: Ensure clusters are created with Private Nodes scored: false - id: 6.3.4 text: "Ensure Network Policy is Enabled and set as appropriate (Manual)" type: "manual" remediation: Ensure Network Policy is Enabled and set as appropriate scored: false - id: 6.3.5 text: "Encrypt traffic to HTTPS load balancers with TLS certificates (Manual)" type: "manual" remediation: Encrypt traffic to HTTPS load balancers with TLS certificates scored: false - id: 6.4 text: "Storage" checks: - id: 6.4.1 text: "Enable data disk encryption for Alibaba Cloud Disks (Manual)" type: "manual" remediation: Enable data disk encryption for Alibaba Cloud Disks scored: false - id: 6.5 text: "Logging" checks: - id: 6.5.1 text: "Ensure Cluster Auditing is Enabled (Manual)" type: "manual" remediation: Ensure Cluster Auditing is Enabled scored: false - id: 6.6 text: "Other Cluster Configurations" checks: - id: 6.6.1 text: "Ensure Pod Security Policy is Enabled and set as appropriate (Manual)" type: "manual" remediation: Ensure Pod Security Policy is Enabled and set as appropriate scored: false - id: 6.6.2 text: "Enable Cloud Security Center (Manual)" type: "manual" remediation: Enable Cloud Security Center scored: false - id: 6.6.3 text: "Consider ACK Sandboxed-Container for running untrusted workloads (Manual)" type: "manual" remediation: Consider ACK Sandboxed-Container for running untrusted workloads - id: 6.6.4 text: "Consider ACK TEE-based when running confidential computing (Manual)" type: "manual" remediation: Consider ACK TEE-based when running confidential computing - id: 6.6.5 text: "Consider use service account token volume projection (Manual)" type: "manual" remediation: Consider use service account token volume projection ================================================ FILE: cfg/ack-1.0/master.yaml ================================================ --- controls: version: "ack-1.0" id: 1 text: "Master Node Security Configuration" type: "master" groups: - id: 1.1 text: "Master Node Configuration Files" checks: - id: 1.1.1 text: "Ensure that the API server pod specification file permissions are set to 644 or more restrictive (Automated)" audit: "/bin/sh -c 'if test -e $apiserverconf; then stat -c permissions=%a $apiserverconf; fi'" tests: test_items: - flag: "permissions" compare: op: bitmask value: "644" remediation: | Run the below command (based on the file location on your system) on the master node. For example, chmod 644 $apiserverconf scored: true - id: 1.1.2 text: "Ensure that the API server pod specification file ownership is set to root:root (Automated)" audit: "/bin/sh -c 'if test -e $apiserverconf; then stat -c %U:%G $apiserverconf; fi'" tests: test_items: - flag: "root:root" remediation: | Run the below command (based on the file location on your system) on the master node. For example, chown root:root $apiserverconf scored: true - id: 1.1.3 text: "Ensure that the controller manager pod specification file permissions are set to 644 or more restrictive (Automated)" audit: "/bin/sh -c 'if test -e $controllermanagerconf; then stat -c permissions=%a $controllermanagerconf; fi'" tests: test_items: - flag: "permissions" compare: op: bitmask value: "644" remediation: | Run the below command (based on the file location on your system) on the master node. For example, chmod 644 $controllermanagerconf scored: true - id: 1.1.4 text: "Ensure that the controller manager pod specification file ownership is set to root:root (Automated)" audit: "/bin/sh -c 'if test -e $controllermanagerconf; then stat -c %U:%G $controllermanagerconf; fi'" tests: test_items: - flag: "root:root" remediation: | Run the below command (based on the file location on your system) on the master node. For example, chown root:root $controllermanagerconf scored: true - id: 1.1.5 text: "Ensure that the scheduler pod specification file permissions are set to 644 or more restrictive (Automated)" audit: "/bin/sh -c 'if test -e $schedulerconf; then stat -c permissions=%a $schedulerconf; fi'" tests: test_items: - flag: "permissions" compare: op: bitmask value: "644" remediation: | Run the below command (based on the file location on your system) on the master node. For example, chmod 644 $schedulerconf scored: true - id: 1.1.6 text: "Ensure that the scheduler pod specification file ownership is set to root:root (Automated)" audit: "/bin/sh -c 'if test -e $schedulerconf; then stat -c %U:%G $schedulerconf; fi'" tests: test_items: - flag: "root:root" remediation: | Run the below command (based on the file location on your system) on the master node. For example, chown root:root $schedulerconf scored: true - id: 1.1.7 text: "Ensure that the etcd service file permissions are set to 644 or more restrictive (Automated)" audit: | stat -c permissions=%a /usr/lib/systemd/system/etcd.service || \ if test -e $etcdconf; then find $etcdconf -name '*etcd*' | xargs stat -c permissions=%a; fi use_multiple_values: true tests: test_items: - flag: "permissions" compare: op: bitmask value: "644" remediation: | Run the below command (based on the file location on your system) on the master node. For example, chmod 644 $etcdconf scored: true - id: 1.1.8 text: "Ensure that the etcd service file ownership is set to root:root (Automated)" audit: | stat -c %U:%G /usr/lib/systemd/system/etcd.service || \ if test -e $etcdconf; then find $etcdconf -name '*etcd*' | xargs stat -c %U:%G; fi use_multiple_values: true tests: test_items: - flag: "root:root" remediation: | Run the below command (based on the file location on your system) on the master node. For example, chown root:root $etcdconf scored: true - id: 1.1.9 text: "Ensure that the Container Network Interface file permissions are set to 644 or more restrictive (Manual)" audit: "stat -c permissions=%a " type: "manual" remediation: | Run the below command (based on the file location on your system) on the master node. For example, chmod 644 scored: false - id: 1.1.10 text: "Ensure that the Container Network Interface file ownership is set to root:root (Manual)" audit: "stat -c %U:%G " type: "manual" remediation: | Run the below command (based on the file location on your system) on the master node. For example, chown root:root scored: false - id: 1.1.11 text: "Ensure that the etcd data directory permissions are set to 700 or more restrictive (Automated)" audit: | stat -c permissions=%a /var/lib/etcd/data.etcd || \ ps -ef | grep $etcdbin | grep -- --data-dir | sed 's%.*data-dir[= ]\([^ ]*\).*%\1%' | xargs stat -c permissions=%a tests: test_items: - flag: "permissions" compare: op: bitmask value: "700" remediation: | On the etcd server node, get the etcd data directory, passed as an argument --data-dir, from the below command: ps -ef | grep etcd Run the below command (based on the etcd data directory found above). For example, chmod 700 /var/lib/etcd scored: true - id: 1.1.12 text: "Ensure that the etcd data directory ownership is set to etcd:etcd (Automated)" audit: | stat -c %U:%G /var/lib/etcd/data.etcd || \ ps -ef | grep $etcdbin | grep -- --data-dir | sed 's%.*data-dir[= ]\([^ ]*\).*%\1%' | xargs stat -c %U:%G tests: test_items: - flag: "etcd:etcd" remediation: | On the etcd server node, get the etcd data directory, passed as an argument --data-dir, from the below command: ps -ef | grep etcd Run the below command (based on the etcd data directory found above). For example, chown etcd:etcd /var/lib/etcd scored: true - id: 1.1.13 text: "Ensure that the admin.conf file permissions are set to 644 or more restrictive (Automated)" audit: "/bin/sh -c 'if test -e /etc/kubernetes/admin.conf; then stat -c permissions=%a /etc/kubernetes/admin.conf; fi'" tests: test_items: - flag: "permissions" compare: op: bitmask value: "644" remediation: | Run the below command (based on the file location on your system) on the master node. For example, chmod 644 /etc/kubernetes/admin.conf scored: true - id: 1.1.14 text: "Ensure that the admin.conf file ownership is set to root:root (Automated)" audit: "/bin/sh -c 'if test -e /etc/kubernetes/admin.conf; then stat -c %U:%G /etc/kubernetes/admin.conf; fi'" tests: test_items: - flag: "root:root" remediation: | Run the below command (based on the file location on your system) on the master node. For example, chown root:root /etc/kubernetes/admin.conf scored: true - id: 1.1.15 text: "Ensure that the scheduler.conf file permissions are set to 644 or more restrictive (Automated)" audit: "/bin/sh -c 'if test -e $schedulerkubeconfig; then stat -c permissions=%a $schedulerkubeconfig; fi'" tests: test_items: - flag: "permissions" compare: op: bitmask value: "644" remediation: | Run the below command (based on the file location on your system) on the master node. For example, chmod 644 $schedulerkubeconfig scored: true - id: 1.1.16 text: "Ensure that the scheduler.conf file ownership is set to root:root (Automated)" audit: "/bin/sh -c 'if test -e $schedulerkubeconfig; then stat -c %U:%G $schedulerkubeconfig; fi'" tests: test_items: - flag: "root:root" remediation: | Run the below command (based on the file location on your system) on the master node. For example, chown root:root $schedulerkubeconfig scored: true - id: 1.1.17 text: "Ensure that the controller-manager.conf file permissions are set to 644 or more restrictive (Automated)" audit: "/bin/sh -c 'if test -e $controllermanagerkubeconfig; then stat -c permissions=%a $controllermanagerkubeconfig; fi'" tests: test_items: - flag: "permissions" compare: op: bitmask value: "644" remediation: | Run the below command (based on the file location on your system) on the master node. For example, chmod 644 $controllermanagerkubeconfig scored: true - id: 1.1.18 text: "Ensure that the controller-manager.conf file ownership is set to root:root (Automated)" audit: "/bin/sh -c 'if test -e $controllermanagerkubeconfig; then stat -c %U:%G $controllermanagerkubeconfig; fi'" tests: test_items: - flag: "root:root" remediation: | Run the below command (based on the file location on your system) on the master node. For example, chown root:root $controllermanagerkubeconfig scored: true - id: 1.1.19 text: "Ensure that the Kubernetes PKI directory and file ownership is set to root:root (Automated)" audit: "find /etc/kubernetes/pki/ | xargs stat -c %U:%G" use_multiple_values: true tests: test_items: - flag: "root:root" remediation: | Run the below command (based on the file location on your system) on the master node. For example, chown -R root:root /etc/kubernetes/pki/ scored: true - id: 1.1.20 text: "Ensure that the Kubernetes PKI certificate file permissions are set to 644 or more restrictive (Manual)" audit: "find /etc/kubernetes/pki -name '*.crt' | xargs stat -c permissions=%a" use_multiple_values: true tests: test_items: - flag: "permissions" compare: op: bitmask value: "644" remediation: | Run the below command (based on the file location on your system) on the master node. For example, chmod -R 644 /etc/kubernetes/pki/*.crt scored: false - id: 1.1.21 text: "Ensure that the Kubernetes PKI key file permissions are set to 600 (Manual)" audit: "find /etc/kubernetes/pki -name '*.key' | xargs stat -c permissions=%a" use_multiple_values: true tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Run the below command (based on the file location on your system) on the master node. For example, chmod -R 600 /etc/kubernetes/pki/*.key scored: false - id: 1.2 text: "API Server" checks: - id: 1.2.1 text: "Ensure that the --basic-auth-file argument is not set (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--basic-auth-file" set: false remediation: | Follow the documentation and configure alternate mechanisms for authentication. Then, edit the API server pod specification file $apiserverconf on the master node and remove the --basic-auth-file= parameter. scored: true - id: 1.2.2 text: "Ensure that the --token-auth-file parameter is not set (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--token-auth-file" set: false remediation: | Follow the documentation and configure alternate mechanisms for authentication. Then, edit the API server pod specification file $apiserverconf on the master node and remove the --token-auth-file= parameter. scored: true - id: 1.2.3 text: "Ensure that the --kubelet-https argument is set to true (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: bin_op: or test_items: - flag: "--kubelet-https" compare: op: eq value: true - flag: "--kubelet-https" set: false remediation: | Edit the API server pod specification file $apiserverconf on the master node and remove the --kubelet-https parameter. scored: true - id: 1.2.4 text: "Ensure that the --kubelet-client-certificate and --kubelet-client-key arguments are set as appropriate (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: bin_op: and test_items: - flag: "--kubelet-client-certificate" - flag: "--kubelet-client-key" remediation: | Follow the Kubernetes documentation and set up the TLS connection between the apiserver and kubelets. Then, edit API server pod specification file $apiserverconf on the master node and set the kubelet client certificate and key parameters as below. --kubelet-client-certificate= --kubelet-client-key= scored: true - id: 1.2.5 text: "Ensure that the --authorization-mode argument is not set to AlwaysAllow (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--authorization-mode" compare: op: nothave value: "AlwaysAllow" remediation: | Edit the API server pod specification file $apiserverconf on the master node and set the --authorization-mode parameter to values other than AlwaysAllow. One such example could be as below. --authorization-mode=RBAC scored: true - id: 1.2.6 text: "Ensure that the --authorization-mode argument includes Node (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--authorization-mode" compare: op: has value: "Node" remediation: | Edit the API server pod specification file $apiserverconf on the master node and set the --authorization-mode parameter to a value that includes Node. --authorization-mode=Node,RBAC scored: true - id: 1.2.7 text: "Ensure that the --authorization-mode argument includes RBAC (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--authorization-mode" compare: op: has value: "RBAC" remediation: | Edit the API server pod specification file $apiserverconf on the master node and set the --authorization-mode parameter to a value that includes RBAC, for example: --authorization-mode=Node,RBAC scored: true - id: 1.2.8 text: "Ensure that the admission control plugin EventRateLimit is set (Manual)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--enable-admission-plugins" compare: op: has value: "EventRateLimit" remediation: | Follow the Kubernetes documentation and set the desired limits in a configuration file. Then, edit the API server pod specification file $apiserverconf and set the below parameters. --enable-admission-plugins=...,EventRateLimit,... --admission-control-config-file= scored: false - id: 1.2.9 text: "Ensure that the admission control plugin AlwaysAdmit is not set (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: bin_op: or test_items: - flag: "--enable-admission-plugins" compare: op: nothave value: AlwaysAdmit - flag: "--enable-admission-plugins" set: false remediation: | Edit the API server pod specification file $apiserverconf on the master node and either remove the --enable-admission-plugins parameter, or set it to a value that does not include AlwaysAdmit. scored: true - id: 1.2.10 text: "Ensure that the admission control plugin AlwaysPullImages is set (Manual)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--enable-admission-plugins" compare: op: has value: "AlwaysPullImages" remediation: | Edit the API server pod specification file $apiserverconf on the master node and set the --enable-admission-plugins parameter to include AlwaysPullImages. --enable-admission-plugins=...,AlwaysPullImages,... scored: false - id: 1.2.11 text: "Ensure that the admission control plugin SecurityContextDeny is set if PodSecurityPolicy is not used (Manual)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: bin_op: or test_items: - flag: "--enable-admission-plugins" compare: op: has value: "SecurityContextDeny" - flag: "--enable-admission-plugins" compare: op: has value: "PodSecurityPolicy" remediation: | Edit the API server pod specification file $apiserverconf on the master node and set the --enable-admission-plugins parameter to include SecurityContextDeny, unless PodSecurityPolicy is already in place. --enable-admission-plugins=...,SecurityContextDeny,... scored: false - id: 1.2.12 text: "Ensure that the admission control plugin ServiceAccount is set (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: bin_op: or test_items: - flag: "--disable-admission-plugins" compare: op: nothave value: "ServiceAccount" - flag: "--disable-admission-plugins" set: false remediation: | Follow the documentation and create ServiceAccount objects as per your environment. Then, edit the API server pod specification file $apiserverconf on the master node and ensure that the --disable-admission-plugins parameter is set to a value that does not include ServiceAccount. scored: true - id: 1.2.13 text: "Ensure that the admission control plugin NamespaceLifecycle is set (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: bin_op: or test_items: - flag: "--disable-admission-plugins" compare: op: nothave value: "NamespaceLifecycle" - flag: "--disable-admission-plugins" set: false remediation: | Edit the API server pod specification file $apiserverconf on the master node and set the --disable-admission-plugins parameter to ensure it does not include NamespaceLifecycle. scored: true - id: 1.2.14 text: "Ensure that the admission control plugin PodSecurityPolicy is set (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--enable-admission-plugins" compare: op: has value: "PodSecurityPolicy" remediation: | Follow the documentation and create Pod Security Policy objects as per your environment. Then, edit the API server pod specification file $apiserverconf on the master node and set the --enable-admission-plugins parameter to a value that includes PodSecurityPolicy: --enable-admission-plugins=...,PodSecurityPolicy,... Then restart the API Server. scored: true - id: 1.2.15 text: "Ensure that the admission control plugin NodeRestriction is set (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--enable-admission-plugins" compare: op: has value: "NodeRestriction" remediation: | Follow the Kubernetes documentation and configure NodeRestriction plug-in on kubelets. Then, edit the API server pod specification file $apiserverconf on the master node and set the --enable-admission-plugins parameter to a value that includes NodeRestriction. --enable-admission-plugins=...,NodeRestriction,... scored: true - id: 1.2.16 text: "Ensure that the --insecure-bind-address argument is not set (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--insecure-bind-address" set: false remediation: | Edit the API server pod specification file $apiserverconf on the master node and remove the --insecure-bind-address parameter. scored: true - id: 1.2.17 text: "Ensure that the --insecure-port argument is set to 0 (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--insecure-port" compare: op: eq value: 0 remediation: | Edit the API server pod specification file $apiserverconf on the master node and set the below parameter. --insecure-port=0 scored: true - id: 1.2.18 text: "Ensure that the --secure-port argument is not set to 0 (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: bin_op: or test_items: - flag: "--secure-port" compare: op: gt value: 0 - flag: "--secure-port" set: false remediation: | Edit the API server pod specification file $apiserverconf on the master node and either remove the --secure-port parameter or set it to a different (non-zero) desired port. scored: true - id: 1.2.19 text: "Ensure that the --profiling argument is set to false (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--profiling" compare: op: eq value: false remediation: | Edit the API server pod specification file $apiserverconf on the master node and set the below parameter. --profiling=false scored: true - id: 1.2.20 text: "Ensure that the --audit-log-path argument is set (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--audit-log-path" remediation: | Edit the API server pod specification file $apiserverconf on the master node and set the --audit-log-path parameter to a suitable path and file where you would like audit logs to be written, for example: --audit-log-path=/var/log/apiserver/audit.log scored: true - id: 1.2.21 text: "Ensure that the --audit-log-maxage argument is set to 30 or as appropriate (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--audit-log-maxage" compare: op: gte value: 30 remediation: | Edit the API server pod specification file $apiserverconf on the master node and set the --audit-log-maxage parameter to 30 or as an appropriate number of days: --audit-log-maxage=30 scored: true - id: 1.2.22 text: "Ensure that the --audit-log-maxbackup argument is set to 10 or as appropriate (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--audit-log-maxbackup" compare: op: gte value: 10 remediation: | Edit the API server pod specification file $apiserverconf on the master node and set the --audit-log-maxbackup parameter to 10 or to an appropriate value. --audit-log-maxbackup=10 scored: true - id: 1.2.23 text: "Ensure that the --audit-log-maxsize argument is set to 100 or as appropriate (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--audit-log-maxsize" compare: op: gte value: 100 remediation: | Edit the API server pod specification file $apiserverconf on the master node and set the --audit-log-maxsize parameter to an appropriate size in MB. For example, to set it as 100 MB: --audit-log-maxsize=100 scored: true - id: 1.2.24 text: "Ensure that the --request-timeout argument is set as appropriate (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" type: manual remediation: | Edit the API server pod specification file $apiserverconf and set the below parameter as appropriate and if needed. For example, --request-timeout=300s scored: true - id: 1.2.25 text: "Ensure that the --service-account-lookup argument is set to true (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: bin_op: or test_items: - flag: "--service-account-lookup" set: false - flag: "--service-account-lookup" compare: op: eq value: true remediation: | Edit the API server pod specification file $apiserverconf on the master node and set the below parameter. --service-account-lookup=true Alternatively, you can delete the --service-account-lookup parameter from this file so that the default takes effect. scored: true - id: 1.2.26 text: "Ensure that the --service-account-key-file argument is set as appropriate (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--service-account-key-file" remediation: | Edit the API server pod specification file $apiserverconf on the master node and set the --service-account-key-file parameter to the public key file for service accounts: --service-account-key-file= scored: true - id: 1.2.27 text: "Ensure that the --etcd-certfile and --etcd-keyfile arguments are set as appropriate (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: bin_op: and test_items: - flag: "--etcd-certfile" - flag: "--etcd-keyfile" remediation: | Follow the Kubernetes documentation and set up the TLS connection between the apiserver and etcd. Then, edit the API server pod specification file $apiserverconf on the master node and set the etcd certificate and key file parameters. --etcd-certfile= --etcd-keyfile= scored: true - id: 1.2.28 text: "Ensure that the --tls-cert-file and --tls-private-key-file arguments are set as appropriate (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: bin_op: and test_items: - flag: "--tls-cert-file" - flag: "--tls-private-key-file" remediation: | Follow the Kubernetes documentation and set up the TLS connection on the apiserver. Then, edit the API server pod specification file $apiserverconf on the master node and set the TLS certificate and private key file parameters. --tls-cert-file= --tls-private-key-file= scored: true - id: 1.2.29 text: "Ensure that the --client-ca-file argument is set as appropriate (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--client-ca-file" remediation: | Follow the Kubernetes documentation and set up the TLS connection on the apiserver. Then, edit the API server pod specification file $apiserverconf on the master node and set the client certificate authority file. --client-ca-file= scored: true - id: 1.2.30 text: "Ensure that the --etcd-cafile argument is set as appropriate (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--etcd-cafile" remediation: | Follow the Kubernetes documentation and set up the TLS connection between the apiserver and etcd. Then, edit the API server pod specification file $apiserverconf on the master node and set the etcd certificate authority file parameter. --etcd-cafile= scored: true - id: 1.2.31 text: "Ensure that the --encryption-provider-config argument is set as appropriate (Manual)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--encryption-provider-config" remediation: | Follow the Kubernetes documentation and configure a EncryptionConfig file. Then, edit the API server pod specification file $apiserverconf on the master node and set the --encryption-provider-config parameter to the path of that file: --encryption-provider-config= scored: false - id: 1.2.32 text: "Ensure that encryption providers are appropriately configured (Manual)" audit: | ENCRYPTION_PROVIDER_CONFIG=$(ps -ef | grep $apiserverbin | grep -- --encryption-provider-config | sed 's%.*encryption-provider-config[= ]\([^ ]*\).*%\1%') if test -e $ENCRYPTION_PROVIDER_CONFIG; then grep -A1 'providers:' $ENCRYPTION_PROVIDER_CONFIG | tail -n1 | grep -o "[A-Za-z]*" | sed 's/^/provider=/'; fi tests: test_items: - flag: "provider" compare: op: valid_elements value: "aescbc,kms,secretbox" remediation: | Follow the Kubernetes documentation and configure a EncryptionConfig file. In this file, choose aescbc, kms or secretbox as the encryption provider. scored: false - id: 1.2.33 text: "Ensure that the API Server only makes use of Strong Cryptographic Ciphers (Manual)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--tls-cipher-suites" compare: op: valid_elements value: "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" remediation: | Edit the API server pod specification file /etc/kubernetes/manifests/kube-apiserver.yaml on the master node and set the below parameter. --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 scored: false - id: 1.3 text: "Controller Manager" checks: - id: 1.3.1 text: "Ensure that the --terminated-pod-gc-threshold argument is set as appropriate (Manual)" audit: "/bin/ps -ef | grep $controllermanagerbin | grep -v grep" tests: test_items: - flag: "--terminated-pod-gc-threshold" remediation: | Edit the Controller Manager pod specification file $controllermanagerconf on the master node and set the --terminated-pod-gc-threshold to an appropriate threshold, for example: --terminated-pod-gc-threshold=10 scored: false - id: 1.3.2 text: "Ensure that the --profiling argument is set to false (Automated)" audit: "/bin/ps -ef | grep $controllermanagerbin | grep -v grep" tests: test_items: - flag: "--profiling" compare: op: eq value: false remediation: | Edit the Controller Manager pod specification file $controllermanagerconf on the master node and set the below parameter. --profiling=false scored: true - id: 1.3.3 text: "Ensure that the --use-service-account-credentials argument is set to true (Automated)" audit: "/bin/ps -ef | grep $controllermanagerbin | grep -v grep" tests: test_items: - flag: "--use-service-account-credentials" compare: op: noteq value: false remediation: | Edit the Controller Manager pod specification file $controllermanagerconf on the master node to set the below parameter. --use-service-account-credentials=true scored: true - id: 1.3.4 text: "Ensure that the --service-account-private-key-file argument is set as appropriate (Automated)" audit: "/bin/ps -ef | grep $controllermanagerbin | grep -v grep" tests: test_items: - flag: "--service-account-private-key-file" remediation: | Edit the Controller Manager pod specification file $controllermanagerconf on the master node and set the --service-account-private-key-file parameter to the private key file for service accounts. --service-account-private-key-file= scored: true - id: 1.3.5 text: "Ensure that the --root-ca-file argument is set as appropriate (Automated)" audit: "/bin/ps -ef | grep $controllermanagerbin | grep -v grep" tests: test_items: - flag: "--root-ca-file" remediation: | Edit the Controller Manager pod specification file $controllermanagerconf on the master node and set the --root-ca-file parameter to the certificate bundle file`. --root-ca-file= scored: true - id: 1.3.6 text: "Ensure that the RotateKubeletServerCertificate argument is set to true (Automated)" audit: "/bin/ps -ef | grep $controllermanagerbin | grep -v grep" tests: bin_op: or test_items: - flag: "--feature-gates" compare: op: nothave value: "RotateKubeletServerCertificate=false" set: true - flag: "--feature-gates" set: false remediation: | Edit the Controller Manager pod specification file $controllermanagerconf on the master node and set the --feature-gates parameter to include RotateKubeletServerCertificate=true. --feature-gates=RotateKubeletServerCertificate=true scored: true - id: 1.3.7 text: "Ensure that the --bind-address argument is set to 127.0.0.1 (Automated)" audit: "/bin/ps -ef | grep $controllermanagerbin | grep -v grep" tests: bin_op: or test_items: - flag: "--bind-address" compare: op: eq value: "127.0.0.1" - flag: "--bind-address" set: false remediation: | Edit the Controller Manager pod specification file $controllermanagerconf on the master node and ensure the correct value for the --bind-address parameter scored: true - id: 1.4 text: "Scheduler" checks: - id: 1.4.1 text: "Ensure that the --profiling argument is set to false (Automated)" audit: "/bin/ps -ef | grep $schedulerbin | grep -v grep" tests: test_items: - flag: "--profiling" compare: op: eq value: false remediation: | Edit the Scheduler pod specification file $schedulerconf file on the master node and set the below parameter. --profiling=false scored: true - id: 1.4.2 text: "Ensure that the --bind-address argument is set to 127.0.0.1 (Automated)" audit: "/bin/ps -ef | grep $schedulerbin | grep -v grep" tests: bin_op: or test_items: - flag: "--bind-address" compare: op: eq value: "127.0.0.1" - flag: "--bind-address" set: false remediation: | Edit the Scheduler pod specification file $schedulerconf on the master node and ensure the correct value for the --bind-address parameter scored: true ================================================ FILE: cfg/ack-1.0/node.yaml ================================================ --- controls: version: "ack-1.0" id: 4 text: "Worker Node Security Configuration" type: "node" groups: - id: 4.1 text: "Worker Node Configuration Files" checks: - id: 4.1.1 text: "Ensure that the kubelet service file permissions are set to 644 or more restrictive (Automated)" audit: '/bin/sh -c ''if test -e $kubeletsvc; then stat -c permissions=%a $kubeletsvc; fi'' ' tests: test_items: - flag: "permissions" compare: op: bitmask value: "644" remediation: | Run the below command (based on the file location on your system) on the each worker node. For example, chmod 644 $kubeletsvc scored: true - id: 4.1.2 text: "Ensure that the kubelet service file ownership is set to root:root (Automated)" audit: '/bin/sh -c "if test -e $kubeletsvc; then stat -c %U:%G $kubeletsvc; else echo \"File not found\"; fi"' tests: bin_op: or test_items: - flag: root:root - flag: "File not found" remediation: | Run the below command (based on the file location on your system) on the each worker node. For example, chown root:root $kubeletsvc scored: true - id: 4.1.3 text: "If proxy kubeconfig file exists ensure permissions are set to 644 or more restrictive (Manual)" audit: '/bin/sh -c ''if test -e $proxykubeconfig; then stat -c permissions=%a $proxykubeconfig; fi'' ' tests: bin_op: or test_items: - flag: "permissions" compare: op: bitmask value: "644" remediation: | Run the below command (based on the file location on your system) on the each worker node. For example, chmod 644 $proxykubeconfig scored: false - id: 4.1.4 text: "If proxy kubeconfig file exists ensure ownership is set to root:root (Manual)" audit: '/bin/sh -c ''if test -e $proxykubeconfig; then stat -c %U:%G $proxykubeconfig; fi'' ' tests: bin_op: or test_items: - flag: root:root remediation: | Run the below command (based on the file location on your system) on the each worker node. For example, chown root:root $proxykubeconfig scored: false - id: 4.1.5 text: "Ensure that the --kubeconfig kubelet.conf file permissions are set to 644 or more restrictive (Automated)" audit: '/bin/sh -c ''if test -e $kubeletkubeconfig; then stat -c permissions=%a $kubeletkubeconfig; fi'' ' tests: test_items: - flag: "permissions" compare: op: bitmask value: "644" remediation: | Run the below command (based on the file location on your system) on the each worker node. For example, chmod 644 $kubeletkubeconfig scored: true - id: 4.1.6 text: "Ensure that the --kubeconfig kubelet.conf file ownership is set to root:root (Manual)" audit: '/bin/sh -c ''if test -e $kubeletkubeconfig; then stat -c %U:%G $kubeletkubeconfig; fi'' ' tests: test_items: - flag: root:root remediation: | Run the below command (based on the file location on your system) on the each worker node. For example, chown root:root $kubeletkubeconfig scored: false - id: 4.1.7 text: "Ensure that the certificate authorities file permissions are set to 644 or more restrictive (Manual)" audit: | CAFILE=$(ps -ef | grep kubelet | grep -v apiserver | grep -- --client-ca-file= | awk -F '--client-ca-file=' '{print $2}' | awk '{print $1}' | uniq) if test -z $CAFILE; then CAFILE=$kubeletcafile; fi if test -e $CAFILE; then stat -c permissions=%a $CAFILE; fi tests: test_items: - flag: "permissions" compare: op: bitmask value: "644" remediation: | Run the following command to modify the file permissions of the --client-ca-file chmod 644 scored: false - id: 4.1.8 text: "Ensure that the client certificate authorities file ownership is set to root:root (Manual)" audit: | CAFILE=$(ps -ef | grep kubelet | grep -v apiserver | grep -- --client-ca-file= | awk -F '--client-ca-file=' '{print $2}' | awk '{print $1}' | uniq) if test -z $CAFILE; then CAFILE=$kubeletcafile; fi if test -e $CAFILE; then stat -c %U:%G $CAFILE; fi tests: test_items: - flag: root:root compare: op: eq value: root:root remediation: | Run the following command to modify the ownership of the --client-ca-file. chown root:root scored: false - id: 4.1.9 text: "Ensure that the kubelet --config configuration file has permissions set to 644 or more restrictive (Automated)" audit: '/bin/sh -c ''if test -e $kubeletconf; then stat -c permissions=%a $kubeletconf; fi'' ' tests: test_items: - flag: "permissions" compare: op: bitmask value: "644" remediation: | Run the following command (using the config file location identified in the Audit step) chmod 644 $kubeletconf scored: true - id: 4.1.10 text: "Ensure that the kubelet --config configuration file ownership is set to root:root (Automated)" audit: '/bin/sh -c ''if test -e $kubeletconf; then stat -c %U:%G $kubeletconf; fi'' ' tests: test_items: - flag: root:root remediation: | Run the following command (using the config file location identified in the Audit step) chown root:root $kubeletconf scored: true - id: 4.2 text: "Kubelet" checks: - id: 4.2.1 text: "Ensure that the anonymous-auth argument is set to false (Automated)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: "--anonymous-auth" path: '{.authentication.anonymous.enabled}' compare: op: eq value: false remediation: | If using a Kubelet config file, edit the file to set authentication: anonymous: enabled to false. If using executable arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_SYSTEM_PODS_ARGS variable. --anonymous-auth=false Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: true - id: 4.2.2 text: "Ensure that the --authorization-mode argument is not set to AlwaysAllow (Automated)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --authorization-mode path: '{.authorization.mode}' compare: op: nothave value: AlwaysAllow remediation: | If using a Kubelet config file, edit the file to set authorization: mode to Webhook. If using executable arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_AUTHZ_ARGS variable. --authorization-mode=Webhook Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: true - id: 4.2.3 text: "Ensure that the --client-ca-file argument is set as appropriate (Automated)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --client-ca-file path: '{.authentication.x509.clientCAFile}' remediation: | If using a Kubelet config file, edit the file to set authentication: x509: clientCAFile to the location of the client CA file. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_AUTHZ_ARGS variable. --client-ca-file= Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: true - id: 4.2.4 text: "Ensure that the --read-only-port argument is set to 0 (Manual)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: bin_op: or test_items: - flag: "--read-only-port" path: '{.readOnlyPort}' compare: op: eq value: 0 - flag: "--read-only-port" path: '{.readOnlyPort}' set: false remediation: | If using a Kubelet config file, edit the file to set readOnlyPort to 0. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_SYSTEM_PODS_ARGS variable. --read-only-port=0 Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: false - id: 4.2.5 text: "Ensure that the --streaming-connection-idle-timeout argument is not set to 0 (Manual)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --streaming-connection-idle-timeout path: '{.streamingConnectionIdleTimeout}' compare: op: noteq value: 0 - flag: --streaming-connection-idle-timeout path: '{.streamingConnectionIdleTimeout}' set: false bin_op: or remediation: | If using a Kubelet config file, edit the file to set streamingConnectionIdleTimeout to a value other than 0. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_SYSTEM_PODS_ARGS variable. --streaming-connection-idle-timeout=5m Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: false - id: 4.2.6 text: "Ensure that the --protect-kernel-defaults argument is set to true (Manual)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --protect-kernel-defaults path: '{.protectKernelDefaults}' compare: op: eq value: true remediation: | If using a Kubelet config file, edit the file to set protectKernelDefaults: true. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_SYSTEM_PODS_ARGS variable. --protect-kernel-defaults=true Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: false - id: 4.2.7 text: "Ensure that the --make-iptables-util-chains argument is set to true (Automated)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --make-iptables-util-chains path: '{.makeIPTablesUtilChains}' compare: op: eq value: true - flag: --make-iptables-util-chains path: '{.makeIPTablesUtilChains}' set: false bin_op: or remediation: | If using a Kubelet config file, edit the file to set makeIPTablesUtilChains: true. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and remove the --make-iptables-util-chains argument from the KUBELET_SYSTEM_PODS_ARGS variable. Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: true - id: 4.2.8 text: "Ensure that the --event-qps argument is set to 0 or a level which ensures appropriate event capture (Manual)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --event-qps path: '{.eventRecordQPS}' compare: op: eq value: 0 remediation: | If using a Kubelet config file, edit the file to set eventRecordQPS: to an appropriate level. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_SYSTEM_PODS_ARGS variable. Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: false - id: 4.2.9 text: "Ensure that the --tls-cert-file and --tls-private-key-file arguments are set as appropriate (Manual)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --tls-cert-file path: '{.tlsCertFile}' - flag: --tls-private-key-file path: '{.tlsPrivateKeyFile}' remediation: | If using a Kubelet config file, edit the file to set tlsCertFile to the location of the certificate file to use to identify this Kubelet, and tlsPrivateKeyFile to the location of the corresponding private key file. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameters in KUBELET_CERTIFICATE_ARGS variable. --tls-cert-file= --tls-private-key-file= Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: false - id: 4.2.10 text: "Ensure that the Kubelet only makes use of Strong Cryptographic Ciphers (Manual)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --tls-cipher-suites path: '{range .tlsCipherSuites[:]}{}{'',''}{end}' compare: op: valid_elements value: 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 remediation: | If using a Kubelet config file, edit the file to set tlsCipherSuites: to 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 or to a subset of these values. If using executable arguments, edit the kubelet service file $kubeletsvc on each worker node and set the --tls-cipher-suites parameter as follows, or to a subset of these values. --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 Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: false ================================================ FILE: cfg/ack-1.0/policies.yaml ================================================ --- controls: version: "ack-1.0" id: 5 text: "Kubernetes Policies" type: "policies" groups: - id: 5.1 text: "RBAC and Service Accounts" checks: - id: 5.1.1 text: "Ensure that the cluster-admin role is only used where required (Manual)" type: "manual" remediation: | Identify all clusterrolebindings to the cluster-admin role. Check if they are used and if they need this role or if they could use a role with fewer privileges. Where possible, first bind users to a lower privileged role and then remove the clusterrolebinding to the cluster-admin role : kubectl delete clusterrolebinding [name] scored: false - id: 5.1.2 text: "Minimize access to secrets (Manual)" type: "manual" remediation: | Where possible, remove get, list and watch access to secret objects in the cluster. scored: false - id: 5.1.3 text: "Minimize wildcard use in Roles and ClusterRoles (Manual)" type: "manual" remediation: | Where possible replace any use of wildcards in clusterroles and roles with specific objects or actions. scored: false - id: 5.1.4 text: "Minimize access to create pods (Manual)" type: "manual" remediation: | Where possible, remove create access to pod objects in the cluster. scored: false - id: 5.1.5 text: "Ensure that default service accounts are not actively used. (Manual)" type: "manual" remediation: | Create explicit service accounts wherever a Kubernetes workload requires specific access to the Kubernetes API server. Modify the configuration of each default service account to include this value automountServiceAccountToken: false scored: false - id: 5.1.6 text: "Ensure that Service Account Tokens are only mounted where necessary (Manual)" type: "manual" remediation: | Modify the definition of pods and service accounts which do not need to mount service account tokens to disable it. scored: false - id: 5.2 text: "Pod Security Policies" checks: - id: 5.2.1 text: "Minimize the admission of privileged containers (Manual)" type: "manual" remediation: | Create a PSP as described in the Kubernetes documentation, ensuring that the .spec.privileged field is omitted or set to false. scored: false - id: 5.2.2 text: "Minimize the admission of containers wishing to share the host process ID namespace (Manual)" type: "manual" remediation: | Create a PSP as described in the Kubernetes documentation, ensuring that the .spec.hostPID field is omitted or set to false. scored: false - id: 5.2.3 text: "Minimize the admission of containers wishing to share the host IPC namespace (Manual)" type: "manual" remediation: | Create a PSP as described in the Kubernetes documentation, ensuring that the .spec.hostIPC field is omitted or set to false. scored: false - id: 5.2.4 text: "Minimize the admission of containers wishing to share the host network namespace (Manual)" type: "manual" remediation: | Create a PSP as described in the Kubernetes documentation, ensuring that the .spec.hostNetwork field is omitted or set to false. scored: false - id: 5.2.5 text: "Minimize the admission of containers with allowPrivilegeEscalation (Manual)" type: "manual" remediation: | Create a PSP as described in the Kubernetes documentation, ensuring that the .spec.allowPrivilegeEscalation field is omitted or set to false. scored: false - id: 5.2.6 text: "Minimize the admission of root containers (Manual)" type: "manual" remediation: | Create a PSP as described in the Kubernetes documentation, ensuring that the .spec.runAsUser.rule is set to either MustRunAsNonRoot or MustRunAs with the range of UIDs not including 0. scored: false - id: 5.2.7 text: "Minimize the admission of containers with the NET_RAW capability (Manual)" type: "manual" remediation: | Create a PSP as described in the Kubernetes documentation, ensuring that the .spec.requiredDropCapabilities is set to include either NET_RAW or ALL. scored: false - id: 5.2.8 text: "Minimize the admission of containers with added capabilities (Manual)" type: "manual" remediation: | Ensure that allowedCapabilities is not present in PSPs for the cluster unless it is set to an empty array. scored: false - id: 5.2.9 text: "Minimize the admission of containers with capabilities assigned (Manual)" type: "manual" remediation: | Review the use of capabilites in applications runnning on your cluster. Where a namespace contains applications which do not require any Linux capabities to operate consider adding a PSP which forbids the admission of containers which do not drop all capabilities. scored: false - id: 5.3 text: "Network Policies and CNI" checks: - id: 5.3.1 text: "Ensure that the CNI in use supports Network Policies (Manual)" type: "manual" remediation: | If the CNI plugin in use does not support network policies, consideration should be given to making use of a different plugin, or finding an alternate mechanism for restricting traffic in the Kubernetes cluster. scored: false - id: 5.3.2 text: "Ensure that all Namespaces have Network Policies defined (Manual)" type: "manual" remediation: | Follow the documentation and create NetworkPolicy objects as you need them. scored: false - id: 5.4 text: "Secrets Management" checks: - id: 5.4.1 text: "Prefer using secrets as files over secrets as environment variables (Manual)" type: "manual" remediation: | if possible, rewrite application code to read secrets from mounted secret files, rather than from environment variables. scored: false - id: 5.4.2 text: "Consider external secret storage (Manual)" type: "manual" remediation: | Refer to the secrets management options offered by your cloud provider or a third-party secrets management solution. scored: false - id: 5.5 text: "Extensible Admission Control" checks: - id: 5.5.1 text: "Configure Image Provenance using ImagePolicyWebhook admission controller (Manual)" type: "manual" remediation: | Follow the Kubernetes documentation and setup image provenance. scored: false - id: 5.6 text: "General Policies" checks: - id: 5.6.1 text: "Create administrative boundaries between resources using namespaces (Manual)" type: "manual" remediation: | Follow the documentation and create namespaces for objects in your deployment as you need them. scored: false - id: 5.6.2 text: "Ensure that the seccomp profile is set to docker/default in your pod definitions (Manual)" type: "manual" remediation: | Seccomp is an alpha feature currently. By default, all alpha features are disabled. So, you would need to enable alpha features in the apiserver by passing "--feature- gates=AllAlpha=true" argument. Edit the /etc/kubernetes/apiserver file on the master node and set the KUBE_API_ARGS parameter to "--feature-gates=AllAlpha=true" KUBE_API_ARGS="--feature-gates=AllAlpha=true" Based on your system, restart the kube-apiserver service. For example: systemctl restart kube-apiserver.service Use annotations to enable the docker/default seccomp profile in your pod definitions. An example is as below: apiVersion: v1 kind: Pod metadata: name: trustworthy-pod annotations: seccomp.security.alpha.kubernetes.io/pod: docker/default spec: containers: - name: trustworthy-container image: sotrustworthy:latest scored: false - id: 5.6.3 text: "Apply Security Context to Your Pods and Containers (Manual)" type: "manual" remediation: | Follow the Kubernetes documentation and apply security contexts to your pods. For a suggested list of security contexts, you may refer to the CIS Security Benchmark for Docker Containers. scored: false - id: 5.6.4 text: "The default namespace should not be used (Manual)" type: "manual" remediation: | Ensure that namespaces are created to allow for appropriate segregation of Kubernetes resources and that all new resources are created in a specific namespace. scored: false ================================================ FILE: cfg/aks-1.0/config.yaml ================================================ --- ## Version-specific settings that override the values in cfg/config.yaml ================================================ FILE: cfg/aks-1.0/controlplane.yaml ================================================ --- controls: version: "aks-1.0" id: 2 text: "Control Plane Configuration" type: "controlplane" groups: - id: 2.1 text: "Logging" checks: - id: 2.1.1 text: "Enable audit Logs" type: "manual" remediation: | Azure audit logs are enabled and managed in the Azure portal. To enable log collection for the Kubernetes master components in your AKS cluster, open the Azure portal in a web browser and complete the following steps: 1. Select the resource group for your AKS cluster, such as myResourceGroup. Don't select the resource group that contains your individual AKS cluster resources, such as MC_myResourceGroup_myAKSCluster_eastus. 2. On the left-hand side, choose Diagnostic settings. 3. Select your AKS cluster, such as myAKSCluster, then choose to Add diagnostic setting. 4. Enter a name, such as myAKSClusterLogs, then select the option to Send to Log Analytics. 5. Select an existing workspace or create a new one. If you create a workspace, provide a workspace name, a resource group, and a location. 6. In the list of available logs, select the logs you wish to enable. For this example, enable the kube-audit and kube-audit-admin logs. Common logs include the kube- apiserver, kube-controller-manager, and kube-scheduler. You can return and change the collected logs once Log Analytics workspaces are enabled. 7. When ready, select Save to enable collection of the selected logs. scored: false ================================================ FILE: cfg/aks-1.0/managedservices.yaml ================================================ --- controls: version: "aks-1.0" id: 5 text: "Managed Services" type: "managedservices" groups: - id: 5.1 text: "Image Registry and Image Scanning" checks: - id: 5.1.1 text: "Ensure Image Vulnerability Scanning using Azure Defender image scanning or a third party provider (Manual)" type: "manual" remediation: "No remediation" scored: false - id: 5.1.2 text: "Minimize user access to Azure Container Registry (ACR) (Manual)" type: "manual" remediation: | Azure Container Registry If you use Azure Container Registry (ACR) as your container image store, you need to grant permissions to the service principal for your AKS cluster to read and pull images. Currently, the recommended configuration is to use the az aks create or az aks update command to integrate with a registry and assign the appropriate role for the service principal. For detailed steps, see Authenticate with Azure Container Registry from Azure Kubernetes Service. To avoid needing an Owner or Azure account administrator role, you can configure a service principal manually or use an existing service principal to authenticate ACR from AKS. For more information, see ACR authentication with service principals or Authenticate from Kubernetes with a pull secret. scored: false - id: 5.1.3 text: "Minimize cluster access to read-only for Azure Container Registry (ACR) (Manual)" type: "manual" remediation: "No remediation" scored: false - id: 5.1.4 text: "Minimize Container Registries to only those approved (Manual)" type: "manual" remediation: "No remediation" scored: false - id: 5.2 text: "Access and identity options for Azure Kubernetes Service (AKS)" checks: - id: 5.2.1 text: "Prefer using dedicated AKS Service Accounts (Manual)" type: "manual" remediation: | Azure Active Directory integration The security of AKS clusters can be enhanced with the integration of Azure Active Directory (AD). Built on decades of enterprise identity management, Azure AD is a multi-tenant, cloud-based directory, and identity management service that combines core directory services, application access management, and identity protection. With Azure AD, you can integrate on-premises identities into AKS clusters to provide a single source for account management and security. Azure Active Directory integration with AKS clusters With Azure AD-integrated AKS clusters, you can grant users or groups access to Kubernetes resources within a namespace or across the cluster. To obtain a kubectl configuration context, a user can run the az aks get-credentials command. When a user then interacts with the AKS cluster with kubectl, they're prompted to sign in with their Azure AD credentials. This approach provides a single source for user account management and password credentials. The user can only access the resources as defined by the cluster administrator. Azure AD authentication is provided to AKS clusters with OpenID Connect. OpenID Connect is an identity layer built on top of the OAuth 2.0 protocol. For more information on OpenID Connect, see the Open ID connect documentation. From inside of the Kubernetes cluster, Webhook Token Authentication is used to verify authentication tokens. Webhook token authentication is configured and managed as part of the AKS cluster. scored: false - id: 5.3 text: "Key Management Service (KMS)" checks: - id: 5.3.1 text: "Ensure Kubernetes Secrets are encrypted (Manual)" type: "manual" remediation: "No remediation" scored: false - id: 5.4 text: "Cluster Networking" checks: - id: 5.4.1 text: "Restrict Access to the Control Plane Endpoint (Manual)" type: "manual" remediation: "No remediation" scored: false - id: 5.4.2 text: "Ensure clusters are created with Private Endpoint Enabled and Public Access Disabled (Manual)" type: "manual" remediation: "No remediation" scored: false - id: 5.4.3 text: "Ensure clusters are created with Private Nodes (Manual)" type: "manual" remediation: "No remediation" scored: false - id: 5.4.4 text: "Ensure Network Policy is Enabled and set as appropriate (Manual)" type: "manual" remediation: "No remediation" scored: false - id: 5.4.5 text: "Encrypt traffic to HTTPS load balancers with TLS certificates (Manual)" type: "manual" remediation: "No remediation" scored: false - id: 5.5 text: "Authentication and Authorization" checks: - id: 5.5.1 text: "Manage Kubernetes RBAC users with Azure AD (Manual)" type: "manual" remediation: "No remediation" scored: false - id: 5.5.2 text: "Use Azure RBAC for Kubernetes Authorization (Manual)" type: "manual" remediation: "No remediation" scored: false - id: 5.6 text: "Other Cluster Configurations" checks: - id: 5.6.1 text: "Restrict untrusted workloads (Manual)" type: "manual" remediation: "No remediation" scored: false - id: 5.6.2 text: "Hostile multi-tenant workloads (Manual)" type: "manual" remediation: "No remediation" scored: false ================================================ FILE: cfg/aks-1.0/master.yaml ================================================ --- controls: version: "aks-1.0" id: 1 text: "Control Plane Components" type: "master" ================================================ FILE: cfg/aks-1.0/node.yaml ================================================ --- controls: version: "aks-1.0" id: 3 text: "Worker Node Security Configuration" type: "node" groups: - id: 3.1 text: "Worker Node Configuration Files" checks: - id: 3.1.1 text: "Ensure that the kubeconfig file permissions are set to 644 or more restrictive (Manual)" audit: '/bin/sh -c ''if test -e $kubeletkubeconfig; then stat -c permissions=%a $kubeletkubeconfig; fi'' ' tests: test_items: - flag: "permissions" compare: op: bitmask value: "644" remediation: | Run the below command (based on the file location on your system) on the each worker node. For example, chmod 644 $kubeletkubeconfig scored: false - id: 3.1.2 text: "Ensure that the kubelet kubeconfig file ownership is set to root:root (Manual)" audit: '/bin/sh -c ''if test -e $kubeletkubeconfig; then stat -c %U:%G $kubeletkubeconfig; fi'' ' tests: test_items: - flag: root:root remediation: | Run the below command (based on the file location on your system) on the each worker node. For example, chown root:root $kubeletkubeconfig scored: false - id: 3.1.3 text: "Ensure that the kubelet configuration file has permissions set to 644 or more restrictive (Manual)" audit: '/bin/sh -c ''if test -e $kubeletconf; then stat -c permissions=%a $kubeletconf; fi'' ' tests: test_items: - flag: "permissions" compare: op: bitmask value: "644" remediation: | Run the following command (using the config file location identified in the Audit step) chmod 644 $kubeletconf scored: false - id: 3.1.4 text: "Ensure that the kubelet configuration file ownership is set to root:root (Manual)" audit: '/bin/sh -c ''if test -e $kubeletconf; then stat -c %U:%G $kubeletconf; fi'' ' tests: test_items: - flag: root:root remediation: | Run the following command (using the config file location identified in the Audit step) chown root:root $kubeletconf scored: false - id: 3.2 text: "Kubelet" checks: - id: 3.2.1 text: "Ensure that the --anonymous-auth argument is set to false (Manual)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: "--anonymous-auth" path: '{.authentication.anonymous.enabled}' compare: op: eq value: false remediation: | If using a Kubelet config file, edit the file to set authentication: anonymous: enabled to false. If using executable arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_SYSTEM_PODS_ARGS variable. --anonymous-auth=false Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: false - id: 3.2.2 text: "Ensure that the --authorization-mode argument is not set to AlwaysAllow (Manual)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --authorization-mode path: '{.authorization.mode}' compare: op: nothave value: AlwaysAllow remediation: | If using a Kubelet config file, edit the file to set authorization: mode to Webhook. If using executable arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_AUTHZ_ARGS variable. --authorization-mode=Webhook Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: false - id: 3.2.3 text: "Ensure that the --client-ca-file argument is set as appropriate (Manual)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --client-ca-file path: '{.authentication.x509.clientCAFile}' set: true remediation: | If using a Kubelet config file, edit the file to set authentication: x509: clientCAFile to the location of the client CA file. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_AUTHZ_ARGS variable. --client-ca-file= Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: false - id: 3.2.4 text: "Ensure that the --read-only-port argument is set to 0 (Manual)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: "--read-only-port" path: '{.readOnlyPort}' set: true compare: op: eq value: 0 remediation: | If using a Kubelet config file, edit the file to set readOnlyPort to 0. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_SYSTEM_PODS_ARGS variable. --read-only-port=0 Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: false - id: 3.2.5 text: "Ensure that the --streaming-connection-idle-timeout argument is not set to 0 (Manual)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --streaming-connection-idle-timeout path: '{.streamingConnectionIdleTimeout}' set: true compare: op: noteq value: 0 - flag: --streaming-connection-idle-timeout path: '{.streamingConnectionIdleTimeout}' set: false bin_op: or remediation: | If using a Kubelet config file, edit the file to set streamingConnectionIdleTimeout to a value other than 0. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_SYSTEM_PODS_ARGS variable. --streaming-connection-idle-timeout=5m Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: false - id: 3.2.6 text: "Ensure that the --protect-kernel-defaults argument is set to true (Manual)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --protect-kernel-defaults path: '{.protectKernelDefaults}' set: true compare: op: eq value: true remediation: | If using a Kubelet config file, edit the file to set protectKernelDefaults: true. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_SYSTEM_PODS_ARGS variable. --protect-kernel-defaults=true Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: false - id: 3.2.7 text: "Ensure that the --make-iptables-util-chains argument is set to true (Manual) " audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --make-iptables-util-chains path: '{.makeIPTablesUtilChains}' set: true compare: op: eq value: true - flag: --make-iptables-util-chains path: '{.makeIPTablesUtilChains}' set: false bin_op: or remediation: | If using a Kubelet config file, edit the file to set makeIPTablesUtilChains: true. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and remove the --make-iptables-util-chains argument from the KUBELET_SYSTEM_PODS_ARGS variable. Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: false - id: 3.2.8 text: "Ensure that the --hostname-override argument is not set (Manual)" # This is one of those properties that can only be set as a command line argument. # To check if the property is set as expected, we need to parse the kubelet command # instead reading the Kubelet Configuration file. audit: "/bin/ps -fC $kubeletbin " tests: test_items: - flag: --hostname-override set: false remediation: | Edit the kubelet service file $kubeletsvc on each worker node and remove the --hostname-override argument from the KUBELET_SYSTEM_PODS_ARGS variable. Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: false - id: 3.2.9 text: "Ensure that the --event-qps argument is set to 0 or a level which ensures appropriate event capture (Manual)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --event-qps path: '{.eventRecordQPS}' set: true compare: op: eq value: 0 remediation: | If using a Kubelet config file, edit the file to set eventRecordQPS: to an appropriate level. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_SYSTEM_PODS_ARGS variable. Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: false - id: 3.2.10 text: "Ensure that the --rotate-certificates argument is not set to false (Manual)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --rotate-certificates path: '{.rotateCertificates}' set: true compare: op: eq value: true - flag: --rotate-certificates path: '{.rotateCertificates}' set: false bin_op: or remediation: | If using a Kubelet config file, edit the file to add the line rotateCertificates: true or remove it altogether to use the default value. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and remove --rotate-certificates=false argument from the KUBELET_CERTIFICATE_ARGS variable. Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: false - id: 3.2.11 text: "Ensure that the RotateKubeletServerCertificate argument is set to true (Manual)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: RotateKubeletServerCertificate path: '{.featureGates.RotateKubeletServerCertificate}' set: true compare: op: eq value: true remediation: | Edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_CERTIFICATE_ARGS variable. --feature-gates=RotateKubeletServerCertificate=true Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: false ================================================ FILE: cfg/aks-1.0/policies.yaml ================================================ --- controls: version: "aks-1.0" id: 4 text: "Policies" type: "policies" groups: - id: 4.1 text: "RBAC and Service Accounts" checks: - id: 4.1.1 text: "Ensure that the cluster-admin role is only used where required (Manual)" type: "manual" remediation: | Identify all clusterrolebindings to the cluster-admin role. Check if they are used and if they need this role or if they could use a role with fewer privileges. Where possible, first bind users to a lower privileged role and then remove the clusterrolebinding to the cluster-admin role : kubectl delete clusterrolebinding [name] scored: false - id: 4.1.2 text: "Minimize access to secrets (Manual)" type: "manual" remediation: | Where possible, remove get, list and watch access to secret objects in the cluster. scored: false - id: 4.1.3 text: "Minimize wildcard use in Roles and ClusterRoles (Manual)" type: "manual" remediation: | Where possible replace any use of wildcards in clusterroles and roles with specific objects or actions. scored: false - id: 4.1.4 text: "Minimize access to create pods (Manual)" type: "manual" remediation: | Where possible, remove create access to pod objects in the cluster. scored: false - id: 4.1.5 text: "Ensure that default service accounts are not actively used. (Manual)" type: "manual" remediation: | Create explicit service accounts wherever a Kubernetes workload requires specific access to the Kubernetes API server. Modify the configuration of each default service account to include this value automountServiceAccountToken: false scored: false - id: 4.1.6 text: "Ensure that Service Account Tokens are only mounted where necessary (Manual)" type: "manual" remediation: | Modify the definition of pods and service accounts which do not need to mount service account tokens to disable it. scored: false - id: 4.2 text: "Pod Security Policies" checks: - id: 4.2.1 text: "Minimize the admission of privileged containers (Automated)" type: "manual" remediation: | Create a PSP as described in the Kubernetes documentation, ensuring that the .spec.privileged field is omitted or set to false. scored: false - id: 4.2.2 text: "Minimize the admission of containers wishing to share the host process ID namespace (Automated)" type: "manual" remediation: | Create a PSP as described in the Kubernetes documentation, ensuring that the .spec.hostPID field is omitted or set to false. scored: false - id: 4.2.3 text: "Minimize the admission of containers wishing to share the host IPC namespace (Automated)" type: "manual" remediation: | Create a PSP as described in the Kubernetes documentation, ensuring that the .spec.hostIPC field is omitted or set to false. scored: false - id: 4.2.4 text: "Minimize the admission of containers wishing to share the host network namespace (Automated)" type: "manual" remediation: | Create a PSP as described in the Kubernetes documentation, ensuring that the .spec.hostNetwork field is omitted or set to false. scored: false - id: 4.2.5 text: "Minimize the admission of containers with allowPrivilegeEscalation (Automated)" type: "manual" remediation: | Create a PSP as described in the Kubernetes documentation, ensuring that the .spec.allowPrivilegeEscalation field is omitted or set to false. scored: false - id: 4.2.6 text: "Minimize the admission of root containers (Automated)" type: "manual" remediation: | Create a PSP as described in the Kubernetes documentation, ensuring that the .spec.runAsUser.rule is set to either MustRunAsNonRoot or MustRunAs with the range of UIDs not including 0. scored: false - id: 4.2.7 text: "Minimize the admission of containers with the NET_RAW capability (Automated)" type: "manual" remediation: | Create a PSP as described in the Kubernetes documentation, ensuring that the .spec.requiredDropCapabilities is set to include either NET_RAW or ALL. scored: false - id: 4.2.8 text: "Minimize the admission of containers with added capabilities (Automated)" type: "manual" remediation: | Ensure that allowedCapabilities is not present in PSPs for the cluster unless it is set to an empty array. scored: false - id: 4.2.9 text: "Minimize the admission of containers with capabilities assigned (Manual)" type: "manual" remediation: | Review the use of capabilities in applications running on your cluster. Where a namespace contains applications which do not require any Linux capabities to operate consider adding a PSP which forbids the admission of containers which do not drop all capabilities. scored: false - id: 4.3 text: "Azure Policy / OPA" checks: [] - id: 4.4 text: "CNI Plugin" checks: - id: 4.4.1 text: "Ensure that the latest CNI version is used (Manual)" type: "manual" remediation: | Review the documentation of AWS CNI plugin, and ensure latest CNI version is used. scored: false - id: 4.4.2 text: "Ensure that all Namespaces have Network Policies defined (Manual)" type: "manual" remediation: | Follow the documentation and create NetworkPolicy objects as you need them. scored: false - id: 4.5 text: "Secrets Management" checks: - id: 4.5.1 text: "Prefer using secrets as files over secrets as environment variables (Manual)" type: "manual" remediation: | If possible, rewrite application code to read secrets from mounted secret files, rather than from environment variables. scored: false - id: 4.5.2 text: "Consider external secret storage (Manual)" type: "manual" remediation: | Refer to the secrets management options offered by your cloud provider or a third-party secrets management solution. scored: false - id: 4.6 text: "Extensible Admission Control" checks: - id: 4.6.1 text: "Verify that admission controllers are working as expected (Manual)" type: "manual" remediation: "No remediation" scored: false - id: 4.7 text: "General Policies" checks: - id: 4.7.1 text: "Create administrative boundaries between resources using namespaces (Manual)" type: "manual" remediation: | Follow the documentation and create namespaces for objects in your deployment as you need them. scored: false - id: 4.7.2 text: "Apply Security Context to Your Pods and Containers (Manual)" type: "manual" remediation: | Follow the Kubernetes documentation and apply security contexts to your pods. For a suggested list of security contexts, you may refer to the CIS Security Benchmark for Docker Containers. scored: false - id: 4.7.3 text: "The default namespace should not be used (Manual)" type: "manual" remediation: | Ensure that namespaces are created to allow for appropriate segregation of Kubernetes resources and that all new resources are created in a specific namespace. scored: false ================================================ FILE: cfg/aks-1.7/config.yaml ================================================ --- ## Version-specific settings that override the values in cfg/config.yaml ================================================ FILE: cfg/aks-1.7/controlplane.yaml ================================================ --- controls: version: "aks-1.7" id: 2 text: "Control Plane Configuration" type: "controlplane" groups: - id: 2.1 text: "Logging" checks: - id: 2.1.1 text: "Enable audit Logs" type: "manual" remediation: | Azure audit logs are enabled and managed in the Azure portal. To enable log collection for the Kubernetes master components in your AKS cluster, open the Azure portal in a web browser and complete the following steps: 1. Select the resource group for your AKS cluster, such as myResourceGroup. Don't select the resource group that contains your individual AKS cluster resources, such as MC_myResourceGroup_myAKSCluster_eastus. 2. On the left-hand side, choose Diagnostic settings. 3. Select your AKS cluster, such as myAKSCluster, then choose to Add diagnostic setting. 4. Enter a name, such as myAKSClusterLogs, then select the option to Send to Log Analytics. 5. Select an existing workspace or create a new one. If you create a workspace, provide a workspace name, a resource group, and a location. 6. In the list of available logs, select the logs you wish to enable. For this example, enable the kube-audit and kube-audit-admin logs. Common logs include the kube- apiserver, kube-controller-manager, and kube-scheduler. You can return and change the collected logs once Log Analytics workspaces are enabled. 7. When ready, select Save to enable collection of the selected logs. scored: false ================================================ FILE: cfg/aks-1.7/managedservices.yaml ================================================ --- controls: version: "aks-1.7" id: 5 text: "Managed Services" type: "managedservices" groups: - id: 5.1 text: "Image Registry and Image Scanning" checks: - id: 5.1.1 text: "Ensure Image Vulnerability Scanning using Microsoft Defender for Cloud (MDC) image scanning or a third party provider (Manual)" type: "manual" remediation: | Enable MDC for Container Registries by running the following Azure CLI command: az security pricing create --name ContainerRegistry --tier Standard Alternatively, use the following command to enable image scanning for your container registry: az resource update --ids /subscriptions/{subscription-id}/resourceGroups/{resource-group-name}/providers/Microsoft.ContainerRegistry/registries/{registry-name} --set properties.enabled=true Replace `subscription-id`, `resource-group-name`, and `registry-name` with the correct values for your environment. Please note that enabling MDC for Container Registries will incur additional costs, so be sure to review the pricing information provided in the Azure documentation before enabling it. scored: false - id: 5.1.2 text: "Minimize user access to Azure Container Registry (ACR) (Manual)" type: "manual" remediation: | Azure Container Registry If you use Azure Container Registry (ACR) as your container image store, you need to grant permissions to the service principal for your AKS cluster to read and pull images. Currently, the recommended configuration is to use the az aks create or az aks update command to integrate with a registry and assign the appropriate role for the service principal. For detailed steps, see Authenticate with Azure Container Registry from Azure Kubernetes Service. To avoid needing an Owner or Azure account administrator role, you can configure a service principal manually or use an existing service principal to authenticate ACR from AKS. For more information, see ACR authentication with service principals or Authenticate from Kubernetes with a pull secret. scored: false - id: 5.1.3 text: "Minimize cluster access to read-only for Azure Container Registry (ACR) (Manual)" type: "manual" remediation: "No remediation" scored: false - id: 5.1.4 text: "Minimize Container Registries to only those approved (Manual)" type: "manual" remediation: | If you are using **Azure Container Registry**, you can restrict access using firewall rules as described in the official documentation: https://docs.microsoft.com/en-us/azure/container-registry/container-registry-firewall-access-rules For other non-AKS repositories, you can use **admission controllers** or **Azure Policy** to enforce registry access restrictions. Limiting or locking down egress traffic to specific container registries is also recommended. For more information, refer to: https://docs.microsoft.com/en-us/azure/aks/limit-egress-traffic scored: false - id: 5.2 text: "Access and identity options for Azure Kubernetes Service (AKS)" checks: - id: 5.2.1 text: "Prefer using dedicated AKS Service Accounts (Manual)" type: "manual" remediation: | Azure Active Directory integration The security of AKS clusters can be enhanced with the integration of Azure Active Directory (AD). Built on decades of enterprise identity management, Azure AD is a multi-tenant, cloud-based directory, and identity management service that combines core directory services, application access management, and identity protection. With Azure AD, you can integrate on-premises identities into AKS clusters to provide a single source for account management and security. Azure Active Directory integration with AKS clusters With Azure AD-integrated AKS clusters, you can grant users or groups access to Kubernetes resources within a namespace or across the cluster. To obtain a kubectl configuration context, a user can run the az aks get-credentials command. When a user then interacts with the AKS cluster with kubectl, they're prompted to sign in with their Azure AD credentials. This approach provides a single source for user account management and password credentials. The user can only access the resources as defined by the cluster administrator. Azure AD authentication is provided to AKS clusters with OpenID Connect. OpenID Connect is an identity layer built on top of the OAuth 2.0 protocol. For more information on OpenID Connect, see the Open ID connect documentation. From inside of the Kubernetes cluster, Webhook Token Authentication is used to verify authentication tokens. Webhook token authentication is configured and managed as part of the AKS cluster. scored: false - id: 5.3 text: "Key Management Service (KMS)" checks: - id: 5.3.1 text: "Ensure Kubernetes Secrets are encrypted (Manual)" type: "manual" remediation: "No remediation" scored: false - id: 5.4 text: "Cluster Networking" checks: - id: 5.4.1 text: "Restrict Access to the Control Plane Endpoint (Manual)" type: "manual" remediation: | By enabling private endpoint access to the Kubernetes API server, all communication between your nodes and the API server stays within your VPC. You can also limit the IP addresses that can access your API server from the internet, or completely disable internet access to the API server. With this in mind, you can update your cluster accordingly using the AKS CLI to ensure that Private Endpoint Access is enabled. If you choose to also enable Public Endpoint Access then you should also configure a list of allowable CIDR blocks, resulting in restricted access from the internet. If you specify no CIDR blocks, then the public API server endpoint is able to receive and process requests from all IP addresses by defaulting to ['0.0.0.0/0']. Example: az aks update --name ${CLUSTER_NAME} --resource-group ${RESOURCE_GROUP} --api-server-access-profile enablePrivateCluster=true --api-server-access-profile authorizedIpRanges=192.168.1.0/24 scored: false - id: 5.4.2 text: "Ensure clusters are created with Private Endpoint Enabled and Public Access Disabled (Manual)" type: "manual" remediation: | To use a private endpoint, create a new private endpoint in your virtual network, then create a link between your virtual network and a new private DNS zone. You can also restrict access to the public endpoint by enabling only specific CIDR blocks to access it. For example: az aks update --name ${CLUSTER_NAME} --resource-group ${RESOURCE_GROUP} --api-server-access-profile enablePublicFqdn=false This command disables the public API endpoint for your AKS cluster. scored: false - id: 5.4.3 text: "Ensure clusters are created with Private Nodes (Manual)" type: "manual" remediation: | To create a private cluster, use the following command: az aks create \ --resource-group \ --name \ --load-balancer-sku standard \ --enable-private-cluster \ --network-plugin azure \ --vnet-subnet-id \ --docker-bridge-address \ --dns-service-ip \ --service-cidr Ensure that --enable-private-cluster flag is set to enable private nodes in your cluster. scored: false - id: 5.4.4 text: "Ensure Network Policy is Enabled and set as appropriate (Manual)" type: "manual" remediation: | Utilize Calico or another network policy engine to segment and isolate your traffic. Enable network policies on your AKS cluster by following the Azure documentation or using the `az aks` CLI to enable the network policy add-on. scored: false - id: 5.4.5 text: "Encrypt traffic to HTTPS load balancers with TLS certificates (Manual)" type: "manual" remediation: "No remediation" scored: false - id: 5.5 text: "Authentication and Authorization" checks: - id: 5.5.1 text: "Manage Kubernetes RBAC users with Azure AD (Manual)" type: "manual" remediation: "No remediation" scored: false - id: 5.5.2 text: "Use Azure RBAC for Kubernetes Authorization (Manual)" type: "manual" remediation: "No remediation" scored: false ================================================ FILE: cfg/aks-1.7/master.yaml ================================================ --- controls: version: "aks-1.7" id: 1 text: "Control Plane Components" type: "master" ================================================ FILE: cfg/aks-1.7/node.yaml ================================================ --- controls: version: "aks-1.7" id: 3 text: "Worker Node Security Configuration" type: "node" groups: - id: 3.1 text: "Worker Node Configuration Files" checks: - id: 3.1.1 text: "Ensure that the kubeconfig file permissions are set to 644 or more restrictive (Automated)" audit: '/bin/sh -c ''if test -e $kubeletkubeconfig; then stat -c permissions=%a $kubeletkubeconfig; fi'' ' tests: test_items: - flag: "permissions" compare: op: bitmask value: "644" remediation: | Run the below command (based on the file location on your system) on the each worker node. For example, chmod 644 $kubeletkubeconfig scored: true - id: 3.1.2 text: "Ensure that the kubelet kubeconfig file ownership is set to root:root (Automated)" audit: '/bin/sh -c ''if test -e $kubeletkubeconfig; then stat -c %U:%G $kubeletkubeconfig; fi'' ' tests: test_items: - flag: root:root remediation: | Run the below command (based on the file location on your system) on the each worker node. For example, chown root:root $kubeletkubeconfig scored: true - id: 3.1.3 text: "Ensure that the azure.json file has permissions set to 644 or more restrictive (Automated)" audit: '/bin/sh -c ''if test -e /etc/kubernetes/azure.json; then stat -c permissions=%a /etc/kubernetes/azure.json; fi'' ' tests: test_items: - flag: "permissions" compare: op: bitmask value: "644" remediation: | Run the following command (using the config file location identified in the Audit step) chmod 644 $kubeletconf scored: true - id: 3.1.4 text: "Ensure that the azure.json file ownership is set to root:root (Automated)" audit: '/bin/sh -c ''if test -e $kubeletconf; then stat -c %U:%G $kubeletconf; fi'' ' tests: test_items: - flag: root:root remediation: | Run the following command (using the config file location identified in the Audit step) chown root:root $kubeletconf scored: true - id: 3.2 text: "Kubelet" checks: - id: 3.2.1 text: "Ensure that the --anonymous-auth argument is set to false (Automated)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: "--anonymous-auth" path: '{.authentication.anonymous.enabled}' compare: op: eq value: false remediation: | If using a Kubelet config file, edit the file to set authentication: anonymous: enabled to false. If using executable arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_SYSTEM_PODS_ARGS variable. --anonymous-auth=false Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: true - id: 3.2.2 text: "Ensure that the --authorization-mode argument is not set to AlwaysAllow (Automated)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --authorization-mode path: '{.authorization.mode}' compare: op: nothave value: AlwaysAllow remediation: | If using a Kubelet config file, edit the file to set authorization: mode to Webhook. If using executable arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_AUTHZ_ARGS variable. --authorization-mode=Webhook Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: true - id: 3.2.3 text: "Ensure that the --client-ca-file argument is set as appropriate (Automated)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --client-ca-file path: '{.authentication.x509.clientCAFile}' set: true remediation: | If using a Kubelet config file, edit the file to set authentication: x509: clientCAFile to the location of the client CA file. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_AUTHZ_ARGS variable. --client-ca-file= Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: true - id: 3.2.4 text: "Ensure that the --read-only-port is secured (Automated)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: "--read-only-port" path: '{.readOnlyPort}' set: true compare: op: eq value: 0 remediation: | If using a Kubelet config file, edit the file to set readOnlyPort to 0. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_SYSTEM_PODS_ARGS variable. --read-only-port=0 Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: true - id: 3.2.5 text: "Ensure that the --streaming-connection-idle-timeout argument is not set to 0 (Automated)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --streaming-connection-idle-timeout path: '{.streamingConnectionIdleTimeout}' set: true compare: op: noteq value: 0 - flag: --streaming-connection-idle-timeout path: '{.streamingConnectionIdleTimeout}' set: false bin_op: or remediation: | If using a Kubelet config file, edit the file to set streamingConnectionIdleTimeout to a value other than 0. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_SYSTEM_PODS_ARGS variable. --streaming-connection-idle-timeout=5m Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: true - id: 3.2.6 text: "Ensure that the --make-iptables-util-chains argument is set to true (Automated) " audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --make-iptables-util-chains path: '{.makeIPTablesUtilChains}' set: true compare: op: eq value: true - flag: --make-iptables-util-chains path: '{.makeIPTablesUtilChains}' set: false bin_op: or remediation: | If using a Kubelet config file, edit the file to set makeIPTablesUtilChains: true. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and remove the --make-iptables-util-chains argument from the KUBELET_SYSTEM_PODS_ARGS variable. Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: true - id: 3.2.7 text: "Ensure that the --eventRecordQPS argument is set to 0 or a level which ensures appropriate event capture (Automated)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --event-qps path: '{.eventRecordQPS}' set: true compare: op: eq value: 0 remediation: | If using a Kubelet config file, edit the file to set the 'eventRecordQPS' value to an appropriate level (e.g., 5). If using executable arguments, check the Kubelet service file `$kubeletsvc` on each worker node, and add the following parameter to the `KUBELET_ARGS` variable: --eventRecordQPS=5 Ensure that there is no conflicting `--eventRecordQPS` setting in the service file that overrides the config file. After making the changes, restart the Kubelet service: systemctl daemon-reload systemctl restart kubelet.service systemctl status kubelet -l scored: true - id: 3.2.8 text: "Ensure that the --rotate-certificates argument is not set to false (Automated)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --rotate-certificates path: '{.rotateCertificates}' set: true compare: op: eq value: true - flag: --rotate-certificates path: '{.rotateCertificates}' set: false bin_op: or remediation: | If modifying the Kubelet config file, edit the `kubelet-config.json` file located at `/etc/kubernetes/kubelet/kubelet-config.json` and set the following parameter to `true`: "rotateCertificates": true Ensure that the Kubelet service file located at `/etc/systemd/system/kubelet.service.d/10-kubelet-args.conf` does not define the `--rotate-certificates` argument as `false`, as this would override the config file. If using executable arguments, add the following line to the `KUBELET_CERTIFICATE_ARGS` variable: --rotate-certificates=true After making the necessary changes, restart the Kubelet service: systemctl daemon-reload systemctl restart kubelet.service systemctl status kubelet -l scored: true - id: 3.2.9 text: "Ensure that the RotateKubeletServerCertificate argument is set to true (Automated)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: RotateKubeletServerCertificate path: '{.featureGates.RotateKubeletServerCertificate}' set: true compare: op: eq value: true remediation: | Edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_CERTIFICATE_ARGS variable. --feature-gates=RotateKubeletServerCertificate=true Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: true ================================================ FILE: cfg/aks-1.7/policies.yaml ================================================ --- controls: version: "aks-1.7" id: 4 text: "Policies" type: "policies" groups: - id: 4.1 text: "RBAC and Service Accounts" checks: - id: 4.1.1 text: "Ensure that the cluster-admin role is only used where required (Automated)" audit: | kubectl get clusterrolebindings -o json | jq -r ' .items[] | select(.roleRef.name == "cluster-admin") | .subjects[]? | select(.kind != "Group" or (.name != "system:masters" and .name != "system:nodes")) | "FOUND_CLUSTER_ADMIN_BINDING" ' || echo "NO_CLUSTER_ADMIN_BINDINGS" tests: test_items: - flag: "NO_CLUSTER_ADMIN_BINDINGS" set: true compare: op: eq value: "NO_CLUSTER_ADMIN_BINDINGS" remediation: | Identify all clusterrolebindings to the cluster-admin role using: kubectl get clusterrolebindings --no-headers | grep cluster-admin Review if each of them actually needs this role. If not, remove the binding: kubectl delete clusterrolebinding Where possible, assign a less privileged ClusterRole. scored: true - id: 4.1.2 text: "Minimize access to secrets (Automated)" audit: | count=$(kubectl get roles --all-namespaces -o json | jq ' .items[] | select(.rules[]? | (.resources[]? == "secrets") and ((.verbs[]? == "get") or (.verbs[]? == "list") or (.verbs[]? == "watch")) )' | wc -l) if [ "$count" -gt 0 ]; then echo "SECRETS_ACCESS_FOUND" fi tests: test_items: - flag: "SECRETS_ACCESS_FOUND" set: false remediation: | Identify all roles that grant access to secrets via get/list/watch verbs. Use `kubectl edit role -n ` to remove these permissions. Alternatively, create a new least-privileged role that excludes secret access. scored: true - id: 4.1.3 text: "Minimize wildcard use in Roles and ClusterRoles (Automated)" audit: | wildcards=$(kubectl get roles --all-namespaces -o json | jq ' .items[] | select( .rules[]? | (.verbs[]? == "*" or .resources[]? == "*" or .apiGroups[]? == "*") )' | wc -l) wildcards_clusterroles=$(kubectl get clusterroles -o json | jq ' .items[] | select( .rules[]? | (.verbs[]? == "*" or .resources[]? == "*" or .apiGroups[]? == "*") )' | wc -l) total=$((wildcards + wildcards_clusterroles)) if [ "$total" -gt 0 ]; then echo "wildcards_present" fi tests: test_items: - flag: wildcards_present set: false remediation: | Identify roles and clusterroles using wildcards (*) in 'verbs', 'resources', or 'apiGroups'. Replace wildcards with specific values to enforce least privilege access. Use `kubectl edit role -n ` or `kubectl edit clusterrole ` to update. scored: true - id: 4.1.4 text: "Minimize access to create pods (Automated)" audit: | echo "🔹 Roles and ClusterRoles with 'create' access on 'pods':" access=$(kubectl get roles,clusterroles -A -o json | jq ' [.items[] | select( .rules[]? | (.resources[]? == "pods" and .verbs[]? == "create") ) ] | length') if [ "$access" -gt 0 ]; then echo "pods_create_access" fi tests: test_items: - flag: pods_create_access set: false remediation: | Review all roles and clusterroles that have "create" permission on "pods". 🔒 Where possible, remove or restrict this permission to only required service accounts. 🛠 Use: - `kubectl edit role -n ` - `kubectl edit clusterrole ` ✅ Apply least privilege principle across the cluster. scored: true - id: 4.1.5 text: "Ensure that default service accounts are not actively used (Automated)" audit: | echo "🔹 Default Service Accounts with automountServiceAccountToken enabled:" default_sa_count=$(kubectl get serviceaccounts --all-namespaces -o json | jq ' [.items[] | select(.metadata.name == "default" and (.automountServiceAccountToken != false))] | length') if [ "$default_sa_count" -gt 0 ]; then echo "default_sa_not_auto_mounted" fi echo "\n🔹 Pods using default ServiceAccount:" pods_using_default_sa=$(kubectl get pods --all-namespaces -o json | jq ' [.items[] | select(.spec.serviceAccountName == "default")] | length') if [ "$pods_using_default_sa" -gt 0 ]; then echo "default_sa_used_in_pods" fi tests: test_items: - flag: default_sa_not_auto_mounted set: false - flag: default_sa_used_in_pods set: false remediation: | 1. Avoid using default service accounts for workloads. 2. Set `automountServiceAccountToken: false` on all default SAs: kubectl patch serviceaccount default -n -p '{"automountServiceAccountToken": false}' 3. Use custom service accounts with only the necessary permissions. scored: true - id: 4.1.6 text: "Ensure that Service Account Tokens are only mounted where necessary (Automated)" audit: | echo "🔹 Pods with automountServiceAccountToken enabled:" pods_with_token_mount=$(kubectl get pods --all-namespaces -o json | jq ' [.items[] | select(.spec.automountServiceAccountToken != false)] | length') if [ "$pods_with_token_mount" -gt 0 ]; then echo "automountServiceAccountToken" fi tests: test_items: - flag: automountServiceAccountToken set: false remediation: | Pods that do not need access to the Kubernetes API should not mount service account tokens. ✅ To disable token mounting in a pod definition: ```yaml spec: automountServiceAccountToken: false ``` ✅ Or patch an existing pod's spec (recommended via workload template): Patch not possible for running pods — update the deployment YAML or recreate pods with updated spec. scored: true - id: 4.2 text: "Pod Security Policies" checks: - id: 4.2.1 text: "Minimize the admission of privileged containers (Automated)" audit: | kubectl get pods --all-namespaces -o json | \ jq -r 'if any(.items[]?.spec.containers[]?; .securityContext?.privileged == true) then "PRIVILEGED_FOUND" else "NO_PRIVILEGED" end' tests: test_items: - flag: "NO_PRIVILEGED" set: true compare: op: eq value: "NO_PRIVILEGED" remediation: | Add a Pod Security Admission (PSA) policy to each namespace in the cluster to restrict the admission of privileged containers. To enforce a restricted policy for a specific namespace, use the following command: kubectl label --overwrite ns NAMESPACE pod-security.kubernetes.io/enforce=restricted You can also enforce PSA for all namespaces: kubectl label --overwrite ns --all pod-security.kubernetes.io/warn=baseline Additionally, review the namespaces that should be excluded (e.g., `kube-system`, `gatekeeper-system`, `azure-arc`, `azure-extensions-usage-system`) and adjust your filtering if necessary. To enable Pod Security Policies, refer to the detailed documentation for Kubernetes and Azure integration at: https://learn.microsoft.com/en-us/azure/governance/policy/concepts/policy-for-kubernetes scored: true - id: 4.2.2 text: "Minimize the admission of containers wishing to share the host process ID namespace (Automated)" audit: | kubectl get pods --all-namespaces -o json | \ jq -r 'if any(.items[]?; .spec.hostPID == true) then "HOSTPID_FOUND" else "NO_HOSTPID" end' tests: test_items: - flag: "NO_HOSTPID" set: true compare: op: eq value: "NO_HOSTPID" remediation: | Add a policy to each namespace in the cluster that restricts the admission of containers with hostPID. For namespaces that need it, ensure RBAC controls limit access to a specific service account. You can label your namespaces as follows to restrict or enforce the policy: kubectl label --overwrite ns NAMESPACE pod-security.kubernetes.io/enforce=restricted You can also use the following to warn about policies: kubectl label --overwrite ns --all pod-security.kubernetes.io/warn=baseline For more information, refer to the official Kubernetes and Azure documentation on policies: https://learn.microsoft.com/en-us/azure/governance/policy/concepts/policy-for-kubernetes scored: true - id: 4.2.3 text: "Minimize the admission of containers wishing to share the host IPC namespace (Automated)" audit: | kubectl get pods --all-namespaces -o json | jq -r 'if any(.items[]?; .spec.hostIPC == true) then "HOSTIPC_FOUND" else "NO_HOSTIPC" end' tests: test_items: - flag: "NO_HOSTIPC" set: true compare: op: eq value: "NO_HOSTIPC" remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of hostIPC containers. You can label your namespaces as follows to restrict or enforce the policy: kubectl label --overwrite ns NAMESPACE pod-security.kubernetes.io/enforce=restricted You can also use the following to warn about policies: kubectl label --overwrite ns --all pod-security.kubernetes.io/warn=baseline For more information, refer to the official Kubernetes and Azure documentation on policies: https://learn.microsoft.com/en-us/azure/governance/policy/concepts/policy-for-kubernetes scored: true - id: 4.2.4 text: "Minimize the admission of containers wishing to share the host network namespace (Automated)" audit: | kubectl get pods --all-namespaces -o json | jq -r 'if any(.items[]?; .spec.hostNetwork == true) then "HOSTNETWORK_FOUND" else "NO_HOSTNETWORK" end' tests: test_items: - flag: "NO_HOSTNETWORK" set: true compare: op: eq value: "NO_HOSTNETWORK" remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of hostNetwork containers. You can label your namespaces as follows to restrict or enforce the policy: kubectl label --overwrite ns NAMESPACE pod-security.kubernetes.io/enforce=restricted You can also use the following to warn about policies: kubectl label --overwrite ns --all pod-security.kubernetes.io/warn=baseline For more information, refer to the official Kubernetes and Azure documentation on policies: https://learn.microsoft.com/en-us/azure/governance/policy/concepts/policy-for-kubernetes scored: true - id: 4.2.5 text: "Minimize the admission of containers with allowPrivilegeEscalation (Automated)" audit: | kubectl get pods --all-namespaces -o json | \ jq -r 'if any(.items[]?.spec.containers[]?; .securityContext?.allowPrivilegeEscalation == true) then "ALLOWPRIVILEGEESCALTION_FOUND" else "NO_ALLOWPRIVILEGEESCALTION" end' tests: test_items: - flag: "NO_ALLOWPRIVILEGEESCALTION" set: true compare: op: eq value: "NO_ALLOWPRIVILEGEESCALTION" remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of containers with .spec.allowPrivilegeEscalation set to true. You can label your namespaces as follows to restrict or enforce the policy: kubectl label --overwrite ns NAMESPACE pod-security.kubernetes.io/enforce=restricted You can also use the following to warn about policies: kubectl label --overwrite ns --all pod-security.kubernetes.io/warn=baseline For more information, refer to the official Kubernetes and Azure documentation on policies: https://learn.microsoft.com/en-us/azure/governance/policy/concepts/policy-for-kubernetes scored: true - id: 4.3 text: "Azure Policy / OPA" checks: [] - id: 4.4 text: "CNI Plugin" checks: - id: 4.4.1 text: "Ensure latest CNI version is used (Manual)" type: "manual" remediation: | Review the documentation of AWS CNI plugin, and ensure latest CNI version is used. scored: false - id: 4.4.2 text: "Ensure that all Namespaces have Network Policies defined (Automated)" audit: | ns_without_np=$(comm -23 \ <(kubectl get ns -o jsonpath='{.items[*].metadata.name}' | tr ' ' '\n' | sort) \ <(kubectl get networkpolicy --all-namespaces -o jsonpath='{.items[*].metadata.namespace}' | tr ' ' '\n' | sort)) if [ -z "$ns_without_np" ]; then echo "ALL_NAMESPACES_HAVE_NETWORKPOLICIES"; else echo "MISSING_NETWORKPOLICIES"; fi tests: test_items: - flag: "ALL_NAMESPACES_HAVE_NETWORKPOLICIES" set: true compare: op: eq value: "ALL_NAMESPACES_HAVE_NETWORKPOLICIES" remediation: | Define at least one NetworkPolicy in each namespace to control pod-level traffic. Example: kubectl apply -n -f - </dev/null | grep -v '^service\s\+kubernetes\s' || true) if [ -z "$output" ]; then echo "DEFAULT_NAMESPACE_UNUSED"; else echo "DEFAULT_NAMESPACE_IN_USE"; fi tests: test_items: - flag: "DEFAULT_NAMESPACE_UNUSED" set: true compare: op: eq value: "DEFAULT_NAMESPACE_UNUSED" remediation: | Avoid using the default namespace for user workloads. - Create separate namespaces for your applications and infrastructure components. - Move any user-defined resources out of the default namespace. Example to create a namespace: kubectl create namespace my-namespace Example to move resources: kubectl get deployment my-app -n default -o yaml | sed 's/namespace: default/namespace: my-namespace/' | kubectl apply -f - kubectl delete deployment my-app -n default scored: true ================================================ FILE: cfg/aks-1.8/config.yaml ================================================ --- ## Version-specific settings that override the values in cfg/config.yaml ================================================ FILE: cfg/aks-1.8/controlplane.yaml ================================================ --- controls: version: "aks-1.8" id: 2 text: "Control Plane Configuration" type: "controlplane" groups: - id: 2.1 text: "Logging" checks: - id: 2.1.1 text: "Enable audit Logs" type: "manual" remediation: | Azure audit logs are enabled and managed in the Azure portal. To enable log collection for the Kubernetes master components in your AKS cluster, open the Azure portal in a web browser and complete the following steps: 1. Select the resource group for your AKS cluster, such as myResourceGroup. Don't select the resource group that contains your individual AKS cluster resources, such as MC_myResourceGroup_myAKSCluster_eastus. 2. On the left-hand side, choose Diagnostic settings. 3. Select your AKS cluster, such as myAKSCluster, then choose to Add diagnostic setting. 4. Enter a name, such as myAKSClusterLogs, then select the option to Send to Log Analytics. 5. Select an existing workspace or create a new one. If you create a workspace, provide a workspace name, a resource group, and a location. 6. In the list of available logs, select the logs you wish to enable. For this example, enable the kube-audit and kube-audit-admin logs. Common logs include the kube- apiserver, kube-controller-manager, and kube-scheduler. You can return and change the collected logs once Log Analytics workspaces are enabled. 7. When ready, select Save to enable collection of the selected logs. scored: false ================================================ FILE: cfg/aks-1.8/managedservices.yaml ================================================ --- controls: version: "aks-1.8" id: 5 text: "Managed Services" type: "managedservices" groups: - id: 5.1 text: "Image Registry and Image Scanning" checks: - id: 5.1.1 text: "Ensure Image Vulnerability Scanning using Microsoft Defender for Cloud (MDC) image scanning or a third party provider (Manual)" type: "manual" remediation: | Enable MDC for Container Registries by running the following Azure CLI command: az security pricing create --name ContainerRegistry --tier Standard Alternatively, use the following command to enable image scanning for your container registry: az resource update --ids /subscriptions/{subscription-id}/resourceGroups/{resource-group-name}/providers/Microsoft.ContainerRegistry/registries/{registry-name} --set properties.enabled=true Replace `subscription-id`, `resource-group-name`, and `registry-name` with the correct values for your environment. Please note that enabling MDC for Container Registries will incur additional costs, so be sure to review the pricing information provided in the Azure documentation before enabling it. scored: false - id: 5.1.2 text: "Minimize user access to Azure Container Registry (ACR) (Manual)" type: "manual" remediation: | Azure Container Registry If you use Azure Container Registry (ACR) as your container image store, you need to grant permissions to the service principal for your AKS cluster to read and pull images. Currently, the recommended configuration is to use the az aks create or az aks update command to integrate with a registry and assign the appropriate role for the service principal. For detailed steps, see Authenticate with Azure Container Registry from Azure Kubernetes Service. To avoid needing an Owner or Azure account administrator role, you can configure a service principal manually or use an existing service principal to authenticate ACR from AKS. For more information, see ACR authentication with service principals or Authenticate from Kubernetes with a pull secret. scored: false - id: 5.1.3 text: "Minimize cluster access to read-only for Azure Container Registry (ACR) (Manual)" type: "manual" remediation: "No remediation" scored: false - id: 5.1.4 text: "Minimize Container Registries to only those approved (Manual)" type: "manual" remediation: | If you are using **Azure Container Registry**, you can restrict access using firewall rules as described in the official documentation: https://docs.microsoft.com/en-us/azure/container-registry/container-registry-firewall-access-rules For other non-AKS repositories, you can use **admission controllers** or **Azure Policy** to enforce registry access restrictions. Limiting or locking down egress traffic to specific container registries is also recommended. For more information, refer to: https://docs.microsoft.com/en-us/azure/aks/limit-egress-traffic scored: false - id: 5.2 text: "Access and identity options for Azure Kubernetes Service (AKS)" checks: - id: 5.2.1 text: "Prefer using dedicated AKS Service Accounts (Manual)" type: "manual" remediation: | Azure Active Directory integration The security of AKS clusters can be enhanced with the integration of Azure Active Directory (AD). Built on decades of enterprise identity management, Azure AD is a multi-tenant, cloud-based directory, and identity management service that combines core directory services, application access management, and identity protection. With Azure AD, you can integrate on-premises identities into AKS clusters to provide a single source for account management and security. Azure Active Directory integration with AKS clusters With Azure AD-integrated AKS clusters, you can grant users or groups access to Kubernetes resources within a namespace or across the cluster. To obtain a kubectl configuration context, a user can run the az aks get-credentials command. When a user then interacts with the AKS cluster with kubectl, they're prompted to sign in with their Azure AD credentials. This approach provides a single source for user account management and password credentials. The user can only access the resources as defined by the cluster administrator. Azure AD authentication is provided to AKS clusters with OpenID Connect. OpenID Connect is an identity layer built on top of the OAuth 2.0 protocol. For more information on OpenID Connect, see the Open ID connect documentation. From inside of the Kubernetes cluster, Webhook Token Authentication is used to verify authentication tokens. Webhook token authentication is configured and managed as part of the AKS cluster. scored: false - id: 5.3 text: "Key Management Service (KMS)" checks: - id: 5.3.1 text: "Ensure Kubernetes Secrets are encrypted (Manual)" type: "manual" remediation: "No remediation" scored: false - id: 5.4 text: "Cluster Networking" checks: - id: 5.4.1 text: "Restrict Access to the Control Plane Endpoint (Manual)" type: "manual" remediation: | By enabling private endpoint access to the Kubernetes API server, all communication between your nodes and the API server stays within your VPC. You can also limit the IP addresses that can access your API server from the internet, or completely disable internet access to the API server. With this in mind, you can update your cluster accordingly using the AKS CLI to ensure that Private Endpoint Access is enabled. If you choose to also enable Public Endpoint Access then you should also configure a list of allowable CIDR blocks, resulting in restricted access from the internet. If you specify no CIDR blocks, then the public API server endpoint is able to receive and process requests from all IP addresses by defaulting to ['0.0.0.0/0']. Example: az aks update --name ${CLUSTER_NAME} --resource-group ${RESOURCE_GROUP} --api-server-access-profile enablePrivateCluster=true --api-server-access-profile authorizedIpRanges=192.168.1.0/24 scored: false - id: 5.4.2 text: "Ensure clusters are created with Private Endpoint Enabled and Public Access Disabled (Manual)" type: "manual" remediation: | To use a private endpoint, create a new private endpoint in your virtual network, then create a link between your virtual network and a new private DNS zone. You can also restrict access to the public endpoint by enabling only specific CIDR blocks to access it. For example: az aks update --name ${CLUSTER_NAME} --resource-group ${RESOURCE_GROUP} --api-server-access-profile enablePublicFqdn=false This command disables the public API endpoint for your AKS cluster. scored: false - id: 5.4.3 text: "Ensure clusters are created with Private Nodes (Manual)" type: "manual" remediation: | To create a private cluster, use the following command: az aks create \ --resource-group \ --name \ --load-balancer-sku standard \ --enable-private-cluster \ --network-plugin azure \ --vnet-subnet-id \ --docker-bridge-address \ --dns-service-ip \ --service-cidr Ensure that --enable-private-cluster flag is set to enable private nodes in your cluster. scored: false - id: 5.4.4 text: "Ensure Network Policy is Enabled and set as appropriate (Manual)" type: "manual" remediation: | Utilize Calico or another network policy engine to segment and isolate your traffic. Enable network policies on your AKS cluster by following the Azure documentation or using the `az aks` CLI to enable the network policy add-on. scored: false - id: 5.4.5 text: "Encrypt traffic to HTTPS load balancers with TLS certificates (Manual)" type: "manual" remediation: "No remediation" scored: false - id: 5.5 text: "Authentication and Authorization" checks: - id: 5.5.1 text: "Manage Kubernetes RBAC users with Azure AD (Manual)" type: "manual" remediation: "No remediation" scored: false - id: 5.5.2 text: "Use Azure RBAC for Kubernetes Authorization (Manual)" type: "manual" remediation: "No remediation" scored: false ================================================ FILE: cfg/aks-1.8/master.yaml ================================================ --- controls: version: "aks-1.8" id: 1 text: "Control Plane Components" type: "master" ================================================ FILE: cfg/aks-1.8/node.yaml ================================================ --- controls: version: "aks-1.8" id: 3 text: "Worker Node Security Configuration" type: "node" groups: - id: 3.1 text: "Worker Node Configuration Files" checks: - id: 3.1.1 text: "Ensure that the kubeconfig file permissions are set to 644 or more restrictive (Automated)" audit: '/bin/sh -c ''if test -e $kubeletkubeconfig; then stat -c permissions=%a $kubeletkubeconfig; fi'' ' tests: test_items: - flag: "permissions" compare: op: bitmask value: "644" remediation: | Run the below command (based on the file location on your system) on each worker node. For example: chmod 644 $kubeletkubeconfig scored: true - id: 3.1.2 text: "Ensure that the kubelet kubeconfig file ownership is set to root:root (Automated)" audit: '/bin/sh -c ''if test -e $kubeletkubeconfig; then stat -c %U:%G $kubeletkubeconfig; fi'' ' tests: test_items: - flag: root:root remediation: | Run the below command (based on the file location on your system) on each worker node. For example: chown root:root $kubeletkubeconfig scored: true - id: 3.1.3 text: "Ensure that the azure.json file has permissions set to 644 or more restrictive (Automated)" audit: '/bin/sh -c ''if test -e $kubeletconf; then stat -c permissions=%a $kubeletconf; fi'' ' tests: test_items: - flag: "permissions" compare: op: bitmask value: "644" remediation: | Run the following command on each worker node: chmod 644 $azurejson scored: true - id: 3.1.4 text: "Ensure that the azure.json file ownership is set to root:root (Automated)" audit: '/bin/sh -c ''if test -e $kubeletconf; then stat -c %U:%G $kubeletconf; fi'' ' tests: test_items: - flag: root:root remediation: | Run the following command on each worker node: chown root:root $azurejson scored: true - id: 3.2 text: "Kubelet" checks: - id: 3.2.1 text: "Ensure that the --anonymous-auth argument is set to false (Automated)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: "--anonymous-auth" path: '{.authentication.anonymous.enabled}' compare: op: eq value: false remediation: | If using a Kubelet config file, edit the file to set authentication: anonymous: enabled to false. If using executable arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_SYSTEM_PODS_ARGS variable. --anonymous-auth=false Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: true - id: 3.2.2 text: "Ensure that the --authorization-mode argument is not set to AlwaysAllow (Automated)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: "--anonymous-auth" path: '{.authorization.mode}' compare: op: nothave value: AlwaysAllow remediation: | If using a Kubelet config file, edit the file to set authorization: mode to Webhook. If using executable arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_AUTHZ_ARGS variable. --authorization-mode=Webhook Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: true - id: 3.2.3 text: "Ensure that the --client-ca-file argument is set as appropriate (Automated)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --client-ca-file path: '{.authentication.x509.clientCAFile}' set: true remediation: | If using a Kubelet config file, edit the file to set authentication: x509: clientCAFile to the location of the client CA file. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_AUTHZ_ARGS variable. --client-ca-file= Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: true - id: 3.2.4 text: "Ensure that the --read-only-port is secured (Automated)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: "--read-only-port" path: '{.readOnlyPort}' set: true compare: op: eq value: 0 remediation: | If using a Kubelet config file, edit the file to set readOnlyPort to 0. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_SYSTEM_PODS_ARGS variable. --read-only-port=0 Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: true - id: 3.2.5 text: "Ensure that the --streaming-connection-idle-timeout argument is not set to 0 (Automated)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --streaming-connection-idle-timeout path: '{.streamingConnectionIdleTimeout}' set: true compare: op: noteq value: 0 - flag: --streaming-connection-idle-timeout path: '{.streamingConnectionIdleTimeout}' set: false bin_op: or remediation: | If using a Kubelet config file, edit the file to set streamingConnectionIdleTimeout to a value other than 0. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_SYSTEM_PODS_ARGS variable. --streaming-connection-idle-timeout=5m Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: true - id: 3.2.6 text: "Ensure that the --make-iptables-util-chains argument is set to true (Automated) " audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --make-iptables-util-chains path: '{.makeIPTablesUtilChains}' set: true compare: op: eq value: true - flag: --make-iptables-util-chains path: '{.makeIPTablesUtilChains}' set: false bin_op: or remediation: | If using a Kubelet config file, edit the file to set makeIPTablesUtilChains: true. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and remove the --make-iptables-util-chains argument from the KUBELET_SYSTEM_PODS_ARGS variable. Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: true - id: 3.2.7 text: "Ensure that the --eventRecordQPS argument is set to 0 or a level which ensures appropriate event capture (Automated)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --event-qps path: '{.eventRecordQPS}' set: true compare: op: eq value: 0 remediation: | If using a Kubelet config file, edit the file to set the 'eventRecordQPS' value to an appropriate level (e.g., 5). If using executable arguments, check the Kubelet service file `$kubeletsvc` on each worker node, and add the following parameter to the `KUBELET_ARGS` variable: --eventRecordQPS=5 Ensure that there is no conflicting `--eventRecordQPS` setting in the service file that overrides the config file. After making the changes, restart the Kubelet service: systemctl daemon-reload systemctl restart kubelet.service systemctl status kubelet -l scored: true - id: 3.2.8 text: "Ensure that the --rotate-certificates argument is not set to false (Automated)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --rotate-certificates path: '{.rotateCertificates}' set: true compare: op: eq value: true - flag: --rotate-certificates path: '{.rotateCertificates}' set: false bin_op: or remediation: | If modifying the Kubelet config file, edit the `kubelet-config.json` file located at `/etc/kubernetes/kubelet/kubelet-config.json` and set the following parameter to `true`: "rotateCertificates": true Ensure that the Kubelet service file located at `/etc/systemd/system/kubelet.service.d/10-kubelet-args.conf` does not define the `--rotate-certificates` argument as `false`, as this would override the config file. If using executable arguments, add the following line to the `KUBELET_CERTIFICATE_ARGS` variable: --rotate-certificates=true After making the necessary changes, restart the Kubelet service: systemctl daemon-reload systemctl restart kubelet.service systemctl status kubelet -l scored: true - id: 3.2.9 text: "Ensure that the RotateKubeletServerCertificate argument is set to true (Automated)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: RotateKubeletServerCertificate path: '{.featureGates.RotateKubeletServerCertificate}' set: true compare: op: eq value: true remediation: | Edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_CERTIFICATE_ARGS variable. --feature-gates=RotateKubeletServerCertificate=true Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: true ================================================ FILE: cfg/aks-1.8/policies.yaml ================================================ --- controls: version: "aks-1.8" id: 4 text: "Policies" type: "policies" groups: - id: 4.1 text: "RBAC and Service Accounts" checks: - id: 4.1.1 text: "Ensure that the cluster-admin role is only used where required (Manual)" type: "manual" remediation: | Identify all clusterrolebindings to the cluster-admin role using: kubectl get clusterrolebindings --no-headers | grep cluster-admin Review if each of them actually needs this role. If not, remove the binding: kubectl delete clusterrolebinding Where possible, assign a less privileged ClusterRole. scored: true - id: 4.1.2 text: "Minimize access to secrets (Manual)" type: "manual" remediation: | Identify all roles that grant access to secrets via get/list/watch verbs. Use `kubectl edit role -n ` to remove these permissions. Alternatively, create a new least-privileged role that excludes secret access. scored: true - id: 4.1.3 text: "Minimize wildcard use in Roles and ClusterRoles (Manual)" type: "manual" remediation: | Identify roles and clusterroles using wildcards (*) in 'verbs', 'resources', or 'apiGroups'. Replace wildcards with specific values to enforce least privilege access. Use `kubectl edit role -n ` or `kubectl edit clusterrole ` to update. scored: true - id: 4.1.4 text: "Minimize access to create pods (Manual)" type: "manual" remediation: | Review all roles and clusterroles that have "create" permission on "pods". Where possible, remove or restrict this permission to only required service accounts. Use: - `kubectl edit role -n ` - `kubectl edit clusterrole ` Apply least privilege principle across the cluster. scored: true - id: 4.1.5 text: "Ensure that default service accounts are not actively used (Manual)" type: "manual" remediation: | 1. Avoid using default service accounts for workloads. 2. Set `automountServiceAccountToken: false` on all default SAs: kubectl patch serviceaccount default -n -p '{"automountServiceAccountToken": false}' 3. Use custom service accounts with only the necessary permissions. scored: true - id: 4.1.6 text: "Ensure that Service Account Tokens are only mounted where necessary (Manual)" type: "manual" remediation: | Pods that do not need access to the Kubernetes API should not mount service account tokens. To disable token mounting in a pod definition: ```yaml spec: automountServiceAccountToken: false ``` Or patch an existing pod's spec (recommended via workload template): Patch not possible for running pods — update the deployment YAML or recreate pods with updated spec. scored: true - id: 4.1.7 text: "Limit use of the Bind, Impersonate and Escalate permissions in the Kubernetes cluster (Manual)" type: "manual" remediation: | Review ClusterRoles and Roles that grant the bind, impersonate, or escalate verbs. These permissions should only be granted where they are strictly required for cluster operation. Remove these privileges from non-system users and service accounts where possible. Take care not to remove required permissions from Kubernetes system roles such as system:masters or clusterrole-aggregation-controller, as this may impact cluster functionality. scored: false - id: 4.1.8 text: "Minimize access to create Persistent Volumes (Manual)" type: "manual" remediation: | Review ClusterRoles and Roles that grant create access to PersistentVolume resources. Creation of PersistentVolumes should be limited to trusted administrators only. Where possible, remove create permissions on PersistentVolume objects from non-administrative users and service accounts to reduce the risk of privilege escalation via hostPath volumes. scored: false - id: 4.1.9 text: "Minimize access to the proxy sub-resource of Nodes (Manual)" type: "manual" remediation: | Review ClusterRoles and Roles that grant access to the proxy sub-resource of Node objects. Limit access to this sub-resource to trusted administrators only. Removing unnecessary access reduces the risk of privilege escalation via the kubelet API and ensures cluster security controls, like audit logging and admission control, remain effective. scored: false - id: 4.1.10 text: "Minimize access to the approval sub-resource of CertificateSigningRequests objects (Manual)" type: "manual" remediation: | Review ClusterRoles and Roles that grant access to update the approval sub-resource of CertificateSigningRequests (CSR) objects. Limit this access to trusted administrators only. Removing unnecessary access reduces the risk of privilege escalation via approval of high-privileged certificates, which could allow creation of new cluster-admin level users. scored: false - id: 4.1.11 text: "Minimize access to webhook configuration objects (Manual)" type: "manual" remediation: | Review ClusterRoles and Roles that grant access to create, modify, or delete validatingwebhookconfigurations or mutatingwebhookconfigurations. Limit this access to trusted administrators only. Removing unnecessary access reduces the risk of privilege escalation or disruption of cluster operations via malicious or misconfigured webhooks. scored: false - id: 4.1.12 text: "Minimize access to the service account token creation (Manual)" type: "manual" remediation: | Review ClusterRoles and Roles that grant access to create the token sub-resource of ServiceAccount objects. Limit this access to trusted administrators only. Removing unnecessary access reduces the risk of privilege escalation or persistent unauthorized access to the cluster via service account tokens. scored: false - id: 4.2 text: "Pod Security Policies" checks: - id: 4.2.1 text: "Minimize the admission of privileged containers (Manual)" type: "manual" remediation: | Add a Pod Security Admission (PSA) policy to each namespace in the cluster to restrict the admission of privileged containers. To enforce a restricted policy for a specific namespace, use the following command: kubectl label --overwrite ns NAMESPACE pod-security.kubernetes.io/enforce=restricted You can also enforce PSA for all namespaces: kubectl label --overwrite ns --all pod-security.kubernetes.io/warn=baseline Additionally, review the namespaces that should be excluded (e.g., `kube-system`, `gatekeeper-system`, `azure-arc`, `azure-extensions-usage-system`) and adjust your filtering if necessary. To enable Pod Security Policies, refer to the detailed documentation for Kubernetes and Azure integration at: https://learn.microsoft.com/en-us/azure/governance/policy/concepts/policy-for-kubernetes scored: false - id: 4.2.2 text: "Minimize the admission of containers wishing to share the host process ID namespace (Manual)" type: "manual" remediation: | Add a policy to each namespace in the cluster that restricts the admission of containers with hostPID. For namespaces that need it, ensure RBAC controls limit access to a specific service account. You can label your namespaces as follows to restrict or enforce the policy: kubectl label --overwrite ns NAMESPACE pod-security.kubernetes.io/enforce=restricted You can also use the following to warn about policies: kubectl label --overwrite ns --all pod-security.kubernetes.io/warn=baseline For more information, refer to the official Kubernetes and Azure documentation on policies: https://learn.microsoft.com/en-us/azure/governance/policy/concepts/policy-for-kubernetes scored: false - id: 4.2.3 text: "Minimize the admission of containers wishing to share the host IPC namespace (Manual)" type: "manual" remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of hostIPC containers. You can label your namespaces as follows to restrict or enforce the policy: kubectl label --overwrite ns NAMESPACE pod-security.kubernetes.io/enforce=restricted You can also use the following to warn about policies: kubectl label --overwrite ns --all pod-security.kubernetes.io/warn=baseline For more information, refer to the official Kubernetes and Azure documentation on policies: https://learn.microsoft.com/en-us/azure/governance/policy/concepts/policy-for-kubernetes scored: false - id: 4.2.4 text: "Minimize the admission of containers wishing to share the host network namespace (Manual)" type: "manual" remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of hostNetwork containers. You can label your namespaces as follows to restrict or enforce the policy: kubectl label --overwrite ns NAMESPACE pod-security.kubernetes.io/enforce=restricted You can also use the following to warn about policies: kubectl label --overwrite ns --all pod-security.kubernetes.io/warn=baseline For more information, refer to the official Kubernetes and Azure documentation on policies: https://learn.microsoft.com/en-us/azure/governance/policy/concepts/policy-for-kubernetes scored: false - id: 4.2.5 text: "Minimize the admission of containers with allowPrivilegeEscalation (Manual)" type: "manual" remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of containers with .spec.allowPrivilegeEscalation set to true. You can label your namespaces as follows to restrict or enforce the policy: kubectl label --overwrite ns NAMESPACE pod-security.kubernetes.io/enforce=restricted You can also use the following to warn about policies: kubectl label --overwrite ns --all pod-security.kubernetes.io/warn=baseline For more information, refer to the official Kubernetes and Azure documentation on policies: https://learn.microsoft.com/en-us/azure/governance/policy/concepts/policy-for-kubernetes scored: false - id: 4.3 text: "Azure Policy / OPA" checks: [] - id: 4.4 text: "CNI Plugin" checks: - id: 4.4.1 text: "Ensure latest CNI version is used (Manual)" type: "manual" remediation: | Review the documentation of AWS CNI plugin, and ensure latest CNI version is used. scored: false - id: 4.4.2 text: "Ensure that all Namespaces have Network Policies defined (Automated)" type: "skip" audit: | ns_without_np=$(comm -23 \ <(kubectl get ns -o jsonpath='{.items[*].metadata.name}' | tr ' ' '\n' | sort) \ <(kubectl get networkpolicy --all-namespaces -o jsonpath='{.items[*].metadata.namespace}' | tr ' ' '\n' | sort)) if [ -z "$ns_without_np" ]; then echo "ALL_NAMESPACES_HAVE_NETWORKPOLICIES"; else echo "MISSING_NETWORKPOLICIES"; fi tests: test_items: - flag: "ALL_NAMESPACES_HAVE_NETWORKPOLICIES" set: true compare: op: eq value: "ALL_NAMESPACES_HAVE_NETWORKPOLICIES" remediation: | Define at least one NetworkPolicy in each namespace to control pod-level traffic. Example: kubectl apply -n -f - </dev/null | grep -v '^service\s\+kubernetes\s' || true) if [ -z "$output" ]; then echo "DEFAULT_NAMESPACE_UNUSED"; else echo "DEFAULT_NAMESPACE_IN_USE"; fi tests: test_items: - flag: "DEFAULT_NAMESPACE_UNUSED" set: true compare: op: eq value: "DEFAULT_NAMESPACE_UNUSED" remediation: | Avoid using the default namespace for user workloads. - Create separate namespaces for your applications and infrastructure components. - Move any user-defined resources out of the default namespace. Example to create a namespace: kubectl create namespace my-namespace Example to move resources: kubectl get deployment my-app -n default -o yaml | sed 's/namespace: default/namespace: my-namespace/' | kubectl apply -f - kubectl delete deployment my-app -n default scored: true ================================================ FILE: cfg/cis-1.10/config.yaml ================================================ --- ## Version-specific settings that override the values in cfg/config.yaml ================================================ FILE: cfg/cis-1.10/controlplane.yaml ================================================ --- controls: version: "cis-1.10" id: 3 text: "Control Plane Configuration" type: "controlplane" groups: - id: 3.1 text: "Authentication and Authorization" checks: - id: 3.1.1 text: "Client certificate authentication should not be used for users (Manual)" type: "manual" remediation: | Alternative mechanisms provided by Kubernetes such as the use of OIDC should be implemented in place of client certificates. scored: false - id: 3.1.2 text: "Service account token authentication should not be used for users (Manual)" type: "manual" remediation: | Alternative mechanisms provided by Kubernetes such as the use of OIDC should be implemented in place of service account tokens. scored: false - id: 3.1.3 text: "Bootstrap token authentication should not be used for users (Manual)" type: "manual" remediation: | Alternative mechanisms provided by Kubernetes such as the use of OIDC should be implemented in place of bootstrap tokens. scored: false - id: 3.2 text: "Logging" checks: - id: 3.2.1 text: "Ensure that a minimal audit policy is created (Manual)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--audit-policy-file" set: true remediation: | Create an audit policy file for your cluster. scored: false - id: 3.2.2 text: "Ensure that the audit policy covers key security concerns (Manual)" type: "manual" remediation: | Review the audit policy provided for the cluster and ensure that it covers at least the following areas, - Access to Secrets managed by the cluster. Care should be taken to only log Metadata for requests to Secrets, ConfigMaps, and TokenReviews, in order to avoid risk of logging sensitive data. - Modification of Pod and Deployment objects. - Use of `pods/exec`, `pods/portforward`, `pods/proxy` and `services/proxy`. For most requests, minimally logging at the Metadata level is recommended (the most basic level of logging). scored: false ================================================ FILE: cfg/cis-1.10/etcd.yaml ================================================ --- controls: version: "cis-1.10" id: 2 text: "Etcd Node Configuration" type: "etcd" groups: - id: 2 text: "Etcd Node Configuration" checks: - id: 2.1 text: "Ensure that the --cert-file and --key-file arguments are set as appropriate (Automated)" audit: "/bin/ps -ef | /bin/grep $etcdbin | /bin/grep -v grep" tests: bin_op: and test_items: - flag: "--cert-file" env: "ETCD_CERT_FILE" - flag: "--key-file" env: "ETCD_KEY_FILE" remediation: | Follow the etcd service documentation and configure TLS encryption. Then, edit the etcd pod specification file /etc/kubernetes/manifests/etcd.yaml on the master node and set the below parameters. --cert-file= --key-file= scored: true - id: 2.2 text: "Ensure that the --client-cert-auth argument is set to true (Automated)" audit: "/bin/ps -ef | /bin/grep $etcdbin | /bin/grep -v grep" tests: test_items: - flag: "--client-cert-auth" env: "ETCD_CLIENT_CERT_AUTH" compare: op: eq value: true remediation: | Edit the etcd pod specification file $etcdconf on the master node and set the below parameter. --client-cert-auth="true" scored: true - id: 2.3 text: "Ensure that the --auto-tls argument is not set to true (Automated)" audit: "/bin/ps -ef | /bin/grep $etcdbin | /bin/grep -v grep" tests: bin_op: or test_items: - flag: "--auto-tls" env: "ETCD_AUTO_TLS" set: false - flag: "--auto-tls" env: "ETCD_AUTO_TLS" compare: op: eq value: false remediation: | Edit the etcd pod specification file $etcdconf on the master node and either remove the --auto-tls parameter or set it to false. --auto-tls=false scored: true - id: 2.4 text: "Ensure that the --peer-cert-file and --peer-key-file arguments are set as appropriate (Automated)" audit: "/bin/ps -ef | /bin/grep $etcdbin | /bin/grep -v grep" tests: bin_op: and test_items: - flag: "--peer-cert-file" env: "ETCD_PEER_CERT_FILE" - flag: "--peer-key-file" env: "ETCD_PEER_KEY_FILE" remediation: | Follow the etcd service documentation and configure peer TLS encryption as appropriate for your etcd cluster. Then, edit the etcd pod specification file $etcdconf on the master node and set the below parameters. --peer-client-file= --peer-key-file= scored: true - id: 2.5 text: "Ensure that the --peer-client-cert-auth argument is set to true (Automated)" audit: "/bin/ps -ef | /bin/grep $etcdbin | /bin/grep -v grep" tests: test_items: - flag: "--peer-client-cert-auth" env: "ETCD_PEER_CLIENT_CERT_AUTH" compare: op: eq value: true remediation: | Edit the etcd pod specification file $etcdconf on the master node and set the below parameter. --peer-client-cert-auth=true scored: true - id: 2.6 text: "Ensure that the --peer-auto-tls argument is not set to true (Automated)" audit: "/bin/ps -ef | /bin/grep $etcdbin | /bin/grep -v grep" tests: bin_op: or test_items: - flag: "--peer-auto-tls" env: "ETCD_PEER_AUTO_TLS" set: false - flag: "--peer-auto-tls" env: "ETCD_PEER_AUTO_TLS" compare: op: eq value: false remediation: | Edit the etcd pod specification file $etcdconf on the master node and either remove the --peer-auto-tls parameter or set it to false. --peer-auto-tls=false scored: true - id: 2.7 text: "Ensure that a unique Certificate Authority is used for etcd (Manual)" audit: "/bin/ps -ef | /bin/grep $etcdbin | /bin/grep -v grep" tests: test_items: - flag: "--trusted-ca-file" env: "ETCD_TRUSTED_CA_FILE" remediation: | [Manual test] Follow the etcd documentation and create a dedicated certificate authority setup for the etcd service. Then, edit the etcd pod specification file $etcdconf on the master node and set the below parameter. --trusted-ca-file= scored: false ================================================ FILE: cfg/cis-1.10/master.yaml ================================================ --- controls: version: "cis-1.10" id: 1 text: "Control Plane Security Configuration" type: "master" groups: - id: 1.1 text: "Control Plane Node Configuration Files" checks: - id: 1.1.1 text: "Ensure that the API server pod specification file permissions are set to 600 or more restrictive (Automated)" audit: "/bin/sh -c 'if test -e $apiserverconf; then stat -c permissions=%a $apiserverconf; fi'" tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chmod 600 $apiserverconf scored: true - id: 1.1.2 text: "Ensure that the API server pod specification file ownership is set to root:root (Automated)" audit: "/bin/sh -c 'if test -e $apiserverconf; then stat -c %U:%G $apiserverconf; fi'" tests: test_items: - flag: "root:root" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chown root:root $apiserverconf scored: true - id: 1.1.3 text: "Ensure that the controller manager pod specification file permissions are set to 600 or more restrictive (Automated)" audit: "/bin/sh -c 'if test -e $controllermanagerconf; then stat -c permissions=%a $controllermanagerconf; fi'" tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chmod 600 $controllermanagerconf scored: true - id: 1.1.4 text: "Ensure that the controller manager pod specification file ownership is set to root:root (Automated)" audit: "/bin/sh -c 'if test -e $controllermanagerconf; then stat -c %U:%G $controllermanagerconf; fi'" tests: test_items: - flag: "root:root" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chown root:root $controllermanagerconf scored: true - id: 1.1.5 text: "Ensure that the scheduler pod specification file permissions are set to 600 or more restrictive (Automated)" audit: "/bin/sh -c 'if test -e $schedulerconf; then stat -c permissions=%a $schedulerconf; fi'" tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chmod 600 $schedulerconf scored: true - id: 1.1.6 text: "Ensure that the scheduler pod specification file ownership is set to root:root (Automated)" audit: "/bin/sh -c 'if test -e $schedulerconf; then stat -c %U:%G $schedulerconf; fi'" tests: test_items: - flag: "root:root" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chown root:root $schedulerconf scored: true - id: 1.1.7 text: "Ensure that the etcd pod specification file permissions are set to 600 or more restrictive (Automated)" audit: "/bin/sh -c 'if test -e $etcdconf; then find $etcdconf -name '*etcd*' | xargs stat -c permissions=%a; fi'" use_multiple_values: true tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chmod 600 $etcdconf scored: true - id: 1.1.8 text: "Ensure that the etcd pod specification file ownership is set to root:root (Automated)" audit: "/bin/sh -c 'if test -e $etcdconf; then find $etcdconf -name '*etcd*' | xargs stat -c %U:%G; fi'" use_multiple_values: true tests: test_items: - flag: "root:root" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chown root:root $etcdconf scored: true - id: 1.1.9 text: "Ensure that the Container Network Interface file permissions are set to 600 or more restrictive (Manual)" audit: | ps -ef | grep $kubeletbin | grep -- --cni-conf-dir | sed 's%.*cni-conf-dir[= ]\([^ ]*\).*%\1%' | xargs -I{} find {} -mindepth 1 | xargs --no-run-if-empty stat -c permissions=%a find /var/lib/cni/networks -type f 2> /dev/null | xargs --no-run-if-empty stat -c permissions=%a use_multiple_values: true tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chmod 600 scored: false - id: 1.1.10 text: "Ensure that the Container Network Interface file ownership is set to root:root (Manual)" audit: | ps -ef | grep $kubeletbin | grep -- --cni-conf-dir | sed 's%.*cni-conf-dir[= ]\([^ ]*\).*%\1%' | xargs -I{} find {} -mindepth 1 | xargs --no-run-if-empty stat -c %U:%G find /var/lib/cni/networks -type f 2> /dev/null | xargs --no-run-if-empty stat -c %U:%G use_multiple_values: true tests: test_items: - flag: "root:root" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chown root:root scored: false - id: 1.1.11 text: "Ensure that the etcd data directory permissions are set to 700 or more restrictive (Automated)" audit: | DATA_DIR='' for d in $(ps -ef | grep $etcdbin | grep -- --data-dir | sed 's%.*data-dir[= ]\([^ ]*\).*%\1%'); do if test -d "$d"; then DATA_DIR="$d"; fi done if ! test -d "$DATA_DIR"; then DATA_DIR=$etcddatadir; fi stat -c permissions=%a "$DATA_DIR" tests: test_items: - flag: "permissions" compare: op: bitmask value: "700" remediation: | On the etcd server node, get the etcd data directory, passed as an argument --data-dir, from the command 'ps -ef | grep etcd'. Run the below command (based on the etcd data directory found above). For example, chmod 700 /var/lib/etcd scored: true - id: 1.1.12 text: "Ensure that the etcd data directory ownership is set to etcd:etcd (Automated)" audit: | DATA_DIR='' for d in $(ps -ef | grep $etcdbin | grep -- --data-dir | sed 's%.*data-dir[= ]\([^ ]*\).*%\1%'); do if test -d "$d"; then DATA_DIR="$d"; fi done if ! test -d "$DATA_DIR"; then DATA_DIR=$etcddatadir; fi stat -c %U:%G "$DATA_DIR" tests: test_items: - flag: "etcd:etcd" remediation: | On the etcd server node, get the etcd data directory, passed as an argument --data-dir, from the command 'ps -ef | grep etcd'. Run the below command (based on the etcd data directory found above). For example, chown etcd:etcd /var/lib/etcd scored: true - id: 1.1.13 text: "Ensure that the default administrative credential file permissions are set to 600 (Automated)" audit: | for adminconf in /etc/kubernetes/admin.conf /etc/kubernetes/super-admin.conf; do if test -e $adminconf; then stat -c "permissions=%a %n" $adminconf; fi; done use_multiple_values: true tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chmod 600 /etc/kubernetes/admin.conf On Kubernetes 1.29+ the super-admin.conf file should also be modified, if present. For example, chmod 600 /etc/kubernetes/super-admin.conf scored: true - id: 1.1.14 text: "Ensure that the default administrative credential file ownership is set to root:root (Automated)" audit: | for adminconf in /etc/kubernetes/admin.conf /etc/kubernetes/super-admin.conf; do if test -e $adminconf; then stat -c "ownership=%U:%G %n" $adminconf; fi; done use_multiple_values: true tests: test_items: - flag: "ownership" compare: op: eq value: "root:root" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chown root:root /etc/kubernetes/admin.conf On Kubernetes 1.29+ the super-admin.conf file should also be modified, if present. For example, chown root:root /etc/kubernetes/super-admin.conf scored: true - id: 1.1.15 text: "Ensure that the scheduler.conf file permissions are set to 600 or more restrictive (Automated)" audit: "/bin/sh -c 'if test -e $schedulerkubeconfig; then stat -c permissions=%a $schedulerkubeconfig; fi'" tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chmod 600 $schedulerkubeconfig scored: true - id: 1.1.16 text: "Ensure that the scheduler.conf file ownership is set to root:root (Automated)" audit: "/bin/sh -c 'if test -e $schedulerkubeconfig; then stat -c %U:%G $schedulerkubeconfig; fi'" tests: test_items: - flag: "root:root" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chown root:root $schedulerkubeconfig scored: true - id: 1.1.17 text: "Ensure that the controller-manager.conf file permissions are set to 600 or more restrictive (Automated)" audit: "/bin/sh -c 'if test -e $controllermanagerkubeconfig; then stat -c permissions=%a $controllermanagerkubeconfig; fi'" tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chmod 600 $controllermanagerkubeconfig scored: true - id: 1.1.18 text: "Ensure that the controller-manager.conf file ownership is set to root:root (Automated)" audit: "/bin/sh -c 'if test -e $controllermanagerkubeconfig; then stat -c %U:%G $controllermanagerkubeconfig; fi'" tests: test_items: - flag: "root:root" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chown root:root $controllermanagerkubeconfig scored: true - id: 1.1.19 text: "Ensure that the Kubernetes PKI directory and file ownership is set to root:root (Automated)" audit: "find /etc/kubernetes/pki/ | xargs stat -c %U:%G" use_multiple_values: true tests: test_items: - flag: "root:root" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chown -R root:root /etc/kubernetes/pki/ scored: true - id: 1.1.20 text: "Ensure that the Kubernetes PKI certificate file permissions are set to 600 or more restrictive (Manual)" audit: "find /etc/kubernetes/pki/ -name '*.crt' | xargs stat -c permissions=%a" use_multiple_values: true tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chmod -R 600 /etc/kubernetes/pki/*.crt scored: false - id: 1.1.21 text: "Ensure that the Kubernetes PKI key file permissions are set to 600 (Manual)" audit: "find /etc/kubernetes/pki/ -name '*.key' | xargs stat -c permissions=%a" use_multiple_values: true tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chmod -R 600 /etc/kubernetes/pki/*.key scored: false - id: 1.2 text: "API Server" checks: - id: 1.2.1 text: "Ensure that the --anonymous-auth argument is set to false (Manual)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--anonymous-auth" compare: op: eq value: false remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the below parameter. --anonymous-auth=false scored: false - id: 1.2.2 text: "Ensure that the --token-auth-file parameter is not set (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--token-auth-file" set: false remediation: | Follow the documentation and configure alternate mechanisms for authentication. Then, edit the API server pod specification file $apiserverconf on the control plane node and remove the --token-auth-file= parameter. scored: true - id: 1.2.3 text: "Ensure that the --DenyServiceExternalIPs is set (Manual)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: bin_op: or test_items: - flag: "--enable-admission-plugins" compare: op: has value: "DenyServiceExternalIPs" remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and add the `DenyServiceExternalIPs` plugin to the enabled admission plugins, as such --enable-admission-plugin=DenyServiceExternalIPs. scored: false - id: 1.2.4 text: "Ensure that the --kubelet-client-certificate and --kubelet-client-key arguments are set as appropriate (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: bin_op: and test_items: - flag: "--kubelet-client-certificate" - flag: "--kubelet-client-key" remediation: | Follow the Kubernetes documentation and set up the TLS connection between the apiserver and kubelets. Then, edit API server pod specification file $apiserverconf on the control plane node and set the kubelet client certificate and key parameters as below. --kubelet-client-certificate= --kubelet-client-key= scored: true - id: 1.2.5 text: "Ensure that the --kubelet-certificate-authority argument is set as appropriate (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--kubelet-certificate-authority" remediation: | Follow the Kubernetes documentation and setup the TLS connection between the apiserver and kubelets. Then, edit the API server pod specification file $apiserverconf on the control plane node and set the --kubelet-certificate-authority parameter to the path to the cert file for the certificate authority. --kubelet-certificate-authority= scored: true - id: 1.2.6 text: "Ensure that the --authorization-mode argument is not set to AlwaysAllow (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--authorization-mode" compare: op: nothave value: "AlwaysAllow" remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the --authorization-mode parameter to values other than AlwaysAllow. One such example could be as below. --authorization-mode=RBAC scored: true - id: 1.2.7 text: "Ensure that the --authorization-mode argument includes Node (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--authorization-mode" compare: op: has value: "Node" remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the --authorization-mode parameter to a value that includes Node. --authorization-mode=Node,RBAC scored: true - id: 1.2.8 text: "Ensure that the --authorization-mode argument includes RBAC (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--authorization-mode" compare: op: has value: "RBAC" remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the --authorization-mode parameter to a value that includes RBAC, for example `--authorization-mode=Node,RBAC`. scored: true - id: 1.2.9 text: "Ensure that the admission control plugin EventRateLimit is set (Manual)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--enable-admission-plugins" compare: op: has value: "EventRateLimit" remediation: | Follow the Kubernetes documentation and set the desired limits in a configuration file. Then, edit the API server pod specification file $apiserverconf and set the below parameters. --enable-admission-plugins=...,EventRateLimit,... --admission-control-config-file= scored: false - id: 1.2.10 text: "Ensure that the admission control plugin AlwaysAdmit is not set (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: bin_op: or test_items: - flag: "--enable-admission-plugins" compare: op: nothave value: AlwaysAdmit - flag: "--enable-admission-plugins" set: false remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and either remove the --enable-admission-plugins parameter, or set it to a value that does not include AlwaysAdmit. scored: true - id: 1.2.11 text: "Ensure that the admission control plugin AlwaysPullImages is set (Manual)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--enable-admission-plugins" compare: op: has value: "AlwaysPullImages" remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the --enable-admission-plugins parameter to include AlwaysPullImages. --enable-admission-plugins=...,AlwaysPullImages,... scored: false - id: 1.2.12 text: "Ensure that the admission control plugin ServiceAccount is set (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: bin_op: or test_items: - flag: "--disable-admission-plugins" compare: op: nothave value: "ServiceAccount" - flag: "--disable-admission-plugins" set: false remediation: | Follow the documentation and create ServiceAccount objects as per your environment. Then, edit the API server pod specification file $apiserverconf on the control plane node and ensure that the --disable-admission-plugins parameter is set to a value that does not include ServiceAccount. scored: true - id: 1.2.13 text: "Ensure that the admission control plugin NamespaceLifecycle is set (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: bin_op: or test_items: - flag: "--disable-admission-plugins" compare: op: nothave value: "NamespaceLifecycle" - flag: "--disable-admission-plugins" set: false remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the --disable-admission-plugins parameter to ensure it does not include NamespaceLifecycle. scored: true - id: 1.2.14 text: "Ensure that the admission control plugin NodeRestriction is set (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--enable-admission-plugins" compare: op: has value: "NodeRestriction" remediation: | Follow the Kubernetes documentation and configure NodeRestriction plug-in on kubelets. Then, edit the API server pod specification file $apiserverconf on the control plane node and set the --enable-admission-plugins parameter to a value that includes NodeRestriction. --enable-admission-plugins=...,NodeRestriction,... scored: true - id: 1.2.15 text: "Ensure that the --profiling argument is set to false (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--profiling" compare: op: eq value: false remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the below parameter. --profiling=false scored: true - id: 1.2.16 text: "Ensure that the --audit-log-path argument is set (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--audit-log-path" remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the --audit-log-path parameter to a suitable path and file where you would like audit logs to be written, for example, --audit-log-path=/var/log/apiserver/audit.log scored: true - id: 1.2.17 text: "Ensure that the --audit-log-maxage argument is set to 30 or as appropriate (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--audit-log-maxage" compare: op: gte value: 30 remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the --audit-log-maxage parameter to 30 or as an appropriate number of days, for example, --audit-log-maxage=30 scored: true - id: 1.2.18 text: "Ensure that the --audit-log-maxbackup argument is set to 10 or as appropriate (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--audit-log-maxbackup" compare: op: gte value: 10 remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the --audit-log-maxbackup parameter to 10 or to an appropriate value. For example, --audit-log-maxbackup=10 scored: true - id: 1.2.19 text: "Ensure that the --audit-log-maxsize argument is set to 100 or as appropriate (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--audit-log-maxsize" compare: op: gte value: 100 remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the --audit-log-maxsize parameter to an appropriate size in MB. For example, to set it as 100 MB, --audit-log-maxsize=100 scored: true - id: 1.2.20 text: "Ensure that the --request-timeout argument is set as appropriate (Manual)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" type: manual remediation: | Edit the API server pod specification file $apiserverconf and set the below parameter as appropriate and if needed. For example, --request-timeout=300s scored: false - id: 1.2.21 text: "Ensure that the --service-account-lookup argument is set to true (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: bin_op: or test_items: - flag: "--service-account-lookup" set: false - flag: "--service-account-lookup" compare: op: eq value: true remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the below parameter. --service-account-lookup=true Alternatively, you can delete the --service-account-lookup parameter from this file so that the default takes effect. scored: true - id: 1.2.22 text: "Ensure that the --service-account-key-file argument is set as appropriate (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--service-account-key-file" remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the --service-account-key-file parameter to the public key file for service accounts. For example, --service-account-key-file= scored: true - id: 1.2.23 text: "Ensure that the --etcd-certfile and --etcd-keyfile arguments are set as appropriate (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: bin_op: and test_items: - flag: "--etcd-certfile" - flag: "--etcd-keyfile" remediation: | Follow the Kubernetes documentation and set up the TLS connection between the apiserver and etcd. Then, edit the API server pod specification file $apiserverconf on the control plane node and set the etcd certificate and key file parameters. --etcd-certfile= --etcd-keyfile= scored: true - id: 1.2.24 text: "Ensure that the --tls-cert-file and --tls-private-key-file arguments are set as appropriate (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: bin_op: and test_items: - flag: "--tls-cert-file" - flag: "--tls-private-key-file" remediation: | Follow the Kubernetes documentation and set up the TLS connection on the apiserver. Then, edit the API server pod specification file $apiserverconf on the control plane node and set the TLS certificate and private key file parameters. --tls-cert-file= --tls-private-key-file= scored: true - id: 1.2.25 text: "Ensure that the --client-ca-file argument is set as appropriate (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--client-ca-file" remediation: | Follow the Kubernetes documentation and set up the TLS connection on the apiserver. Then, edit the API server pod specification file $apiserverconf on the control plane node and set the client certificate authority file. --client-ca-file= scored: true - id: 1.2.26 text: "Ensure that the --etcd-cafile argument is set as appropriate (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--etcd-cafile" remediation: | Follow the Kubernetes documentation and set up the TLS connection between the apiserver and etcd. Then, edit the API server pod specification file $apiserverconf on the control plane node and set the etcd certificate authority file parameter. --etcd-cafile= scored: true - id: 1.2.27 text: "Ensure that the --encryption-provider-config argument is set as appropriate (Manual)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--encryption-provider-config" remediation: | Follow the Kubernetes documentation and configure a EncryptionConfig file. Then, edit the API server pod specification file $apiserverconf on the control plane node and set the --encryption-provider-config parameter to the path of that file. For example, --encryption-provider-config= scored: false - id: 1.2.28 text: "Ensure that encryption providers are appropriately configured (Manual)" audit: | ENCRYPTION_PROVIDER_CONFIG=$(ps -ef | grep $apiserverbin | grep -- --encryption-provider-config | sed 's%.*encryption-provider-config[= ]\([^ ]*\).*%\1%') if test -e $ENCRYPTION_PROVIDER_CONFIG; then grep -A1 'providers:' $ENCRYPTION_PROVIDER_CONFIG | tail -n1 | grep -o "[A-Za-z]*" | sed 's/^/provider=/'; fi tests: test_items: - flag: "provider" compare: op: valid_elements value: "aescbc,kms,secretbox" remediation: | Follow the Kubernetes documentation and configure a EncryptionConfig file. In this file, choose aescbc, kms or secretbox as the encryption provider. scored: false - id: 1.2.29 text: "Ensure that the API Server only makes use of Strong Cryptographic Ciphers (Manual)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--tls-cipher-suites" compare: op: valid_elements value: "TLS_AES_128_GCM_SHA256,TLS_AES_256_GCM_SHA384,TLS_CHACHA20_POLY1305_SHA256,TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256" remediation: | Edit the API server pod specification file /etc/kubernetes/manifests/kube-apiserver.yaml on the control plane node and set the below parameter. --tls-cipher-suites=TLS_AES_128_GCM_SHA256,TLS_AES_256_GCM_SHA384,TLS_CHACHA20_POLY1305_SHA256, TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 scored: false - id: 1.3 text: "Controller Manager" checks: - id: 1.3.1 text: "Ensure that the --terminated-pod-gc-threshold argument is set as appropriate (Manual)" audit: "/bin/ps -ef | grep $controllermanagerbin | grep -v grep" tests: test_items: - flag: "--terminated-pod-gc-threshold" remediation: | Edit the Controller Manager pod specification file $controllermanagerconf on the control plane node and set the --terminated-pod-gc-threshold to an appropriate threshold, for example, --terminated-pod-gc-threshold=10 scored: false - id: 1.3.2 text: "Ensure that the --profiling argument is set to false (Automated)" audit: "/bin/ps -ef | grep $controllermanagerbin | grep -v grep" tests: test_items: - flag: "--profiling" compare: op: eq value: false remediation: | Edit the Controller Manager pod specification file $controllermanagerconf on the control plane node and set the below parameter. --profiling=false scored: true - id: 1.3.3 text: "Ensure that the --use-service-account-credentials argument is set to true (Automated)" audit: "/bin/ps -ef | grep $controllermanagerbin | grep -v grep" tests: test_items: - flag: "--use-service-account-credentials" compare: op: noteq value: false remediation: | Edit the Controller Manager pod specification file $controllermanagerconf on the control plane node to set the below parameter. --use-service-account-credentials=true scored: true - id: 1.3.4 text: "Ensure that the --service-account-private-key-file argument is set as appropriate (Automated)" audit: "/bin/ps -ef | grep $controllermanagerbin | grep -v grep" tests: test_items: - flag: "--service-account-private-key-file" remediation: | Edit the Controller Manager pod specification file $controllermanagerconf on the control plane node and set the --service-account-private-key-file parameter to the private key file for service accounts. --service-account-private-key-file= scored: true - id: 1.3.5 text: "Ensure that the --root-ca-file argument is set as appropriate (Automated)" audit: "/bin/ps -ef | grep $controllermanagerbin | grep -v grep" tests: test_items: - flag: "--root-ca-file" remediation: | Edit the Controller Manager pod specification file $controllermanagerconf on the control plane node and set the --root-ca-file parameter to the certificate bundle file`. --root-ca-file= scored: true - id: 1.3.6 text: "Ensure that the RotateKubeletServerCertificate argument is set to true (Automated)" audit: "/bin/ps -ef | grep $controllermanagerbin | grep -v grep" tests: bin_op: or test_items: - flag: "--feature-gates" compare: op: nothave value: "RotateKubeletServerCertificate=false" set: true - flag: "--feature-gates" set: false remediation: | Edit the Controller Manager pod specification file $controllermanagerconf on the control plane node and set the --feature-gates parameter to include RotateKubeletServerCertificate=true. --feature-gates=RotateKubeletServerCertificate=true scored: true - id: 1.3.7 text: "Ensure that the --bind-address argument is set to 127.0.0.1 (Automated)" audit: "/bin/ps -ef | grep $controllermanagerbin | grep -v grep" tests: bin_op: or test_items: - flag: "--bind-address" compare: op: eq value: "127.0.0.1" - flag: "--bind-address" set: false remediation: | Edit the Controller Manager pod specification file $controllermanagerconf on the control plane node and ensure the correct value for the --bind-address parameter scored: true - id: 1.4 text: "Scheduler" checks: - id: 1.4.1 text: "Ensure that the --profiling argument is set to false (Automated)" audit: "/bin/ps -ef | grep $schedulerbin | grep -v grep" tests: test_items: - flag: "--profiling" compare: op: eq value: false remediation: | Edit the Scheduler pod specification file $schedulerconf file on the control plane node and set the below parameter. --profiling=false scored: true - id: 1.4.2 text: "Ensure that the --bind-address argument is set to 127.0.0.1 (Automated)" audit: "/bin/ps -ef | grep $schedulerbin | grep -v grep" tests: bin_op: or test_items: - flag: "--bind-address" compare: op: eq value: "127.0.0.1" - flag: "--bind-address" set: false remediation: | Edit the Scheduler pod specification file $schedulerconf on the control plane node and ensure the correct value for the --bind-address parameter scored: true ================================================ FILE: cfg/cis-1.10/node.yaml ================================================ --- controls: version: "cis-1.10" id: 4 text: "Worker Node Security Configuration" type: "node" groups: - id: 4.1 text: "Worker Node Configuration Files" checks: - id: 4.1.1 text: "Ensure that the kubelet service file permissions are set to 600 or more restrictive (Automated)" audit: '/bin/sh -c ''if test -e $kubeletsvc; then stat -c permissions=%a $kubeletsvc; fi'' ' tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Run the below command (based on the file location on your system) on the each worker node. For example, chmod 600 $kubeletsvc scored: true - id: 4.1.2 text: "Ensure that the kubelet service file ownership is set to root:root (Automated)" audit: '/bin/sh -c "if test -e $kubeletsvc; then stat -c %U:%G $kubeletsvc; else echo \"File not found\"; fi"' tests: bin_op: or test_items: - flag: root:root - flag: "File not found" remediation: | Run the below command (based on the file location on your system) on the each worker node. For example, chown root:root $kubeletsvc scored: true - id: 4.1.3 text: "If proxy kubeconfig file exists ensure permissions are set to 600 or more restrictive (Manual)" audit: '/bin/sh -c ''if test -e $proxykubeconfig; then stat -c permissions=%a $proxykubeconfig; fi'' ' tests: bin_op: or test_items: - flag: "permissions" set: true compare: op: bitmask value: "600" remediation: | Run the below command (based on the file location on your system) on the each worker node. For example, chmod 600 $proxykubeconfig scored: false - id: 4.1.4 text: "If proxy kubeconfig file exists ensure ownership is set to root:root (Manual)" audit: '/bin/sh -c ''if test -e $proxykubeconfig; then stat -c %U:%G $proxykubeconfig; fi'' ' tests: bin_op: or test_items: - flag: root:root remediation: | Run the below command (based on the file location on your system) on the each worker node. For example, chown root:root $proxykubeconfig scored: false - id: 4.1.5 text: "Ensure that the --kubeconfig kubelet.conf file permissions are set to 600 or more restrictive (Automated)" audit: '/bin/sh -c ''if test -e $kubeletkubeconfig; then stat -c permissions=%a $kubeletkubeconfig; fi'' ' tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Run the below command (based on the file location on your system) on the each worker node. For example, chmod 600 $kubeletkubeconfig scored: true - id: 4.1.6 text: "Ensure that the --kubeconfig kubelet.conf file ownership is set to root:root (Automated)" audit: '/bin/sh -c ''if test -e $kubeletkubeconfig; then stat -c %U:%G $kubeletkubeconfig; fi'' ' tests: test_items: - flag: root:root remediation: | Run the below command (based on the file location on your system) on the each worker node. For example, chown root:root $kubeletkubeconfig scored: true - id: 4.1.7 text: "Ensure that the certificate authorities file permissions are set to 600 or more restrictive (Manual)" audit: | CAFILE=$(ps -ef | grep kubelet | grep -v apiserver | grep -- --client-ca-file= | awk -F '--client-ca-file=' '{print $2}' | awk '{print $1}' | uniq) if test -z $CAFILE; then CAFILE=$kubeletcafile; fi if test -e $CAFILE; then stat -c permissions=%a $CAFILE; fi tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Run the following command to modify the file permissions of the --client-ca-file chmod 600 scored: false - id: 4.1.8 text: "Ensure that the client certificate authorities file ownership is set to root:root (Manual)" audit: | CAFILE=$(ps -ef | grep kubelet | grep -v apiserver | grep -- --client-ca-file= | awk -F '--client-ca-file=' '{print $2}' | awk '{print $1}' | uniq) if test -z $CAFILE; then CAFILE=$kubeletcafile; fi if test -e $CAFILE; then stat -c %U:%G $CAFILE; fi tests: test_items: - flag: root:root compare: op: eq value: root:root remediation: | Run the following command to modify the ownership of the --client-ca-file. chown root:root scored: false - id: 4.1.9 text: "If the kubelet config.yaml configuration file is being used validate permissions set to 600 or more restrictive (Automated)" audit: '/bin/sh -c ''if test -e $kubeletconf; then stat -c permissions=%a $kubeletconf; fi'' ' tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Run the following command (using the config file location identified in the Audit step) chmod 600 $kubeletconf scored: true - id: 4.1.10 text: "If the kubelet config.yaml configuration file is being used validate file ownership is set to root:root (Automated)" audit: '/bin/sh -c ''if test -e $kubeletconf; then stat -c %U:%G $kubeletconf; fi'' ' tests: test_items: - flag: root:root remediation: | Run the following command (using the config file location identified in the Audit step) chown root:root $kubeletconf scored: true - id: 4.2 text: "Kubelet" checks: - id: 4.2.1 text: "Ensure that the --anonymous-auth argument is set to false (Automated)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: "--anonymous-auth" path: '{.authentication.anonymous.enabled}' compare: op: eq value: false remediation: | If using a Kubelet config file, edit the file to set `authentication: anonymous: enabled` to `false`. If using executable arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_SYSTEM_PODS_ARGS variable. `--anonymous-auth=false` Based on your system, restart the kubelet service. For example, systemctl daemon-reload systemctl restart kubelet.service scored: true - id: 4.2.2 text: "Ensure that the --authorization-mode argument is not set to AlwaysAllow (Automated)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --authorization-mode path: '{.authorization.mode}' compare: op: nothave value: AlwaysAllow remediation: | If using a Kubelet config file, edit the file to set `authorization.mode` to Webhook. If using executable arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_AUTHZ_ARGS variable. --authorization-mode=Webhook Based on your system, restart the kubelet service. For example, systemctl daemon-reload systemctl restart kubelet.service scored: true - id: 4.2.3 text: "Ensure that the --client-ca-file argument is set as appropriate (Automated)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --client-ca-file path: '{.authentication.x509.clientCAFile}' remediation: | If using a Kubelet config file, edit the file to set `authentication.x509.clientCAFile` to the location of the client CA file. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_AUTHZ_ARGS variable. --client-ca-file= Based on your system, restart the kubelet service. For example, systemctl daemon-reload systemctl restart kubelet.service scored: true - id: 4.2.4 text: "Verify that the --read-only-port argument is set to 0 (Manual)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: bin_op: or test_items: - flag: "--read-only-port" path: '{.readOnlyPort}' compare: op: eq value: 0 - flag: "--read-only-port" path: '{.readOnlyPort}' set: false remediation: | If using a Kubelet config file, edit the file to set `readOnlyPort` to 0. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_SYSTEM_PODS_ARGS variable. --read-only-port=0 Based on your system, restart the kubelet service. For example, systemctl daemon-reload systemctl restart kubelet.service scored: false - id: 4.2.5 text: "Ensure that the --streaming-connection-idle-timeout argument is not set to 0 (Manual)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --streaming-connection-idle-timeout path: '{.streamingConnectionIdleTimeout}' compare: op: noteq value: 0 - flag: --streaming-connection-idle-timeout path: '{.streamingConnectionIdleTimeout}' set: false bin_op: or remediation: | If using a Kubelet config file, edit the file to set `streamingConnectionIdleTimeout` to a value other than 0. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_SYSTEM_PODS_ARGS variable. --streaming-connection-idle-timeout=5m Based on your system, restart the kubelet service. For example, systemctl daemon-reload systemctl restart kubelet.service scored: false - id: 4.2.6 text: "Ensure that the --make-iptables-util-chains argument is set to true (Automated)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --make-iptables-util-chains path: '{.makeIPTablesUtilChains}' compare: op: eq value: true - flag: --make-iptables-util-chains path: '{.makeIPTablesUtilChains}' set: false bin_op: or remediation: | If using a Kubelet config file, edit the file to set `makeIPTablesUtilChains` to `true`. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and remove the --make-iptables-util-chains argument from the KUBELET_SYSTEM_PODS_ARGS variable. Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: true - id: 4.2.7 text: "Ensure that the --hostname-override argument is not set (Manual)" # This is one of those properties that can only be set as a command line argument. # To check if the property is set as expected, we need to parse the kubelet command # instead reading the Kubelet Configuration file. audit: "/bin/ps -fC $kubeletbin" tests: test_items: - flag: --hostname-override set: false remediation: | Edit the kubelet service file $kubeletsvc on each worker node and remove the --hostname-override argument from the KUBELET_SYSTEM_PODS_ARGS variable. Based on your system, restart the kubelet service. For example, systemctl daemon-reload systemctl restart kubelet.service scored: false - id: 4.2.8 text: "Ensure that the eventRecordQPS argument is set to a level which ensures appropriate event capture (Manual)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --event-qps path: '{.eventRecordQPS}' compare: op: gte value: 0 - flag: --event-qps path: '{.eventRecordQPS}' set: false bin_op: or remediation: | If using a Kubelet config file, edit the file to set `eventRecordQPS` to an appropriate level. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_SYSTEM_PODS_ARGS variable. Based on your system, restart the kubelet service. For example, systemctl daemon-reload systemctl restart kubelet.service scored: false - id: 4.2.9 text: "Ensure that the --tls-cert-file and --tls-private-key-file arguments are set as appropriate (Manual)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --tls-cert-file path: '{.tlsCertFile}' - flag: --tls-private-key-file path: '{.tlsPrivateKeyFile}' remediation: | If using a Kubelet config file, edit the file to set `tlsCertFile` to the location of the certificate file to use to identify this Kubelet, and `tlsPrivateKeyFile` to the location of the corresponding private key file. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameters in KUBELET_CERTIFICATE_ARGS variable. --tls-cert-file= --tls-private-key-file= Based on your system, restart the kubelet service. For example, systemctl daemon-reload systemctl restart kubelet.service scored: false - id: 4.2.10 text: "Ensure that the --rotate-certificates argument is not set to false (Automated)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --rotate-certificates path: '{.rotateCertificates}' compare: op: eq value: true - flag: --rotate-certificates path: '{.rotateCertificates}' set: false bin_op: or remediation: | If using a Kubelet config file, edit the file to add the line `rotateCertificates` to `true` or remove it altogether to use the default value. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and remove --rotate-certificates=false argument from the KUBELET_CERTIFICATE_ARGS variable. Based on your system, restart the kubelet service. For example, systemctl daemon-reload systemctl restart kubelet.service scored: true - id: 4.2.11 text: "Verify that the RotateKubeletServerCertificate argument is set to true (Manual)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: bin_op: or test_items: - flag: RotateKubeletServerCertificate path: '{.featureGates.RotateKubeletServerCertificate}' compare: op: nothave value: false - flag: RotateKubeletServerCertificate path: '{.featureGates.RotateKubeletServerCertificate}' set: false remediation: | Edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_CERTIFICATE_ARGS variable. --feature-gates=RotateKubeletServerCertificate=true Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: false - id: 4.2.12 text: "Ensure that the Kubelet only makes use of Strong Cryptographic Ciphers (Manual)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --tls-cipher-suites path: '{range .tlsCipherSuites[:]}{}{'',''}{end}' compare: op: valid_elements value: 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 remediation: | If using a Kubelet config file, edit the file to set `tlsCipherSuites` to 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 or to a subset of these values. If using executable arguments, edit the kubelet service file $kubeletsvc on each worker node and set the --tls-cipher-suites parameter as follows, or to a subset of these values. --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 Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: false - id: 4.2.13 text: "Ensure that a limit is set on pod PIDs (Manual)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --pod-max-pids path: '{.podPidsLimit}' remediation: | Decide on an appropriate level for this parameter and set it, either via the --pod-max-pids command line parameter or the PodPidsLimit configuration file setting. scored: false - id: 4.3 text: "kube-proxy" checks: - id: 4.3.1 text: "Ensure that the kube-proxy metrics service is bound to localhost (Automated)" audit: "/bin/ps -fC $proxybin" audit_config: "/bin/sh -c 'if test -e $proxykubeconfig; then cat $proxykubeconfig; fi'" tests: bin_op: or test_items: - flag: "--metrics-bind-address" path: '{.metricsBindAddress}' compare: op: has value: "127.0.0.1" - flag: "--metrics-bind-address" path: '{.metricsBindAddress}' set: false remediation: | Modify or remove any values which bind the metrics service to a non-localhost address. The default value is 127.0.0.1:10249. scored: true ================================================ FILE: cfg/cis-1.10/policies.yaml ================================================ --- controls: version: "cis-1.10" id: 5 text: "Kubernetes Policies" type: "policies" groups: - id: 5.1 text: "RBAC and Service Accounts" checks: - id: 5.1.1 text: "Ensure that the cluster-admin role is only used where required (Automated)" audit: | kubectl get clusterrolebindings -o=custom-columns=NAME:.metadata.name,ROLE:.roleRef.name,SUBJECT:.subjects[*].name --no-headers | while read -r role_name role_binding subject do if [[ "${role_name}" != "cluster-admin" && "${role_binding}" == "cluster-admin" ]]; then is_compliant="false" else is_compliant="true" fi; echo "**role_name: ${role_name} role_binding: ${role_binding} subject: ${subject} is_compliant: ${is_compliant}" done use_multiple_values: true tests: test_items: - flag: "is_compliant" compare: op: eq value: true remediation: | Identify all clusterrolebindings to the cluster-admin role. Check if they are used and if they need this role or if they could use a role with fewer privileges. Where possible, first bind users to a lower privileged role and then remove the clusterrolebinding to the cluster-admin role : kubectl delete clusterrolebinding [name] Condition: is_compliant is false if rolename is not cluster-admin and rolebinding is cluster-admin. scored: false - id: 5.1.2 text: "Minimize access to secrets (Automated)" audit: "echo \"canGetListWatchSecretsAsSystemAuthenticated: $(kubectl auth can-i get,list,watch secrets --all-namespaces --as=system:authenticated)\"" tests: test_items: - flag: "canGetListWatchSecretsAsSystemAuthenticated" compare: op: eq value: no remediation: | Where possible, remove get, list and watch access to Secret objects in the cluster. scored: false - id: 5.1.3 text: "Minimize wildcard use in Roles and ClusterRoles (Automated)" audit: | # Check Roles kubectl get roles --all-namespaces -o custom-columns=ROLE_NAMESPACE:.metadata.namespace,ROLE_NAME:.metadata.name --no-headers | while read -r role_namespace role_name do role_rules=$(kubectl get role -n "${role_namespace}" "${role_name}" -o=json | jq -c '.rules') if echo "${role_rules}" | grep -q "\[\"\*\"\]"; then role_is_compliant="false" else role_is_compliant="true" fi; echo "**role_name: ${role_name} role_namespace: ${role_namespace} role_rules: ${role_rules} role_is_compliant: ${role_is_compliant}" done # Check ClusterRoles kubectl get clusterroles -o custom-columns=CLUSTERROLE_NAME:.metadata.name --no-headers | while read -r clusterrole_name do clusterrole_rules=$(kubectl get clusterrole "${clusterrole_name}" -o=json | jq -c '.rules') if echo "${clusterrole_rules}" | grep -q "\[\"\*\"\]"; then clusterrole_is_compliant="false" else clusterrole_is_compliant="true" fi; echo "**clusterrole_name: ${clusterrole_name} clusterrole_rules: ${clusterrole_rules} clusterrole_is_compliant: ${clusterrole_is_compliant}" done use_multiple_values: true tests: bin_op: or test_items: - flag: "role_is_compliant" compare: op: eq value: true set: true - flag: "clusterrole_is_compliant" compare: op: eq value: true set: true remediation: | Where possible replace any use of wildcards ["*"] in roles and clusterroles with specific objects or actions. Condition: role_is_compliant is false if ["*"] is found in rules. Condition: clusterrole_is_compliant is false if ["*"] is found in rules. scored: false - id: 5.1.4 text: "Minimize access to create pods (Automated)" audit: | echo "canCreatePodsAsSystemAuthenticated: $(kubectl auth can-i create pods --all-namespaces --as=system:authenticated)" tests: test_items: - flag: "canCreatePodsAsSystemAuthenticated" compare: op: eq value: no remediation: | Where possible, remove create access to pod objects in the cluster. scored: false - id: 5.1.5 text: "Ensure that default service accounts are not actively used (Automated)" audit: | kubectl get serviceaccount --all-namespaces --field-selector metadata.name=default -o=json | jq -r '.items[] | " namespace: \(.metadata.namespace), kind: \(.kind), name: \(.metadata.name), automountServiceAccountToken: \(.automountServiceAccountToken | if . == null then "notset" else . end )"' | xargs -L 1 use_multiple_values: true tests: test_items: - flag: "automountServiceAccountToken" compare: op: eq value: false set: true remediation: | Create explicit service accounts wherever a Kubernetes workload requires specific access to the Kubernetes API server. Modify the configuration of each default service account to include this value `automountServiceAccountToken: false`. scored: false - id: 5.1.6 text: "Ensure that Service Account Tokens are only mounted where necessary (Automated)" audit: | kubectl get pods --all-namespaces -o custom-columns=POD_NAMESPACE:.metadata.namespace,POD_NAME:.metadata.name,POD_SERVICE_ACCOUNT:.spec.serviceAccount,POD_IS_AUTOMOUNTSERVICEACCOUNTTOKEN:.spec.automountServiceAccountToken --no-headers | while read -r pod_namespace pod_name pod_service_account pod_is_automountserviceaccounttoken do # Retrieve automountServiceAccountToken's value for ServiceAccount and Pod, set to notset if null or . svacc_is_automountserviceaccounttoken=$(kubectl get serviceaccount -n "${pod_namespace}" "${pod_service_account}" -o json | jq -r '.automountServiceAccountToken' | sed -e 's//notset/g' -e 's/null/notset/g') pod_is_automountserviceaccounttoken=$(echo "${pod_is_automountserviceaccounttoken}" | sed -e 's//notset/g' -e 's/null/notset/g') if [ "${svacc_is_automountserviceaccounttoken}" = "false" ] && ( [ "${pod_is_automountserviceaccounttoken}" = "false" ] || [ "${pod_is_automountserviceaccounttoken}" = "notset" ] ); then is_compliant="true" elif [ "${svacc_is_automountserviceaccounttoken}" = "true" ] && [ "${pod_is_automountserviceaccounttoken}" = "false" ]; then is_compliant="true" else is_compliant="false" fi echo "**namespace: ${pod_namespace} pod_name: ${pod_name} service_account: ${pod_service_account} pod_is_automountserviceaccounttoken: ${pod_is_automountserviceaccounttoken} svacc_is_automountServiceAccountToken: ${svacc_is_automountserviceaccounttoken} is_compliant: ${is_compliant}" done use_multiple_values: true tests: test_items: - flag: "is_compliant" compare: op: eq value: true remediation: | Modify the definition of ServiceAccounts and Pods which do not need to mount service account tokens to disable it, with `automountServiceAccountToken: false`. If both the ServiceAccount and the Pod's .spec specify a value for automountServiceAccountToken, the Pod spec takes precedence. Condition: Pod is_compliant to true when - ServiceAccount is automountServiceAccountToken: false and Pod is automountServiceAccountToken: false or notset - ServiceAccount is automountServiceAccountToken: true notset and Pod is automountServiceAccountToken: false scored: false - id: 5.1.7 text: "Avoid use of system:masters group (Manual)" type: "manual" remediation: | Remove the system:masters group from all users in the cluster. scored: false - id: 5.1.8 text: "Limit use of the Bind, Impersonate and Escalate permissions in the Kubernetes cluster (Manual)" type: "manual" remediation: | Where possible, remove the impersonate, bind and escalate rights from subjects. scored: false - id: 5.1.9 text: "Minimize access to create persistent volumes (Manual)" type: "manual" remediation: | Where possible, remove create access to PersistentVolume objects in the cluster. scored: false - id: 5.1.10 text: "Minimize access to the proxy sub-resource of nodes (Manual)" type: "manual" remediation: | Where possible, remove access to the proxy sub-resource of node objects. scored: false - id: 5.1.11 text: "Minimize access to the approval sub-resource of certificatesigningrequests objects (Manual)" type: "manual" remediation: | Where possible, remove access to the approval sub-resource of certificatesigningrequests objects. scored: false - id: 5.1.12 text: "Minimize access to webhook configuration objects (Manual)" type: "manual" remediation: | Where possible, remove access to the validatingwebhookconfigurations or mutatingwebhookconfigurations objects scored: false - id: 5.1.13 text: "Minimize access to the service account token creation (Manual)" type: "manual" remediation: | Where possible, remove access to the token sub-resource of serviceaccount objects. scored: false - id: 5.2 text: "Pod Security Standards" checks: - id: 5.2.1 text: "Ensure that the cluster has at least one active policy control mechanism in place (Manual)" type: "manual" remediation: | Ensure that either Pod Security Admission or an external policy control system is in place for every namespace which contains user workloads. scored: false - id: 5.2.2 text: "Minimize the admission of privileged containers (Manual)" audit: | kubectl get pods --all-namespaces -o custom-columns=POD_NAME:.metadata.name,POD_NAMESPACE:.metadata.namespace --no-headers | while read -r pod_name pod_namespace do # Retrieve container(s) for each Pod. kubectl get pod "${pod_name}" --namespace "${pod_namespace}" -o json | jq -c '.spec.containers[]' | while read -r container do # Retrieve container's name. container_name=$(echo ${container} | jq -r '.name') # Retrieve container's .securityContext.privileged value. container_privileged=$(echo ${container} | jq -r '.securityContext.privileged' | sed -e 's/null/notset/g') if [ "${container_privileged}" = "false" ] || [ "${container_privileged}" = "notset" ] ; then echo "***pod_name: ${pod_name} container_name: ${container_name} pod_namespace: ${pod_namespace} is_container_privileged: ${container_privileged} is_compliant: true" else echo "***pod_name: ${pod_name} container_name: ${container_name} pod_namespace: ${pod_namespace} is_container_privileged: ${container_privileged} is_compliant: false" fi done done use_multiple_values: true tests: test_items: - flag: "is_compliant" compare: op: eq value: true remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of privileged containers. Audit: the audit list all pods' containers to retrieve their .securityContext.privileged value. Condition: is_compliant is false if container's `.securityContext.privileged` is set to `true`. Default: by default, there are no restrictions on the creation of privileged containers. scored: false - id: 5.2.3 text: "Minimize the admission of containers wishing to share the host process ID namespace (Manual)" audit: | kubectl get pods --all-namespaces -o custom-columns=POD_NAME:.metadata.name,POD_NAMESPACE:.metadata.namespace --no-headers | while read -r pod_name pod_namespace do # Retrieve spec.hostPID for each pod. pod_hostpid=$(kubectl get pod "${pod_name}" --namespace "${pod_namespace}" -o jsonpath='{.spec.hostPID}' 2>/dev/null) if [ -z "${pod_hostpid}" ]; then pod_hostpid="false" echo "***pod_name: ${pod_name} pod_namespace: ${pod_namespace} is_pod_hostpid: ${pod_hostpid} is_compliant: true" else echo "***pod_name: ${pod_name} pod_namespace: ${pod_namespace} is_pod_hostpid: ${pod_hostpid} is_compliant: false" fi done use_multiple_values: true tests: test_items: - flag: "is_compliant" compare: op: eq value: true remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of `hostPID` containers. Audit: the audit retrieves each Pod' spec.hostPID. Condition: is_compliant is false if Pod's spec.hostPID is set to `true`. Default: by default, there are no restrictions on the creation of hostPID containers. scored: false - id: 5.2.4 text: "Minimize the admission of containers wishing to share the host IPC namespace (Manual)" audit: | kubectl get pods --all-namespaces -o custom-columns=POD_NAME:.metadata.name,POD_NAMESPACE:.metadata.namespace --no-headers | while read -r pod_name pod_namespace do # Retrieve spec.hostIPC for each pod. pod_hostipc=$(kubectl get pod "${pod_name}" --namespace "${pod_namespace}" -o jsonpath='{.spec.hostIPC}' 2>/dev/null) if [ -z "${pod_hostipc}" ]; then pod_hostipc="false" echo "***pod_name: ${pod_name} pod_namespace: ${pod_namespace} is_pod_hostipc: ${pod_hostipc} is_compliant: true" else echo "***pod_name: ${pod_name} pod_namespace: ${pod_namespace} is_pod_hostipc: ${pod_hostipc} is_compliant: false" fi done use_multiple_values: true tests: test_items: - flag: "is_compliant" compare: op: eq value: true remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of `hostIPC` containers. Audit: the audit retrieves each Pod' spec.IPC. Condition: is_compliant is false if Pod's spec.hostIPC is set to `true`. Default: by default, there are no restrictions on the creation of hostIPC containers. scored: false - id: 5.2.5 text: "Minimize the admission of containers wishing to share the host network namespace (Manual)" audit: | kubectl get pods --all-namespaces -o custom-columns=POD_NAME:.metadata.name,POD_NAMESPACE:.metadata.namespace --no-headers | while read -r pod_name pod_namespace do # Retrieve spec.hostNetwork for each pod. pod_hostnetwork=$(kubectl get pod "${pod_name}" --namespace "${pod_namespace}" -o jsonpath='{.spec.hostNetwork}' 2>/dev/null) if [ -z "${pod_hostnetwork}" ]; then pod_hostnetwork="false" echo "***pod_name: ${pod_name} pod_namespace: ${pod_namespace} is_pod_hostnetwork: ${pod_hostnetwork} is_compliant: true" else echo "***pod_name: ${pod_name} pod_namespace: ${pod_namespace} is_pod_hostnetwork: ${pod_hostnetwork} is_compliant: false" fi done use_multiple_values: true tests: test_items: - flag: "is_compliant" compare: op: eq value: true remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of `hostNetwork` containers. Audit: the audit retrieves each Pod' spec.hostNetwork. Condition: is_compliant is false if Pod's spec.hostNetwork is set to `true`. Default: by default, there are no restrictions on the creation of hostNetwork containers. scored: false - id: 5.2.6 text: "Minimize the admission of containers with allowPrivilegeEscalation (Manual)" audit: | kubectl get pods --all-namespaces -o custom-columns=POD_NAME:.metadata.name,POD_NAMESPACE:.metadata.namespace --no-headers | while read -r pod_name pod_namespace do # Retrieve container(s) for each Pod. kubectl get pod "${pod_name}" --namespace "${pod_namespace}" -o json | jq -c '.spec.containers[]' | while read -r container do # Retrieve container's name container_name=$(echo ${container} | jq -r '.name') # Retrieve container's .securityContext.allowPrivilegeEscalation container_allowprivesc=$(echo ${container} | jq -r '.securityContext.allowPrivilegeEscalation' | sed -e 's/null/notset/g') if [ "${container_allowprivesc}" = "false" ] || [ "${container_allowprivesc}" = "notset" ]; then echo "***pod_name: ${pod_name} container_name: ${container_name} pod_namespace: ${pod_namespace} is_container_allowprivesc: ${container_allowprivesc} is_compliant: true" else echo "***pod_name: ${pod_name} container_name: ${container_name} pod_namespace: ${pod_namespace} is_container_allowprivesc: ${container_allowprivesc} is_compliant: false" fi done done use_multiple_values: true tests: test_items: - flag: "is_compliant" compare: op: eq value: true remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of containers with `.securityContext.allowPrivilegeEscalation` set to `true`. Audit: the audit retrieves each Pod's container(s) `.securityContext.allowPrivilegeEscalation`. Condition: is_compliant is false if container's `.securityContext.allowPrivilegeEscalation` is set to `true`. Default: If notset, privilege escalation is allowed (default to true). However if PSP/PSA is used with a `restricted` profile, privilege escalation is explicitly disallowed unless configured otherwise. scored: false - id: 5.2.7 text: "Minimize the admission of root containers (Manual)" type: "manual" remediation: | Create a policy for each namespace in the cluster, ensuring that either `MustRunAsNonRoot` or `MustRunAs` with the range of UIDs not including 0, is set. scored: false - id: 5.2.8 text: "Minimize the admission of containers with the NET_RAW capability (Manual)" type: "manual" remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of containers with the `NET_RAW` capability. scored: false - id: 5.2.9 text: "Minimize the admission of containers with added capabilities (Manual)" audit: | kubectl get pods --all-namespaces -o custom-columns=POD_NAME:.metadata.name,POD_NAMESPACE:.metadata.namespace --no-headers | while read -r pod_name pod_namespace do # Retrieve container(s) for each Pod. kubectl get pod "${pod_name}" --namespace "${pod_namespace}" -o json | jq -c '.spec.containers[]' | while read -r container do # Retrieve container's name container_name=$(echo ${container} | jq -r '.name') # Retrieve container's added capabilities container_caps_add=$(echo ${container} | jq -r '.securityContext.capabilities.add' | sed -e 's/null/notset/g') # Set is_compliant to true by default. is_compliant=true caps_list="" if [ "${container_caps_add}" != "notset" ]; then # Loop through all caps and append caps_list, then set is_compliant to false. for cap in $(echo "${container_caps_add}" | jq -r '.[]'); do caps_list+="${cap}," is_compliant=false done # Remove trailing comma for the last list member. caps_list=${caps_list%,} fi if [ "${is_compliant}" = true ]; then echo "***pod_name: ${pod_name} container_name: ${container_name} pod_namespace: ${pod_namespace} container_caps_add: ${container_caps_add} is_compliant: true" else echo "***pod_name: ${pod_name} container_name: ${container_name} pod_namespace: ${pod_namespace} container_caps_add: ${caps_list} is_compliant: false" fi done done use_multiple_values: true tests: test_items: - flag: "is_compliant" compare: op: eq value: true remediation: | Ensure that `allowedCapabilities` is not present in policies for the cluster unless it is set to an empty array. Audit: the audit retrieves each Pod's container(s) added capabilities. Condition: is_compliant is false if added capabilities are added for a given container. Default: Containers run with a default set of capabilities as assigned by the Container Runtime. scored: false - id: 5.2.10 text: "Minimize the admission of containers with capabilities assigned (Manual)" type: "manual" remediation: | Review the use of capabilites in applications running on your cluster. Where a namespace contains applications which do not require any Linux capabities to operate consider adding a PSP which forbids the admission of containers which do not drop all capabilities. scored: false - id: 5.2.11 text: "Minimize the admission of Windows HostProcess containers (Manual)" type: "manual" remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of containers that have `.securityContext.windowsOptions.hostProcess` set to `true`. scored: false - id: 5.2.12 text: "Minimize the admission of HostPath volumes (Manual)" type: "manual" remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of containers with `hostPath` volumes. scored: false - id: 5.2.13 text: "Minimize the admission of containers which use HostPorts (Manual)" type: "manual" remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of containers which use `hostPort` sections. scored: false - id: 5.3 text: "Network Policies and CNI" checks: - id: 5.3.1 text: "Ensure that the CNI in use supports NetworkPolicies (Manual)" type: "manual" remediation: | If the CNI plugin in use does not support network policies, consideration should be given to making use of a different plugin, or finding an alternate mechanism for restricting traffic in the Kubernetes cluster. scored: false - id: 5.3.2 text: "Ensure that all Namespaces have NetworkPolicies defined (Manual)" type: "manual" remediation: | Follow the documentation and create NetworkPolicy objects as you need them. scored: false - id: 5.4 text: "Secrets Management" checks: - id: 5.4.1 text: "Prefer using Secrets as files over Secrets as environment variables (Manual)" type: "manual" remediation: | If possible, rewrite application code to read Secrets from mounted secret files, rather than from environment variables. scored: false - id: 5.4.2 text: "Consider external secret storage (Manual)" type: "manual" remediation: | Refer to the Secrets management options offered by your cloud provider or a third-party secrets management solution. scored: false - id: 5.5 text: "Extensible Admission Control" checks: - id: 5.5.1 text: "Configure Image Provenance using ImagePolicyWebhook admission controller (Manual)" type: "manual" remediation: | Follow the Kubernetes documentation and setup image provenance. scored: false - id: 5.7 text: "General Policies" checks: - id: 5.7.1 text: "Create administrative boundaries between resources using namespaces (Manual)" type: "manual" remediation: | Follow the documentation and create namespaces for objects in your deployment as you need them. scored: false - id: 5.7.2 text: "Ensure that the seccomp profile is set to docker/default in your Pod definitions (Manual)" type: "manual" remediation: | Use `securityContext` to enable the docker/default seccomp profile in your pod definitions. An example is as below: securityContext: seccompProfile: type: RuntimeDefault scored: false - id: 5.7.3 text: "Apply SecurityContext to your Pods and Containers (Manual)" type: "manual" remediation: | Follow the Kubernetes documentation and apply SecurityContexts to your Pods. For a suggested list of SecurityContexts, you may refer to the CIS Security Benchmark for Docker Containers. scored: false - id: 5.7.4 text: "The default namespace should not be used (Manual)" type: "manual" remediation: | Ensure that namespaces are created to allow for appropriate segregation of Kubernetes resources and that all new resources are created in a specific namespace. scored: false ================================================ FILE: cfg/cis-1.11/config.yaml ================================================ --- ## Version-specific settings that override the values in cfg/config.yaml ================================================ FILE: cfg/cis-1.11/controlplane.yaml ================================================ --- controls: version: "cis-1.11" id: 3 text: "Control Plane Configuration" type: "controlplane" groups: - id: 3.1 text: "Authentication and Authorization" checks: - id: 3.1.1 text: "Client certificate authentication should not be used for users (Manual)" type: "manual" remediation: | Alternative mechanisms provided by Kubernetes such as the use of OIDC should be implemented in place of client certificates. scored: false - id: 3.1.2 text: "Service account token authentication should not be used for users (Manual)" type: "manual" remediation: | Alternative mechanisms provided by Kubernetes such as the use of OIDC should be implemented in place of service account tokens. scored: false - id: 3.1.3 text: "Bootstrap token authentication should not be used for users (Manual)" type: "manual" remediation: | Alternative mechanisms provided by Kubernetes such as the use of OIDC should be implemented in place of bootstrap tokens. scored: false - id: 3.2 text: "Logging" checks: - id: 3.2.1 text: "Ensure that a minimal audit policy is created (Manual)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--audit-policy-file" set: true remediation: | Create an audit policy file for your cluster. scored: false - id: 3.2.2 text: "Ensure that the audit policy covers key security concerns (Manual)" type: "manual" remediation: | Review the audit policy provided for the cluster and ensure that it covers at least the following areas, - Access to Secrets managed by the cluster. Care should be taken to only log Metadata for requests to Secrets, ConfigMaps, and TokenReviews, in order to avoid risk of logging sensitive data. - Modification of Pod and Deployment objects. - Use of `pods/exec`, `pods/portforward`, `pods/proxy` and `services/proxy`. For most requests, minimally logging at the Metadata level is recommended (the most basic level of logging). scored: false ================================================ FILE: cfg/cis-1.11/etcd.yaml ================================================ --- controls: version: "cis-1.11" id: 2 text: "Etcd Node Configuration" type: "etcd" groups: - id: 2 text: "Etcd Node Configuration" checks: - id: 2.1 text: "Ensure that the --cert-file and --key-file arguments are set as appropriate (Automated)" audit: "/bin/ps -ef | /bin/grep $etcdbin | /bin/grep -v grep" tests: bin_op: and test_items: - flag: "--cert-file" env: "ETCD_CERT_FILE" - flag: "--key-file" env: "ETCD_KEY_FILE" remediation: | Follow the etcd service documentation and configure TLS encryption. Then, edit the etcd pod specification file /etc/kubernetes/manifests/etcd.yaml on the master node and set the below parameters. --cert-file= --key-file= scored: true - id: 2.2 text: "Ensure that the --client-cert-auth argument is set to true (Automated)" audit: "/bin/ps -ef | /bin/grep $etcdbin | /bin/grep -v grep" tests: test_items: - flag: "--client-cert-auth" env: "ETCD_CLIENT_CERT_AUTH" compare: op: eq value: true remediation: | Edit the etcd pod specification file $etcdconf on the master node and set the below parameter. --client-cert-auth="true" scored: true - id: 2.3 text: "Ensure that the --auto-tls argument is not set to true (Automated)" audit: "/bin/ps -ef | /bin/grep $etcdbin | /bin/grep -v grep" tests: bin_op: or test_items: - flag: "--auto-tls" env: "ETCD_AUTO_TLS" set: false - flag: "--auto-tls" env: "ETCD_AUTO_TLS" compare: op: eq value: false remediation: | Edit the etcd pod specification file $etcdconf on the master node and either remove the --auto-tls parameter or set it to false. --auto-tls=false scored: true - id: 2.4 text: "Ensure that the --peer-cert-file and --peer-key-file arguments are set as appropriate (Automated)" audit: "/bin/ps -ef | /bin/grep $etcdbin | /bin/grep -v grep" tests: bin_op: and test_items: - flag: "--peer-cert-file" env: "ETCD_PEER_CERT_FILE" - flag: "--peer-key-file" env: "ETCD_PEER_KEY_FILE" remediation: | Follow the etcd service documentation and configure peer TLS encryption as appropriate for your etcd cluster. Then, edit the etcd pod specification file $etcdconf on the master node and set the below parameters. --peer-client-file= --peer-key-file= scored: true - id: 2.5 text: "Ensure that the --peer-client-cert-auth argument is set to true (Automated)" audit: "/bin/ps -ef | /bin/grep $etcdbin | /bin/grep -v grep" tests: test_items: - flag: "--peer-client-cert-auth" env: "ETCD_PEER_CLIENT_CERT_AUTH" compare: op: eq value: true remediation: | Edit the etcd pod specification file $etcdconf on the master node and set the below parameter. --peer-client-cert-auth=true scored: true - id: 2.6 text: "Ensure that the --peer-auto-tls argument is not set to true (Automated)" audit: "/bin/ps -ef | /bin/grep $etcdbin | /bin/grep -v grep" tests: bin_op: or test_items: - flag: "--peer-auto-tls" env: "ETCD_PEER_AUTO_TLS" set: false - flag: "--peer-auto-tls" env: "ETCD_PEER_AUTO_TLS" compare: op: eq value: false remediation: | Edit the etcd pod specification file $etcdconf on the master node and either remove the --peer-auto-tls parameter or set it to false. --peer-auto-tls=false scored: true - id: 2.7 text: "Ensure that a unique Certificate Authority is used for etcd (Manual)" audit: "/bin/ps -ef | /bin/grep $etcdbin | /bin/grep -v grep" tests: test_items: - flag: "--trusted-ca-file" env: "ETCD_TRUSTED_CA_FILE" remediation: | [Manual test] Follow the etcd documentation and create a dedicated certificate authority setup for the etcd service. Then, edit the etcd pod specification file $etcdconf on the master node and set the below parameter. --trusted-ca-file= scored: false ================================================ FILE: cfg/cis-1.11/master.yaml ================================================ --- controls: version: "cis-1.11" id: 1 text: "Control Plane Security Configuration" type: "master" groups: - id: 1.1 text: "Control Plane Node Configuration Files" checks: - id: 1.1.1 text: "Ensure that the API server pod specification file permissions are set to 600 or more restrictive (Automated)" audit: "/bin/sh -c 'if test -e $apiserverconf; then stat -c permissions=%a $apiserverconf; fi'" tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chmod 600 $apiserverconf scored: true - id: 1.1.2 text: "Ensure that the API server pod specification file ownership is set to root:root (Automated)" audit: "/bin/sh -c 'if test -e $apiserverconf; then stat -c %U:%G $apiserverconf; fi'" tests: test_items: - flag: "root:root" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chown root:root $apiserverconf scored: true - id: 1.1.3 text: "Ensure that the controller manager pod specification file permissions are set to 600 or more restrictive (Automated)" audit: "/bin/sh -c 'if test -e $controllermanagerconf; then stat -c permissions=%a $controllermanagerconf; fi'" tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chmod 600 $controllermanagerconf scored: true - id: 1.1.4 text: "Ensure that the controller manager pod specification file ownership is set to root:root (Automated)" audit: "/bin/sh -c 'if test -e $controllermanagerconf; then stat -c %U:%G $controllermanagerconf; fi'" tests: test_items: - flag: "root:root" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chown root:root $controllermanagerconf scored: true - id: 1.1.5 text: "Ensure that the scheduler pod specification file permissions are set to 600 or more restrictive (Automated)" audit: "/bin/sh -c 'if test -e $schedulerconf; then stat -c permissions=%a $schedulerconf; fi'" tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chmod 600 $schedulerconf scored: true - id: 1.1.6 text: "Ensure that the scheduler pod specification file ownership is set to root:root (Automated)" audit: "/bin/sh -c 'if test -e $schedulerconf; then stat -c %U:%G $schedulerconf; fi'" tests: test_items: - flag: "root:root" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chown root:root $schedulerconf scored: true - id: 1.1.7 text: "Ensure that the etcd pod specification file permissions are set to 600 or more restrictive (Automated)" audit: "/bin/sh -c 'if test -e $etcdconf; then find $etcdconf -name '*etcd*' | xargs stat -c permissions=%a; fi'" use_multiple_values: true tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chmod 600 $etcdconf scored: true - id: 1.1.8 text: "Ensure that the etcd pod specification file ownership is set to root:root (Automated)" audit: "/bin/sh -c 'if test -e $etcdconf; then find $etcdconf -name '*etcd*' | xargs stat -c %U:%G; fi'" use_multiple_values: true tests: test_items: - flag: "root:root" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chown root:root $etcdconf scored: true - id: 1.1.9 text: "Ensure that the Container Network Interface file permissions are set to 600 or more restrictive (Manual)" audit: | ps -ef | grep $kubeletbin | grep -- --cni-conf-dir | sed 's%.*cni-conf-dir[= ]\([^ ]*\).*%\1%' | xargs -I{} find {} -mindepth 1 | xargs --no-run-if-empty stat -c permissions=%a find /var/lib/cni/networks -type f 2> /dev/null | xargs --no-run-if-empty stat -c permissions=%a use_multiple_values: true tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chmod 600 scored: false - id: 1.1.10 text: "Ensure that the Container Network Interface file ownership is set to root:root (Manual)" audit: | ps -ef | grep $kubeletbin | grep -- --cni-conf-dir | sed 's%.*cni-conf-dir[= ]\([^ ]*\).*%\1%' | xargs -I{} find {} -mindepth 1 | xargs --no-run-if-empty stat -c %U:%G find /var/lib/cni/networks -type f 2> /dev/null | xargs --no-run-if-empty stat -c %U:%G use_multiple_values: true tests: test_items: - flag: "root:root" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chown root:root scored: false - id: 1.1.11 text: "Ensure that the etcd data directory permissions are set to 700 or more restrictive (Automated)" audit: | DATA_DIR='' for d in $(ps -ef | grep $etcdbin | grep -- --data-dir | sed 's%.*data-dir[= ]\([^ ]*\).*%\1%'); do if test -d "$d"; then DATA_DIR="$d"; fi done if ! test -d "$DATA_DIR"; then DATA_DIR=$etcddatadir; fi stat -c permissions=%a "$DATA_DIR" tests: test_items: - flag: "permissions" compare: op: bitmask value: "700" remediation: | On the etcd server node, get the etcd data directory, passed as an argument --data-dir, from the command 'ps -ef | grep etcd'. Run the below command (based on the etcd data directory found above). For example, chmod 700 /var/lib/etcd scored: true - id: 1.1.12 text: "Ensure that the etcd data directory ownership is set to etcd:etcd (Automated)" audit: | DATA_DIR='' for d in $(ps -ef | grep $etcdbin | grep -- --data-dir | sed 's%.*data-dir[= ]\([^ ]*\).*%\1%'); do if test -d "$d"; then DATA_DIR="$d"; fi done if ! test -d "$DATA_DIR"; then DATA_DIR=$etcddatadir; fi stat -c %U:%G "$DATA_DIR" tests: test_items: - flag: "etcd:etcd" remediation: | On the etcd server node, get the etcd data directory, passed as an argument --data-dir, from the command 'ps -ef | grep etcd'. Run the below command (based on the etcd data directory found above). For example, chown etcd:etcd /var/lib/etcd scored: true - id: 1.1.13 text: "Ensure that the default administrative credential file permissions are set to 600 (Automated)" audit: | for adminconf in /etc/kubernetes/admin.conf /etc/kubernetes/super-admin.conf; do if test -e $adminconf; then stat -c "permissions=%a %n" $adminconf; fi; done use_multiple_values: true tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chmod 600 /etc/kubernetes/admin.conf On Kubernetes 1.29+ the super-admin.conf file should also be modified, if present. For example, chmod 600 /etc/kubernetes/super-admin.conf scored: true - id: 1.1.14 text: "Ensure that the default administrative credential file ownership is set to root:root (Automated)" audit: | for adminconf in /etc/kubernetes/admin.conf /etc/kubernetes/super-admin.conf; do if test -e $adminconf; then stat -c "ownership=%U:%G %n" $adminconf; fi; done use_multiple_values: true tests: test_items: - flag: "ownership" compare: op: eq value: "root:root" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chown root:root /etc/kubernetes/admin.conf On Kubernetes 1.29+ the super-admin.conf file should also be modified, if present. For example, chown root:root /etc/kubernetes/super-admin.conf scored: true - id: 1.1.15 text: "Ensure that the scheduler.conf file permissions are set to 600 or more restrictive (Automated)" audit: "/bin/sh -c 'if test -e $schedulerkubeconfig; then stat -c permissions=%a $schedulerkubeconfig; fi'" tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chmod 600 $schedulerkubeconfig scored: true - id: 1.1.16 text: "Ensure that the scheduler.conf file ownership is set to root:root (Automated)" audit: "/bin/sh -c 'if test -e $schedulerkubeconfig; then stat -c %U:%G $schedulerkubeconfig; fi'" tests: test_items: - flag: "root:root" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chown root:root $schedulerkubeconfig scored: true - id: 1.1.17 text: "Ensure that the controller-manager.conf file permissions are set to 600 or more restrictive (Automated)" audit: "/bin/sh -c 'if test -e $controllermanagerkubeconfig; then stat -c permissions=%a $controllermanagerkubeconfig; fi'" tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chmod 600 $controllermanagerkubeconfig scored: true - id: 1.1.18 text: "Ensure that the controller-manager.conf file ownership is set to root:root (Automated)" audit: "/bin/sh -c 'if test -e $controllermanagerkubeconfig; then stat -c %U:%G $controllermanagerkubeconfig; fi'" tests: test_items: - flag: "root:root" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chown root:root $controllermanagerkubeconfig scored: true - id: 1.1.19 text: "Ensure that the Kubernetes PKI directory and file ownership is set to root:root (Automated)" audit: "find /etc/kubernetes/pki/ | xargs stat -c %U:%G" use_multiple_values: true tests: test_items: - flag: "root:root" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chown -R root:root /etc/kubernetes/pki/ scored: true - id: 1.1.20 text: "Ensure that the Kubernetes PKI certificate file permissions are set to 644 or more restrictive (Manual)" audit: "find /etc/kubernetes/pki/ -name '*.crt' | xargs stat -c permissions=%a" use_multiple_values: true tests: test_items: - flag: "permissions" compare: op: bitmask value: "644" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chmod -R 644 /etc/kubernetes/pki/*.crt scored: false - id: 1.1.21 text: "Ensure that the Kubernetes PKI key file permissions are set to 600 (Manual)" audit: "find /etc/kubernetes/pki/ -name '*.key' | xargs stat -c permissions=%a" use_multiple_values: true tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chmod -R 600 /etc/kubernetes/pki/*.key scored: false - id: 1.2 text: "API Server" checks: - id: 1.2.1 text: "Ensure that the --anonymous-auth argument is set to false (Manual)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--anonymous-auth" compare: op: eq value: false remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the below parameter. --anonymous-auth=false scored: false - id: 1.2.2 text: "Ensure that the --token-auth-file parameter is not set (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--token-auth-file" set: false remediation: | Follow the documentation and configure alternate mechanisms for authentication. Then, edit the API server pod specification file $apiserverconf on the control plane node and remove the --token-auth-file= parameter. scored: true - id: 1.2.3 text: "Ensure that the --DenyServiceExternalIPs is set (Manual)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: bin_op: or test_items: - flag: "--enable-admission-plugins" compare: op: has value: "DenyServiceExternalIPs" remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and add the `DenyServiceExternalIPs` plugin to the enabled admission plugins, as such --enable-admission-plugin=DenyServiceExternalIPs. scored: false - id: 1.2.4 text: "Ensure that the --kubelet-client-certificate and --kubelet-client-key arguments are set as appropriate (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: bin_op: and test_items: - flag: "--kubelet-client-certificate" - flag: "--kubelet-client-key" remediation: | Follow the Kubernetes documentation and set up the TLS connection between the apiserver and kubelets. Then, edit API server pod specification file $apiserverconf on the control plane node and set the kubelet client certificate and key parameters as below. --kubelet-client-certificate= --kubelet-client-key= scored: true - id: 1.2.5 text: "Ensure that the --kubelet-certificate-authority argument is set as appropriate (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--kubelet-certificate-authority" remediation: | Follow the Kubernetes documentation and setup the TLS connection between the apiserver and kubelets. Then, edit the API server pod specification file $apiserverconf on the control plane node and set the --kubelet-certificate-authority parameter to the path to the cert file for the certificate authority. --kubelet-certificate-authority= scored: true - id: 1.2.6 text: "Ensure that the --authorization-mode argument is not set to AlwaysAllow (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--authorization-mode" compare: op: nothave value: "AlwaysAllow" remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the --authorization-mode parameter to values other than AlwaysAllow. One such example could be as below. --authorization-mode=RBAC scored: true - id: 1.2.7 text: "Ensure that the --authorization-mode argument includes Node (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--authorization-mode" compare: op: has value: "Node" remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the --authorization-mode parameter to a value that includes Node. --authorization-mode=Node,RBAC scored: true - id: 1.2.8 text: "Ensure that the --authorization-mode argument includes RBAC (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--authorization-mode" compare: op: has value: "RBAC" remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the --authorization-mode parameter to a value that includes RBAC, for example `--authorization-mode=Node,RBAC`. scored: true - id: 1.2.9 text: "Ensure that the admission control plugin EventRateLimit is set (Manual)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--enable-admission-plugins" compare: op: has value: "EventRateLimit" remediation: | Follow the Kubernetes documentation and set the desired limits in a configuration file. Then, edit the API server pod specification file $apiserverconf and set the below parameters. --enable-admission-plugins=...,EventRateLimit,... --admission-control-config-file= scored: false - id: 1.2.10 text: "Ensure that the admission control plugin AlwaysAdmit is not set (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: bin_op: or test_items: - flag: "--enable-admission-plugins" compare: op: nothave value: AlwaysAdmit - flag: "--enable-admission-plugins" set: false remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and either remove the --enable-admission-plugins parameter, or set it to a value that does not include AlwaysAdmit. scored: true - id: 1.2.11 text: "Ensure that the admission control plugin AlwaysPullImages is set (Manual)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--enable-admission-plugins" compare: op: has value: "AlwaysPullImages" remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the --enable-admission-plugins parameter to include AlwaysPullImages. --enable-admission-plugins=...,AlwaysPullImages,... scored: false - id: 1.2.12 text: "Ensure that the admission control plugin ServiceAccount is set (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: bin_op: or test_items: - flag: "--disable-admission-plugins" compare: op: nothave value: "ServiceAccount" - flag: "--disable-admission-plugins" set: false remediation: | Follow the documentation and create ServiceAccount objects as per your environment. Then, edit the API server pod specification file $apiserverconf on the control plane node and ensure that the --disable-admission-plugins parameter is set to a value that does not include ServiceAccount. scored: true - id: 1.2.13 text: "Ensure that the admission control plugin NamespaceLifecycle is set (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: bin_op: or test_items: - flag: "--disable-admission-plugins" compare: op: nothave value: "NamespaceLifecycle" - flag: "--disable-admission-plugins" set: false remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the --disable-admission-plugins parameter to ensure it does not include NamespaceLifecycle. scored: true - id: 1.2.14 text: "Ensure that the admission control plugin NodeRestriction is set (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--enable-admission-plugins" compare: op: has value: "NodeRestriction" remediation: | Follow the Kubernetes documentation and configure NodeRestriction plug-in on kubelets. Then, edit the API server pod specification file $apiserverconf on the control plane node and set the --enable-admission-plugins parameter to a value that includes NodeRestriction. --enable-admission-plugins=...,NodeRestriction,... scored: true - id: 1.2.15 text: "Ensure that the --profiling argument is set to false (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--profiling" compare: op: eq value: false remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the below parameter. --profiling=false scored: true - id: 1.2.16 text: "Ensure that the --audit-log-path argument is set (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--audit-log-path" remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the --audit-log-path parameter to a suitable path and file where you would like audit logs to be written, for example, --audit-log-path=/var/log/apiserver/audit.log scored: true - id: 1.2.17 text: "Ensure that the --audit-log-maxage argument is set to 30 or as appropriate (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--audit-log-maxage" compare: op: gte value: 30 remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the --audit-log-maxage parameter to 30 or as an appropriate number of days, for example, --audit-log-maxage=30 scored: true - id: 1.2.18 text: "Ensure that the --audit-log-maxbackup argument is set to 10 or as appropriate (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--audit-log-maxbackup" compare: op: gte value: 10 remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the --audit-log-maxbackup parameter to 10 or to an appropriate value. For example, --audit-log-maxbackup=10 scored: true - id: 1.2.19 text: "Ensure that the --audit-log-maxsize argument is set to 100 or as appropriate (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--audit-log-maxsize" compare: op: gte value: 100 remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the --audit-log-maxsize parameter to an appropriate size in MB. For example, to set it as 100 MB, --audit-log-maxsize=100 scored: true - id: 1.2.20 text: "Ensure that the --request-timeout argument is set as appropriate (Manual)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" type: manual remediation: | Edit the API server pod specification file $apiserverconf and set the below parameter as appropriate and if needed. For example, --request-timeout=300s scored: false - id: 1.2.21 text: "Ensure that the --service-account-lookup argument is set to true (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: bin_op: or test_items: - flag: "--service-account-lookup" set: false - flag: "--service-account-lookup" compare: op: eq value: true remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the below parameter. --service-account-lookup=true Alternatively, you can delete the --service-account-lookup parameter from this file so that the default takes effect. scored: true - id: 1.2.22 text: "Ensure that the --service-account-key-file argument is set as appropriate (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--service-account-key-file" remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the --service-account-key-file parameter to the public key file for service accounts. For example, --service-account-key-file= scored: true - id: 1.2.23 text: "Ensure that the --etcd-certfile and --etcd-keyfile arguments are set as appropriate (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: bin_op: and test_items: - flag: "--etcd-certfile" - flag: "--etcd-keyfile" remediation: | Follow the Kubernetes documentation and set up the TLS connection between the apiserver and etcd. Then, edit the API server pod specification file $apiserverconf on the control plane node and set the etcd certificate and key file parameters. --etcd-certfile= --etcd-keyfile= scored: true - id: 1.2.24 text: "Ensure that the --tls-cert-file and --tls-private-key-file arguments are set as appropriate (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: bin_op: and test_items: - flag: "--tls-cert-file" - flag: "--tls-private-key-file" remediation: | Follow the Kubernetes documentation and set up the TLS connection on the apiserver. Then, edit the API server pod specification file $apiserverconf on the control plane node and set the TLS certificate and private key file parameters. --tls-cert-file= --tls-private-key-file= scored: true - id: 1.2.25 text: "Ensure that the --client-ca-file argument is set as appropriate (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--client-ca-file" remediation: | Follow the Kubernetes documentation and set up the TLS connection on the apiserver. Then, edit the API server pod specification file $apiserverconf on the control plane node and set the client certificate authority file. --client-ca-file= scored: true - id: 1.2.26 text: "Ensure that the --etcd-cafile argument is set as appropriate (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--etcd-cafile" remediation: | Follow the Kubernetes documentation and set up the TLS connection between the apiserver and etcd. Then, edit the API server pod specification file $apiserverconf on the control plane node and set the etcd certificate authority file parameter. --etcd-cafile= scored: true - id: 1.2.27 text: "Ensure that the --encryption-provider-config argument is set as appropriate (Manual)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--encryption-provider-config" remediation: | Follow the Kubernetes documentation and configure a EncryptionConfig file. Then, edit the API server pod specification file $apiserverconf on the control plane node and set the --encryption-provider-config parameter to the path of that file. For example, --encryption-provider-config= scored: false - id: 1.2.28 text: "Ensure that encryption providers are appropriately configured (Manual)" audit: | ENCRYPTION_PROVIDER_CONFIG=$(ps -ef | grep $apiserverbin | grep -- --encryption-provider-config | sed 's%.*encryption-provider-config[= ]\([^ ]*\).*%\1%') if test -e $ENCRYPTION_PROVIDER_CONFIG; then grep -A1 'providers:' $ENCRYPTION_PROVIDER_CONFIG | tail -n1 | grep -o "[A-Za-z]*" | sed 's/^/provider=/'; fi tests: test_items: - flag: "provider" compare: op: valid_elements value: "aescbc,kms,secretbox" remediation: | Follow the Kubernetes documentation and configure a EncryptionConfig file. In this file, choose aescbc, kms or secretbox as the encryption provider. scored: false - id: 1.2.29 text: "Ensure that the API Server only makes use of Strong Cryptographic Ciphers (Manual)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--tls-cipher-suites" compare: op: valid_elements value: "TLS_AES_128_GCM_SHA256,TLS_AES_256_GCM_SHA384,TLS_CHACHA20_POLY1305_SHA256,TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256" remediation: | Edit the API server pod specification file /etc/kubernetes/manifests/kube-apiserver.yaml on the control plane node and set the below parameter. --tls-cipher-suites=TLS_AES_128_GCM_SHA256,TLS_AES_256_GCM_SHA384,TLS_CHACHA20_POLY1305_SHA256, TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 scored: false - id: 1.2.30 text: "Ensure that the --service-account-extend-token-expiration parameter is set to false (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--service-account-extend-token-expiration" compare: op: eq value: false remediation: | Edit the API server pod specification file /etc/kubernetes/manifests/kube-apiserver.yaml on the Control Plane node and set the --service-account-extend-token-expiration parameter to false. `--service-account-extend-token-expiration=false` By default, this parameter is set to true. scored: true - id: 1.3 text: "Controller Manager" checks: - id: 1.3.1 text: "Ensure that the --terminated-pod-gc-threshold argument is set as appropriate (Manual)" audit: "/bin/ps -ef | grep $controllermanagerbin | grep -v grep" tests: test_items: - flag: "--terminated-pod-gc-threshold" remediation: | Edit the Controller Manager pod specification file $controllermanagerconf on the control plane node and set the --terminated-pod-gc-threshold to an appropriate threshold, for example, --terminated-pod-gc-threshold=10 scored: false - id: 1.3.2 text: "Ensure that the --profiling argument is set to false (Automated)" audit: "/bin/ps -ef | grep $controllermanagerbin | grep -v grep" tests: test_items: - flag: "--profiling" compare: op: eq value: false remediation: | Edit the Controller Manager pod specification file $controllermanagerconf on the control plane node and set the below parameter. --profiling=false scored: true - id: 1.3.3 text: "Ensure that the --use-service-account-credentials argument is set to true (Automated)" audit: "/bin/ps -ef | grep $controllermanagerbin | grep -v grep" tests: test_items: - flag: "--use-service-account-credentials" compare: op: noteq value: false remediation: | Edit the Controller Manager pod specification file $controllermanagerconf on the control plane node to set the below parameter. --use-service-account-credentials=true scored: true - id: 1.3.4 text: "Ensure that the --service-account-private-key-file argument is set as appropriate (Automated)" audit: "/bin/ps -ef | grep $controllermanagerbin | grep -v grep" tests: test_items: - flag: "--service-account-private-key-file" remediation: | Edit the Controller Manager pod specification file $controllermanagerconf on the control plane node and set the --service-account-private-key-file parameter to the private key file for service accounts. --service-account-private-key-file= scored: true - id: 1.3.5 text: "Ensure that the --root-ca-file argument is set as appropriate (Automated)" audit: "/bin/ps -ef | grep $controllermanagerbin | grep -v grep" tests: test_items: - flag: "--root-ca-file" remediation: | Edit the Controller Manager pod specification file $controllermanagerconf on the control plane node and set the --root-ca-file parameter to the certificate bundle file`. --root-ca-file= scored: true - id: 1.3.6 text: "Ensure that the RotateKubeletServerCertificate argument is set to true (Automated)" audit: "/bin/ps -ef | grep $controllermanagerbin | grep -v grep" tests: bin_op: or test_items: - flag: "--feature-gates" compare: op: nothave value: "RotateKubeletServerCertificate=false" set: true - flag: "--feature-gates" set: false remediation: | Edit the Controller Manager pod specification file $controllermanagerconf on the control plane node and set the --feature-gates parameter to include RotateKubeletServerCertificate=true. --feature-gates=RotateKubeletServerCertificate=true scored: true - id: 1.3.7 text: "Ensure that the --bind-address argument is set to 127.0.0.1 (Automated)" audit: "/bin/ps -ef | grep $controllermanagerbin | grep -v grep" tests: bin_op: or test_items: - flag: "--bind-address" compare: op: eq value: "127.0.0.1" - flag: "--bind-address" set: false remediation: | Edit the Controller Manager pod specification file $controllermanagerconf on the control plane node and ensure the correct value for the --bind-address parameter scored: true - id: 1.4 text: "Scheduler" checks: - id: 1.4.1 text: "Ensure that the --profiling argument is set to false (Automated)" audit: "/bin/ps -ef | grep $schedulerbin | grep -v grep" tests: test_items: - flag: "--profiling" compare: op: eq value: false remediation: | Edit the Scheduler pod specification file $schedulerconf file on the control plane node and set the below parameter. --profiling=false scored: true - id: 1.4.2 text: "Ensure that the --bind-address argument is set to 127.0.0.1 (Automated)" audit: "/bin/ps -ef | grep $schedulerbin | grep -v grep" tests: bin_op: or test_items: - flag: "--bind-address" compare: op: eq value: "127.0.0.1" - flag: "--bind-address" set: false remediation: | Edit the Scheduler pod specification file $schedulerconf on the control plane node and ensure the correct value for the --bind-address parameter scored: true ================================================ FILE: cfg/cis-1.11/node.yaml ================================================ --- controls: version: "cis-1.11" id: 4 text: "Worker Node Security Configuration" type: "node" groups: - id: 4.1 text: "Worker Node Configuration Files" checks: - id: 4.1.1 text: "Ensure that the kubelet service file permissions are set to 600 or more restrictive (Automated)" audit: '/bin/sh -c ''if test -e $kubeletsvc; then stat -c permissions=%a $kubeletsvc; fi'' ' tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Run the below command (based on the file location on your system) on the each worker node. For example, chmod 600 $kubeletsvc scored: true - id: 4.1.2 text: "Ensure that the kubelet service file ownership is set to root:root (Automated)" audit: '/bin/sh -c "if test -e $kubeletsvc; then stat -c %U:%G $kubeletsvc; else echo \"File not found\"; fi"' tests: bin_op: or test_items: - flag: root:root - flag: "File not found" remediation: | Run the below command (based on the file location on your system) on the each worker node. For example, chown root:root $kubeletsvc scored: true - id: 4.1.3 text: "If proxy kubeconfig file exists ensure permissions are set to 600 or more restrictive (Manual)" audit: '/bin/sh -c ''if test -e $proxykubeconfig; then stat -c permissions=%a $proxykubeconfig; fi'' ' tests: bin_op: or test_items: - flag: "permissions" set: true compare: op: bitmask value: "600" remediation: | Run the below command (based on the file location on your system) on the each worker node. For example, chmod 600 $proxykubeconfig scored: false - id: 4.1.4 text: "If proxy kubeconfig file exists ensure ownership is set to root:root (Manual)" audit: '/bin/sh -c ''if test -e $proxykubeconfig; then stat -c %U:%G $proxykubeconfig; fi'' ' tests: bin_op: or test_items: - flag: root:root remediation: | Run the below command (based on the file location on your system) on the each worker node. For example, chown root:root $proxykubeconfig scored: false - id: 4.1.5 text: "Ensure that the --kubeconfig kubelet.conf file permissions are set to 600 or more restrictive (Automated)" audit: '/bin/sh -c ''if test -e $kubeletkubeconfig; then stat -c permissions=%a $kubeletkubeconfig; fi'' ' tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Run the below command (based on the file location on your system) on the each worker node. For example, chmod 600 $kubeletkubeconfig scored: true - id: 4.1.6 text: "Ensure that the --kubeconfig kubelet.conf file ownership is set to root:root (Automated)" audit: '/bin/sh -c ''if test -e $kubeletkubeconfig; then stat -c %U:%G $kubeletkubeconfig; fi'' ' tests: test_items: - flag: root:root remediation: | Run the below command (based on the file location on your system) on the each worker node. For example, chown root:root $kubeletkubeconfig scored: true - id: 4.1.7 text: "Ensure that the certificate authorities file permissions are set to 644 or more restrictive (Manual)" audit: | CAFILE=$(ps -ef | grep kubelet | grep -v apiserver | grep -- --client-ca-file= | awk -F '--client-ca-file=' '{print $2}' | awk '{print $1}' | uniq) if test -z $CAFILE; then CAFILE=$kubeletcafile; fi if test -e $CAFILE; then stat -c permissions=%a $CAFILE; fi tests: test_items: - flag: "permissions" compare: op: bitmask value: "644" remediation: | Run the following command to modify the file permissions of the --client-ca-file chmod 644 scored: false - id: 4.1.8 text: "Ensure that the client certificate authorities file ownership is set to root:root (Manual)" audit: | CAFILE=$(ps -ef | grep kubelet | grep -v apiserver | grep -- --client-ca-file= | awk -F '--client-ca-file=' '{print $2}' | awk '{print $1}' | uniq) if test -z $CAFILE; then CAFILE=$kubeletcafile; fi if test -e $CAFILE; then stat -c %U:%G $CAFILE; fi tests: test_items: - flag: root:root compare: op: eq value: root:root remediation: | Run the following command to modify the ownership of the --client-ca-file. chown root:root scored: false - id: 4.1.9 text: "If the kubelet config.yaml configuration file is being used validate permissions set to 600 or more restrictive (Automated)" audit: '/bin/sh -c ''if test -e $kubeletconf; then stat -c permissions=%a $kubeletconf; fi'' ' tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Run the following command (using the config file location identified in the Audit step) chmod 600 $kubeletconf scored: true - id: 4.1.10 text: "If the kubelet config.yaml configuration file is being used validate file ownership is set to root:root (Automated)" audit: '/bin/sh -c ''if test -e $kubeletconf; then stat -c %U:%G $kubeletconf; fi'' ' tests: test_items: - flag: root:root remediation: | Run the following command (using the config file location identified in the Audit step) chown root:root $kubeletconf scored: true - id: 4.2 text: "Kubelet" checks: - id: 4.2.1 text: "Ensure that the --anonymous-auth argument is set to false (Automated)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: "--anonymous-auth" path: '{.authentication.anonymous.enabled}' compare: op: eq value: false remediation: | If using a Kubelet config file, edit the file to set `authentication: anonymous: enabled` to `false`. If using executable arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_SYSTEM_PODS_ARGS variable. `--anonymous-auth=false` Based on your system, restart the kubelet service. For example, systemctl daemon-reload systemctl restart kubelet.service scored: true - id: 4.2.2 text: "Ensure that the --authorization-mode argument is not set to AlwaysAllow (Automated)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --authorization-mode path: '{.authorization.mode}' compare: op: nothave value: AlwaysAllow remediation: | If using a Kubelet config file, edit the file to set `authorization.mode` to Webhook. If using executable arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_AUTHZ_ARGS variable. --authorization-mode=Webhook Based on your system, restart the kubelet service. For example, systemctl daemon-reload systemctl restart kubelet.service scored: true - id: 4.2.3 text: "Ensure that the --client-ca-file argument is set as appropriate (Automated)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --client-ca-file path: '{.authentication.x509.clientCAFile}' remediation: | If using a Kubelet config file, edit the file to set `authentication.x509.clientCAFile` to the location of the client CA file. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_AUTHZ_ARGS variable. --client-ca-file= Based on your system, restart the kubelet service. For example, systemctl daemon-reload systemctl restart kubelet.service scored: true - id: 4.2.4 text: "Verify that if defined, the --read-only-port argument is set to 0 (Manual)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: bin_op: or test_items: - flag: "--read-only-port" path: '{.readOnlyPort}' compare: op: eq value: 0 - flag: "--read-only-port" path: '{.readOnlyPort}' set: false remediation: | If using a Kubelet config file, edit the file to set `readOnlyPort` to 0. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_SYSTEM_PODS_ARGS variable. --read-only-port=0 Based on your system, restart the kubelet service. For example, systemctl daemon-reload systemctl restart kubelet.service scored: false - id: 4.2.5 text: "Ensure that the --streaming-connection-idle-timeout argument is not set to 0 (Manual)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --streaming-connection-idle-timeout path: '{.streamingConnectionIdleTimeout}' compare: op: noteq value: 0 - flag: --streaming-connection-idle-timeout path: '{.streamingConnectionIdleTimeout}' set: false bin_op: or remediation: | If using a Kubelet config file, edit the file to set `streamingConnectionIdleTimeout` to a value other than 0. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_SYSTEM_PODS_ARGS variable. --streaming-connection-idle-timeout=5m Based on your system, restart the kubelet service. For example, systemctl daemon-reload systemctl restart kubelet.service scored: false - id: 4.2.6 text: "Ensure that the --make-iptables-util-chains argument is set to true (Automated)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --make-iptables-util-chains path: '{.makeIPTablesUtilChains}' compare: op: eq value: true - flag: --make-iptables-util-chains path: '{.makeIPTablesUtilChains}' set: false bin_op: or remediation: | If using a Kubelet config file, edit the file to set `makeIPTablesUtilChains` to `true`. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and remove the --make-iptables-util-chains argument from the KUBELET_SYSTEM_PODS_ARGS variable. Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: true - id: 4.2.7 text: "Ensure that the --hostname-override argument is not set (Manual)" # This is one of those properties that can only be set as a command line argument. # To check if the property is set as expected, we need to parse the kubelet command # instead reading the Kubelet Configuration file. audit: "/bin/ps -fC $kubeletbin" tests: test_items: - flag: --hostname-override set: false remediation: | Edit the kubelet service file $kubeletsvc on each worker node and remove the --hostname-override argument from the KUBELET_SYSTEM_PODS_ARGS variable. Based on your system, restart the kubelet service. For example, systemctl daemon-reload systemctl restart kubelet.service scored: false - id: 4.2.8 text: "Ensure that the eventRecordQPS argument is set to a level which ensures appropriate event capture (Manual)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --event-qps path: '{.eventRecordQPS}' compare: op: gte value: 0 - flag: --event-qps path: '{.eventRecordQPS}' set: false bin_op: or remediation: | If using a Kubelet config file, edit the file to set `eventRecordQPS` to an appropriate level. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_SYSTEM_PODS_ARGS variable. Based on your system, restart the kubelet service. For example, systemctl daemon-reload systemctl restart kubelet.service scored: false - id: 4.2.9 text: "Ensure that the --tls-cert-file and --tls-private-key-file arguments are set as appropriate (Manual)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --tls-cert-file path: '{.tlsCertFile}' - flag: --tls-private-key-file path: '{.tlsPrivateKeyFile}' remediation: | If using a Kubelet config file, edit the file to set `tlsCertFile` to the location of the certificate file to use to identify this Kubelet, and `tlsPrivateKeyFile` to the location of the corresponding private key file. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameters in KUBELET_CERTIFICATE_ARGS variable. --tls-cert-file= --tls-private-key-file= Based on your system, restart the kubelet service. For example, systemctl daemon-reload systemctl restart kubelet.service scored: false - id: 4.2.10 text: "Ensure that the --rotate-certificates argument is not set to false (Automated)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --rotate-certificates path: '{.rotateCertificates}' compare: op: eq value: true - flag: --rotate-certificates path: '{.rotateCertificates}' set: false bin_op: or remediation: | If using a Kubelet config file, edit the file to add the line `rotateCertificates` to `true` or remove it altogether to use the default value. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and remove --rotate-certificates=false argument from the KUBELET_CERTIFICATE_ARGS variable. Based on your system, restart the kubelet service. For example, systemctl daemon-reload systemctl restart kubelet.service scored: true - id: 4.2.11 text: "Verify that the RotateKubeletServerCertificate argument is set to true (Manual)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: bin_op: or test_items: - flag: RotateKubeletServerCertificate path: '{.featureGates.RotateKubeletServerCertificate}' compare: op: nothave value: false - flag: RotateKubeletServerCertificate path: '{.featureGates.RotateKubeletServerCertificate}' set: false remediation: | Edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_CERTIFICATE_ARGS variable. --feature-gates=RotateKubeletServerCertificate=true Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: false - id: 4.2.12 text: "Ensure that the Kubelet only makes use of Strong Cryptographic Ciphers (Manual)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --tls-cipher-suites path: '{range .tlsCipherSuites[:]}{}{'',''}{end}' compare: op: valid_elements value: 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 remediation: | If using a Kubelet config file, edit the file to set `tlsCipherSuites` to 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 or to a subset of these values. If using executable arguments, edit the kubelet service file $kubeletsvc on each worker node and set the --tls-cipher-suites parameter as follows, or to a subset of these values. --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 Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: false - id: 4.2.13 text: "Ensure that a limit is set on pod PIDs (Manual)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --pod-max-pids path: '{.podPidsLimit}' remediation: | Decide on an appropriate level for this parameter and set it, either via the --pod-max-pids command line parameter or the PodPidsLimit configuration file setting. scored: false - id: 4.2.14 text: "Ensure that the --seccomp-default parameter is set to true (Manual)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --seccomp-default path: '{.seccompDefault}' remediation: | Set the parameter, either via the --seccomp-default command line parameter or the seccompDefault configuration file setting. By default the seccomp profile is not enabled. scored: false - id: 4.2.15 text: "Ensure that the --IPAddressDeny is set to any (Manual)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --IPAddressDeny path: '{.IPAddressDeny}' remediation: | Configuring the setting IPAddressDeny=any will deny service to any IP address not specified in the complimentary setting IPAddressAllow configuration parameter ( IPAddressDeny=any IPAddressAllow={{ kubelet_secure_addresses }} *Note kubelet_secure_addresses: "localhost link-local {{ kube_pods_subnets |regex_replace(',', ' ') }} {{ kube_node_addresses }} {{ loadbalancer_apiserver.address | default('')" By default IPAddressDeny is not enabled. scored: false - id: 4.3 text: "kube-proxy" checks: - id: 4.3.1 text: "Ensure that the kube-proxy metrics service is bound to localhost (Automated)" audit: "/bin/ps -fC $proxybin" audit_config: "/bin/sh -c 'if test -e $proxykubeconfig; then cat $proxykubeconfig; fi'" tests: bin_op: or test_items: - flag: "--metrics-bind-address" path: '{.metricsBindAddress}' compare: op: has value: "127.0.0.1" - flag: "--metrics-bind-address" path: '{.metricsBindAddress}' set: false remediation: | Modify or remove any values which bind the metrics service to a non-localhost address. The default value is 127.0.0.1:10249. scored: true ================================================ FILE: cfg/cis-1.11/policies.yaml ================================================ --- controls: version: "cis-1.11" id: 5 text: "Kubernetes Policies" type: "policies" groups: - id: 5.1 text: "RBAC and Service Accounts" checks: - id: 5.1.1 text: "Ensure that the cluster-admin role is only used where required (Manual)" audit: | kubectl get clusterrolebindings -o=custom-columns=NAME:.metadata.name,ROLE:.roleRef.name,SUBJECT:.subjects[*].name --no-headers | while read -r role_name role_binding subject do if [[ "${role_name}" != "cluster-admin" && "${role_binding}" == "cluster-admin" ]]; then is_compliant="false" else is_compliant="true" fi; echo "**role_name: ${role_name} role_binding: ${role_binding} subject: ${subject} is_compliant: ${is_compliant}" done use_multiple_values: true tests: test_items: - flag: "is_compliant" compare: op: eq value: true remediation: | Identify all clusterrolebindings to the cluster-admin role. Check if they are used and if they need this role or if they could use a role with fewer privileges. Where possible, first bind users to a lower privileged role and then remove the clusterrolebinding to the cluster-admin role : kubectl delete clusterrolebinding [name] Condition: is_compliant is false if rolename is not cluster-admin and rolebinding is cluster-admin. scored: false - id: 5.1.2 text: "Minimize access to secrets (Manual)" audit: "echo \"canGetListWatchSecretsAsSystemAuthenticated: $(kubectl auth can-i get,list,watch secrets --all-namespaces --as=system:authenticated)\"" tests: test_items: - flag: "canGetListWatchSecretsAsSystemAuthenticated" compare: op: eq value: no remediation: | Where possible, remove get, list and watch access to Secret objects in the cluster. scored: false - id: 5.1.3 text: "Minimize wildcard use in Roles and ClusterRoles (Manual)" audit: | # Check Roles kubectl get roles --all-namespaces -o custom-columns=ROLE_NAMESPACE:.metadata.namespace,ROLE_NAME:.metadata.name --no-headers | while read -r role_namespace role_name do role_rules=$(kubectl get role -n "${role_namespace}" "${role_name}" -o=json | jq -c '.rules') if echo "${role_rules}" | grep -q "\[\"\*\"\]"; then role_is_compliant="false" else role_is_compliant="true" fi; echo "**role_name: ${role_name} role_namespace: ${role_namespace} role_rules: ${role_rules} role_is_compliant: ${role_is_compliant}" done # Check ClusterRoles kubectl get clusterroles -o custom-columns=CLUSTERROLE_NAME:.metadata.name --no-headers | while read -r clusterrole_name do clusterrole_rules=$(kubectl get clusterrole "${clusterrole_name}" -o=json | jq -c '.rules') if echo "${clusterrole_rules}" | grep -q "\[\"\*\"\]"; then clusterrole_is_compliant="false" else clusterrole_is_compliant="true" fi; echo "**clusterrole_name: ${clusterrole_name} clusterrole_rules: ${clusterrole_rules} clusterrole_is_compliant: ${clusterrole_is_compliant}" done use_multiple_values: true tests: bin_op: or test_items: - flag: "role_is_compliant" compare: op: eq value: true set: true - flag: "clusterrole_is_compliant" compare: op: eq value: true set: true remediation: | Where possible replace any use of wildcards ["*"] in roles and clusterroles with specific objects or actions. Condition: role_is_compliant is false if ["*"] is found in rules. Condition: clusterrole_is_compliant is false if ["*"] is found in rules. scored: false - id: 5.1.4 text: "Minimize access to create pods (Manual)" audit: | echo "canCreatePodsAsSystemAuthenticated: $(kubectl auth can-i create pods --all-namespaces --as=system:authenticated)" tests: test_items: - flag: "canCreatePodsAsSystemAuthenticated" compare: op: eq value: no remediation: | Where possible, remove create access to pod objects in the cluster. scored: false - id: 5.1.5 text: "Ensure that default service accounts are not actively used (Manual)" audit: | kubectl get serviceaccount --all-namespaces --field-selector metadata.name=default -o=json | jq -r '.items[] | " namespace: \(.metadata.namespace), kind: \(.kind), name: \(.metadata.name), automountServiceAccountToken: \(.automountServiceAccountToken | if . == null then "notset" else . end )"' | xargs -L 1 use_multiple_values: true tests: test_items: - flag: "automountServiceAccountToken" compare: op: eq value: false set: true remediation: | Create explicit service accounts wherever a Kubernetes workload requires specific access to the Kubernetes API server. Modify the configuration of each default service account to include this value `automountServiceAccountToken: false`. scored: false - id: 5.1.6 text: "Ensure that Service Account Tokens are only mounted where necessary (Manual)" audit: | kubectl get pods --all-namespaces -o custom-columns=POD_NAMESPACE:.metadata.namespace,POD_NAME:.metadata.name,POD_SERVICE_ACCOUNT:.spec.serviceAccount,POD_IS_AUTOMOUNTSERVICEACCOUNTTOKEN:.spec.automountServiceAccountToken --no-headers | while read -r pod_namespace pod_name pod_service_account pod_is_automountserviceaccounttoken do # Retrieve automountServiceAccountToken's value for ServiceAccount and Pod, set to notset if null or . svacc_is_automountserviceaccounttoken=$(kubectl get serviceaccount -n "${pod_namespace}" "${pod_service_account}" -o json | jq -r '.automountServiceAccountToken' | sed -e 's//notset/g' -e 's/null/notset/g') pod_is_automountserviceaccounttoken=$(echo "${pod_is_automountserviceaccounttoken}" | sed -e 's//notset/g' -e 's/null/notset/g') if [ "${svacc_is_automountserviceaccounttoken}" = "false" ] && ( [ "${pod_is_automountserviceaccounttoken}" = "false" ] || [ "${pod_is_automountserviceaccounttoken}" = "notset" ] ); then is_compliant="true" elif [ "${svacc_is_automountserviceaccounttoken}" = "true" ] && [ "${pod_is_automountserviceaccounttoken}" = "false" ]; then is_compliant="true" else is_compliant="false" fi echo "**namespace: ${pod_namespace} pod_name: ${pod_name} service_account: ${pod_service_account} pod_is_automountserviceaccounttoken: ${pod_is_automountserviceaccounttoken} svacc_is_automountServiceAccountToken: ${svacc_is_automountserviceaccounttoken} is_compliant: ${is_compliant}" done use_multiple_values: true tests: test_items: - flag: "is_compliant" compare: op: eq value: true remediation: | Modify the definition of ServiceAccounts and Pods which do not need to mount service account tokens to disable it, with `automountServiceAccountToken: false`. If both the ServiceAccount and the Pod's .spec specify a value for automountServiceAccountToken, the Pod spec takes precedence. Condition: Pod is_compliant to true when - ServiceAccount is automountServiceAccountToken: false and Pod is automountServiceAccountToken: false or notset - ServiceAccount is automountServiceAccountToken: true notset and Pod is automountServiceAccountToken: false scored: false - id: 5.1.7 text: "Avoid use of system:masters group (Manual)" type: "manual" remediation: | Remove the system:masters group from all users in the cluster. scored: false - id: 5.1.8 text: "Limit use of the Bind, Impersonate and Escalate permissions in the Kubernetes cluster (Manual)" type: "manual" remediation: | Where possible, remove the impersonate, bind and escalate rights from subjects. scored: false - id: 5.1.9 text: "Minimize access to create persistent volumes (Manual)" type: "manual" remediation: | Where possible, remove create access to PersistentVolume objects in the cluster. scored: false - id: 5.1.10 text: "Minimize access to the proxy sub-resource of nodes (Manual)" type: "manual" remediation: | Where possible, remove access to the proxy sub-resource of node objects. scored: false - id: 5.1.11 text: "Minimize access to the approval sub-resource of certificatesigningrequests objects (Manual)" type: "manual" remediation: | Where possible, remove access to the approval sub-resource of certificatesigningrequests objects. scored: false - id: 5.1.12 text: "Minimize access to webhook configuration objects (Manual)" type: "manual" remediation: | Where possible, remove access to the validatingwebhookconfigurations or mutatingwebhookconfigurations objects scored: false - id: 5.1.13 text: "Minimize access to the service account token creation (Manual)" type: "manual" remediation: | Where possible, remove access to the token sub-resource of serviceaccount objects. scored: false - id: 5.2 text: "Pod Security Standards" checks: - id: 5.2.1 text: "Ensure that the cluster has at least one active policy control mechanism in place (Manual)" type: "manual" remediation: | Ensure that either Pod Security Admission or an external policy control system is in place for every namespace which contains user workloads. scored: false - id: 5.2.2 text: "Minimize the admission of privileged containers (Manual)" audit: | kubectl get pods --all-namespaces -o custom-columns=POD_NAME:.metadata.name,POD_NAMESPACE:.metadata.namespace --no-headers | while read -r pod_name pod_namespace do # Retrieve container(s) for each Pod. kubectl get pod "${pod_name}" --namespace "${pod_namespace}" -o json | jq -c '.spec.containers[]' | while read -r container do # Retrieve container's name. container_name=$(echo ${container} | jq -r '.name') # Retrieve container's .securityContext.privileged value. container_privileged=$(echo ${container} | jq -r '.securityContext.privileged' | sed -e 's/null/notset/g') if [ "${container_privileged}" = "false" ] || [ "${container_privileged}" = "notset" ] ; then echo "***pod_name: ${pod_name} container_name: ${container_name} pod_namespace: ${pod_namespace} is_container_privileged: ${container_privileged} is_compliant: true" else echo "***pod_name: ${pod_name} container_name: ${container_name} pod_namespace: ${pod_namespace} is_container_privileged: ${container_privileged} is_compliant: false" fi done done use_multiple_values: true tests: test_items: - flag: "is_compliant" compare: op: eq value: true remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of privileged containers. Audit: the audit list all pods' containers to retrieve their .securityContext.privileged value. Condition: is_compliant is false if container's `.securityContext.privileged` is set to `true`. Default: by default, there are no restrictions on the creation of privileged containers. scored: false - id: 5.2.3 text: "Minimize the admission of containers wishing to share the host process ID namespace (Manual)" audit: | kubectl get pods --all-namespaces -o custom-columns=POD_NAME:.metadata.name,POD_NAMESPACE:.metadata.namespace --no-headers | while read -r pod_name pod_namespace do # Retrieve spec.hostPID for each pod. pod_hostpid=$(kubectl get pod "${pod_name}" --namespace "${pod_namespace}" -o jsonpath='{.spec.hostPID}' 2>/dev/null) if [ -z "${pod_hostpid}" ]; then pod_hostpid="false" echo "***pod_name: ${pod_name} pod_namespace: ${pod_namespace} is_pod_hostpid: ${pod_hostpid} is_compliant: true" else echo "***pod_name: ${pod_name} pod_namespace: ${pod_namespace} is_pod_hostpid: ${pod_hostpid} is_compliant: false" fi done use_multiple_values: true tests: test_items: - flag: "is_compliant" compare: op: eq value: true remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of `hostPID` containers. Audit: the audit retrieves each Pod' spec.hostPID. Condition: is_compliant is false if Pod's spec.hostPID is set to `true`. Default: by default, there are no restrictions on the creation of hostPID containers. scored: false - id: 5.2.4 text: "Minimize the admission of containers wishing to share the host IPC namespace (Manual)" audit: | kubectl get pods --all-namespaces -o custom-columns=POD_NAME:.metadata.name,POD_NAMESPACE:.metadata.namespace --no-headers | while read -r pod_name pod_namespace do # Retrieve spec.hostIPC for each pod. pod_hostipc=$(kubectl get pod "${pod_name}" --namespace "${pod_namespace}" -o jsonpath='{.spec.hostIPC}' 2>/dev/null) if [ -z "${pod_hostipc}" ]; then pod_hostipc="false" echo "***pod_name: ${pod_name} pod_namespace: ${pod_namespace} is_pod_hostipc: ${pod_hostipc} is_compliant: true" else echo "***pod_name: ${pod_name} pod_namespace: ${pod_namespace} is_pod_hostipc: ${pod_hostipc} is_compliant: false" fi done use_multiple_values: true tests: test_items: - flag: "is_compliant" compare: op: eq value: true remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of `hostIPC` containers. Audit: the audit retrieves each Pod' spec.IPC. Condition: is_compliant is false if Pod's spec.hostIPC is set to `true`. Default: by default, there are no restrictions on the creation of hostIPC containers. scored: false - id: 5.2.5 text: "Minimize the admission of containers wishing to share the host network namespace (Manual)" audit: | kubectl get pods --all-namespaces -o custom-columns=POD_NAME:.metadata.name,POD_NAMESPACE:.metadata.namespace --no-headers | while read -r pod_name pod_namespace do # Retrieve spec.hostNetwork for each pod. pod_hostnetwork=$(kubectl get pod "${pod_name}" --namespace "${pod_namespace}" -o jsonpath='{.spec.hostNetwork}' 2>/dev/null) if [ -z "${pod_hostnetwork}" ]; then pod_hostnetwork="false" echo "***pod_name: ${pod_name} pod_namespace: ${pod_namespace} is_pod_hostnetwork: ${pod_hostnetwork} is_compliant: true" else echo "***pod_name: ${pod_name} pod_namespace: ${pod_namespace} is_pod_hostnetwork: ${pod_hostnetwork} is_compliant: false" fi done use_multiple_values: true tests: test_items: - flag: "is_compliant" compare: op: eq value: true remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of `hostNetwork` containers. Audit: the audit retrieves each Pod' spec.hostNetwork. Condition: is_compliant is false if Pod's spec.hostNetwork is set to `true`. Default: by default, there are no restrictions on the creation of hostNetwork containers. scored: false - id: 5.2.6 text: "Minimize the admission of containers with allowPrivilegeEscalation (Manual)" audit: | kubectl get pods --all-namespaces -o custom-columns=POD_NAME:.metadata.name,POD_NAMESPACE:.metadata.namespace --no-headers | while read -r pod_name pod_namespace do # Retrieve container(s) for each Pod. kubectl get pod "${pod_name}" --namespace "${pod_namespace}" -o json | jq -c '.spec.containers[]' | while read -r container do # Retrieve container's name container_name=$(echo ${container} | jq -r '.name') # Retrieve container's .securityContext.allowPrivilegeEscalation container_allowprivesc=$(echo ${container} | jq -r '.securityContext.allowPrivilegeEscalation' | sed -e 's/null/notset/g') if [ "${container_allowprivesc}" = "false" ] || [ "${container_allowprivesc}" = "notset" ]; then echo "***pod_name: ${pod_name} container_name: ${container_name} pod_namespace: ${pod_namespace} is_container_allowprivesc: ${container_allowprivesc} is_compliant: true" else echo "***pod_name: ${pod_name} container_name: ${container_name} pod_namespace: ${pod_namespace} is_container_allowprivesc: ${container_allowprivesc} is_compliant: false" fi done done use_multiple_values: true tests: test_items: - flag: "is_compliant" compare: op: eq value: true remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of containers with `.securityContext.allowPrivilegeEscalation` set to `true`. Audit: the audit retrieves each Pod's container(s) `.securityContext.allowPrivilegeEscalation`. Condition: is_compliant is false if container's `.securityContext.allowPrivilegeEscalation` is set to `true`. Default: If notset, privilege escalation is allowed (default to true). However if PSP/PSA is used with a `restricted` profile, privilege escalation is explicitly disallowed unless configured otherwise. scored: false - id: 5.2.7 text: "Minimize the admission of root containers (Manual)" type: "manual" remediation: | Create a policy for each namespace in the cluster, ensuring that either `MustRunAsNonRoot` or `MustRunAs` with the range of UIDs not including 0, is set. scored: false - id: 5.2.8 text: "Minimize the admission of containers with the NET_RAW capability (Manual)" type: "manual" remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of containers with the `NET_RAW` capability. scored: false - id: 5.2.9 text: "Minimize the admission of containers with added capabilities (Manual)" audit: | kubectl get pods --all-namespaces -o custom-columns=POD_NAME:.metadata.name,POD_NAMESPACE:.metadata.namespace --no-headers | while read -r pod_name pod_namespace do # Retrieve container(s) for each Pod. kubectl get pod "${pod_name}" --namespace "${pod_namespace}" -o json | jq -c '.spec.containers[]' | while read -r container do # Retrieve container's name container_name=$(echo ${container} | jq -r '.name') # Retrieve container's added capabilities container_caps_add=$(echo ${container} | jq -r '.securityContext.capabilities.add' | sed -e 's/null/notset/g') # Set is_compliant to true by default. is_compliant=true caps_list="" if [ "${container_caps_add}" != "notset" ]; then # Loop through all caps and append caps_list, then set is_compliant to false. for cap in $(echo "${container_caps_add}" | jq -r '.[]'); do caps_list+="${cap}," is_compliant=false done # Remove trailing comma for the last list member. caps_list=${caps_list%,} fi if [ "${is_compliant}" = true ]; then echo "***pod_name: ${pod_name} container_name: ${container_name} pod_namespace: ${pod_namespace} container_caps_add: ${container_caps_add} is_compliant: true" else echo "***pod_name: ${pod_name} container_name: ${container_name} pod_namespace: ${pod_namespace} container_caps_add: ${caps_list} is_compliant: false" fi done done use_multiple_values: true tests: test_items: - flag: "is_compliant" compare: op: eq value: true remediation: | Ensure that `allowedCapabilities` is not present in policies for the cluster unless it is set to an empty array. Audit: the audit retrieves each Pod's container(s) added capabilities. Condition: is_compliant is false if added capabilities are added for a given container. Default: Containers run with a default set of capabilities as assigned by the Container Runtime. scored: false - id: 5.2.10 text: "Minimize the admission of containers with capabilities assigned (Manual)" type: "manual" remediation: | Review the use of capabilites in applications running on your cluster. Where a namespace contains applications which do not require any Linux capabities to operate consider adding a PSP which forbids the admission of containers which do not drop all capabilities. scored: false - id: 5.2.11 text: "Minimize the admission of Windows HostProcess containers (Manual)" type: "manual" remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of containers that have `.securityContext.windowsOptions.hostProcess` set to `true`. scored: false - id: 5.2.12 text: "Minimize the admission of HostPath volumes (Manual)" type: "manual" remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of containers with `hostPath` volumes. scored: false - id: 5.2.13 text: "Minimize the admission of containers which use HostPorts (Manual)" type: "manual" remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of containers which use `hostPort` sections. scored: false - id: 5.3 text: "Network Policies and CNI" checks: - id: 5.3.1 text: "Ensure that the CNI in use supports NetworkPolicies (Manual)" type: "manual" remediation: | If the CNI plugin in use does not support network policies, consideration should be given to making use of a different plugin, or finding an alternate mechanism for restricting traffic in the Kubernetes cluster. scored: false - id: 5.3.2 text: "Ensure that all Namespaces have NetworkPolicies defined (Manual)" type: "manual" remediation: | Follow the documentation and create NetworkPolicy objects as you need them. scored: false - id: 5.4 text: "Secrets Management" checks: - id: 5.4.1 text: "Prefer using Secrets as files over Secrets as environment variables (Manual)" type: "manual" remediation: | If possible, rewrite application code to read Secrets from mounted secret files, rather than from environment variables. scored: false - id: 5.4.2 text: "Consider external secret storage (Manual)" type: "manual" remediation: | Refer to the Secrets management options offered by your cloud provider or a third-party secrets management solution. scored: false - id: 5.5 text: "Extensible Admission Control" checks: - id: 5.5.1 text: "Configure Image Provenance using ImagePolicyWebhook admission controller (Manual)" type: "manual" remediation: | Follow the Kubernetes documentation and setup image provenance. scored: false - id: 5.6 text: "General Policies" checks: - id: 5.6.1 text: "Create administrative boundaries between resources using namespaces (Manual)" type: "manual" remediation: | Follow the documentation and create namespaces for objects in your deployment as you need them. scored: false - id: 5.6.2 text: "Ensure that the seccomp profile is set to docker/default in your Pod definitions (Manual)" type: "manual" remediation: | Use `securityContext` to enable the docker/default seccomp profile in your pod definitions. An example is as below: securityContext: seccompProfile: type: RuntimeDefault scored: false - id: 5.6.3 text: "Apply SecurityContext to your Pods and Containers (Manual)" type: "manual" remediation: | Follow the Kubernetes documentation and apply SecurityContexts to your Pods. For a suggested list of SecurityContexts, you may refer to the CIS Security Benchmark for Docker Containers. scored: false - id: 5.6.4 text: "The default namespace should not be used (Manual)" type: "manual" remediation: | Ensure that namespaces are created to allow for appropriate segregation of Kubernetes resources and that all new resources are created in a specific namespace. scored: false ================================================ FILE: cfg/cis-1.12/config.yaml ================================================ --- ## Version-specific settings that override the values in cfg/config.yaml ================================================ FILE: cfg/cis-1.12/controlplane.yaml ================================================ --- controls: version: "cis-1.12" id: 3 text: "Control Plane Configuration" type: "controlplane" groups: - id: 3.1 text: "Authentication and Authorization" checks: - id: 3.1.1 text: "Client certificate authentication should not be used for users (Manual)" type: "manual" remediation: | Alternative mechanisms provided by Kubernetes such as the use of OIDC should be implemented in place of client certificates. scored: false - id: 3.1.2 text: "Service account token authentication should not be used for users (Manual)" type: "manual" remediation: | Alternative mechanisms provided by Kubernetes such as the use of OIDC should be implemented in place of service account tokens. scored: false - id: 3.1.3 text: "Bootstrap token authentication should not be used for users (Manual)" type: "manual" remediation: | Alternative mechanisms provided by Kubernetes such as the use of OIDC should be implemented in place of bootstrap tokens. scored: false - id: 3.2 text: "Logging" checks: - id: 3.2.1 text: "Ensure that a minimal audit policy is created (Manual)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--audit-policy-file" set: true remediation: | Create an audit policy file for your cluster. scored: false - id: 3.2.2 text: "Ensure that the audit policy covers key security concerns (Manual)" type: "manual" remediation: | Review the audit policy provided for the cluster and ensure that it covers at least the following areas, - Access to Secrets managed by the cluster. Care should be taken to only log Metadata for requests to Secrets, ConfigMaps, and TokenReviews, in order to avoid risk of logging sensitive data. - Modification of Pod and Deployment objects. - Use of `pods/exec`, `pods/portforward`, `pods/proxy` and `services/proxy`. For most requests, minimally logging at the Metadata level is recommended (the most basic level of logging). scored: false ================================================ FILE: cfg/cis-1.12/etcd.yaml ================================================ --- controls: version: "cis-1.12" id: 2 text: "Etcd Node Configuration" type: "etcd" groups: - id: 2 text: "Etcd Node Configuration" checks: - id: 2.1 text: "Ensure that the --cert-file and --key-file arguments are set as appropriate (Automated)" audit: "/bin/ps -ef | /bin/grep $etcdbin | /bin/grep -v grep" tests: bin_op: and test_items: - flag: "--cert-file" env: "ETCD_CERT_FILE" - flag: "--key-file" env: "ETCD_KEY_FILE" remediation: | Follow the etcd service documentation and configure TLS encryption. Then, edit the etcd pod specification file /etc/kubernetes/manifests/etcd.yaml on the master node and set the below parameters. --cert-file= --key-file= scored: true - id: 2.2 text: "Ensure that the --client-cert-auth argument is set to true (Automated)" audit: "/bin/ps -ef | /bin/grep $etcdbin | /bin/grep -v grep" tests: test_items: - flag: "--client-cert-auth" env: "ETCD_CLIENT_CERT_AUTH" compare: op: eq value: true remediation: | Edit the etcd pod specification file $etcdconf on the master node and set the below parameter. --client-cert-auth="true" scored: true - id: 2.3 text: "Ensure that the --auto-tls argument is not set to true (Automated)" audit: "/bin/ps -ef | /bin/grep $etcdbin | /bin/grep -v grep" tests: bin_op: or test_items: - flag: "--auto-tls" env: "ETCD_AUTO_TLS" set: false - flag: "--auto-tls" env: "ETCD_AUTO_TLS" compare: op: eq value: false remediation: | Edit the etcd pod specification file $etcdconf on the master node and either remove the --auto-tls parameter or set it to false. --auto-tls=false scored: true - id: 2.4 text: "Ensure that the --peer-cert-file and --peer-key-file arguments are set as appropriate (Automated)" audit: "/bin/ps -ef | /bin/grep $etcdbin | /bin/grep -v grep" tests: bin_op: and test_items: - flag: "--peer-cert-file" env: "ETCD_PEER_CERT_FILE" - flag: "--peer-key-file" env: "ETCD_PEER_KEY_FILE" remediation: | Follow the etcd service documentation and configure peer TLS encryption as appropriate for your etcd cluster. Then, edit the etcd pod specification file $etcdconf on the master node and set the below parameters. --peer-client-file= --peer-key-file= scored: true - id: 2.5 text: "Ensure that the --peer-client-cert-auth argument is set to true (Automated)" audit: "/bin/ps -ef | /bin/grep $etcdbin | /bin/grep -v grep" tests: test_items: - flag: "--peer-client-cert-auth" env: "ETCD_PEER_CLIENT_CERT_AUTH" compare: op: eq value: true remediation: | Edit the etcd pod specification file $etcdconf on the master node and set the below parameter. --peer-client-cert-auth=true scored: true - id: 2.6 text: "Ensure that the --peer-auto-tls argument is not set to true (Automated)" audit: "/bin/ps -ef | /bin/grep $etcdbin | /bin/grep -v grep" tests: bin_op: or test_items: - flag: "--peer-auto-tls" env: "ETCD_PEER_AUTO_TLS" set: false - flag: "--peer-auto-tls" env: "ETCD_PEER_AUTO_TLS" compare: op: eq value: false remediation: | Edit the etcd pod specification file $etcdconf on the master node and either remove the --peer-auto-tls parameter or set it to false. --peer-auto-tls=false scored: true - id: 2.7 text: "Ensure that a unique Certificate Authority is used for etcd (Manual)" audit: "/bin/ps -ef | /bin/grep $etcdbin | /bin/grep -v grep" tests: test_items: - flag: "--trusted-ca-file" env: "ETCD_TRUSTED_CA_FILE" remediation: | [Manual test] Follow the etcd documentation and create a dedicated certificate authority setup for the etcd service. Then, edit the etcd pod specification file $etcdconf on the master node and set the below parameter. --trusted-ca-file= scored: false ================================================ FILE: cfg/cis-1.12/master.yaml ================================================ --- controls: version: "cis-1.12" id: 1 text: "Control Plane Security Configuration" type: "master" groups: - id: 1.1 text: "Control Plane Node Configuration Files" checks: - id: 1.1.1 text: "Ensure that the API server pod specification file permissions are set to 600 or more restrictive (Automated)" audit: "/bin/sh -c 'if test -e $apiserverconf; then stat -c permissions=%a $apiserverconf; fi'" tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chmod 600 $apiserverconf scored: true - id: 1.1.2 text: "Ensure that the API server pod specification file ownership is set to root:root (Automated)" audit: "/bin/sh -c 'if test -e $apiserverconf; then stat -c %U:%G $apiserverconf; fi'" tests: test_items: - flag: "root:root" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chown root:root $apiserverconf scored: true - id: 1.1.3 text: "Ensure that the controller manager pod specification file permissions are set to 600 or more restrictive (Automated)" audit: "/bin/sh -c 'if test -e $controllermanagerconf; then stat -c permissions=%a $controllermanagerconf; fi'" tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chmod 600 $controllermanagerconf scored: true - id: 1.1.4 text: "Ensure that the controller manager pod specification file ownership is set to root:root (Automated)" audit: "/bin/sh -c 'if test -e $controllermanagerconf; then stat -c %U:%G $controllermanagerconf; fi'" tests: test_items: - flag: "root:root" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chown root:root $controllermanagerconf scored: true - id: 1.1.5 text: "Ensure that the scheduler pod specification file permissions are set to 600 or more restrictive (Automated)" audit: "/bin/sh -c 'if test -e $schedulerconf; then stat -c permissions=%a $schedulerconf; fi'" tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chmod 600 $schedulerconf scored: true - id: 1.1.6 text: "Ensure that the scheduler pod specification file ownership is set to root:root (Automated)" audit: "/bin/sh -c 'if test -e $schedulerconf; then stat -c %U:%G $schedulerconf; fi'" tests: test_items: - flag: "root:root" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chown root:root $schedulerconf scored: true - id: 1.1.7 text: "Ensure that the etcd pod specification file permissions are set to 600 or more restrictive (Automated)" audit: "/bin/sh -c 'if test -e $etcdconf; then find $etcdconf -name '*etcd*' | xargs stat -c permissions=%a; fi'" use_multiple_values: true tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chmod 600 $etcdconf scored: true - id: 1.1.8 text: "Ensure that the etcd pod specification file ownership is set to root:root (Automated)" audit: "/bin/sh -c 'if test -e $etcdconf; then find $etcdconf -name '*etcd*' | xargs stat -c %U:%G; fi'" use_multiple_values: true tests: test_items: - flag: "root:root" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chown root:root $etcdconf scored: true - id: 1.1.9 text: "Ensure that the Container Network Interface file permissions are set to 600 or more restrictive (Manual)" audit: | ps -ef | grep $kubeletbin | grep -- --cni-conf-dir | sed 's%.*cni-conf-dir[= ]\([^ ]*\).*%\1%' | xargs -I{} find {} -mindepth 1 | xargs --no-run-if-empty stat -c permissions=%a find /var/lib/cni/networks -type f 2> /dev/null | xargs --no-run-if-empty stat -c permissions=%a use_multiple_values: true tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chmod 600 scored: false - id: 1.1.10 text: "Ensure that the Container Network Interface file ownership is set to root:root (Manual)" audit: | ps -ef | grep $kubeletbin | grep -- --cni-conf-dir | sed 's%.*cni-conf-dir[= ]\([^ ]*\).*%\1%' | xargs -I{} find {} -mindepth 1 | xargs --no-run-if-empty stat -c %U:%G find /var/lib/cni/networks -type f 2> /dev/null | xargs --no-run-if-empty stat -c %U:%G use_multiple_values: true tests: test_items: - flag: "root:root" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chown root:root scored: false - id: 1.1.11 text: "Ensure that the etcd data directory permissions are set to 700 or more restrictive (Automated)" audit: | DATA_DIR='' for d in $(ps -ef | grep $etcdbin | grep -- --data-dir | sed 's%.*data-dir[= ]\([^ ]*\).*%\1%'); do if test -d "$d"; then DATA_DIR="$d"; fi done if ! test -d "$DATA_DIR"; then DATA_DIR=$etcddatadir; fi stat -c permissions=%a "$DATA_DIR" tests: test_items: - flag: "permissions" compare: op: bitmask value: "700" remediation: | On the etcd server node, get the etcd data directory, passed as an argument --data-dir, from the command 'ps -ef | grep etcd'. Run the below command (based on the etcd data directory found above). For example, chmod 700 /var/lib/etcd scored: true - id: 1.1.12 text: "Ensure that the etcd data directory ownership is set to etcd:etcd (Automated)" audit: | DATA_DIR='' for d in $(ps -ef | grep $etcdbin | grep -- --data-dir | sed 's%.*data-dir[= ]\([^ ]*\).*%\1%'); do if test -d "$d"; then DATA_DIR="$d"; fi done if ! test -d "$DATA_DIR"; then DATA_DIR=$etcddatadir; fi stat -c %U:%G "$DATA_DIR" tests: test_items: - flag: "etcd:etcd" remediation: | On the etcd server node, get the etcd data directory, passed as an argument --data-dir, from the command 'ps -ef | grep etcd'. Run the below command (based on the etcd data directory found above). For example, chown etcd:etcd /var/lib/etcd scored: true - id: 1.1.13 text: "Ensure that the default administrative credential file permissions are set to 600 (Automated)" audit: | for adminconf in /etc/kubernetes/admin.conf /etc/kubernetes/super-admin.conf; do if test -e $adminconf; then stat -c "permissions=%a %n" $adminconf; fi; done use_multiple_values: true tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chmod 600 /etc/kubernetes/admin.conf On Kubernetes 1.29+ the super-admin.conf file should also be modified, if present. For example, chmod 600 /etc/kubernetes/super-admin.conf scored: true - id: 1.1.14 text: "Ensure that the default administrative credential file ownership is set to root:root (Automated)" audit: | for adminconf in /etc/kubernetes/admin.conf /etc/kubernetes/super-admin.conf; do if test -e $adminconf; then stat -c "ownership=%U:%G %n" $adminconf; fi; done use_multiple_values: true tests: test_items: - flag: "ownership" compare: op: eq value: "root:root" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chown root:root /etc/kubernetes/admin.conf On Kubernetes 1.29+ the super-admin.conf file should also be modified, if present. For example, chown root:root /etc/kubernetes/super-admin.conf scored: true - id: 1.1.15 text: "Ensure that the scheduler.conf file permissions are set to 600 or more restrictive (Automated)" audit: "/bin/sh -c 'if test -e $schedulerkubeconfig; then stat -c permissions=%a $schedulerkubeconfig; fi'" tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chmod 600 $schedulerkubeconfig scored: true - id: 1.1.16 text: "Ensure that the scheduler.conf file ownership is set to root:root (Automated)" audit: "/bin/sh -c 'if test -e $schedulerkubeconfig; then stat -c %U:%G $schedulerkubeconfig; fi'" tests: test_items: - flag: "root:root" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chown root:root $schedulerkubeconfig scored: true - id: 1.1.17 text: "Ensure that the controller-manager.conf file permissions are set to 600 or more restrictive (Automated)" audit: "/bin/sh -c 'if test -e $controllermanagerkubeconfig; then stat -c permissions=%a $controllermanagerkubeconfig; fi'" tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chmod 600 $controllermanagerkubeconfig scored: true - id: 1.1.18 text: "Ensure that the controller-manager.conf file ownership is set to root:root (Automated)" audit: "/bin/sh -c 'if test -e $controllermanagerkubeconfig; then stat -c %U:%G $controllermanagerkubeconfig; fi'" tests: test_items: - flag: "root:root" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chown root:root $controllermanagerkubeconfig scored: true - id: 1.1.19 text: "Ensure that the Kubernetes PKI directory and file ownership is set to root:root (Automated)" audit: "find /etc/kubernetes/pki/ | xargs stat -c %U:%G" use_multiple_values: true tests: test_items: - flag: "root:root" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chown -R root:root /etc/kubernetes/pki/ scored: true - id: 1.1.20 text: "Ensure that the Kubernetes PKI certificate file permissions are set to 644 or more restrictive (Manual)" audit: "find /etc/kubernetes/pki/ -name '*.crt' | xargs stat -c permissions=%a" use_multiple_values: true tests: test_items: - flag: "permissions" compare: op: bitmask value: "644" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chmod -R 644 /etc/kubernetes/pki/*.crt scored: false - id: 1.1.21 text: "Ensure that the Kubernetes PKI key file permissions are set to 600 (Manual)" audit: "find /etc/kubernetes/pki/ -name '*.key' | xargs stat -c permissions=%a" use_multiple_values: true tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chmod -R 600 /etc/kubernetes/pki/*.key scored: false - id: 1.2 text: "API Server" checks: - id: 1.2.1 text: "Ensure that the --anonymous-auth argument is set to false (Manual)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--anonymous-auth" compare: op: eq value: false remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the below parameter. --anonymous-auth=false scored: false - id: 1.2.2 text: "Ensure that the --token-auth-file parameter is not set (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--token-auth-file" set: false remediation: | Follow the documentation and configure alternate mechanisms for authentication. Then, edit the API server pod specification file $apiserverconf on the control plane node and remove the --token-auth-file= parameter. scored: true - id: 1.2.3 text: "Ensure that the --DenyServiceExternalIPs is set (Manual)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: bin_op: or test_items: - flag: "--enable-admission-plugins" compare: op: has value: "DenyServiceExternalIPs" remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and add the `DenyServiceExternalIPs` plugin to the enabled admission plugins, as such --enable-admission-plugin=DenyServiceExternalIPs. scored: false - id: 1.2.4 text: "Ensure that the --kubelet-client-certificate and --kubelet-client-key arguments are set as appropriate (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: bin_op: and test_items: - flag: "--kubelet-client-certificate" - flag: "--kubelet-client-key" remediation: | Follow the Kubernetes documentation and set up the TLS connection between the apiserver and kubelets. Then, edit API server pod specification file $apiserverconf on the control plane node and set the kubelet client certificate and key parameters as below. --kubelet-client-certificate= --kubelet-client-key= scored: true - id: 1.2.5 text: "Ensure that the --kubelet-certificate-authority argument is set as appropriate (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--kubelet-certificate-authority" remediation: | Follow the Kubernetes documentation and setup the TLS connection between the apiserver and kubelets. Then, edit the API server pod specification file $apiserverconf on the control plane node and set the --kubelet-certificate-authority parameter to the path to the cert file for the certificate authority. --kubelet-certificate-authority= scored: true - id: 1.2.6 text: "Ensure that the --authorization-mode argument is not set to AlwaysAllow (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--authorization-mode" compare: op: nothave value: "AlwaysAllow" remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the --authorization-mode parameter to values other than AlwaysAllow. One such example could be as below. --authorization-mode=RBAC scored: true - id: 1.2.7 text: "Ensure that the --authorization-mode argument includes Node (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--authorization-mode" compare: op: has value: "Node" remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the --authorization-mode parameter to a value that includes Node. --authorization-mode=Node,RBAC scored: true - id: 1.2.8 text: "Ensure that the --authorization-mode argument includes RBAC (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--authorization-mode" compare: op: has value: "RBAC" remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the --authorization-mode parameter to a value that includes RBAC, for example `--authorization-mode=Node,RBAC`. scored: true - id: 1.2.9 text: "Ensure that the admission control plugin EventRateLimit is set (Manual)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--enable-admission-plugins" compare: op: has value: "EventRateLimit" remediation: | Follow the Kubernetes documentation and set the desired limits in a configuration file. Then, edit the API server pod specification file $apiserverconf and set the below parameters. --enable-admission-plugins=...,EventRateLimit,... --admission-control-config-file= scored: false - id: 1.2.10 text: "Ensure that the admission control plugin AlwaysAdmit is not set (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: bin_op: or test_items: - flag: "--enable-admission-plugins" compare: op: nothave value: AlwaysAdmit - flag: "--enable-admission-plugins" set: false remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and either remove the --enable-admission-plugins parameter, or set it to a value that does not include AlwaysAdmit. scored: true - id: 1.2.11 text: "Ensure that the admission control plugin AlwaysPullImages is set (Manual)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--enable-admission-plugins" compare: op: has value: "AlwaysPullImages" remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the --enable-admission-plugins parameter to include AlwaysPullImages. --enable-admission-plugins=...,AlwaysPullImages,... scored: false - id: 1.2.12 text: "Ensure that the admission control plugin ServiceAccount is set (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: bin_op: or test_items: - flag: "--disable-admission-plugins" compare: op: nothave value: "ServiceAccount" - flag: "--disable-admission-plugins" set: false remediation: | Follow the documentation and create ServiceAccount objects as per your environment. Then, edit the API server pod specification file $apiserverconf on the control plane node and ensure that the --disable-admission-plugins parameter is set to a value that does not include ServiceAccount. scored: true - id: 1.2.13 text: "Ensure that the admission control plugin NamespaceLifecycle is set (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: bin_op: or test_items: - flag: "--disable-admission-plugins" compare: op: nothave value: "NamespaceLifecycle" - flag: "--disable-admission-plugins" set: false remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the --disable-admission-plugins parameter to ensure it does not include NamespaceLifecycle. scored: true - id: 1.2.14 text: "Ensure that the admission control plugin NodeRestriction is set (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--enable-admission-plugins" compare: op: has value: "NodeRestriction" remediation: | Follow the Kubernetes documentation and configure NodeRestriction plug-in on kubelets. Then, edit the API server pod specification file $apiserverconf on the control plane node and set the --enable-admission-plugins parameter to a value that includes NodeRestriction. --enable-admission-plugins=...,NodeRestriction,... scored: true - id: 1.2.15 text: "Ensure that the --profiling argument is set to false (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--profiling" compare: op: eq value: false remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the below parameter. --profiling=false scored: true - id: 1.2.16 text: "Ensure that the --audit-log-path argument is set (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--audit-log-path" remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the --audit-log-path parameter to a suitable path and file where you would like audit logs to be written, for example, --audit-log-path=/var/log/apiserver/audit.log scored: true - id: 1.2.17 text: "Ensure that the --audit-log-maxage argument is set to 30 or as appropriate (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--audit-log-maxage" compare: op: gte value: 30 remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the --audit-log-maxage parameter to 30 or as an appropriate number of days, for example, --audit-log-maxage=30 scored: true - id: 1.2.18 text: "Ensure that the --audit-log-maxbackup argument is set to 10 or as appropriate (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--audit-log-maxbackup" compare: op: gte value: 10 remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the --audit-log-maxbackup parameter to 10 or to an appropriate value. For example, --audit-log-maxbackup=10 scored: true - id: 1.2.19 text: "Ensure that the --audit-log-maxsize argument is set to 100 or as appropriate (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--audit-log-maxsize" compare: op: gte value: 100 remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the --audit-log-maxsize parameter to an appropriate size in MB. For example, to set it as 100 MB, --audit-log-maxsize=100 scored: true - id: 1.2.20 text: "Ensure that the --request-timeout argument is set as appropriate (Manual)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" type: manual remediation: | Edit the API server pod specification file $apiserverconf and set the below parameter as appropriate and if needed. For example, --request-timeout=300s scored: false - id: 1.2.21 text: "Ensure that the --service-account-lookup argument is set to true (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: bin_op: or test_items: - flag: "--service-account-lookup" set: false - flag: "--service-account-lookup" compare: op: eq value: true remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the below parameter. --service-account-lookup=true Alternatively, you can delete the --service-account-lookup parameter from this file so that the default takes effect. scored: true - id: 1.2.22 text: "Ensure that the --service-account-key-file argument is set as appropriate (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--service-account-key-file" remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the --service-account-key-file parameter to the public key file for service accounts. For example, --service-account-key-file= scored: true - id: 1.2.23 text: "Ensure that the --etcd-certfile and --etcd-keyfile arguments are set as appropriate (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: bin_op: and test_items: - flag: "--etcd-certfile" - flag: "--etcd-keyfile" remediation: | Follow the Kubernetes documentation and set up the TLS connection between the apiserver and etcd. Then, edit the API server pod specification file $apiserverconf on the control plane node and set the etcd certificate and key file parameters. --etcd-certfile= --etcd-keyfile= scored: true - id: 1.2.24 text: "Ensure that the --tls-cert-file and --tls-private-key-file arguments are set as appropriate (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: bin_op: and test_items: - flag: "--tls-cert-file" - flag: "--tls-private-key-file" remediation: | Follow the Kubernetes documentation and set up the TLS connection on the apiserver. Then, edit the API server pod specification file $apiserverconf on the control plane node and set the TLS certificate and private key file parameters. --tls-cert-file= --tls-private-key-file= scored: true - id: 1.2.25 text: "Ensure that the --client-ca-file argument is set as appropriate (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--client-ca-file" remediation: | Follow the Kubernetes documentation and set up the TLS connection on the apiserver. Then, edit the API server pod specification file $apiserverconf on the control plane node and set the client certificate authority file. --client-ca-file= scored: true - id: 1.2.26 text: "Ensure that the --etcd-cafile argument is set as appropriate (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--etcd-cafile" remediation: | Follow the Kubernetes documentation and set up the TLS connection between the apiserver and etcd. Then, edit the API server pod specification file $apiserverconf on the control plane node and set the etcd certificate authority file parameter. --etcd-cafile= scored: true - id: 1.2.27 text: "Ensure that the --encryption-provider-config argument is set as appropriate (Manual)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--encryption-provider-config" remediation: | Follow the Kubernetes documentation and configure a EncryptionConfig file. Then, edit the API server pod specification file $apiserverconf on the control plane node and set the --encryption-provider-config parameter to the path of that file. For example, --encryption-provider-config= scored: false - id: 1.2.28 text: "Ensure that encryption providers are appropriately configured (Manual)" audit: | ENCRYPTION_PROVIDER_CONFIG=$(ps -ef | grep $apiserverbin | grep -- --encryption-provider-config | sed 's%.*encryption-provider-config[= ]\([^ ]*\).*%\1%') if test -e $ENCRYPTION_PROVIDER_CONFIG; then grep -A1 'providers:' $ENCRYPTION_PROVIDER_CONFIG | tail -n1 | grep -o "[A-Za-z]*" | sed 's/^/provider=/'; fi tests: test_items: - flag: "provider" compare: op: valid_elements value: "aescbc,kms,secretbox" remediation: | Follow the Kubernetes documentation and configure a EncryptionConfig file. In this file, choose aescbc, kms or secretbox as the encryption provider. scored: false - id: 1.2.29 text: "Ensure that the API Server only makes use of Strong Cryptographic Ciphers (Manual)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--tls-cipher-suites" compare: op: valid_elements value: "TLS_AES_128_GCM_SHA256,TLS_AES_256_GCM_SHA384,TLS_CHACHA20_POLY1305_SHA256,TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256" remediation: | Edit the API server pod specification file /etc/kubernetes/manifests/kube-apiserver.yaml on the control plane node and set the below parameter. --tls-cipher-suites=TLS_AES_128_GCM_SHA256,TLS_AES_256_GCM_SHA384, TLS_CHACHA20_POLY1305_SHA256,TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 scored: false - id: 1.2.30 text: "Ensure that the --service-account-extend-token-expiration parameter is set to false (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--service-account-extend-token-expiration" compare: op: eq value: false remediation: | Edit the API server pod specification file /etc/kubernetes/manifests/kube-apiserver.yaml on the Control Plane node and set the --service-account-extend-token-expiration parameter to false. `--service-account-extend-token-expiration=false` By default, this parameter is set to true. scored: true - id: 1.3 text: "Controller Manager" checks: - id: 1.3.1 text: "Ensure that the --terminated-pod-gc-threshold argument is set as appropriate (Manual)" audit: "/bin/ps -ef | grep $controllermanagerbin | grep -v grep" tests: test_items: - flag: "--terminated-pod-gc-threshold" remediation: | Edit the Controller Manager pod specification file $controllermanagerconf on the control plane node and set the --terminated-pod-gc-threshold to an appropriate threshold, for example, --terminated-pod-gc-threshold=10 scored: false - id: 1.3.2 text: "Ensure that the --profiling argument is set to false (Automated)" audit: "/bin/ps -ef | grep $controllermanagerbin | grep -v grep" tests: test_items: - flag: "--profiling" compare: op: eq value: false remediation: | Edit the Controller Manager pod specification file $controllermanagerconf on the control plane node and set the below parameter. --profiling=false scored: true - id: 1.3.3 text: "Ensure that the --use-service-account-credentials argument is set to true (Automated)" audit: "/bin/ps -ef | grep $controllermanagerbin | grep -v grep" tests: test_items: - flag: "--use-service-account-credentials" compare: op: noteq value: false remediation: | Edit the Controller Manager pod specification file $controllermanagerconf on the control plane node to set the below parameter. --use-service-account-credentials=true scored: true - id: 1.3.4 text: "Ensure that the --service-account-private-key-file argument is set as appropriate (Automated)" audit: "/bin/ps -ef | grep $controllermanagerbin | grep -v grep" tests: test_items: - flag: "--service-account-private-key-file" remediation: | Edit the Controller Manager pod specification file $controllermanagerconf on the control plane node and set the --service-account-private-key-file parameter to the private key file for service accounts. --service-account-private-key-file= scored: true - id: 1.3.5 text: "Ensure that the --root-ca-file argument is set as appropriate (Automated)" audit: "/bin/ps -ef | grep $controllermanagerbin | grep -v grep" tests: test_items: - flag: "--root-ca-file" remediation: | Edit the Controller Manager pod specification file $controllermanagerconf on the control plane node and set the --root-ca-file parameter to the certificate bundle file`. --root-ca-file= scored: true - id: 1.3.6 text: "Ensure that the RotateKubeletServerCertificate argument is set to true (Automated)" audit: "/bin/ps -ef | grep $controllermanagerbin | grep -v grep" tests: bin_op: or test_items: - flag: "--feature-gates" compare: op: nothave value: "RotateKubeletServerCertificate=false" set: true - flag: "--feature-gates" set: false remediation: | Edit the Controller Manager pod specification file $controllermanagerconf on the control plane node and set the --feature-gates parameter to include RotateKubeletServerCertificate=true. --feature-gates=RotateKubeletServerCertificate=true scored: true - id: 1.3.7 text: "Ensure that the --bind-address argument is set to 127.0.0.1 (Automated)" audit: "/bin/ps -ef | grep $controllermanagerbin | grep -v grep" tests: bin_op: or test_items: - flag: "--bind-address" compare: op: eq value: "127.0.0.1" - flag: "--bind-address" set: false remediation: | Edit the Controller Manager pod specification file $controllermanagerconf on the control plane node and ensure the correct value for the --bind-address parameter scored: true - id: 1.4 text: "Scheduler" checks: - id: 1.4.1 text: "Ensure that the --profiling argument is set to false (Automated)" audit: "/bin/ps -ef | grep $schedulerbin | grep -v grep" tests: test_items: - flag: "--profiling" compare: op: eq value: false remediation: | Edit the Scheduler pod specification file $schedulerconf file on the control plane node and set the below parameter. --profiling=false scored: true - id: 1.4.2 text: "Ensure that the --bind-address argument is set to 127.0.0.1 (Automated)" audit: "/bin/ps -ef | grep $schedulerbin | grep -v grep" tests: bin_op: or test_items: - flag: "--bind-address" compare: op: eq value: "127.0.0.1" - flag: "--bind-address" set: false remediation: | Edit the Scheduler pod specification file $schedulerconf on the control plane node and ensure the correct value for the --bind-address parameter scored: true ================================================ FILE: cfg/cis-1.12/node.yaml ================================================ --- controls: version: "cis-1.12" id: 4 text: "Worker Node Security Configuration" type: "node" groups: - id: 4.1 text: "Worker Node Configuration Files" checks: - id: 4.1.1 text: "Ensure that the kubelet service file permissions are set to 600 or more restrictive (Automated)" audit: '/bin/sh -c ''if test -e $kubeletsvc; then stat -c permissions=%a $kubeletsvc; fi'' ' tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Run the below command (based on the file location on your system) on the each worker node. For example, chmod 600 $kubeletsvc scored: true - id: 4.1.2 text: "Ensure that the kubelet service file ownership is set to root:root (Automated)" audit: '/bin/sh -c "if test -e $kubeletsvc; then stat -c %U:%G $kubeletsvc; else echo \"File not found\"; fi"' tests: bin_op: or test_items: - flag: root:root - flag: "File not found" remediation: | Run the below command (based on the file location on your system) on the each worker node. For example, chown root:root $kubeletsvc scored: true - id: 4.1.3 text: "If proxy kubeconfig file exists ensure permissions are set to 600 or more restrictive (Manual)" audit: '/bin/sh -c ''if test -e $proxykubeconfig; then stat -c permissions=%a $proxykubeconfig; fi'' ' tests: bin_op: or test_items: - flag: "permissions" set: true compare: op: bitmask value: "600" remediation: | Run the below command (based on the file location on your system) on the each worker node. For example, chmod 600 $proxykubeconfig scored: false - id: 4.1.4 text: "If proxy kubeconfig file exists ensure ownership is set to root:root (Manual)" audit: '/bin/sh -c ''if test -e $proxykubeconfig; then stat -c %U:%G $proxykubeconfig; fi'' ' tests: bin_op: or test_items: - flag: root:root remediation: | Run the below command (based on the file location on your system) on the each worker node. For example, chown root:root $proxykubeconfig scored: false - id: 4.1.5 text: "Ensure that the --kubeconfig kubelet.conf file permissions are set to 600 or more restrictive (Automated)" audit: '/bin/sh -c ''if test -e $kubeletkubeconfig; then stat -c permissions=%a $kubeletkubeconfig; fi'' ' tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Run the below command (based on the file location on your system) on the each worker node. For example, chmod 600 $kubeletkubeconfig scored: true - id: 4.1.6 text: "Ensure that the --kubeconfig kubelet.conf file ownership is set to root:root (Automated)" audit: '/bin/sh -c ''if test -e $kubeletkubeconfig; then stat -c %U:%G $kubeletkubeconfig; fi'' ' tests: test_items: - flag: root:root remediation: | Run the below command (based on the file location on your system) on the each worker node. For example, chown root:root $kubeletkubeconfig scored: true - id: 4.1.7 text: "Ensure that the certificate authorities file permissions are set to 644 or more restrictive (Manual)" audit: | CAFILE=$(ps -ef | grep kubelet | grep -v apiserver | grep -- --client-ca-file= | awk -F '--client-ca-file=' '{print $2}' | awk '{print $1}' | uniq) if test -z $CAFILE; then CAFILE=$kubeletcafile; fi if test -e $CAFILE; then stat -c permissions=%a $CAFILE; fi tests: test_items: - flag: "permissions" compare: op: bitmask value: "644" remediation: | Run the following command to modify the file permissions of the --client-ca-file chmod 644 scored: false - id: 4.1.8 text: "Ensure that the client certificate authorities file ownership is set to root:root (Manual)" audit: | CAFILE=$(ps -ef | grep kubelet | grep -v apiserver | grep -- --client-ca-file= | awk -F '--client-ca-file=' '{print $2}' | awk '{print $1}' | uniq) if test -z $CAFILE; then CAFILE=$kubeletcafile; fi if test -e $CAFILE; then stat -c %U:%G $CAFILE; fi tests: test_items: - flag: root:root compare: op: eq value: root:root remediation: | Run the following command to modify the ownership of the --client-ca-file. chown root:root scored: false - id: 4.1.9 text: "If the kubelet config.yaml configuration file is being used validate permissions set to 600 or more restrictive (Automated)" audit: '/bin/sh -c ''if test -e $kubeletconf; then stat -c permissions=%a $kubeletconf; fi'' ' tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Run the following command (using the config file location identified in the Audit step) chmod 600 $kubeletconf scored: true - id: 4.1.10 text: "If the kubelet config.yaml configuration file is being used validate file ownership is set to root:root (Automated)" audit: '/bin/sh -c ''if test -e $kubeletconf; then stat -c %U:%G $kubeletconf; fi'' ' tests: test_items: - flag: root:root remediation: | Run the following command (using the config file location identified in the Audit step) chown root:root $kubeletconf scored: true - id: 4.2 text: "Kubelet" checks: - id: 4.2.1 text: "Ensure that the --anonymous-auth argument is set to false (Automated)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: "--anonymous-auth" path: '{.authentication.anonymous.enabled}' compare: op: eq value: false remediation: | If using a Kubelet config file, edit the file to set `authentication: anonymous: enabled` to `false`. If using executable arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_SYSTEM_PODS_ARGS variable. `--anonymous-auth=false` Based on your system, restart the kubelet service. For example, systemctl daemon-reload systemctl restart kubelet.service scored: true - id: 4.2.2 text: "Ensure that the --authorization-mode argument is not set to AlwaysAllow (Automated)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --authorization-mode path: '{.authorization.mode}' compare: op: nothave value: AlwaysAllow remediation: | If using a Kubelet config file, edit the file to set `authorization.mode` to Webhook. If using executable arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_AUTHZ_ARGS variable. --authorization-mode=Webhook Based on your system, restart the kubelet service. For example, systemctl daemon-reload systemctl restart kubelet.service scored: true - id: 4.2.3 text: "Ensure that the --client-ca-file argument is set as appropriate (Automated)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --client-ca-file path: '{.authentication.x509.clientCAFile}' remediation: | If using a Kubelet config file, edit the file to set `authentication.x509.clientCAFile` to the location of the client CA file. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_AUTHZ_ARGS variable. --client-ca-file= Based on your system, restart the kubelet service. For example, systemctl daemon-reload systemctl restart kubelet.service scored: true - id: 4.2.4 text: "Verify that if defined, the --read-only-port argument is set to 0 (Manual)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: bin_op: or test_items: - flag: "--read-only-port" path: '{.readOnlyPort}' compare: op: eq value: 0 - flag: "--read-only-port" path: '{.readOnlyPort}' set: false remediation: | If using a Kubelet config file, edit the file to set `readOnlyPort` to 0. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_SYSTEM_PODS_ARGS variable. --read-only-port=0 Based on your system, restart the kubelet service. For example, systemctl daemon-reload systemctl restart kubelet.service scored: false - id: 4.2.5 text: "Ensure that the --streaming-connection-idle-timeout argument is not set to 0 (Manual)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --streaming-connection-idle-timeout path: '{.streamingConnectionIdleTimeout}' compare: op: noteq value: 0 - flag: --streaming-connection-idle-timeout path: '{.streamingConnectionIdleTimeout}' set: false bin_op: or remediation: | If using a Kubelet config file, edit the file to set `streamingConnectionIdleTimeout` to a value other than 0. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_SYSTEM_PODS_ARGS variable. --streaming-connection-idle-timeout=5m Based on your system, restart the kubelet service. For example, systemctl daemon-reload systemctl restart kubelet.service scored: false - id: 4.2.6 text: "Ensure that the --make-iptables-util-chains argument is set to true (Automated)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --make-iptables-util-chains path: '{.makeIPTablesUtilChains}' compare: op: eq value: true - flag: --make-iptables-util-chains path: '{.makeIPTablesUtilChains}' set: false bin_op: or remediation: | If using a Kubelet config file, edit the file to set `makeIPTablesUtilChains` to `true`. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and remove the --make-iptables-util-chains argument from the KUBELET_SYSTEM_PODS_ARGS variable. Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: true - id: 4.2.7 text: "Ensure that the --hostname-override argument is not set (Manual)" # This is one of those properties that can only be set as a command line argument. # To check if the property is set as expected, we need to parse the kubelet command # instead reading the Kubelet Configuration file. audit: "/bin/ps -fC $kubeletbin" tests: test_items: - flag: --hostname-override set: false remediation: | Edit the kubelet service file $kubeletsvc on each worker node and remove the --hostname-override argument from the KUBELET_SYSTEM_PODS_ARGS variable. Based on your system, restart the kubelet service. For example, systemctl daemon-reload systemctl restart kubelet.service scored: false - id: 4.2.8 text: "Ensure that the eventRecordQPS argument is set to a level which ensures appropriate event capture (Manual)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --event-qps path: '{.eventRecordQPS}' compare: op: gte value: 0 - flag: --event-qps path: '{.eventRecordQPS}' set: false bin_op: or remediation: | If using a Kubelet config file, edit the file to set `eventRecordQPS` to an appropriate level. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_SYSTEM_PODS_ARGS variable. Based on your system, restart the kubelet service. For example, systemctl daemon-reload systemctl restart kubelet.service scored: false - id: 4.2.9 text: "Ensure that the --tls-cert-file and --tls-private-key-file arguments are set as appropriate (Manual)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --tls-cert-file path: '{.tlsCertFile}' - flag: --tls-private-key-file path: '{.tlsPrivateKeyFile}' remediation: | If using a Kubelet config file, edit the file to set `tlsCertFile` to the location of the certificate file to use to identify this Kubelet, and `tlsPrivateKeyFile` to the location of the corresponding private key file. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameters in KUBELET_CERTIFICATE_ARGS variable. --tls-cert-file= --tls-private-key-file= Based on your system, restart the kubelet service. For example, systemctl daemon-reload systemctl restart kubelet.service scored: false - id: 4.2.10 text: "Ensure that the --rotate-certificates argument is not set to false (Automated)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --rotate-certificates path: '{.rotateCertificates}' compare: op: eq value: true - flag: --rotate-certificates path: '{.rotateCertificates}' set: false bin_op: or remediation: | If using a Kubelet config file, edit the file to add the line `rotateCertificates` to `true` or remove it altogether to use the default value. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and remove --rotate-certificates=false argument from the KUBELET_CERTIFICATE_ARGS variable. Based on your system, restart the kubelet service. For example, systemctl daemon-reload systemctl restart kubelet.service scored: true - id: 4.2.11 text: "Verify that the RotateKubeletServerCertificate argument is set to true (Manual)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: bin_op: or test_items: - flag: RotateKubeletServerCertificate path: '{.featureGates.RotateKubeletServerCertificate}' compare: op: nothave value: false - flag: RotateKubeletServerCertificate path: '{.featureGates.RotateKubeletServerCertificate}' set: false remediation: | Edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_CERTIFICATE_ARGS variable. --feature-gates=RotateKubeletServerCertificate=true Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: false - id: 4.2.12 text: "Ensure that the Kubelet only makes use of Strong Cryptographic Ciphers (Manual)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --tls-cipher-suites path: '{range .tlsCipherSuites[:]}{}{'',''}{end}' compare: op: valid_elements value: 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 remediation: | If using a Kubelet config file, edit the file to set `tlsCipherSuites` to 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 or to a subset of these values. If using executable arguments, edit the kubelet service file $kubeletsvc on each worker node and set the --tls-cipher-suites parameter as follows, or to a subset of these values. --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 Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: false - id: 4.2.13 text: "Ensure that a limit is set on pod PIDs (Manual)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --pod-max-pids path: '{.podPidsLimit}' remediation: | Decide on an appropriate level for this parameter and set it, either via the --pod-max-pids command line parameter or the PodPidsLimit configuration file setting. scored: false - id: 4.2.14 text: "Ensure that the --seccomp-default parameter is set to true (Manual)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --seccomp-default path: '{.seccompDefault}' remediation: | Set the parameter, either via the --seccomp-default command line parameter or the seccompDefault configuration file setting. By default the seccomp profile is not enabled. scored: false - id: 4.3 text: "kube-proxy" checks: - id: 4.3.1 text: "Ensure that the kube-proxy metrics service is bound to localhost (Automated)" audit: "/bin/ps -fC $proxybin" audit_config: "/bin/sh -c 'if test -e $proxykubeconfig; then cat $proxykubeconfig; fi'" tests: bin_op: or test_items: - flag: "--metrics-bind-address" path: '{.metricsBindAddress}' compare: op: has value: "127.0.0.1" - flag: "--metrics-bind-address" path: '{.metricsBindAddress}' set: false remediation: | Modify or remove any values which bind the metrics service to a non-localhost address. The default value is 127.0.0.1:10249. scored: true ================================================ FILE: cfg/cis-1.12/policies.yaml ================================================ --- controls: version: "cis-1.12" id: 5 text: "Kubernetes Policies" type: "policies" groups: - id: 5.1 text: "RBAC and Service Accounts" checks: - id: 5.1.1 text: "Ensure that the cluster-admin role is only used where required (Manual)" audit: | kubectl get clusterrolebindings -o=custom-columns=NAME:.metadata.name,ROLE:.roleRef.name,SUBJECT:.subjects[*].name --no-headers | while read -r role_name role_binding subject do if [[ "${role_name}" != "cluster-admin" && "${role_binding}" == "cluster-admin" ]]; then is_compliant="false" else is_compliant="true" fi; echo "**role_name: ${role_name} role_binding: ${role_binding} subject: ${subject} is_compliant: ${is_compliant}" done use_multiple_values: true tests: test_items: - flag: "is_compliant" compare: op: eq value: true remediation: | Identify all clusterrolebindings to the cluster-admin role. Check if they are used and if they need this role or if they could use a role with fewer privileges. Where possible, first bind users to a lower privileged role and then remove the clusterrolebinding to the cluster-admin role : kubectl delete clusterrolebinding [name] Condition: is_compliant is false if rolename is not cluster-admin and rolebinding is cluster-admin. scored: false - id: 5.1.2 text: "Minimize access to secrets (Manual)" audit: "echo \"canGetListWatchSecretsAsSystemAuthenticated: $(kubectl auth can-i get,list,watch secrets --all-namespaces --as=system:authenticated)\"" tests: test_items: - flag: "canGetListWatchSecretsAsSystemAuthenticated" compare: op: eq value: no remediation: | Where possible, remove get, list and watch access to Secret objects in the cluster. scored: false - id: 5.1.3 text: "Minimize wildcard use in Roles and ClusterRoles (Manual)" audit: | # Check Roles kubectl get roles --all-namespaces -o custom-columns=ROLE_NAMESPACE:.metadata.namespace,ROLE_NAME:.metadata.name --no-headers | while read -r role_namespace role_name do role_rules=$(kubectl get role -n "${role_namespace}" "${role_name}" -o=json | jq -c '.rules') if echo "${role_rules}" | grep -q "\[\"\*\"\]"; then role_is_compliant="false" else role_is_compliant="true" fi; echo "**role_name: ${role_name} role_namespace: ${role_namespace} role_rules: ${role_rules} role_is_compliant: ${role_is_compliant}" done # Check ClusterRoles kubectl get clusterroles -o custom-columns=CLUSTERROLE_NAME:.metadata.name --no-headers | while read -r clusterrole_name do clusterrole_rules=$(kubectl get clusterrole "${clusterrole_name}" -o=json | jq -c '.rules') if echo "${clusterrole_rules}" | grep -q "\[\"\*\"\]"; then clusterrole_is_compliant="false" else clusterrole_is_compliant="true" fi; echo "**clusterrole_name: ${clusterrole_name} clusterrole_rules: ${clusterrole_rules} clusterrole_is_compliant: ${clusterrole_is_compliant}" done use_multiple_values: true tests: bin_op: or test_items: - flag: "role_is_compliant" compare: op: eq value: true set: true - flag: "clusterrole_is_compliant" compare: op: eq value: true set: true remediation: | Where possible replace any use of wildcards ["*"] in roles and clusterroles with specific objects or actions. Condition: role_is_compliant is false if ["*"] is found in rules. Condition: clusterrole_is_compliant is false if ["*"] is found in rules. scored: false - id: 5.1.4 text: "Minimize access to create pods (Manual)" audit: | echo "canCreatePodsAsSystemAuthenticated: $(kubectl auth can-i create pods --all-namespaces --as=system:authenticated)" tests: test_items: - flag: "canCreatePodsAsSystemAuthenticated" compare: op: eq value: no remediation: | Where possible, remove create access to pod objects in the cluster. scored: false - id: 5.1.5 text: "Ensure that default service accounts are not actively used (Manual)" audit: | kubectl get serviceaccount --all-namespaces --field-selector metadata.name=default -o=json | jq -r '.items[] | " namespace: \(.metadata.namespace), kind: \(.kind), name: \(.metadata.name), automountServiceAccountToken: \(.automountServiceAccountToken | if . == null then "notset" else . end )"' | xargs -L 1 use_multiple_values: true tests: test_items: - flag: "automountServiceAccountToken" compare: op: eq value: false set: true remediation: | Create explicit service accounts wherever a Kubernetes workload requires specific access to the Kubernetes API server. Modify the configuration of each default service account to include this value `automountServiceAccountToken: false`. scored: false - id: 5.1.6 text: "Ensure that Service Account Tokens are only mounted where necessary (Manual)" audit: | kubectl get pods --all-namespaces -o custom-columns=POD_NAMESPACE:.metadata.namespace,POD_NAME:.metadata.name,POD_SERVICE_ACCOUNT:.spec.serviceAccount,POD_IS_AUTOMOUNTSERVICEACCOUNTTOKEN:.spec.automountServiceAccountToken --no-headers | while read -r pod_namespace pod_name pod_service_account pod_is_automountserviceaccounttoken do # Retrieve automountServiceAccountToken's value for ServiceAccount and Pod, set to notset if null or . svacc_is_automountserviceaccounttoken=$(kubectl get serviceaccount -n "${pod_namespace}" "${pod_service_account}" -o json | jq -r '.automountServiceAccountToken' | sed -e 's//notset/g' -e 's/null/notset/g') pod_is_automountserviceaccounttoken=$(echo "${pod_is_automountserviceaccounttoken}" | sed -e 's//notset/g' -e 's/null/notset/g') if [ "${svacc_is_automountserviceaccounttoken}" = "false" ] && ( [ "${pod_is_automountserviceaccounttoken}" = "false" ] || [ "${pod_is_automountserviceaccounttoken}" = "notset" ] ); then is_compliant="true" elif [ "${svacc_is_automountserviceaccounttoken}" = "true" ] && [ "${pod_is_automountserviceaccounttoken}" = "false" ]; then is_compliant="true" else is_compliant="false" fi echo "**namespace: ${pod_namespace} pod_name: ${pod_name} service_account: ${pod_service_account} pod_is_automountserviceaccounttoken: ${pod_is_automountserviceaccounttoken} svacc_is_automountServiceAccountToken: ${svacc_is_automountserviceaccounttoken} is_compliant: ${is_compliant}" done use_multiple_values: true tests: test_items: - flag: "is_compliant" compare: op: eq value: true remediation: | Modify the definition of ServiceAccounts and Pods which do not need to mount service account tokens to disable it, with `automountServiceAccountToken: false`. If both the ServiceAccount and the Pod's .spec specify a value for automountServiceAccountToken, the Pod spec takes precedence. Condition: Pod is_compliant to true when - ServiceAccount is automountServiceAccountToken: false and Pod is automountServiceAccountToken: false or notset - ServiceAccount is automountServiceAccountToken: true notset and Pod is automountServiceAccountToken: false scored: false - id: 5.1.7 text: "Avoid use of system:masters group (Manual)" type: "manual" remediation: | Remove the system:masters group from all users in the cluster. scored: false - id: 5.1.8 text: "Limit use of the Bind, Impersonate and Escalate permissions in the Kubernetes cluster (Manual)" type: "manual" remediation: | Where possible, remove the impersonate, bind and escalate rights from subjects. scored: false - id: 5.1.9 text: "Minimize access to create persistent volumes (Manual)" type: "manual" remediation: | Where possible, remove create access to PersistentVolume objects in the cluster. scored: false - id: 5.1.10 text: "Minimize access to the proxy sub-resource of nodes (Manual)" type: "manual" remediation: | Where possible, remove access to the proxy sub-resource of node objects. scored: false - id: 5.1.11 text: "Minimize access to the approval sub-resource of certificatesigningrequests objects (Manual)" type: "manual" remediation: | Where possible, remove access to the approval sub-resource of certificatesigningrequests objects. scored: false - id: 5.1.12 text: "Minimize access to webhook configuration objects (Manual)" type: "manual" remediation: | Where possible, remove access to the validatingwebhookconfigurations or mutatingwebhookconfigurations objects scored: false - id: 5.1.13 text: "Minimize access to the service account token creation (Manual)" type: "manual" remediation: | Where possible, remove access to the token sub-resource of serviceaccount objects. scored: false - id: 5.2 text: "Pod Security Standards" checks: - id: 5.2.1 text: "Ensure that the cluster has at least one active policy control mechanism in place (Manual)" type: "manual" remediation: | Ensure that either Pod Security Admission or an external policy control system is in place for every namespace which contains user workloads. scored: false - id: 5.2.2 text: "Minimize the admission of privileged containers (Manual)" audit: | kubectl get pods --all-namespaces -o custom-columns=POD_NAME:.metadata.name,POD_NAMESPACE:.metadata.namespace --no-headers | while read -r pod_name pod_namespace do # Retrieve container(s) for each Pod. kubectl get pod "${pod_name}" --namespace "${pod_namespace}" -o json | jq -c '.spec.containers[]' | while read -r container do # Retrieve container's name. container_name=$(echo ${container} | jq -r '.name') # Retrieve container's .securityContext.privileged value. container_privileged=$(echo ${container} | jq -r '.securityContext.privileged' | sed -e 's/null/notset/g') if [ "${container_privileged}" = "false" ] || [ "${container_privileged}" = "notset" ] ; then echo "***pod_name: ${pod_name} container_name: ${container_name} pod_namespace: ${pod_namespace} is_container_privileged: ${container_privileged} is_compliant: true" else echo "***pod_name: ${pod_name} container_name: ${container_name} pod_namespace: ${pod_namespace} is_container_privileged: ${container_privileged} is_compliant: false" fi done done use_multiple_values: true tests: test_items: - flag: "is_compliant" compare: op: eq value: true remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of privileged containers. Audit: the audit list all pods' containers to retrieve their .securityContext.privileged value. Condition: is_compliant is false if container's `.securityContext.privileged` is set to `true`. Default: by default, there are no restrictions on the creation of privileged containers. scored: false - id: 5.2.3 text: "Minimize the admission of containers wishing to share the host process ID namespace (Manual)" audit: | kubectl get pods --all-namespaces -o custom-columns=POD_NAME:.metadata.name,POD_NAMESPACE:.metadata.namespace --no-headers | while read -r pod_name pod_namespace do # Retrieve spec.hostPID for each pod. pod_hostpid=$(kubectl get pod "${pod_name}" --namespace "${pod_namespace}" -o jsonpath='{.spec.hostPID}' 2>/dev/null) if [ -z "${pod_hostpid}" ]; then pod_hostpid="false" echo "***pod_name: ${pod_name} pod_namespace: ${pod_namespace} is_pod_hostpid: ${pod_hostpid} is_compliant: true" else echo "***pod_name: ${pod_name} pod_namespace: ${pod_namespace} is_pod_hostpid: ${pod_hostpid} is_compliant: false" fi done use_multiple_values: true tests: test_items: - flag: "is_compliant" compare: op: eq value: true remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of `hostPID` containers. Audit: the audit retrieves each Pod' spec.hostPID. Condition: is_compliant is false if Pod's spec.hostPID is set to `true`. Default: by default, there are no restrictions on the creation of hostPID containers. scored: false - id: 5.2.4 text: "Minimize the admission of containers wishing to share the host IPC namespace (Manual)" audit: | kubectl get pods --all-namespaces -o custom-columns=POD_NAME:.metadata.name,POD_NAMESPACE:.metadata.namespace --no-headers | while read -r pod_name pod_namespace do # Retrieve spec.hostIPC for each pod. pod_hostipc=$(kubectl get pod "${pod_name}" --namespace "${pod_namespace}" -o jsonpath='{.spec.hostIPC}' 2>/dev/null) if [ -z "${pod_hostipc}" ]; then pod_hostipc="false" echo "***pod_name: ${pod_name} pod_namespace: ${pod_namespace} is_pod_hostipc: ${pod_hostipc} is_compliant: true" else echo "***pod_name: ${pod_name} pod_namespace: ${pod_namespace} is_pod_hostipc: ${pod_hostipc} is_compliant: false" fi done use_multiple_values: true tests: test_items: - flag: "is_compliant" compare: op: eq value: true remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of `hostIPC` containers. Audit: the audit retrieves each Pod' spec.IPC. Condition: is_compliant is false if Pod's spec.hostIPC is set to `true`. Default: by default, there are no restrictions on the creation of hostIPC containers. scored: false - id: 5.2.5 text: "Minimize the admission of containers wishing to share the host network namespace (Manual)" audit: | kubectl get pods --all-namespaces -o custom-columns=POD_NAME:.metadata.name,POD_NAMESPACE:.metadata.namespace --no-headers | while read -r pod_name pod_namespace do # Retrieve spec.hostNetwork for each pod. pod_hostnetwork=$(kubectl get pod "${pod_name}" --namespace "${pod_namespace}" -o jsonpath='{.spec.hostNetwork}' 2>/dev/null) if [ -z "${pod_hostnetwork}" ]; then pod_hostnetwork="false" echo "***pod_name: ${pod_name} pod_namespace: ${pod_namespace} is_pod_hostnetwork: ${pod_hostnetwork} is_compliant: true" else echo "***pod_name: ${pod_name} pod_namespace: ${pod_namespace} is_pod_hostnetwork: ${pod_hostnetwork} is_compliant: false" fi done use_multiple_values: true tests: test_items: - flag: "is_compliant" compare: op: eq value: true remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of `hostNetwork` containers. Audit: the audit retrieves each Pod' spec.hostNetwork. Condition: is_compliant is false if Pod's spec.hostNetwork is set to `true`. Default: by default, there are no restrictions on the creation of hostNetwork containers. scored: false - id: 5.2.6 text: "Minimize the admission of containers with allowPrivilegeEscalation (Manual)" audit: | kubectl get pods --all-namespaces -o custom-columns=POD_NAME:.metadata.name,POD_NAMESPACE:.metadata.namespace --no-headers | while read -r pod_name pod_namespace do # Retrieve container(s) for each Pod. kubectl get pod "${pod_name}" --namespace "${pod_namespace}" -o json | jq -c '.spec.containers[]' | while read -r container do # Retrieve container's name container_name=$(echo ${container} | jq -r '.name') # Retrieve container's .securityContext.allowPrivilegeEscalation container_allowprivesc=$(echo ${container} | jq -r '.securityContext.allowPrivilegeEscalation' | sed -e 's/null/notset/g') if [ "${container_allowprivesc}" = "false" ] || [ "${container_allowprivesc}" = "notset" ]; then echo "***pod_name: ${pod_name} container_name: ${container_name} pod_namespace: ${pod_namespace} is_container_allowprivesc: ${container_allowprivesc} is_compliant: true" else echo "***pod_name: ${pod_name} container_name: ${container_name} pod_namespace: ${pod_namespace} is_container_allowprivesc: ${container_allowprivesc} is_compliant: false" fi done done use_multiple_values: true tests: test_items: - flag: "is_compliant" compare: op: eq value: true remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of containers with `.securityContext.allowPrivilegeEscalation` set to `true`. Audit: the audit retrieves each Pod's container(s) `.securityContext.allowPrivilegeEscalation`. Condition: is_compliant is false if container's `.securityContext.allowPrivilegeEscalation` is set to `true`. Default: If notset, privilege escalation is allowed (default to true). However if PSP/PSA is used with a `restricted` profile, privilege escalation is explicitly disallowed unless configured otherwise. scored: false - id: 5.2.7 text: "Minimize the admission of root containers (Manual)" type: "manual" remediation: | Create a policy for each namespace in the cluster, ensuring that either `MustRunAsNonRoot` or `MustRunAs` with the range of UIDs not including 0, is set. scored: false - id: 5.2.8 text: "Minimize the admission of containers with the NET_RAW capability (Manual)" type: "manual" remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of containers with the `NET_RAW` capability. scored: false - id: 5.2.9 text: "Minimize the admission of containers with capabilities assigned (Manual)" type: "manual" remediation: | Review the use of capabilities in applications running on your cluster. Where a namespace contains applications which do not require any Linux capabilities to operate consider adding a policy which forbids the admission of containers which do not drop all capabilities. scored: false - id: 5.2.10 text: "Minimize the admission of Windows HostProcess containers (Manual)" type: "manual" remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of containers that have `.securityContext.windowsOptions.hostProcess` set to `true`. scored: false - id: 5.2.11 text: "Minimize the admission of HostPath volumes (Manual)" type: "manual" remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of containers with `hostPath` volumes. scored: false - id: 5.2.12 text: "Minimize the admission of containers which use HostPorts (Manual)" type: "manual" remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of containers which use `hostPort` sections. scored: false - id: 5.3 text: "Network Policies and CNI" checks: - id: 5.3.1 text: "Ensure that the CNI in use supports NetworkPolicies (Manual)" type: "manual" remediation: | If the CNI plugin in use does not support network policies, consideration should be given to making use of a different plugin, or finding an alternate mechanism for restricting traffic in the Kubernetes cluster. scored: false - id: 5.3.2 text: "Ensure that all Namespaces have NetworkPolicies defined (Manual)" type: "manual" remediation: | Follow the documentation and create NetworkPolicy objects as you need them. scored: false - id: 5.4 text: "Secrets Management" checks: - id: 5.4.1 text: "Prefer using Secrets as files over Secrets as environment variables (Manual)" type: "manual" remediation: | If possible, rewrite application code to read Secrets from mounted secret files, rather than from environment variables. scored: false - id: 5.4.2 text: "Consider external secret storage (Manual)" type: "manual" remediation: | Refer to the Secrets management options offered by your cloud provider or a third-party secrets management solution. scored: false - id: 5.5 text: "Extensible Admission Control" checks: - id: 5.5.1 text: "Configure Image Provenance using ImagePolicyWebhook admission controller (Manual)" type: "manual" remediation: | Follow the Kubernetes documentation and setup image provenance. scored: false - id: 5.6 text: "General Policies" checks: - id: 5.6.1 text: "Create administrative boundaries between resources using namespaces (Manual)" type: "manual" remediation: | Follow the documentation and create namespaces for objects in your deployment as you need them. scored: false - id: 5.6.2 text: "Ensure that the seccomp profile is set to docker/default in your Pod definitions (Manual)" type: "manual" remediation: | Use `securityContext` to enable the docker/default seccomp profile in your pod definitions. An example is as below: securityContext: seccompProfile: type: RuntimeDefault scored: false - id: 5.6.3 text: "Apply SecurityContext to your Pods and Containers (Manual)" type: "manual" remediation: | Follow the Kubernetes documentation and apply SecurityContexts to your Pods. For a suggested list of SecurityContexts, you may refer to the CIS Security Benchmark for Docker Containers. scored: false - id: 5.6.4 text: "The default namespace should not be used (Manual)" type: "manual" remediation: | Ensure that namespaces are created to allow for appropriate segregation of Kubernetes resources and that all new resources are created in a specific namespace. scored: false ================================================ FILE: cfg/cis-1.20/config.yaml ================================================ --- ## Version-specific settings that override the values in cfg/config.yaml ================================================ FILE: cfg/cis-1.20/controlplane.yaml ================================================ --- controls: version: "cis-1.20" id: 3 text: "Control Plane Configuration" type: "controlplane" groups: - id: 3.1 text: "Authentication and Authorization" checks: - id: 3.1.1 text: "Client certificate authentication should not be used for users (Manual)" type: "manual" remediation: | Alternative mechanisms provided by Kubernetes such as the use of OIDC should be implemented in place of client certificates. scored: false - id: 3.2 text: "Logging" checks: - id: 3.2.1 text: "Ensure that a minimal audit policy is created (Manual)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--audit-policy-file" set: true remediation: | Create an audit policy file for your cluster. scored: false - id: 3.2.2 text: "Ensure that the audit policy covers key security concerns (Manual)" type: "manual" remediation: | Consider modification of the audit policy in use on the cluster to include these items, at a minimum. scored: false ================================================ FILE: cfg/cis-1.20/etcd.yaml ================================================ --- controls: version: "cis-1.20" id: 2 text: "Etcd Node Configuration" type: "etcd" groups: - id: 2 text: "Etcd Node Configuration Files" checks: - id: 2.1 text: "Ensure that the --cert-file and --key-file arguments are set as appropriate (Automated)" audit: "/bin/ps -ef | /bin/grep $etcdbin | /bin/grep -v grep" tests: bin_op: and test_items: - flag: "--cert-file" env: "ETCD_CERT_FILE" - flag: "--key-file" env: "ETCD_KEY_FILE" remediation: | Follow the etcd service documentation and configure TLS encryption. Then, edit the etcd pod specification file /etc/kubernetes/manifests/etcd.yaml on the master node and set the below parameters. --cert-file= --key-file= scored: true - id: 2.2 text: "Ensure that the --client-cert-auth argument is set to true (Automated)" audit: "/bin/ps -ef | /bin/grep $etcdbin | /bin/grep -v grep" tests: test_items: - flag: "--client-cert-auth" env: "ETCD_CLIENT_CERT_AUTH" compare: op: eq value: true remediation: | Edit the etcd pod specification file $etcdconf on the master node and set the below parameter. --client-cert-auth="true" scored: true - id: 2.3 text: "Ensure that the --auto-tls argument is not set to true (Automated)" audit: "/bin/ps -ef | /bin/grep $etcdbin | /bin/grep -v grep" tests: bin_op: or test_items: - flag: "--auto-tls" env: "ETCD_AUTO_TLS" set: false - flag: "--auto-tls" env: "ETCD_AUTO_TLS" compare: op: eq value: false remediation: | Edit the etcd pod specification file $etcdconf on the master node and either remove the --auto-tls parameter or set it to false. --auto-tls=false scored: true - id: 2.4 text: "Ensure that the --peer-cert-file and --peer-key-file arguments are set as appropriate (Automated)" audit: "/bin/ps -ef | /bin/grep $etcdbin | /bin/grep -v grep" tests: bin_op: and test_items: - flag: "--peer-cert-file" env: "ETCD_PEER_CERT_FILE" - flag: "--peer-key-file" env: "ETCD_PEER_KEY_FILE" remediation: | Follow the etcd service documentation and configure peer TLS encryption as appropriate for your etcd cluster. Then, edit the etcd pod specification file $etcdconf on the master node and set the below parameters. --peer-client-file= --peer-key-file= scored: true - id: 2.5 text: "Ensure that the --peer-client-cert-auth argument is set to true (Automated)" audit: "/bin/ps -ef | /bin/grep $etcdbin | /bin/grep -v grep" tests: test_items: - flag: "--peer-client-cert-auth" env: "ETCD_PEER_CLIENT_CERT_AUTH" compare: op: eq value: true remediation: | Edit the etcd pod specification file $etcdconf on the master node and set the below parameter. --peer-client-cert-auth=true scored: true - id: 2.6 text: "Ensure that the --peer-auto-tls argument is not set to true (Automated)" audit: "/bin/ps -ef | /bin/grep $etcdbin | /bin/grep -v grep" tests: bin_op: or test_items: - flag: "--peer-auto-tls" env: "ETCD_PEER_AUTO_TLS" set: false - flag: "--peer-auto-tls" env: "ETCD_PEER_AUTO_TLS" compare: op: eq value: false remediation: | Edit the etcd pod specification file $etcdconf on the master node and either remove the --peer-auto-tls parameter or set it to false. --peer-auto-tls=false scored: true - id: 2.7 text: "Ensure that a unique Certificate Authority is used for etcd (Manual)" audit: "/bin/ps -ef | /bin/grep $etcdbin | /bin/grep -v grep" tests: test_items: - flag: "--trusted-ca-file" env: "ETCD_TRUSTED_CA_FILE" remediation: | [Manual test] Follow the etcd documentation and create a dedicated certificate authority setup for the etcd service. Then, edit the etcd pod specification file $etcdconf on the master node and set the below parameter. --trusted-ca-file= scored: false ================================================ FILE: cfg/cis-1.20/master.yaml ================================================ --- controls: version: "cis-1.20" id: 1 text: "Master Node Security Configuration" type: "master" groups: - id: 1.1 text: "Master Node Configuration Files" checks: - id: 1.1.1 text: "Ensure that the API server pod specification file permissions are set to 644 or more restrictive (Automated)" audit: "/bin/sh -c 'if test -e $apiserverconf; then stat -c permissions=%a $apiserverconf; fi'" tests: test_items: - flag: "permissions" compare: op: bitmask value: "644" remediation: | Run the below command (based on the file location on your system) on the master node. For example, chmod 644 $apiserverconf scored: true - id: 1.1.2 text: "Ensure that the API server pod specification file ownership is set to root:root (Automated)" audit: "/bin/sh -c 'if test -e $apiserverconf; then stat -c %U:%G $apiserverconf; fi'" tests: test_items: - flag: "root:root" remediation: | Run the below command (based on the file location on your system) on the master node. For example, chown root:root $apiserverconf scored: true - id: 1.1.3 text: "Ensure that the controller manager pod specification file permissions are set to 644 or more restrictive (Automated)" audit: "/bin/sh -c 'if test -e $controllermanagerconf; then stat -c permissions=%a $controllermanagerconf; fi'" tests: test_items: - flag: "permissions" compare: op: bitmask value: "644" remediation: | Run the below command (based on the file location on your system) on the master node. For example, chmod 644 $controllermanagerconf scored: true - id: 1.1.4 text: "Ensure that the controller manager pod specification file ownership is set to root:root (Automated)" audit: "/bin/sh -c 'if test -e $controllermanagerconf; then stat -c %U:%G $controllermanagerconf; fi'" tests: test_items: - flag: "root:root" remediation: | Run the below command (based on the file location on your system) on the master node. For example, chown root:root $controllermanagerconf scored: true - id: 1.1.5 text: "Ensure that the scheduler pod specification file permissions are set to 644 or more restrictive (Automated)" audit: "/bin/sh -c 'if test -e $schedulerconf; then stat -c permissions=%a $schedulerconf; fi'" tests: test_items: - flag: "permissions" compare: op: bitmask value: "644" remediation: | Run the below command (based on the file location on your system) on the master node. For example, chmod 644 $schedulerconf scored: true - id: 1.1.6 text: "Ensure that the scheduler pod specification file ownership is set to root:root (Automated)" audit: "/bin/sh -c 'if test -e $schedulerconf; then stat -c %U:%G $schedulerconf; fi'" tests: test_items: - flag: "root:root" remediation: | Run the below command (based on the file location on your system) on the master node. For example, chown root:root $schedulerconf scored: true - id: 1.1.7 text: "Ensure that the etcd pod specification file permissions are set to 644 or more restrictive (Automated)" audit: "/bin/sh -c 'if test -e $etcdconf; then find $etcdconf -name '*etcd*' | xargs stat -c permissions=%a; fi'" use_multiple_values: true tests: test_items: - flag: "permissions" compare: op: bitmask value: "644" remediation: | Run the below command (based on the file location on your system) on the master node. For example, chmod 644 $etcdconf scored: true - id: 1.1.8 text: "Ensure that the etcd pod specification file ownership is set to root:root (Automated)" audit: "/bin/sh -c 'if test -e $etcdconf; then find $etcdconf -name '*etcd*' | xargs stat -c %U:%G; fi'" use_multiple_values: true tests: test_items: - flag: "root:root" remediation: | Run the below command (based on the file location on your system) on the master node. For example, chown root:root $etcdconf scored: true - id: 1.1.9 text: "Ensure that the Container Network Interface file permissions are set to 644 or more restrictive (Manual)" audit: | ps -ef | grep $kubeletbin | grep -- --cni-conf-dir | sed 's%.*cni-conf-dir[= ]\([^ ]*\).*%\1%' | xargs -I{} find {} -mindepth 1 | xargs --no-run-if-empty stat -c permissions=%a find /var/lib/cni/networks -type f 2> /dev/null | xargs --no-run-if-empty stat -c permissions=%a use_multiple_values: true tests: test_items: - flag: "permissions" compare: op: bitmask value: "644" remediation: | Run the below command (based on the file location on your system) on the master node. For example, chmod 644 scored: false - id: 1.1.10 text: "Ensure that the Container Network Interface file ownership is set to root:root (Manual)" audit: | ps -ef | grep $kubeletbin | grep -- --cni-conf-dir | sed 's%.*cni-conf-dir[= ]\([^ ]*\).*%\1%' | xargs -I{} find {} -mindepth 1 | xargs --no-run-if-empty stat -c %U:%G find /var/lib/cni/networks -type f 2> /dev/null | xargs --no-run-if-empty stat -c %U:%G use_multiple_values: true tests: test_items: - flag: "root:root" remediation: | Run the below command (based on the file location on your system) on the master node. For example, chown root:root scored: false - id: 1.1.11 text: "Ensure that the etcd data directory permissions are set to 700 or more restrictive (Automated)" audit: | DATA_DIR='' for d in $(ps -ef | grep $etcdbin | grep -- --data-dir | sed 's%.*data-dir[= ]\([^ ]*\).*%\1%'); do if test -d "$d"; then DATA_DIR="$d"; fi done if ! test -d "$DATA_DIR"; then DATA_DIR=$etcddatadir; fi stat -c permissions=%a "$DATA_DIR" tests: test_items: - flag: "permissions" compare: op: bitmask value: "700" remediation: | On the etcd server node, get the etcd data directory, passed as an argument --data-dir, from the below command: ps -ef | grep etcd Run the below command (based on the etcd data directory found above). For example, chmod 700 /var/lib/etcd scored: true - id: 1.1.12 text: "Ensure that the etcd data directory ownership is set to etcd:etcd (Automated)" audit: | DATA_DIR='' for d in $(ps -ef | grep $etcdbin | grep -- --data-dir | sed 's%.*data-dir[= ]\([^ ]*\).*%\1%'); do if test -d "$d"; then DATA_DIR="$d"; fi done if ! test -d "$DATA_DIR"; then DATA_DIR=$etcddatadir; fi stat -c %U:%G "$DATA_DIR" tests: test_items: - flag: "etcd:etcd" remediation: | On the etcd server node, get the etcd data directory, passed as an argument --data-dir, from the below command: ps -ef | grep etcd Run the below command (based on the etcd data directory found above). For example, chown etcd:etcd /var/lib/etcd scored: true - id: 1.1.13 text: "Ensure that the admin.conf file permissions are set to 644 or more restrictive (Automated)" audit: "/bin/sh -c 'if test -e /etc/kubernetes/admin.conf; then stat -c permissions=%a /etc/kubernetes/admin.conf; fi'" tests: test_items: - flag: "permissions" compare: op: bitmask value: "644" remediation: | Run the below command (based on the file location on your system) on the master node. For example, chmod 644 /etc/kubernetes/admin.conf scored: true - id: 1.1.14 text: "Ensure that the admin.conf file ownership is set to root:root (Automated)" audit: "/bin/sh -c 'if test -e /etc/kubernetes/admin.conf; then stat -c %U:%G /etc/kubernetes/admin.conf; fi'" tests: test_items: - flag: "root:root" remediation: | Run the below command (based on the file location on your system) on the master node. For example, chown root:root /etc/kubernetes/admin.conf scored: true - id: 1.1.15 text: "Ensure that the scheduler.conf file permissions are set to 644 or more restrictive (Automated)" audit: "/bin/sh -c 'if test -e $schedulerkubeconfig; then stat -c permissions=%a $schedulerkubeconfig; fi'" tests: test_items: - flag: "permissions" compare: op: bitmask value: "644" remediation: | Run the below command (based on the file location on your system) on the master node. For example, chmod 644 $schedulerkubeconfig scored: true - id: 1.1.16 text: "Ensure that the scheduler.conf file ownership is set to root:root (Automated)" audit: "/bin/sh -c 'if test -e $schedulerkubeconfig; then stat -c %U:%G $schedulerkubeconfig; fi'" tests: test_items: - flag: "root:root" remediation: | Run the below command (based on the file location on your system) on the master node. For example, chown root:root $schedulerkubeconfig scored: true - id: 1.1.17 text: "Ensure that the controller-manager.conf file permissions are set to 644 or more restrictive (Automated)" audit: "/bin/sh -c 'if test -e $controllermanagerkubeconfig; then stat -c permissions=%a $controllermanagerkubeconfig; fi'" tests: test_items: - flag: "permissions" compare: op: bitmask value: "644" remediation: | Run the below command (based on the file location on your system) on the master node. For example, chmod 644 $controllermanagerkubeconfig scored: true - id: 1.1.18 text: "Ensure that the controller-manager.conf file ownership is set to root:root (Automated)" audit: "/bin/sh -c 'if test -e $controllermanagerkubeconfig; then stat -c %U:%G $controllermanagerkubeconfig; fi'" tests: test_items: - flag: "root:root" remediation: | Run the below command (based on the file location on your system) on the master node. For example, chown root:root $controllermanagerkubeconfig scored: true - id: 1.1.19 text: "Ensure that the Kubernetes PKI directory and file ownership is set to root:root (Automated)" audit: "find /etc/kubernetes/pki/ | xargs stat -c %U:%G" use_multiple_values: true tests: test_items: - flag: "root:root" remediation: | Run the below command (based on the file location on your system) on the master node. For example, chown -R root:root /etc/kubernetes/pki/ scored: true - id: 1.1.20 text: "Ensure that the Kubernetes PKI certificate file permissions are set to 644 or more restrictive (Manual)" audit: "find /etc/kubernetes/pki/ -name '*.crt' | xargs stat -c permissions=%a" use_multiple_values: true tests: test_items: - flag: "permissions" compare: op: bitmask value: "644" remediation: | Run the below command (based on the file location on your system) on the master node. For example, chmod -R 644 /etc/kubernetes/pki/*.crt scored: false - id: 1.1.21 text: "Ensure that the Kubernetes PKI key file permissions are set to 600 (Manual)" audit: "find /etc/kubernetes/pki/ -name '*.key' | xargs stat -c permissions=%a" use_multiple_values: true tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Run the below command (based on the file location on your system) on the master node. For example, chmod -R 600 /etc/kubernetes/pki/*.key scored: false - id: 1.2 text: "API Server" checks: - id: 1.2.1 text: "Ensure that the --anonymous-auth argument is set to false (Manual)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--anonymous-auth" compare: op: eq value: false remediation: | Edit the API server pod specification file $apiserverconf on the master node and set the below parameter. --anonymous-auth=false scored: false - id: 1.2.2 text: "Ensure that the --token-auth-file parameter is not set (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--token-auth-file" set: false remediation: | Follow the documentation and configure alternate mechanisms for authentication. Then, edit the API server pod specification file $apiserverconf on the master node and remove the --token-auth-file= parameter. scored: true - id: 1.2.3 text: "Ensure that the --kubelet-https argument is set to true (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: bin_op: or test_items: - flag: "--kubelet-https" compare: op: eq value: true - flag: "--kubelet-https" set: false remediation: | Edit the API server pod specification file $apiserverconf on the master node and remove the --kubelet-https parameter. scored: true - id: 1.2.4 text: "Ensure that the --kubelet-client-certificate and --kubelet-client-key arguments are set as appropriate (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: bin_op: and test_items: - flag: "--kubelet-client-certificate" - flag: "--kubelet-client-key" remediation: | Follow the Kubernetes documentation and set up the TLS connection between the apiserver and kubelets. Then, edit API server pod specification file $apiserverconf on the master node and set the kubelet client certificate and key parameters as below. --kubelet-client-certificate= --kubelet-client-key= scored: true - id: 1.2.5 text: "Ensure that the --kubelet-certificate-authority argument is set as appropriate (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--kubelet-certificate-authority" remediation: | Follow the Kubernetes documentation and setup the TLS connection between the apiserver and kubelets. Then, edit the API server pod specification file $apiserverconf on the master node and set the --kubelet-certificate-authority parameter to the path to the cert file for the certificate authority. --kubelet-certificate-authority= scored: true - id: 1.2.6 text: "Ensure that the --authorization-mode argument is not set to AlwaysAllow (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--authorization-mode" compare: op: nothave value: "AlwaysAllow" remediation: | Edit the API server pod specification file $apiserverconf on the master node and set the --authorization-mode parameter to values other than AlwaysAllow. One such example could be as below. --authorization-mode=RBAC scored: true - id: 1.2.7 text: "Ensure that the --authorization-mode argument includes Node (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--authorization-mode" compare: op: has value: "Node" remediation: | Edit the API server pod specification file $apiserverconf on the master node and set the --authorization-mode parameter to a value that includes Node. --authorization-mode=Node,RBAC scored: true - id: 1.2.8 text: "Ensure that the --authorization-mode argument includes RBAC (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--authorization-mode" compare: op: has value: "RBAC" remediation: | Edit the API server pod specification file $apiserverconf on the master node and set the --authorization-mode parameter to a value that includes RBAC, for example: --authorization-mode=Node,RBAC scored: true - id: 1.2.9 text: "Ensure that the admission control plugin EventRateLimit is set (Manual)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--enable-admission-plugins" compare: op: has value: "EventRateLimit" remediation: | Follow the Kubernetes documentation and set the desired limits in a configuration file. Then, edit the API server pod specification file $apiserverconf and set the below parameters. --enable-admission-plugins=...,EventRateLimit,... --admission-control-config-file= scored: false - id: 1.2.10 text: "Ensure that the admission control plugin AlwaysAdmit is not set (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: bin_op: or test_items: - flag: "--enable-admission-plugins" compare: op: nothave value: AlwaysAdmit - flag: "--enable-admission-plugins" set: false remediation: | Edit the API server pod specification file $apiserverconf on the master node and either remove the --enable-admission-plugins parameter, or set it to a value that does not include AlwaysAdmit. scored: true - id: 1.2.11 text: "Ensure that the admission control plugin AlwaysPullImages is set (Manual)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--enable-admission-plugins" compare: op: has value: "AlwaysPullImages" remediation: | Edit the API server pod specification file $apiserverconf on the master node and set the --enable-admission-plugins parameter to include AlwaysPullImages. --enable-admission-plugins=...,AlwaysPullImages,... scored: false - id: 1.2.12 text: "Ensure that the admission control plugin SecurityContextDeny is set if PodSecurityPolicy is not used (Manual)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: bin_op: or test_items: - flag: "--enable-admission-plugins" compare: op: has value: "SecurityContextDeny" - flag: "--enable-admission-plugins" compare: op: has value: "PodSecurityPolicy" remediation: | Edit the API server pod specification file $apiserverconf on the master node and set the --enable-admission-plugins parameter to include SecurityContextDeny, unless PodSecurityPolicy is already in place. --enable-admission-plugins=...,SecurityContextDeny,... scored: false - id: 1.2.13 text: "Ensure that the admission control plugin ServiceAccount is set (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: bin_op: or test_items: - flag: "--disable-admission-plugins" compare: op: nothave value: "ServiceAccount" - flag: "--disable-admission-plugins" set: false remediation: | Follow the documentation and create ServiceAccount objects as per your environment. Then, edit the API server pod specification file $apiserverconf on the master node and ensure that the --disable-admission-plugins parameter is set to a value that does not include ServiceAccount. scored: true - id: 1.2.14 text: "Ensure that the admission control plugin NamespaceLifecycle is set (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: bin_op: or test_items: - flag: "--disable-admission-plugins" compare: op: nothave value: "NamespaceLifecycle" - flag: "--disable-admission-plugins" set: false remediation: | Edit the API server pod specification file $apiserverconf on the master node and set the --disable-admission-plugins parameter to ensure it does not include NamespaceLifecycle. scored: true - id: 1.2.15 text: "Ensure that the admission control plugin PodSecurityPolicy is set (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--enable-admission-plugins" compare: op: has value: "PodSecurityPolicy" remediation: | Follow the documentation and create Pod Security Policy objects as per your environment. Then, edit the API server pod specification file $apiserverconf on the master node and set the --enable-admission-plugins parameter to a value that includes PodSecurityPolicy: --enable-admission-plugins=...,PodSecurityPolicy,... Then restart the API Server. scored: true - id: 1.2.16 text: "Ensure that the admission control plugin NodeRestriction is set (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--enable-admission-plugins" compare: op: has value: "NodeRestriction" remediation: | Follow the Kubernetes documentation and configure NodeRestriction plug-in on kubelets. Then, edit the API server pod specification file $apiserverconf on the master node and set the --enable-admission-plugins parameter to a value that includes NodeRestriction. --enable-admission-plugins=...,NodeRestriction,... scored: true - id: 1.2.17 text: "Ensure that the --insecure-bind-address argument is not set (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--insecure-bind-address" set: false remediation: | Edit the API server pod specification file $apiserverconf on the master node and remove the --insecure-bind-address parameter. scored: true - id: 1.2.18 text: "Ensure that the --insecure-port argument is set to 0 (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--insecure-port" compare: op: eq value: 0 remediation: | Edit the API server pod specification file $apiserverconf on the master node and set the below parameter. --insecure-port=0 scored: true - id: 1.2.19 text: "Ensure that the --secure-port argument is not set to 0 (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: bin_op: or test_items: - flag: "--secure-port" compare: op: gt value: 0 - flag: "--secure-port" set: false remediation: | Edit the API server pod specification file $apiserverconf on the master node and either remove the --secure-port parameter or set it to a different (non-zero) desired port. scored: true - id: 1.2.20 text: "Ensure that the --profiling argument is set to false (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--profiling" compare: op: eq value: false remediation: | Edit the API server pod specification file $apiserverconf on the master node and set the below parameter. --profiling=false scored: true - id: 1.2.21 text: "Ensure that the --audit-log-path argument is set (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--audit-log-path" remediation: | Edit the API server pod specification file $apiserverconf on the master node and set the --audit-log-path parameter to a suitable path and file where you would like audit logs to be written, for example: --audit-log-path=/var/log/apiserver/audit.log scored: true - id: 1.2.22 text: "Ensure that the --audit-log-maxage argument is set to 30 or as appropriate (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--audit-log-maxage" compare: op: gte value: 30 remediation: | Edit the API server pod specification file $apiserverconf on the master node and set the --audit-log-maxage parameter to 30 or as an appropriate number of days: --audit-log-maxage=30 scored: true - id: 1.2.23 text: "Ensure that the --audit-log-maxbackup argument is set to 10 or as appropriate (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--audit-log-maxbackup" compare: op: gte value: 10 remediation: | Edit the API server pod specification file $apiserverconf on the master node and set the --audit-log-maxbackup parameter to 10 or to an appropriate value. --audit-log-maxbackup=10 scored: true - id: 1.2.24 text: "Ensure that the --audit-log-maxsize argument is set to 100 or as appropriate (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--audit-log-maxsize" compare: op: gte value: 100 remediation: | Edit the API server pod specification file $apiserverconf on the master node and set the --audit-log-maxsize parameter to an appropriate size in MB. For example, to set it as 100 MB: --audit-log-maxsize=100 scored: true - id: 1.2.25 text: "Ensure that the --request-timeout argument is set as appropriate (Manual)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" type: manual remediation: | Edit the API server pod specification file $apiserverconf and set the below parameter as appropriate and if needed. For example, --request-timeout=300s scored: false - id: 1.2.26 text: "Ensure that the --service-account-lookup argument is set to true (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: bin_op: or test_items: - flag: "--service-account-lookup" set: false - flag: "--service-account-lookup" compare: op: eq value: true remediation: | Edit the API server pod specification file $apiserverconf on the master node and set the below parameter. --service-account-lookup=true Alternatively, you can delete the --service-account-lookup parameter from this file so that the default takes effect. scored: true - id: 1.2.27 text: "Ensure that the --service-account-key-file argument is set as appropriate (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--service-account-key-file" remediation: | Edit the API server pod specification file $apiserverconf on the master node and set the --service-account-key-file parameter to the public key file for service accounts: --service-account-key-file= scored: true - id: 1.2.28 text: "Ensure that the --etcd-certfile and --etcd-keyfile arguments are set as appropriate (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: bin_op: and test_items: - flag: "--etcd-certfile" - flag: "--etcd-keyfile" remediation: | Follow the Kubernetes documentation and set up the TLS connection between the apiserver and etcd. Then, edit the API server pod specification file $apiserverconf on the master node and set the etcd certificate and key file parameters. --etcd-certfile= --etcd-keyfile= scored: true - id: 1.2.29 text: "Ensure that the --tls-cert-file and --tls-private-key-file arguments are set as appropriate (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: bin_op: and test_items: - flag: "--tls-cert-file" - flag: "--tls-private-key-file" remediation: | Follow the Kubernetes documentation and set up the TLS connection on the apiserver. Then, edit the API server pod specification file $apiserverconf on the master node and set the TLS certificate and private key file parameters. --tls-cert-file= --tls-private-key-file= scored: true - id: 1.2.30 text: "Ensure that the --client-ca-file argument is set as appropriate (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--client-ca-file" remediation: | Follow the Kubernetes documentation and set up the TLS connection on the apiserver. Then, edit the API server pod specification file $apiserverconf on the master node and set the client certificate authority file. --client-ca-file= scored: true - id: 1.2.31 text: "Ensure that the --etcd-cafile argument is set as appropriate (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--etcd-cafile" remediation: | Follow the Kubernetes documentation and set up the TLS connection between the apiserver and etcd. Then, edit the API server pod specification file $apiserverconf on the master node and set the etcd certificate authority file parameter. --etcd-cafile= scored: true - id: 1.2.32 text: "Ensure that the --encryption-provider-config argument is set as appropriate (Manual)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--encryption-provider-config" remediation: | Follow the Kubernetes documentation and configure a EncryptionConfig file. Then, edit the API server pod specification file $apiserverconf on the master node and set the --encryption-provider-config parameter to the path of that file: --encryption-provider-config= scored: false - id: 1.2.33 text: "Ensure that encryption providers are appropriately configured (Manual)" audit: | ENCRYPTION_PROVIDER_CONFIG=$(ps -ef | grep $apiserverbin | grep -- --encryption-provider-config | sed 's%.*encryption-provider-config[= ]\([^ ]*\).*%\1%') if test -e $ENCRYPTION_PROVIDER_CONFIG; then grep -A1 'providers:' $ENCRYPTION_PROVIDER_CONFIG | tail -n1 | grep -o "[A-Za-z]*" | sed 's/^/provider=/'; fi tests: test_items: - flag: "provider" compare: op: valid_elements value: "aescbc,kms,secretbox" remediation: | Follow the Kubernetes documentation and configure a EncryptionConfig file. In this file, choose aescbc, kms or secretbox as the encryption provider. scored: false - id: 1.2.34 text: "Ensure that the API Server only makes use of Strong Cryptographic Ciphers (Manual)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--tls-cipher-suites" compare: op: valid_elements value: "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" remediation: | Edit the API server pod specification file /etc/kubernetes/manifests/kube-apiserver.yaml on the master node and set the below parameter. --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 scored: false - id: 1.3 text: "Controller Manager" checks: - id: 1.3.1 text: "Ensure that the --terminated-pod-gc-threshold argument is set as appropriate (Manual)" audit: "/bin/ps -ef | grep $controllermanagerbin | grep -v grep" tests: test_items: - flag: "--terminated-pod-gc-threshold" remediation: | Edit the Controller Manager pod specification file $controllermanagerconf on the master node and set the --terminated-pod-gc-threshold to an appropriate threshold, for example: --terminated-pod-gc-threshold=10 scored: false - id: 1.3.2 text: "Ensure that the --profiling argument is set to false (Automated)" audit: "/bin/ps -ef | grep $controllermanagerbin | grep -v grep" tests: test_items: - flag: "--profiling" compare: op: eq value: false remediation: | Edit the Controller Manager pod specification file $controllermanagerconf on the master node and set the below parameter. --profiling=false scored: true - id: 1.3.3 text: "Ensure that the --use-service-account-credentials argument is set to true (Automated)" audit: "/bin/ps -ef | grep $controllermanagerbin | grep -v grep" tests: test_items: - flag: "--use-service-account-credentials" compare: op: noteq value: false remediation: | Edit the Controller Manager pod specification file $controllermanagerconf on the master node to set the below parameter. --use-service-account-credentials=true scored: true - id: 1.3.4 text: "Ensure that the --service-account-private-key-file argument is set as appropriate (Automated)" audit: "/bin/ps -ef | grep $controllermanagerbin | grep -v grep" tests: test_items: - flag: "--service-account-private-key-file" remediation: | Edit the Controller Manager pod specification file $controllermanagerconf on the master node and set the --service-account-private-key-file parameter to the private key file for service accounts. --service-account-private-key-file= scored: true - id: 1.3.5 text: "Ensure that the --root-ca-file argument is set as appropriate (Automated)" audit: "/bin/ps -ef | grep $controllermanagerbin | grep -v grep" tests: test_items: - flag: "--root-ca-file" remediation: | Edit the Controller Manager pod specification file $controllermanagerconf on the master node and set the --root-ca-file parameter to the certificate bundle file`. --root-ca-file= scored: true - id: 1.3.6 text: "Ensure that the RotateKubeletServerCertificate argument is set to true (Automated)" audit: "/bin/ps -ef | grep $controllermanagerbin | grep -v grep" tests: bin_op: or test_items: - flag: "--feature-gates" compare: op: nothave value: "RotateKubeletServerCertificate=false" set: true - flag: "--feature-gates" set: false remediation: | Edit the Controller Manager pod specification file $controllermanagerconf on the master node and set the --feature-gates parameter to include RotateKubeletServerCertificate=true. --feature-gates=RotateKubeletServerCertificate=true scored: true - id: 1.3.7 text: "Ensure that the --bind-address argument is set to 127.0.0.1 (Automated)" audit: "/bin/ps -ef | grep $controllermanagerbin | grep -v grep" tests: bin_op: or test_items: - flag: "--bind-address" compare: op: eq value: "127.0.0.1" - flag: "--bind-address" set: false remediation: | Edit the Controller Manager pod specification file $controllermanagerconf on the master node and ensure the correct value for the --bind-address parameter scored: true - id: 1.4 text: "Scheduler" checks: - id: 1.4.1 text: "Ensure that the --profiling argument is set to false (Automated)" audit: "/bin/ps -ef | grep $schedulerbin | grep -v grep" tests: test_items: - flag: "--profiling" compare: op: eq value: false remediation: | Edit the Scheduler pod specification file $schedulerconf file on the master node and set the below parameter. --profiling=false scored: true - id: 1.4.2 text: "Ensure that the --bind-address argument is set to 127.0.0.1 (Automated)" audit: "/bin/ps -ef | grep $schedulerbin | grep -v grep" tests: bin_op: or test_items: - flag: "--bind-address" compare: op: eq value: "127.0.0.1" - flag: "--bind-address" set: false remediation: | Edit the Scheduler pod specification file $schedulerconf on the master node and ensure the correct value for the --bind-address parameter scored: true ================================================ FILE: cfg/cis-1.20/node.yaml ================================================ --- controls: version: "cis-1.20" id: 4 text: "Worker Node Security Configuration" type: "node" groups: - id: 4.1 text: "Worker Node Configuration Files" checks: - id: 4.1.1 text: "Ensure that the kubelet service file permissions are set to 644 or more restrictive (Automated)" audit: '/bin/sh -c ''if test -e $kubeletsvc; then stat -c permissions=%a $kubeletsvc; fi'' ' tests: test_items: - flag: "permissions" compare: op: bitmask value: "644" remediation: | Run the below command (based on the file location on your system) on the each worker node. For example, chmod 644 $kubeletsvc scored: true - id: 4.1.2 text: "Ensure that the kubelet service file ownership is set to root:root (Automated)" audit: '/bin/sh -c "if test -e $kubeletsvc; then stat -c %U:%G $kubeletsvc; else echo \"File not found\"; fi"' tests: bin_op: or test_items: - flag: root:root - flag: "File not found" remediation: | Run the below command (based on the file location on your system) on the each worker node. For example, chown root:root $kubeletsvc scored: true - id: 4.1.3 text: "If proxy kubeconfig file exists ensure permissions are set to 644 or more restrictive (Manual)" audit: '/bin/sh -c ''if test -e $proxykubeconfig; then stat -c permissions=%a $proxykubeconfig; fi'' ' tests: bin_op: or test_items: - flag: "permissions" set: true compare: op: bitmask value: "644" remediation: | Run the below command (based on the file location on your system) on the each worker node. For example, chmod 644 $proxykubeconfig scored: false - id: 4.1.4 text: "If proxy kubeconfig file exists ensure ownership is set to root:root (Manual)" audit: '/bin/sh -c ''if test -e $proxykubeconfig; then stat -c %U:%G $proxykubeconfig; fi'' ' tests: bin_op: or test_items: - flag: root:root remediation: | Run the below command (based on the file location on your system) on the each worker node. For example, chown root:root $proxykubeconfig scored: false - id: 4.1.5 text: "Ensure that the --kubeconfig kubelet.conf file permissions are set to 644 or more restrictive (Automated)" audit: '/bin/sh -c ''if test -e $kubeletkubeconfig; then stat -c permissions=%a $kubeletkubeconfig; fi'' ' tests: test_items: - flag: "permissions" compare: op: bitmask value: "644" remediation: | Run the below command (based on the file location on your system) on the each worker node. For example, chmod 644 $kubeletkubeconfig scored: true - id: 4.1.6 text: "Ensure that the --kubeconfig kubelet.conf file ownership is set to root:root (Automated)" audit: '/bin/sh -c ''if test -e $kubeletkubeconfig; then stat -c %U:%G $kubeletkubeconfig; fi'' ' tests: test_items: - flag: root:root remediation: | Run the below command (based on the file location on your system) on the each worker node. For example, chown root:root $kubeletkubeconfig scored: true - id: 4.1.7 text: "Ensure that the certificate authorities file permissions are set to 644 or more restrictive (Manual)" audit: | CAFILE=$(ps -ef | grep kubelet | grep -v apiserver | grep -- --client-ca-file= | awk -F '--client-ca-file=' '{print $2}' | awk '{print $1}' | uniq) if test -z $CAFILE; then CAFILE=$kubeletcafile; fi if test -e $CAFILE; then stat -c permissions=%a $CAFILE; fi tests: test_items: - flag: "permissions" compare: op: bitmask value: "644" remediation: | Run the following command to modify the file permissions of the --client-ca-file chmod 644 scored: false - id: 4.1.8 text: "Ensure that the client certificate authorities file ownership is set to root:root (Manual)" audit: | CAFILE=$(ps -ef | grep kubelet | grep -v apiserver | grep -- --client-ca-file= | awk -F '--client-ca-file=' '{print $2}' | awk '{print $1}' | uniq) if test -z $CAFILE; then CAFILE=$kubeletcafile; fi if test -e $CAFILE; then stat -c %U:%G $CAFILE; fi tests: test_items: - flag: root:root compare: op: eq value: root:root remediation: | Run the following command to modify the ownership of the --client-ca-file. chown root:root scored: false - id: 4.1.9 text: "Ensure that the kubelet --config configuration file has permissions set to 644 or more restrictive (Automated)" audit: '/bin/sh -c ''if test -e $kubeletconf; then stat -c permissions=%a $kubeletconf; fi'' ' tests: test_items: - flag: "permissions" compare: op: bitmask value: "644" remediation: | Run the following command (using the config file location identified in the Audit step) chmod 644 $kubeletconf scored: true - id: 4.1.10 text: "Ensure that the kubelet --config configuration file ownership is set to root:root (Automated)" audit: '/bin/sh -c ''if test -e $kubeletconf; then stat -c %U:%G $kubeletconf; fi'' ' tests: test_items: - flag: root:root remediation: | Run the following command (using the config file location identified in the Audit step) chown root:root $kubeletconf scored: true - id: 4.2 text: "Kubelet" checks: - id: 4.2.1 text: "Ensure that the anonymous-auth argument is set to false (Automated)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: "--anonymous-auth" path: '{.authentication.anonymous.enabled}' compare: op: eq value: false remediation: | If using a Kubelet config file, edit the file to set authentication: anonymous: enabled to false. If using executable arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_SYSTEM_PODS_ARGS variable. --anonymous-auth=false Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: true - id: 4.2.2 text: "Ensure that the --authorization-mode argument is not set to AlwaysAllow (Automated)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --authorization-mode path: '{.authorization.mode}' compare: op: nothave value: AlwaysAllow remediation: | If using a Kubelet config file, edit the file to set authorization: mode to Webhook. If using executable arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_AUTHZ_ARGS variable. --authorization-mode=Webhook Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: true - id: 4.2.3 text: "Ensure that the --client-ca-file argument is set as appropriate (Automated)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --client-ca-file path: '{.authentication.x509.clientCAFile}' remediation: | If using a Kubelet config file, edit the file to set authentication: x509: clientCAFile to the location of the client CA file. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_AUTHZ_ARGS variable. --client-ca-file= Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: true - id: 4.2.4 text: "Ensure that the --read-only-port argument is set to 0 (Manual)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: bin_op: or test_items: - flag: "--read-only-port" path: '{.readOnlyPort}' compare: op: eq value: 0 - flag: "--read-only-port" path: '{.readOnlyPort}' set: false remediation: | If using a Kubelet config file, edit the file to set readOnlyPort to 0. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_SYSTEM_PODS_ARGS variable. --read-only-port=0 Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: false - id: 4.2.5 text: "Ensure that the --streaming-connection-idle-timeout argument is not set to 0 (Manual)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --streaming-connection-idle-timeout path: '{.streamingConnectionIdleTimeout}' compare: op: noteq value: 0 - flag: --streaming-connection-idle-timeout path: '{.streamingConnectionIdleTimeout}' set: false bin_op: or remediation: | If using a Kubelet config file, edit the file to set streamingConnectionIdleTimeout to a value other than 0. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_SYSTEM_PODS_ARGS variable. --streaming-connection-idle-timeout=5m Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: false - id: 4.2.6 text: "Ensure that the --protect-kernel-defaults argument is set to true (Automated)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --protect-kernel-defaults path: '{.protectKernelDefaults}' compare: op: eq value: true remediation: | If using a Kubelet config file, edit the file to set protectKernelDefaults: true. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_SYSTEM_PODS_ARGS variable. --protect-kernel-defaults=true Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: true - id: 4.2.7 text: "Ensure that the --make-iptables-util-chains argument is set to true (Automated)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --make-iptables-util-chains path: '{.makeIPTablesUtilChains}' compare: op: eq value: true - flag: --make-iptables-util-chains path: '{.makeIPTablesUtilChains}' set: false bin_op: or remediation: | If using a Kubelet config file, edit the file to set makeIPTablesUtilChains: true. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and remove the --make-iptables-util-chains argument from the KUBELET_SYSTEM_PODS_ARGS variable. Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: true - id: 4.2.8 text: "Ensure that the --hostname-override argument is not set (Manual)" # This is one of those properties that can only be set as a command line argument. # To check if the property is set as expected, we need to parse the kubelet command # instead reading the Kubelet Configuration file. audit: "/bin/ps -fC $kubeletbin " tests: test_items: - flag: --hostname-override set: false remediation: | Edit the kubelet service file $kubeletsvc on each worker node and remove the --hostname-override argument from the KUBELET_SYSTEM_PODS_ARGS variable. Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: false - id: 4.2.9 text: "Ensure that the --event-qps argument is set to 0 or a level which ensures appropriate event capture (Manual)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --event-qps path: '{.eventRecordQPS}' compare: op: eq value: 0 remediation: | If using a Kubelet config file, edit the file to set eventRecordQPS: to an appropriate level. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_SYSTEM_PODS_ARGS variable. Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: false - id: 4.2.10 text: "Ensure that the --tls-cert-file and --tls-private-key-file arguments are set as appropriate (Manual)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --tls-cert-file path: '{.tlsCertFile}' - flag: --tls-private-key-file path: '{.tlsPrivateKeyFile}' remediation: | If using a Kubelet config file, edit the file to set tlsCertFile to the location of the certificate file to use to identify this Kubelet, and tlsPrivateKeyFile to the location of the corresponding private key file. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameters in KUBELET_CERTIFICATE_ARGS variable. --tls-cert-file= --tls-private-key-file= Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: false - id: 4.2.11 text: "Ensure that the --rotate-certificates argument is not set to false (Automated)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --rotate-certificates path: '{.rotateCertificates}' compare: op: eq value: true - flag: --rotate-certificates path: '{.rotateCertificates}' set: false bin_op: or remediation: | If using a Kubelet config file, edit the file to add the line rotateCertificates: true or remove it altogether to use the default value. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and remove --rotate-certificates=false argument from the KUBELET_CERTIFICATE_ARGS variable. Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: true - id: 4.2.12 text: "Verify that the RotateKubeletServerCertificate argument is set to true (Manual)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: bin_op: or test_items: - flag: RotateKubeletServerCertificate path: '{.featureGates.RotateKubeletServerCertificate}' compare: op: nothave value: false - flag: RotateKubeletServerCertificate path: '{.featureGates.RotateKubeletServerCertificate}' set: false remediation: | Edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_CERTIFICATE_ARGS variable. --feature-gates=RotateKubeletServerCertificate=true Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: false - id: 4.2.13 text: "Ensure that the Kubelet only makes use of Strong Cryptographic Ciphers (Manual)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --tls-cipher-suites path: '{range .tlsCipherSuites[:]}{}{'',''}{end}' compare: op: valid_elements value: 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 remediation: | If using a Kubelet config file, edit the file to set tlsCipherSuites: to 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 or to a subset of these values. If using executable arguments, edit the kubelet service file $kubeletsvc on each worker node and set the --tls-cipher-suites parameter as follows, or to a subset of these values. --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 Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: false ================================================ FILE: cfg/cis-1.20/policies.yaml ================================================ --- controls: version: "cis-1.20" id: 5 text: "Kubernetes Policies" type: "policies" groups: - id: 5.1 text: "RBAC and Service Accounts" checks: - id: 5.1.1 text: "Ensure that the cluster-admin role is only used where required (Manual)" type: "manual" remediation: | Identify all clusterrolebindings to the cluster-admin role. Check if they are used and if they need this role or if they could use a role with fewer privileges. Where possible, first bind users to a lower privileged role and then remove the clusterrolebinding to the cluster-admin role : kubectl delete clusterrolebinding [name] scored: false - id: 5.1.2 text: "Minimize access to secrets (Manual)" type: "manual" remediation: | Where possible, remove get, list and watch access to secret objects in the cluster. scored: false - id: 5.1.3 text: "Minimize wildcard use in Roles and ClusterRoles (Manual)" type: "manual" remediation: | Where possible replace any use of wildcards in clusterroles and roles with specific objects or actions. scored: false - id: 5.1.4 text: "Minimize access to create pods (Manual)" type: "manual" remediation: | Where possible, remove create access to pod objects in the cluster. scored: false - id: 5.1.5 text: "Ensure that default service accounts are not actively used. (Manual)" type: "manual" remediation: | Create explicit service accounts wherever a Kubernetes workload requires specific access to the Kubernetes API server. Modify the configuration of each default service account to include this value automountServiceAccountToken: false scored: false - id: 5.1.6 text: "Ensure that Service Account Tokens are only mounted where necessary (Manual)" type: "manual" remediation: | Modify the definition of pods and service accounts which do not need to mount service account tokens to disable it. scored: false - id: 5.1.7 text: "Avoid use of system:masters group (Manual)" type: "manual" remediation: | Remove the system:masters group from all users in the cluster. scored: false - id: 5.1.8 text: "Limit use of the Bind, Impersonate and Escalate permissions in the Kubernetes cluster (Manual)" type: "manual" remediation: | Where possible, remove the impersonate, bind and escalate rights from subjects. scored: false - id: 5.2 text: "Pod Security Policies" checks: - id: 5.2.1 text: "Minimize the admission of privileged containers (Automated)" type: "manual" remediation: | Create a PSP as described in the Kubernetes documentation, ensuring that the .spec.privileged field is omitted or set to false. scored: false - id: 5.2.2 text: "Minimize the admission of containers wishing to share the host process ID namespace (Automated)" type: "manual" remediation: | Create a PSP as described in the Kubernetes documentation, ensuring that the .spec.hostPID field is omitted or set to false. scored: false - id: 5.2.3 text: "Minimize the admission of containers wishing to share the host IPC namespace (Automated)" type: "manual" remediation: | Create a PSP as described in the Kubernetes documentation, ensuring that the .spec.hostIPC field is omitted or set to false. scored: false - id: 5.2.4 text: "Minimize the admission of containers wishing to share the host network namespace (Automated)" type: "manual" remediation: | Create a PSP as described in the Kubernetes documentation, ensuring that the .spec.hostNetwork field is omitted or set to false. scored: false - id: 5.2.5 text: "Minimize the admission of containers with allowPrivilegeEscalation (Automated)" type: "manual" remediation: | Create a PSP as described in the Kubernetes documentation, ensuring that the .spec.allowPrivilegeEscalation field is omitted or set to false. scored: false - id: 5.2.6 text: "Minimize the admission of root containers (Automated)" type: "manual" remediation: | Create a PSP as described in the Kubernetes documentation, ensuring that the .spec.runAsUser.rule is set to either MustRunAsNonRoot or MustRunAs with the range of UIDs not including 0. scored: false - id: 5.2.7 text: "Minimize the admission of containers with the NET_RAW capability (Automated)" type: "manual" remediation: | Create a PSP as described in the Kubernetes documentation, ensuring that the .spec.requiredDropCapabilities is set to include either NET_RAW or ALL. scored: false - id: 5.2.8 text: "Minimize the admission of containers with added capabilities (Automated)" type: "manual" remediation: | Ensure that allowedCapabilities is not present in PSPs for the cluster unless it is set to an empty array. scored: false - id: 5.2.9 text: "Minimize the admission of containers with capabilities assigned (Manual)" type: "manual" remediation: | Review the use of capabilites in applications running on your cluster. Where a namespace contains applications which do not require any Linux capabities to operate consider adding a PSP which forbids the admission of containers which do not drop all capabilities. scored: false - id: 5.3 text: "Network Policies and CNI" checks: - id: 5.3.1 text: "Ensure that the CNI in use supports Network Policies (Manual)" type: "manual" remediation: | If the CNI plugin in use does not support network policies, consideration should be given to making use of a different plugin, or finding an alternate mechanism for restricting traffic in the Kubernetes cluster. scored: false - id: 5.3.2 text: "Ensure that all Namespaces have Network Policies defined (Manual)" type: "manual" remediation: | Follow the documentation and create NetworkPolicy objects as you need them. scored: false - id: 5.4 text: "Secrets Management" checks: - id: 5.4.1 text: "Prefer using secrets as files over secrets as environment variables (Manual)" type: "manual" remediation: | if possible, rewrite application code to read secrets from mounted secret files, rather than from environment variables. scored: false - id: 5.4.2 text: "Consider external secret storage (Manual)" type: "manual" remediation: | Refer to the secrets management options offered by your cloud provider or a third-party secrets management solution. scored: false - id: 5.5 text: "Extensible Admission Control" checks: - id: 5.5.1 text: "Configure Image Provenance using ImagePolicyWebhook admission controller (Manual)" type: "manual" remediation: | Follow the Kubernetes documentation and setup image provenance. scored: false - id: 5.7 text: "General Policies" checks: - id: 5.7.1 text: "Create administrative boundaries between resources using namespaces (Manual)" type: "manual" remediation: | Follow the documentation and create namespaces for objects in your deployment as you need them. scored: false - id: 5.7.2 text: "Ensure that the seccomp profile is set to docker/default in your pod definitions (Manual)" type: "manual" remediation: | Use security context to enable the docker/default seccomp profile in your pod definitions. An example is as below: securityContext: seccompProfile: type: RuntimeDefault scored: false - id: 5.7.3 text: "Apply Security Context to Your Pods and Containers (Manual)" type: "manual" remediation: | Follow the Kubernetes documentation and apply security contexts to your pods. For a suggested list of security contexts, you may refer to the CIS Security Benchmark for Docker Containers. scored: false - id: 5.7.4 text: "The default namespace should not be used (Manual)" type: "manual" remediation: | Ensure that namespaces are created to allow for appropriate segregation of Kubernetes resources and that all new resources are created in a specific namespace. scored: false ================================================ FILE: cfg/cis-1.23/config.yaml ================================================ --- ## Version-specific settings that override the values in cfg/config.yaml ================================================ FILE: cfg/cis-1.23/controlplane.yaml ================================================ --- controls: version: "cis-1.23" id: 3 text: "Control Plane Configuration" type: "controlplane" groups: - id: 3.1 text: "Authentication and Authorization" checks: - id: 3.1.1 text: "Client certificate authentication should not be used for users (Manual)" type: "manual" remediation: | Alternative mechanisms provided by Kubernetes such as the use of OIDC should be implemented in place of client certificates. scored: false - id: 3.2 text: "Logging" checks: - id: 3.2.1 text: "Ensure that a minimal audit policy is created (Manual)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--audit-policy-file" set: true remediation: | Create an audit policy file for your cluster. scored: false - id: 3.2.2 text: "Ensure that the audit policy covers key security concerns (Manual)" type: "manual" remediation: | Review the audit policy provided for the cluster and ensure that it covers at least the following areas, - Access to Secrets managed by the cluster. Care should be taken to only log Metadata for requests to Secrets, ConfigMaps, and TokenReviews, in order to avoid risk of logging sensitive data. - Modification of Pod and Deployment objects. - Use of `pods/exec`, `pods/portforward`, `pods/proxy` and `services/proxy`. For most requests, minimally logging at the Metadata level is recommended (the most basic level of logging). scored: false ================================================ FILE: cfg/cis-1.23/etcd.yaml ================================================ --- controls: version: "cis-1.23" id: 2 text: "Etcd Node Configuration" type: "etcd" groups: - id: 2 text: "Etcd Node Configuration" checks: - id: 2.1 text: "Ensure that the --cert-file and --key-file arguments are set as appropriate (Automated)" audit: "/bin/ps -ef | /bin/grep $etcdbin | /bin/grep -v grep" tests: bin_op: and test_items: - flag: "--cert-file" env: "ETCD_CERT_FILE" - flag: "--key-file" env: "ETCD_KEY_FILE" remediation: | Follow the etcd service documentation and configure TLS encryption. Then, edit the etcd pod specification file /etc/kubernetes/manifests/etcd.yaml on the master node and set the below parameters. --cert-file= --key-file= scored: true - id: 2.2 text: "Ensure that the --client-cert-auth argument is set to true (Automated)" audit: "/bin/ps -ef | /bin/grep $etcdbin | /bin/grep -v grep" tests: test_items: - flag: "--client-cert-auth" env: "ETCD_CLIENT_CERT_AUTH" compare: op: eq value: true remediation: | Edit the etcd pod specification file $etcdconf on the master node and set the below parameter. --client-cert-auth="true" scored: true - id: 2.3 text: "Ensure that the --auto-tls argument is not set to true (Automated)" audit: "/bin/ps -ef | /bin/grep $etcdbin | /bin/grep -v grep" tests: bin_op: or test_items: - flag: "--auto-tls" env: "ETCD_AUTO_TLS" set: false - flag: "--auto-tls" env: "ETCD_AUTO_TLS" compare: op: eq value: false remediation: | Edit the etcd pod specification file $etcdconf on the master node and either remove the --auto-tls parameter or set it to false. --auto-tls=false scored: true - id: 2.4 text: "Ensure that the --peer-cert-file and --peer-key-file arguments are set as appropriate (Automated)" audit: "/bin/ps -ef | /bin/grep $etcdbin | /bin/grep -v grep" tests: bin_op: and test_items: - flag: "--peer-cert-file" env: "ETCD_PEER_CERT_FILE" - flag: "--peer-key-file" env: "ETCD_PEER_KEY_FILE" remediation: | Follow the etcd service documentation and configure peer TLS encryption as appropriate for your etcd cluster. Then, edit the etcd pod specification file $etcdconf on the master node and set the below parameters. --peer-client-file= --peer-key-file= scored: true - id: 2.5 text: "Ensure that the --peer-client-cert-auth argument is set to true (Automated)" audit: "/bin/ps -ef | /bin/grep $etcdbin | /bin/grep -v grep" tests: test_items: - flag: "--peer-client-cert-auth" env: "ETCD_PEER_CLIENT_CERT_AUTH" compare: op: eq value: true remediation: | Edit the etcd pod specification file $etcdconf on the master node and set the below parameter. --peer-client-cert-auth=true scored: true - id: 2.6 text: "Ensure that the --peer-auto-tls argument is not set to true (Automated)" audit: "/bin/ps -ef | /bin/grep $etcdbin | /bin/grep -v grep" tests: bin_op: or test_items: - flag: "--peer-auto-tls" env: "ETCD_PEER_AUTO_TLS" set: false - flag: "--peer-auto-tls" env: "ETCD_PEER_AUTO_TLS" compare: op: eq value: false remediation: | Edit the etcd pod specification file $etcdconf on the master node and either remove the --peer-auto-tls parameter or set it to false. --peer-auto-tls=false scored: true - id: 2.7 text: "Ensure that a unique Certificate Authority is used for etcd (Manual)" audit: "/bin/ps -ef | /bin/grep $etcdbin | /bin/grep -v grep" tests: test_items: - flag: "--trusted-ca-file" env: "ETCD_TRUSTED_CA_FILE" remediation: | [Manual test] Follow the etcd documentation and create a dedicated certificate authority setup for the etcd service. Then, edit the etcd pod specification file $etcdconf on the master node and set the below parameter. --trusted-ca-file= scored: false ================================================ FILE: cfg/cis-1.23/master.yaml ================================================ --- controls: version: "cis-1.23" id: 1 text: "Control Plane Security Configuration" type: "master" groups: - id: 1.1 text: "Control Plane Node Configuration Files" checks: - id: 1.1.1 text: "Ensure that the API server pod specification file permissions are set to 644 or more restrictive (Automated)" audit: "/bin/sh -c 'if test -e $apiserverconf; then stat -c permissions=%a $apiserverconf; fi'" tests: test_items: - flag: "permissions" compare: op: bitmask value: "644" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chmod 644 $apiserverconf scored: true - id: 1.1.2 text: "Ensure that the API server pod specification file ownership is set to root:root (Automated)" audit: "/bin/sh -c 'if test -e $apiserverconf; then stat -c %U:%G $apiserverconf; fi'" tests: test_items: - flag: "root:root" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chown root:root $apiserverconf scored: true - id: 1.1.3 text: "Ensure that the controller manager pod specification file permissions are set to 644 or more restrictive (Automated)" audit: "/bin/sh -c 'if test -e $controllermanagerconf; then stat -c permissions=%a $controllermanagerconf; fi'" tests: test_items: - flag: "permissions" compare: op: bitmask value: "644" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chmod 644 $controllermanagerconf scored: true - id: 1.1.4 text: "Ensure that the controller manager pod specification file ownership is set to root:root (Automated)" audit: "/bin/sh -c 'if test -e $controllermanagerconf; then stat -c %U:%G $controllermanagerconf; fi'" tests: test_items: - flag: "root:root" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chown root:root $controllermanagerconf scored: true - id: 1.1.5 text: "Ensure that the scheduler pod specification file permissions are set to 644 or more restrictive (Automated)" audit: "/bin/sh -c 'if test -e $schedulerconf; then stat -c permissions=%a $schedulerconf; fi'" tests: test_items: - flag: "permissions" compare: op: bitmask value: "644" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chmod 644 $schedulerconf scored: true - id: 1.1.6 text: "Ensure that the scheduler pod specification file ownership is set to root:root (Automated)" audit: "/bin/sh -c 'if test -e $schedulerconf; then stat -c %U:%G $schedulerconf; fi'" tests: test_items: - flag: "root:root" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chown root:root $schedulerconf scored: true - id: 1.1.7 text: "Ensure that the etcd pod specification file permissions are set to 644 or more restrictive (Automated)" audit: "/bin/sh -c 'if test -e $etcdconf; then find $etcdconf -name '*etcd*' | xargs stat -c permissions=%a; fi'" use_multiple_values: true tests: test_items: - flag: "permissions" compare: op: bitmask value: "644" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chmod 644 $etcdconf scored: true - id: 1.1.8 text: "Ensure that the etcd pod specification file ownership is set to root:root (Automated)" audit: "/bin/sh -c 'if test -e $etcdconf; then find $etcdconf -name '*etcd*' | xargs stat -c %U:%G; fi'" use_multiple_values: true tests: test_items: - flag: "root:root" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chown root:root $etcdconf scored: true - id: 1.1.9 text: "Ensure that the Container Network Interface file permissions are set to 644 or more restrictive (Manual)" audit: | ps -ef | grep $kubeletbin | grep -- --cni-conf-dir | sed 's%.*cni-conf-dir[= ]\([^ ]*\).*%\1%' | xargs -I{} find {} -mindepth 1 | xargs --no-run-if-empty stat -c permissions=%a find /var/lib/cni/networks -type f 2> /dev/null | xargs --no-run-if-empty stat -c permissions=%a use_multiple_values: true tests: test_items: - flag: "permissions" compare: op: bitmask value: "644" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chmod 644 scored: false - id: 1.1.10 text: "Ensure that the Container Network Interface file ownership is set to root:root (Manual)" audit: | ps -ef | grep $kubeletbin | grep -- --cni-conf-dir | sed 's%.*cni-conf-dir[= ]\([^ ]*\).*%\1%' | xargs -I{} find {} -mindepth 1 | xargs --no-run-if-empty stat -c %U:%G find /var/lib/cni/networks -type f 2> /dev/null | xargs --no-run-if-empty stat -c %U:%G use_multiple_values: true tests: test_items: - flag: "root:root" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chown root:root scored: false - id: 1.1.11 text: "Ensure that the etcd data directory permissions are set to 700 or more restrictive (Automated)" audit: | DATA_DIR='' for d in $(ps -ef | grep $etcdbin | grep -- --data-dir | sed 's%.*data-dir[= ]\([^ ]*\).*%\1%'); do if test -d "$d"; then DATA_DIR="$d"; fi done if ! test -d "$DATA_DIR"; then DATA_DIR=$etcddatadir; fi stat -c permissions=%a "$DATA_DIR" tests: test_items: - flag: "permissions" compare: op: bitmask value: "700" remediation: | On the etcd server node, get the etcd data directory, passed as an argument --data-dir, from the command 'ps -ef | grep etcd'. Run the below command (based on the etcd data directory found above). For example, chmod 700 /var/lib/etcd scored: true - id: 1.1.12 text: "Ensure that the etcd data directory ownership is set to etcd:etcd (Automated)" audit: | DATA_DIR='' for d in $(ps -ef | grep $etcdbin | grep -- --data-dir | sed 's%.*data-dir[= ]\([^ ]*\).*%\1%'); do if test -d "$d"; then DATA_DIR="$d"; fi done if ! test -d "$DATA_DIR"; then DATA_DIR=$etcddatadir; fi stat -c %U:%G "$DATA_DIR" tests: test_items: - flag: "etcd:etcd" remediation: | On the etcd server node, get the etcd data directory, passed as an argument --data-dir, from the command 'ps -ef | grep etcd'. Run the below command (based on the etcd data directory found above). For example, chown etcd:etcd /var/lib/etcd scored: true - id: 1.1.13 text: "Ensure that the admin.conf file permissions are set to 600 or more restrictive (Automated)" audit: "/bin/sh -c 'if test -e /etc/kubernetes/admin.conf; then stat -c permissions=%a /etc/kubernetes/admin.conf; fi'" tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chmod 600 /etc/kubernetes/admin.conf scored: true - id: 1.1.14 text: "Ensure that the admin.conf file ownership is set to root:root (Automated)" audit: "/bin/sh -c 'if test -e /etc/kubernetes/admin.conf; then stat -c %U:%G /etc/kubernetes/admin.conf; fi'" tests: test_items: - flag: "root:root" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chown root:root /etc/kubernetes/admin.conf scored: true - id: 1.1.15 text: "Ensure that the scheduler.conf file permissions are set to 644 or more restrictive (Automated)" audit: "/bin/sh -c 'if test -e $schedulerkubeconfig; then stat -c permissions=%a $schedulerkubeconfig; fi'" tests: test_items: - flag: "permissions" compare: op: bitmask value: "644" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chmod 644 $schedulerkubeconfig scored: true - id: 1.1.16 text: "Ensure that the scheduler.conf file ownership is set to root:root (Automated)" audit: "/bin/sh -c 'if test -e $schedulerkubeconfig; then stat -c %U:%G $schedulerkubeconfig; fi'" tests: test_items: - flag: "root:root" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chown root:root $schedulerkubeconfig scored: true - id: 1.1.17 text: "Ensure that the controller-manager.conf file permissions are set to 644 or more restrictive (Automated)" audit: "/bin/sh -c 'if test -e $controllermanagerkubeconfig; then stat -c permissions=%a $controllermanagerkubeconfig; fi'" tests: test_items: - flag: "permissions" compare: op: bitmask value: "644" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chmod 644 $controllermanagerkubeconfig scored: true - id: 1.1.18 text: "Ensure that the controller-manager.conf file ownership is set to root:root (Automated)" audit: "/bin/sh -c 'if test -e $controllermanagerkubeconfig; then stat -c %U:%G $controllermanagerkubeconfig; fi'" tests: test_items: - flag: "root:root" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chown root:root $controllermanagerkubeconfig scored: true - id: 1.1.19 text: "Ensure that the Kubernetes PKI directory and file ownership is set to root:root (Automated)" audit: "find /etc/kubernetes/pki/ | xargs stat -c %U:%G" use_multiple_values: true tests: test_items: - flag: "root:root" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chown -R root:root /etc/kubernetes/pki/ scored: true - id: 1.1.20 text: "Ensure that the Kubernetes PKI certificate file permissions are set to 644 or more restrictive (Manual)" audit: "find /etc/kubernetes/pki/ -name '*.crt' | xargs stat -c permissions=%a" use_multiple_values: true tests: test_items: - flag: "permissions" compare: op: bitmask value: "644" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chmod -R 644 /etc/kubernetes/pki/*.crt scored: false - id: 1.1.21 text: "Ensure that the Kubernetes PKI key file permissions are set to 600 (Manual)" audit: "find /etc/kubernetes/pki/ -name '*.key' | xargs stat -c permissions=%a" use_multiple_values: true tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chmod -R 600 /etc/kubernetes/pki/*.key scored: false - id: 1.2 text: "API Server" checks: - id: 1.2.1 text: "Ensure that the --anonymous-auth argument is set to false (Manual)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--anonymous-auth" compare: op: eq value: false remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the below parameter. --anonymous-auth=false scored: false - id: 1.2.2 text: "Ensure that the --token-auth-file parameter is not set (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--token-auth-file" set: false remediation: | Follow the documentation and configure alternate mechanisms for authentication. Then, edit the API server pod specification file $apiserverconf on the control plane node and remove the --token-auth-file= parameter. scored: true - id: 1.2.3 text: "Ensure that the --DenyServiceExternalIPs is not set (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: bin_op: or test_items: - flag: "--enable-admission-plugins" compare: op: nothave value: "DenyServiceExternalIPs" - flag: "--enable-admission-plugins" set: false remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and remove the `DenyServiceExternalIPs` from enabled admission plugins. scored: true - id: 1.2.4 text: "Ensure that the --kubelet-https argument is set to true (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: bin_op: or test_items: - flag: "--kubelet-https" compare: op: eq value: true - flag: "--kubelet-https" set: false remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and remove the --kubelet-https parameter. scored: true - id: 1.2.5 text: "Ensure that the --kubelet-client-certificate and --kubelet-client-key arguments are set as appropriate (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: bin_op: and test_items: - flag: "--kubelet-client-certificate" - flag: "--kubelet-client-key" remediation: | Follow the Kubernetes documentation and set up the TLS connection between the apiserver and kubelets. Then, edit API server pod specification file $apiserverconf on the control plane node and set the kubelet client certificate and key parameters as below. --kubelet-client-certificate= --kubelet-client-key= scored: true - id: 1.2.6 text: "Ensure that the --kubelet-certificate-authority argument is set as appropriate (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--kubelet-certificate-authority" remediation: | Follow the Kubernetes documentation and setup the TLS connection between the apiserver and kubelets. Then, edit the API server pod specification file $apiserverconf on the control plane node and set the --kubelet-certificate-authority parameter to the path to the cert file for the certificate authority. --kubelet-certificate-authority= scored: true - id: 1.2.7 text: "Ensure that the --authorization-mode argument is not set to AlwaysAllow (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--authorization-mode" compare: op: nothave value: "AlwaysAllow" remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the --authorization-mode parameter to values other than AlwaysAllow. One such example could be as below. --authorization-mode=RBAC scored: true - id: 1.2.8 text: "Ensure that the --authorization-mode argument includes Node (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--authorization-mode" compare: op: has value: "Node" remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the --authorization-mode parameter to a value that includes Node. --authorization-mode=Node,RBAC scored: true - id: 1.2.9 text: "Ensure that the --authorization-mode argument includes RBAC (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--authorization-mode" compare: op: has value: "RBAC" remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the --authorization-mode parameter to a value that includes RBAC, for example `--authorization-mode=Node,RBAC`. scored: true - id: 1.2.10 text: "Ensure that the admission control plugin EventRateLimit is set (Manual)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--enable-admission-plugins" compare: op: has value: "EventRateLimit" remediation: | Follow the Kubernetes documentation and set the desired limits in a configuration file. Then, edit the API server pod specification file $apiserverconf and set the below parameters. --enable-admission-plugins=...,EventRateLimit,... --admission-control-config-file= scored: false - id: 1.2.11 text: "Ensure that the admission control plugin AlwaysAdmit is not set (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: bin_op: or test_items: - flag: "--enable-admission-plugins" compare: op: nothave value: AlwaysAdmit - flag: "--enable-admission-plugins" set: false remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and either remove the --enable-admission-plugins parameter, or set it to a value that does not include AlwaysAdmit. scored: true - id: 1.2.12 text: "Ensure that the admission control plugin AlwaysPullImages is set (Manual)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--enable-admission-plugins" compare: op: has value: "AlwaysPullImages" remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the --enable-admission-plugins parameter to include AlwaysPullImages. --enable-admission-plugins=...,AlwaysPullImages,... scored: false - id: 1.2.13 text: "Ensure that the admission control plugin SecurityContextDeny is set if PodSecurityPolicy is not used (Manual)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: bin_op: or test_items: - flag: "--enable-admission-plugins" compare: op: has value: "SecurityContextDeny" - flag: "--enable-admission-plugins" compare: op: has value: "PodSecurityPolicy" remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the --enable-admission-plugins parameter to include SecurityContextDeny, unless PodSecurityPolicy is already in place. --enable-admission-plugins=...,SecurityContextDeny,... scored: false - id: 1.2.14 text: "Ensure that the admission control plugin ServiceAccount is set (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: bin_op: or test_items: - flag: "--disable-admission-plugins" compare: op: nothave value: "ServiceAccount" - flag: "--disable-admission-plugins" set: false remediation: | Follow the documentation and create ServiceAccount objects as per your environment. Then, edit the API server pod specification file $apiserverconf on the control plane node and ensure that the --disable-admission-plugins parameter is set to a value that does not include ServiceAccount. scored: true - id: 1.2.15 text: "Ensure that the admission control plugin NamespaceLifecycle is set (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: bin_op: or test_items: - flag: "--disable-admission-plugins" compare: op: nothave value: "NamespaceLifecycle" - flag: "--disable-admission-plugins" set: false remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the --disable-admission-plugins parameter to ensure it does not include NamespaceLifecycle. scored: true - id: 1.2.16 text: "Ensure that the admission control plugin NodeRestriction is set (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--enable-admission-plugins" compare: op: has value: "NodeRestriction" remediation: | Follow the Kubernetes documentation and configure NodeRestriction plug-in on kubelets. Then, edit the API server pod specification file $apiserverconf on the control plane node and set the --enable-admission-plugins parameter to a value that includes NodeRestriction. --enable-admission-plugins=...,NodeRestriction,... scored: true - id: 1.2.17 text: "Ensure that the --secure-port argument is not set to 0 (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: bin_op: or test_items: - flag: "--secure-port" compare: op: gt value: 0 - flag: "--secure-port" set: false remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and either remove the --secure-port parameter or set it to a different (non-zero) desired port. scored: true - id: 1.2.18 text: "Ensure that the --profiling argument is set to false (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--profiling" compare: op: eq value: false remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the below parameter. --profiling=false scored: true - id: 1.2.19 text: "Ensure that the --audit-log-path argument is set (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--audit-log-path" remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the --audit-log-path parameter to a suitable path and file where you would like audit logs to be written, for example, --audit-log-path=/var/log/apiserver/audit.log scored: true - id: 1.2.20 text: "Ensure that the --audit-log-maxage argument is set to 30 or as appropriate (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--audit-log-maxage" compare: op: gte value: 30 remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the --audit-log-maxage parameter to 30 or as an appropriate number of days, for example, --audit-log-maxage=30 scored: true - id: 1.2.21 text: "Ensure that the --audit-log-maxbackup argument is set to 10 or as appropriate (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--audit-log-maxbackup" compare: op: gte value: 10 remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the --audit-log-maxbackup parameter to 10 or to an appropriate value. For example, --audit-log-maxbackup=10 scored: true - id: 1.2.22 text: "Ensure that the --audit-log-maxsize argument is set to 100 or as appropriate (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--audit-log-maxsize" compare: op: gte value: 100 remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the --audit-log-maxsize parameter to an appropriate size in MB. For example, to set it as 100 MB, --audit-log-maxsize=100 scored: true - id: 1.2.23 text: "Ensure that the --request-timeout argument is set as appropriate (Manual)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" type: manual remediation: | Edit the API server pod specification file $apiserverconf and set the below parameter as appropriate and if needed. For example, --request-timeout=300s scored: false - id: 1.2.24 text: "Ensure that the --service-account-lookup argument is set to true (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: bin_op: or test_items: - flag: "--service-account-lookup" set: false - flag: "--service-account-lookup" compare: op: eq value: true remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the below parameter. --service-account-lookup=true Alternatively, you can delete the --service-account-lookup parameter from this file so that the default takes effect. scored: true - id: 1.2.25 text: "Ensure that the --service-account-key-file argument is set as appropriate (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--service-account-key-file" remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the --service-account-key-file parameter to the public key file for service accounts. For example, --service-account-key-file= scored: true - id: 1.2.26 text: "Ensure that the --etcd-certfile and --etcd-keyfile arguments are set as appropriate (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: bin_op: and test_items: - flag: "--etcd-certfile" - flag: "--etcd-keyfile" remediation: | Follow the Kubernetes documentation and set up the TLS connection between the apiserver and etcd. Then, edit the API server pod specification file $apiserverconf on the control plane node and set the etcd certificate and key file parameters. --etcd-certfile= --etcd-keyfile= scored: true - id: 1.2.27 text: "Ensure that the --tls-cert-file and --tls-private-key-file arguments are set as appropriate (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: bin_op: and test_items: - flag: "--tls-cert-file" - flag: "--tls-private-key-file" remediation: | Follow the Kubernetes documentation and set up the TLS connection on the apiserver. Then, edit the API server pod specification file $apiserverconf on the control plane node and set the TLS certificate and private key file parameters. --tls-cert-file= --tls-private-key-file= scored: true - id: 1.2.28 text: "Ensure that the --client-ca-file argument is set as appropriate (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--client-ca-file" remediation: | Follow the Kubernetes documentation and set up the TLS connection on the apiserver. Then, edit the API server pod specification file $apiserverconf on the control plane node and set the client certificate authority file. --client-ca-file= scored: true - id: 1.2.29 text: "Ensure that the --etcd-cafile argument is set as appropriate (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--etcd-cafile" remediation: | Follow the Kubernetes documentation and set up the TLS connection between the apiserver and etcd. Then, edit the API server pod specification file $apiserverconf on the control plane node and set the etcd certificate authority file parameter. --etcd-cafile= scored: true - id: 1.2.30 text: "Ensure that the --encryption-provider-config argument is set as appropriate (Manual)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--encryption-provider-config" remediation: | Follow the Kubernetes documentation and configure a EncryptionConfig file. Then, edit the API server pod specification file $apiserverconf on the control plane node and set the --encryption-provider-config parameter to the path of that file. For example, --encryption-provider-config= scored: false - id: 1.2.31 text: "Ensure that encryption providers are appropriately configured (Manual)" audit: | ENCRYPTION_PROVIDER_CONFIG=$(ps -ef | grep $apiserverbin | grep -- --encryption-provider-config | sed 's%.*encryption-provider-config[= ]\([^ ]*\).*%\1%') if test -e $ENCRYPTION_PROVIDER_CONFIG; then grep -A1 'providers:' $ENCRYPTION_PROVIDER_CONFIG | tail -n1 | grep -o "[A-Za-z]*" | sed 's/^/provider=/'; fi tests: test_items: - flag: "provider" compare: op: valid_elements value: "aescbc,kms,secretbox" remediation: | Follow the Kubernetes documentation and configure a EncryptionConfig file. In this file, choose aescbc, kms or secretbox as the encryption provider. scored: false - id: 1.2.32 text: "Ensure that the API Server only makes use of Strong Cryptographic Ciphers (Manual)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--tls-cipher-suites" compare: op: valid_elements value: "TLS_AES_128_GCM_SHA256,TLS_AES_256_GCM_SHA384,TLS_CHACHA20_POLY1305_SHA256,TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA,TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,TLS_RSA_WITH_3DES_EDE_CBC_SHA,TLS_RSA_WITH_AES_128_CBC_SHA,TLS_RSA_WITH_AES_128_GCM_SHA256,TLS_RSA_WITH_AES_256_CBC_SHA,TLS_RSA_WITH_AES_256_GCM_SHA384" remediation: | Edit the API server pod specification file /etc/kubernetes/manifests/kube-apiserver.yaml on the control plane node and set the below parameter. --tls-cipher-suites=TLS_AES_128_GCM_SHA256,TLS_AES_256_GCM_SHA384,TLS_CHACHA20_POLY1305_SHA256, TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA,TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305, TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,TLS_RSA_WITH_3DES_EDE_CBC_SHA,TLS_RSA_WITH_AES_128_CBC_SHA, TLS_RSA_WITH_AES_128_GCM_SHA256,TLS_RSA_WITH_AES_256_CBC_SHA,TLS_RSA_WITH_AES_256_GCM_SHA384 scored: false - id: 1.3 text: "Controller Manager" checks: - id: 1.3.1 text: "Ensure that the --terminated-pod-gc-threshold argument is set as appropriate (Manual)" audit: "/bin/ps -ef | grep $controllermanagerbin | grep -v grep" tests: test_items: - flag: "--terminated-pod-gc-threshold" remediation: | Edit the Controller Manager pod specification file $controllermanagerconf on the control plane node and set the --terminated-pod-gc-threshold to an appropriate threshold, for example, --terminated-pod-gc-threshold=10 scored: false - id: 1.3.2 text: "Ensure that the --profiling argument is set to false (Automated)" audit: "/bin/ps -ef | grep $controllermanagerbin | grep -v grep" tests: test_items: - flag: "--profiling" compare: op: eq value: false remediation: | Edit the Controller Manager pod specification file $controllermanagerconf on the control plane node and set the below parameter. --profiling=false scored: true - id: 1.3.3 text: "Ensure that the --use-service-account-credentials argument is set to true (Automated)" audit: "/bin/ps -ef | grep $controllermanagerbin | grep -v grep" tests: test_items: - flag: "--use-service-account-credentials" compare: op: noteq value: false remediation: | Edit the Controller Manager pod specification file $controllermanagerconf on the control plane node to set the below parameter. --use-service-account-credentials=true scored: true - id: 1.3.4 text: "Ensure that the --service-account-private-key-file argument is set as appropriate (Automated)" audit: "/bin/ps -ef | grep $controllermanagerbin | grep -v grep" tests: test_items: - flag: "--service-account-private-key-file" remediation: | Edit the Controller Manager pod specification file $controllermanagerconf on the control plane node and set the --service-account-private-key-file parameter to the private key file for service accounts. --service-account-private-key-file= scored: true - id: 1.3.5 text: "Ensure that the --root-ca-file argument is set as appropriate (Automated)" audit: "/bin/ps -ef | grep $controllermanagerbin | grep -v grep" tests: test_items: - flag: "--root-ca-file" remediation: | Edit the Controller Manager pod specification file $controllermanagerconf on the control plane node and set the --root-ca-file parameter to the certificate bundle file`. --root-ca-file= scored: true - id: 1.3.6 text: "Ensure that the RotateKubeletServerCertificate argument is set to true (Automated)" audit: "/bin/ps -ef | grep $controllermanagerbin | grep -v grep" tests: bin_op: or test_items: - flag: "--feature-gates" compare: op: nothave value: "RotateKubeletServerCertificate=false" set: true - flag: "--feature-gates" set: false remediation: | Edit the Controller Manager pod specification file $controllermanagerconf on the control plane node and set the --feature-gates parameter to include RotateKubeletServerCertificate=true. --feature-gates=RotateKubeletServerCertificate=true scored: true - id: 1.3.7 text: "Ensure that the --bind-address argument is set to 127.0.0.1 (Automated)" audit: "/bin/ps -ef | grep $controllermanagerbin | grep -v grep" tests: bin_op: or test_items: - flag: "--bind-address" compare: op: eq value: "127.0.0.1" - flag: "--bind-address" set: false remediation: | Edit the Controller Manager pod specification file $controllermanagerconf on the control plane node and ensure the correct value for the --bind-address parameter scored: true - id: 1.4 text: "Scheduler" checks: - id: 1.4.1 text: "Ensure that the --profiling argument is set to false (Automated)" audit: "/bin/ps -ef | grep $schedulerbin | grep -v grep" tests: test_items: - flag: "--profiling" compare: op: eq value: false remediation: | Edit the Scheduler pod specification file $schedulerconf file on the control plane node and set the below parameter. --profiling=false scored: true - id: 1.4.2 text: "Ensure that the --bind-address argument is set to 127.0.0.1 (Automated)" audit: "/bin/ps -ef | grep $schedulerbin | grep -v grep" tests: bin_op: or test_items: - flag: "--bind-address" compare: op: eq value: "127.0.0.1" - flag: "--bind-address" set: false remediation: | Edit the Scheduler pod specification file $schedulerconf on the control plane node and ensure the correct value for the --bind-address parameter scored: true ================================================ FILE: cfg/cis-1.23/node.yaml ================================================ --- controls: version: "cis-1.23" id: 4 text: "Worker Node Security Configuration" type: "node" groups: - id: 4.1 text: "Worker Node Configuration Files" checks: - id: 4.1.1 text: "Ensure that the kubelet service file permissions are set to 644 or more restrictive (Automated)" audit: '/bin/sh -c ''if test -e $kubeletsvc; then stat -c permissions=%a $kubeletsvc; fi'' ' tests: test_items: - flag: "permissions" compare: op: bitmask value: "644" remediation: | Run the below command (based on the file location on your system) on the each worker node. For example, chmod 644 $kubeletsvc scored: true - id: 4.1.2 text: "Ensure that the kubelet service file ownership is set to root:root (Automated)" audit: '/bin/sh -c "if test -e $kubeletsvc; then stat -c %U:%G $kubeletsvc; else echo \"File not found\"; fi"' tests: bin_op: or test_items: - flag: root:root - flag: "File not found" remediation: | Run the below command (based on the file location on your system) on the each worker node. For example, chown root:root $kubeletsvc scored: true - id: 4.1.3 text: "If proxy kubeconfig file exists ensure permissions are set to 644 or more restrictive (Manual)" audit: '/bin/sh -c ''if test -e $proxykubeconfig; then stat -c permissions=%a $proxykubeconfig; fi'' ' tests: bin_op: or test_items: - flag: "permissions" set: true compare: op: bitmask value: "644" remediation: | Run the below command (based on the file location on your system) on the each worker node. For example, chmod 644 $proxykubeconfig scored: false - id: 4.1.4 text: "If proxy kubeconfig file exists ensure ownership is set to root:root (Manual)" audit: '/bin/sh -c ''if test -e $proxykubeconfig; then stat -c %U:%G $proxykubeconfig; fi'' ' tests: bin_op: or test_items: - flag: root:root remediation: | Run the below command (based on the file location on your system) on the each worker node. For example, chown root:root $proxykubeconfig scored: false - id: 4.1.5 text: "Ensure that the --kubeconfig kubelet.conf file permissions are set to 644 or more restrictive (Automated)" audit: '/bin/sh -c ''if test -e $kubeletkubeconfig; then stat -c permissions=%a $kubeletkubeconfig; fi'' ' tests: test_items: - flag: "permissions" compare: op: bitmask value: "644" remediation: | Run the below command (based on the file location on your system) on the each worker node. For example, chmod 644 $kubeletkubeconfig scored: true - id: 4.1.6 text: "Ensure that the --kubeconfig kubelet.conf file ownership is set to root:root (Automated)" audit: '/bin/sh -c ''if test -e $kubeletkubeconfig; then stat -c %U:%G $kubeletkubeconfig; fi'' ' tests: test_items: - flag: root:root remediation: | Run the below command (based on the file location on your system) on the each worker node. For example, chown root:root $kubeletkubeconfig scored: true - id: 4.1.7 text: "Ensure that the certificate authorities file permissions are set to 644 or more restrictive (Manual)" audit: | CAFILE=$(ps -ef | grep kubelet | grep -v apiserver | grep -- --client-ca-file= | awk -F '--client-ca-file=' '{print $2}' | awk '{print $1}' | uniq) if test -z $CAFILE; then CAFILE=$kubeletcafile; fi if test -e $CAFILE; then stat -c permissions=%a $CAFILE; fi tests: test_items: - flag: "permissions" compare: op: bitmask value: "644" remediation: | Run the following command to modify the file permissions of the --client-ca-file chmod 644 scored: false - id: 4.1.8 text: "Ensure that the client certificate authorities file ownership is set to root:root (Manual)" audit: | CAFILE=$(ps -ef | grep kubelet | grep -v apiserver | grep -- --client-ca-file= | awk -F '--client-ca-file=' '{print $2}' | awk '{print $1}' | uniq) if test -z $CAFILE; then CAFILE=$kubeletcafile; fi if test -e $CAFILE; then stat -c %U:%G $CAFILE; fi tests: test_items: - flag: root:root compare: op: eq value: root:root remediation: | Run the following command to modify the ownership of the --client-ca-file. chown root:root scored: false - id: 4.1.9 text: "Ensure that the kubelet --config configuration file has permissions set to 644 or more restrictive (Automated)" audit: '/bin/sh -c ''if test -e $kubeletconf; then stat -c permissions=%a $kubeletconf; fi'' ' tests: test_items: - flag: "permissions" compare: op: bitmask value: "644" remediation: | Run the following command (using the config file location identified in the Audit step) chmod 644 $kubeletconf scored: true - id: 4.1.10 text: "Ensure that the kubelet --config configuration file ownership is set to root:root (Automated)" audit: '/bin/sh -c ''if test -e $kubeletconf; then stat -c %U:%G $kubeletconf; fi'' ' tests: test_items: - flag: root:root remediation: | Run the following command (using the config file location identified in the Audit step) chown root:root $kubeletconf scored: true - id: 4.2 text: "Kubelet" checks: - id: 4.2.1 text: "Ensure that the --anonymous-auth argument is set to false (Automated)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: "--anonymous-auth" path: '{.authentication.anonymous.enabled}' compare: op: eq value: false remediation: | If using a Kubelet config file, edit the file to set `authentication: anonymous: enabled` to `false`. If using executable arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_SYSTEM_PODS_ARGS variable. `--anonymous-auth=false` Based on your system, restart the kubelet service. For example, systemctl daemon-reload systemctl restart kubelet.service scored: true - id: 4.2.2 text: "Ensure that the --authorization-mode argument is not set to AlwaysAllow (Automated)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --authorization-mode path: '{.authorization.mode}' compare: op: nothave value: AlwaysAllow remediation: | If using a Kubelet config file, edit the file to set `authorization.mode` to Webhook. If using executable arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_AUTHZ_ARGS variable. --authorization-mode=Webhook Based on your system, restart the kubelet service. For example, systemctl daemon-reload systemctl restart kubelet.service scored: true - id: 4.2.3 text: "Ensure that the --client-ca-file argument is set as appropriate (Automated)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --client-ca-file path: '{.authentication.x509.clientCAFile}' remediation: | If using a Kubelet config file, edit the file to set `authentication.x509.clientCAFile` to the location of the client CA file. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_AUTHZ_ARGS variable. --client-ca-file= Based on your system, restart the kubelet service. For example, systemctl daemon-reload systemctl restart kubelet.service scored: true - id: 4.2.4 text: "Ensure that the --read-only-port argument is set to 0 (Manual)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: bin_op: or test_items: - flag: "--read-only-port" path: '{.readOnlyPort}' compare: op: eq value: 0 - flag: "--read-only-port" path: '{.readOnlyPort}' set: false remediation: | If using a Kubelet config file, edit the file to set `readOnlyPort` to 0. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_SYSTEM_PODS_ARGS variable. --read-only-port=0 Based on your system, restart the kubelet service. For example, systemctl daemon-reload systemctl restart kubelet.service scored: false - id: 4.2.5 text: "Ensure that the --streaming-connection-idle-timeout argument is not set to 0 (Manual)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --streaming-connection-idle-timeout path: '{.streamingConnectionIdleTimeout}' compare: op: noteq value: 0 - flag: --streaming-connection-idle-timeout path: '{.streamingConnectionIdleTimeout}' set: false bin_op: or remediation: | If using a Kubelet config file, edit the file to set `streamingConnectionIdleTimeout` to a value other than 0. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_SYSTEM_PODS_ARGS variable. --streaming-connection-idle-timeout=5m Based on your system, restart the kubelet service. For example, systemctl daemon-reload systemctl restart kubelet.service scored: false - id: 4.2.6 text: "Ensure that the --protect-kernel-defaults argument is set to true (Automated)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --protect-kernel-defaults path: '{.protectKernelDefaults}' compare: op: eq value: true remediation: | If using a Kubelet config file, edit the file to set `protectKernelDefaults` to `true`. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_SYSTEM_PODS_ARGS variable. --protect-kernel-defaults=true Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: true - id: 4.2.7 text: "Ensure that the --make-iptables-util-chains argument is set to true (Automated)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --make-iptables-util-chains path: '{.makeIPTablesUtilChains}' compare: op: eq value: true - flag: --make-iptables-util-chains path: '{.makeIPTablesUtilChains}' set: false bin_op: or remediation: | If using a Kubelet config file, edit the file to set `makeIPTablesUtilChains` to `true`. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and remove the --make-iptables-util-chains argument from the KUBELET_SYSTEM_PODS_ARGS variable. Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: true - id: 4.2.8 text: "Ensure that the --hostname-override argument is not set (Manual)" # This is one of those properties that can only be set as a command line argument. # To check if the property is set as expected, we need to parse the kubelet command # instead reading the Kubelet Configuration file. audit: "/bin/ps -fC $kubeletbin " tests: test_items: - flag: --hostname-override set: false remediation: | Edit the kubelet service file $kubeletsvc on each worker node and remove the --hostname-override argument from the KUBELET_SYSTEM_PODS_ARGS variable. Based on your system, restart the kubelet service. For example, systemctl daemon-reload systemctl restart kubelet.service scored: false - id: 4.2.9 text: "Ensure that the --event-qps argument is set to 0 or a level which ensures appropriate event capture (Manual)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --event-qps path: '{.eventRecordQPS}' compare: op: eq value: 0 remediation: | If using a Kubelet config file, edit the file to set `eventRecordQPS` to an appropriate level. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_SYSTEM_PODS_ARGS variable. Based on your system, restart the kubelet service. For example, systemctl daemon-reload systemctl restart kubelet.service scored: false - id: 4.2.10 text: "Ensure that the --tls-cert-file and --tls-private-key-file arguments are set as appropriate (Manual)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --tls-cert-file path: '{.tlsCertFile}' - flag: --tls-private-key-file path: '{.tlsPrivateKeyFile}' remediation: | If using a Kubelet config file, edit the file to set `tlsCertFile` to the location of the certificate file to use to identify this Kubelet, and `tlsPrivateKeyFile` to the location of the corresponding private key file. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameters in KUBELET_CERTIFICATE_ARGS variable. --tls-cert-file= --tls-private-key-file= Based on your system, restart the kubelet service. For example, systemctl daemon-reload systemctl restart kubelet.service scored: false - id: 4.2.11 text: "Ensure that the --rotate-certificates argument is not set to false (Automated)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --rotate-certificates path: '{.rotateCertificates}' compare: op: eq value: true - flag: --rotate-certificates path: '{.rotateCertificates}' set: false bin_op: or remediation: | If using a Kubelet config file, edit the file to add the line `rotateCertificates` to `true` or remove it altogether to use the default value. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and remove --rotate-certificates=false argument from the KUBELET_CERTIFICATE_ARGS variable. Based on your system, restart the kubelet service. For example, systemctl daemon-reload systemctl restart kubelet.service scored: true - id: 4.2.12 text: "Verify that the RotateKubeletServerCertificate argument is set to true (Manual)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: bin_op: or test_items: - flag: RotateKubeletServerCertificate path: '{.featureGates.RotateKubeletServerCertificate}' compare: op: nothave value: false - flag: RotateKubeletServerCertificate path: '{.featureGates.RotateKubeletServerCertificate}' set: false remediation: | Edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_CERTIFICATE_ARGS variable. --feature-gates=RotateKubeletServerCertificate=true Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: false - id: 4.2.13 text: "Ensure that the Kubelet only makes use of Strong Cryptographic Ciphers (Manual)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --tls-cipher-suites path: '{range .tlsCipherSuites[:]}{}{'',''}{end}' compare: op: valid_elements value: 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 remediation: | If using a Kubelet config file, edit the file to set `tlsCipherSuites` to 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 or to a subset of these values. If using executable arguments, edit the kubelet service file $kubeletsvc on each worker node and set the --tls-cipher-suites parameter as follows, or to a subset of these values. --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 Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: false ================================================ FILE: cfg/cis-1.23/policies.yaml ================================================ --- controls: version: "cis-1.23" id: 5 text: "Kubernetes Policies" type: "policies" groups: - id: 5.1 text: "RBAC and Service Accounts" checks: - id: 5.1.1 text: "Ensure that the cluster-admin role is only used where required (Manual)" type: "manual" remediation: | Identify all clusterrolebindings to the cluster-admin role. Check if they are used and if they need this role or if they could use a role with fewer privileges. Where possible, first bind users to a lower privileged role and then remove the clusterrolebinding to the cluster-admin role : kubectl delete clusterrolebinding [name] scored: false - id: 5.1.2 text: "Minimize access to secrets (Manual)" type: "manual" remediation: | Where possible, remove get, list and watch access to Secret objects in the cluster. scored: false - id: 5.1.3 text: "Minimize wildcard use in Roles and ClusterRoles (Manual)" type: "manual" remediation: | Where possible replace any use of wildcards in clusterroles and roles with specific objects or actions. scored: false - id: 5.1.4 text: "Minimize access to create pods (Manual)" type: "manual" remediation: | Where possible, remove create access to pod objects in the cluster. scored: false - id: 5.1.5 text: "Ensure that default service accounts are not actively used. (Manual)" type: "manual" remediation: | Create explicit service accounts wherever a Kubernetes workload requires specific access to the Kubernetes API server. Modify the configuration of each default service account to include this value automountServiceAccountToken: false scored: false - id: 5.1.6 text: "Ensure that Service Account Tokens are only mounted where necessary (Manual)" type: "manual" remediation: | Modify the definition of pods and service accounts which do not need to mount service account tokens to disable it. scored: false - id: 5.1.7 text: "Avoid use of system:masters group (Manual)" type: "manual" remediation: | Remove the system:masters group from all users in the cluster. scored: false - id: 5.1.8 text: "Limit use of the Bind, Impersonate and Escalate permissions in the Kubernetes cluster (Manual)" type: "manual" remediation: | Where possible, remove the impersonate, bind and escalate rights from subjects. scored: false - id: 5.2 text: "Pod Security Standards" checks: - id: 5.2.1 text: "Ensure that the cluster has at least one active policy control mechanism in place (Manual)" type: "manual" remediation: | Ensure that either Pod Security Admission or an external policy control system is in place for every namespace which contains user workloads. scored: false - id: 5.2.2 text: "Minimize the admission of privileged containers (Manual)" type: "manual" remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of privileged containers. scored: false - id: 5.2.3 text: "Minimize the admission of containers wishing to share the host process ID namespace (Automated)" type: "manual" remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of `hostPID` containers. scored: false - id: 5.2.4 text: "Minimize the admission of containers wishing to share the host IPC namespace (Automated)" type: "manual" remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of `hostIPC` containers. scored: false - id: 5.2.5 text: "Minimize the admission of containers wishing to share the host network namespace (Automated)" type: "manual" remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of `hostNetwork` containers. scored: false - id: 5.2.6 text: "Minimize the admission of containers with allowPrivilegeEscalation (Automated)" type: "manual" remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of containers with `.spec.allowPrivilegeEscalation` set to `true`. scored: false - id: 5.2.7 text: "Minimize the admission of root containers (Automated)" type: "manual" remediation: | Create a policy for each namespace in the cluster, ensuring that either `MustRunAsNonRoot` or `MustRunAs` with the range of UIDs not including 0, is set. scored: false - id: 5.2.8 text: "Minimize the admission of containers with the NET_RAW capability (Automated)" type: "manual" remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of containers with the `NET_RAW` capability. scored: false - id: 5.2.9 text: "Minimize the admission of containers with added capabilities (Automated)" type: "manual" remediation: | Ensure that `allowedCapabilities` is not present in policies for the cluster unless it is set to an empty array. scored: false - id: 5.2.10 text: "Minimize the admission of containers with capabilities assigned (Manual)" type: "manual" remediation: | Review the use of capabilites in applications running on your cluster. Where a namespace contains applications which do not require any Linux capabities to operate consider adding a PSP which forbids the admission of containers which do not drop all capabilities. scored: false - id: 5.2.11 text: "Minimize the admission of Windows HostProcess containers (Manual)" type: "manual" remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of containers that have `.securityContext.windowsOptions.hostProcess` set to `true`. scored: false - id: 5.2.12 text: "Minimize the admission of HostPath volumes (Manual)" type: "manual" remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of containers with `hostPath` volumes. scored: false - id: 5.2.13 text: "Minimize the admission of containers which use HostPorts (Manual)" type: "manual" remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of containers which use `hostPort` sections. scored: false - id: 5.3 text: "Network Policies and CNI" checks: - id: 5.3.1 text: "Ensure that the CNI in use supports NetworkPolicies (Manual)" type: "manual" remediation: | If the CNI plugin in use does not support network policies, consideration should be given to making use of a different plugin, or finding an alternate mechanism for restricting traffic in the Kubernetes cluster. scored: false - id: 5.3.2 text: "Ensure that all Namespaces have NetworkPolicies defined (Manual)" type: "manual" remediation: | Follow the documentation and create NetworkPolicy objects as you need them. scored: false - id: 5.4 text: "Secrets Management" checks: - id: 5.4.1 text: "Prefer using Secrets as files over Secrets as environment variables (Manual)" type: "manual" remediation: | If possible, rewrite application code to read Secrets from mounted secret files, rather than from environment variables. scored: false - id: 5.4.2 text: "Consider external secret storage (Manual)" type: "manual" remediation: | Refer to the Secrets management options offered by your cloud provider or a third-party secrets management solution. scored: false - id: 5.5 text: "Extensible Admission Control" checks: - id: 5.5.1 text: "Configure Image Provenance using ImagePolicyWebhook admission controller (Manual)" type: "manual" remediation: | Follow the Kubernetes documentation and setup image provenance. scored: false - id: 5.7 text: "General Policies" checks: - id: 5.7.1 text: "Create administrative boundaries between resources using namespaces (Manual)" type: "manual" remediation: | Follow the documentation and create namespaces for objects in your deployment as you need them. scored: false - id: 5.7.2 text: "Ensure that the seccomp profile is set to docker/default in your Pod definitions (Manual)" type: "manual" remediation: | Use `securityContext` to enable the docker/default seccomp profile in your pod definitions. An example is as below: securityContext: seccompProfile: type: RuntimeDefault scored: false - id: 5.7.3 text: "Apply SecurityContext to your Pods and Containers (Manual)" type: "manual" remediation: | Follow the Kubernetes documentation and apply SecurityContexts to your Pods. For a suggested list of SecurityContexts, you may refer to the CIS Security Benchmark for Docker Containers. scored: false - id: 5.7.4 text: "The default namespace should not be used (Manual)" type: "manual" remediation: | Ensure that namespaces are created to allow for appropriate segregation of Kubernetes resources and that all new resources are created in a specific namespace. scored: false ================================================ FILE: cfg/cis-1.24/config.yaml ================================================ --- ## Version-specific settings that override the values in cfg/config.yaml ================================================ FILE: cfg/cis-1.24/controlplane.yaml ================================================ --- controls: version: "cis-1.24" id: 3 text: "Control Plane Configuration" type: "controlplane" groups: - id: 3.1 text: "Authentication and Authorization" checks: - id: 3.1.1 text: "Client certificate authentication should not be used for users (Manual)" type: "manual" remediation: | Alternative mechanisms provided by Kubernetes such as the use of OIDC should be implemented in place of client certificates. scored: false - id: 3.2 text: "Logging" checks: - id: 3.2.1 text: "Ensure that a minimal audit policy is created (Manual)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--audit-policy-file" set: true remediation: | Create an audit policy file for your cluster. scored: false - id: 3.2.2 text: "Ensure that the audit policy covers key security concerns (Manual)" type: "manual" remediation: | Review the audit policy provided for the cluster and ensure that it covers at least the following areas, - Access to Secrets managed by the cluster. Care should be taken to only log Metadata for requests to Secrets, ConfigMaps, and TokenReviews, in order to avoid risk of logging sensitive data. - Modification of Pod and Deployment objects. - Use of `pods/exec`, `pods/portforward`, `pods/proxy` and `services/proxy`. For most requests, minimally logging at the Metadata level is recommended (the most basic level of logging). scored: false ================================================ FILE: cfg/cis-1.24/etcd.yaml ================================================ --- controls: version: "cis-1.24" id: 2 text: "Etcd Node Configuration" type: "etcd" groups: - id: 2 text: "Etcd Node Configuration" checks: - id: 2.1 text: "Ensure that the --cert-file and --key-file arguments are set as appropriate (Automated)" audit: "/bin/ps -ef | /bin/grep $etcdbin | /bin/grep -v grep" tests: bin_op: and test_items: - flag: "--cert-file" env: "ETCD_CERT_FILE" - flag: "--key-file" env: "ETCD_KEY_FILE" remediation: | Follow the etcd service documentation and configure TLS encryption. Then, edit the etcd pod specification file /etc/kubernetes/manifests/etcd.yaml on the master node and set the below parameters. --cert-file= --key-file= scored: true - id: 2.2 text: "Ensure that the --client-cert-auth argument is set to true (Automated)" audit: "/bin/ps -ef | /bin/grep $etcdbin | /bin/grep -v grep" tests: test_items: - flag: "--client-cert-auth" env: "ETCD_CLIENT_CERT_AUTH" compare: op: eq value: true remediation: | Edit the etcd pod specification file $etcdconf on the master node and set the below parameter. --client-cert-auth="true" scored: true - id: 2.3 text: "Ensure that the --auto-tls argument is not set to true (Automated)" audit: "/bin/ps -ef | /bin/grep $etcdbin | /bin/grep -v grep" tests: bin_op: or test_items: - flag: "--auto-tls" env: "ETCD_AUTO_TLS" set: false - flag: "--auto-tls" env: "ETCD_AUTO_TLS" compare: op: eq value: false remediation: | Edit the etcd pod specification file $etcdconf on the master node and either remove the --auto-tls parameter or set it to false. --auto-tls=false scored: true - id: 2.4 text: "Ensure that the --peer-cert-file and --peer-key-file arguments are set as appropriate (Automated)" audit: "/bin/ps -ef | /bin/grep $etcdbin | /bin/grep -v grep" tests: bin_op: and test_items: - flag: "--peer-cert-file" env: "ETCD_PEER_CERT_FILE" - flag: "--peer-key-file" env: "ETCD_PEER_KEY_FILE" remediation: | Follow the etcd service documentation and configure peer TLS encryption as appropriate for your etcd cluster. Then, edit the etcd pod specification file $etcdconf on the master node and set the below parameters. --peer-client-file= --peer-key-file= scored: true - id: 2.5 text: "Ensure that the --peer-client-cert-auth argument is set to true (Automated)" audit: "/bin/ps -ef | /bin/grep $etcdbin | /bin/grep -v grep" tests: test_items: - flag: "--peer-client-cert-auth" env: "ETCD_PEER_CLIENT_CERT_AUTH" compare: op: eq value: true remediation: | Edit the etcd pod specification file $etcdconf on the master node and set the below parameter. --peer-client-cert-auth=true scored: true - id: 2.6 text: "Ensure that the --peer-auto-tls argument is not set to true (Automated)" audit: "/bin/ps -ef | /bin/grep $etcdbin | /bin/grep -v grep" tests: bin_op: or test_items: - flag: "--peer-auto-tls" env: "ETCD_PEER_AUTO_TLS" set: false - flag: "--peer-auto-tls" env: "ETCD_PEER_AUTO_TLS" compare: op: eq value: false remediation: | Edit the etcd pod specification file $etcdconf on the master node and either remove the --peer-auto-tls parameter or set it to false. --peer-auto-tls=false scored: true - id: 2.7 text: "Ensure that a unique Certificate Authority is used for etcd (Manual)" audit: "/bin/ps -ef | /bin/grep $etcdbin | /bin/grep -v grep" tests: test_items: - flag: "--trusted-ca-file" env: "ETCD_TRUSTED_CA_FILE" remediation: | [Manual test] Follow the etcd documentation and create a dedicated certificate authority setup for the etcd service. Then, edit the etcd pod specification file $etcdconf on the master node and set the below parameter. --trusted-ca-file= scored: false ================================================ FILE: cfg/cis-1.24/master.yaml ================================================ --- controls: version: "cis-1.24" id: 1 text: "Control Plane Security Configuration" type: "master" groups: - id: 1.1 text: "Control Plane Node Configuration Files" checks: - id: 1.1.1 text: "Ensure that the API server pod specification file permissions are set to 600 or more restrictive (Automated)" audit: "/bin/sh -c 'if test -e $apiserverconf; then stat -c permissions=%a $apiserverconf; fi'" tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chmod 600 $apiserverconf scored: true - id: 1.1.2 text: "Ensure that the API server pod specification file ownership is set to root:root (Automated)" audit: "/bin/sh -c 'if test -e $apiserverconf; then stat -c %U:%G $apiserverconf; fi'" tests: test_items: - flag: "root:root" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chown root:root $apiserverconf scored: true - id: 1.1.3 text: "Ensure that the controller manager pod specification file permissions are set to 600 or more restrictive (Automated)" audit: "/bin/sh -c 'if test -e $controllermanagerconf; then stat -c permissions=%a $controllermanagerconf; fi'" tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chmod 600 $controllermanagerconf scored: true - id: 1.1.4 text: "Ensure that the controller manager pod specification file ownership is set to root:root (Automated)" audit: "/bin/sh -c 'if test -e $controllermanagerconf; then stat -c %U:%G $controllermanagerconf; fi'" tests: test_items: - flag: "root:root" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chown root:root $controllermanagerconf scored: true - id: 1.1.5 text: "Ensure that the scheduler pod specification file permissions are set to 600 or more restrictive (Automated)" audit: "/bin/sh -c 'if test -e $schedulerconf; then stat -c permissions=%a $schedulerconf; fi'" tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chmod 600 $schedulerconf scored: true - id: 1.1.6 text: "Ensure that the scheduler pod specification file ownership is set to root:root (Automated)" audit: "/bin/sh -c 'if test -e $schedulerconf; then stat -c %U:%G $schedulerconf; fi'" tests: test_items: - flag: "root:root" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chown root:root $schedulerconf scored: true - id: 1.1.7 text: "Ensure that the etcd pod specification file permissions are set to 600 or more restrictive (Automated)" audit: "/bin/sh -c 'if test -e $etcdconf; then find $etcdconf -name '*etcd*' | xargs stat -c permissions=%a; fi'" use_multiple_values: true tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chmod 600 $etcdconf scored: true - id: 1.1.8 text: "Ensure that the etcd pod specification file ownership is set to root:root (Automated)" audit: "/bin/sh -c 'if test -e $etcdconf; then find $etcdconf -name '*etcd*' | xargs stat -c %U:%G; fi'" use_multiple_values: true tests: test_items: - flag: "root:root" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chown root:root $etcdconf scored: true - id: 1.1.9 text: "Ensure that the Container Network Interface file permissions are set to 600 or more restrictive (Manual)" audit: | ps -ef | grep $kubeletbin | grep -- --cni-conf-dir | sed 's%.*cni-conf-dir[= ]\([^ ]*\).*%\1%' | xargs -I{} find {} -mindepth 1 | xargs --no-run-if-empty stat -c permissions=%a find /var/lib/cni/networks -type f 2> /dev/null | xargs --no-run-if-empty stat -c permissions=%a use_multiple_values: true tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chmod 600 scored: false - id: 1.1.10 text: "Ensure that the Container Network Interface file ownership is set to root:root (Manual)" audit: | ps -ef | grep $kubeletbin | grep -- --cni-conf-dir | sed 's%.*cni-conf-dir[= ]\([^ ]*\).*%\1%' | xargs -I{} find {} -mindepth 1 | xargs --no-run-if-empty stat -c %U:%G find /var/lib/cni/networks -type f 2> /dev/null | xargs --no-run-if-empty stat -c %U:%G use_multiple_values: true tests: test_items: - flag: "root:root" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chown root:root scored: false - id: 1.1.11 text: "Ensure that the etcd data directory permissions are set to 700 or more restrictive (Automated)" audit: | DATA_DIR='' for d in $(ps -ef | grep $etcdbin | grep -- --data-dir | sed 's%.*data-dir[= ]\([^ ]*\).*%\1%'); do if test -d "$d"; then DATA_DIR="$d"; fi done if ! test -d "$DATA_DIR"; then DATA_DIR=$etcddatadir; fi stat -c permissions=%a "$DATA_DIR" tests: test_items: - flag: "permissions" compare: op: bitmask value: "700" remediation: | On the etcd server node, get the etcd data directory, passed as an argument --data-dir, from the command 'ps -ef | grep etcd'. Run the below command (based on the etcd data directory found above). For example, chmod 700 /var/lib/etcd scored: true - id: 1.1.12 text: "Ensure that the etcd data directory ownership is set to etcd:etcd (Automated)" audit: | DATA_DIR='' for d in $(ps -ef | grep $etcdbin | grep -- --data-dir | sed 's%.*data-dir[= ]\([^ ]*\).*%\1%'); do if test -d "$d"; then DATA_DIR="$d"; fi done if ! test -d "$DATA_DIR"; then DATA_DIR=$etcddatadir; fi stat -c %U:%G "$DATA_DIR" tests: test_items: - flag: "etcd:etcd" remediation: | On the etcd server node, get the etcd data directory, passed as an argument --data-dir, from the command 'ps -ef | grep etcd'. Run the below command (based on the etcd data directory found above). For example, chown etcd:etcd /var/lib/etcd scored: true - id: 1.1.13 text: "Ensure that the admin.conf file permissions are set to 600 or more restrictive (Automated)" audit: "/bin/sh -c 'if test -e /etc/kubernetes/admin.conf; then stat -c permissions=%a /etc/kubernetes/admin.conf; fi'" tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chmod 600 /etc/kubernetes/admin.conf scored: true - id: 1.1.14 text: "Ensure that the admin.conf file ownership is set to root:root (Automated)" audit: "/bin/sh -c 'if test -e /etc/kubernetes/admin.conf; then stat -c %U:%G /etc/kubernetes/admin.conf; fi'" tests: test_items: - flag: "root:root" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chown root:root /etc/kubernetes/admin.conf scored: true - id: 1.1.15 text: "Ensure that the scheduler.conf file permissions are set to 600 or more restrictive (Automated)" audit: "/bin/sh -c 'if test -e $schedulerkubeconfig; then stat -c permissions=%a $schedulerkubeconfig; fi'" tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chmod 600 $schedulerkubeconfig scored: true - id: 1.1.16 text: "Ensure that the scheduler.conf file ownership is set to root:root (Automated)" audit: "/bin/sh -c 'if test -e $schedulerkubeconfig; then stat -c %U:%G $schedulerkubeconfig; fi'" tests: test_items: - flag: "root:root" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chown root:root $schedulerkubeconfig scored: true - id: 1.1.17 text: "Ensure that the controller-manager.conf file permissions are set to 600 or more restrictive (Automated)" audit: "/bin/sh -c 'if test -e $controllermanagerkubeconfig; then stat -c permissions=%a $controllermanagerkubeconfig; fi'" tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chmod 600 $controllermanagerkubeconfig scored: true - id: 1.1.18 text: "Ensure that the controller-manager.conf file ownership is set to root:root (Automated)" audit: "/bin/sh -c 'if test -e $controllermanagerkubeconfig; then stat -c %U:%G $controllermanagerkubeconfig; fi'" tests: test_items: - flag: "root:root" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chown root:root $controllermanagerkubeconfig scored: true - id: 1.1.19 text: "Ensure that the Kubernetes PKI directory and file ownership is set to root:root (Automated)" audit: "find /etc/kubernetes/pki/ | xargs stat -c %U:%G" use_multiple_values: true tests: test_items: - flag: "root:root" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chown -R root:root /etc/kubernetes/pki/ scored: true - id: 1.1.20 text: "Ensure that the Kubernetes PKI certificate file permissions are set to 600 or more restrictive (Manual)" audit: "find /etc/kubernetes/pki/ -name '*.crt' | xargs stat -c permissions=%a" use_multiple_values: true tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chmod -R 600 /etc/kubernetes/pki/*.crt scored: false - id: 1.1.21 text: "Ensure that the Kubernetes PKI key file permissions are set to 600 (Manual)" audit: "find /etc/kubernetes/pki/ -name '*.key' | xargs stat -c permissions=%a" use_multiple_values: true tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chmod -R 600 /etc/kubernetes/pki/*.key scored: false - id: 1.2 text: "API Server" checks: - id: 1.2.1 text: "Ensure that the --anonymous-auth argument is set to false (Manual)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--anonymous-auth" compare: op: eq value: false remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the below parameter. --anonymous-auth=false scored: false - id: 1.2.2 text: "Ensure that the --token-auth-file parameter is not set (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--token-auth-file" set: false remediation: | Follow the documentation and configure alternate mechanisms for authentication. Then, edit the API server pod specification file $apiserverconf on the control plane node and remove the --token-auth-file= parameter. scored: true - id: 1.2.3 text: "Ensure that the --DenyServiceExternalIPs is not set (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: bin_op: or test_items: - flag: "--enable-admission-plugins" compare: op: nothave value: "DenyServiceExternalIPs" - flag: "--enable-admission-plugins" set: false remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and remove the `DenyServiceExternalIPs` from enabled admission plugins. scored: true - id: 1.2.4 text: "Ensure that the --kubelet-client-certificate and --kubelet-client-key arguments are set as appropriate (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: bin_op: and test_items: - flag: "--kubelet-client-certificate" - flag: "--kubelet-client-key" remediation: | Follow the Kubernetes documentation and set up the TLS connection between the apiserver and kubelets. Then, edit API server pod specification file $apiserverconf on the control plane node and set the kubelet client certificate and key parameters as below. --kubelet-client-certificate= --kubelet-client-key= scored: true - id: 1.2.5 text: "Ensure that the --kubelet-certificate-authority argument is set as appropriate (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--kubelet-certificate-authority" remediation: | Follow the Kubernetes documentation and setup the TLS connection between the apiserver and kubelets. Then, edit the API server pod specification file $apiserverconf on the control plane node and set the --kubelet-certificate-authority parameter to the path to the cert file for the certificate authority. --kubelet-certificate-authority= scored: true - id: 1.2.6 text: "Ensure that the --authorization-mode argument is not set to AlwaysAllow (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--authorization-mode" compare: op: nothave value: "AlwaysAllow" remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the --authorization-mode parameter to values other than AlwaysAllow. One such example could be as below. --authorization-mode=RBAC scored: true - id: 1.2.7 text: "Ensure that the --authorization-mode argument includes Node (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--authorization-mode" compare: op: has value: "Node" remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the --authorization-mode parameter to a value that includes Node. --authorization-mode=Node,RBAC scored: true - id: 1.2.8 text: "Ensure that the --authorization-mode argument includes RBAC (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--authorization-mode" compare: op: has value: "RBAC" remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the --authorization-mode parameter to a value that includes RBAC, for example `--authorization-mode=Node,RBAC`. scored: true - id: 1.2.9 text: "Ensure that the admission control plugin EventRateLimit is set (Manual)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--enable-admission-plugins" compare: op: has value: "EventRateLimit" remediation: | Follow the Kubernetes documentation and set the desired limits in a configuration file. Then, edit the API server pod specification file $apiserverconf and set the below parameters. --enable-admission-plugins=...,EventRateLimit,... --admission-control-config-file= scored: false - id: 1.2.10 text: "Ensure that the admission control plugin AlwaysAdmit is not set (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: bin_op: or test_items: - flag: "--enable-admission-plugins" compare: op: nothave value: AlwaysAdmit - flag: "--enable-admission-plugins" set: false remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and either remove the --enable-admission-plugins parameter, or set it to a value that does not include AlwaysAdmit. scored: true - id: 1.2.11 text: "Ensure that the admission control plugin AlwaysPullImages is set (Manual)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--enable-admission-plugins" compare: op: has value: "AlwaysPullImages" remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the --enable-admission-plugins parameter to include AlwaysPullImages. --enable-admission-plugins=...,AlwaysPullImages,... scored: false - id: 1.2.12 text: "Ensure that the admission control plugin SecurityContextDeny is set if PodSecurityPolicy is not used (Manual)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: bin_op: or test_items: - flag: "--enable-admission-plugins" compare: op: has value: "SecurityContextDeny" - flag: "--enable-admission-plugins" compare: op: has value: "PodSecurityPolicy" remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the --enable-admission-plugins parameter to include SecurityContextDeny, unless PodSecurityPolicy is already in place. --enable-admission-plugins=...,SecurityContextDeny,... scored: false - id: 1.2.13 text: "Ensure that the admission control plugin ServiceAccount is set (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: bin_op: or test_items: - flag: "--disable-admission-plugins" compare: op: nothave value: "ServiceAccount" - flag: "--disable-admission-plugins" set: false remediation: | Follow the documentation and create ServiceAccount objects as per your environment. Then, edit the API server pod specification file $apiserverconf on the control plane node and ensure that the --disable-admission-plugins parameter is set to a value that does not include ServiceAccount. scored: true - id: 1.2.14 text: "Ensure that the admission control plugin NamespaceLifecycle is set (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: bin_op: or test_items: - flag: "--disable-admission-plugins" compare: op: nothave value: "NamespaceLifecycle" - flag: "--disable-admission-plugins" set: false remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the --disable-admission-plugins parameter to ensure it does not include NamespaceLifecycle. scored: true - id: 1.2.15 text: "Ensure that the admission control plugin NodeRestriction is set (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--enable-admission-plugins" compare: op: has value: "NodeRestriction" remediation: | Follow the Kubernetes documentation and configure NodeRestriction plug-in on kubelets. Then, edit the API server pod specification file $apiserverconf on the control plane node and set the --enable-admission-plugins parameter to a value that includes NodeRestriction. --enable-admission-plugins=...,NodeRestriction,... scored: true - id: 1.2.16 text: "Ensure that the --secure-port argument is not set to 0 (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: bin_op: or test_items: - flag: "--secure-port" compare: op: gt value: 0 - flag: "--secure-port" set: false remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and either remove the --secure-port parameter or set it to a different (non-zero) desired port. scored: true - id: 1.2.17 text: "Ensure that the --profiling argument is set to false (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--profiling" compare: op: eq value: false remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the below parameter. --profiling=false scored: true - id: 1.2.18 text: "Ensure that the --audit-log-path argument is set (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--audit-log-path" remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the --audit-log-path parameter to a suitable path and file where you would like audit logs to be written, for example, --audit-log-path=/var/log/apiserver/audit.log scored: true - id: 1.2.19 text: "Ensure that the --audit-log-maxage argument is set to 30 or as appropriate (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--audit-log-maxage" compare: op: gte value: 30 remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the --audit-log-maxage parameter to 30 or as an appropriate number of days, for example, --audit-log-maxage=30 scored: true - id: 1.2.20 text: "Ensure that the --audit-log-maxbackup argument is set to 10 or as appropriate (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--audit-log-maxbackup" compare: op: gte value: 10 remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the --audit-log-maxbackup parameter to 10 or to an appropriate value. For example, --audit-log-maxbackup=10 scored: true - id: 1.2.21 text: "Ensure that the --audit-log-maxsize argument is set to 100 or as appropriate (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--audit-log-maxsize" compare: op: gte value: 100 remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the --audit-log-maxsize parameter to an appropriate size in MB. For example, to set it as 100 MB, --audit-log-maxsize=100 scored: true - id: 1.2.22 text: "Ensure that the --request-timeout argument is set as appropriate (Manual)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" type: manual remediation: | Edit the API server pod specification file $apiserverconf and set the below parameter as appropriate and if needed. For example, --request-timeout=300s scored: false - id: 1.2.23 text: "Ensure that the --service-account-lookup argument is set to true (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: bin_op: or test_items: - flag: "--service-account-lookup" set: false - flag: "--service-account-lookup" compare: op: eq value: true remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the below parameter. --service-account-lookup=true Alternatively, you can delete the --service-account-lookup parameter from this file so that the default takes effect. scored: true - id: 1.2.24 text: "Ensure that the --service-account-key-file argument is set as appropriate (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--service-account-key-file" remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the --service-account-key-file parameter to the public key file for service accounts. For example, --service-account-key-file= scored: true - id: 1.2.25 text: "Ensure that the --etcd-certfile and --etcd-keyfile arguments are set as appropriate (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: bin_op: and test_items: - flag: "--etcd-certfile" - flag: "--etcd-keyfile" remediation: | Follow the Kubernetes documentation and set up the TLS connection between the apiserver and etcd. Then, edit the API server pod specification file $apiserverconf on the control plane node and set the etcd certificate and key file parameters. --etcd-certfile= --etcd-keyfile= scored: true - id: 1.2.26 text: "Ensure that the --tls-cert-file and --tls-private-key-file arguments are set as appropriate (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: bin_op: and test_items: - flag: "--tls-cert-file" - flag: "--tls-private-key-file" remediation: | Follow the Kubernetes documentation and set up the TLS connection on the apiserver. Then, edit the API server pod specification file $apiserverconf on the control plane node and set the TLS certificate and private key file parameters. --tls-cert-file= --tls-private-key-file= scored: true - id: 1.2.27 text: "Ensure that the --client-ca-file argument is set as appropriate (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--client-ca-file" remediation: | Follow the Kubernetes documentation and set up the TLS connection on the apiserver. Then, edit the API server pod specification file $apiserverconf on the control plane node and set the client certificate authority file. --client-ca-file= scored: true - id: 1.2.28 text: "Ensure that the --etcd-cafile argument is set as appropriate (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--etcd-cafile" remediation: | Follow the Kubernetes documentation and set up the TLS connection between the apiserver and etcd. Then, edit the API server pod specification file $apiserverconf on the control plane node and set the etcd certificate authority file parameter. --etcd-cafile= scored: true - id: 1.2.29 text: "Ensure that the --encryption-provider-config argument is set as appropriate (Manual)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--encryption-provider-config" remediation: | Follow the Kubernetes documentation and configure a EncryptionConfig file. Then, edit the API server pod specification file $apiserverconf on the control plane node and set the --encryption-provider-config parameter to the path of that file. For example, --encryption-provider-config= scored: false - id: 1.2.30 text: "Ensure that encryption providers are appropriately configured (Manual)" audit: | ENCRYPTION_PROVIDER_CONFIG=$(ps -ef | grep $apiserverbin | grep -- --encryption-provider-config | sed 's%.*encryption-provider-config[= ]\([^ ]*\).*%\1%') if test -e $ENCRYPTION_PROVIDER_CONFIG; then grep -A1 'providers:' $ENCRYPTION_PROVIDER_CONFIG | tail -n1 | grep -o "[A-Za-z]*" | sed 's/^/provider=/'; fi tests: test_items: - flag: "provider" compare: op: valid_elements value: "aescbc,kms,secretbox" remediation: | Follow the Kubernetes documentation and configure a EncryptionConfig file. In this file, choose aescbc, kms or secretbox as the encryption provider. scored: false - id: 1.2.31 text: "Ensure that the API Server only makes use of Strong Cryptographic Ciphers (Manual)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--tls-cipher-suites" compare: op: valid_elements value: "TLS_AES_128_GCM_SHA256,TLS_AES_256_GCM_SHA384,TLS_CHACHA20_POLY1305_SHA256,TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA,TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,TLS_RSA_WITH_3DES_EDE_CBC_SHA,TLS_RSA_WITH_AES_128_CBC_SHA,TLS_RSA_WITH_AES_128_GCM_SHA256,TLS_RSA_WITH_AES_256_CBC_SHA,TLS_RSA_WITH_AES_256_GCM_SHA384" remediation: | Edit the API server pod specification file /etc/kubernetes/manifests/kube-apiserver.yaml on the control plane node and set the below parameter. --tls-cipher-suites=TLS_AES_128_GCM_SHA256,TLS_AES_256_GCM_SHA384,TLS_CHACHA20_POLY1305_SHA256, TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA,TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305, TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,TLS_RSA_WITH_3DES_EDE_CBC_SHA,TLS_RSA_WITH_AES_128_CBC_SHA, TLS_RSA_WITH_AES_128_GCM_SHA256,TLS_RSA_WITH_AES_256_CBC_SHA,TLS_RSA_WITH_AES_256_GCM_SHA384 scored: false - id: 1.3 text: "Controller Manager" checks: - id: 1.3.1 text: "Ensure that the --terminated-pod-gc-threshold argument is set as appropriate (Manual)" audit: "/bin/ps -ef | grep $controllermanagerbin | grep -v grep" tests: test_items: - flag: "--terminated-pod-gc-threshold" remediation: | Edit the Controller Manager pod specification file $controllermanagerconf on the control plane node and set the --terminated-pod-gc-threshold to an appropriate threshold, for example, --terminated-pod-gc-threshold=10 scored: false - id: 1.3.2 text: "Ensure that the --profiling argument is set to false (Automated)" audit: "/bin/ps -ef | grep $controllermanagerbin | grep -v grep" tests: test_items: - flag: "--profiling" compare: op: eq value: false remediation: | Edit the Controller Manager pod specification file $controllermanagerconf on the control plane node and set the below parameter. --profiling=false scored: true - id: 1.3.3 text: "Ensure that the --use-service-account-credentials argument is set to true (Automated)" audit: "/bin/ps -ef | grep $controllermanagerbin | grep -v grep" tests: test_items: - flag: "--use-service-account-credentials" compare: op: noteq value: false remediation: | Edit the Controller Manager pod specification file $controllermanagerconf on the control plane node to set the below parameter. --use-service-account-credentials=true scored: true - id: 1.3.4 text: "Ensure that the --service-account-private-key-file argument is set as appropriate (Automated)" audit: "/bin/ps -ef | grep $controllermanagerbin | grep -v grep" tests: test_items: - flag: "--service-account-private-key-file" remediation: | Edit the Controller Manager pod specification file $controllermanagerconf on the control plane node and set the --service-account-private-key-file parameter to the private key file for service accounts. --service-account-private-key-file= scored: true - id: 1.3.5 text: "Ensure that the --root-ca-file argument is set as appropriate (Automated)" audit: "/bin/ps -ef | grep $controllermanagerbin | grep -v grep" tests: test_items: - flag: "--root-ca-file" remediation: | Edit the Controller Manager pod specification file $controllermanagerconf on the control plane node and set the --root-ca-file parameter to the certificate bundle file`. --root-ca-file= scored: true - id: 1.3.6 text: "Ensure that the RotateKubeletServerCertificate argument is set to true (Automated)" audit: "/bin/ps -ef | grep $controllermanagerbin | grep -v grep" tests: bin_op: or test_items: - flag: "--feature-gates" compare: op: nothave value: "RotateKubeletServerCertificate=false" set: true - flag: "--feature-gates" set: false remediation: | Edit the Controller Manager pod specification file $controllermanagerconf on the control plane node and set the --feature-gates parameter to include RotateKubeletServerCertificate=true. --feature-gates=RotateKubeletServerCertificate=true scored: true - id: 1.3.7 text: "Ensure that the --bind-address argument is set to 127.0.0.1 (Automated)" audit: "/bin/ps -ef | grep $controllermanagerbin | grep -v grep" tests: test_items: - flag: "--bind-address" compare: op: eq value: "127.0.0.1" remediation: | Edit the Controller Manager pod specification file $controllermanagerconf on the control plane node and ensure the correct value for the --bind-address parameter scored: true - id: 1.4 text: "Scheduler" checks: - id: 1.4.1 text: "Ensure that the --profiling argument is set to false (Automated)" audit: "/bin/ps -ef | grep $schedulerbin | grep -v grep" tests: test_items: - flag: "--profiling" compare: op: eq value: false remediation: | Edit the Scheduler pod specification file $schedulerconf file on the control plane node and set the below parameter. --profiling=false scored: true - id: 1.4.2 text: "Ensure that the --bind-address argument is set to 127.0.0.1 (Automated)" audit: "/bin/ps -ef | grep $schedulerbin | grep -v grep" tests: test_items: - flag: "--bind-address" compare: op: eq value: "127.0.0.1" remediation: | Edit the Scheduler pod specification file $schedulerconf on the control plane node and ensure the correct value for the --bind-address parameter scored: true ================================================ FILE: cfg/cis-1.24/node.yaml ================================================ --- controls: version: "cis-1.24" id: 4 text: "Worker Node Security Configuration" type: "node" groups: - id: 4.1 text: "Worker Node Configuration Files" checks: - id: 4.1.1 text: "Ensure that the kubelet service file permissions are set to 600 or more restrictive (Automated)" audit: '/bin/sh -c ''if test -e $kubeletsvc; then stat -c permissions=%a $kubeletsvc; fi'' ' tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Run the below command (based on the file location on your system) on the each worker node. For example, chmod 600 $kubeletsvc scored: true - id: 4.1.2 text: "Ensure that the kubelet service file ownership is set to root:root (Automated)" audit: '/bin/sh -c "if test -e $kubeletsvc; then stat -c %U:%G $kubeletsvc; else echo \"File not found\"; fi"' tests: bin_op: or test_items: - flag: root:root - flag: "File not found" remediation: | Run the below command (based on the file location on your system) on the each worker node. For example, chown root:root $kubeletsvc scored: true - id: 4.1.3 text: "If proxy kubeconfig file exists ensure permissions are set to 600 or more restrictive (Manual)" audit: '/bin/sh -c ''if test -e $proxykubeconfig; then stat -c permissions=%a $proxykubeconfig; fi'' ' tests: bin_op: or test_items: - flag: "permissions" set: true compare: op: bitmask value: "600" remediation: | Run the below command (based on the file location on your system) on the each worker node. For example, chmod 600 $proxykubeconfig scored: false - id: 4.1.4 text: "If proxy kubeconfig file exists ensure ownership is set to root:root (Manual)" audit: '/bin/sh -c ''if test -e $proxykubeconfig; then stat -c %U:%G $proxykubeconfig; fi'' ' tests: bin_op: or test_items: - flag: root:root remediation: | Run the below command (based on the file location on your system) on the each worker node. For example, chown root:root $proxykubeconfig scored: false - id: 4.1.5 text: "Ensure that the --kubeconfig kubelet.conf file permissions are set to 600 or more restrictive (Automated)" audit: '/bin/sh -c ''if test -e $kubeletkubeconfig; then stat -c permissions=%a $kubeletkubeconfig; fi'' ' tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Run the below command (based on the file location on your system) on the each worker node. For example, chmod 600 $kubeletkubeconfig scored: true - id: 4.1.6 text: "Ensure that the --kubeconfig kubelet.conf file ownership is set to root:root (Automated)" audit: '/bin/sh -c ''if test -e $kubeletkubeconfig; then stat -c %U:%G $kubeletkubeconfig; fi'' ' tests: test_items: - flag: root:root remediation: | Run the below command (based on the file location on your system) on the each worker node. For example, chown root:root $kubeletkubeconfig scored: true - id: 4.1.7 text: "Ensure that the certificate authorities file permissions are set to 600 or more restrictive (Manual)" audit: | CAFILE=$(ps -ef | grep kubelet | grep -v apiserver | grep -- --client-ca-file= | awk -F '--client-ca-file=' '{print $2}' | awk '{print $1}' | uniq) if test -z $CAFILE; then CAFILE=$kubeletcafile; fi if test -e $CAFILE; then stat -c permissions=%a $CAFILE; fi tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Run the following command to modify the file permissions of the --client-ca-file chmod 600 scored: false - id: 4.1.8 text: "Ensure that the client certificate authorities file ownership is set to root:root (Manual)" audit: | CAFILE=$(ps -ef | grep kubelet | grep -v apiserver | grep -- --client-ca-file= | awk -F '--client-ca-file=' '{print $2}' | awk '{print $1}' | uniq) if test -z $CAFILE; then CAFILE=$kubeletcafile; fi if test -e $CAFILE; then stat -c %U:%G $CAFILE; fi tests: test_items: - flag: root:root compare: op: eq value: root:root remediation: | Run the following command to modify the ownership of the --client-ca-file. chown root:root scored: false - id: 4.1.9 text: "If the kubelet config.yaml configuration file is being used validate permissions set to 600 or more restrictive (Manual)" audit: '/bin/sh -c ''if test -e $kubeletconf; then stat -c permissions=%a $kubeletconf; fi'' ' tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Run the following command (using the config file location identified in the Audit step) chmod 600 $kubeletconf scored: false - id: 4.1.10 text: "If the kubelet config.yaml configuration file is being used validate file ownership is set to root:root (Manual)" audit: '/bin/sh -c ''if test -e $kubeletconf; then stat -c %U:%G $kubeletconf; fi'' ' tests: test_items: - flag: root:root remediation: | Run the following command (using the config file location identified in the Audit step) chown root:root $kubeletconf scored: false - id: 4.2 text: "Kubelet" checks: - id: 4.2.1 text: "Ensure that the --anonymous-auth argument is set to false (Automated)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: "--anonymous-auth" path: '{.authentication.anonymous.enabled}' compare: op: eq value: false remediation: | If using a Kubelet config file, edit the file to set `authentication: anonymous: enabled` to `false`. If using executable arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_SYSTEM_PODS_ARGS variable. `--anonymous-auth=false` Based on your system, restart the kubelet service. For example, systemctl daemon-reload systemctl restart kubelet.service scored: true - id: 4.2.2 text: "Ensure that the --authorization-mode argument is not set to AlwaysAllow (Automated)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --authorization-mode path: '{.authorization.mode}' compare: op: nothave value: AlwaysAllow remediation: | If using a Kubelet config file, edit the file to set `authorization.mode` to Webhook. If using executable arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_AUTHZ_ARGS variable. --authorization-mode=Webhook Based on your system, restart the kubelet service. For example, systemctl daemon-reload systemctl restart kubelet.service scored: true - id: 4.2.3 text: "Ensure that the --client-ca-file argument is set as appropriate (Automated)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --client-ca-file path: '{.authentication.x509.clientCAFile}' remediation: | If using a Kubelet config file, edit the file to set `authentication.x509.clientCAFile` to the location of the client CA file. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_AUTHZ_ARGS variable. --client-ca-file= Based on your system, restart the kubelet service. For example, systemctl daemon-reload systemctl restart kubelet.service scored: true - id: 4.2.4 text: "Verify that the --read-only-port argument is set to 0 (Manual)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: bin_op: or test_items: - flag: "--read-only-port" path: '{.readOnlyPort}' compare: op: eq value: 0 - flag: "--read-only-port" path: '{.readOnlyPort}' set: false remediation: | If using a Kubelet config file, edit the file to set `readOnlyPort` to 0. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_SYSTEM_PODS_ARGS variable. --read-only-port=0 Based on your system, restart the kubelet service. For example, systemctl daemon-reload systemctl restart kubelet.service scored: false - id: 4.2.5 text: "Ensure that the --streaming-connection-idle-timeout argument is not set to 0 (Manual)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --streaming-connection-idle-timeout path: '{.streamingConnectionIdleTimeout}' compare: op: noteq value: 0 - flag: --streaming-connection-idle-timeout path: '{.streamingConnectionIdleTimeout}' set: false bin_op: or remediation: | If using a Kubelet config file, edit the file to set `streamingConnectionIdleTimeout` to a value other than 0. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_SYSTEM_PODS_ARGS variable. --streaming-connection-idle-timeout=5m Based on your system, restart the kubelet service. For example, systemctl daemon-reload systemctl restart kubelet.service scored: false - id: 4.2.6 text: "Ensure that the --protect-kernel-defaults argument is set to true (Automated)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --protect-kernel-defaults path: '{.protectKernelDefaults}' compare: op: eq value: true remediation: | If using a Kubelet config file, edit the file to set `protectKernelDefaults` to `true`. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_SYSTEM_PODS_ARGS variable. --protect-kernel-defaults=true Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: true - id: 4.2.7 text: "Ensure that the --make-iptables-util-chains argument is set to true (Automated)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --make-iptables-util-chains path: '{.makeIPTablesUtilChains}' compare: op: eq value: true - flag: --make-iptables-util-chains path: '{.makeIPTablesUtilChains}' set: false bin_op: or remediation: | If using a Kubelet config file, edit the file to set `makeIPTablesUtilChains` to `true`. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and remove the --make-iptables-util-chains argument from the KUBELET_SYSTEM_PODS_ARGS variable. Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: true - id: 4.2.8 text: "Ensure that the --hostname-override argument is not set (Manual)" # This is one of those properties that can only be set as a command line argument. # To check if the property is set as expected, we need to parse the kubelet command # instead reading the Kubelet Configuration file. audit: "/bin/ps -fC $kubeletbin " tests: test_items: - flag: --hostname-override set: false remediation: | Edit the kubelet service file $kubeletsvc on each worker node and remove the --hostname-override argument from the KUBELET_SYSTEM_PODS_ARGS variable. Based on your system, restart the kubelet service. For example, systemctl daemon-reload systemctl restart kubelet.service scored: false - id: 4.2.9 text: "Ensure that the eventRecordQPS argument is set to a level which ensures appropriate event capture (Manual)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --event-qps path: '{.eventRecordQPS}' compare: op: gte value: 0 - flag: --event-qps path: '{.eventRecordQPS}' set: false bin_op: or remediation: | If using a Kubelet config file, edit the file to set `eventRecordQPS` to an appropriate level. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_SYSTEM_PODS_ARGS variable. Based on your system, restart the kubelet service. For example, systemctl daemon-reload systemctl restart kubelet.service scored: false - id: 4.2.10 text: "Ensure that the --tls-cert-file and --tls-private-key-file arguments are set as appropriate (Manual)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --tls-cert-file path: '{.tlsCertFile}' - flag: --tls-private-key-file path: '{.tlsPrivateKeyFile}' remediation: | If using a Kubelet config file, edit the file to set `tlsCertFile` to the location of the certificate file to use to identify this Kubelet, and `tlsPrivateKeyFile` to the location of the corresponding private key file. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameters in KUBELET_CERTIFICATE_ARGS variable. --tls-cert-file= --tls-private-key-file= Based on your system, restart the kubelet service. For example, systemctl daemon-reload systemctl restart kubelet.service scored: false - id: 4.2.11 text: "Ensure that the --rotate-certificates argument is not set to false (Automated)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --rotate-certificates path: '{.rotateCertificates}' compare: op: eq value: true - flag: --rotate-certificates path: '{.rotateCertificates}' set: false bin_op: or remediation: | If using a Kubelet config file, edit the file to add the line `rotateCertificates` to `true` or remove it altogether to use the default value. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and remove --rotate-certificates=false argument from the KUBELET_CERTIFICATE_ARGS variable. Based on your system, restart the kubelet service. For example, systemctl daemon-reload systemctl restart kubelet.service scored: true - id: 4.2.12 text: "Verify that the RotateKubeletServerCertificate argument is set to true (Manual)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: bin_op: or test_items: - flag: RotateKubeletServerCertificate path: '{.featureGates.RotateKubeletServerCertificate}' compare: op: nothave value: false - flag: RotateKubeletServerCertificate path: '{.featureGates.RotateKubeletServerCertificate}' set: false remediation: | Edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_CERTIFICATE_ARGS variable. --feature-gates=RotateKubeletServerCertificate=true Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: false - id: 4.2.13 text: "Ensure that the Kubelet only makes use of Strong Cryptographic Ciphers (Manual)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --tls-cipher-suites path: '{range .tlsCipherSuites[:]}{}{'',''}{end}' compare: op: valid_elements value: 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 remediation: | If using a Kubelet config file, edit the file to set `tlsCipherSuites` to 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 or to a subset of these values. If using executable arguments, edit the kubelet service file $kubeletsvc on each worker node and set the --tls-cipher-suites parameter as follows, or to a subset of these values. --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 Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: false ================================================ FILE: cfg/cis-1.24/policies.yaml ================================================ --- controls: version: "cis-1.24" id: 5 text: "Kubernetes Policies" type: "policies" groups: - id: 5.1 text: "RBAC and Service Accounts" checks: - id: 5.1.1 text: "Ensure that the cluster-admin role is only used where required (Manual)" type: "manual" remediation: | Identify all clusterrolebindings to the cluster-admin role. Check if they are used and if they need this role or if they could use a role with fewer privileges. Where possible, first bind users to a lower privileged role and then remove the clusterrolebinding to the cluster-admin role : kubectl delete clusterrolebinding [name] scored: false - id: 5.1.2 text: "Minimize access to secrets (Manual)" type: "manual" remediation: | Where possible, remove get, list and watch access to Secret objects in the cluster. scored: false - id: 5.1.3 text: "Minimize wildcard use in Roles and ClusterRoles (Manual)" type: "manual" remediation: | Where possible replace any use of wildcards in clusterroles and roles with specific objects or actions. scored: false - id: 5.1.4 text: "Minimize access to create pods (Manual)" type: "manual" remediation: | Where possible, remove create access to pod objects in the cluster. scored: false - id: 5.1.5 text: "Ensure that default service accounts are not actively used. (Manual)" type: "manual" remediation: | Create explicit service accounts wherever a Kubernetes workload requires specific access to the Kubernetes API server. Modify the configuration of each default service account to include this value automountServiceAccountToken: false scored: false - id: 5.1.6 text: "Ensure that Service Account Tokens are only mounted where necessary (Manual)" type: "manual" remediation: | Modify the definition of pods and service accounts which do not need to mount service account tokens to disable it. scored: false - id: 5.1.7 text: "Avoid use of system:masters group (Manual)" type: "manual" remediation: | Remove the system:masters group from all users in the cluster. scored: false - id: 5.1.8 text: "Limit use of the Bind, Impersonate and Escalate permissions in the Kubernetes cluster (Manual)" type: "manual" remediation: | Where possible, remove the impersonate, bind and escalate rights from subjects. scored: false - id: 5.2 text: "Pod Security Standards" checks: - id: 5.2.1 text: "Ensure that the cluster has at least one active policy control mechanism in place (Manual)" type: "manual" remediation: | Ensure that either Pod Security Admission or an external policy control system is in place for every namespace which contains user workloads. scored: false - id: 5.2.2 text: "Minimize the admission of privileged containers (Manual)" type: "manual" remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of privileged containers. scored: false - id: 5.2.3 text: "Minimize the admission of containers wishing to share the host process ID namespace (Automated)" type: "manual" remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of `hostPID` containers. scored: false - id: 5.2.4 text: "Minimize the admission of containers wishing to share the host IPC namespace (Automated)" type: "manual" remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of `hostIPC` containers. scored: false - id: 5.2.5 text: "Minimize the admission of containers wishing to share the host network namespace (Automated)" type: "manual" remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of `hostNetwork` containers. scored: false - id: 5.2.6 text: "Minimize the admission of containers with allowPrivilegeEscalation (Automated)" type: "manual" remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of containers with `.spec.allowPrivilegeEscalation` set to `true`. scored: false - id: 5.2.7 text: "Minimize the admission of root containers (Automated)" type: "manual" remediation: | Create a policy for each namespace in the cluster, ensuring that either `MustRunAsNonRoot` or `MustRunAs` with the range of UIDs not including 0, is set. scored: false - id: 5.2.8 text: "Minimize the admission of containers with the NET_RAW capability (Automated)" type: "manual" remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of containers with the `NET_RAW` capability. scored: false - id: 5.2.9 text: "Minimize the admission of containers with added capabilities (Automated)" type: "manual" remediation: | Ensure that `allowedCapabilities` is not present in policies for the cluster unless it is set to an empty array. scored: false - id: 5.2.10 text: "Minimize the admission of containers with capabilities assigned (Manual)" type: "manual" remediation: | Review the use of capabilites in applications running on your cluster. Where a namespace contains applications which do not require any Linux capabities to operate consider adding a PSP which forbids the admission of containers which do not drop all capabilities. scored: false - id: 5.2.11 text: "Minimize the admission of Windows HostProcess containers (Manual)" type: "manual" remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of containers that have `.securityContext.windowsOptions.hostProcess` set to `true`. scored: false - id: 5.2.12 text: "Minimize the admission of HostPath volumes (Manual)" type: "manual" remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of containers with `hostPath` volumes. scored: false - id: 5.2.13 text: "Minimize the admission of containers which use HostPorts (Manual)" type: "manual" remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of containers which use `hostPort` sections. scored: false - id: 5.3 text: "Network Policies and CNI" checks: - id: 5.3.1 text: "Ensure that the CNI in use supports NetworkPolicies (Manual)" type: "manual" remediation: | If the CNI plugin in use does not support network policies, consideration should be given to making use of a different plugin, or finding an alternate mechanism for restricting traffic in the Kubernetes cluster. scored: false - id: 5.3.2 text: "Ensure that all Namespaces have NetworkPolicies defined (Manual)" type: "manual" remediation: | Follow the documentation and create NetworkPolicy objects as you need them. scored: false - id: 5.4 text: "Secrets Management" checks: - id: 5.4.1 text: "Prefer using Secrets as files over Secrets as environment variables (Manual)" type: "manual" remediation: | If possible, rewrite application code to read Secrets from mounted secret files, rather than from environment variables. scored: false - id: 5.4.2 text: "Consider external secret storage (Manual)" type: "manual" remediation: | Refer to the Secrets management options offered by your cloud provider or a third-party secrets management solution. scored: false - id: 5.5 text: "Extensible Admission Control" checks: - id: 5.5.1 text: "Configure Image Provenance using ImagePolicyWebhook admission controller (Manual)" type: "manual" remediation: | Follow the Kubernetes documentation and setup image provenance. scored: false - id: 5.7 text: "General Policies" checks: - id: 5.7.1 text: "Create administrative boundaries between resources using namespaces (Manual)" type: "manual" remediation: | Follow the documentation and create namespaces for objects in your deployment as you need them. scored: false - id: 5.7.2 text: "Ensure that the seccomp profile is set to docker/default in your Pod definitions (Manual)" type: "manual" remediation: | Use `securityContext` to enable the docker/default seccomp profile in your pod definitions. An example is as below: securityContext: seccompProfile: type: RuntimeDefault scored: false - id: 5.7.3 text: "Apply SecurityContext to your Pods and Containers (Manual)" type: "manual" remediation: | Follow the Kubernetes documentation and apply SecurityContexts to your Pods. For a suggested list of SecurityContexts, you may refer to the CIS Security Benchmark for Docker Containers. scored: false - id: 5.7.4 text: "The default namespace should not be used (Manual)" type: "manual" remediation: | Ensure that namespaces are created to allow for appropriate segregation of Kubernetes resources and that all new resources are created in a specific namespace. scored: false ================================================ FILE: cfg/cis-1.24-microk8s/config.yaml ================================================ --- ## Version-specific settings that override the values in cfg/config.yaml ================================================ FILE: cfg/cis-1.24-microk8s/controlplane.yaml ================================================ --- controls: version: "cis-1.24" id: 3 text: "Control Plane Configuration" type: "controlplane" groups: - id: 3.1 text: "Authentication and Authorization" checks: - id: 3.1.1 text: "Client certificate authentication should not be used for users (Manual)" type: "manual" remediation: | Alternative mechanisms provided by Kubernetes such as the use of OIDC should be implemented in place of client certificates. scored: false - id: 3.2 text: "Logging" checks: - id: 3.2.1 text: "Ensure that a minimal audit policy is created (Manual)" audit: "cat $apiserverconf | grep -v grep" tests: test_items: - flag: "--audit-policy-file" set: true remediation: | Create an audit policy file for your cluster. scored: false - id: 3.2.2 text: "Ensure that the audit policy covers key security concerns (Manual)" type: "manual" remediation: | Review the audit policy provided for the cluster and ensure that it covers at least the following areas, - Access to Secrets managed by the cluster. Care should be taken to only log Metadata for requests to Secrets, ConfigMaps, and TokenReviews, in order to avoid risk of logging sensitive data. - Modification of Pod and Deployment objects. - Use of `pods/exec`, `pods/portforward`, `pods/proxy` and `services/proxy`. For most requests, minimally logging at the Metadata level is recommended (the most basic level of logging). scored: false ================================================ FILE: cfg/cis-1.24-microk8s/etcd.yaml ================================================ --- controls: version: "cis-1.24" id: 2 text: "Etcd Node Configuration" type: "etcd" groups: - id: 2 text: "Etcd Node Configuration" checks: - id: 2.1 text: "Ensure that the --cert-file and --key-file arguments are set as appropriate (Automated)" audit: "/bin/ps -ef | /bin/grep $etcdbin | /bin/grep -v grep" tests: bin_op: and test_items: - flag: "--cert-file" env: "ETCD_CERT_FILE" - flag: "--key-file" env: "ETCD_KEY_FILE" remediation: | Not applicable. MicroK8s used dqlite and the communication to this service is done through a local socket (/var/snap/microk8s/current/var/kubernetes/backend/kine.sock:12379) accessible to users with root permissions. scored: false - id: 2.2 text: "Ensure that the --client-cert-auth argument is set to true (Automated)" audit: "/bin/ps -ef | /bin/grep $etcdbin | /bin/grep -v grep" tests: test_items: - flag: "--client-cert-auth" env: "ETCD_CLIENT_CERT_AUTH" compare: op: eq value: true remediation: | Not applicable. MicroK8s used dqlite and the communication to this service is done through a local socket (/var/snap/microk8s/current/var/kubernetes/backend/kine.sock:12379) accessible to users with root permissions. scored: false - id: 2.3 text: "Ensure that the --auto-tls argument is not set to true (Automated)" audit: "/bin/ps -ef | /bin/grep $etcdbin | /bin/grep -v grep" tests: bin_op: or test_items: - flag: "--auto-tls" env: "ETCD_AUTO_TLS" set: false - flag: "--auto-tls" env: "ETCD_AUTO_TLS" compare: op: eq value: false remediation: | Not applicable. MicroK8s used dqlite and the communication to this service is done through a local socket (/var/snap/microk8s/current/var/kubernetes/backend/kine.sock:12379) accessible to users with root permissions. scored: false - id: 2.4 text: "Ensure that the --peer-cert-file and --peer-key-file arguments are set as appropriate (Automated)" audit: "if test -e /var/snap/microk8s/current/var/kubernetes/backend/cluster.crt && test -e /var/snap/microk8s/current/var/kubernetes/backend/cluster.key; then echo 'certs-found'; fi" tests: test_items: - flag: "certs-found" remediation: | The certificate pair for dqlite and tls peer communication is /var/snap/microk8s/current/var/kubernetes/backend/cluster.crt and /var/snap/microk8s/current/var/kubernetes/backend/cluster.key. scored: true - id: 2.5 text: "Ensure that the --peer-client-cert-auth argument is set to true (Automated)" audit: "/bin/cat $etcdconf | /bin/grep enable-tls || true" tests: bin_op: or test_items: - flag: "--enable-tls" compare: op: eq value: true - flag: "--enable-tls" set: false remediation: | MicroK8s used dqlite and tls peer communication uses is TLS if the --enable-tls is set in /var/snap/microk8s/current/args/k8s-dqlite, set to true by default. scored: true - id: 2.6 text: "Ensure that the --peer-auto-tls argument is not set to true (Automated)" audit: "/bin/ps -ef | /bin/grep $etcdbin | /bin/grep -v grep" tests: bin_op: or test_items: - flag: "--peer-auto-tls" env: "ETCD_PEER_AUTO_TLS" set: false - flag: "--peer-auto-tls" env: "ETCD_PEER_AUTO_TLS" compare: op: eq value: false remediation: | Not applicable. MicroK8s used dqlite and tls peer communication uses the certificates created upon the snap creation. scored: false - id: 2.7 text: "Ensure that a unique Certificate Authority is used for etcd (Manual)" audit: "/bin/ps -ef | /bin/grep $etcdbin | /bin/grep -v grep" tests: test_items: - flag: "--trusted-ca-file" env: "ETCD_TRUSTED_CA_FILE" remediation: | Not applicable. MicroK8s used dqlite and tls peer communication uses the certificates created upon the snap creation. scored: false ================================================ FILE: cfg/cis-1.24-microk8s/master.yaml ================================================ --- controls: version: "cis-1.24" id: 1 text: "Control Plane Security Configuration" type: "master" groups: - id: 1.1 text: "Control Plane Node Configuration Files" checks: - id: 1.1.1 text: "Ensure that the API server pod specification file permissions are set to 644 or more restrictive (Automated)" audit: "/bin/sh -c 'if test -e $apiserverconf; then stat -c permissions=%a $apiserverconf; fi'" tests: test_items: - flag: "permissions" compare: op: bitmask value: "644" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chmod 644 $apiserverconf scored: true - id: 1.1.2 text: "Ensure that the API server pod specification file ownership is set to root:root (Automated)" audit: "/bin/sh -c 'if test -e $apiserverconf; then stat -c %U:%G $apiserverconf; fi'" tests: test_items: - flag: "root:root" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chown root:root $apiserverconf scored: true - id: 1.1.3 text: "Ensure that the controller manager pod specification file permissions are set to 600 or more restrictive (Automated)" audit: "/bin/sh -c 'if test -e $controllermanagerconf; then stat -c permissions=%a $controllermanagerconf; fi'" tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chmod 600 $controllermanagerconf scored: true - id: 1.1.4 text: "Ensure that the controller manager pod specification file ownership is set to root:root (Automated)" audit: "/bin/sh -c 'if test -e $controllermanagerconf; then stat -c %U:%G $controllermanagerconf; fi'" tests: test_items: - flag: "root:root" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chown root:root $controllermanagerconf scored: true - id: 1.1.5 text: "Ensure that the scheduler pod specification file permissions are set to 600 or more restrictive (Automated)" audit: "/bin/sh -c 'if test -e $schedulerconf; then stat -c permissions=%a $schedulerconf; fi'" tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chmod 600 $schedulerconf scored: true - id: 1.1.6 text: "Ensure that the scheduler pod specification file ownership is set to root:root (Automated)" audit: "/bin/sh -c 'if test -e $schedulerconf; then stat -c %U:%G $schedulerconf; fi'" tests: test_items: - flag: "root:root" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chown root:root $schedulerconf scored: true - id: 1.1.7 text: "Ensure that the etcd pod specification file permissions are set to 600 or more restrictive (Automated)" audit: "/bin/sh -c 'if test -e $etcdconf; then stat -c permissions=%a $etcdconf; fi'" use_multiple_values: true tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chmod 600 $etcdconf scored: true - id: 1.1.8 text: "Ensure that the etcd pod specification file ownership is set to root:root (Automated)" audit: "/bin/sh -c 'if test -e $etcdconf; then stat -c %U:%G $etcdconf; fi'" use_multiple_values: true tests: test_items: - flag: "root:root" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chown root:root $etcdconf scored: true - id: 1.1.9 text: "Ensure that the Container Network Interface file permissions are set to 600 or more restrictive (Manual)" audit: | find /var/snap/microk8s/current/args/cni-network/10-calico.conflist -type f 2> /dev/null | xargs --no-run-if-empty stat -c permissions=%a use_multiple_values: true tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chmod 600 scored: false - id: 1.1.10 text: "Ensure that the Container Network Interface file ownership is set to root:root (Manual)" audit: | ps -ef | grep $kubeletbin | grep -- --cni-conf-dir | sed 's%.*cni-conf-dir[= ]\([^ ]*\).*%\1%' | xargs -I{} find {} -mindepth 1 | xargs --no-run-if-empty stat -c %U:%G find /var/snap/microk8s/current/args/cni-network/10-calico.conflist -type f 2> /dev/null | xargs --no-run-if-empty stat -c %U:%G use_multiple_values: true tests: test_items: - flag: "root:root" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chown root:root scored: false # Etcd is not running on MicroK8s master nodes - id: 1.1.11 text: "Ensure that the etcd data directory permissions are set to 700 or more restrictive (Automated)" audit: | DATA_DIR='/var/snap/microk8s/current/var/kubernetes/backend/' if ! test -d "$DATA_DIR"; then DATA_DIR=$etcddatadir; fi stat -c permissions=%a "$DATA_DIR" tests: test_items: - flag: "permissions" compare: op: bitmask value: "700" remediation: | On the etcd server node, get the etcd data directory, passed as an argument --data-dir, from the command 'ps -ef | grep etcd'. Run the below command (based on the etcd data directory found above). For example, chmod 700 /var/snap/microk8s/current/var/kubernetes/backend/ scored: true # Etcd is not running on MicroK8s master nodes - id: 1.1.12 text: "Ensure that the etcd data directory ownership is set to etcd:etcd (Automated)" audit: | DATA_DIR='/var/snap/microk8s/current/var/kubernetes/backend/' if ! test -d "$DATA_DIR"; then DATA_DIR=$etcddatadir; fi stat -c %U:%G "$DATA_DIR" tests: test_items: - flag: "root:root" remediation: | On the etcd server node, get the etcd data directory, passed as an argument --data-dir, from the command 'ps -ef | grep etcd'. Run the below command (based on the etcd data directory found above). For example, chown root:root /var/snap/microk8s/current/var/kubernetes/backend/ scored: true - id: 1.1.13 text: "Ensure that the admin.conf file permissions are set to 600 or more restrictive (Automated)" audit: "/bin/sh -c 'if test -e /var/snap/microk8s/current/credentials/client.config; then stat -c permissions=%a /var/snap/microk8s/current/credentials/client.config; fi'" tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chmod 600 /var/snap/microk8s/current/credentials/client.config scored: true - id: 1.1.14 text: "Ensure that the admin.conf file ownership is set to root:root (Automated)" audit: "/bin/sh -c 'if test -e /var/snap/microk8s/current/credentials/client.config; then stat -c %U:%G /var/snap/microk8s/current/credentials/client.config; fi'" tests: test_items: - flag: "root:root" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chown root:root /var/snap/microk8s/current/credentials/client.config scored: true - id: 1.1.15 text: "Ensure that the scheduler.conf file permissions are set to 600 or more restrictive (Automated)" audit: "/bin/sh -c 'if test -e $schedulerkubeconfig; then stat -c permissions=%a $schedulerkubeconfig; fi'" tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chmod 600 $schedulerkubeconfig scored: true - id: 1.1.16 text: "Ensure that the scheduler.conf file ownership is set to root:root (Automated)" audit: "/bin/sh -c 'if test -e $schedulerkubeconfig; then stat -c %U:%G $schedulerkubeconfig; fi'" tests: test_items: - flag: "root:root" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chown root:root $schedulerkubeconfig scored: true - id: 1.1.17 text: "Ensure that the controller-manager.conf file permissions are set to 600 or more restrictive (Automated)" audit: "/bin/sh -c 'if test -e $controllermanagerkubeconfig; then stat -c permissions=%a $controllermanagerkubeconfig; fi'" tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chmod 600 $controllermanagerkubeconfig scored: true - id: 1.1.18 text: "Ensure that the controller-manager.conf file ownership is set to root:root (Automated)" audit: "/bin/sh -c 'if test -e $controllermanagerkubeconfig; then stat -c %U:%G $controllermanagerkubeconfig; fi'" tests: test_items: - flag: "root:root" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chown root:root $controllermanagerkubeconfig scored: true - id: 1.1.19 text: "Ensure that the Kubernetes PKI directory and file ownership is set to root:root (Automated)" audit: "find /var/snap/microk8s/current/certs/ | xargs stat -c %U:%G" use_multiple_values: true tests: test_items: - flag: "root:root" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chown -R root:root /var/snap/microk8s/current/certs/ scored: true - id: 1.1.20 text: "Ensure that the Kubernetes PKI certificate file permissions are set to 600 or more restrictive (Manual)" audit: "find /var/snap/microk8s/current/certs/ -name '*.crt' | xargs stat -c permissions=%a" use_multiple_values: true tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chmod -R 600 /var/snap/microk8s/current/certs/*.crt scored: false - id: 1.1.21 text: "Ensure that the Kubernetes PKI key file permissions are set to 600 (Manual)" audit: "find /var/snap/microk8s/current/certs/ -name '*.key' | xargs stat -c permissions=%a" use_multiple_values: true tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chmod -R 600 /var/snap/microk8s/current/certs/*.key scored: false - id: 1.2 text: "API Server" checks: - id: 1.2.1 text: "Ensure that the --anonymous-auth argument is set to false (Manual)" audit: "cat $apiserverconf | grep -v grep" tests: bin_op: or test_items: - flag: "--anonymous-auth" compare: op: eq value: false - flag: "--anonymous-auth" set: false remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the below parameter. --anonymous-auth=false scored: false - id: 1.2.2 text: "Ensure that the --token-auth-file parameter is not set (Automated)" audit: "cat $apiserverconf | grep -v grep" tests: test_items: - flag: "--token-auth-file" set: false remediation: | Follow the documentation and configure alternate mechanisms for authentication. Then, edit the API server pod specification file $apiserverconf on the control plane node and remove the --token-auth-file= parameter. scored: true - id: 1.2.3 text: "Ensure that the --DenyServiceExternalIPs is not set (Automated)" audit: "cat $apiserverconf | grep -v grep" tests: bin_op: or test_items: - flag: "--enable-admission-plugins" compare: op: nothave value: "DenyServiceExternalIPs" - flag: "--enable-admission-plugins" set: false remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and remove the `DenyServiceExternalIPs` from enabled admission plugins. scored: true - id: 1.2.4 text: "Ensure that the --kubelet-client-certificate and --kubelet-client-key arguments are set as appropriate (Automated)" audit: "cat $apiserverconf | grep -v grep" tests: bin_op: and test_items: - flag: "--kubelet-client-certificate" - flag: "--kubelet-client-key" remediation: | Follow the Kubernetes documentation and set up the TLS connection between the apiserver and kubelets. Then, edit API server pod specification file $apiserverconf on the control plane node and set the kubelet client certificate and key parameters as below. --kubelet-client-certificate= --kubelet-client-key= scored: true - id: 1.2.5 text: "Ensure that the --kubelet-certificate-authority argument is set as appropriate (Automated)" audit: "cat $apiserverconf | grep -v grep" tests: test_items: - flag: "--kubelet-certificate-authority" remediation: | Follow the Kubernetes documentation and setup the TLS connection between the apiserver and kubelets. Then, edit the API server pod specification file $apiserverconf on the control plane node and set the --kubelet-certificate-authority parameter to the path to the cert file for the certificate authority. --kubelet-certificate-authority= scored: true - id: 1.2.6 text: "Ensure that the --authorization-mode argument is not set to AlwaysAllow (Automated)" audit: "cat $apiserverconf | grep -v grep" tests: test_items: - flag: "--authorization-mode" compare: op: nothave value: "AlwaysAllow" remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the --authorization-mode parameter to values other than AlwaysAllow. One such example could be as below. --authorization-mode=RBAC scored: true - id: 1.2.7 text: "Ensure that the --authorization-mode argument includes Node (Automated)" audit: "cat $apiserverconf | grep -v grep" tests: test_items: - flag: "--authorization-mode" compare: op: has value: "Node" remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the --authorization-mode parameter to a value that includes Node. --authorization-mode=Node,RBAC scored: true - id: 1.2.8 text: "Ensure that the --authorization-mode argument includes RBAC (Automated)" audit: "cat $apiserverconf | grep -v grep" tests: test_items: - flag: "--authorization-mode" compare: op: has value: "RBAC" remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the --authorization-mode parameter to a value that includes RBAC, for example `--authorization-mode=Node,RBAC`. scored: true - id: 1.2.9 text: "Ensure that the admission control plugin EventRateLimit is set (Manual)" audit: "cat $apiserverconf | grep -v grep" tests: test_items: - flag: "--enable-admission-plugins" compare: op: has value: "EventRateLimit" remediation: | Follow the Kubernetes documentation and set the desired limits in a configuration file. Then, edit the API server pod specification file $apiserverconf and set the below parameters. --enable-admission-plugins=...,EventRateLimit,... --admission-control-config-file= scored: false - id: 1.2.10 text: "Ensure that the admission control plugin AlwaysAdmit is not set (Automated)" audit: "cat $apiserverconf | grep -v grep" tests: bin_op: or test_items: - flag: "--enable-admission-plugins" compare: op: nothave value: AlwaysAdmit - flag: "--enable-admission-plugins" set: false remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and either remove the --enable-admission-plugins parameter, or set it to a value that does not include AlwaysAdmit. scored: true - id: 1.2.11 text: "Ensure that the admission control plugin AlwaysPullImages is set (Manual)" audit: "cat $apiserverconf | grep -v grep" tests: test_items: - flag: "--enable-admission-plugins" compare: op: has value: "AlwaysPullImages" remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the --enable-admission-plugins parameter to include AlwaysPullImages. --enable-admission-plugins=...,AlwaysPullImages,... scored: false - id: 1.2.12 text: "Ensure that the admission control plugin SecurityContextDeny is set if PodSecurityPolicy is not used (Manual)" audit: "cat $apiserverconf | grep -v grep" tests: bin_op: or test_items: - flag: "--enable-admission-plugins" compare: op: has value: "SecurityContextDeny" - flag: "--enable-admission-plugins" compare: op: has value: "PodSecurityPolicy" remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the --enable-admission-plugins parameter to include SecurityContextDeny, unless PodSecurityPolicy is already in place. --enable-admission-plugins=...,SecurityContextDeny,... scored: false - id: 1.2.13 text: "Ensure that the admission control plugin ServiceAccount is set (Automated)" audit: "cat $apiserverconf | grep -v grep" tests: bin_op: or test_items: - flag: "--disable-admission-plugins" compare: op: nothave value: "ServiceAccount" - flag: "--disable-admission-plugins" set: false remediation: | Follow the documentation and create ServiceAccount objects as per your environment. Then, edit the API server pod specification file $apiserverconf on the control plane node and ensure that the --disable-admission-plugins parameter is set to a value that does not include ServiceAccount. scored: true - id: 1.2.14 text: "Ensure that the admission control plugin NamespaceLifecycle is set (Automated)" audit: "cat $apiserverconf | grep -v grep" tests: bin_op: or test_items: - flag: "--disable-admission-plugins" compare: op: nothave value: "NamespaceLifecycle" - flag: "--disable-admission-plugins" set: false remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the --disable-admission-plugins parameter to ensure it does not include NamespaceLifecycle. scored: true - id: 1.2.15 text: "Ensure that the admission control plugin NodeRestriction is set (Automated)" audit: "cat $apiserverconf | grep -v grep" tests: test_items: - flag: "--enable-admission-plugins" compare: op: has value: "NodeRestriction" remediation: | Follow the Kubernetes documentation and configure NodeRestriction plug-in on kubelets. Then, edit the API server pod specification file $apiserverconf on the control plane node and set the --enable-admission-plugins parameter to a value that includes NodeRestriction. --enable-admission-plugins=...,NodeRestriction,... scored: true - id: 1.2.16 text: "Ensure that the --secure-port argument is not set to 0 (Automated)" audit: "cat $apiserverconf | grep -v grep" tests: bin_op: or test_items: - flag: "--secure-port" compare: op: gt value: 0 - flag: "--secure-port" set: false remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and either remove the --secure-port parameter or set it to a different (non-zero) desired port. scored: true - id: 1.2.17 text: "Ensure that the --profiling argument is set to false (Automated)" audit: "cat $apiserverconf | grep -v grep" tests: test_items: - flag: "--profiling" compare: op: eq value: false remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the below parameter. --profiling=false scored: true - id: 1.2.18 text: "Ensure that the --audit-log-path argument is set (Automated)" audit: "cat $apiserverconf | grep -v grep" tests: test_items: - flag: "--audit-log-path" remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the --audit-log-path parameter to a suitable path and file where you would like audit logs to be written, for example, --audit-log-path=/var/log/apiserver/audit.log scored: true - id: 1.2.19 text: "Ensure that the --audit-log-maxage argument is set to 30 or as appropriate (Automated)" audit: "cat $apiserverconf | grep -v grep" tests: test_items: - flag: "--audit-log-maxage" compare: op: gte value: 30 remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the --audit-log-maxage parameter to 30 or as an appropriate number of days, for example, --audit-log-maxage=30 scored: true - id: 1.2.20 text: "Ensure that the --audit-log-maxbackup argument is set to 10 or as appropriate (Automated)" audit: "cat $apiserverconf | grep -v grep" tests: test_items: - flag: "--audit-log-maxbackup" compare: op: gte value: 10 remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the --audit-log-maxbackup parameter to 10 or to an appropriate value. For example, --audit-log-maxbackup=10 scored: true - id: 1.2.21 text: "Ensure that the --audit-log-maxsize argument is set to 100 or as appropriate (Automated)" audit: "cat $apiserverconf | grep -v grep" tests: test_items: - flag: "--audit-log-maxsize" compare: op: gte value: 100 remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the --audit-log-maxsize parameter to an appropriate size in MB. For example, to set it as 100 MB, --audit-log-maxsize=100 scored: true - id: 1.2.22 text: "Ensure that the --request-timeout argument is set as appropriate (Manual)" audit: "cat $apiserverconf | grep -v grep" type: manual remediation: | Edit the API server pod specification file $apiserverconf and set the below parameter as appropriate and if needed. For example, --request-timeout=300s scored: false - id: 1.2.23 text: "Ensure that the --service-account-lookup argument is set to true (Automated)" audit: "cat $apiserverconf | grep -v grep" tests: bin_op: or test_items: - flag: "--service-account-lookup" set: false - flag: "--service-account-lookup" compare: op: eq value: true remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the below parameter. --service-account-lookup=true Alternatively, you can delete the --service-account-lookup parameter from this file so that the default takes effect. scored: true - id: 1.2.24 text: "Ensure that the --service-account-key-file argument is set as appropriate (Automated)" audit: "cat $apiserverconf | grep -v grep" tests: test_items: - flag: "--service-account-key-file" remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the --service-account-key-file parameter to the public key file for service accounts. For example, --service-account-key-file= scored: true # MicroK8s does not use etcd. The API server talk to a local dqlite instance - id: 1.2.25 text: "Ensure that the --etcd-certfile and --etcd-keyfile arguments are set as appropriate (Automated)" audit: "cat $apiserverconf | grep -v grep" tests: bin_op: and test_items: - flag: "--etcd-certfile" - flag: "--etcd-keyfile" remediation: | Follow the Kubernetes documentation and set up the TLS connection between the apiserver and etcd. Then, edit the API server pod specification file $apiserverconf on the control plane node and set the etcd certificate and key file parameters. --etcd-certfile= --etcd-keyfile= scored: false - id: 1.2.26 text: "Ensure that the --tls-cert-file and --tls-private-key-file arguments are set as appropriate (Automated)" audit: "cat $apiserverconf | grep -v grep" tests: bin_op: and test_items: - flag: "--tls-cert-file" - flag: "--tls-private-key-file" remediation: | Follow the Kubernetes documentation and set up the TLS connection on the apiserver. Then, edit the API server pod specification file $apiserverconf on the control plane node and set the TLS certificate and private key file parameters. --tls-cert-file= --tls-private-key-file= scored: true - id: 1.2.27 text: "Ensure that the --client-ca-file argument is set as appropriate (Automated)" audit: "cat $apiserverconf | grep -v grep" tests: test_items: - flag: "--client-ca-file" remediation: | Follow the Kubernetes documentation and set up the TLS connection on the apiserver. Then, edit the API server pod specification file $apiserverconf on the control plane node and set the client certificate authority file. --client-ca-file= scored: true - id: 1.2.28 text: "Ensure that the --etcd-cafile argument is set as appropriate (Automated)" audit: "cat $apiserverconf | grep -v grep" tests: test_items: - flag: "--etcd-cafile" remediation: | Follow the Kubernetes documentation and set up the TLS connection between the apiserver and etcd. Then, edit the API server pod specification file $apiserverconf on the control plane node and set the etcd certificate authority file parameter. --etcd-cafile= scored: false - id: 1.2.29 text: "Ensure that the --encryption-provider-config argument is set as appropriate (Manual)" audit: "cat $apiserverconf | grep -v grep" tests: test_items: - flag: "--encryption-provider-config" remediation: | Follow the Kubernetes documentation and configure a EncryptionConfig file. Then, edit the API server pod specification file $apiserverconf on the control plane node and set the --encryption-provider-config parameter to the path of that file. For example, --encryption-provider-config= scored: false - id: 1.2.30 text: "Ensure that encryption providers are appropriately configured (Manual)" audit: | ENCRYPTION_PROVIDER_CONFIG=$(cat $apiserverconf | grep -- --encryption-provider-config | sed 's%.*encryption-provider-config[= ]\([^ ]*\).*%\1%') if test -e $ENCRYPTION_PROVIDER_CONFIG; then grep -A1 'providers:' $ENCRYPTION_PROVIDER_CONFIG | tail -n1 | grep -o "[A-Za-z]*" | sed 's/^/provider=/'; fi tests: test_items: - flag: "provider" compare: op: valid_elements value: "aescbc,kms,secretbox" remediation: | Follow the Kubernetes documentation and configure a EncryptionConfig file. In this file, choose aescbc, kms or secretbox as the encryption provider. scored: false - id: 1.2.31 text: "Ensure that the API Server only makes use of Strong Cryptographic Ciphers (Manual)" audit: "cat $apiserverconf | grep -v grep" tests: test_items: - flag: "--tls-cipher-suites" compare: op: valid_elements value: "TLS_AES_128_GCM_SHA256,TLS_AES_256_GCM_SHA384,TLS_CHACHA20_POLY1305_SHA256,TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA,TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,TLS_RSA_WITH_3DES_EDE_CBC_SHA,TLS_RSA_WITH_AES_128_CBC_SHA,TLS_RSA_WITH_AES_128_GCM_SHA256,TLS_RSA_WITH_AES_256_CBC_SHA,TLS_RSA_WITH_AES_256_GCM_SHA384" remediation: | Edit the API server pod specification file /etc/kubernetes/manifests/kube-apiserver.yaml on the control plane node and set the below parameter. --tls-cipher-suites=TLS_AES_128_GCM_SHA256,TLS_AES_256_GCM_SHA384,TLS_CHACHA20_POLY1305_SHA256, TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA,TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305, TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,TLS_RSA_WITH_3DES_EDE_CBC_SHA,TLS_RSA_WITH_AES_128_CBC_SHA, TLS_RSA_WITH_AES_128_GCM_SHA256,TLS_RSA_WITH_AES_256_CBC_SHA,TLS_RSA_WITH_AES_256_GCM_SHA384 scored: false - id: 1.3 text: "Controller Manager" checks: - id: 1.3.1 text: "Ensure that the --terminated-pod-gc-threshold argument is set as appropriate (Manual)" audit: "cat $controllermanagerconf | grep -v grep" tests: test_items: - flag: "--terminated-pod-gc-threshold" remediation: | Edit the Controller Manager pod specification file $controllermanagerconf on the control plane node and set the --terminated-pod-gc-threshold to an appropriate threshold, for example, --terminated-pod-gc-threshold=10 scored: false - id: 1.3.2 text: "Ensure that the --profiling argument is set to false (Automated)" audit: "cat $controllermanagerconf | grep -v grep" tests: test_items: - flag: "--profiling" compare: op: eq value: false remediation: | Edit the Controller Manager pod specification file $controllermanagerconf on the control plane node and set the below parameter. --profiling=false scored: true - id: 1.3.3 text: "Ensure that the --use-service-account-credentials argument is set to true (Automated)" audit: "cat $controllermanagerconf | grep -v grep" tests: test_items: - flag: "--use-service-account-credentials" compare: op: noteq value: false remediation: | Edit the Controller Manager pod specification file $controllermanagerconf on the control plane node to set the below parameter. --use-service-account-credentials=true scored: true - id: 1.3.4 text: "Ensure that the --service-account-private-key-file argument is set as appropriate (Automated)" audit: "cat $controllermanagerconf | grep -v grep" tests: test_items: - flag: "--service-account-private-key-file" remediation: | Edit the Controller Manager pod specification file $controllermanagerconf on the control plane node and set the --service-account-private-key-file parameter to the private key file for service accounts. --service-account-private-key-file= scored: true - id: 1.3.5 text: "Ensure that the --root-ca-file argument is set as appropriate (Automated)" audit: "cat $controllermanagerconf | grep -v grep" tests: test_items: - flag: "--root-ca-file" remediation: | Edit the Controller Manager pod specification file $controllermanagerconf on the control plane node and set the --root-ca-file parameter to the certificate bundle file`. --root-ca-file= scored: true - id: 1.3.6 text: "Ensure that the RotateKubeletServerCertificate argument is set to true (Automated)" audit: "cat $controllermanagerconf | grep -v grep" tests: bin_op: or test_items: - flag: "--feature-gates" compare: op: nothave value: "RotateKubeletServerCertificate=false" set: true - flag: "--feature-gates" set: false remediation: | Edit the Controller Manager pod specification file $controllermanagerconf on the control plane node and set the --feature-gates parameter to include RotateKubeletServerCertificate=true. --feature-gates=RotateKubeletServerCertificate=true scored: true - id: 1.3.7 text: "Ensure that the --bind-address argument is set to 127.0.0.1 (Automated)" audit: "cat $controllermanagerconf | grep -v grep" tests: bin_op: or test_items: - flag: "--bind-address" compare: op: eq value: "127.0.0.1" - flag: "--bind-address" set: false remediation: | Edit the Controller Manager pod specification file $controllermanagerconf on the control plane node and ensure the correct value for the --bind-address parameter scored: true - id: 1.4 text: "Scheduler" checks: - id: 1.4.1 text: "Ensure that the --profiling argument is set to false (Automated)" audit: "cat $schedulerconf | grep -v grep" tests: test_items: - flag: "--profiling" compare: op: eq value: false remediation: | Edit the Scheduler pod specification file $schedulerconf file on the control plane node and set the below parameter. --profiling=false scored: true - id: 1.4.2 text: "Ensure that the --bind-address argument is set to 127.0.0.1 (Automated)" audit: "cat $schedulerconf | grep -v grep" tests: bin_op: or test_items: - flag: "--bind-address" compare: op: eq value: "127.0.0.1" - flag: "--bind-address" set: false remediation: | Edit the Scheduler pod specification file $schedulerconf on the control plane node and ensure the correct value for the --bind-address parameter scored: true ================================================ FILE: cfg/cis-1.24-microk8s/node.yaml ================================================ --- controls: version: "cis-1.24" id: 4 text: "Worker Node Security Configuration" type: "node" groups: - id: 4.1 text: "Worker Node Configuration Files" checks: - id: 4.1.1 text: "Ensure that the kubelet service file permissions are set to 600 or more restrictive (Automated)" audit: "/bin/sh -c 'if test -e $kubeletsvc; then stat -c permissions=%a $kubeletsvc; fi' " tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Run the below command (based on the file location on your system) on the each worker node. For example, chmod 600 $kubeletsvc scored: true - id: 4.1.2 text: "Ensure that the kubelet service file ownership is set to root:root (Automated)" audit: "/bin/sh -c 'if test -e $kubeletsvc; then stat -c %U:%G $kubeletsvc; fi' " tests: test_items: - flag: root:root remediation: | Run the below command (based on the file location on your system) on the each worker node. For example, chown root:root $kubeletsvc scored: true - id: 4.1.3 text: "If proxy kubeconfig file exists ensure permissions are set to 600 or more restrictive (Manual)" audit: "/bin/sh -c 'if test -e $proxykubeconfig; then stat -c permissions=%a $proxykubeconfig; fi' " tests: bin_op: or test_items: - flag: "permissions" set: true compare: op: bitmask value: "600" remediation: | Run the below command (based on the file location on your system) on the each worker node. For example, chmod 600 $proxykubeconfig scored: false - id: 4.1.4 text: "If proxy kubeconfig file exists ensure ownership is set to root:root (Manual)" audit: "/bin/sh -c 'if test -e $proxykubeconfig; then stat -c %U:%G $proxykubeconfig; fi' " tests: bin_op: or test_items: - flag: root:root remediation: | Run the below command (based on the file location on your system) on the each worker node. For example, chown root:root $proxykubeconfig scored: false - id: 4.1.5 text: "Ensure that the --kubeconfig kubelet.conf file permissions are set to 600 or more restrictive (Automated)" audit: "/bin/sh -c 'if test -e $kubeletkubeconfig; then stat -c permissions=%a $kubeletkubeconfig; fi' " tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Run the below command (based on the file location on your system) on the each worker node. For example, chmod 600 $kubeletkubeconfig scored: true - id: 4.1.6 text: "Ensure that the --kubeconfig kubelet.conf file ownership is set to root:root (Automated)" audit: "/bin/sh -c 'if test -e $kubeletkubeconfig; then stat -c %U:%G $kubeletkubeconfig; fi' " tests: test_items: - flag: root:root remediation: | Run the below command (based on the file location on your system) on the each worker node. For example, chown root:root $kubeletkubeconfig scored: true - id: 4.1.7 text: "Ensure that the certificate authorities file permissions are set to 600 or more restrictive (Manual)" audit: | CAFILE=$(ps -ef | grep kubelet | grep -v apiserver | grep -- --client-ca-file= | awk -F '--client-ca-file=' '{print $2}' | awk '{print $1}') if test -z $CAFILE; then CAFILE=$kubeletcafile; fi if test -e $CAFILE; then stat -c permissions=%a $CAFILE; fi tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Run the following command to modify the file permissions of the --client-ca-file chmod 600 scored: false - id: 4.1.8 text: "Ensure that the client certificate authorities file ownership is set to root:root (Manual)" audit: | CAFILE=$(ps -ef | grep kubelet | grep -v apiserver | grep -- --client-ca-file= | awk -F '--client-ca-file=' '{print $2}' | awk '{print $1}') if test -z $CAFILE; then CAFILE=$kubeletcafile; fi if test -e $CAFILE; then stat -c %U:%G $CAFILE; fi tests: test_items: - flag: root:root compare: op: eq value: root:root remediation: | Run the following command to modify the ownership of the --client-ca-file. chown root:root scored: false - id: 4.1.9 text: "If the kubelet config.yaml configuration file is being used validate permissions set to 600 or more restrictive (Manual)" audit: "/bin/sh -c 'if test -e $kubeletconf; then stat -c permissions=%a $kubeletconf; fi' " tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Run the following command (using the config file location identified in the Audit step) chmod 600 $kubeletconf scored: false - id: 4.1.10 text: "If the kubelet config.yaml configuration file is being used validate file ownership is set to root:root (Manual)" audit: "/bin/sh -c 'if test -e $kubeletconf; then stat -c %U:%G $kubeletconf; fi' " tests: test_items: - flag: root:root remediation: | Run the following command (using the config file location identified in the Audit step) chown root:root $kubeletconf scored: false - id: 4.2 text: "Kubelet" checks: - id: 4.2.1 text: "Ensure that the --anonymous-auth argument is set to false (Automated)" audit: "cat $kubeletconf" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: "--anonymous-auth" path: "{.authentication.anonymous.enabled}" compare: op: eq value: false remediation: | If using a Kubelet config file, edit the file to set `authentication: anonymous: enabled` to `false`. If using executable arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_SYSTEM_PODS_ARGS variable. `--anonymous-auth=false` Based on your system, restart the kubelet service. For example, systemctl daemon-reload systemctl restart kubelet.service scored: true - id: 4.2.2 text: "Ensure that the --authorization-mode argument is not set to AlwaysAllow (Automated)" audit: "cat $kubeletconf" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --authorization-mode path: "{.authorization.mode}" compare: op: nothave value: AlwaysAllow remediation: | If using a Kubelet config file, edit the file to set `authorization.mode` to Webhook. If using executable arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_AUTHZ_ARGS variable. --authorization-mode=Webhook Based on your system, restart the kubelet service. For example, systemctl daemon-reload systemctl restart kubelet.service scored: true - id: 4.2.3 text: "Ensure that the --client-ca-file argument is set as appropriate (Automated)" audit: "cat $kubeletconf" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --client-ca-file path: "{.authentication.x509.clientCAFile}" remediation: | If using a Kubelet config file, edit the file to set `authentication.x509.clientCAFile` to the location of the client CA file. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_AUTHZ_ARGS variable. --client-ca-file= Based on your system, restart the kubelet service. For example, systemctl daemon-reload systemctl restart kubelet.service scored: true - id: 4.2.4 text: "Verify that the --read-only-port argument is set to 0 (Manual)" audit: "cat $kubeletconf" audit_config: "/bin/cat $kubeletconf" tests: bin_op: or test_items: - flag: "--read-only-port" path: "{.readOnlyPort}" compare: op: eq value: 0 - flag: "--read-only-port" path: "{.readOnlyPort}" set: false remediation: | If using a Kubelet config file, edit the file to set `readOnlyPort` to 0. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_SYSTEM_PODS_ARGS variable. --read-only-port=0 Based on your system, restart the kubelet service. For example, systemctl daemon-reload systemctl restart kubelet.service scored: false - id: 4.2.5 text: "Ensure that the --streaming-connection-idle-timeout argument is not set to 0 (Manual)" audit: "cat $kubeletconf" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --streaming-connection-idle-timeout path: "{.streamingConnectionIdleTimeout}" compare: op: noteq value: 0 - flag: --streaming-connection-idle-timeout path: "{.streamingConnectionIdleTimeout}" set: false bin_op: or remediation: | If using a Kubelet config file, edit the file to set `streamingConnectionIdleTimeout` to a value other than 0. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_SYSTEM_PODS_ARGS variable. --streaming-connection-idle-timeout=5m Based on your system, restart the kubelet service. For example, systemctl daemon-reload systemctl restart kubelet.service scored: false - id: 4.2.6 text: "Ensure that the --protect-kernel-defaults argument is set to true (Automated)" audit: "cat $kubeletconf" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --protect-kernel-defaults path: "{.protectKernelDefaults}" compare: op: eq value: true remediation: | If using a Kubelet config file, edit the file to set `protectKernelDefaults` to `true`. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_SYSTEM_PODS_ARGS variable. --protect-kernel-defaults=true Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: true - id: 4.2.7 text: "Ensure that the --make-iptables-util-chains argument is set to true (Automated)" audit: "cat $kubeletconf" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --make-iptables-util-chains path: "{.makeIPTablesUtilChains}" compare: op: eq value: true - flag: --make-iptables-util-chains path: "{.makeIPTablesUtilChains}" set: false bin_op: or remediation: | If using a Kubelet config file, edit the file to set `makeIPTablesUtilChains` to `true`. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and remove the --make-iptables-util-chains argument from the KUBELET_SYSTEM_PODS_ARGS variable. Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: true - id: 4.2.8 text: "Ensure that the --hostname-override argument is not set (Manual)" # This is one of those properties that can only be set as a command line argument. # To check if the property is set as expected, we need to parse the kubelet command # instead reading the Kubelet Configuration file. audit: "cat $kubeletconf" tests: test_items: - flag: --hostname-override set: false remediation: | Edit the kubelet service file $kubeletsvc on each worker node and remove the --hostname-override argument from the KUBELET_SYSTEM_PODS_ARGS variable. Based on your system, restart the kubelet service. For example, systemctl daemon-reload systemctl restart kubelet.service scored: false - id: 4.2.9 text: "Ensure that the eventRecordQPS argument is set to a level which ensures appropriate event capture (Manual)" audit: "cat $kubeletconf" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --event-qps path: "{.eventRecordQPS}" compare: op: eq value: 0 remediation: | If using a Kubelet config file, edit the file to set `eventRecordQPS` to an appropriate level. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_SYSTEM_PODS_ARGS variable. Based on your system, restart the kubelet service. For example, systemctl daemon-reload systemctl restart kubelet.service scored: false - id: 4.2.10 text: "Ensure that the --tls-cert-file and --tls-private-key-file arguments are set as appropriate (Manual)" audit: "cat $kubeletconf" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --tls-cert-file path: "{.tlsCertFile}" - flag: --tls-private-key-file path: "{.tlsPrivateKeyFile}" remediation: | If using a Kubelet config file, edit the file to set `tlsCertFile` to the location of the certificate file to use to identify this Kubelet, and `tlsPrivateKeyFile` to the location of the corresponding private key file. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameters in KUBELET_CERTIFICATE_ARGS variable. --tls-cert-file= --tls-private-key-file= Based on your system, restart the kubelet service. For example, systemctl daemon-reload systemctl restart kubelet.service scored: false - id: 4.2.11 text: "Ensure that the --rotate-certificates argument is not set to false (Automated)" audit: "cat $kubeletconf" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --rotate-certificates path: "{.rotateCertificates}" compare: op: eq value: true - flag: --rotate-certificates path: "{.rotateCertificates}" set: false bin_op: or remediation: | If using a Kubelet config file, edit the file to add the line `rotateCertificates` to `true` or remove it altogether to use the default value. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and remove --rotate-certificates=false argument from the KUBELET_CERTIFICATE_ARGS variable. Based on your system, restart the kubelet service. For example, systemctl daemon-reload systemctl restart kubelet.service scored: true - id: 4.2.12 text: "Verify that the RotateKubeletServerCertificate argument is set to true (Manual)" audit: "cat $kubeletconf" audit_config: "/bin/cat $kubeletconf" tests: bin_op: or test_items: - flag: RotateKubeletServerCertificate path: "{.featureGates.RotateKubeletServerCertificate}" compare: op: nothave value: false - flag: RotateKubeletServerCertificate path: "{.featureGates.RotateKubeletServerCertificate}" set: false remediation: | Edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_CERTIFICATE_ARGS variable. --feature-gates=RotateKubeletServerCertificate=true Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: false - id: 4.2.13 text: "Ensure that the Kubelet only makes use of Strong Cryptographic Ciphers (Manual)" audit: "cat $kubeletconf" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --tls-cipher-suites path: "{range .tlsCipherSuites[:]}{}{','}{end}" compare: op: valid_elements value: 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 remediation: | If using a Kubelet config file, edit the file to set `tlsCipherSuites` to 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 or to a subset of these values. If using executable arguments, edit the kubelet service file $kubeletsvc on each worker node and set the --tls-cipher-suites parameter as follows, or to a subset of these values. --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 Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: false ================================================ FILE: cfg/cis-1.24-microk8s/policies.yaml ================================================ --- controls: version: "cis-1.24" id: 5 text: "Kubernetes Policies" type: "policies" groups: - id: 5.1 text: "RBAC and Service Accounts" checks: - id: 5.1.1 text: "Ensure that the cluster-admin role is only used where required (Manual)" type: "manual" remediation: | Identify all clusterrolebindings to the cluster-admin role. Check if they are used and if they need this role or if they could use a role with fewer privileges. Where possible, first bind users to a lower privileged role and then remove the clusterrolebinding to the cluster-admin role : kubectl delete clusterrolebinding [name] scored: false - id: 5.1.2 text: "Minimize access to secrets (Manual)" type: "manual" remediation: | Where possible, remove get, list and watch access to Secret objects in the cluster. scored: false - id: 5.1.3 text: "Minimize wildcard use in Roles and ClusterRoles (Manual)" type: "manual" remediation: | Where possible replace any use of wildcards in clusterroles and roles with specific objects or actions. scored: false - id: 5.1.4 text: "Minimize access to create pods (Manual)" type: "manual" remediation: | Where possible, remove create access to pod objects in the cluster. scored: false - id: 5.1.5 text: "Ensure that default service accounts are not actively used. (Manual)" type: "manual" remediation: | Create explicit service accounts wherever a Kubernetes workload requires specific access to the Kubernetes API server. Modify the configuration of each default service account to include this value automountServiceAccountToken: false scored: false - id: 5.1.6 text: "Ensure that Service Account Tokens are only mounted where necessary (Manual)" type: "manual" remediation: | Modify the definition of pods and service accounts which do not need to mount service account tokens to disable it. scored: false - id: 5.1.7 text: "Avoid use of system:masters group (Manual)" type: "manual" remediation: | Remove the system:masters group from all users in the cluster. scored: false - id: 5.1.8 text: "Limit use of the Bind, Impersonate and Escalate permissions in the Kubernetes cluster (Manual)" type: "manual" remediation: | Where possible, remove the impersonate, bind and escalate rights from subjects. scored: false - id: 5.2 text: "Pod Security Standards" checks: - id: 5.2.1 text: "Ensure that the cluster has at least one active policy control mechanism in place (Manual)" type: "manual" remediation: | Ensure that either Pod Security Admission or an external policy control system is in place for every namespace which contains user workloads. scored: false - id: 5.2.2 text: "Minimize the admission of privileged containers (Manual)" type: "manual" remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of privileged containers. scored: false - id: 5.2.3 text: "Minimize the admission of containers wishing to share the host process ID namespace (Automated)" type: "manual" remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of `hostPID` containers. scored: false - id: 5.2.4 text: "Minimize the admission of containers wishing to share the host IPC namespace (Automated)" type: "manual" remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of `hostIPC` containers. scored: false - id: 5.2.5 text: "Minimize the admission of containers wishing to share the host network namespace (Automated)" type: "manual" remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of `hostNetwork` containers. scored: false - id: 5.2.6 text: "Minimize the admission of containers with allowPrivilegeEscalation (Automated)" type: "manual" remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of containers with `.spec.allowPrivilegeEscalation` set to `true`. scored: false - id: 5.2.7 text: "Minimize the admission of root containers (Automated)" type: "manual" remediation: | Create a policy for each namespace in the cluster, ensuring that either `MustRunAsNonRoot` or `MustRunAs` with the range of UIDs not including 0, is set. scored: false - id: 5.2.8 text: "Minimize the admission of containers with the NET_RAW capability (Automated)" type: "manual" remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of containers with the `NET_RAW` capability. scored: false - id: 5.2.9 text: "Minimize the admission of containers with added capabilities (Automated)" type: "manual" remediation: | Ensure that `allowedCapabilities` is not present in policies for the cluster unless it is set to an empty array. scored: false - id: 5.2.10 text: "Minimize the admission of containers with capabilities assigned (Manual)" type: "manual" remediation: | Review the use of capabilities in applications running on your cluster. Where a namespace contains applications which do not require any Linux capabities to operate consider adding a PSP which forbids the admission of containers which do not drop all capabilities. scored: false - id: 5.2.11 text: "Minimize the admission of Windows HostProcess containers (Manual)" type: "manual" remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of containers that have `.securityContext.windowsOptions.hostProcess` set to `true`. scored: false - id: 5.2.12 text: "Minimize the admission of HostPath volumes (Manual)" type: "manual" remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of containers with `hostPath` volumes. scored: false - id: 5.2.13 text: "Minimize the admission of containers which use HostPorts (Manual)" type: "manual" remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of containers which use `hostPort` sections. scored: false - id: 5.3 text: "Network Policies and CNI" checks: - id: 5.3.1 text: "Ensure that the CNI in use supports NetworkPolicies (Manual)" type: "manual" remediation: | If the CNI plugin in use does not support network policies, consideration should be given to making use of a different plugin, or finding an alternate mechanism for restricting traffic in the Kubernetes cluster. scored: false - id: 5.3.2 text: "Ensure that all Namespaces have NetworkPolicies defined (Manual)" type: "manual" remediation: | Follow the documentation and create NetworkPolicy objects as you need them. scored: false - id: 5.4 text: "Secrets Management" checks: - id: 5.4.1 text: "Prefer using Secrets as files over Secrets as environment variables (Manual)" type: "manual" remediation: | If possible, rewrite application code to read Secrets from mounted secret files, rather than from environment variables. scored: false - id: 5.4.2 text: "Consider external secret storage (Manual)" type: "manual" remediation: | Refer to the Secrets management options offered by your cloud provider or a third-party secrets management solution. scored: false - id: 5.5 text: "Extensible Admission Control" checks: - id: 5.5.1 text: "Configure Image Provenance using ImagePolicyWebhook admission controller (Manual)" type: "manual" remediation: | Follow the Kubernetes documentation and setup image provenance. scored: false - id: 5.7 text: "General Policies" checks: - id: 5.7.1 text: "Create administrative boundaries between resources using namespaces (Manual)" type: "manual" remediation: | Follow the documentation and create namespaces for objects in your deployment as you need them. scored: false - id: 5.7.2 text: "Ensure that the seccomp profile is set to docker/default in your Pod definitions (Manual)" type: "manual" remediation: | Use `securityContext` to enable the docker/default seccomp profile in your pod definitions. An example is as below: securityContext: seccompProfile: type: RuntimeDefault scored: false - id: 5.7.3 text: "Apply SecurityContext to your Pods and Containers (Manual)" type: "manual" remediation: | Follow the Kubernetes documentation and apply SecurityContexts to your Pods. For a suggested list of SecurityContexts, you may refer to the CIS Security Benchmark for Docker Containers. scored: false - id: 5.7.4 text: "The default namespace should not be used (Manual)" type: "manual" remediation: | Ensure that namespaces are created to allow for appropriate segregation of Kubernetes resources and that all new resources are created in a specific namespace. scored: false ================================================ FILE: cfg/cis-1.5/config.yaml ================================================ --- ## Version-specific settings that override the values in cfg/config.yaml ================================================ FILE: cfg/cis-1.5/controlplane.yaml ================================================ --- controls: version: "cis-1.5" id: 3 text: "Control Plane Configuration" type: "controlplane" groups: - id: 3.1 text: "Authentication and Authorization" checks: - id: 3.1.1 text: "Client certificate authentication should not be used for users (Not Scored)" type: "manual" remediation: | Alternative mechanisms provided by Kubernetes such as the use of OIDC should be implemented in place of client certificates. scored: false - id: 3.2 text: "Logging" checks: - id: 3.2.1 text: "Ensure that a minimal audit policy is created (Scored)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--audit-policy-file" set: true remediation: | Create an audit policy file for your cluster. scored: true - id: 3.2.2 text: "Ensure that the audit policy covers key security concerns (Not Scored)" type: "manual" remediation: | Consider modification of the audit policy in use on the cluster to include these items, at a minimum. scored: false ================================================ FILE: cfg/cis-1.5/etcd.yaml ================================================ --- controls: version: "cis-1.5" id: 2 text: "Etcd Node Configuration" type: "etcd" groups: - id: 2 text: "Etcd Node Configuration Files" checks: - id: 2.1 text: "Ensure that the --cert-file and --key-file arguments are set as appropriate (Scored)" audit: "/bin/ps -ef | /bin/grep $etcdbin | /bin/grep -v grep" tests: bin_op: and test_items: - flag: "--cert-file" set: true - flag: "--key-file" set: true remediation: | Follow the etcd service documentation and configure TLS encryption. Then, edit the etcd pod specification file /etc/kubernetes/manifests/etcd.yaml on the master node and set the below parameters. --cert-file= --key-file= scored: true - id: 2.2 text: "Ensure that the --client-cert-auth argument is set to true (Scored)" audit: "/bin/ps -ef | /bin/grep $etcdbin | /bin/grep -v grep" tests: test_items: - flag: "--client-cert-auth" compare: op: eq value: true set: true remediation: | Edit the etcd pod specification file $etcdconf on the master node and set the below parameter. --client-cert-auth="true" scored: true - id: 2.3 text: "Ensure that the --auto-tls argument is not set to true (Scored)" audit: "/bin/ps -ef | /bin/grep $etcdbin | /bin/grep -v grep" tests: bin_op: or test_items: - flag: "--auto-tls" set: false - flag: "--auto-tls" compare: op: eq value: false remediation: | Edit the etcd pod specification file $etcdconf on the master node and either remove the --auto-tls parameter or set it to false. --auto-tls=false scored: true - id: 2.4 text: "Ensure that the --peer-cert-file and --peer-key-file arguments are set as appropriate (Scored)" audit: "/bin/ps -ef | /bin/grep $etcdbin | /bin/grep -v grep" tests: bin_op: and test_items: - flag: "--peer-cert-file" set: true - flag: "--peer-key-file" set: true remediation: | Follow the etcd service documentation and configure peer TLS encryption as appropriate for your etcd cluster. Then, edit the etcd pod specification file $etcdconf on the master node and set the below parameters. --peer-client-file= --peer-key-file= scored: true - id: 2.5 text: "Ensure that the --peer-client-cert-auth argument is set to true (Scored)" audit: "/bin/ps -ef | /bin/grep $etcdbin | /bin/grep -v grep" tests: test_items: - flag: "--peer-client-cert-auth" compare: op: eq value: true set: true remediation: | Edit the etcd pod specification file $etcdconf on the master node and set the below parameter. --peer-client-cert-auth=true scored: true - id: 2.6 text: "Ensure that the --peer-auto-tls argument is not set to true (Scored)" audit: "/bin/ps -ef | /bin/grep $etcdbin | /bin/grep -v grep" tests: bin_op: or test_items: - flag: "--peer-auto-tls" set: false - flag: "--peer-auto-tls" compare: op: eq value: false set: true remediation: | Edit the etcd pod specification file $etcdconf on the master node and either remove the --peer-auto-tls parameter or set it to false. --peer-auto-tls=false scored: true - id: 2.7 text: "Ensure that a unique Certificate Authority is used for etcd (Not Scored)" audit: "/bin/ps -ef | /bin/grep $etcdbin | /bin/grep -v grep" tests: test_items: - flag: "--trusted-ca-file" set: true remediation: | [Manual test] Follow the etcd documentation and create a dedicated certificate authority setup for the etcd service. Then, edit the etcd pod specification file $etcdconf on the master node and set the below parameter. --trusted-ca-file= scored: false ================================================ FILE: cfg/cis-1.5/master.yaml ================================================ --- controls: version: "cis-1.5" id: 1 text: "Master Node Security Configuration" type: "master" groups: - id: 1.1 text: "Master Node Configuration Files" checks: - id: 1.1.1 text: "Ensure that the API server pod specification file permissions are set to 644 or more restrictive (Scored)" audit: "/bin/sh -c 'if test -e $apiserverconf; then stat -c permissions=%a $apiserverconf; fi'" tests: test_items: - flag: "permissions" compare: op: bitmask value: "644" set: true remediation: | Run the below command (based on the file location on your system) on the master node. For example, chmod 644 $apiserverconf scored: true - id: 1.1.2 text: "Ensure that the API server pod specification file ownership is set to root:root (Scored)" audit: "/bin/sh -c 'if test -e $apiserverconf; then stat -c %U:%G $apiserverconf; fi'" tests: test_items: - flag: "root:root" compare: op: eq value: "root:root" set: true remediation: | Run the below command (based on the file location on your system) on the master node. For example, chown root:root $apiserverconf scored: true - id: 1.1.3 text: "Ensure that the controller manager pod specification file permissions are set to 644 or more restrictive (Scored)" audit: "/bin/sh -c 'if test -e $controllermanagerconf; then stat -c permissions=%a $controllermanagerconf; fi'" tests: test_items: - flag: "permissions" compare: op: bitmask value: "644" set: true remediation: | Run the below command (based on the file location on your system) on the master node. For example, chmod 644 $controllermanagerconf scored: true - id: 1.1.4 text: "Ensure that the controller manager pod specification file ownership is set to root:root (Scored)" audit: "/bin/sh -c 'if test -e $controllermanagerconf; then stat -c %U:%G $controllermanagerconf; fi'" tests: test_items: - flag: "root:root" compare: op: eq value: "root:root" set: true remediation: | Run the below command (based on the file location on your system) on the master node. For example, chown root:root $controllermanagerconf scored: true - id: 1.1.5 text: "Ensure that the scheduler pod specification file permissions are set to 644 or more restrictive (Scored)" audit: "/bin/sh -c 'if test -e $schedulerconf; then stat -c permissions=%a $schedulerconf; fi'" tests: test_items: - flag: "permissions" compare: op: bitmask value: "644" set: true remediation: | Run the below command (based on the file location on your system) on the master node. For example, chmod 644 $schedulerconf scored: true - id: 1.1.6 text: "Ensure that the scheduler pod specification file ownership is set to root:root (Scored)" audit: "/bin/sh -c 'if test -e $schedulerconf; then stat -c %U:%G $schedulerconf; fi'" tests: test_items: - flag: "root:root" compare: op: eq value: "root:root" set: true remediation: | Run the below command (based on the file location on your system) on the master node. For example, chown root:root $schedulerconf scored: true - id: 1.1.7 text: "Ensure that the etcd pod specification file permissions are set to 644 or more restrictive (Scored)" audit: "/bin/sh -c 'if test -e $etcdconf; then stat -c permissions=%a $etcdconf; fi'" tests: test_items: - flag: "permissions" compare: op: bitmask value: "644" set: true remediation: | Run the below command (based on the file location on your system) on the master node. For example, chmod 644 $etcdconf scored: true - id: 1.1.8 text: "Ensure that the etcd pod specification file ownership is set to root:root (Scored)" audit: "/bin/sh -c 'if test -e $etcdconf; then stat -c %U:%G $etcdconf; fi'" tests: test_items: - flag: "root:root" compare: op: eq value: "root:root" set: true remediation: | Run the below command (based on the file location on your system) on the master node. For example, chown root:root $etcdconf scored: true - id: 1.1.9 text: "Ensure that the Container Network Interface file permissions are set to 644 or more restrictive (Not Scored)" audit: "stat -c permissions=%a " type: "manual" remediation: | Run the below command (based on the file location on your system) on the master node. For example, chmod 644 scored: false - id: 1.1.10 text: "Ensure that the Container Network Interface file ownership is set to root:root (Not Scored)" audit: "stat -c %U:%G " type: "manual" remediation: | Run the below command (based on the file location on your system) on the master node. For example, chown root:root scored: false - id: 1.1.11 text: "Ensure that the etcd data directory permissions are set to 700 or more restrictive (Scored)" audit: | DATA_DIR='' for d in $(ps -ef | grep $etcdbin | grep -- --data-dir | sed 's%.*data-dir[= ]\([^ ]*\).*%\1%'); do if test -d "$d"; then DATA_DIR="$d"; fi done if ! test -d "$DATA_DIR"; then DATA_DIR=$etcddatadir; fi stat -c permissions=%a "$DATA_DIR" tests: test_items: - flag: "permissions" compare: op: bitmask value: "700" set: true remediation: | On the etcd server node, get the etcd data directory, passed as an argument --data-dir, from the below command: ps -ef | grep etcd Run the below command (based on the etcd data directory found above). For example, chmod 700 /var/lib/etcd scored: true - id: 1.1.12 text: "Ensure that the etcd data directory ownership is set to etcd:etcd (Scored)" audit: | DATA_DIR='' for d in $(ps -ef | grep $etcdbin | grep -- --data-dir | sed 's%.*data-dir[= ]\([^ ]*\).*%\1%'); do if test -d "$d"; then DATA_DIR="$d"; fi done if ! test -d "$DATA_DIR"; then DATA_DIR=$etcddatadir; fi stat -c %U:%G $DATA_DIR tests: test_items: - flag: "etcd:etcd" set: true remediation: | On the etcd server node, get the etcd data directory, passed as an argument --data-dir, from the below command: ps -ef | grep etcd Run the below command (based on the etcd data directory found above). For example, chown etcd:etcd /var/lib/etcd scored: true - id: 1.1.13 text: "Ensure that the admin.conf file permissions are set to 644 or more restrictive (Scored)" audit: "/bin/sh -c 'if test -e /etc/kubernetes/admin.conf; then stat -c permissions=%a /etc/kubernetes/admin.conf; fi'" tests: test_items: - flag: "permissions" compare: op: bitmask value: "644" set: true remediation: | Run the below command (based on the file location on your system) on the master node. For example, chmod 644 /etc/kubernetes/admin.conf scored: true - id: 1.1.14 text: "Ensure that the admin.conf file ownership is set to root:root (Scored)" audit: "/bin/sh -c 'if test -e /etc/kubernetes/admin.conf; then stat -c %U:%G /etc/kubernetes/admin.conf; fi'" tests: test_items: - flag: "root:root" compare: op: eq value: "root:root" set: true remediation: | Run the below command (based on the file location on your system) on the master node. For example, chown root:root /etc/kubernetes/admin.conf scored: true - id: 1.1.15 text: "Ensure that the scheduler.conf file permissions are set to 644 or more restrictive (Scored)" audit: "/bin/sh -c 'if test -e $schedulerkubeconfig; then stat -c permissions=%a $schedulerkubeconfig; fi'" tests: test_items: - flag: "permissions" compare: op: bitmask value: "644" set: true remediation: | Run the below command (based on the file location on your system) on the master node. For example, chmod 644 $schedulerkubeconfig scored: true - id: 1.1.16 text: "Ensure that the scheduler.conf file ownership is set to root:root (Scored)" audit: "/bin/sh -c 'if test -e $schedulerkubeconfig; then stat -c %U:%G $schedulerkubeconfig; fi'" tests: test_items: - flag: "root:root" compare: op: eq value: "root:root" set: true remediation: | Run the below command (based on the file location on your system) on the master node. For example, chown root:root $schedulerkubeconfig scored: true - id: 1.1.17 text: "Ensure that the controller-manager.conf file permissions are set to 644 or more restrictive (Scored)" audit: "/bin/sh -c 'if test -e $controllermanagerkubeconfig; then stat -c permissions=%a $controllermanagerkubeconfig; fi'" tests: test_items: - flag: "permissions" compare: op: bitmask value: "644" set: true remediation: | Run the below command (based on the file location on your system) on the master node. For example, chmod 644 $controllermanagerkubeconfig scored: true - id: 1.1.18 text: "Ensure that the controller-manager.conf file ownership is set to root:root (Scored)" audit: "/bin/sh -c 'if test -e $controllermanagerkubeconfig; then stat -c %U:%G $controllermanagerkubeconfig; fi'" tests: test_items: - flag: "root:root" compare: op: eq value: "root:root" set: true remediation: | Run the below command (based on the file location on your system) on the master node. For example, chown root:root $controllermanagerkubeconfig scored: true - id: 1.1.19 text: "Ensure that the Kubernetes PKI directory and file ownership is set to root:root (Scored)" audit: "find /etc/kubernetes/pki/ | xargs stat -c %U:%G" use_multiple_values: true tests: test_items: - flag: "root:root" compare: op: eq value: "root:root" set: true remediation: | Run the below command (based on the file location on your system) on the master node. For example, chown -R root:root /etc/kubernetes/pki/ scored: true - id: 1.1.20 text: "Ensure that the Kubernetes PKI certificate file permissions are set to 644 or more restrictive (Not Scored)" audit: "find /etc/kubernetes/pki/ -name '*.crt' | xargs stat -c permissions=%a" use_multiple_values: true tests: test_items: - flag: "permissions" compare: op: bitmask value: "644" set: true remediation: | Run the below command (based on the file location on your system) on the master node. For example, chmod -R 644 /etc/kubernetes/pki/*.crt scored: false - id: 1.1.21 text: "Ensure that the Kubernetes PKI key file permissions are set to 600 (Not Scored)" audit: "find /etc/kubernetes/pki/ -name '*.key' | xargs stat -c permissions=%a" use_multiple_values: true tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" set: true remediation: | Run the below command (based on the file location on your system) on the master node. For example, chmod -R 600 /etc/kubernetes/pki/*.key scored: false - id: 1.2 text: "API Server" checks: - id: 1.2.1 text: "Ensure that the --anonymous-auth argument is set to false (Not Scored)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--anonymous-auth" compare: op: eq value: false set: true remediation: | Edit the API server pod specification file $apiserverconf on the master node and set the below parameter. --anonymous-auth=false scored: false - id: 1.2.2 text: "Ensure that the --basic-auth-file argument is not set (Scored)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--basic-auth-file" set: false remediation: | Follow the documentation and configure alternate mechanisms for authentication. Then, edit the API server pod specification file $apiserverconf on the master node and remove the --basic-auth-file= parameter. scored: true - id: 1.2.3 text: "Ensure that the --token-auth-file parameter is not set (Scored)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--token-auth-file" set: false remediation: | Follow the documentation and configure alternate mechanisms for authentication. Then, edit the API server pod specification file $apiserverconf on the master node and remove the --token-auth-file= parameter. scored: true - id: 1.2.4 text: "Ensure that the --kubelet-https argument is set to true (Scored)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: bin_op: or test_items: - flag: "--kubelet-https" compare: op: eq value: true set: true - flag: "--kubelet-https" set: false remediation: | Edit the API server pod specification file $apiserverconf on the master node and remove the --kubelet-https parameter. scored: true - id: 1.2.5 text: "Ensure that the --kubelet-client-certificate and --kubelet-client-key arguments are set as appropriate (Scored)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: bin_op: and test_items: - flag: "--kubelet-client-certificate" set: true - flag: "--kubelet-client-key" set: true remediation: | Follow the Kubernetes documentation and set up the TLS connection between the apiserver and kubelets. Then, edit API server pod specification file $apiserverconf on the master node and set the kubelet client certificate and key parameters as below. --kubelet-client-certificate= --kubelet-client-key= scored: true - id: 1.2.6 text: "Ensure that the --kubelet-certificate-authority argument is set as appropriate (Scored)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--kubelet-certificate-authority" set: true remediation: | Follow the Kubernetes documentation and setup the TLS connection between the apiserver and kubelets. Then, edit the API server pod specification file $apiserverconf on the master node and set the --kubelet-certificate-authority parameter to the path to the cert file for the certificate authority. --kubelet-certificate-authority= scored: true - id: 1.2.7 text: "Ensure that the --authorization-mode argument is not set to AlwaysAllow (Scored)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--authorization-mode" compare: op: nothave value: "AlwaysAllow" set: true remediation: | Edit the API server pod specification file $apiserverconf on the master node and set the --authorization-mode parameter to values other than AlwaysAllow. One such example could be as below. --authorization-mode=RBAC scored: true - id: 1.2.8 text: "Ensure that the --authorization-mode argument includes Node (Scored)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--authorization-mode" compare: op: has value: "Node" set: true remediation: | Edit the API server pod specification file $apiserverconf on the master node and set the --authorization-mode parameter to a value that includes Node. --authorization-mode=Node,RBAC scored: true - id: 1.2.9 text: "Ensure that the --authorization-mode argument includes RBAC (Scored)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--authorization-mode" compare: op: has value: "RBAC" set: true remediation: | Edit the API server pod specification file $apiserverconf on the master node and set the --authorization-mode parameter to a value that includes RBAC, for example: --authorization-mode=Node,RBAC scored: true - id: 1.2.10 text: "Ensure that the admission control plugin EventRateLimit is set (Not Scored)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--enable-admission-plugins" compare: op: has value: "EventRateLimit" set: true remediation: | Follow the Kubernetes documentation and set the desired limits in a configuration file. Then, edit the API server pod specification file $apiserverconf and set the below parameters. --enable-admission-plugins=...,EventRateLimit,... --admission-control-config-file= scored: false - id: 1.2.11 text: "Ensure that the admission control plugin AlwaysAdmit is not set (Scored)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: bin_op: or test_items: - flag: "--enable-admission-plugins" compare: op: nothave value: AlwaysAdmit set: true - flag: "--enable-admission-plugins" set: false remediation: | Edit the API server pod specification file $apiserverconf on the master node and either remove the --enable-admission-plugins parameter, or set it to a value that does not include AlwaysAdmit. scored: true - id: 1.2.12 text: "Ensure that the admission control plugin AlwaysPullImages is set (Not Scored)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--enable-admission-plugins" compare: op: has value: "AlwaysPullImages" set: true remediation: | Edit the API server pod specification file $apiserverconf on the master node and set the --enable-admission-plugins parameter to include AlwaysPullImages. --enable-admission-plugins=...,AlwaysPullImages,... scored: false - id: 1.2.13 text: "Ensure that the admission control plugin SecurityContextDeny is set if PodSecurityPolicy is not used (Not Scored)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: bin_op: or test_items: - flag: "--enable-admission-plugins" compare: op: has value: "SecurityContextDeny" set: true - flag: "--enable-admission-plugins" compare: op: has value: "PodSecurityPolicy" set: true remediation: | Edit the API server pod specification file $apiserverconf on the master node and set the --enable-admission-plugins parameter to include SecurityContextDeny, unless PodSecurityPolicy is already in place. --enable-admission-plugins=...,SecurityContextDeny,... scored: false - id: 1.2.14 text: "Ensure that the admission control plugin ServiceAccount is set (Scored)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: bin_op: or test_items: - flag: "--disable-admission-plugins" compare: op: nothave value: "ServiceAccount" set: true - flag: "--disable-admission-plugins" set: false remediation: | Follow the documentation and create ServiceAccount objects as per your environment. Then, edit the API server pod specification file $apiserverconf on the master node and ensure that the --disable-admission-plugins parameter is set to a value that does not include ServiceAccount. scored: true - id: 1.2.15 text: "Ensure that the admission control plugin NamespaceLifecycle is set (Scored)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: bin_op: or test_items: - flag: "--disable-admission-plugins" compare: op: nothave value: "NamespaceLifecycle" set: true - flag: "--disable-admission-plugins" set: false remediation: | Edit the API server pod specification file $apiserverconf on the master node and set the --disable-admission-plugins parameter to ensure it does not include NamespaceLifecycle. scored: true - id: 1.2.16 text: "Ensure that the admission control plugin PodSecurityPolicy is set (Scored)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--enable-admission-plugins" compare: op: has value: "PodSecurityPolicy" set: true remediation: | Follow the documentation and create Pod Security Policy objects as per your environment. Then, edit the API server pod specification file $apiserverconf on the master node and set the --enable-admission-plugins parameter to a value that includes PodSecurityPolicy: --enable-admission-plugins=...,PodSecurityPolicy,... Then restart the API Server. scored: true - id: 1.2.17 text: "Ensure that the admission control plugin NodeRestriction is set (Scored)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--enable-admission-plugins" compare: op: has value: "NodeRestriction" set: true remediation: | Follow the Kubernetes documentation and configure NodeRestriction plug-in on kubelets. Then, edit the API server pod specification file $apiserverconf on the master node and set the --enable-admission-plugins parameter to a value that includes NodeRestriction. --enable-admission-plugins=...,NodeRestriction,... scored: true - id: 1.2.18 text: "Ensure that the --insecure-bind-address argument is not set (Scored)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--insecure-bind-address" set: false remediation: | Edit the API server pod specification file $apiserverconf on the master node and remove the --insecure-bind-address parameter. scored: true - id: 1.2.19 text: "Ensure that the --insecure-port argument is set to 0 (Scored)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--insecure-port" compare: op: eq value: 0 set: true remediation: | Edit the API server pod specification file $apiserverconf on the master node and set the below parameter. --insecure-port=0 scored: true - id: 1.2.20 text: "Ensure that the --secure-port argument is not set to 0 (Scored)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: bin_op: or test_items: - flag: "--secure-port" compare: op: gt value: 0 set: true - flag: "--secure-port" set: false remediation: | Edit the API server pod specification file $apiserverconf on the master node and either remove the --secure-port parameter or set it to a different (non-zero) desired port. scored: true - id: 1.2.21 text: "Ensure that the --profiling argument is set to false (Scored)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--profiling" compare: op: eq value: false set: true remediation: | Edit the API server pod specification file $apiserverconf on the master node and set the below parameter. --profiling=false scored: true - id: 1.2.22 text: "Ensure that the --audit-log-path argument is set (Scored)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--audit-log-path" set: true remediation: | Edit the API server pod specification file $apiserverconf on the master node and set the --audit-log-path parameter to a suitable path and file where you would like audit logs to be written, for example: --audit-log-path=/var/log/apiserver/audit.log scored: true - id: 1.2.23 text: "Ensure that the --audit-log-maxage argument is set to 30 or as appropriate (Scored)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--audit-log-maxage" compare: op: gte value: 30 set: true remediation: | Edit the API server pod specification file $apiserverconf on the master node and set the --audit-log-maxage parameter to 30 or as an appropriate number of days: --audit-log-maxage=30 scored: true - id: 1.2.24 text: "Ensure that the --audit-log-maxbackup argument is set to 10 or as appropriate (Scored)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--audit-log-maxbackup" compare: op: gte value: 10 set: true remediation: | Edit the API server pod specification file $apiserverconf on the master node and set the --audit-log-maxbackup parameter to 10 or to an appropriate value. --audit-log-maxbackup=10 scored: true - id: 1.2.25 text: "Ensure that the --audit-log-maxsize argument is set to 100 or as appropriate (Scored)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--audit-log-maxsize" compare: op: gte value: 100 set: true remediation: | Edit the API server pod specification file $apiserverconf on the master node and set the --audit-log-maxsize parameter to an appropriate size in MB. For example, to set it as 100 MB: --audit-log-maxsize=100 scored: true - id: 1.2.26 text: "Ensure that the --request-timeout argument is set as appropriate (Scored)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" type: manual remediation: | Edit the API server pod specification file $apiserverconf and set the below parameter as appropriate and if needed. For example, --request-timeout=300s scored: true - id: 1.2.27 text: "Ensure that the --service-account-lookup argument is set to true (Scored)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: bin_op: or test_items: - flag: "--service-account-lookup" set: false - flag: "--service-account-lookup" compare: op: eq value: true set: true remediation: | Edit the API server pod specification file $apiserverconf on the master node and set the below parameter. --service-account-lookup=true Alternatively, you can delete the --service-account-lookup parameter from this file so that the default takes effect. scored: true - id: 1.2.28 text: "Ensure that the --service-account-key-file argument is set as appropriate (Scored)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--service-account-key-file" set: true remediation: | Edit the API server pod specification file $apiserverconf on the master node and set the --service-account-key-file parameter to the public key file for service accounts: --service-account-key-file= scored: true - id: 1.2.29 text: "Ensure that the --etcd-certfile and --etcd-keyfile arguments are set as appropriate (Scored)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: bin_op: and test_items: - flag: "--etcd-certfile" set: true - flag: "--etcd-keyfile" set: true remediation: | Follow the Kubernetes documentation and set up the TLS connection between the apiserver and etcd. Then, edit the API server pod specification file $apiserverconf on the master node and set the etcd certificate and key file parameters. --etcd-certfile= --etcd-keyfile= scored: true - id: 1.2.30 text: "Ensure that the --tls-cert-file and --tls-private-key-file arguments are set as appropriate (Scored)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: bin_op: and test_items: - flag: "--tls-cert-file" set: true - flag: "--tls-private-key-file" set: true remediation: | Follow the Kubernetes documentation and set up the TLS connection on the apiserver. Then, edit the API server pod specification file $apiserverconf on the master node and set the TLS certificate and private key file parameters. --tls-cert-file= --tls-private-key-file= scored: true - id: 1.2.31 text: "Ensure that the --client-ca-file argument is set as appropriate (Scored)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--client-ca-file" set: true remediation: | Follow the Kubernetes documentation and set up the TLS connection on the apiserver. Then, edit the API server pod specification file $apiserverconf on the master node and set the client certificate authority file. --client-ca-file= scored: true - id: 1.2.32 text: "Ensure that the --etcd-cafile argument is set as appropriate (Scored)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--etcd-cafile" set: true remediation: | Follow the Kubernetes documentation and set up the TLS connection between the apiserver and etcd. Then, edit the API server pod specification file $apiserverconf on the master node and set the etcd certificate authority file parameter. --etcd-cafile= scored: true - id: 1.2.33 text: "Ensure that the --encryption-provider-config argument is set as appropriate (Not Scored)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--encryption-provider-config" set: true remediation: | Follow the Kubernetes documentation and configure a EncryptionConfig file. Then, edit the API server pod specification file $apiserverconf on the master node and set the --encryption-provider-config parameter to the path of that file: --encryption-provider-config= scored: false - id: 1.2.34 text: "Ensure that encryption providers are appropriately configured (Not Scored)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" type: "manual" remediation: | Follow the Kubernetes documentation and configure a EncryptionConfig file. In this file, choose aescbc, kms or secretbox as the encryption provider. scored: false - id: 1.2.35 text: "Ensure that the API Server only makes use of Strong Cryptographic Ciphers (Not Scored)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--tls-cipher-suites" compare: op: valid_elements value: "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" set: true remediation: | Edit the API server pod specification file $apiserverconf on the master node and set the below parameter. --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 scored: false - id: 1.3 text: "Controller Manager" checks: - id: 1.3.1 text: "Ensure that the --terminated-pod-gc-threshold argument is set as appropriate (Not Scored)" audit: "/bin/ps -ef | grep $controllermanagerbin | grep -v grep" tests: test_items: - flag: "--terminated-pod-gc-threshold" set: true remediation: | Edit the Controller Manager pod specification file $controllermanagerconf on the master node and set the --terminated-pod-gc-threshold to an appropriate threshold, for example: --terminated-pod-gc-threshold=10 scored: false - id: 1.3.2 text: "Ensure that the --profiling argument is set to false (Scored)" audit: "/bin/ps -ef | grep $controllermanagerbin | grep -v grep" tests: test_items: - flag: "--profiling" compare: op: eq value: false set: true remediation: | Edit the Controller Manager pod specification file $controllermanagerconf on the master node and set the below parameter. --profiling=false scored: true - id: 1.3.3 text: "Ensure that the --use-service-account-credentials argument is set to true (Scored)" audit: "/bin/ps -ef | grep $controllermanagerbin | grep -v grep" tests: test_items: - flag: "--use-service-account-credentials" compare: op: noteq value: false set: true remediation: | Edit the Controller Manager pod specification file $controllermanagerconf on the master node to set the below parameter. --use-service-account-credentials=true scored: true - id: 1.3.4 text: "Ensure that the --service-account-private-key-file argument is set as appropriate (Scored)" audit: "/bin/ps -ef | grep $controllermanagerbin | grep -v grep" tests: test_items: - flag: "--service-account-private-key-file" set: true remediation: | Edit the Controller Manager pod specification file $controllermanagerconf on the master node and set the --service-account-private-key-file parameter to the private key file for service accounts. --service-account-private-key-file= scored: true - id: 1.3.5 text: "Ensure that the --root-ca-file argument is set as appropriate (Scored)" audit: "/bin/ps -ef | grep $controllermanagerbin | grep -v grep" tests: test_items: - flag: "--root-ca-file" set: true remediation: | Edit the Controller Manager pod specification file $controllermanagerconf on the master node and set the --root-ca-file parameter to the certificate bundle file`. --root-ca-file= scored: true - id: 1.3.6 text: "Ensure that the RotateKubeletServerCertificate argument is set to true (Scored)" audit: "/bin/ps -ef | grep $controllermanagerbin | grep -v grep" tests: bin_op: or test_items: - flag: "--feature-gates" compare: op: nothave value: "RotateKubeletServerCertificate=false" set: true - flag: "--feature-gates" set: false remediation: | Edit the Controller Manager pod specification file $controllermanagerconf on the master node and set the --feature-gates parameter to include RotateKubeletServerCertificate=true. --feature-gates=RotateKubeletServerCertificate=true scored: true - id: 1.3.7 text: "Ensure that the --bind-address argument is set to 127.0.0.1 (Scored)" audit: "/bin/ps -ef | grep $controllermanagerbin | grep -v grep" tests: bin_op: or test_items: - flag: "--bind-address" compare: op: eq value: "127.0.0.1" set: true - flag: "--bind-address" set: false remediation: | Edit the Controller Manager pod specification file $controllermanagerconf on the master node and ensure the correct value for the --bind-address parameter scored: true - id: 1.4 text: "Scheduler" checks: - id: 1.4.1 text: "Ensure that the --profiling argument is set to false (Scored)" audit: "/bin/ps -ef | grep $schedulerbin | grep -v grep" tests: test_items: - flag: "--profiling" compare: op: eq value: false set: true remediation: | Edit the Scheduler pod specification file $schedulerconf file on the master node and set the below parameter. --profiling=false scored: true - id: 1.4.2 text: "Ensure that the --bind-address argument is set to 127.0.0.1 (Scored)" audit: "/bin/ps -ef | grep $schedulerbin | grep -v grep" tests: bin_op: or test_items: - flag: "--bind-address" compare: op: eq value: "127.0.0.1" set: true - flag: "--bind-address" set: false remediation: | Edit the Scheduler pod specification file $schedulerconf on the master node and ensure the correct value for the --bind-address parameter scored: true ================================================ FILE: cfg/cis-1.5/node.yaml ================================================ --- controls: version: "cis-1.5" id: 4 text: "Worker Node Security Configuration" type: "node" groups: - id: 4.1 text: "Worker Node Configuration Files" checks: - id: 4.1.1 text: "Ensure that the kubelet service file permissions are set to 644 or more restrictive (Scored)" audit: '/bin/sh -c ''if test -e $kubeletsvc; then stat -c permissions=%a $kubeletsvc; fi'' ' tests: test_items: - flag: "permissions" set: true compare: op: bitmask value: "644" remediation: | Run the below command (based on the file location on your system) on the each worker node. For example, chmod 644 $kubeletsvc scored: true - id: 4.1.2 text: "Ensure that the kubelet service file ownership is set to root:root (Automated)" audit: '/bin/sh -c "if test -e $kubeletsvc; then stat -c %U:%G $kubeletsvc; else echo \"File not found\"; fi"' tests: bin_op: or test_items: - flag: root:root - flag: "File not found" remediation: | Run the below command (based on the file location on your system) on the each worker node. For example, chown root:root $kubeletsvc scored: true - id: 4.1.3 text: "Ensure that the proxy kubeconfig file permissions are set to 644 or more restrictive (Scored)" audit: '/bin/sh -c ''if test -e $proxykubeconfig; then stat -c permissions=%a $proxykubeconfig; fi'' ' tests: bin_op: or test_items: - flag: "permissions" set: true compare: op: bitmask value: "644" remediation: | Run the below command (based on the file location on your system) on the each worker node. For example, chmod 644 $proxykubeconfig scored: true - id: 4.1.4 text: "Ensure that the proxy kubeconfig file ownership is set to root:root (Scored)" audit: '/bin/sh -c ''if test -e $proxykubeconfig; then stat -c %U:%G $proxykubeconfig; fi'' ' tests: bin_op: or test_items: - flag: root:root set: true remediation: | Run the below command (based on the file location on your system) on the each worker node. For example, chown root:root $proxykubeconfig scored: true - id: 4.1.5 text: "Ensure that the kubelet.conf file permissions are set to 644 or more restrictive (Scored)" audit: '/bin/sh -c ''if test -e $kubeletkubeconfig; then stat -c permissions=%a $kubeletkubeconfig; fi'' ' tests: test_items: - flag: "permissions" set: true compare: op: bitmask value: "644" remediation: | Run the below command (based on the file location on your system) on the each worker node. For example, chmod 644 $kubeletkubeconfig scored: true - id: 4.1.6 text: "Ensure that the kubelet.conf file ownership is set to root:root (Scored)" audit: '/bin/sh -c ''if test -e $kubeletkubeconfig; then stat -c %U:%G $kubeletkubeconfig; fi'' ' tests: test_items: - flag: root:root set: true compare: op: eq value: root:root remediation: | Run the below command (based on the file location on your system) on the each worker node. For example, chown root:root $kubeletkubeconfig scored: true - id: 4.1.7 text: "Ensure that the certificate authorities file permissions are set to 644 or more restrictive (Scored)" audit: | CAFILE=$(ps -ef | grep kubelet | grep -v apiserver | grep -- --client-ca-file= | awk -F '--client-ca-file=' '{print $2}' | awk '{print $1}' | uniq) if test -z $CAFILE; then CAFILE=$kubeletcafile; fi if test -e $CAFILE; then stat -c permissions=%a $CAFILE; fi tests: test_items: - flag: "permissions" set: true compare: op: bitmask value: "644" remediation: | Run the following command to modify the file permissions of the --client-ca-file chmod 644 scored: true - id: 4.1.8 text: "Ensure that the client certificate authorities file ownership is set to root:root (Scored)" audit: | CAFILE=$(ps -ef | grep kubelet | grep -v apiserver | grep -- --client-ca-file= | awk -F '--client-ca-file=' '{print $2}' | awk '{print $1}' | uniq) if test -z $CAFILE; then CAFILE=$kubeletcafile; fi if test -e $CAFILE; then stat -c %U:%G $CAFILE; fi tests: test_items: - flag: root:root set: true compare: op: eq value: root:root remediation: | Run the following command to modify the ownership of the --client-ca-file. chown root:root scored: true - id: 4.1.9 text: "Ensure that the kubelet configuration file has permissions set to 644 or more restrictive (Scored)" audit: '/bin/sh -c ''if test -e $kubeletconf; then stat -c permissions=%a $kubeletconf; fi'' ' tests: test_items: - flag: "permissions" set: true compare: op: bitmask value: "644" remediation: | Run the following command (using the config file location identified in the Audit step) chmod 644 $kubeletconf scored: true - id: 4.1.10 text: "Ensure that the kubelet configuration file ownership is set to root:root (Scored)" audit: '/bin/sh -c ''if test -e $kubeletconf; then stat -c %U:%G $kubeletconf; fi'' ' tests: test_items: - flag: root:root set: true remediation: | Run the following command (using the config file location identified in the Audit step) chown root:root $kubeletconf scored: true - id: 4.2 text: "Kubelet" checks: - id: 4.2.1 text: "Ensure that the anonymous-auth argument is set to false (Scored)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: "--anonymous-auth" path: '{.authentication.anonymous.enabled}' set: true compare: op: eq value: false remediation: | If using a Kubelet config file, edit the file to set authentication: anonymous: enabled to false. If using executable arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_SYSTEM_PODS_ARGS variable. --anonymous-auth=false Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: true - id: 4.2.2 text: "Ensure that the --authorization-mode argument is not set to AlwaysAllow (Scored)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --authorization-mode path: '{.authorization.mode}' set: true compare: op: nothave value: AlwaysAllow remediation: | If using a Kubelet config file, edit the file to set authorization: mode to Webhook. If using executable arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_AUTHZ_ARGS variable. --authorization-mode=Webhook Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: true - id: 4.2.3 text: "Ensure that the --client-ca-file argument is set as appropriate (Scored)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --client-ca-file path: '{.authentication.x509.clientCAFile}' set: true remediation: | If using a Kubelet config file, edit the file to set authentication: x509: clientCAFile to the location of the client CA file. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_AUTHZ_ARGS variable. --client-ca-file= Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: true - id: 4.2.4 text: "Ensure that the --read-only-port argument is set to 0 (Scored)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: bin_op: or test_items: - flag: "--read-only-port" path: '{.readOnlyPort}' set: true compare: op: eq value: 0 - flag: "--read-only-port" path: '{.readOnlyPort}' set: false remediation: | If using a Kubelet config file, edit the file to set readOnlyPort to 0. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_SYSTEM_PODS_ARGS variable. --read-only-port=0 Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: true - id: 4.2.5 text: "Ensure that the --streaming-connection-idle-timeout argument is not set to 0 (Scored)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --streaming-connection-idle-timeout path: '{.streamingConnectionIdleTimeout}' set: true compare: op: noteq value: 0 - flag: --streaming-connection-idle-timeout path: '{.streamingConnectionIdleTimeout}' set: false bin_op: or remediation: | If using a Kubelet config file, edit the file to set streamingConnectionIdleTimeout to a value other than 0. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_SYSTEM_PODS_ARGS variable. --streaming-connection-idle-timeout=5m Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: true - id: 4.2.6 text: "Ensure that the --protect-kernel-defaults argument is set to true (Scored)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --protect-kernel-defaults path: '{.protectKernelDefaults}' set: true compare: op: eq value: true remediation: | If using a Kubelet config file, edit the file to set protectKernelDefaults: true. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_SYSTEM_PODS_ARGS variable. --protect-kernel-defaults=true Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: true - id: 4.2.7 text: "Ensure that the --make-iptables-util-chains argument is set to true (Scored)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --make-iptables-util-chains path: '{.makeIPTablesUtilChains}' set: true compare: op: eq value: true - flag: --make-iptables-util-chains path: '{.makeIPTablesUtilChains}' set: false bin_op: or remediation: | If using a Kubelet config file, edit the file to set makeIPTablesUtilChains: true. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and remove the --make-iptables-util-chains argument from the KUBELET_SYSTEM_PODS_ARGS variable. Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: true - id: 4.2.8 text: "Ensure that the --hostname-override argument is not set (Not Scored)" # This is one of those properties that can only be set as a command line argument. # To check if the property is set as expected, we need to parse the kubelet command # instead reading the Kubelet Configuration file. audit: "/bin/ps -fC $kubeletbin " tests: test_items: - flag: --hostname-override set: false remediation: | Edit the kubelet service file $kubeletsvc on each worker node and remove the --hostname-override argument from the KUBELET_SYSTEM_PODS_ARGS variable. Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: false - id: 4.2.9 text: "Ensure that the --event-qps argument is set to 0 or a level which ensures appropriate event capture (Not Scored)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --event-qps path: '{.eventRecordQPS}' set: true compare: op: eq value: 0 remediation: | If using a Kubelet config file, edit the file to set eventRecordQPS: to an appropriate level. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_SYSTEM_PODS_ARGS variable. Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: false - id: 4.2.10 text: "Ensure that the --tls-cert-file and --tls-private-key-file arguments are set as appropriate (Scored)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --tls-cert-file path: '{.tlsCertFile}' set: true - flag: --tls-private-key-file path: '{.tlsPrivateKeyFile}' set: true remediation: | If using a Kubelet config file, edit the file to set tlsCertFile to the location of the certificate file to use to identify this Kubelet, and tlsPrivateKeyFile to the location of the corresponding private key file. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameters in KUBELET_CERTIFICATE_ARGS variable. --tls-cert-file= --tls-private-key-file= Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: true - id: 4.2.11 text: "Ensure that the --rotate-certificates argument is not set to false (Scored)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --rotate-certificates path: '{.rotateCertificates}' set: true compare: op: eq value: true - flag: --rotate-certificates path: '{.rotateCertificates}' set: false bin_op: or remediation: | If using a Kubelet config file, edit the file to add the line rotateCertificates: true or remove it altogether to use the default value. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and remove --rotate-certificates=false argument from the KUBELET_CERTIFICATE_ARGS variable. Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: true - id: 4.2.12 text: "Ensure that the RotateKubeletServerCertificate argument is set to true (Scored)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: bin_op: or test_items: - flag: RotateKubeletServerCertificate path: '{.featureGates.RotateKubeletServerCertificate}' set: true compare: op: nothave value: false - flag: RotateKubeletServerCertificate path: '{.featureGates.RotateKubeletServerCertificate}' set: false remediation: | Edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_CERTIFICATE_ARGS variable. --feature-gates=RotateKubeletServerCertificate=true Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: true - id: 4.2.13 text: "Ensure that the Kubelet only makes use of Strong Cryptographic Ciphers (Not Scored)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --tls-cipher-suites path: '{range .tlsCipherSuites[:]}{}{'',''}{end}' set: true compare: op: valid_elements value: 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 remediation: | If using a Kubelet config file, edit the file to set tlsCipherSuites: to 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 or to a subset of these values. If using executable arguments, edit the kubelet service file $kubeletsvc on each worker node and set the --tls-cipher-suites parameter as follows, or to a subset of these values. --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 Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: false ================================================ FILE: cfg/cis-1.5/policies.yaml ================================================ --- controls: version: "cis-1.5" id: 5 text: "Kubernetes Policies" type: "policies" groups: - id: 5.1 text: "RBAC and Service Accounts" checks: - id: 5.1.1 text: "Ensure that the cluster-admin role is only used where required (Not Scored)" type: "manual" remediation: | Identify all clusterrolebindings to the cluster-admin role. Check if they are used and if they need this role or if they could use a role with fewer privileges. Where possible, first bind users to a lower privileged role and then remove the clusterrolebinding to the cluster-admin role : kubectl delete clusterrolebinding [name] scored: false - id: 5.1.2 text: "Minimize access to secrets (Not Scored)" type: "manual" remediation: | Where possible, remove get, list and watch access to secret objects in the cluster. scored: false - id: 5.1.3 text: "Minimize wildcard use in Roles and ClusterRoles (Not Scored)" type: "manual" remediation: | Where possible replace any use of wildcards in clusterroles and roles with specific objects or actions. scored: false - id: 5.1.4 text: "Minimize access to create pods (Not Scored)" type: "manual" remediation: | Where possible, remove create access to pod objects in the cluster. scored: false - id: 5.1.5 text: "Ensure that default service accounts are not actively used. (Scored)" type: "manual" remediation: | Create explicit service accounts wherever a Kubernetes workload requires specific access to the Kubernetes API server. Modify the configuration of each default service account to include this value automountServiceAccountToken: false scored: true - id: 5.1.6 text: "Ensure that Service Account Tokens are only mounted where necessary (Not Scored)" type: "manual" remediation: | Modify the definition of pods and service accounts which do not need to mount service account tokens to disable it. scored: false - id: 5.2 text: "Pod Security Policies" checks: - id: 5.2.1 text: "Minimize the admission of privileged containers (Not Scored)" type: "manual" remediation: | Create a PSP as described in the Kubernetes documentation, ensuring that the .spec.privileged field is omitted or set to false. scored: false - id: 5.2.2 text: "Minimize the admission of containers wishing to share the host process ID namespace (Scored)" type: "manual" remediation: | Create a PSP as described in the Kubernetes documentation, ensuring that the .spec.hostPID field is omitted or set to false. scored: true - id: 5.2.3 text: "Minimize the admission of containers wishing to share the host IPC namespace (Scored)" type: "manual" remediation: | Create a PSP as described in the Kubernetes documentation, ensuring that the .spec.hostIPC field is omitted or set to false. scored: true - id: 5.2.4 text: "Minimize the admission of containers wishing to share the host network namespace (Scored)" type: "manual" remediation: | Create a PSP as described in the Kubernetes documentation, ensuring that the .spec.hostNetwork field is omitted or set to false. scored: true - id: 5.2.5 text: "Minimize the admission of containers with allowPrivilegeEscalation (Scored)" type: "manual" remediation: | Create a PSP as described in the Kubernetes documentation, ensuring that the .spec.allowPrivilegeEscalation field is omitted or set to false. scored: true - id: 5.2.6 text: "Minimize the admission of root containers (Not Scored)" type: "manual" remediation: | Create a PSP as described in the Kubernetes documentation, ensuring that the .spec.runAsUser.rule is set to either MustRunAsNonRoot or MustRunAs with the range of UIDs not including 0. scored: false - id: 5.2.7 text: "Minimize the admission of containers with the NET_RAW capability (Not Scored)" type: "manual" remediation: | Create a PSP as described in the Kubernetes documentation, ensuring that the .spec.requiredDropCapabilities is set to include either NET_RAW or ALL. scored: false - id: 5.2.8 text: "Minimize the admission of containers with added capabilities (Not Scored)" type: "manual" remediation: | Ensure that allowedCapabilities is not present in PSPs for the cluster unless it is set to an empty array. scored: false - id: 5.2.9 text: "Minimize the admission of containers with capabilities assigned (Not Scored)" type: "manual" remediation: | Review the use of capabilites in applications running on your cluster. Where a namespace contains applications which do not require any Linux capabities to operate consider adding a PSP which forbids the admission of containers which do not drop all capabilities. scored: false - id: 5.3 text: "Network Policies and CNI" checks: - id: 5.3.1 text: "Ensure that the CNI in use supports Network Policies (Not Scored)" type: "manual" remediation: | If the CNI plugin in use does not support network policies, consideration should be given to making use of a different plugin, or finding an alternate mechanism for restricting traffic in the Kubernetes cluster. scored: false - id: 5.3.2 text: "Ensure that all Namespaces have Network Policies defined (Scored)" type: "manual" remediation: | Follow the documentation and create NetworkPolicy objects as you need them. scored: true - id: 5.4 text: "Secrets Management" checks: - id: 5.4.1 text: "Prefer using secrets as files over secrets as environment variables (Not Scored)" type: "manual" remediation: | if possible, rewrite application code to read secrets from mounted secret files, rather than from environment variables. scored: false - id: 5.4.2 text: "Consider external secret storage (Not Scored)" type: "manual" remediation: | Refer to the secrets management options offered by your cloud provider or a third-party secrets management solution. scored: false - id: 5.5 text: "Extensible Admission Control" checks: - id: 5.5.1 text: "Configure Image Provenance using ImagePolicyWebhook admission controller (Not Scored)" type: "manual" remediation: | Follow the Kubernetes documentation and setup image provenance. scored: false - id: 5.7 text: "General Policies" checks: - id: 5.7.1 text: "Create administrative boundaries between resources using namespaces (Not Scored)" type: "manual" remediation: | Follow the documentation and create namespaces for objects in your deployment as you need them. scored: false - id: 5.7.2 text: "Ensure that the seccomp profile is set to docker/default in your pod definitions (Not Scored)" type: "manual" remediation: | Seccomp is an alpha feature currently. By default, all alpha features are disabled. So, you would need to enable alpha features in the apiserver by passing "--feature- gates=AllAlpha=true" argument. Edit the /etc/kubernetes/apiserver file on the master node and set the KUBE_API_ARGS parameter to "--feature-gates=AllAlpha=true" KUBE_API_ARGS="--feature-gates=AllAlpha=true" Based on your system, restart the kube-apiserver service. For example: systemctl restart kube-apiserver.service Use annotations to enable the docker/default seccomp profile in your pod definitions. An example is as below: apiVersion: v1 kind: Pod metadata: name: trustworthy-pod annotations: seccomp.security.alpha.kubernetes.io/pod: docker/default spec: containers: - name: trustworthy-container image: sotrustworthy:latest scored: false - id: 5.7.3 text: "Apply Security Context to Your Pods and Containers (Not Scored)" type: "manual" remediation: | Follow the Kubernetes documentation and apply security contexts to your pods. For a suggested list of security contexts, you may refer to the CIS Security Benchmark for Docker Containers. scored: false - id: 5.7.4 text: "The default namespace should not be used (Scored)" type: "manual" remediation: | Ensure that namespaces are created to allow for appropriate segregation of Kubernetes resources and that all new resources are created in a specific namespace. scored: true ================================================ FILE: cfg/cis-1.6/config.yaml ================================================ --- ## Version-specific settings that override the values in cfg/config.yaml ================================================ FILE: cfg/cis-1.6/controlplane.yaml ================================================ --- controls: version: "cis-1.6" id: 3 text: "Control Plane Configuration" type: "controlplane" groups: - id: 3.1 text: "Authentication and Authorization" checks: - id: 3.1.1 text: "Client certificate authentication should not be used for users (Manual)" type: "manual" remediation: | Alternative mechanisms provided by Kubernetes such as the use of OIDC should be implemented in place of client certificates. scored: false - id: 3.2 text: "Logging" checks: - id: 3.2.1 text: "Ensure that a minimal audit policy is created (Manual)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--audit-policy-file" set: true remediation: | Create an audit policy file for your cluster. scored: false - id: 3.2.2 text: "Ensure that the audit policy covers key security concerns (Manual)" type: "manual" remediation: | Consider modification of the audit policy in use on the cluster to include these items, at a minimum. scored: false ================================================ FILE: cfg/cis-1.6/etcd.yaml ================================================ --- controls: version: "cis-1.6" id: 2 text: "Etcd Node Configuration" type: "etcd" groups: - id: 2 text: "Etcd Node Configuration Files" checks: - id: 2.1 text: "Ensure that the --cert-file and --key-file arguments are set as appropriate (Automated)" audit: "/bin/ps -ef | /bin/grep $etcdbin | /bin/grep -v grep" tests: bin_op: and test_items: - flag: "--cert-file" env: "ETCD_CERT_FILE" - flag: "--key-file" env: "ETCD_KEY_FILE" remediation: | Follow the etcd service documentation and configure TLS encryption. Then, edit the etcd pod specification file /etc/kubernetes/manifests/etcd.yaml on the master node and set the below parameters. --cert-file= --key-file= scored: true - id: 2.2 text: "Ensure that the --client-cert-auth argument is set to true (Automated)" audit: "/bin/ps -ef | /bin/grep $etcdbin | /bin/grep -v grep" tests: test_items: - flag: "--client-cert-auth" env: "ETCD_CLIENT_CERT_AUTH" compare: op: eq value: true remediation: | Edit the etcd pod specification file $etcdconf on the master node and set the below parameter. --client-cert-auth="true" scored: true - id: 2.3 text: "Ensure that the --auto-tls argument is not set to true (Automated)" audit: "/bin/ps -ef | /bin/grep $etcdbin | /bin/grep -v grep" tests: bin_op: or test_items: - flag: "--auto-tls" env: "ETCD_AUTO_TLS" set: false - flag: "--auto-tls" env: "ETCD_AUTO_TLS" compare: op: eq value: false remediation: | Edit the etcd pod specification file $etcdconf on the master node and either remove the --auto-tls parameter or set it to false. --auto-tls=false scored: true - id: 2.4 text: "Ensure that the --peer-cert-file and --peer-key-file arguments are set as appropriate (Automated)" audit: "/bin/ps -ef | /bin/grep $etcdbin | /bin/grep -v grep" tests: bin_op: and test_items: - flag: "--peer-cert-file" env: "ETCD_PEER_CERT_FILE" - flag: "--peer-key-file" env: "ETCD_PEER_KEY_FILE" remediation: | Follow the etcd service documentation and configure peer TLS encryption as appropriate for your etcd cluster. Then, edit the etcd pod specification file $etcdconf on the master node and set the below parameters. --peer-client-file= --peer-key-file= scored: true - id: 2.5 text: "Ensure that the --peer-client-cert-auth argument is set to true (Automated)" audit: "/bin/ps -ef | /bin/grep $etcdbin | /bin/grep -v grep" tests: test_items: - flag: "--peer-client-cert-auth" env: "ETCD_PEER_CLIENT_CERT_AUTH" compare: op: eq value: true remediation: | Edit the etcd pod specification file $etcdconf on the master node and set the below parameter. --peer-client-cert-auth=true scored: true - id: 2.6 text: "Ensure that the --peer-auto-tls argument is not set to true (Automated)" audit: "/bin/ps -ef | /bin/grep $etcdbin | /bin/grep -v grep" tests: bin_op: or test_items: - flag: "--peer-auto-tls" env: "ETCD_PEER_AUTO_TLS" set: false - flag: "--peer-auto-tls" env: "ETCD_PEER_AUTO_TLS" compare: op: eq value: false remediation: | Edit the etcd pod specification file $etcdconf on the master node and either remove the --peer-auto-tls parameter or set it to false. --peer-auto-tls=false scored: true - id: 2.7 text: "Ensure that a unique Certificate Authority is used for etcd (Manual)" audit: "/bin/ps -ef | /bin/grep $etcdbin | /bin/grep -v grep" tests: test_items: - flag: "--trusted-ca-file" env: "ETCD_TRUSTED_CA_FILE" remediation: | [Manual test] Follow the etcd documentation and create a dedicated certificate authority setup for the etcd service. Then, edit the etcd pod specification file $etcdconf on the master node and set the below parameter. --trusted-ca-file= scored: false ================================================ FILE: cfg/cis-1.6/master.yaml ================================================ --- controls: version: "cis-1.6" id: 1 text: "Master Node Security Configuration" type: "master" groups: - id: 1.1 text: "Master Node Configuration Files" checks: - id: 1.1.1 text: "Ensure that the API server pod specification file permissions are set to 644 or more restrictive (Automated)" audit: "/bin/sh -c 'if test -e $apiserverconf; then stat -c permissions=%a $apiserverconf; fi'" tests: test_items: - flag: "permissions" compare: op: bitmask value: "644" remediation: | Run the below command (based on the file location on your system) on the master node. For example, chmod 644 $apiserverconf scored: true - id: 1.1.2 text: "Ensure that the API server pod specification file ownership is set to root:root (Automated)" audit: "/bin/sh -c 'if test -e $apiserverconf; then stat -c %U:%G $apiserverconf; fi'" tests: test_items: - flag: "root:root" remediation: | Run the below command (based on the file location on your system) on the master node. For example, chown root:root $apiserverconf scored: true - id: 1.1.3 text: "Ensure that the controller manager pod specification file permissions are set to 644 or more restrictive (Automated)" audit: "/bin/sh -c 'if test -e $controllermanagerconf; then stat -c permissions=%a $controllermanagerconf; fi'" tests: test_items: - flag: "permissions" compare: op: bitmask value: "644" remediation: | Run the below command (based on the file location on your system) on the master node. For example, chmod 644 $controllermanagerconf scored: true - id: 1.1.4 text: "Ensure that the controller manager pod specification file ownership is set to root:root (Automated)" audit: "/bin/sh -c 'if test -e $controllermanagerconf; then stat -c %U:%G $controllermanagerconf; fi'" tests: test_items: - flag: "root:root" remediation: | Run the below command (based on the file location on your system) on the master node. For example, chown root:root $controllermanagerconf scored: true - id: 1.1.5 text: "Ensure that the scheduler pod specification file permissions are set to 644 or more restrictive (Automated)" audit: "/bin/sh -c 'if test -e $schedulerconf; then stat -c permissions=%a $schedulerconf; fi'" tests: test_items: - flag: "permissions" compare: op: bitmask value: "644" remediation: | Run the below command (based on the file location on your system) on the master node. For example, chmod 644 $schedulerconf scored: true - id: 1.1.6 text: "Ensure that the scheduler pod specification file ownership is set to root:root (Automated)" audit: "/bin/sh -c 'if test -e $schedulerconf; then stat -c %U:%G $schedulerconf; fi'" tests: test_items: - flag: "root:root" remediation: | Run the below command (based on the file location on your system) on the master node. For example, chown root:root $schedulerconf scored: true - id: 1.1.7 text: "Ensure that the etcd pod specification file permissions are set to 644 or more restrictive (Automated)" audit: "/bin/sh -c 'if test -e $etcdconf; then find $etcdconf -name '*etcd*' | xargs stat -c permissions=%a; fi'" use_multiple_values: true tests: test_items: - flag: "permissions" compare: op: bitmask value: "644" remediation: | Run the below command (based on the file location on your system) on the master node. For example, chmod 644 $etcdconf scored: true - id: 1.1.8 text: "Ensure that the etcd pod specification file ownership is set to root:root (Automated)" audit: "/bin/sh -c 'if test -e $etcdconf; then find $etcdconf -name '*etcd*' | xargs stat -c %U:%G; fi'" use_multiple_values: true tests: test_items: - flag: "root:root" remediation: | Run the below command (based on the file location on your system) on the master node. For example, chown root:root $etcdconf scored: true - id: 1.1.9 text: "Ensure that the Container Network Interface file permissions are set to 644 or more restrictive (Manual)" audit: | ps -ef | grep $kubeletbin | grep -- --cni-conf-dir | sed 's%.*cni-conf-dir[= ]\([^ ]*\).*%\1%' | xargs -I{} find {} -mindepth 1 | xargs --no-run-if-empty stat -c permissions=%a find /var/lib/cni/networks -type f 2> /dev/null | xargs --no-run-if-empty stat -c permissions=%a use_multiple_values: true tests: test_items: - flag: "permissions" compare: op: bitmask value: "644" remediation: | Run the below command (based on the file location on your system) on the master node. For example, chmod 644 scored: false - id: 1.1.10 text: "Ensure that the Container Network Interface file ownership is set to root:root (Manual)" audit: | ps -ef | grep $kubeletbin | grep -- --cni-conf-dir | sed 's%.*cni-conf-dir[= ]\([^ ]*\).*%\1%' | xargs -I{} find {} -mindepth 1 | xargs --no-run-if-empty stat -c %U:%G find /var/lib/cni/networks -type f 2> /dev/null | xargs --no-run-if-empty stat -c %U:%G use_multiple_values: true tests: test_items: - flag: "root:root" remediation: | Run the below command (based on the file location on your system) on the master node. For example, chown root:root scored: false - id: 1.1.11 text: "Ensure that the etcd data directory permissions are set to 700 or more restrictive (Automated)" audit: | DATA_DIR='' for d in $(ps -ef | grep $etcdbin | grep -- --data-dir | sed 's%.*data-dir[= ]\([^ ]*\).*%\1%'); do if test -d "$d"; then DATA_DIR="$d"; fi done if ! test -d "$DATA_DIR"; then DATA_DIR=$etcddatadir; fi stat -c permissions=%a "$DATA_DIR" tests: test_items: - flag: "permissions" compare: op: bitmask value: "700" remediation: | On the etcd server node, get the etcd data directory, passed as an argument --data-dir, from the below command: ps -ef | grep etcd Run the below command (based on the etcd data directory found above). For example, chmod 700 /var/lib/etcd scored: true - id: 1.1.12 text: "Ensure that the etcd data directory ownership is set to etcd:etcd (Automated)" audit: | DATA_DIR='' for d in $(ps -ef | grep $etcdbin | grep -- --data-dir | sed 's%.*data-dir[= ]\([^ ]*\).*%\1%'); do if test -d "$d"; then DATA_DIR="$d"; fi done if ! test -d "$DATA_DIR"; then DATA_DIR=$etcddatadir; fi stat -c %U:%G $DATA_DIR tests: test_items: - flag: "etcd:etcd" remediation: | On the etcd server node, get the etcd data directory, passed as an argument --data-dir, from the below command: ps -ef | grep etcd Run the below command (based on the etcd data directory found above). For example, chown etcd:etcd /var/lib/etcd scored: true - id: 1.1.13 text: "Ensure that the admin.conf file permissions are set to 644 or more restrictive (Automated)" audit: "/bin/sh -c 'if test -e /etc/kubernetes/admin.conf; then stat -c permissions=%a /etc/kubernetes/admin.conf; fi'" tests: test_items: - flag: "permissions" compare: op: bitmask value: "644" remediation: | Run the below command (based on the file location on your system) on the master node. For example, chmod 644 /etc/kubernetes/admin.conf scored: true - id: 1.1.14 text: "Ensure that the admin.conf file ownership is set to root:root (Automated)" audit: "/bin/sh -c 'if test -e /etc/kubernetes/admin.conf; then stat -c %U:%G /etc/kubernetes/admin.conf; fi'" tests: test_items: - flag: "root:root" remediation: | Run the below command (based on the file location on your system) on the master node. For example, chown root:root /etc/kubernetes/admin.conf scored: true - id: 1.1.15 text: "Ensure that the scheduler.conf file permissions are set to 644 or more restrictive (Automated)" audit: "/bin/sh -c 'if test -e $schedulerkubeconfig; then stat -c permissions=%a $schedulerkubeconfig; fi'" tests: test_items: - flag: "permissions" compare: op: bitmask value: "644" remediation: | Run the below command (based on the file location on your system) on the master node. For example, chmod 644 $schedulerkubeconfig scored: true - id: 1.1.16 text: "Ensure that the scheduler.conf file ownership is set to root:root (Automated)" audit: "/bin/sh -c 'if test -e $schedulerkubeconfig; then stat -c %U:%G $schedulerkubeconfig; fi'" tests: test_items: - flag: "root:root" remediation: | Run the below command (based on the file location on your system) on the master node. For example, chown root:root $schedulerkubeconfig scored: true - id: 1.1.17 text: "Ensure that the controller-manager.conf file permissions are set to 644 or more restrictive (Automated)" audit: "/bin/sh -c 'if test -e $controllermanagerkubeconfig; then stat -c permissions=%a $controllermanagerkubeconfig; fi'" tests: test_items: - flag: "permissions" compare: op: bitmask value: "644" remediation: | Run the below command (based on the file location on your system) on the master node. For example, chmod 644 $controllermanagerkubeconfig scored: true - id: 1.1.18 text: "Ensure that the controller-manager.conf file ownership is set to root:root (Automated)" audit: "/bin/sh -c 'if test -e $controllermanagerkubeconfig; then stat -c %U:%G $controllermanagerkubeconfig; fi'" tests: test_items: - flag: "root:root" remediation: | Run the below command (based on the file location on your system) on the master node. For example, chown root:root $controllermanagerkubeconfig scored: true - id: 1.1.19 text: "Ensure that the Kubernetes PKI directory and file ownership is set to root:root (Automated)" audit: "find /etc/kubernetes/pki/ | xargs stat -c %U:%G" use_multiple_values: true tests: test_items: - flag: "root:root" remediation: | Run the below command (based on the file location on your system) on the master node. For example, chown -R root:root /etc/kubernetes/pki/ scored: true - id: 1.1.20 text: "Ensure that the Kubernetes PKI certificate file permissions are set to 644 or more restrictive (Manual)" audit: "find /etc/kubernetes/pki/ -name '*.crt' | xargs stat -c permissions=%a" use_multiple_values: true tests: test_items: - flag: "permissions" compare: op: bitmask value: "644" remediation: | Run the below command (based on the file location on your system) on the master node. For example, chmod -R 644 /etc/kubernetes/pki/*.crt scored: false - id: 1.1.21 text: "Ensure that the Kubernetes PKI key file permissions are set to 600 (Manual)" audit: "find /etc/kubernetes/pki/ -name '*.key' | xargs stat -c permissions=%a" use_multiple_values: true tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Run the below command (based on the file location on your system) on the master node. For example, chmod -R 600 /etc/kubernetes/pki/*.key scored: false - id: 1.2 text: "API Server" checks: - id: 1.2.1 text: "Ensure that the --anonymous-auth argument is set to false (Manual)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--anonymous-auth" compare: op: eq value: false remediation: | Edit the API server pod specification file $apiserverconf on the master node and set the below parameter. --anonymous-auth=false scored: false - id: 1.2.2 text: "Ensure that the --basic-auth-file argument is not set (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--basic-auth-file" set: false remediation: | Follow the documentation and configure alternate mechanisms for authentication. Then, edit the API server pod specification file $apiserverconf on the master node and remove the --basic-auth-file= parameter. scored: true - id: 1.2.3 text: "Ensure that the --token-auth-file parameter is not set (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--token-auth-file" set: false remediation: | Follow the documentation and configure alternate mechanisms for authentication. Then, edit the API server pod specification file $apiserverconf on the master node and remove the --token-auth-file= parameter. scored: true - id: 1.2.4 text: "Ensure that the --kubelet-https argument is set to true (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: bin_op: or test_items: - flag: "--kubelet-https" compare: op: eq value: true - flag: "--kubelet-https" set: false remediation: | Edit the API server pod specification file $apiserverconf on the master node and remove the --kubelet-https parameter. scored: true - id: 1.2.5 text: "Ensure that the --kubelet-client-certificate and --kubelet-client-key arguments are set as appropriate (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: bin_op: and test_items: - flag: "--kubelet-client-certificate" - flag: "--kubelet-client-key" remediation: | Follow the Kubernetes documentation and set up the TLS connection between the apiserver and kubelets. Then, edit API server pod specification file $apiserverconf on the master node and set the kubelet client certificate and key parameters as below. --kubelet-client-certificate= --kubelet-client-key= scored: true - id: 1.2.6 text: "Ensure that the --kubelet-certificate-authority argument is set as appropriate (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--kubelet-certificate-authority" remediation: | Follow the Kubernetes documentation and setup the TLS connection between the apiserver and kubelets. Then, edit the API server pod specification file $apiserverconf on the master node and set the --kubelet-certificate-authority parameter to the path to the cert file for the certificate authority. --kubelet-certificate-authority= scored: true - id: 1.2.7 text: "Ensure that the --authorization-mode argument is not set to AlwaysAllow (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--authorization-mode" compare: op: nothave value: "AlwaysAllow" remediation: | Edit the API server pod specification file $apiserverconf on the master node and set the --authorization-mode parameter to values other than AlwaysAllow. One such example could be as below. --authorization-mode=RBAC scored: true - id: 1.2.8 text: "Ensure that the --authorization-mode argument includes Node (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--authorization-mode" compare: op: has value: "Node" remediation: | Edit the API server pod specification file $apiserverconf on the master node and set the --authorization-mode parameter to a value that includes Node. --authorization-mode=Node,RBAC scored: true - id: 1.2.9 text: "Ensure that the --authorization-mode argument includes RBAC (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--authorization-mode" compare: op: has value: "RBAC" remediation: | Edit the API server pod specification file $apiserverconf on the master node and set the --authorization-mode parameter to a value that includes RBAC, for example: --authorization-mode=Node,RBAC scored: true - id: 1.2.10 text: "Ensure that the admission control plugin EventRateLimit is set (Manual)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--enable-admission-plugins" compare: op: has value: "EventRateLimit" remediation: | Follow the Kubernetes documentation and set the desired limits in a configuration file. Then, edit the API server pod specification file $apiserverconf and set the below parameters. --enable-admission-plugins=...,EventRateLimit,... --admission-control-config-file= scored: false - id: 1.2.11 text: "Ensure that the admission control plugin AlwaysAdmit is not set (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: bin_op: or test_items: - flag: "--enable-admission-plugins" compare: op: nothave value: AlwaysAdmit - flag: "--enable-admission-plugins" set: false remediation: | Edit the API server pod specification file $apiserverconf on the master node and either remove the --enable-admission-plugins parameter, or set it to a value that does not include AlwaysAdmit. scored: true - id: 1.2.12 text: "Ensure that the admission control plugin AlwaysPullImages is set (Manual)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--enable-admission-plugins" compare: op: has value: "AlwaysPullImages" remediation: | Edit the API server pod specification file $apiserverconf on the master node and set the --enable-admission-plugins parameter to include AlwaysPullImages. --enable-admission-plugins=...,AlwaysPullImages,... scored: false - id: 1.2.13 text: "Ensure that the admission control plugin SecurityContextDeny is set if PodSecurityPolicy is not used (Manual)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: bin_op: or test_items: - flag: "--enable-admission-plugins" compare: op: has value: "SecurityContextDeny" - flag: "--enable-admission-plugins" compare: op: has value: "PodSecurityPolicy" remediation: | Edit the API server pod specification file $apiserverconf on the master node and set the --enable-admission-plugins parameter to include SecurityContextDeny, unless PodSecurityPolicy is already in place. --enable-admission-plugins=...,SecurityContextDeny,... scored: false - id: 1.2.14 text: "Ensure that the admission control plugin ServiceAccount is set (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: bin_op: or test_items: - flag: "--disable-admission-plugins" compare: op: nothave value: "ServiceAccount" - flag: "--disable-admission-plugins" set: false remediation: | Follow the documentation and create ServiceAccount objects as per your environment. Then, edit the API server pod specification file $apiserverconf on the master node and ensure that the --disable-admission-plugins parameter is set to a value that does not include ServiceAccount. scored: true - id: 1.2.15 text: "Ensure that the admission control plugin NamespaceLifecycle is set (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: bin_op: or test_items: - flag: "--disable-admission-plugins" compare: op: nothave value: "NamespaceLifecycle" - flag: "--disable-admission-plugins" set: false remediation: | Edit the API server pod specification file $apiserverconf on the master node and set the --disable-admission-plugins parameter to ensure it does not include NamespaceLifecycle. scored: true - id: 1.2.16 text: "Ensure that the admission control plugin PodSecurityPolicy is set (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--enable-admission-plugins" compare: op: has value: "PodSecurityPolicy" remediation: | Follow the documentation and create Pod Security Policy objects as per your environment. Then, edit the API server pod specification file $apiserverconf on the master node and set the --enable-admission-plugins parameter to a value that includes PodSecurityPolicy: --enable-admission-plugins=...,PodSecurityPolicy,... Then restart the API Server. scored: true - id: 1.2.17 text: "Ensure that the admission control plugin NodeRestriction is set (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--enable-admission-plugins" compare: op: has value: "NodeRestriction" remediation: | Follow the Kubernetes documentation and configure NodeRestriction plug-in on kubelets. Then, edit the API server pod specification file $apiserverconf on the master node and set the --enable-admission-plugins parameter to a value that includes NodeRestriction. --enable-admission-plugins=...,NodeRestriction,... scored: true - id: 1.2.18 text: "Ensure that the --insecure-bind-address argument is not set (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--insecure-bind-address" set: false remediation: | Edit the API server pod specification file $apiserverconf on the master node and remove the --insecure-bind-address parameter. scored: true - id: 1.2.19 text: "Ensure that the --insecure-port argument is set to 0 (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--insecure-port" compare: op: eq value: 0 remediation: | Edit the API server pod specification file $apiserverconf on the master node and set the below parameter. --insecure-port=0 scored: true - id: 1.2.20 text: "Ensure that the --secure-port argument is not set to 0 (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: bin_op: or test_items: - flag: "--secure-port" compare: op: gt value: 0 - flag: "--secure-port" set: false remediation: | Edit the API server pod specification file $apiserverconf on the master node and either remove the --secure-port parameter or set it to a different (non-zero) desired port. scored: true - id: 1.2.21 text: "Ensure that the --profiling argument is set to false (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--profiling" compare: op: eq value: false remediation: | Edit the API server pod specification file $apiserverconf on the master node and set the below parameter. --profiling=false scored: true - id: 1.2.22 text: "Ensure that the --audit-log-path argument is set (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--audit-log-path" remediation: | Edit the API server pod specification file $apiserverconf on the master node and set the --audit-log-path parameter to a suitable path and file where you would like audit logs to be written, for example: --audit-log-path=/var/log/apiserver/audit.log scored: true - id: 1.2.23 text: "Ensure that the --audit-log-maxage argument is set to 30 or as appropriate (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--audit-log-maxage" compare: op: gte value: 30 remediation: | Edit the API server pod specification file $apiserverconf on the master node and set the --audit-log-maxage parameter to 30 or as an appropriate number of days: --audit-log-maxage=30 scored: true - id: 1.2.24 text: "Ensure that the --audit-log-maxbackup argument is set to 10 or as appropriate (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--audit-log-maxbackup" compare: op: gte value: 10 remediation: | Edit the API server pod specification file $apiserverconf on the master node and set the --audit-log-maxbackup parameter to 10 or to an appropriate value. --audit-log-maxbackup=10 scored: true - id: 1.2.25 text: "Ensure that the --audit-log-maxsize argument is set to 100 or as appropriate (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--audit-log-maxsize" compare: op: gte value: 100 remediation: | Edit the API server pod specification file $apiserverconf on the master node and set the --audit-log-maxsize parameter to an appropriate size in MB. For example, to set it as 100 MB: --audit-log-maxsize=100 scored: true - id: 1.2.26 text: "Ensure that the --request-timeout argument is set as appropriate (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" type: manual remediation: | Edit the API server pod specification file $apiserverconf and set the below parameter as appropriate and if needed. For example, --request-timeout=300s scored: true - id: 1.2.27 text: "Ensure that the --service-account-lookup argument is set to true (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: bin_op: or test_items: - flag: "--service-account-lookup" set: false - flag: "--service-account-lookup" compare: op: eq value: true remediation: | Edit the API server pod specification file $apiserverconf on the master node and set the below parameter. --service-account-lookup=true Alternatively, you can delete the --service-account-lookup parameter from this file so that the default takes effect. scored: true - id: 1.2.28 text: "Ensure that the --service-account-key-file argument is set as appropriate (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--service-account-key-file" remediation: | Edit the API server pod specification file $apiserverconf on the master node and set the --service-account-key-file parameter to the public key file for service accounts: --service-account-key-file= scored: true - id: 1.2.29 text: "Ensure that the --etcd-certfile and --etcd-keyfile arguments are set as appropriate (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: bin_op: and test_items: - flag: "--etcd-certfile" - flag: "--etcd-keyfile" remediation: | Follow the Kubernetes documentation and set up the TLS connection between the apiserver and etcd. Then, edit the API server pod specification file $apiserverconf on the master node and set the etcd certificate and key file parameters. --etcd-certfile= --etcd-keyfile= scored: true - id: 1.2.30 text: "Ensure that the --tls-cert-file and --tls-private-key-file arguments are set as appropriate (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: bin_op: and test_items: - flag: "--tls-cert-file" - flag: "--tls-private-key-file" remediation: | Follow the Kubernetes documentation and set up the TLS connection on the apiserver. Then, edit the API server pod specification file $apiserverconf on the master node and set the TLS certificate and private key file parameters. --tls-cert-file= --tls-private-key-file= scored: true - id: 1.2.31 text: "Ensure that the --client-ca-file argument is set as appropriate (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--client-ca-file" remediation: | Follow the Kubernetes documentation and set up the TLS connection on the apiserver. Then, edit the API server pod specification file $apiserverconf on the master node and set the client certificate authority file. --client-ca-file= scored: true - id: 1.2.32 text: "Ensure that the --etcd-cafile argument is set as appropriate (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--etcd-cafile" remediation: | Follow the Kubernetes documentation and set up the TLS connection between the apiserver and etcd. Then, edit the API server pod specification file $apiserverconf on the master node and set the etcd certificate authority file parameter. --etcd-cafile= scored: true - id: 1.2.33 text: "Ensure that the --encryption-provider-config argument is set as appropriate (Manual)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--encryption-provider-config" remediation: | Follow the Kubernetes documentation and configure a EncryptionConfig file. Then, edit the API server pod specification file $apiserverconf on the master node and set the --encryption-provider-config parameter to the path of that file: --encryption-provider-config= scored: false - id: 1.2.34 text: "Ensure that encryption providers are appropriately configured (Manual)" audit: | ENCRYPTION_PROVIDER_CONFIG=$(ps -ef | grep $apiserverbin | grep -- --encryption-provider-config | sed 's%.*encryption-provider-config[= ]\([^ ]*\).*%\1%') if test -e $ENCRYPTION_PROVIDER_CONFIG; then grep -A1 'providers:' $ENCRYPTION_PROVIDER_CONFIG | tail -n1 | grep -o "[A-Za-z]*" | sed 's/^/provider=/'; fi tests: test_items: - flag: "provider" compare: op: valid_elements value: "aescbc,kms,secretbox" remediation: | Follow the Kubernetes documentation and configure a EncryptionConfig file. In this file, choose aescbc, kms or secretbox as the encryption provider. scored: false - id: 1.2.35 text: "Ensure that the API Server only makes use of Strong Cryptographic Ciphers (Manual)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--tls-cipher-suites" compare: op: valid_elements value: "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" remediation: | Edit the API server pod specification file /etc/kubernetes/manifests/kube-apiserver.yaml on the master node and set the below parameter. --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 scored: false - id: 1.3 text: "Controller Manager" checks: - id: 1.3.1 text: "Ensure that the --terminated-pod-gc-threshold argument is set as appropriate (Manual)" audit: "/bin/ps -ef | grep $controllermanagerbin | grep -v grep" tests: test_items: - flag: "--terminated-pod-gc-threshold" remediation: | Edit the Controller Manager pod specification file $controllermanagerconf on the master node and set the --terminated-pod-gc-threshold to an appropriate threshold, for example: --terminated-pod-gc-threshold=10 scored: false - id: 1.3.2 text: "Ensure that the --profiling argument is set to false (Automated)" audit: "/bin/ps -ef | grep $controllermanagerbin | grep -v grep" tests: test_items: - flag: "--profiling" compare: op: eq value: false remediation: | Edit the Controller Manager pod specification file $controllermanagerconf on the master node and set the below parameter. --profiling=false scored: true - id: 1.3.3 text: "Ensure that the --use-service-account-credentials argument is set to true (Automated)" audit: "/bin/ps -ef | grep $controllermanagerbin | grep -v grep" tests: test_items: - flag: "--use-service-account-credentials" compare: op: noteq value: false remediation: | Edit the Controller Manager pod specification file $controllermanagerconf on the master node to set the below parameter. --use-service-account-credentials=true scored: true - id: 1.3.4 text: "Ensure that the --service-account-private-key-file argument is set as appropriate (Automated)" audit: "/bin/ps -ef | grep $controllermanagerbin | grep -v grep" tests: test_items: - flag: "--service-account-private-key-file" remediation: | Edit the Controller Manager pod specification file $controllermanagerconf on the master node and set the --service-account-private-key-file parameter to the private key file for service accounts. --service-account-private-key-file= scored: true - id: 1.3.5 text: "Ensure that the --root-ca-file argument is set as appropriate (Automated)" audit: "/bin/ps -ef | grep $controllermanagerbin | grep -v grep" tests: test_items: - flag: "--root-ca-file" remediation: | Edit the Controller Manager pod specification file $controllermanagerconf on the master node and set the --root-ca-file parameter to the certificate bundle file`. --root-ca-file= scored: true - id: 1.3.6 text: "Ensure that the RotateKubeletServerCertificate argument is set to true (Automated)" audit: "/bin/ps -ef | grep $controllermanagerbin | grep -v grep" tests: bin_op: or test_items: - flag: "--feature-gates" compare: op: nothave value: "RotateKubeletServerCertificate=false" set: true - flag: "--feature-gates" set: false remediation: | Edit the Controller Manager pod specification file $controllermanagerconf on the master node and set the --feature-gates parameter to include RotateKubeletServerCertificate=true. --feature-gates=RotateKubeletServerCertificate=true scored: true - id: 1.3.7 text: "Ensure that the --bind-address argument is set to 127.0.0.1 (Automated)" audit: "/bin/ps -ef | grep $controllermanagerbin | grep -v grep" tests: bin_op: or test_items: - flag: "--bind-address" compare: op: eq value: "127.0.0.1" - flag: "--bind-address" set: false remediation: | Edit the Controller Manager pod specification file $controllermanagerconf on the master node and ensure the correct value for the --bind-address parameter scored: true - id: 1.4 text: "Scheduler" checks: - id: 1.4.1 text: "Ensure that the --profiling argument is set to false (Automated)" audit: "/bin/ps -ef | grep $schedulerbin | grep -v grep" tests: test_items: - flag: "--profiling" compare: op: eq value: false remediation: | Edit the Scheduler pod specification file $schedulerconf file on the master node and set the below parameter. --profiling=false scored: true - id: 1.4.2 text: "Ensure that the --bind-address argument is set to 127.0.0.1 (Automated)" audit: "/bin/ps -ef | grep $schedulerbin | grep -v grep" tests: bin_op: or test_items: - flag: "--bind-address" compare: op: eq value: "127.0.0.1" - flag: "--bind-address" set: false remediation: | Edit the Scheduler pod specification file $schedulerconf on the master node and ensure the correct value for the --bind-address parameter scored: true ================================================ FILE: cfg/cis-1.6/node.yaml ================================================ --- controls: version: "cis-1.6" id: 4 text: "Worker Node Security Configuration" type: "node" groups: - id: 4.1 text: "Worker Node Configuration Files" checks: - id: 4.1.1 text: "Ensure that the kubelet service file permissions are set to 644 or more restrictive (Automated)" audit: '/bin/sh -c ''if test -e $kubeletsvc; then stat -c permissions=%a $kubeletsvc; fi'' ' tests: test_items: - flag: "permissions" compare: op: bitmask value: "644" remediation: | Run the below command (based on the file location on your system) on the each worker node. For example, chmod 644 $kubeletsvc scored: true - id: 4.1.2 text: "Ensure that the kubelet service file ownership is set to root:root (Automated)" audit: '/bin/sh -c "if test -e $kubeletsvc; then stat -c %U:%G $kubeletsvc; else echo \"File not found\"; fi"' tests: bin_op: or test_items: - flag: root:root - flag: "File not found" remediation: | Run the below command (based on the file location on your system) on the each worker node. For example, chown root:root $kubeletsvc scored: true - id: 4.1.3 text: "If proxy kubeconfig file exists ensure permissions are set to 644 or more restrictive (Manual)" audit: '/bin/sh -c ''if test -e $proxykubeconfig; then stat -c permissions=%a $proxykubeconfig; fi'' ' tests: bin_op: or test_items: - flag: "permissions" set: true compare: op: bitmask value: "644" - flag: "$proxykubeconfig" set: false remediation: | Run the below command (based on the file location on your system) on the each worker node. For example, chmod 644 $proxykubeconfig scored: false - id: 4.1.4 text: "Ensure that the proxy kubeconfig file ownership is set to root:root (Manual)" audit: '/bin/sh -c ''if test -e $proxykubeconfig; then stat -c %U:%G $proxykubeconfig; fi'' ' tests: bin_op: or test_items: - flag: root:root - flag: "$proxykubeconfig" set: false remediation: | Run the below command (based on the file location on your system) on the each worker node. For example, chown root:root $proxykubeconfig scored: false - id: 4.1.5 text: "Ensure that the --kubeconfig kubelet.conf file permissions are set to 644 or more restrictive (Automated)" audit: '/bin/sh -c ''if test -e $kubeletkubeconfig; then stat -c permissions=%a $kubeletkubeconfig; fi'' ' tests: test_items: - flag: "permissions" compare: op: bitmask value: "644" remediation: | Run the below command (based on the file location on your system) on the each worker node. For example, chmod 644 $kubeletkubeconfig scored: true - id: 4.1.6 text: "Ensure that the --kubeconfig kubelet.conf file ownership is set to root:root (Manual)" audit: '/bin/sh -c ''if test -e $kubeletkubeconfig; then stat -c %U:%G $kubeletkubeconfig; fi'' ' tests: test_items: - flag: root:root remediation: | Run the below command (based on the file location on your system) on the each worker node. For example, chown root:root $kubeletkubeconfig scored: false - id: 4.1.7 text: "Ensure that the certificate authorities file permissions are set to 644 or more restrictive (Manual)" audit: | CAFILE=$(ps -ef | grep kubelet | grep -v apiserver | grep -- --client-ca-file= | awk -F '--client-ca-file=' '{print $2}' | awk '{print $1}' | uniq) if test -z $CAFILE; then CAFILE=$kubeletcafile; fi if test -e $CAFILE; then stat -c permissions=%a $CAFILE; fi tests: test_items: - flag: "permissions" compare: op: bitmask value: "644" remediation: | Run the following command to modify the file permissions of the --client-ca-file chmod 644 scored: false - id: 4.1.8 text: "Ensure that the client certificate authorities file ownership is set to root:root (Manual)" audit: | CAFILE=$(ps -ef | grep kubelet | grep -v apiserver | grep -- --client-ca-file= | awk -F '--client-ca-file=' '{print $2}' | awk '{print $1}' | uniq) if test -z $CAFILE; then CAFILE=$kubeletcafile; fi if test -e $CAFILE; then stat -c %U:%G $CAFILE; fi tests: test_items: - flag: root:root compare: op: eq value: root:root remediation: | Run the following command to modify the ownership of the --client-ca-file. chown root:root scored: false - id: 4.1.9 text: "Ensure that the kubelet --config configuration file has permissions set to 644 or more restrictive (Automated)" audit: '/bin/sh -c ''if test -e $kubeletconf; then stat -c permissions=%a $kubeletconf; fi'' ' tests: test_items: - flag: "permissions" compare: op: bitmask value: "644" remediation: | Run the following command (using the config file location identified in the Audit step) chmod 644 $kubeletconf scored: true - id: 4.1.10 text: "Ensure that the kubelet --config configuration file ownership is set to root:root (Automated)" audit: '/bin/sh -c ''if test -e $kubeletconf; then stat -c %U:%G $kubeletconf; fi'' ' tests: test_items: - flag: root:root remediation: | Run the following command (using the config file location identified in the Audit step) chown root:root $kubeletconf scored: true - id: 4.2 text: "Kubelet" checks: - id: 4.2.1 text: "Ensure that the anonymous-auth argument is set to false (Automated)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: "--anonymous-auth" path: '{.authentication.anonymous.enabled}' compare: op: eq value: false remediation: | If using a Kubelet config file, edit the file to set authentication: anonymous: enabled to false. If using executable arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_SYSTEM_PODS_ARGS variable. --anonymous-auth=false Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: true - id: 4.2.2 text: "Ensure that the --authorization-mode argument is not set to AlwaysAllow (Automated)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --authorization-mode path: '{.authorization.mode}' compare: op: nothave value: AlwaysAllow remediation: | If using a Kubelet config file, edit the file to set authorization: mode to Webhook. If using executable arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_AUTHZ_ARGS variable. --authorization-mode=Webhook Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: true - id: 4.2.3 text: "Ensure that the --client-ca-file argument is set as appropriate (Automated)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --client-ca-file path: '{.authentication.x509.clientCAFile}' remediation: | If using a Kubelet config file, edit the file to set authentication: x509: clientCAFile to the location of the client CA file. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_AUTHZ_ARGS variable. --client-ca-file= Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: true - id: 4.2.4 text: "Ensure that the --read-only-port argument is set to 0 (Manual)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: bin_op: or test_items: - flag: "--read-only-port" path: '{.readOnlyPort}' compare: op: eq value: 0 - flag: "--read-only-port" path: '{.readOnlyPort}' set: false remediation: | If using a Kubelet config file, edit the file to set readOnlyPort to 0. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_SYSTEM_PODS_ARGS variable. --read-only-port=0 Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: false - id: 4.2.5 text: "Ensure that the --streaming-connection-idle-timeout argument is not set to 0 (Manual)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --streaming-connection-idle-timeout path: '{.streamingConnectionIdleTimeout}' compare: op: noteq value: 0 - flag: --streaming-connection-idle-timeout path: '{.streamingConnectionIdleTimeout}' set: false bin_op: or remediation: | If using a Kubelet config file, edit the file to set streamingConnectionIdleTimeout to a value other than 0. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_SYSTEM_PODS_ARGS variable. --streaming-connection-idle-timeout=5m Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: false - id: 4.2.6 text: "Ensure that the --protect-kernel-defaults argument is set to true (Automated)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --protect-kernel-defaults path: '{.protectKernelDefaults}' compare: op: eq value: true remediation: | If using a Kubelet config file, edit the file to set protectKernelDefaults: true. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_SYSTEM_PODS_ARGS variable. --protect-kernel-defaults=true Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: true - id: 4.2.7 text: "Ensure that the --make-iptables-util-chains argument is set to true (Automated)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --make-iptables-util-chains path: '{.makeIPTablesUtilChains}' compare: op: eq value: true - flag: --make-iptables-util-chains path: '{.makeIPTablesUtilChains}' set: false bin_op: or remediation: | If using a Kubelet config file, edit the file to set makeIPTablesUtilChains: true. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and remove the --make-iptables-util-chains argument from the KUBELET_SYSTEM_PODS_ARGS variable. Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: true - id: 4.2.8 text: "Ensure that the --hostname-override argument is not set (Manual)" # This is one of those properties that can only be set as a command line argument. # To check if the property is set as expected, we need to parse the kubelet command # instead reading the Kubelet Configuration file. audit: "/bin/ps -fC $kubeletbin " tests: test_items: - flag: --hostname-override set: false remediation: | Edit the kubelet service file $kubeletsvc on each worker node and remove the --hostname-override argument from the KUBELET_SYSTEM_PODS_ARGS variable. Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: false - id: 4.2.9 text: "Ensure that the --event-qps argument is set to 0 or a level which ensures appropriate event capture (Manual)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --event-qps path: '{.eventRecordQPS}' compare: op: eq value: 0 remediation: | If using a Kubelet config file, edit the file to set eventRecordQPS: to an appropriate level. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_SYSTEM_PODS_ARGS variable. Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: false - id: 4.2.10 text: "Ensure that the --tls-cert-file and --tls-private-key-file arguments are set as appropriate (Manual)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --tls-cert-file path: '{.tlsCertFile}' - flag: --tls-private-key-file path: '{.tlsPrivateKeyFile}' remediation: | If using a Kubelet config file, edit the file to set tlsCertFile to the location of the certificate file to use to identify this Kubelet, and tlsPrivateKeyFile to the location of the corresponding private key file. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameters in KUBELET_CERTIFICATE_ARGS variable. --tls-cert-file= --tls-private-key-file= Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: false - id: 4.2.11 text: "Ensure that the --rotate-certificates argument is not set to false (Manual)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --rotate-certificates path: '{.rotateCertificates}' compare: op: eq value: true - flag: --rotate-certificates path: '{.rotateCertificates}' set: false bin_op: or remediation: | If using a Kubelet config file, edit the file to add the line rotateCertificates: true or remove it altogether to use the default value. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and remove --rotate-certificates=false argument from the KUBELET_CERTIFICATE_ARGS variable. Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: false - id: 4.2.12 text: "Verify that the RotateKubeletServerCertificate argument is set to true (Manual)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: bin_op: or test_items: - flag: RotateKubeletServerCertificate path: '{.featureGates.RotateKubeletServerCertificate}' compare: op: nothave value: false - flag: RotateKubeletServerCertificate path: '{.featureGates.RotateKubeletServerCertificate}' set: false remediation: | Edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_CERTIFICATE_ARGS variable. --feature-gates=RotateKubeletServerCertificate=true Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: false - id: 4.2.13 text: "Ensure that the Kubelet only makes use of Strong Cryptographic Ciphers (Manual)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --tls-cipher-suites path: '{range .tlsCipherSuites[:]}{}{'',''}{end}' compare: op: valid_elements value: 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 remediation: | If using a Kubelet config file, edit the file to set tlsCipherSuites: to 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 or to a subset of these values. If using executable arguments, edit the kubelet service file $kubeletsvc on each worker node and set the --tls-cipher-suites parameter as follows, or to a subset of these values. --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 Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: false ================================================ FILE: cfg/cis-1.6/policies.yaml ================================================ --- controls: version: "cis-1.6" id: 5 text: "Kubernetes Policies" type: "policies" groups: - id: 5.1 text: "RBAC and Service Accounts" checks: - id: 5.1.1 text: "Ensure that the cluster-admin role is only used where required (Manual)" type: "manual" remediation: | Identify all clusterrolebindings to the cluster-admin role. Check if they are used and if they need this role or if they could use a role with fewer privileges. Where possible, first bind users to a lower privileged role and then remove the clusterrolebinding to the cluster-admin role : kubectl delete clusterrolebinding [name] scored: false - id: 5.1.2 text: "Minimize access to secrets (Manual)" type: "manual" remediation: | Where possible, remove get, list and watch access to secret objects in the cluster. scored: false - id: 5.1.3 text: "Minimize wildcard use in Roles and ClusterRoles (Manual)" type: "manual" remediation: | Where possible replace any use of wildcards in clusterroles and roles with specific objects or actions. scored: false - id: 5.1.4 text: "Minimize access to create pods (Manual)" type: "manual" remediation: | Where possible, remove create access to pod objects in the cluster. scored: false - id: 5.1.5 text: "Ensure that default service accounts are not actively used. (Manual)" type: "manual" remediation: | Create explicit service accounts wherever a Kubernetes workload requires specific access to the Kubernetes API server. Modify the configuration of each default service account to include this value automountServiceAccountToken: false scored: false - id: 5.1.6 text: "Ensure that Service Account Tokens are only mounted where necessary (Manual)" type: "manual" remediation: | Modify the definition of pods and service accounts which do not need to mount service account tokens to disable it. scored: false - id: 5.2 text: "Pod Security Policies" checks: - id: 5.2.1 text: "Minimize the admission of privileged containers (Manual)" type: "manual" remediation: | Create a PSP as described in the Kubernetes documentation, ensuring that the .spec.privileged field is omitted or set to false. scored: false - id: 5.2.2 text: "Minimize the admission of containers wishing to share the host process ID namespace (Manual)" type: "manual" remediation: | Create a PSP as described in the Kubernetes documentation, ensuring that the .spec.hostPID field is omitted or set to false. scored: false - id: 5.2.3 text: "Minimize the admission of containers wishing to share the host IPC namespace (Manual)" type: "manual" remediation: | Create a PSP as described in the Kubernetes documentation, ensuring that the .spec.hostIPC field is omitted or set to false. scored: false - id: 5.2.4 text: "Minimize the admission of containers wishing to share the host network namespace (Manual)" type: "manual" remediation: | Create a PSP as described in the Kubernetes documentation, ensuring that the .spec.hostNetwork field is omitted or set to false. scored: false - id: 5.2.5 text: "Minimize the admission of containers with allowPrivilegeEscalation (Manual)" type: "manual" remediation: | Create a PSP as described in the Kubernetes documentation, ensuring that the .spec.allowPrivilegeEscalation field is omitted or set to false. scored: false - id: 5.2.6 text: "Minimize the admission of root containers (Manual)" type: "manual" remediation: | Create a PSP as described in the Kubernetes documentation, ensuring that the .spec.runAsUser.rule is set to either MustRunAsNonRoot or MustRunAs with the range of UIDs not including 0. scored: false - id: 5.2.7 text: "Minimize the admission of containers with the NET_RAW capability (Manual)" type: "manual" remediation: | Create a PSP as described in the Kubernetes documentation, ensuring that the .spec.requiredDropCapabilities is set to include either NET_RAW or ALL. scored: false - id: 5.2.8 text: "Minimize the admission of containers with added capabilities (Manual)" type: "manual" remediation: | Ensure that allowedCapabilities is not present in PSPs for the cluster unless it is set to an empty array. scored: false - id: 5.2.9 text: "Minimize the admission of containers with capabilities assigned (Manual)" type: "manual" remediation: | Review the use of capabilites in applications running on your cluster. Where a namespace contains applications which do not require any Linux capabities to operate consider adding a PSP which forbids the admission of containers which do not drop all capabilities. scored: false - id: 5.3 text: "Network Policies and CNI" checks: - id: 5.3.1 text: "Ensure that the CNI in use supports Network Policies (Manual)" type: "manual" remediation: | If the CNI plugin in use does not support network policies, consideration should be given to making use of a different plugin, or finding an alternate mechanism for restricting traffic in the Kubernetes cluster. scored: false - id: 5.3.2 text: "Ensure that all Namespaces have Network Policies defined (Manual)" type: "manual" remediation: | Follow the documentation and create NetworkPolicy objects as you need them. scored: false - id: 5.4 text: "Secrets Management" checks: - id: 5.4.1 text: "Prefer using secrets as files over secrets as environment variables (Manual)" type: "manual" remediation: | if possible, rewrite application code to read secrets from mounted secret files, rather than from environment variables. scored: false - id: 5.4.2 text: "Consider external secret storage (Manual)" type: "manual" remediation: | Refer to the secrets management options offered by your cloud provider or a third-party secrets management solution. scored: false - id: 5.5 text: "Extensible Admission Control" checks: - id: 5.5.1 text: "Configure Image Provenance using ImagePolicyWebhook admission controller (Manual)" type: "manual" remediation: | Follow the Kubernetes documentation and setup image provenance. scored: false - id: 5.7 text: "General Policies" checks: - id: 5.7.1 text: "Create administrative boundaries between resources using namespaces (Manual)" type: "manual" remediation: | Follow the documentation and create namespaces for objects in your deployment as you need them. scored: false - id: 5.7.2 text: "Ensure that the seccomp profile is set to docker/default in your pod definitions (Manual)" type: "manual" remediation: | Seccomp is an alpha feature currently. By default, all alpha features are disabled. So, you would need to enable alpha features in the apiserver by passing "--feature- gates=AllAlpha=true" argument. Edit the /etc/kubernetes/apiserver file on the master node and set the KUBE_API_ARGS parameter to "--feature-gates=AllAlpha=true" KUBE_API_ARGS="--feature-gates=AllAlpha=true" Based on your system, restart the kube-apiserver service. For example: systemctl restart kube-apiserver.service Use annotations to enable the docker/default seccomp profile in your pod definitions. An example is as below: apiVersion: v1 kind: Pod metadata: name: trustworthy-pod annotations: seccomp.security.alpha.kubernetes.io/pod: docker/default spec: containers: - name: trustworthy-container image: sotrustworthy:latest scored: false - id: 5.7.3 text: "Apply Security Context to Your Pods and Containers (Manual)" type: "manual" remediation: | Follow the Kubernetes documentation and apply security contexts to your pods. For a suggested list of security contexts, you may refer to the CIS Security Benchmark for Docker Containers. scored: false - id: 5.7.4 text: "The default namespace should not be used (Manual)" type: "manual" remediation: | Ensure that namespaces are created to allow for appropriate segregation of Kubernetes resources and that all new resources are created in a specific namespace. scored: false ================================================ FILE: cfg/cis-1.6-k3s/config.yaml ================================================ --- ## Version-specific settings that override the values in cfg/config.yaml master: components: - scheduler - controllermanager - node scheduler: kubeconfig: - /var/lib/rancher/k3s/server/cred/scheduler.kubeconfig defaultkubeconfig: /var/lib/rancher/k3s/server/cred/scheduler.kubeconfig controllermanager: kubeconfig: - /var/lib/rancher/k3s/server/cred/cloud-controller.kubeconfig defaultkubeconfig: /var/lib/rancher/k3s/server/cred/cloud-controller.kubeconfig etcd: components: - etcd etcd: confs: - /var/lib/rancher/k3s/server/db/etcd/config defaultconf: /var/lib/rancher/k3s/server/db/etcd/config node: components: - proxy - kubelet proxy: kubeconfig: - "/var/lib/rancher/k3s/agent/kubeproxy.kubeconfig" defaultkubeconfig: "/var/lib/rancher/k3s/agent/kubeproxy.kubeconfig" kubelet: kubeconfig: - "/var/lib/rancher/k3s/agent/kubelet.kubeconfig" defaultkubeconfig: "/var/lib/rancher/k3s/agent/kubelet.kubeconfig" cafile: - "/var/lib/rancher/k3s/server/tls/server-ca.crt" defaultcafile: "/var/lib/rancher/k3s/server/tls/server-ca.crt" ================================================ FILE: cfg/cis-1.6-k3s/controlplane.yaml ================================================ --- controls: version: "cis-1.6-k3s" id: 3 text: "Control Plane Configuration" type: "controlplane" groups: - id: 3.1 text: "Authentication and Authorization" checks: - id: 3.1.1 text: "Client certificate authentication should not be used for users (Manual)" type: "manual" remediation: | Alternative mechanisms provided by Kubernetes such as the use of OIDC should be implemented in place of client certificates. scored: false - id: 3.2 text: "Logging" checks: - id: 3.2.1 text: "Ensure that a minimal audit policy is created (Automated)" audit: journalctl -u k3s | grep "Running kube-apiserver" | tail -n1 | grep "audit-policy-file" tests: test_items: - flag: "--audit-policy-file" set: true remediation: | Create an audit policy file for your cluster and pass it to k3s. e.g. --kube-apiserver-arg='audit-log-path=/var/lib/rancher/k3s/server/logs/audit-log' scored: true - id: 3.2.2 text: "Ensure that the audit policy covers key security concerns (Manual)" type: "manual" remediation: | Consider modification of the audit policy in use on the cluster to include these items, at a minimum. scored: false ================================================ FILE: cfg/cis-1.6-k3s/etcd.yaml ================================================ --- controls: version: "cis-1.6-k3s" id: 2 text: "Etcd Node Configuration" type: "etcd" groups: - id: 2 text: "Etcd Node Configuration Files" checks: - id: 2.1 text: "Ensure that the --cert-file and --key-file arguments are set as appropriate if use etcd as database (Automated)" audit: grep -A 4 'client-transport-security' $etcdconf | grep -E 'cert-file|key-file' tests: bin_op: and test_items: - flag: "cert-file" - flag: "key-file" remediation: | By default, K3s uses a config file for etcd that can be found at $etcdconf. The config file contains client-transport-security: which has fields that have the peer cert and peer key files. No manual remediation needed. scored: true - id: 2.2 text: "Ensure that the --client-cert-auth argument is set to true (Automated)" audit: grep 'client-cert-auth' $etcdconf tests: test_items: - flag: "client-cert-auth" compare: op: eq value: true remediation: | By default, K3s uses a config file for etcd that can be found at $etcdconf. client-cert-auth is set to true. No manual remediation needed. scored: true - id: 2.3 text: "Ensure that the --auto-tls argument is not set to true (Automated)" audit: grep 'auto-tls' $etcdconf | cat tests: bin_op: or test_items: - flag: "auto-tls" set: false - flag: "auto-tls" compare: op: eq value: false remediation: | By default, K3s starts Etcd without this flag. It is set to false by default. scored: true - id: 2.4 text: "Ensure that the --peer-cert-file and --peer-key-file arguments are set as appropriate (Automated)" audit: grep -A 4 'peer-transport-security' $etcdconf | grep -E 'cert-file|key-file' tests: bin_op: and test_items: - flag: "cert-file" - flag: "key-file" remediation: | By default, K3s starts Etcd with a config file found here, $etcdconf. The config file contains peer-transport-security: which has fields that have the peer cert and peer key files. scored: true - id: 2.5 text: "Ensure that the --peer-client-cert-auth argument is set to true (Automated)" audit: grep -A 4 'peer-transport-security' $etcdconf | grep 'client-cert-auth' tests: test_items: - flag: "client-cert-auth" compare: op: eq value: true remediation: | By default, K3s uses a config file for etcd that can be found at $etcdconf. The config file contains peer-transport-security: which has client-cert-auth set to true. No manual remediation needed. scored: true - id: 2.6 text: "Ensure that the --peer-auto-tls argument is not set to true (Automated)" audit: grep 'peer-auto-tls' $etcdconf | cat tests: bin_op: or test_items: - flag: "peer-auto-tls" set: false - flag: "peer-auto-tls" compare: op: eq value: false remediation: | By default, K3s uses a config file for etcd that can be found at $etcdconf. Within the file, it does not contain the peer-auto-tls field. No manual remediation needed. scored: true - id: 2.7 text: "Ensure that a unique Certificate Authority is used for etcd (Manual)" audit: | if [ -f "$etcdconf" ];then etcd_ca=$(grep 'trusted-ca-file' $etcdconf | awk -F ":|: *" '{print $NF}'); apiserver_ca=$(journalctl -u k3s | grep "Running kube-apiserver" | tail -n1 | grep "trusted-ca-file" | awk -F "=" '{print $NF}') if [ "$etcd_ca" == "$apiserver_ca" ]; then echo 'etcd_and_apiserver_have_same_ca'; else echo 'etcd_and_apiserver_ca_not_same1' ; fi else echo 'etcd_and_apiserver_ca_not_same'; return ; fi tests: test_items: - flag: "etcd_and_apiserver_ca_not_same" remediation: | By default, K3s uses a config file for etcd that can be found at $etcdconf and the trusted-ca-file parameters in it are set to unique values specific to etcd. No manual remediation needed. scored: false ================================================ FILE: cfg/cis-1.6-k3s/master.yaml ================================================ --- controls: version: "cis-1.6-k3s" id: 1 text: "Master Node Security Configuration" type: "master" groups: - id: 1.1 text: "Master Node Configuration Files" checks: - id: 1.1.1 text: "Ensure that the API server pod specification file permissions are set to 644 or more restrictive (Not Applicable)" scored: false - id: 1.1.2 text: "Ensure that the API server pod specification file ownership is set to root:root (Not Applicable)" scored: false - id: 1.1.3 text: "Ensure that the controller manager pod specification file permissions are set to 644 or more restrictive (Not Applicable)" scored: false - id: 1.1.4 text: "Ensure that the controller manager pod specification file ownership is set to root:root (Not Applicable)" scored: false - id: 1.1.5 text: "Ensure that the scheduler pod specification file permissions are set to 644 or more restrictive (Not Applicable)" scored: false - id: 1.1.6 text: "Ensure that the scheduler pod specification file ownership is set to root:root (Not Applicable)" scored: false - id: 1.1.7 text: "Ensure that the etcd pod specification file permissions are set to 644 or more restrictive (Not Applicable)" scored: false - id: 1.1.8 text: "Ensure that the etcd pod specification file ownership is set to root:root (Not Applicable)" scored: false - id: 1.1.9 text: "Ensure that the Container Network Interface file permissions are set to 644 or more restrictive (Not Applicable)" scored: false - id: 1.1.10 text: "Ensure that the Container Network Interface file ownership is set to root:root (Not Applicable)" scored: false - id: 1.1.11 text: "Ensure that the etcd data directory permissions are set to 700 or more restrictive if etcd is used (Automated)" audit: stat -c permissions=%a /var/lib/rancher/k3s/server/db/etcd tests: test_items: - flag: "permissions" compare: op: bitmask value: "700" remediation: | On the etcd server node, get the etcd data directory, passed as an argument --data-dir, from the below command: journalctl -u k3s | grep 'Managed etcd' | grep -v grep Run the below command (based on the etcd data directory found above). For example, chmod 700 /var/lib/rancher/k3s/server/db/etcd scored: true - id: 1.1.12 text: "Ensure that the etcd data directory ownership is set to etcd:etcd if etcd is used (Not Applicable)" scored: false - id: 1.1.13 text: "Ensure that the admin.kubeconfig file permissions are set to 644 or more restrictive (Automated)" audit: "/bin/sh -c 'if test -e /var/lib/rancher/k3s/server/cred/admin.kubeconfig; then stat -c permissions=%a /var/lib/rancher/k3s/server/cred/admin.kubeconfig; fi'" tests: test_items: - flag: "permissions" compare: op: bitmask value: "644" remediation: | Run the below command (based on the file location on your system) on the k3s node. For example, chmod 644 /var/lib/rancher/k3s/server/cred/admin.kubeconfig scored: true - id: 1.1.14 text: "Ensure that the admin.kubeconfig file ownership is set to root:root (Automated)" audit: "/bin/sh -c 'if test -e /var/lib/rancher/k3s/server/cred/admin.kubeconfig; then stat -c %U:%G /var/lib/rancher/k3s/server/cred/admin.kubeconfig; fi'" tests: test_items: - flag: "root:root" remediation: | Run the below command (based on the file location on your system) on the k3s node. For example, chown root:root /var/lib/rancher/k3s/server/cred/admin.kubeconfig scored: true - id: 1.1.15 text: "Ensure that the scheduler.kubeconfig file permissions are set to 644 or more restrictive (Automated)" audit: "/bin/sh -c 'if test -e $schedulerkubeconfig; then stat -c permissions=%a $schedulerkubeconfig; fi'" tests: test_items: - flag: "permissions" compare: op: bitmask value: "644" remediation: | Run the below command (based on the file location on your system) on the k3s node. For example, chmod 644 $schedulerkubeconfig scored: true - id: 1.1.16 text: "Ensure that the scheduler.kubeconfig file ownership is set to root:root (Automated)" audit: "/bin/sh -c 'if test -e $schedulerkubeconfig; then stat -c %U:%G $schedulerkubeconfig; fi'" tests: test_items: - flag: "root:root" remediation: | Run the below command (based on the file location on your system) on the k3s node. For example, chown root:root $schedulerkubeconfig scored: true - id: 1.1.17 text: "Ensure that the cloud-controller.kubeconfig file permissions are set to 644 or more restrictive (Automated)" audit: "/bin/sh -c 'if test -e $controllermanagerkubeconfig; then stat -c permissions=%a $controllermanagerkubeconfig; fi'" tests: test_items: - flag: "permissions" compare: op: bitmask value: "644" remediation: | Run the below command (based on the file location on your system) on the master node. For example, chmod 644 $controllermanagerkubeconfig scored: true - id: 1.1.18 text: "Ensure that the $controllermanagerkubeconfig file ownership is set to root:root (Automated)" audit: "/bin/sh -c 'if test -e $controllermanagerkubeconfig; then stat -c %U:%G $controllermanagerkubeconfig; fi'" tests: test_items: - flag: "root:root" remediation: | Run the below command (based on the file location on your system) on the master node. For example, chown root:root $controllermanagerkubeconfig scored: true - id: 1.1.19 text: "Ensure that the Kubernetes PKI directory and file ownership is set to root:root (Automated)" audit: "stat -c %U:%G /var/lib/rancher/k3s/server/tls" use_multiple_values: true tests: test_items: - flag: "root:root" remediation: | Run the below command (based on the file location on your system) on the k3s node. For example, chown -R root:root /var/lib/rancher/k3s/server/tls scored: true - id: 1.1.20 text: "Ensure that the Kubernetes PKI certificate file permissions are set to 644 or more restrictive (Automated)" audit: "find /var/lib/rancher/k3s/server/tls/ -name '*.crt' | xargs stat -c permissions=%a" use_multiple_values: true tests: test_items: - flag: "permissions" compare: op: bitmask value: "644" remediation: | Run the below command (based on the file location on your system) on the master node. For example, chmod -R 644 /var/lib/rancher/k3s/server/tls/*.crt scored: true - id: 1.1.21 text: "Ensure that the Kubernetes PKI key file permissions are set to 600 (Automated)" audit: "find /var/lib/rancher/k3s/server/tls/ -name '*.key' | xargs stat -c permissions=%a" use_multiple_values: true tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Run the below command (based on the file location on your system) on the master node. For example, chmod -R 600 /etc/kubernetes/pki/*.key scored: true - id: 1.2 text: "API Server" checks: - id: 1.2.1 text: "Ensure that the --anonymous-auth argument is set to false (Automated)" audit: journalctl -u k3s | grep "Running kube-apiserver" | tail -n1 | grep "anonymous-auth" tests: test_items: - flag: "--anonymous-auth" compare: op: eq value: false remediation: | By default, K3s kube-apiserver is configured to run with --anonymous-auth=false flag and value. scored: false - id: 1.2.2 text: "Ensure that the --basic-auth-file argument is not set (Automated)" audit: journalctl -u k3s | grep "Running kube-apiserver" | tail -n1 | grep "basic-auth-file" | cat tests: test_items: - flag: "--basic-auth-file" set: false remediation: | By default, K3s does not run with basic authentication enabled. No manual remediation is needed. scored: true - id: 1.2.3 text: "Ensure that the --token-auth-file parameter is not set (Automated)" audit: journalctl -u k3s | grep "Running kube-apiserver" | tail -n1 | grep "token-auth-file" | cat tests: test_items: - flag: "--token-auth-file" set: false remediation: | By default, K3s does not run with basic authentication enabled. No manual remediation is needed. scored: true - id: 1.2.4 text: "Ensure that the --kubelet-https argument is set to true (Automated)" audit: journalctl -u k3s | grep "Running kube-apiserver" | tail -n1 | grep "kubelet-https" | cat tests: bin_op: or test_items: - flag: "--kubelet-https" compare: op: eq value: true - flag: "--kubelet-https" set: false remediation: | By default, K3s kube-apiserver doesn't run with the --kubelet-https parameter as it runs with TLS. No manual remediation is needed. scored: true - id: 1.2.5 text: "Ensure that the --kubelet-client-certificate and --kubelet-client-key arguments are set as appropriate (Automated)" audit: journalctl -u k3s | grep "Running kube-apiserver" | tail -n1 | grep -E 'kubelet-client-certificate|kubelet-client-key' tests: bin_op: and test_items: - flag: "--kubelet-client-certificate" - flag: "--kubelet-client-key" remediation: | By default, K3s kube-apiserver is ran with these arguments for secure communication with kubelet. No manual remediation is needed. scored: true - id: 1.2.6 text: "Ensure that the --kubelet-certificate-authority argument is set as appropriate (Automated)" audit: journalctl -u k3s | grep "Running kube-apiserver" | tail -n1 | grep "kubelet-certificate-authority" tests: test_items: - flag: "--kubelet-certificate-authority" remediation: | By default, K3s kube-apiserver is ran with this argument for secure communication with kubelet. No manual remediation is needed. scored: true - id: 1.2.7 text: "Ensure that the --authorization-mode argument is not set to AlwaysAllow (Automated)" audit: journalctl -u k3s | grep "Running kube-apiserver" | tail -n1 | grep "authorization-mode" tests: test_items: - flag: "--authorization-mode" compare: op: nothave value: "AlwaysAllow" remediation: | By default, K3s sets Node,RBAC as the parameter to the --authorization-mode argument. No manual remediation is needed. scored: true - id: 1.2.8 text: "Ensure that the --authorization-mode argument includes Node (Automated)" audit: journalctl -u k3s | grep "Running kube-apiserver" | tail -n1 | grep "authorization-mode" tests: test_items: - flag: "--authorization-mode" compare: op: has value: "Node" remediation: | By default, K3s sets Node,RBAC as the parameter to the --authorization-mode argument. No manual remediation is needed. scored: true - id: 1.2.9 text: "Ensure that the --authorization-mode argument includes RBAC (Automated)" audit: journalctl -u k3s | grep "Running kube-apiserver" | tail -n1 | grep "authorization-mode" tests: test_items: - flag: "--authorization-mode" compare: op: has value: "RBAC" remediation: | By default, K3s sets Node,RBAC as the parameter to the --authorization-mode argument. No manual remediation is needed. scored: true - id: 1.2.10 text: "Ensure that the admission control plugin EventRateLimit is set (Manual)" audit: journalctl -u k3s | grep "Running kube-apiserver" | tail -n1 | grep "enable-admission-plugins" tests: test_items: - flag: "--enable-admission-plugins" compare: op: has value: "EventRateLimit" remediation: | By default, K3s only sets NodeRestriction,PodSecurityPolicy as the parameter to the --enable-admission-plugins argument. To configure this, follow the Kubernetes documentation and set the desired limits in a configuration file. Then refer to K3s's documentation to see how to supply additional api server configuration via the kube-apiserver-arg parameter. scored: false - id: 1.2.11 text: "Ensure that the admission control plugin AlwaysAdmit is not set (Automated)" audit: journalctl -u k3s | grep "Running kube-apiserver" | tail -n1 | grep "enable-admission-plugins" tests: bin_op: or test_items: - flag: "--enable-admission-plugins" compare: op: nothave value: AlwaysAdmit - flag: "--enable-admission-plugins" set: false remediation: | By default, K3s only sets NodeRestriction,PodSecurityPolicy as the parameter to the --enable-admission-plugins argument. No manual remediation needed. scored: true - id: 1.2.12 text: "Ensure that the admission control plugin AlwaysPullImages is set (Manual)" audit: journalctl -u k3s | grep "Running kube-apiserver" | tail -n1 | grep "enable-admission-plugins" tests: test_items: - flag: "--enable-admission-plugins" compare: op: has value: "AlwaysPullImages" remediation: | By default, K3s only sets NodeRestriction,PodSecurityPolicy as the parameter to the --enable-admission-plugins argument. To configure this, follow the Kubernetes documentation and set the desired limits in a configuration file. Then refer to K3s's documentation to see how to supply additional api server configuration via the kube-apiserver-arg parameter. scored: false - id: 1.2.13 text: "Ensure that the admission control plugin SecurityContextDeny is set if PodSecurityPolicy is not used (Manual)" audit: journalctl -u k3s | grep "Running kube-apiserver" | tail -n1 | grep "enable-admission-plugins" tests: bin_op: or test_items: - flag: "--enable-admission-plugins" compare: op: has value: "SecurityContextDeny" - flag: "--enable-admission-plugins" compare: op: has value: "PodSecurityPolicy" remediation: | K3s would need to have the SecurityContextDeny admission plugin enabled by passing it as an argument to K3s. --kube-apiserver-arg='enable-admission-plugins=SecurityContextDeny scored: false - id: 1.2.14 text: "Ensure that the admission control plugin ServiceAccount is set (Automated)" audit: journalctl -u k3s | grep "Running kube-apiserver" | tail -n1 | grep "ServiceAccount" tests: bin_op: or test_items: - flag: "--disable-admission-plugins" compare: op: nothave value: "ServiceAccount" - flag: "--disable-admission-plugins" set: false remediation: | By default, K3s does not use this argument. If there's a desire to use this argument, follow the documentation and create ServiceAccount objects as per your environment. Then refer to K3s's documentation to see how to supply additional api server configuration via the kube-apiserver-arg parameter. scored: true - id: 1.2.15 text: "Ensure that the admission control plugin NamespaceLifecycle is set (Automated)" audit: journalctl -u k3s | grep "Running kube-apiserver" | tail -n1 | grep "disable-admission-plugins" | cat tests: bin_op: or test_items: - flag: "--disable-admission-plugins" compare: op: nothave value: "NamespaceLifecycle" - flag: "--disable-admission-plugins" set: false remediation: | By default, K3s does not use this argument. No manual remediation needed. scored: true - id: 1.2.16 text: "Ensure that the admission control plugin PodSecurityPolicy is set (Automated)" audit: journalctl -u k3s | grep "Running kube-apiserver" | tail -n1 | grep "enable-admission-plugins" tests: test_items: - flag: "--enable-admission-plugins" compare: op: has value: "PodSecurityPolicy" remediation: | K3s would need to have the PodSecurityPolicy admission plugin enabled by passing it as an argument to K3s. --kube-apiserver-arg='enable-admission-plugins=PodSecurityPolicy. scored: true - id: 1.2.17 text: "Ensure that the admission control plugin NodeRestriction is set (Automated)" audit: journalctl -u k3s | grep "Running kube-apiserver" | tail -n1 | grep "enable-admission-plugins" tests: test_items: - flag: "--enable-admission-plugins" compare: op: has value: "NodeRestriction" remediation: | K3s would need to have the NodeRestriction admission plugin enabled by passing it as an argument to K3s. --kube-apiserver-arg='enable-admission-plugins=NodeRestriction. scored: true - id: 1.2.18 text: "Ensure that the --insecure-bind-address argument is not set (Automated)" audit: journalctl -u k3s | grep "Running kube-apiserver" | tail -n1 | grep "enable-admission-plugins" tests: test_items: - flag: "--insecure-bind-address" set: false remediation: | By default, K3s explicitly excludes the use of the --insecure-bind-address parameter. No manual remediation is needed. scored: true - id: 1.2.19 text: "Ensure that the --insecure-port argument is set to 0 (Automated)" audit: journalctl -u k3s | grep "Running kube-apiserver" | tail -n1 | grep "insecure-port" tests: test_items: - flag: "--insecure-port" compare: op: eq value: 0 remediation: | By default, K3s starts the kube-apiserver process with this argument's parameter set to 0. No manual remediation is needed. scored: true - id: 1.2.20 text: "Ensure that the --secure-port argument is not set to 0 (Automated)" audit: journalctl -u k3s | grep "Running kube-apiserver" | tail -n1 | grep "secure-port" tests: bin_op: or test_items: - flag: "--secure-port" compare: op: gt value: 0 - flag: "--secure-port" set: false remediation: | By default, K3s sets the parameter of 6444 for the --secure-port argument. No manual remediation is needed. scored: true - id: 1.2.21 text: "Ensure that the --profiling argument is set to false (Automated)" audit: journalctl -u k3s | grep "Running kube-apiserver" | tail -n1 | grep "profiling" tests: test_items: - flag: "--profiling" compare: op: eq value: false remediation: | By default, K3s sets the --profiling flag parameter to false. No manual remediation needed. scored: true - id: 1.2.22 text: "Ensure that the --audit-log-path argument is set (Automated)" audit: journalctl -u k3s | grep "Running kube-apiserver" | tail -n1 | grep "audit-log-path" tests: test_items: - flag: "--audit-log-path" remediation: | K3s server needs to be run with the following argument, --kube-apiserver-arg='audit-log-path=/path/to/log/file' scored: true - id: 1.2.23 text: "Ensure that the --audit-log-maxage argument is set to 30 or as appropriate (Automated)" audit: journalctl -u k3s | grep "Running kube-apiserver" | tail -n1 | grep "audit-log-maxage" tests: test_items: - flag: "--audit-log-maxage" compare: op: gte value: 30 remediation: | K3s server needs to be run with the following argument, --kube-apiserver-arg='audit-log-maxage=30' scored: true - id: 1.2.24 text: "Ensure that the --audit-log-maxbackup argument is set to 10 or as appropriate (Automated)" audit: journalctl -u k3s | grep "Running kube-apiserver" | tail -n1 | grep "audit-log-maxbackup" tests: test_items: - flag: "--audit-log-maxbackup" compare: op: gte value: 10 remediation: | K3s server needs to be run with the following argument, --kube-apiserver-arg='audit-log-maxbackup=10' scored: true - id: 1.2.25 text: "Ensure that the --audit-log-maxsize argument is set to 100 or as appropriate (Automated)" audit: journalctl -u k3s | grep "Running kube-apiserver" | tail -n1 | grep "audit-log-maxsize" tests: test_items: - flag: "--audit-log-maxsize" compare: op: gte value: 100 remediation: | K3s server needs to be run with the following argument, --kube-apiserver-arg='audit-log-maxsize=100' scored: true - id: 1.2.26 text: "Ensure that the --request-timeout argument is set as appropriate (Automated)" audit: journalctl -u k3s | grep "Running kube-apiserver" | tail -n1 | grep "request-timeout" | cat tests: bin_op: or test_items: - flag: "--request-timeout" set: false - flag: "--request-timeout" compare: op: lte value: 60 remediation: | By default, K3s does not set the --request-timeout argument. No manual remediation needed. scored: true - id: 1.2.27 text: "Ensure that the --service-account-lookup argument is set to true (Automated)" audit: journalctl -u k3s | grep "Running kube-apiserver" | tail -n1 | grep "service-account-lookup" tests: bin_op: or test_items: - flag: "--service-account-lookup" set: false - flag: "--service-account-lookup" compare: op: eq value: true remediation: | K3s server needs to be run with the following argument, --kube-apiserver-arg='service-account-lookup=true' scored: true - id: 1.2.28 text: "Ensure that the --service-account-key-file argument is set as appropriate (Automated)" audit: journalctl -u k3s | grep "Running kube-apiserver" | tail -n1 | grep "service-account-key-file" tests: test_items: - flag: "--service-account-key-file" remediation: | By default, K3s sets the --service-account-key-file explicitly. No manual remediation needed. scored: true - id: 1.2.29 text: "Ensure that the --etcd-certfile and --etcd-keyfile arguments are set as appropriate (Automated)" audit: journalctl -u k3s | grep "Running kube-apiserver" | tail -n1 | grep -E 'etcd-certfile|etcd-keyfile' tests: bin_op: and test_items: - flag: "--etcd-certfile" - flag: "--etcd-keyfile" remediation: | By default, K3s sets the --etcd-certfile and --etcd-keyfile arguments explicitly. No manual remediation needed. scored: true - id: 1.2.30 text: "Ensure that the --tls-cert-file and --tls-private-key-file arguments are set as appropriate (Automated)" audit: journalctl -u k3s | grep "Running kube-apiserver" | tail -n1 | grep -E 'tls-cert-file|tls-private-key-file' tests: bin_op: and test_items: - flag: "--tls-cert-file" - flag: "--tls-private-key-file" remediation: | By default, K3s sets the --tls-cert-file and --tls-private-key-file arguments explicitly. No manual remediation needed. scored: true - id: 1.2.31 text: "Ensure that the --client-ca-file argument is set as appropriate (Automated)" audit: journalctl -u k3s | grep "Running kube-apiserver" | tail -n1 | grep "client-ca-file" tests: test_items: - flag: "--client-ca-file" remediation: | By default, K3s sets the --client-ca-file argument explicitly. No manual remediation needed. scored: true - id: 1.2.32 text: "Ensure that the --etcd-cafile argument is set as appropriate (Automated)" audit: journalctl -u k3s | grep "Running kube-apiserver" | tail -n1 | grep -E 'etcd-cafile' tests: test_items: - flag: "--etcd-cafile" remediation: | By default, K3s sets the --etcd-cafile argument explicitly. No manual remediation needed. scored: true - id: 1.2.33 text: "Ensure that the --encryption-provider-config argument is set as appropriate (Manual)" audit: journalctl -u k3s | grep "Running kube-apiserver" | tail -n1 | grep -E "encryption-provider-config" tests: test_items: - flag: "--encryption-provider-config" remediation: | K3s server needs to be ran with the follow, --kube-apiserver-arg='encryption-provider-config=/path/to/encryption_config'. This can be done by running k3s with the --secrets-encryptiuon argument which will configure the encryption provider. scored: false - id: 1.2.34 text: "Ensure that encryption providers are appropriately configured (Manual)" type: manual remediation: | K3s server needs to be run with the following, --secrets-encryption=true, and verify that one of the allowed encryption providers is present. Run the below command on the master node. grep aescbc /path/to/encryption-config.json Verify that aescbc/kms/secretbox is set as the encryption provider for all the desired resources. scored: true - id: 1.2.35 text: "Ensure that the API Server only makes use of Strong Cryptographic Ciphers (Manual)" audit: journalctl -u k3s | grep "Running kube-apiserver" | tail -n1 | grep "tls-cipher-suites" tests: test_items: - flag: "--tls-cipher-suites" compare: op: valid_elements value: "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" remediation: | By default, K3s explicitly doesn't set this flag. No manual remediation needed. scored: false - id: 1.3 text: "Controller Manager" checks: - id: 1.3.1 text: "Ensure that the --terminated-pod-gc-threshold argument is set as appropriate (Manual)" audit: | journalctl -u k3s | grep "Running kube-controller-manager" | tail -n1 | grep "terminated-pod-gc-threshold" tests: test_items: - flag: "--terminated-pod-gc-threshold" remediation: | K3s server needs to be run with the following, --kube-controller-manager-arg='terminated-pod-gc-threshold=10. scored: false - id: 1.3.2 text: "Ensure that the --profiling argument is set to false (Automated)" audit: journalctl -u k3s | grep "Running kube-controller-manager" | tail -n1 | grep "profiling" tests: test_items: - flag: "--profiling" compare: op: eq value: false remediation: | By default, K3s sets the --profiling flag parameter to false. No manual remediation needed. scored: true - id: 1.3.3 text: "Ensure that the --use-service-account-credentials argument is set to true (Automated)" audit: journalctl -u k3s | grep "Running kube-controller-manager" | tail -n1 | grep "use-service-account-credentials" tests: test_items: - flag: "--use-service-account-credentials" compare: op: noteq value: false remediation: | K3s server needs to be run with the following, --kube-controller-manager-arg='use-service-account-credentials=true' scored: true - id: 1.3.4 text: "Ensure that the --service-account-private-key-file argument is set as appropriate (Automated)" audit: journalctl -u k3s | grep "Running kube-controller-manager" | tail -n1 | grep "service-account-private-key-file" tests: test_items: - flag: "--service-account-private-key-file" remediation: | By default, K3s sets the --service-account-private-key-file argument with the service account key file. No manual remediation needed. scored: true - id: 1.3.5 text: "Ensure that the --root-ca-file argument is set as appropriate (Automated)" audit: journalctl -u k3s | grep "Running kube-controller-manager" | tail -n1 | grep "root-ca-file" tests: test_items: - flag: "--root-ca-file" remediation: | By default, K3s sets the --root-ca-file argument with the root ca file. No manual remediation needed. scored: true - id: 1.3.6 text: "Ensure that the RotateKubeletServerCertificate argument is set to true (Automated)" audit: journalctl -u k3s | grep "Running kube-controller-manager" | tail -n1 | grep "RotateKubeletServerCertificate" | cat tests: bin_op: or test_items: - flag: "--feature-gates" compare: op: nothave value: "RotateKubeletServerCertificate=false" set: true - flag: "--feature-gates" set: false remediation: | By default, K3s implements its own logic for certificate generation and rotation. scored: true - id: 1.3.7 text: "Ensure that the --bind-address argument is set to 127.0.0.1 (Automated)" audit: journalctl -u k3s | grep "Running kube-controller-manager" | tail -n1 | grep "bind-address" tests: bin_op: or test_items: - flag: "--bind-address" compare: op: eq value: "127.0.0.1" - flag: "--bind-address" set: false remediation: | By default, K3s sets the --bind-address argument to 127.0.0.1. No manual remediation needed. scored: true - id: 1.4 text: "Scheduler" checks: - id: 1.4.1 text: "Ensure that the --profiling argument is set to false (Automated)" audit: journalctl -u k3s | grep "Running kube-scheduler" | tail -n1 | grep "profiling" tests: test_items: - flag: "--profiling" compare: op: eq value: false remediation: | By default, K3s sets the --profiling flag parameter to false. No manual remediation needed. scored: true - id: 1.4.2 text: "Ensure that the --bind-address argument is set to 127.0.0.1 (Automated)" audit: journalctl -u k3s | grep "Running kube-controller-manager" | tail -n1 | grep "bind-address" tests: bin_op: or test_items: - flag: "--bind-address" compare: op: eq value: "127.0.0.1" - flag: "--bind-address" set: false remediation: | By default, K3s sets the --bind-address argument to 127.0.0.1. No manual remediation needed. scored: true ================================================ FILE: cfg/cis-1.6-k3s/node.yaml ================================================ --- controls: version: "cis-1.6-k3s" id: 4 text: "Worker Node Security Configuration" type: "node" groups: - id: 4.1 text: "Worker Node Configuration Files" checks: - id: 4.1.1 text: "Ensure that the kubelet service file permissions are set to 644 or more restrictive (Not Applicable)" scored: false - id: 4.1.2 text: "Ensure that the kubelet service file ownership is set to root:root (Not Applicable)" scored: false - id: 4.1.3 text: "If proxy kubeproxy.kubeconfig file exists ensure permissions are set to 644 or more restrictive (Automated)" audit: stat -c %a $proxykubeconfig tests: bin_op: or test_items: - flag: "permissions" set: true compare: op: bitmask value: "644" - flag: "$proxykubeconfig" set: false remediation: | Run the below command (based on the file location on your system) on the each worker node. For example, chmod 644 $proxykubeconfig scored: true - id: 4.1.4 text: "Ensure that the proxy kubeconfig file ownership is set to root:root (Automated)" audit: '/bin/sh -c ''if test -e $proxykubeconfig; then stat -c %U:%G $proxykubeconfig; fi'' ' tests: bin_op: or test_items: - flag: root:root - flag: "$proxykubeconfig" set: false remediation: | Run the below command (based on the file location on your system) on the each worker node. For example, chown root:root $proxykubeconfig scored: true - id: 4.1.5 text: "Ensure that the --kubeconfig kubelet.conf file permissions are set to 644 or more restrictive (Automated)" audit: '/bin/sh -c ''if test -e $kubeletkubeconfig; then stat -c permissions=%a $kubeletkubeconfig; fi'' ' tests: test_items: - flag: "permissions" compare: op: bitmask value: "644" remediation: | By default, K3s creates $kubeletkubeconfig with 644 permissions. No manual remediation needed. scored: true - id: 4.1.6 text: "Ensure that the --kubeconfig kubelet.conf file ownership is set to root:root (Automated)" audit: '/bin/sh -c ''if test -e $kubeletkubeconfig; then stat -c %U:%G $kubeletkubeconfig; fi'' ' tests: test_items: - flag: root:root remediation: | By default, K3s creates $kubeletkubeconfig with root:root ownership. No manual remediation needed. scored: true - id: 4.1.7 text: "Ensure that the certificate authorities file permissions are set to 644 or more restrictive (Automated)" audit: stat -c permissions=%a $kubeletcafile tests: test_items: - flag: "permissions" compare: op: bitmask value: "644" remediation: | By default, K3s creates $kubeletcafile with 644 permissions. scored: true - id: 4.1.8 text: "Ensure that the client certificate authorities file ownership is set to root:root (Automated)" audit: stat -c %U:%G $kubeletcafile tests: test_items: - flag: root:root compare: op: eq value: root:root remediation: | By default, K3s creates $kubeletcafile with root:root ownership. scored: true - id: 4.1.9 text: "Ensure that the kubelet --config configuration file has permissions set to 644 or more restrictive (Not Applicable)" scored: false - id: 4.1.10 text: "Ensure that the kubelet --config configuration file ownership is set to root:root (Not Applicable)" scored: false - id: 4.2 text: "Kubelet" checks: - id: 4.2.1 text: "Ensure that the anonymous-auth argument is set to false (Automated)" audit: journalctl -u k3s | grep "Running kube-apiserver" | tail -n1 | grep "anonymous-auth" tests: test_items: - flag: "--anonymous-auth" compare: op: eq value: false remediation: | By default, K3s starts kubelet with --anonymous-auth set to false. No manual remediation needed. scored: true - id: 4.2.2 text: "Ensure that the --authorization-mode argument is not set to AlwaysAllow (Automated)" audit: journalctl -u k3s | grep "Running kube-apiserver" | tail -n1 | grep "authorization-mode" tests: test_items: - flag: --authorization-mode compare: op: nothave value: AlwaysAllow remediation: | K3s starts kubelet with Webhook as the value for the --authorization-mode argument. No manual remediation needed. scored: true - id: 4.2.3 text: "Ensure that the --client-ca-file argument is set as appropriate (Automated)" audit: journalctl -u k3s | grep "Running kube-apiserver" | tail -n1 | grep "client-ca-file" tests: test_items: - flag: --client-ca-file remediation: | By default, K3s starts the kubelet process with the --client-ca-file. No manual remediation needed. scored: true - id: 4.2.4 text: "Ensure that the --read-only-port argument is set to 0 (Automated)" audit: journalctl -u k3s | grep "Running kubelet" | tail -n1 | grep "read-only-port" tests: bin_op: or test_items: - flag: "--read-only-port" compare: op: eq value: 0 - flag: "--read-only-port" set: false remediation: | By default, K3s starts the kubelet process with the --read-only-port argument set to 0. scored: false - id: 4.2.5 text: "Ensure that the --streaming-connection-idle-timeout argument is not set to 0 (Automated)" audit: journalctl -u k3s | grep "Running kubelet" | tail -n1 | grep "streaming-connection-idle-timeout" tests: test_items: - flag: --streaming-connection-idle-timeout compare: op: noteq value: 0 - flag: --streaming-connection-idle-timeout set: false bin_op: or remediation: | By default, K3s does not set --streaming-connection-idle-timeout when starting kubelet. scored: true - id: 4.2.6 text: "Ensure that the --protect-kernel-defaults argument is set to true (Automated)" audit: journalctl -u k3s | grep "Running kubelet" | tail -n1 | grep "protect-kernel-defaults" tests: test_items: - flag: --protect-kernel-defaults compare: op: eq value: true remediation: | K3s server needs to be started with the following, --protect-kernel-defaults=true. scored: true - id: 4.2.7 text: "Ensure that the --make-iptables-util-chains argument is set to true (Automated)" audit: journalctl -u k3s | grep "Running kubelet" | tail -n1 | grep "make-iptables-util-chains" tests: test_items: - flag: --make-iptables-util-chains compare: op: eq value: true - flag: --make-iptables-util-chains set: false bin_op: or remediation: | K3s server needs to be run with the following, --kube-apiserver-arg='make-iptables-util-chains=true'. scored: true - id: 4.2.8 text: "Ensure that the --hostname-override argument is not set (Not Applicable)" scored: false - id: 4.2.9 text: "Ensure that the --event-qps argument is set to 0 or a level which ensures appropriate event capture (Manual)" audit: /bin/ps -fC containerd tests: test_items: - flag: --event-qps compare: op: eq value: 0 remediation: | If using a Kubelet config file, edit the file to set eventRecordQPS: to an appropriate level. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_SYSTEM_PODS_ARGS variable. Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: false - id: 4.2.10 text: "Ensure that the --tls-cert-file and --tls-private-key-file arguments are set as appropriate (Manual)" audit: journalctl -u k3s | grep "Running kubelet" | tail -n1 | grep -E 'tls-cert-file|tls-private-key-file' tests: test_items: - flag: --tls-cert-file - flag: --tls-private-key-file remediation: | By default, K3s sets the --tls-cert-file and --tls-private-key-file arguments when executing the kubelet process. scored: false - id: 4.2.11 text: "Ensure that the --rotate-certificates argument is not set to false (Not Applicable)" scored: false - id: 4.2.12 text: "Verify that the RotateKubeletServerCertificate argument is set to true (Not Applicable)" scored: false - id: 4.2.13 text: "Ensure that the Kubelet only makes use of Strong Cryptographic Ciphers (Not Applicable)" scored: false ================================================ FILE: cfg/cis-1.6-k3s/policies.yaml ================================================ --- controls: version: "cis-1.6-k3s" id: 5 text: "Kubernetes Policies" type: "policies" groups: - id: 5.1 text: "RBAC and Service Accounts" checks: - id: 5.1.1 text: "Ensure that the cluster-admin role is only used where required (Manual)" type: "manual" remediation: | Identify all clusterrolebindings to the cluster-admin role. Check if they are used and if they need this role or if they could use a role with fewer privileges. Where possible, first bind users to a lower privileged role and then remove the clusterrolebinding to the cluster-admin role : kubectl delete clusterrolebinding [name] scored: false - id: 5.1.2 text: "Minimize access to secrets (Manual)" type: "manual" remediation: | Where possible, remove get, list and watch access to secret objects in the cluster. scored: false - id: 5.1.3 text: "Minimize wildcard use in Roles and ClusterRoles (Manual)" type: "manual" remediation: | Where possible replace any use of wildcards in clusterroles and roles with specific objects or actions. kubectl get roles --all-namespaces -o yaml kubectl get clusterroles -o yaml scored: false - id: 5.1.4 text: "Minimize access to create pods (Manual)" type: "manual" remediation: | Where possible, remove create access to pod objects in the cluster. scored: false - id: 5.1.5 text: "Ensure that default service accounts are not actively used. (Manual)" type: "manual" remediation: | Create explicit service accounts wherever a Kubernetes workload requires specific access to the Kubernetes API server. Modify the configuration of each default service account to include this value automountServiceAccountToken: false scored: false - id: 5.1.6 text: "Ensure that Service Account Tokens are only mounted where necessary (Manual)" type: "manual" remediation: | Modify the definition of pods and service accounts which do not need to mount service account tokens to disable it. scored: false - id: 5.2 text: "Pod Security Policies" checks: - id: 5.2.1 text: "Minimize the admission of privileged containers (Manual)" type: "manual" remediation: | kubectl describe psp | grep MustRunAsNonRoot An operator should apply a PodSecurityPolicy that sets the Rule value to MustRunAsNonRoot. An example of this can be found in the Hardening Guide https://docs.rancher.cn/docs/k3s/security/hardening-guide/_index scored: false - id: 5.2.2 text: "Minimize the admission of containers wishing to share the host process ID namespace (Manual)" type: "manual" remediation: | kubectl get psp -o json | jq .items[] | jq -r 'select((.spec.hostPID == null) or (.spec.hostPID == false))' | jq .metadata.name | wc -l | xargs -I {} echo '--count={}' An operator should apply a PodSecurityPolicy that sets the hostPID value to false explicitly for the PSP it creates. An example of this can be found in the Hardening Guide. https://docs.rancher.cn/docs/k3s/security/hardening-guide/_index scored: false - id: 5.2.3 text: "Minimize the admission of containers wishing to share the host IPC namespace (Manual)" type: "manual" remediation: | kubectl get psp -o json | jq .items[] | jq -r 'select((.spec.hostIPC == null) or (.spec.hostIPC == false))' | jq .metadata.name | wc -l | xargs -I {} echo '--count={}' An operator should apply a PodSecurityPolicy that sets the HostIPC value to false explicitly for the PSP it creates. An example of this can be found in the Hardening Guide. https://docs.rancher.cn/docs/k3s/security/hardening-guide/_index scored: false - id: 5.2.4 text: "Minimize the admission of containers wishing to share the host network namespace (Manual)" type: "manual" remediation: | kubectl get psp -o json | jq .items[] | jq -r 'select((.spec.hostNetwork == null) or (.spec.hostNetwork == false))' | jq .metadata.name | wc -l | xargs -I {} echo '--count={}' An operator should apply a PodSecurityPolicy that sets the HostNetwork value to false explicitly for the PSP it creates. An example of this can be found in the Hardening Guide. https://docs.rancher.cn/docs/k3s/security/hardening-guide/_index scored: false - id: 5.2.5 text: "Minimize the admission of containers with allowPrivilegeEscalation (Manual)" type: "manual" remediation: | kubectl get psp -o json | jq .items[] | jq -r 'select((.spec.allowPrivilegeEscalation == null) or (.spec.allowPrivilegeEscalation == false))' | jq .metadata.name | wc -l | xargs -I {} echo '--count={}' An operator should apply a PodSecurityPolicy that sets the allowPrivilegeEscalation value to false explicitly for the PSP it creates. An example of this can be found in the Hardening Guide. https://docs.rancher.cn/docs/k3s/security/hardening-guide/_index scored: false - id: 5.2.6 text: "Minimize the admission of root containers (Manual)" type: "manual" remediation: | kubectl get psp -o json | jq .items[] | jq -r 'select((.spec.allowPrivilegeEscalation == null) or (.spec.allowPrivilegeEscalation == false))' | jq .metadata.name | wc -l | xargs -I {} echo '--count={}' An operator should apply a PodSecurityPolicy that sets the runAsUser.Rule value to MustRunAsNonRoot. An example of this can be found in the Hardening Guide. https://docs.rancher.cn/docs/k3s/security/hardening-guide/_index scored: false - id: 5.2.7 text: "Minimize the admission of containers with the NET_RAW capability (Manual)" type: "manual" remediation: | kubectl get psp -o json | jq .spec.requiredDropCapabilities[] An operator should apply a PodSecurityPolicy that sets .spec.requiredDropCapabilities[] to a value of All. An example of this can be found in the Hardening Guide. https://docs.rancher.cn/docs/k3s/security/hardening-guide/_index scored: false - id: 5.2.8 text: "Minimize the admission of containers with added capabilities (Manual)" type: "manual" remediation: | kubectl get psp An operator should apply a PodSecurityPolicy that sets allowedCapabilities to anything other than an empty array. An example of this can be found in the Hardening Guide. https://docs.rancher.cn/docs/k3s/security/hardening-guide/_index scored: false - id: 5.2.9 text: "Minimize the admission of containers with capabilities assigned (Manual)" type: "manual" remediation: | kubectl get psp An operator should apply a PodSecurityPolicy that sets requiredDropCapabilities to ALL. An example of this can be found in the Hardening Guide. https://docs.rancher.cn/docs/k3s/security/hardening-guide/_index scored: false - id: 5.3 text: "Network Policies and CNI" checks: - id: 5.3.1 text: "Ensure that the CNI in use supports Network Policies (Manual)" type: "manual" remediation: | By default, K3s use Canal (Calico and Flannel) and fully supports network policies. scored: false - id: 5.3.2 text: "Ensure that all Namespaces have Network Policies defined (Manual)" type: "manual" remediation: | Run the below command on the master node. for i in kube-system kube-public default; do kubectl get networkpolicies -n $i; done Verify that there are network policies applied to each of the namespaces. An operator should apply NetworkPolcyies that prevent unneeded traffic from traversing networks unnecessarily. An example of applying a NetworkPolcy can be found in the Hardening Guide. https://docs.rancher.cn/docs/k3s/security/hardening-guide/_index scored: false - id: 5.4 text: "Secrets Management" checks: - id: 5.4.1 text: "Prefer using secrets as files over secrets as environment variables (Manual)" type: "manual" remediation: | Run the following command to find references to objects which use environment variables defined from secrets. kubectl get all -o jsonpath='{range .items[?(@..secretKeyRef)]} {.kind} {.metadata.name} {"\n"}{end}' -A if possible, rewrite application code to read secrets from mounted secret files, rather than from environment variables. scored: false - id: 5.4.2 text: "Consider external secret storage (Manual)" type: "manual" remediation: | Refer to the secrets management options offered by your cloud provider or a third-party secrets management solution. scored: false - id: 5.5 text: "Extensible Admission Control" checks: - id: 5.5.1 text: "Configure Image Provenance using ImagePolicyWebhook admission controller (Manual)" type: "manual" remediation: | Follow the Kubernetes documentation and setup image provenance. scored: false - id: 5.7 text: "General Policies" checks: - id: 5.7.1 text: "Create administrative boundaries between resources using namespaces (Manual)" audit: kubectl get namespaces type: "manual" remediation: | Ensure that these namespaces are the ones you need and are adequately administered as per your requirements. Follow the documentation and create namespaces for objects in your deployment as you need them. scored: false - id: 5.7.2 text: "Ensure that the seccomp profile is set to docker/default in your pod definitions (Manual)" type: "manual" remediation: | Seccomp is an alpha feature currently. By default, all alpha features are disabled. So, you would need to enable alpha features in the apiserver by passing "--feature- gates=AllAlpha=true" argument. Edit the /etc/kubernetes/apiserver file on the master node and set the KUBE_API_ARGS parameter to "--feature-gates=AllAlpha=true" KUBE_API_ARGS="--feature-gates=AllAlpha=true" Based on your system, restart the kube-apiserver service. For example: systemctl restart kube-apiserver.service Use annotations to enable the docker/default seccomp profile in your pod definitions. An example is as below: apiVersion: v1 kind: Pod metadata: name: trustworthy-pod annotations: seccomp.security.alpha.kubernetes.io/pod: docker/default spec: containers: - name: trustworthy-container image: sotrustworthy:latest scored: false - id: 5.7.3 text: "Apply Security Context to Your Pods and Containers (Manual)" type: "manual" remediation: | Follow the Kubernetes documentation and apply security contexts to your pods. For a suggested list of security contexts, you may refer to the CIS Security Benchmark for Docker Containers. scored: false - id: 5.7.4 text: "The default namespace should not be used (Manual)" type: "manual" remediation: | Run the below command on the master node. kubectl get all -n default The only entries there should be system-managed resources such as the kubernetes service. Ensure that namespaces are created to allow for appropriate segregation of Kubernetes resources and that all new resources are created in a specific namespace. scored: false ================================================ FILE: cfg/cis-1.7/config.yaml ================================================ --- ## Version-specific settings that override the values in cfg/config.yaml ================================================ FILE: cfg/cis-1.7/controlplane.yaml ================================================ --- controls: version: "cis-1.7" id: 3 text: "Control Plane Configuration" type: "controlplane" groups: - id: 3.1 text: "Authentication and Authorization" checks: - id: 3.1.1 text: "Client certificate authentication should not be used for users (Manual)" type: "manual" remediation: | Alternative mechanisms provided by Kubernetes such as the use of OIDC should be implemented in place of client certificates. scored: false - id: 3.1.2 text: "Service account token authentication should not be used for users (Manual)" type: "manual" remediation: | Alternative mechanisms provided by Kubernetes such as the use of OIDC should be implemented in place of service account tokens. scored: false - id: 3.1.3 text: "Bootstrap token authentication should not be used for users (Manual)" type: "manual" remediation: | Alternative mechanisms provided by Kubernetes such as the use of OIDC should be implemented in place of bootstrap tokens. scored: false - id: 3.2 text: "Logging" checks: - id: 3.2.1 text: "Ensure that a minimal audit policy is created (Manual)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--audit-policy-file" set: true remediation: | Create an audit policy file for your cluster. scored: false - id: 3.2.2 text: "Ensure that the audit policy covers key security concerns (Manual)" type: "manual" remediation: | Review the audit policy provided for the cluster and ensure that it covers at least the following areas, - Access to Secrets managed by the cluster. Care should be taken to only log Metadata for requests to Secrets, ConfigMaps, and TokenReviews, in order to avoid risk of logging sensitive data. - Modification of Pod and Deployment objects. - Use of `pods/exec`, `pods/portforward`, `pods/proxy` and `services/proxy`. For most requests, minimally logging at the Metadata level is recommended (the most basic level of logging). scored: false ================================================ FILE: cfg/cis-1.7/etcd.yaml ================================================ --- controls: version: "cis-1.7" id: 2 text: "Etcd Node Configuration" type: "etcd" groups: - id: 2 text: "Etcd Node Configuration" checks: - id: 2.1 text: "Ensure that the --cert-file and --key-file arguments are set as appropriate (Automated)" audit: "/bin/ps -ef | /bin/grep $etcdbin | /bin/grep -v grep" tests: bin_op: and test_items: - flag: "--cert-file" env: "ETCD_CERT_FILE" - flag: "--key-file" env: "ETCD_KEY_FILE" remediation: | Follow the etcd service documentation and configure TLS encryption. Then, edit the etcd pod specification file /etc/kubernetes/manifests/etcd.yaml on the master node and set the below parameters. --cert-file= --key-file= scored: true - id: 2.2 text: "Ensure that the --client-cert-auth argument is set to true (Automated)" audit: "/bin/ps -ef | /bin/grep $etcdbin | /bin/grep -v grep" tests: test_items: - flag: "--client-cert-auth" env: "ETCD_CLIENT_CERT_AUTH" compare: op: eq value: true remediation: | Edit the etcd pod specification file $etcdconf on the master node and set the below parameter. --client-cert-auth="true" scored: true - id: 2.3 text: "Ensure that the --auto-tls argument is not set to true (Automated)" audit: "/bin/ps -ef | /bin/grep $etcdbin | /bin/grep -v grep" tests: bin_op: or test_items: - flag: "--auto-tls" env: "ETCD_AUTO_TLS" set: false - flag: "--auto-tls" env: "ETCD_AUTO_TLS" compare: op: eq value: false remediation: | Edit the etcd pod specification file $etcdconf on the master node and either remove the --auto-tls parameter or set it to false. --auto-tls=false scored: true - id: 2.4 text: "Ensure that the --peer-cert-file and --peer-key-file arguments are set as appropriate (Automated)" audit: "/bin/ps -ef | /bin/grep $etcdbin | /bin/grep -v grep" tests: bin_op: and test_items: - flag: "--peer-cert-file" env: "ETCD_PEER_CERT_FILE" - flag: "--peer-key-file" env: "ETCD_PEER_KEY_FILE" remediation: | Follow the etcd service documentation and configure peer TLS encryption as appropriate for your etcd cluster. Then, edit the etcd pod specification file $etcdconf on the master node and set the below parameters. --peer-client-file= --peer-key-file= scored: true - id: 2.5 text: "Ensure that the --peer-client-cert-auth argument is set to true (Automated)" audit: "/bin/ps -ef | /bin/grep $etcdbin | /bin/grep -v grep" tests: test_items: - flag: "--peer-client-cert-auth" env: "ETCD_PEER_CLIENT_CERT_AUTH" compare: op: eq value: true remediation: | Edit the etcd pod specification file $etcdconf on the master node and set the below parameter. --peer-client-cert-auth=true scored: true - id: 2.6 text: "Ensure that the --peer-auto-tls argument is not set to true (Automated)" audit: "/bin/ps -ef | /bin/grep $etcdbin | /bin/grep -v grep" tests: bin_op: or test_items: - flag: "--peer-auto-tls" env: "ETCD_PEER_AUTO_TLS" set: false - flag: "--peer-auto-tls" env: "ETCD_PEER_AUTO_TLS" compare: op: eq value: false remediation: | Edit the etcd pod specification file $etcdconf on the master node and either remove the --peer-auto-tls parameter or set it to false. --peer-auto-tls=false scored: true - id: 2.7 text: "Ensure that a unique Certificate Authority is used for etcd (Manual)" audit: "/bin/ps -ef | /bin/grep $etcdbin | /bin/grep -v grep" tests: test_items: - flag: "--trusted-ca-file" env: "ETCD_TRUSTED_CA_FILE" remediation: | [Manual test] Follow the etcd documentation and create a dedicated certificate authority setup for the etcd service. Then, edit the etcd pod specification file $etcdconf on the master node and set the below parameter. --trusted-ca-file= scored: false ================================================ FILE: cfg/cis-1.7/master.yaml ================================================ --- controls: version: "cis-1.7" id: 1 text: "Control Plane Security Configuration" type: "master" groups: - id: 1.1 text: "Control Plane Node Configuration Files" checks: - id: 1.1.1 text: "Ensure that the API server pod specification file permissions are set to 600 or more restrictive (Automated)" audit: "/bin/sh -c 'if test -e $apiserverconf; then stat -c permissions=%a $apiserverconf; fi'" tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chmod 600 $apiserverconf scored: true - id: 1.1.2 text: "Ensure that the API server pod specification file ownership is set to root:root (Automated)" audit: "/bin/sh -c 'if test -e $apiserverconf; then stat -c %U:%G $apiserverconf; fi'" tests: test_items: - flag: "root:root" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chown root:root $apiserverconf scored: true - id: 1.1.3 text: "Ensure that the controller manager pod specification file permissions are set to 600 or more restrictive (Automated)" audit: "/bin/sh -c 'if test -e $controllermanagerconf; then stat -c permissions=%a $controllermanagerconf; fi'" tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chmod 600 $controllermanagerconf scored: true - id: 1.1.4 text: "Ensure that the controller manager pod specification file ownership is set to root:root (Automated)" audit: "/bin/sh -c 'if test -e $controllermanagerconf; then stat -c %U:%G $controllermanagerconf; fi'" tests: test_items: - flag: "root:root" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chown root:root $controllermanagerconf scored: true - id: 1.1.5 text: "Ensure that the scheduler pod specification file permissions are set to 600 or more restrictive (Automated)" audit: "/bin/sh -c 'if test -e $schedulerconf; then stat -c permissions=%a $schedulerconf; fi'" tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chmod 600 $schedulerconf scored: true - id: 1.1.6 text: "Ensure that the scheduler pod specification file ownership is set to root:root (Automated)" audit: "/bin/sh -c 'if test -e $schedulerconf; then stat -c %U:%G $schedulerconf; fi'" tests: test_items: - flag: "root:root" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chown root:root $schedulerconf scored: true - id: 1.1.7 text: "Ensure that the etcd pod specification file permissions are set to 600 or more restrictive (Automated)" audit: "/bin/sh -c 'if test -e $etcdconf; then find $etcdconf -name '*etcd*' | xargs stat -c permissions=%a; fi'" use_multiple_values: true tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chmod 600 $etcdconf scored: true - id: 1.1.8 text: "Ensure that the etcd pod specification file ownership is set to root:root (Automated)" audit: "/bin/sh -c 'if test -e $etcdconf; then find $etcdconf -name '*etcd*' | xargs stat -c %U:%G; fi'" use_multiple_values: true tests: test_items: - flag: "root:root" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chown root:root $etcdconf scored: true - id: 1.1.9 text: "Ensure that the Container Network Interface file permissions are set to 600 or more restrictive (Manual)" audit: | ps -ef | grep $kubeletbin | grep -- --cni-conf-dir | sed 's%.*cni-conf-dir[= ]\([^ ]*\).*%\1%' | xargs -I{} find {} -mindepth 1 | xargs --no-run-if-empty stat -c permissions=%a find /var/lib/cni/networks -type f 2> /dev/null | xargs --no-run-if-empty stat -c permissions=%a use_multiple_values: true tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chmod 600 scored: false - id: 1.1.10 text: "Ensure that the Container Network Interface file ownership is set to root:root (Manual)" audit: | ps -ef | grep $kubeletbin | grep -- --cni-conf-dir | sed 's%.*cni-conf-dir[= ]\([^ ]*\).*%\1%' | xargs -I{} find {} -mindepth 1 | xargs --no-run-if-empty stat -c %U:%G find /var/lib/cni/networks -type f 2> /dev/null | xargs --no-run-if-empty stat -c %U:%G use_multiple_values: true tests: test_items: - flag: "root:root" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chown root:root scored: false - id: 1.1.11 text: "Ensure that the etcd data directory permissions are set to 700 or more restrictive (Automated)" audit: | DATA_DIR='' for d in $(ps -ef | grep $etcdbin | grep -- --data-dir | sed 's%.*data-dir[= ]\([^ ]*\).*%\1%'); do if test -d "$d"; then DATA_DIR="$d"; fi done if ! test -d "$DATA_DIR"; then DATA_DIR=$etcddatadir; fi stat -c permissions=%a "$DATA_DIR" tests: test_items: - flag: "permissions" compare: op: bitmask value: "700" remediation: | On the etcd server node, get the etcd data directory, passed as an argument --data-dir, from the command 'ps -ef | grep etcd'. Run the below command (based on the etcd data directory found above). For example, chmod 700 /var/lib/etcd scored: true - id: 1.1.12 text: "Ensure that the etcd data directory ownership is set to etcd:etcd (Automated)" audit: | DATA_DIR='' for d in $(ps -ef | grep $etcdbin | grep -- --data-dir | sed 's%.*data-dir[= ]\([^ ]*\).*%\1%'); do if test -d "$d"; then DATA_DIR="$d"; fi done if ! test -d "$DATA_DIR"; then DATA_DIR=$etcddatadir; fi stat -c %U:%G "$DATA_DIR" tests: test_items: - flag: "etcd:etcd" remediation: | On the etcd server node, get the etcd data directory, passed as an argument --data-dir, from the command 'ps -ef | grep etcd'. Run the below command (based on the etcd data directory found above). For example, chown etcd:etcd /var/lib/etcd scored: true - id: 1.1.13 text: "Ensure that the admin.conf file permissions are set to 600 or more restrictive (Automated)" audit: "/bin/sh -c 'if test -e /etc/kubernetes/admin.conf; then stat -c permissions=%a /etc/kubernetes/admin.conf; fi'" tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chmod 600 /etc/kubernetes/admin.conf scored: true - id: 1.1.14 text: "Ensure that the admin.conf file ownership is set to root:root (Automated)" audit: "/bin/sh -c 'if test -e /etc/kubernetes/admin.conf; then stat -c %U:%G /etc/kubernetes/admin.conf; fi'" tests: test_items: - flag: "root:root" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chown root:root /etc/kubernetes/admin.conf scored: true - id: 1.1.15 text: "Ensure that the scheduler.conf file permissions are set to 600 or more restrictive (Automated)" audit: "/bin/sh -c 'if test -e $schedulerkubeconfig; then stat -c permissions=%a $schedulerkubeconfig; fi'" tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chmod 600 $schedulerkubeconfig scored: true - id: 1.1.16 text: "Ensure that the scheduler.conf file ownership is set to root:root (Automated)" audit: "/bin/sh -c 'if test -e $schedulerkubeconfig; then stat -c %U:%G $schedulerkubeconfig; fi'" tests: test_items: - flag: "root:root" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chown root:root $schedulerkubeconfig scored: true - id: 1.1.17 text: "Ensure that the controller-manager.conf file permissions are set to 600 or more restrictive (Automated)" audit: "/bin/sh -c 'if test -e $controllermanagerkubeconfig; then stat -c permissions=%a $controllermanagerkubeconfig; fi'" tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chmod 600 $controllermanagerkubeconfig scored: true - id: 1.1.18 text: "Ensure that the controller-manager.conf file ownership is set to root:root (Automated)" audit: "/bin/sh -c 'if test -e $controllermanagerkubeconfig; then stat -c %U:%G $controllermanagerkubeconfig; fi'" tests: test_items: - flag: "root:root" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chown root:root $controllermanagerkubeconfig scored: true - id: 1.1.19 text: "Ensure that the Kubernetes PKI directory and file ownership is set to root:root (Automated)" audit: "find /etc/kubernetes/pki/ | xargs stat -c %U:%G" use_multiple_values: true tests: test_items: - flag: "root:root" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chown -R root:root /etc/kubernetes/pki/ scored: true - id: 1.1.20 text: "Ensure that the Kubernetes PKI certificate file permissions are set to 600 or more restrictive (Manual)" audit: "find /etc/kubernetes/pki/ -name '*.crt' | xargs stat -c permissions=%a" use_multiple_values: true tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chmod -R 600 /etc/kubernetes/pki/*.crt scored: false - id: 1.1.21 text: "Ensure that the Kubernetes PKI key file permissions are set to 600 (Manual)" audit: "find /etc/kubernetes/pki/ -name '*.key' | xargs stat -c permissions=%a" use_multiple_values: true tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chmod -R 600 /etc/kubernetes/pki/*.key scored: false - id: 1.2 text: "API Server" checks: - id: 1.2.1 text: "Ensure that the --anonymous-auth argument is set to false (Manual)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--anonymous-auth" compare: op: eq value: false remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the below parameter. --anonymous-auth=false scored: false - id: 1.2.2 text: "Ensure that the --token-auth-file parameter is not set (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--token-auth-file" set: false remediation: | Follow the documentation and configure alternate mechanisms for authentication. Then, edit the API server pod specification file $apiserverconf on the control plane node and remove the --token-auth-file= parameter. scored: true - id: 1.2.3 text: "Ensure that the --DenyServiceExternalIPs is set (Manual)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--enable-admission-plugins" compare: op: has value: "DenyServiceExternalIPs" remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and add the `DenyServiceExternalIPs` plugin to the enabled admission plugins, as such --enable-admission-plugin=DenyServiceExternalIPs. scored: false - id: 1.2.4 text: "Ensure that the --kubelet-client-certificate and --kubelet-client-key arguments are set as appropriate (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: bin_op: and test_items: - flag: "--kubelet-client-certificate" - flag: "--kubelet-client-key" remediation: | Follow the Kubernetes documentation and set up the TLS connection between the apiserver and kubelets. Then, edit API server pod specification file $apiserverconf on the control plane node and set the kubelet client certificate and key parameters as below. --kubelet-client-certificate= --kubelet-client-key= scored: true - id: 1.2.5 text: "Ensure that the --kubelet-certificate-authority argument is set as appropriate (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--kubelet-certificate-authority" remediation: | Follow the Kubernetes documentation and setup the TLS connection between the apiserver and kubelets. Then, edit the API server pod specification file $apiserverconf on the control plane node and set the --kubelet-certificate-authority parameter to the path to the cert file for the certificate authority. --kubelet-certificate-authority= scored: true - id: 1.2.6 text: "Ensure that the --authorization-mode argument is not set to AlwaysAllow (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--authorization-mode" compare: op: nothave value: "AlwaysAllow" remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the --authorization-mode parameter to values other than AlwaysAllow. One such example could be as below. --authorization-mode=RBAC scored: true - id: 1.2.7 text: "Ensure that the --authorization-mode argument includes Node (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--authorization-mode" compare: op: has value: "Node" remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the --authorization-mode parameter to a value that includes Node. --authorization-mode=Node,RBAC scored: true - id: 1.2.8 text: "Ensure that the --authorization-mode argument includes RBAC (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--authorization-mode" compare: op: has value: "RBAC" remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the --authorization-mode parameter to a value that includes RBAC, for example `--authorization-mode=Node,RBAC`. scored: true - id: 1.2.9 text: "Ensure that the admission control plugin EventRateLimit is set (Manual)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--enable-admission-plugins" compare: op: has value: "EventRateLimit" remediation: | Follow the Kubernetes documentation and set the desired limits in a configuration file. Then, edit the API server pod specification file $apiserverconf and set the below parameters. --enable-admission-plugins=...,EventRateLimit,... --admission-control-config-file= scored: false - id: 1.2.10 text: "Ensure that the admission control plugin AlwaysAdmit is not set (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: bin_op: or test_items: - flag: "--enable-admission-plugins" compare: op: nothave value: AlwaysAdmit - flag: "--enable-admission-plugins" set: false remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and either remove the --enable-admission-plugins parameter, or set it to a value that does not include AlwaysAdmit. scored: true - id: 1.2.11 text: "Ensure that the admission control plugin AlwaysPullImages is set (Manual)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--enable-admission-plugins" compare: op: has value: "AlwaysPullImages" remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the --enable-admission-plugins parameter to include AlwaysPullImages. --enable-admission-plugins=...,AlwaysPullImages,... scored: false - id: 1.2.12 text: "Ensure that the admission control plugin SecurityContextDeny is set if PodSecurityPolicy is not used (Manual)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: bin_op: or test_items: - flag: "--enable-admission-plugins" compare: op: has value: "SecurityContextDeny" - flag: "--enable-admission-plugins" compare: op: has value: "PodSecurityPolicy" remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the --enable-admission-plugins parameter to include SecurityContextDeny, unless PodSecurityPolicy is already in place. --enable-admission-plugins=...,SecurityContextDeny,... scored: false - id: 1.2.13 text: "Ensure that the admission control plugin ServiceAccount is set (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: bin_op: or test_items: - flag: "--disable-admission-plugins" compare: op: nothave value: "ServiceAccount" - flag: "--disable-admission-plugins" set: false remediation: | Follow the documentation and create ServiceAccount objects as per your environment. Then, edit the API server pod specification file $apiserverconf on the control plane node and ensure that the --disable-admission-plugins parameter is set to a value that does not include ServiceAccount. scored: true - id: 1.2.14 text: "Ensure that the admission control plugin NamespaceLifecycle is set (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: bin_op: or test_items: - flag: "--disable-admission-plugins" compare: op: nothave value: "NamespaceLifecycle" - flag: "--disable-admission-plugins" set: false remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the --disable-admission-plugins parameter to ensure it does not include NamespaceLifecycle. scored: true - id: 1.2.15 text: "Ensure that the admission control plugin NodeRestriction is set (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--enable-admission-plugins" compare: op: has value: "NodeRestriction" remediation: | Follow the Kubernetes documentation and configure NodeRestriction plug-in on kubelets. Then, edit the API server pod specification file $apiserverconf on the control plane node and set the --enable-admission-plugins parameter to a value that includes NodeRestriction. --enable-admission-plugins=...,NodeRestriction,... scored: true - id: 1.2.16 text: "Ensure that the --secure-port argument is not set to 0 - NoteThis recommendation is obsolete and will be deleted per the consensus process (Manual)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: bin_op: or test_items: - flag: "--secure-port" compare: op: gt value: 0 - flag: "--secure-port" set: false remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and either remove the --secure-port parameter or set it to a different (non-zero) desired port. scored: false - id: 1.2.17 text: "Ensure that the --profiling argument is set to false (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--profiling" compare: op: eq value: false remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the below parameter. --profiling=false scored: true - id: 1.2.18 text: "Ensure that the --audit-log-path argument is set (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--audit-log-path" remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the --audit-log-path parameter to a suitable path and file where you would like audit logs to be written, for example, --audit-log-path=/var/log/apiserver/audit.log scored: true - id: 1.2.19 text: "Ensure that the --audit-log-maxage argument is set to 30 or as appropriate (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--audit-log-maxage" compare: op: gte value: 30 remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the --audit-log-maxage parameter to 30 or as an appropriate number of days, for example, --audit-log-maxage=30 scored: true - id: 1.2.20 text: "Ensure that the --audit-log-maxbackup argument is set to 10 or as appropriate (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--audit-log-maxbackup" compare: op: gte value: 10 remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the --audit-log-maxbackup parameter to 10 or to an appropriate value. For example, --audit-log-maxbackup=10 scored: true - id: 1.2.21 text: "Ensure that the --audit-log-maxsize argument is set to 100 or as appropriate (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--audit-log-maxsize" compare: op: gte value: 100 remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the --audit-log-maxsize parameter to an appropriate size in MB. For example, to set it as 100 MB, --audit-log-maxsize=100 scored: true - id: 1.2.22 text: "Ensure that the --request-timeout argument is set as appropriate (Manual)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" type: manual remediation: | Edit the API server pod specification file $apiserverconf and set the below parameter as appropriate and if needed. For example, --request-timeout=300s scored: false - id: 1.2.23 text: "Ensure that the --service-account-lookup argument is set to true (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: bin_op: or test_items: - flag: "--service-account-lookup" set: false - flag: "--service-account-lookup" compare: op: eq value: true remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the below parameter. --service-account-lookup=true Alternatively, you can delete the --service-account-lookup parameter from this file so that the default takes effect. scored: true - id: 1.2.24 text: "Ensure that the --service-account-key-file argument is set as appropriate (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--service-account-key-file" remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the --service-account-key-file parameter to the public key file for service accounts. For example, --service-account-key-file= scored: true - id: 1.2.25 text: "Ensure that the --etcd-certfile and --etcd-keyfile arguments are set as appropriate (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: bin_op: and test_items: - flag: "--etcd-certfile" - flag: "--etcd-keyfile" remediation: | Follow the Kubernetes documentation and set up the TLS connection between the apiserver and etcd. Then, edit the API server pod specification file $apiserverconf on the control plane node and set the etcd certificate and key file parameters. --etcd-certfile= --etcd-keyfile= scored: true - id: 1.2.26 text: "Ensure that the --tls-cert-file and --tls-private-key-file arguments are set as appropriate (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: bin_op: and test_items: - flag: "--tls-cert-file" - flag: "--tls-private-key-file" remediation: | Follow the Kubernetes documentation and set up the TLS connection on the apiserver. Then, edit the API server pod specification file $apiserverconf on the control plane node and set the TLS certificate and private key file parameters. --tls-cert-file= --tls-private-key-file= scored: true - id: 1.2.27 text: "Ensure that the --client-ca-file argument is set as appropriate (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--client-ca-file" remediation: | Follow the Kubernetes documentation and set up the TLS connection on the apiserver. Then, edit the API server pod specification file $apiserverconf on the control plane node and set the client certificate authority file. --client-ca-file= scored: true - id: 1.2.28 text: "Ensure that the --etcd-cafile argument is set as appropriate (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--etcd-cafile" remediation: | Follow the Kubernetes documentation and set up the TLS connection between the apiserver and etcd. Then, edit the API server pod specification file $apiserverconf on the control plane node and set the etcd certificate authority file parameter. --etcd-cafile= scored: true - id: 1.2.29 text: "Ensure that the --encryption-provider-config argument is set as appropriate (Manual)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--encryption-provider-config" remediation: | Follow the Kubernetes documentation and configure a EncryptionConfig file. Then, edit the API server pod specification file $apiserverconf on the control plane node and set the --encryption-provider-config parameter to the path of that file. For example, --encryption-provider-config= scored: false - id: 1.2.30 text: "Ensure that encryption providers are appropriately configured (Manual)" audit: | ENCRYPTION_PROVIDER_CONFIG=$(ps -ef | grep $apiserverbin | grep -- --encryption-provider-config | sed 's%.*encryption-provider-config[= ]\([^ ]*\).*%\1%') if test -e $ENCRYPTION_PROVIDER_CONFIG; then grep -A1 'providers:' $ENCRYPTION_PROVIDER_CONFIG | tail -n1 | grep -o "[A-Za-z]*" | sed 's/^/provider=/'; fi tests: test_items: - flag: "provider" compare: op: valid_elements value: "aescbc,kms,secretbox" remediation: | Follow the Kubernetes documentation and configure a EncryptionConfig file. In this file, choose aescbc, kms or secretbox as the encryption provider. scored: false - id: 1.2.31 text: "Ensure that the API Server only makes use of Strong Cryptographic Ciphers (Manual)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--tls-cipher-suites" compare: op: valid_elements value: "TLS_AES_128_GCM_SHA256,TLS_AES_256_GCM_SHA384,TLS_CHACHA20_POLY1305_SHA256,TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA,TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,TLS_RSA_WITH_3DES_EDE_CBC_SHA,TLS_RSA_WITH_AES_128_CBC_SHA,TLS_RSA_WITH_AES_128_GCM_SHA256,TLS_RSA_WITH_AES_256_CBC_SHA,TLS_RSA_WITH_AES_256_GCM_SHA384" remediation: | Edit the API server pod specification file /etc/kubernetes/manifests/kube-apiserver.yaml on the control plane node and set the below parameter. --tls-cipher-suites=TLS_AES_128_GCM_SHA256,TLS_AES_256_GCM_SHA384,TLS_CHACHA20_POLY1305_SHA256, TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA,TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305, TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,TLS_RSA_WITH_3DES_EDE_CBC_SHA,TLS_RSA_WITH_AES_128_CBC_SHA, TLS_RSA_WITH_AES_128_GCM_SHA256,TLS_RSA_WITH_AES_256_CBC_SHA,TLS_RSA_WITH_AES_256_GCM_SHA384 scored: false - id: 1.3 text: "Controller Manager" checks: - id: 1.3.1 text: "Ensure that the --terminated-pod-gc-threshold argument is set as appropriate (Manual)" audit: "/bin/ps -ef | grep $controllermanagerbin | grep -v grep" tests: test_items: - flag: "--terminated-pod-gc-threshold" remediation: | Edit the Controller Manager pod specification file $controllermanagerconf on the control plane node and set the --terminated-pod-gc-threshold to an appropriate threshold, for example, --terminated-pod-gc-threshold=10 scored: false - id: 1.3.2 text: "Ensure that the --profiling argument is set to false (Automated)" audit: "/bin/ps -ef | grep $controllermanagerbin | grep -v grep" tests: test_items: - flag: "--profiling" compare: op: eq value: false remediation: | Edit the Controller Manager pod specification file $controllermanagerconf on the control plane node and set the below parameter. --profiling=false scored: true - id: 1.3.3 text: "Ensure that the --use-service-account-credentials argument is set to true (Automated)" audit: "/bin/ps -ef | grep $controllermanagerbin | grep -v grep" tests: test_items: - flag: "--use-service-account-credentials" compare: op: noteq value: false remediation: | Edit the Controller Manager pod specification file $controllermanagerconf on the control plane node to set the below parameter. --use-service-account-credentials=true scored: true - id: 1.3.4 text: "Ensure that the --service-account-private-key-file argument is set as appropriate (Automated)" audit: "/bin/ps -ef | grep $controllermanagerbin | grep -v grep" tests: test_items: - flag: "--service-account-private-key-file" remediation: | Edit the Controller Manager pod specification file $controllermanagerconf on the control plane node and set the --service-account-private-key-file parameter to the private key file for service accounts. --service-account-private-key-file= scored: true - id: 1.3.5 text: "Ensure that the --root-ca-file argument is set as appropriate (Automated)" audit: "/bin/ps -ef | grep $controllermanagerbin | grep -v grep" tests: test_items: - flag: "--root-ca-file" remediation: | Edit the Controller Manager pod specification file $controllermanagerconf on the control plane node and set the --root-ca-file parameter to the certificate bundle file`. --root-ca-file= scored: true - id: 1.3.6 text: "Ensure that the RotateKubeletServerCertificate argument is set to true (Automated)" audit: "/bin/ps -ef | grep $controllermanagerbin | grep -v grep" tests: bin_op: or test_items: - flag: "--feature-gates" compare: op: nothave value: "RotateKubeletServerCertificate=false" set: true - flag: "--feature-gates" set: false remediation: | Edit the Controller Manager pod specification file $controllermanagerconf on the control plane node and set the --feature-gates parameter to include RotateKubeletServerCertificate=true. --feature-gates=RotateKubeletServerCertificate=true scored: true - id: 1.3.7 text: "Ensure that the --bind-address argument is set to 127.0.0.1 (Automated)" audit: "/bin/ps -ef | grep $controllermanagerbin | grep -v grep" tests: bin_op: or test_items: - flag: "--bind-address" compare: op: eq value: "127.0.0.1" - flag: "--bind-address" set: false remediation: | Edit the Controller Manager pod specification file $controllermanagerconf on the control plane node and ensure the correct value for the --bind-address parameter scored: true - id: 1.4 text: "Scheduler" checks: - id: 1.4.1 text: "Ensure that the --profiling argument is set to false (Automated)" audit: "/bin/ps -ef | grep $schedulerbin | grep -v grep" tests: test_items: - flag: "--profiling" compare: op: eq value: false remediation: | Edit the Scheduler pod specification file $schedulerconf file on the control plane node and set the below parameter. --profiling=false scored: true - id: 1.4.2 text: "Ensure that the --bind-address argument is set to 127.0.0.1 (Automated)" audit: "/bin/ps -ef | grep $schedulerbin | grep -v grep" tests: bin_op: or test_items: - flag: "--bind-address" compare: op: eq value: "127.0.0.1" - flag: "--bind-address" set: false remediation: | Edit the Scheduler pod specification file $schedulerconf on the control plane node and ensure the correct value for the --bind-address parameter scored: true ================================================ FILE: cfg/cis-1.7/node.yaml ================================================ --- controls: version: "cis-1.7" id: 4 text: "Worker Node Security Configuration" type: "node" groups: - id: 4.1 text: "Worker Node Configuration Files" checks: - id: 4.1.1 text: "Ensure that the kubelet service file permissions are set to 600 or more restrictive (Automated)" audit: '/bin/sh -c ''if test -e $kubeletsvc; then stat -c permissions=%a $kubeletsvc; fi'' ' tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Run the below command (based on the file location on your system) on the each worker node. For example, chmod 600 $kubeletsvc scored: true - id: 4.1.2 text: "Ensure that the kubelet service file ownership is set to root:root (Automated)" audit: '/bin/sh -c "if test -e $kubeletsvc; then stat -c %U:%G $kubeletsvc; else echo \"File not found\"; fi"' tests: bin_op: or test_items: - flag: root:root - flag: "File not found" remediation: | Run the below command (based on the file location on your system) on the each worker node. For example, chown root:root $kubeletsvc scored: true - id: 4.1.3 text: "If proxy kubeconfig file exists ensure permissions are set to 600 or more restrictive (Manual)" audit: '/bin/sh -c ''if test -e $proxykubeconfig; then stat -c permissions=%a $proxykubeconfig; fi'' ' tests: bin_op: or test_items: - flag: "permissions" set: true compare: op: bitmask value: "600" remediation: | Run the below command (based on the file location on your system) on the each worker node. For example, chmod 600 $proxykubeconfig scored: false - id: 4.1.4 text: "If proxy kubeconfig file exists ensure ownership is set to root:root (Manual)" audit: '/bin/sh -c ''if test -e $proxykubeconfig; then stat -c %U:%G $proxykubeconfig; fi'' ' tests: bin_op: or test_items: - flag: root:root remediation: | Run the below command (based on the file location on your system) on the each worker node. For example, chown root:root $proxykubeconfig scored: false - id: 4.1.5 text: "Ensure that the --kubeconfig kubelet.conf file permissions are set to 600 or more restrictive (Automated)" audit: '/bin/sh -c ''if test -e $kubeletkubeconfig; then stat -c permissions=%a $kubeletkubeconfig; fi'' ' tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Run the below command (based on the file location on your system) on the each worker node. For example, chmod 600 $kubeletkubeconfig scored: true - id: 4.1.6 text: "Ensure that the --kubeconfig kubelet.conf file ownership is set to root:root (Automated)" audit: '/bin/sh -c ''if test -e $kubeletkubeconfig; then stat -c %U:%G $kubeletkubeconfig; fi'' ' tests: test_items: - flag: root:root remediation: | Run the below command (based on the file location on your system) on the each worker node. For example, chown root:root $kubeletkubeconfig scored: true - id: 4.1.7 text: "Ensure that the certificate authorities file permissions are set to 600 or more restrictive (Manual)" audit: | CAFILE=$(ps -ef | grep kubelet | grep -v apiserver | grep -- --client-ca-file= | awk -F '--client-ca-file=' '{print $2}' | awk '{print $1}' | uniq) if test -z $CAFILE; then CAFILE=$kubeletcafile; fi if test -e $CAFILE; then stat -c permissions=%a $CAFILE; fi tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Run the following command to modify the file permissions of the --client-ca-file chmod 600 scored: false - id: 4.1.8 text: "Ensure that the client certificate authorities file ownership is set to root:root (Manual)" audit: | CAFILE=$(ps -ef | grep kubelet | grep -v apiserver | grep -- --client-ca-file= | awk -F '--client-ca-file=' '{print $2}' | awk '{print $1}' | uniq) if test -z $CAFILE; then CAFILE=$kubeletcafile; fi if test -e $CAFILE; then stat -c %U:%G $CAFILE; fi tests: test_items: - flag: root:root compare: op: eq value: root:root remediation: | Run the following command to modify the ownership of the --client-ca-file. chown root:root scored: false - id: 4.1.9 text: "If the kubelet config.yaml configuration file is being used validate permissions set to 600 or more restrictive (Manual)" audit: '/bin/sh -c ''if test -e $kubeletconf; then stat -c permissions=%a $kubeletconf; fi'' ' tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Run the following command (using the config file location identified in the Audit step) chmod 600 $kubeletconf scored: false - id: 4.1.10 text: "If the kubelet config.yaml configuration file is being used validate file ownership is set to root:root (Manual)" audit: '/bin/sh -c ''if test -e $kubeletconf; then stat -c %U:%G $kubeletconf; fi'' ' tests: test_items: - flag: root:root remediation: | Run the following command (using the config file location identified in the Audit step) chown root:root $kubeletconf scored: false - id: 4.2 text: "Kubelet" checks: - id: 4.2.1 text: "Ensure that the --anonymous-auth argument is set to false (Automated)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: "--anonymous-auth" path: '{.authentication.anonymous.enabled}' compare: op: eq value: false remediation: | If using a Kubelet config file, edit the file to set `authentication: anonymous: enabled` to `false`. If using executable arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_SYSTEM_PODS_ARGS variable. `--anonymous-auth=false` Based on your system, restart the kubelet service. For example, systemctl daemon-reload systemctl restart kubelet.service scored: true - id: 4.2.2 text: "Ensure that the --authorization-mode argument is not set to AlwaysAllow (Automated)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --authorization-mode path: '{.authorization.mode}' compare: op: nothave value: AlwaysAllow remediation: | If using a Kubelet config file, edit the file to set `authorization.mode` to Webhook. If using executable arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_AUTHZ_ARGS variable. --authorization-mode=Webhook Based on your system, restart the kubelet service. For example, systemctl daemon-reload systemctl restart kubelet.service scored: true - id: 4.2.3 text: "Ensure that the --client-ca-file argument is set as appropriate (Automated)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --client-ca-file path: '{.authentication.x509.clientCAFile}' remediation: | If using a Kubelet config file, edit the file to set `authentication.x509.clientCAFile` to the location of the client CA file. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_AUTHZ_ARGS variable. --client-ca-file= Based on your system, restart the kubelet service. For example, systemctl daemon-reload systemctl restart kubelet.service scored: true - id: 4.2.4 text: "Verify that the --read-only-port argument is set to 0 (Manual)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: bin_op: or test_items: - flag: "--read-only-port" path: '{.readOnlyPort}' compare: op: eq value: 0 - flag: "--read-only-port" path: '{.readOnlyPort}' set: false remediation: | If using a Kubelet config file, edit the file to set `readOnlyPort` to 0. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_SYSTEM_PODS_ARGS variable. --read-only-port=0 Based on your system, restart the kubelet service. For example, systemctl daemon-reload systemctl restart kubelet.service scored: false - id: 4.2.5 text: "Ensure that the --streaming-connection-idle-timeout argument is not set to 0 (Manual)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --streaming-connection-idle-timeout path: '{.streamingConnectionIdleTimeout}' compare: op: noteq value: 0 - flag: --streaming-connection-idle-timeout path: '{.streamingConnectionIdleTimeout}' set: false bin_op: or remediation: | If using a Kubelet config file, edit the file to set `streamingConnectionIdleTimeout` to a value other than 0. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_SYSTEM_PODS_ARGS variable. --streaming-connection-idle-timeout=5m Based on your system, restart the kubelet service. For example, systemctl daemon-reload systemctl restart kubelet.service scored: false - id: 4.2.6 text: "Ensure that the --make-iptables-util-chains argument is set to true (Automated)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --make-iptables-util-chains path: '{.makeIPTablesUtilChains}' compare: op: eq value: true - flag: --make-iptables-util-chains path: '{.makeIPTablesUtilChains}' set: false bin_op: or remediation: | If using a Kubelet config file, edit the file to set `makeIPTablesUtilChains` to `true`. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and remove the --make-iptables-util-chains argument from the KUBELET_SYSTEM_PODS_ARGS variable. Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: true - id: 4.2.7 text: "Ensure that the --hostname-override argument is not set (Manual)" # This is one of those properties that can only be set as a command line argument. # To check if the property is set as expected, we need to parse the kubelet command # instead reading the Kubelet Configuration file. audit: "/bin/ps -fC $kubeletbin " tests: test_items: - flag: --hostname-override set: false remediation: | Edit the kubelet service file $kubeletsvc on each worker node and remove the --hostname-override argument from the KUBELET_SYSTEM_PODS_ARGS variable. Based on your system, restart the kubelet service. For example, systemctl daemon-reload systemctl restart kubelet.service scored: false - id: 4.2.8 text: "Ensure that the eventRecordQPS argument is set to a level which ensures appropriate event capture (Manual)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --event-qps path: '{.eventRecordQPS}' compare: op: gte value: 0 - flag: --event-qps path: '{.eventRecordQPS}' set: false bin_op: or remediation: | If using a Kubelet config file, edit the file to set `eventRecordQPS` to an appropriate level. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_SYSTEM_PODS_ARGS variable. Based on your system, restart the kubelet service. For example, systemctl daemon-reload systemctl restart kubelet.service scored: false - id: 4.2.9 text: "Ensure that the --tls-cert-file and --tls-private-key-file arguments are set as appropriate (Manual)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --tls-cert-file path: '{.tlsCertFile}' - flag: --tls-private-key-file path: '{.tlsPrivateKeyFile}' remediation: | If using a Kubelet config file, edit the file to set `tlsCertFile` to the location of the certificate file to use to identify this Kubelet, and `tlsPrivateKeyFile` to the location of the corresponding private key file. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameters in KUBELET_CERTIFICATE_ARGS variable. --tls-cert-file= --tls-private-key-file= Based on your system, restart the kubelet service. For example, systemctl daemon-reload systemctl restart kubelet.service scored: false - id: 4.2.10 text: "Ensure that the --rotate-certificates argument is not set to false (Automated)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --rotate-certificates path: '{.rotateCertificates}' compare: op: eq value: true - flag: --rotate-certificates path: '{.rotateCertificates}' set: false bin_op: or remediation: | If using a Kubelet config file, edit the file to add the line `rotateCertificates` to `true` or remove it altogether to use the default value. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and remove --rotate-certificates=false argument from the KUBELET_CERTIFICATE_ARGS variable. Based on your system, restart the kubelet service. For example, systemctl daemon-reload systemctl restart kubelet.service scored: true - id: 4.2.11 text: "Verify that the RotateKubeletServerCertificate argument is set to true (Manual)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: bin_op: or test_items: - flag: RotateKubeletServerCertificate path: '{.featureGates.RotateKubeletServerCertificate}' compare: op: nothave value: false - flag: RotateKubeletServerCertificate path: '{.featureGates.RotateKubeletServerCertificate}' set: false remediation: | Edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_CERTIFICATE_ARGS variable. --feature-gates=RotateKubeletServerCertificate=true Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: false - id: 4.2.12 text: "Ensure that the Kubelet only makes use of Strong Cryptographic Ciphers (Manual)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --tls-cipher-suites path: '{range .tlsCipherSuites[:]}{}{'',''}{end}' compare: op: valid_elements value: 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 remediation: | If using a Kubelet config file, edit the file to set `tlsCipherSuites` to 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 or to a subset of these values. If using executable arguments, edit the kubelet service file $kubeletsvc on each worker node and set the --tls-cipher-suites parameter as follows, or to a subset of these values. --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 Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: false - id: 4.2.13 text: "Ensure that a limit is set on pod PIDs (Manual)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --pod-max-pids path: '{.podPidsLimit}' remediation: | Decide on an appropriate level for this parameter and set it, either via the --pod-max-pids command line parameter or the PodPidsLimit configuration file setting. scored: false ================================================ FILE: cfg/cis-1.7/policies.yaml ================================================ --- controls: version: "cis-1.7" id: 5 text: "Kubernetes Policies" type: "policies" groups: - id: 5.1 text: "RBAC and Service Accounts" checks: - id: 5.1.1 text: "Ensure that the cluster-admin role is only used where required (Manual)" type: "manual" remediation: | Identify all clusterrolebindings to the cluster-admin role. Check if they are used and if they need this role or if they could use a role with fewer privileges. Where possible, first bind users to a lower privileged role and then remove the clusterrolebinding to the cluster-admin role : kubectl delete clusterrolebinding [name] scored: false - id: 5.1.2 text: "Minimize access to secrets (Manual)" type: "manual" remediation: | Where possible, remove get, list and watch access to Secret objects in the cluster. scored: false - id: 5.1.3 text: "Minimize wildcard use in Roles and ClusterRoles (Manual)" type: "manual" remediation: | Where possible replace any use of wildcards in clusterroles and roles with specific objects or actions. scored: false - id: 5.1.4 text: "Minimize access to create pods (Manual)" type: "manual" remediation: | Where possible, remove create access to pod objects in the cluster. scored: false - id: 5.1.5 text: "Ensure that default service accounts are not actively used. (Manual)" type: "manual" remediation: | Create explicit service accounts wherever a Kubernetes workload requires specific access to the Kubernetes API server. Modify the configuration of each default service account to include this value automountServiceAccountToken: false scored: false - id: 5.1.6 text: "Ensure that Service Account Tokens are only mounted where necessary (Manual)" type: "manual" remediation: | Modify the definition of pods and service accounts which do not need to mount service account tokens to disable it. scored: false - id: 5.1.7 text: "Avoid use of system:masters group (Manual)" type: "manual" remediation: | Remove the system:masters group from all users in the cluster. scored: false - id: 5.1.8 text: "Limit use of the Bind, Impersonate and Escalate permissions in the Kubernetes cluster (Manual)" type: "manual" remediation: | Where possible, remove the impersonate, bind and escalate rights from subjects. scored: false - id: 5.1.9 text: "Minimize access to create persistent volumes (Manual)" type: "manual" remediation: | Where possible, remove create access to PersistentVolume objects in the cluster. scored: false - id: 5.1.10 text: "Minimize access to the proxy sub-resource of nodes (Manual)" type: "manual" remediation: | Where possible, remove access to the proxy sub-resource of node objects. scored: false - id: 5.1.11 text: "Minimize access to the approval sub-resource of certificatesigningrequests objects (Manual)" type: "manual" remediation: | Where possible, remove access to the approval sub-resource of certificatesigningrequest objects. scored: false - id: 5.1.12 text: "Minimize access to webhook configuration objects (Manual)" type: "manual" remediation: | Where possible, remove access to the validatingwebhookconfigurations or mutatingwebhookconfigurations objects scored: false - id: 5.1.13 text: "Minimize access to the service account token creation (Manual)" type: "manual" remediation: | Where possible, remove access to the token sub-resource of serviceaccount objects. scored: false - id: 5.2 text: "Pod Security Standards" checks: - id: 5.2.1 text: "Ensure that the cluster has at least one active policy control mechanism in place (Manual)" type: "manual" remediation: | Ensure that either Pod Security Admission or an external policy control system is in place for every namespace which contains user workloads. scored: false - id: 5.2.2 text: "Minimize the admission of privileged containers (Manual)" type: "manual" remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of privileged containers. scored: false - id: 5.2.3 text: "Minimize the admission of containers wishing to share the host process ID namespace (Automated)" type: "manual" remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of `hostPID` containers. scored: false - id: 5.2.4 text: "Minimize the admission of containers wishing to share the host IPC namespace (Automated)" type: "manual" remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of `hostIPC` containers. scored: false - id: 5.2.5 text: "Minimize the admission of containers wishing to share the host network namespace (Automated)" type: "manual" remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of `hostNetwork` containers. scored: false - id: 5.2.6 text: "Minimize the admission of containers with allowPrivilegeEscalation (Automated)" type: "manual" remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of containers with `.spec.allowPrivilegeEscalation` set to `true`. scored: false - id: 5.2.7 text: "Minimize the admission of root containers (Automated)" type: "manual" remediation: | Create a policy for each namespace in the cluster, ensuring that either `MustRunAsNonRoot` or `MustRunAs` with the range of UIDs not including 0, is set. scored: false - id: 5.2.8 text: "Minimize the admission of containers with the NET_RAW capability (Automated)" type: "manual" remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of containers with the `NET_RAW` capability. scored: false - id: 5.2.9 text: "Minimize the admission of containers with added capabilities (Automated)" type: "manual" remediation: | Ensure that `allowedCapabilities` is not present in policies for the cluster unless it is set to an empty array. scored: false - id: 5.2.10 text: "Minimize the admission of containers with capabilities assigned (Manual)" type: "manual" remediation: | Review the use of capabilites in applications running on your cluster. Where a namespace contains applications which do not require any Linux capabities to operate consider adding a PSP which forbids the admission of containers which do not drop all capabilities. scored: false - id: 5.2.11 text: "Minimize the admission of Windows HostProcess containers (Manual)" type: "manual" remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of containers that have `.securityContext.windowsOptions.hostProcess` set to `true`. scored: false - id: 5.2.12 text: "Minimize the admission of HostPath volumes (Manual)" type: "manual" remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of containers with `hostPath` volumes. scored: false - id: 5.2.13 text: "Minimize the admission of containers which use HostPorts (Manual)" type: "manual" remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of containers which use `hostPort` sections. scored: false - id: 5.3 text: "Network Policies and CNI" checks: - id: 5.3.1 text: "Ensure that the CNI in use supports NetworkPolicies (Manual)" type: "manual" remediation: | If the CNI plugin in use does not support network policies, consideration should be given to making use of a different plugin, or finding an alternate mechanism for restricting traffic in the Kubernetes cluster. scored: false - id: 5.3.2 text: "Ensure that all Namespaces have NetworkPolicies defined (Manual)" type: "manual" remediation: | Follow the documentation and create NetworkPolicy objects as you need them. scored: false - id: 5.4 text: "Secrets Management" checks: - id: 5.4.1 text: "Prefer using Secrets as files over Secrets as environment variables (Manual)" type: "manual" remediation: | If possible, rewrite application code to read Secrets from mounted secret files, rather than from environment variables. scored: false - id: 5.4.2 text: "Consider external secret storage (Manual)" type: "manual" remediation: | Refer to the Secrets management options offered by your cloud provider or a third-party secrets management solution. scored: false - id: 5.5 text: "Extensible Admission Control" checks: - id: 5.5.1 text: "Configure Image Provenance using ImagePolicyWebhook admission controller (Manual)" type: "manual" remediation: | Follow the Kubernetes documentation and setup image provenance. scored: false - id: 5.7 text: "General Policies" checks: - id: 5.7.1 text: "Create administrative boundaries between resources using namespaces (Manual)" type: "manual" remediation: | Follow the documentation and create namespaces for objects in your deployment as you need them. scored: false - id: 5.7.2 text: "Ensure that the seccomp profile is set to docker/default in your Pod definitions (Manual)" type: "manual" remediation: | Use `securityContext` to enable the docker/default seccomp profile in your pod definitions. An example is as below: securityContext: seccompProfile: type: RuntimeDefault scored: false - id: 5.7.3 text: "Apply SecurityContext to your Pods and Containers (Manual)" type: "manual" remediation: | Follow the Kubernetes documentation and apply SecurityContexts to your Pods. For a suggested list of SecurityContexts, you may refer to the CIS Security Benchmark for Docker Containers. scored: false - id: 5.7.4 text: "The default namespace should not be used (Manual)" type: "manual" remediation: | Ensure that namespaces are created to allow for appropriate segregation of Kubernetes resources and that all new resources are created in a specific namespace. scored: false ================================================ FILE: cfg/cis-1.8/config.yaml ================================================ --- ## Version-specific settings that override the values in cfg/config.yaml ================================================ FILE: cfg/cis-1.8/controlplane.yaml ================================================ --- controls: version: "cis-1.8" id: 3 text: "Control Plane Configuration" type: "controlplane" groups: - id: 3.1 text: "Authentication and Authorization" checks: - id: 3.1.1 text: "Client certificate authentication should not be used for users (Manual)" type: "manual" remediation: | Alternative mechanisms provided by Kubernetes such as the use of OIDC should be implemented in place of client certificates. scored: false - id: 3.1.2 text: "Service account token authentication should not be used for users (Manual)" type: "manual" remediation: | Alternative mechanisms provided by Kubernetes such as the use of OIDC should be implemented in place of service account tokens. scored: false - id: 3.1.3 text: "Bootstrap token authentication should not be used for users (Manual)" type: "manual" remediation: | Alternative mechanisms provided by Kubernetes such as the use of OIDC should be implemented in place of bootstrap tokens. scored: false - id: 3.2 text: "Logging" checks: - id: 3.2.1 text: "Ensure that a minimal audit policy is created (Manual)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--audit-policy-file" set: true remediation: | Create an audit policy file for your cluster. scored: false - id: 3.2.2 text: "Ensure that the audit policy covers key security concerns (Manual)" type: "manual" remediation: | Review the audit policy provided for the cluster and ensure that it covers at least the following areas, - Access to Secrets managed by the cluster. Care should be taken to only log Metadata for requests to Secrets, ConfigMaps, and TokenReviews, in order to avoid risk of logging sensitive data. - Modification of Pod and Deployment objects. - Use of `pods/exec`, `pods/portforward`, `pods/proxy` and `services/proxy`. For most requests, minimally logging at the Metadata level is recommended (the most basic level of logging). scored: false ================================================ FILE: cfg/cis-1.8/etcd.yaml ================================================ --- controls: version: "cis-1.8" id: 2 text: "Etcd Node Configuration" type: "etcd" groups: - id: 2 text: "Etcd Node Configuration" checks: - id: 2.1 text: "Ensure that the --cert-file and --key-file arguments are set as appropriate (Automated)" audit: "/bin/ps -ef | /bin/grep $etcdbin | /bin/grep -v grep" tests: bin_op: and test_items: - flag: "--cert-file" env: "ETCD_CERT_FILE" - flag: "--key-file" env: "ETCD_KEY_FILE" remediation: | Follow the etcd service documentation and configure TLS encryption. Then, edit the etcd pod specification file /etc/kubernetes/manifests/etcd.yaml on the master node and set the below parameters. --cert-file= --key-file= scored: true - id: 2.2 text: "Ensure that the --client-cert-auth argument is set to true (Automated)" audit: "/bin/ps -ef | /bin/grep $etcdbin | /bin/grep -v grep" tests: test_items: - flag: "--client-cert-auth" env: "ETCD_CLIENT_CERT_AUTH" compare: op: eq value: true remediation: | Edit the etcd pod specification file $etcdconf on the master node and set the below parameter. --client-cert-auth="true" scored: true - id: 2.3 text: "Ensure that the --auto-tls argument is not set to true (Automated)" audit: "/bin/ps -ef | /bin/grep $etcdbin | /bin/grep -v grep" tests: bin_op: or test_items: - flag: "--auto-tls" env: "ETCD_AUTO_TLS" set: false - flag: "--auto-tls" env: "ETCD_AUTO_TLS" compare: op: eq value: false remediation: | Edit the etcd pod specification file $etcdconf on the master node and either remove the --auto-tls parameter or set it to false. --auto-tls=false scored: true - id: 2.4 text: "Ensure that the --peer-cert-file and --peer-key-file arguments are set as appropriate (Automated)" audit: "/bin/ps -ef | /bin/grep $etcdbin | /bin/grep -v grep" tests: bin_op: and test_items: - flag: "--peer-cert-file" env: "ETCD_PEER_CERT_FILE" - flag: "--peer-key-file" env: "ETCD_PEER_KEY_FILE" remediation: | Follow the etcd service documentation and configure peer TLS encryption as appropriate for your etcd cluster. Then, edit the etcd pod specification file $etcdconf on the master node and set the below parameters. --peer-client-file= --peer-key-file= scored: true - id: 2.5 text: "Ensure that the --peer-client-cert-auth argument is set to true (Automated)" audit: "/bin/ps -ef | /bin/grep $etcdbin | /bin/grep -v grep" tests: test_items: - flag: "--peer-client-cert-auth" env: "ETCD_PEER_CLIENT_CERT_AUTH" compare: op: eq value: true remediation: | Edit the etcd pod specification file $etcdconf on the master node and set the below parameter. --peer-client-cert-auth=true scored: true - id: 2.6 text: "Ensure that the --peer-auto-tls argument is not set to true (Automated)" audit: "/bin/ps -ef | /bin/grep $etcdbin | /bin/grep -v grep" tests: bin_op: or test_items: - flag: "--peer-auto-tls" env: "ETCD_PEER_AUTO_TLS" set: false - flag: "--peer-auto-tls" env: "ETCD_PEER_AUTO_TLS" compare: op: eq value: false remediation: | Edit the etcd pod specification file $etcdconf on the master node and either remove the --peer-auto-tls parameter or set it to false. --peer-auto-tls=false scored: true - id: 2.7 text: "Ensure that a unique Certificate Authority is used for etcd (Manual)" audit: "/bin/ps -ef | /bin/grep $etcdbin | /bin/grep -v grep" tests: test_items: - flag: "--trusted-ca-file" env: "ETCD_TRUSTED_CA_FILE" remediation: | [Manual test] Follow the etcd documentation and create a dedicated certificate authority setup for the etcd service. Then, edit the etcd pod specification file $etcdconf on the master node and set the below parameter. --trusted-ca-file= scored: false ================================================ FILE: cfg/cis-1.8/master.yaml ================================================ --- controls: version: "cis-1.8" id: 1 text: "Control Plane Security Configuration" type: "master" groups: - id: 1.1 text: "Control Plane Node Configuration Files" checks: - id: 1.1.1 text: "Ensure that the API server pod specification file permissions are set to 600 or more restrictive (Automated)" audit: "/bin/sh -c 'if test -e $apiserverconf; then stat -c permissions=%a $apiserverconf; fi'" tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chmod 600 $apiserverconf scored: true - id: 1.1.2 text: "Ensure that the API server pod specification file ownership is set to root:root (Automated)" audit: "/bin/sh -c 'if test -e $apiserverconf; then stat -c %U:%G $apiserverconf; fi'" tests: test_items: - flag: "root:root" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chown root:root $apiserverconf scored: true - id: 1.1.3 text: "Ensure that the controller manager pod specification file permissions are set to 600 or more restrictive (Automated)" audit: "/bin/sh -c 'if test -e $controllermanagerconf; then stat -c permissions=%a $controllermanagerconf; fi'" tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chmod 600 $controllermanagerconf scored: true - id: 1.1.4 text: "Ensure that the controller manager pod specification file ownership is set to root:root (Automated)" audit: "/bin/sh -c 'if test -e $controllermanagerconf; then stat -c %U:%G $controllermanagerconf; fi'" tests: test_items: - flag: "root:root" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chown root:root $controllermanagerconf scored: true - id: 1.1.5 text: "Ensure that the scheduler pod specification file permissions are set to 600 or more restrictive (Automated)" audit: "/bin/sh -c 'if test -e $schedulerconf; then stat -c permissions=%a $schedulerconf; fi'" tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chmod 600 $schedulerconf scored: true - id: 1.1.6 text: "Ensure that the scheduler pod specification file ownership is set to root:root (Automated)" audit: "/bin/sh -c 'if test -e $schedulerconf; then stat -c %U:%G $schedulerconf; fi'" tests: test_items: - flag: "root:root" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chown root:root $schedulerconf scored: true - id: 1.1.7 text: "Ensure that the etcd pod specification file permissions are set to 600 or more restrictive (Automated)" audit: "/bin/sh -c 'if test -e $etcdconf; then find $etcdconf -name '*etcd*' | xargs stat -c permissions=%a; fi'" use_multiple_values: true tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chmod 600 $etcdconf scored: true - id: 1.1.8 text: "Ensure that the etcd pod specification file ownership is set to root:root (Automated)" audit: "/bin/sh -c 'if test -e $etcdconf; then find $etcdconf -name '*etcd*' | xargs stat -c %U:%G; fi'" use_multiple_values: true tests: test_items: - flag: "root:root" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chown root:root $etcdconf scored: true - id: 1.1.9 text: "Ensure that the Container Network Interface file permissions are set to 600 or more restrictive (Manual)" audit: | ps -ef | grep $kubeletbin | grep -- --cni-conf-dir | sed 's%.*cni-conf-dir[= ]\([^ ]*\).*%\1%' | xargs -I{} find {} -mindepth 1 | xargs --no-run-if-empty stat -c permissions=%a find /var/lib/cni/networks -type f 2> /dev/null | xargs --no-run-if-empty stat -c permissions=%a use_multiple_values: true tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chmod 600 scored: false - id: 1.1.10 text: "Ensure that the Container Network Interface file ownership is set to root:root (Manual)" audit: | ps -ef | grep $kubeletbin | grep -- --cni-conf-dir | sed 's%.*cni-conf-dir[= ]\([^ ]*\).*%\1%' | xargs -I{} find {} -mindepth 1 | xargs --no-run-if-empty stat -c %U:%G find /var/lib/cni/networks -type f 2> /dev/null | xargs --no-run-if-empty stat -c %U:%G use_multiple_values: true tests: test_items: - flag: "root:root" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chown root:root scored: false - id: 1.1.11 text: "Ensure that the etcd data directory permissions are set to 700 or more restrictive (Automated)" audit: | DATA_DIR='' for d in $(ps -ef | grep $etcdbin | grep -- --data-dir | sed 's%.*data-dir[= ]\([^ ]*\).*%\1%'); do if test -d "$d"; then DATA_DIR="$d"; fi done if ! test -d "$DATA_DIR"; then DATA_DIR=$etcddatadir; fi stat -c permissions=%a "$DATA_DIR" tests: test_items: - flag: "permissions" compare: op: bitmask value: "700" remediation: | On the etcd server node, get the etcd data directory, passed as an argument --data-dir, from the command 'ps -ef | grep etcd'. Run the below command (based on the etcd data directory found above). For example, chmod 700 /var/lib/etcd scored: true - id: 1.1.12 text: "Ensure that the etcd data directory ownership is set to etcd:etcd (Automated)" audit: | DATA_DIR='' for d in $(ps -ef | grep $etcdbin | grep -- --data-dir | sed 's%.*data-dir[= ]\([^ ]*\).*%\1%'); do if test -d "$d"; then DATA_DIR="$d"; fi done if ! test -d "$DATA_DIR"; then DATA_DIR=$etcddatadir; fi stat -c %U:%G "$DATA_DIR" tests: test_items: - flag: "etcd:etcd" remediation: | On the etcd server node, get the etcd data directory, passed as an argument --data-dir, from the command 'ps -ef | grep etcd'. Run the below command (based on the etcd data directory found above). For example, chown etcd:etcd /var/lib/etcd scored: true - id: 1.1.13 text: "Ensure that the admin.conf file permissions are set to 600 or more restrictive (Automated)" audit: "/bin/sh -c 'if test -e /etc/kubernetes/admin.conf; then stat -c permissions=%a /etc/kubernetes/admin.conf; fi'" tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chmod 600 /etc/kubernetes/admin.conf scored: true - id: 1.1.14 text: "Ensure that the admin.conf file ownership is set to root:root (Automated)" audit: "/bin/sh -c 'if test -e /etc/kubernetes/admin.conf; then stat -c %U:%G /etc/kubernetes/admin.conf; fi'" tests: test_items: - flag: "root:root" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chown root:root /etc/kubernetes/admin.conf scored: true - id: 1.1.15 text: "Ensure that the scheduler.conf file permissions are set to 600 or more restrictive (Automated)" audit: "/bin/sh -c 'if test -e $schedulerkubeconfig; then stat -c permissions=%a $schedulerkubeconfig; fi'" tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chmod 600 $schedulerkubeconfig scored: true - id: 1.1.16 text: "Ensure that the scheduler.conf file ownership is set to root:root (Automated)" audit: "/bin/sh -c 'if test -e $schedulerkubeconfig; then stat -c %U:%G $schedulerkubeconfig; fi'" tests: test_items: - flag: "root:root" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chown root:root $schedulerkubeconfig scored: true - id: 1.1.17 text: "Ensure that the controller-manager.conf file permissions are set to 600 or more restrictive (Automated)" audit: "/bin/sh -c 'if test -e $controllermanagerkubeconfig; then stat -c permissions=%a $controllermanagerkubeconfig; fi'" tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chmod 600 $controllermanagerkubeconfig scored: true - id: 1.1.18 text: "Ensure that the controller-manager.conf file ownership is set to root:root (Automated)" audit: "/bin/sh -c 'if test -e $controllermanagerkubeconfig; then stat -c %U:%G $controllermanagerkubeconfig; fi'" tests: test_items: - flag: "root:root" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chown root:root $controllermanagerkubeconfig scored: true - id: 1.1.19 text: "Ensure that the Kubernetes PKI directory and file ownership is set to root:root (Automated)" audit: "find /etc/kubernetes/pki/ | xargs stat -c %U:%G" use_multiple_values: true tests: test_items: - flag: "root:root" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chown -R root:root /etc/kubernetes/pki/ scored: true - id: 1.1.20 text: "Ensure that the Kubernetes PKI certificate file permissions are set to 600 or more restrictive (Manual)" audit: "find /etc/kubernetes/pki/ -name '*.crt' | xargs stat -c permissions=%a" use_multiple_values: true tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chmod -R 600 /etc/kubernetes/pki/*.crt scored: false - id: 1.1.21 text: "Ensure that the Kubernetes PKI key file permissions are set to 600 (Manual)" audit: "find /etc/kubernetes/pki/ -name '*.key' | xargs stat -c permissions=%a" use_multiple_values: true tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chmod -R 600 /etc/kubernetes/pki/*.key scored: false - id: 1.2 text: "API Server" checks: - id: 1.2.1 text: "Ensure that the --anonymous-auth argument is set to false (Manual)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--anonymous-auth" compare: op: eq value: false remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the below parameter. --anonymous-auth=false scored: false - id: 1.2.2 text: "Ensure that the --token-auth-file parameter is not set (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--token-auth-file" set: false remediation: | Follow the documentation and configure alternate mechanisms for authentication. Then, edit the API server pod specification file $apiserverconf on the control plane node and remove the --token-auth-file= parameter. scored: true - id: 1.2.3 text: "Ensure that the --DenyServiceExternalIPs is set (Manual)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--enable-admission-plugins" compare: op: has value: "DenyServiceExternalIPs" remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and add the `DenyServiceExternalIPs` plugin to the enabled admission plugins, as such --enable-admission-plugin=DenyServiceExternalIPs. scored: false - id: 1.2.4 text: "Ensure that the --kubelet-client-certificate and --kubelet-client-key arguments are set as appropriate (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: bin_op: and test_items: - flag: "--kubelet-client-certificate" - flag: "--kubelet-client-key" remediation: | Follow the Kubernetes documentation and set up the TLS connection between the apiserver and kubelets. Then, edit API server pod specification file $apiserverconf on the control plane node and set the kubelet client certificate and key parameters as below. --kubelet-client-certificate= --kubelet-client-key= scored: true - id: 1.2.5 text: "Ensure that the --kubelet-certificate-authority argument is set as appropriate (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--kubelet-certificate-authority" remediation: | Follow the Kubernetes documentation and setup the TLS connection between the apiserver and kubelets. Then, edit the API server pod specification file $apiserverconf on the control plane node and set the --kubelet-certificate-authority parameter to the path to the cert file for the certificate authority. --kubelet-certificate-authority= scored: true - id: 1.2.6 text: "Ensure that the --authorization-mode argument is not set to AlwaysAllow (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--authorization-mode" compare: op: nothave value: "AlwaysAllow" remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the --authorization-mode parameter to values other than AlwaysAllow. One such example could be as below. --authorization-mode=RBAC scored: true - id: 1.2.7 text: "Ensure that the --authorization-mode argument includes Node (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--authorization-mode" compare: op: has value: "Node" remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the --authorization-mode parameter to a value that includes Node. --authorization-mode=Node,RBAC scored: true - id: 1.2.8 text: "Ensure that the --authorization-mode argument includes RBAC (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--authorization-mode" compare: op: has value: "RBAC" remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the --authorization-mode parameter to a value that includes RBAC, for example `--authorization-mode=Node,RBAC`. scored: true - id: 1.2.9 text: "Ensure that the admission control plugin EventRateLimit is set (Manual)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--enable-admission-plugins" compare: op: has value: "EventRateLimit" remediation: | Follow the Kubernetes documentation and set the desired limits in a configuration file. Then, edit the API server pod specification file $apiserverconf and set the below parameters. --enable-admission-plugins=...,EventRateLimit,... --admission-control-config-file= scored: false - id: 1.2.10 text: "Ensure that the admission control plugin AlwaysAdmit is not set (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: bin_op: or test_items: - flag: "--enable-admission-plugins" compare: op: nothave value: AlwaysAdmit - flag: "--enable-admission-plugins" set: false remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and either remove the --enable-admission-plugins parameter, or set it to a value that does not include AlwaysAdmit. scored: true - id: 1.2.11 text: "Ensure that the admission control plugin AlwaysPullImages is set (Manual)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--enable-admission-plugins" compare: op: has value: "AlwaysPullImages" remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the --enable-admission-plugins parameter to include AlwaysPullImages. --enable-admission-plugins=...,AlwaysPullImages,... scored: false - id: 1.2.12 text: "Ensure that the admission control plugin SecurityContextDeny is set if PodSecurityPolicy is not used (Manual)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: bin_op: or test_items: - flag: "--enable-admission-plugins" compare: op: has value: "SecurityContextDeny" - flag: "--enable-admission-plugins" compare: op: has value: "PodSecurityPolicy" remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the --enable-admission-plugins parameter to include SecurityContextDeny, unless PodSecurityPolicy is already in place. --enable-admission-plugins=...,SecurityContextDeny,... scored: false - id: 1.2.13 text: "Ensure that the admission control plugin ServiceAccount is set (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: bin_op: or test_items: - flag: "--disable-admission-plugins" compare: op: nothave value: "ServiceAccount" - flag: "--disable-admission-plugins" set: false remediation: | Follow the documentation and create ServiceAccount objects as per your environment. Then, edit the API server pod specification file $apiserverconf on the control plane node and ensure that the --disable-admission-plugins parameter is set to a value that does not include ServiceAccount. scored: true - id: 1.2.14 text: "Ensure that the admission control plugin NamespaceLifecycle is set (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: bin_op: or test_items: - flag: "--disable-admission-plugins" compare: op: nothave value: "NamespaceLifecycle" - flag: "--disable-admission-plugins" set: false remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the --disable-admission-plugins parameter to ensure it does not include NamespaceLifecycle. scored: true - id: 1.2.15 text: "Ensure that the admission control plugin NodeRestriction is set (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--enable-admission-plugins" compare: op: has value: "NodeRestriction" remediation: | Follow the Kubernetes documentation and configure NodeRestriction plug-in on kubelets. Then, edit the API server pod specification file $apiserverconf on the control plane node and set the --enable-admission-plugins parameter to a value that includes NodeRestriction. --enable-admission-plugins=...,NodeRestriction,... scored: true - id: 1.2.16 text: "Ensure that the --profiling argument is set to false (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--profiling" compare: op: eq value: false remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the below parameter. --profiling=false scored: true - id: 1.2.17 text: "Ensure that the --audit-log-path argument is set (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--audit-log-path" remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the --audit-log-path parameter to a suitable path and file where you would like audit logs to be written, for example, --audit-log-path=/var/log/apiserver/audit.log scored: true - id: 1.2.18 text: "Ensure that the --audit-log-maxage argument is set to 30 or as appropriate (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--audit-log-maxage" compare: op: gte value: 30 remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the --audit-log-maxage parameter to 30 or as an appropriate number of days, for example, --audit-log-maxage=30 scored: true - id: 1.2.19 text: "Ensure that the --audit-log-maxbackup argument is set to 10 or as appropriate (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--audit-log-maxbackup" compare: op: gte value: 10 remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the --audit-log-maxbackup parameter to 10 or to an appropriate value. For example, --audit-log-maxbackup=10 scored: true - id: 1.2.20 text: "Ensure that the --audit-log-maxsize argument is set to 100 or as appropriate (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--audit-log-maxsize" compare: op: gte value: 100 remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the --audit-log-maxsize parameter to an appropriate size in MB. For example, to set it as 100 MB, --audit-log-maxsize=100 scored: true - id: 1.2.21 text: "Ensure that the --request-timeout argument is set as appropriate (Manual)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" type: manual remediation: | Edit the API server pod specification file $apiserverconf and set the below parameter as appropriate and if needed. For example, --request-timeout=300s scored: false - id: 1.2.22 text: "Ensure that the --service-account-lookup argument is set to true (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: bin_op: or test_items: - flag: "--service-account-lookup" set: false - flag: "--service-account-lookup" compare: op: eq value: true remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the below parameter. --service-account-lookup=true Alternatively, you can delete the --service-account-lookup parameter from this file so that the default takes effect. scored: true - id: 1.2.23 text: "Ensure that the --service-account-key-file argument is set as appropriate (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--service-account-key-file" remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the --service-account-key-file parameter to the public key file for service accounts. For example, --service-account-key-file= scored: true - id: 1.2.24 text: "Ensure that the --etcd-certfile and --etcd-keyfile arguments are set as appropriate (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: bin_op: and test_items: - flag: "--etcd-certfile" - flag: "--etcd-keyfile" remediation: | Follow the Kubernetes documentation and set up the TLS connection between the apiserver and etcd. Then, edit the API server pod specification file $apiserverconf on the control plane node and set the etcd certificate and key file parameters. --etcd-certfile= --etcd-keyfile= scored: true - id: 1.2.25 text: "Ensure that the --tls-cert-file and --tls-private-key-file arguments are set as appropriate (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: bin_op: and test_items: - flag: "--tls-cert-file" - flag: "--tls-private-key-file" remediation: | Follow the Kubernetes documentation and set up the TLS connection on the apiserver. Then, edit the API server pod specification file $apiserverconf on the control plane node and set the TLS certificate and private key file parameters. --tls-cert-file= --tls-private-key-file= scored: true - id: 1.2.26 text: "Ensure that the --client-ca-file argument is set as appropriate (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--client-ca-file" remediation: | Follow the Kubernetes documentation and set up the TLS connection on the apiserver. Then, edit the API server pod specification file $apiserverconf on the control plane node and set the client certificate authority file. --client-ca-file= scored: true - id: 1.2.27 text: "Ensure that the --etcd-cafile argument is set as appropriate (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--etcd-cafile" remediation: | Follow the Kubernetes documentation and set up the TLS connection between the apiserver and etcd. Then, edit the API server pod specification file $apiserverconf on the control plane node and set the etcd certificate authority file parameter. --etcd-cafile= scored: true - id: 1.2.28 text: "Ensure that the --encryption-provider-config argument is set as appropriate (Manual)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--encryption-provider-config" remediation: | Follow the Kubernetes documentation and configure a EncryptionConfig file. Then, edit the API server pod specification file $apiserverconf on the control plane node and set the --encryption-provider-config parameter to the path of that file. For example, --encryption-provider-config= scored: false - id: 1.2.29 text: "Ensure that encryption providers are appropriately configured (Manual)" audit: | ENCRYPTION_PROVIDER_CONFIG=$(ps -ef | grep $apiserverbin | grep -- --encryption-provider-config | sed 's%.*encryption-provider-config[= ]\([^ ]*\).*%\1%') if test -e $ENCRYPTION_PROVIDER_CONFIG; then grep -A1 'providers:' $ENCRYPTION_PROVIDER_CONFIG | tail -n1 | grep -o "[A-Za-z]*" | sed 's/^/provider=/'; fi tests: test_items: - flag: "provider" compare: op: valid_elements value: "aescbc,kms,secretbox" remediation: | Follow the Kubernetes documentation and configure a EncryptionConfig file. In this file, choose aescbc, kms or secretbox as the encryption provider. scored: false - id: 1.2.30 text: "Ensure that the API Server only makes use of Strong Cryptographic Ciphers (Manual)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--tls-cipher-suites" compare: op: valid_elements value: "TLS_AES_128_GCM_SHA256,TLS_AES_256_GCM_SHA384,TLS_CHACHA20_POLY1305_SHA256,TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA,TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,TLS_RSA_WITH_3DES_EDE_CBC_SHA,TLS_RSA_WITH_AES_128_CBC_SHA,TLS_RSA_WITH_AES_128_GCM_SHA256,TLS_RSA_WITH_AES_256_CBC_SHA,TLS_RSA_WITH_AES_256_GCM_SHA384" remediation: | Edit the API server pod specification file /etc/kubernetes/manifests/kube-apiserver.yaml on the control plane node and set the below parameter. --tls-cipher-suites=TLS_AES_128_GCM_SHA256,TLS_AES_256_GCM_SHA384,TLS_CHACHA20_POLY1305_SHA256, TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA,TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305, TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,TLS_RSA_WITH_3DES_EDE_CBC_SHA,TLS_RSA_WITH_AES_128_CBC_SHA, TLS_RSA_WITH_AES_128_GCM_SHA256,TLS_RSA_WITH_AES_256_CBC_SHA,TLS_RSA_WITH_AES_256_GCM_SHA384 scored: false - id: 1.3 text: "Controller Manager" checks: - id: 1.3.1 text: "Ensure that the --terminated-pod-gc-threshold argument is set as appropriate (Manual)" audit: "/bin/ps -ef | grep $controllermanagerbin | grep -v grep" tests: test_items: - flag: "--terminated-pod-gc-threshold" remediation: | Edit the Controller Manager pod specification file $controllermanagerconf on the control plane node and set the --terminated-pod-gc-threshold to an appropriate threshold, for example, --terminated-pod-gc-threshold=10 scored: false - id: 1.3.2 text: "Ensure that the --profiling argument is set to false (Automated)" audit: "/bin/ps -ef | grep $controllermanagerbin | grep -v grep" tests: test_items: - flag: "--profiling" compare: op: eq value: false remediation: | Edit the Controller Manager pod specification file $controllermanagerconf on the control plane node and set the below parameter. --profiling=false scored: true - id: 1.3.3 text: "Ensure that the --use-service-account-credentials argument is set to true (Automated)" audit: "/bin/ps -ef | grep $controllermanagerbin | grep -v grep" tests: test_items: - flag: "--use-service-account-credentials" compare: op: noteq value: false remediation: | Edit the Controller Manager pod specification file $controllermanagerconf on the control plane node to set the below parameter. --use-service-account-credentials=true scored: true - id: 1.3.4 text: "Ensure that the --service-account-private-key-file argument is set as appropriate (Automated)" audit: "/bin/ps -ef | grep $controllermanagerbin | grep -v grep" tests: test_items: - flag: "--service-account-private-key-file" remediation: | Edit the Controller Manager pod specification file $controllermanagerconf on the control plane node and set the --service-account-private-key-file parameter to the private key file for service accounts. --service-account-private-key-file= scored: true - id: 1.3.5 text: "Ensure that the --root-ca-file argument is set as appropriate (Automated)" audit: "/bin/ps -ef | grep $controllermanagerbin | grep -v grep" tests: test_items: - flag: "--root-ca-file" remediation: | Edit the Controller Manager pod specification file $controllermanagerconf on the control plane node and set the --root-ca-file parameter to the certificate bundle file`. --root-ca-file= scored: true - id: 1.3.6 text: "Ensure that the RotateKubeletServerCertificate argument is set to true (Automated)" audit: "/bin/ps -ef | grep $controllermanagerbin | grep -v grep" tests: bin_op: or test_items: - flag: "--feature-gates" compare: op: nothave value: "RotateKubeletServerCertificate=false" set: true - flag: "--feature-gates" set: false remediation: | Edit the Controller Manager pod specification file $controllermanagerconf on the control plane node and set the --feature-gates parameter to include RotateKubeletServerCertificate=true. --feature-gates=RotateKubeletServerCertificate=true scored: true - id: 1.3.7 text: "Ensure that the --bind-address argument is set to 127.0.0.1 (Automated)" audit: "/bin/ps -ef | grep $controllermanagerbin | grep -v grep" tests: bin_op: or test_items: - flag: "--bind-address" compare: op: eq value: "127.0.0.1" - flag: "--bind-address" set: false remediation: | Edit the Controller Manager pod specification file $controllermanagerconf on the control plane node and ensure the correct value for the --bind-address parameter scored: true - id: 1.4 text: "Scheduler" checks: - id: 1.4.1 text: "Ensure that the --profiling argument is set to false (Automated)" audit: "/bin/ps -ef | grep $schedulerbin | grep -v grep" tests: test_items: - flag: "--profiling" compare: op: eq value: false remediation: | Edit the Scheduler pod specification file $schedulerconf file on the control plane node and set the below parameter. --profiling=false scored: true - id: 1.4.2 text: "Ensure that the --bind-address argument is set to 127.0.0.1 (Automated)" audit: "/bin/ps -ef | grep $schedulerbin | grep -v grep" tests: bin_op: or test_items: - flag: "--bind-address" compare: op: eq value: "127.0.0.1" - flag: "--bind-address" set: false remediation: | Edit the Scheduler pod specification file $schedulerconf on the control plane node and ensure the correct value for the --bind-address parameter scored: true ================================================ FILE: cfg/cis-1.8/node.yaml ================================================ --- controls: version: "cis-1.8" id: 4 text: "Worker Node Security Configuration" type: "node" groups: - id: 4.1 text: "Worker Node Configuration Files" checks: - id: 4.1.1 text: "Ensure that the kubelet service file permissions are set to 600 or more restrictive (Automated)" audit: '/bin/sh -c ''if test -e $kubeletsvc; then stat -c permissions=%a $kubeletsvc; fi'' ' tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Run the below command (based on the file location on your system) on the each worker node. For example, chmod 600 $kubeletsvc scored: true - id: 4.1.2 text: "Ensure that the kubelet service file ownership is set to root:root (Automated)" audit: '/bin/sh -c "if test -e $kubeletsvc; then stat -c %U:%G $kubeletsvc; else echo \"File not found\"; fi"' tests: bin_op: or test_items: - flag: root:root - flag: "File not found" remediation: | Run the below command (based on the file location on your system) on the each worker node. For example, chown root:root $kubeletsvc scored: true - id: 4.1.3 text: "If proxy kubeconfig file exists ensure permissions are set to 600 or more restrictive (Manual)" audit: '/bin/sh -c ''if test -e $proxykubeconfig; then stat -c permissions=%a $proxykubeconfig; fi'' ' tests: bin_op: or test_items: - flag: "permissions" set: true compare: op: bitmask value: "600" remediation: | Run the below command (based on the file location on your system) on the each worker node. For example, chmod 600 $proxykubeconfig scored: false - id: 4.1.4 text: "If proxy kubeconfig file exists ensure ownership is set to root:root (Manual)" audit: '/bin/sh -c ''if test -e $proxykubeconfig; then stat -c %U:%G $proxykubeconfig; fi'' ' tests: bin_op: or test_items: - flag: root:root remediation: | Run the below command (based on the file location on your system) on the each worker node. For example, chown root:root $proxykubeconfig scored: false - id: 4.1.5 text: "Ensure that the --kubeconfig kubelet.conf file permissions are set to 600 or more restrictive (Automated)" audit: '/bin/sh -c ''if test -e $kubeletkubeconfig; then stat -c permissions=%a $kubeletkubeconfig; fi'' ' tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Run the below command (based on the file location on your system) on the each worker node. For example, chmod 600 $kubeletkubeconfig scored: true - id: 4.1.6 text: "Ensure that the --kubeconfig kubelet.conf file ownership is set to root:root (Automated)" audit: '/bin/sh -c ''if test -e $kubeletkubeconfig; then stat -c %U:%G $kubeletkubeconfig; fi'' ' tests: test_items: - flag: root:root remediation: | Run the below command (based on the file location on your system) on the each worker node. For example, chown root:root $kubeletkubeconfig scored: true - id: 4.1.7 text: "Ensure that the certificate authorities file permissions are set to 600 or more restrictive (Manual)" audit: | CAFILE=$(ps -ef | grep kubelet | grep -v apiserver | grep -- --client-ca-file= | awk -F '--client-ca-file=' '{print $2}' | awk '{print $1}' | uniq) if test -z $CAFILE; then CAFILE=$kubeletcafile; fi if test -e $CAFILE; then stat -c permissions=%a $CAFILE; fi tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Run the following command to modify the file permissions of the --client-ca-file chmod 600 scored: false - id: 4.1.8 text: "Ensure that the client certificate authorities file ownership is set to root:root (Manual)" audit: | CAFILE=$(ps -ef | grep kubelet | grep -v apiserver | grep -- --client-ca-file= | awk -F '--client-ca-file=' '{print $2}' | awk '{print $1}' | uniq) if test -z $CAFILE; then CAFILE=$kubeletcafile; fi if test -e $CAFILE; then stat -c %U:%G $CAFILE; fi tests: test_items: - flag: root:root compare: op: eq value: root:root remediation: | Run the following command to modify the ownership of the --client-ca-file. chown root:root scored: false - id: 4.1.9 text: "If the kubelet config.yaml configuration file is being used validate permissions set to 600 or more restrictive (Automated)" audit: '/bin/sh -c ''if test -e $kubeletconf; then stat -c permissions=%a $kubeletconf; fi'' ' tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Run the following command (using the config file location identified in the Audit step) chmod 600 $kubeletconf scored: true - id: 4.1.10 text: "If the kubelet config.yaml configuration file is being used validate file ownership is set to root:root (Automated)" audit: '/bin/sh -c ''if test -e $kubeletconf; then stat -c %U:%G $kubeletconf; fi'' ' tests: test_items: - flag: root:root remediation: | Run the following command (using the config file location identified in the Audit step) chown root:root $kubeletconf scored: true - id: 4.2 text: "Kubelet" checks: - id: 4.2.1 text: "Ensure that the --anonymous-auth argument is set to false (Automated)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: "--anonymous-auth" path: '{.authentication.anonymous.enabled}' compare: op: eq value: false remediation: | If using a Kubelet config file, edit the file to set `authentication: anonymous: enabled` to `false`. If using executable arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_SYSTEM_PODS_ARGS variable. `--anonymous-auth=false` Based on your system, restart the kubelet service. For example, systemctl daemon-reload systemctl restart kubelet.service scored: true - id: 4.2.2 text: "Ensure that the --authorization-mode argument is not set to AlwaysAllow (Automated)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --authorization-mode path: '{.authorization.mode}' compare: op: nothave value: AlwaysAllow remediation: | If using a Kubelet config file, edit the file to set `authorization.mode` to Webhook. If using executable arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_AUTHZ_ARGS variable. --authorization-mode=Webhook Based on your system, restart the kubelet service. For example, systemctl daemon-reload systemctl restart kubelet.service scored: true - id: 4.2.3 text: "Ensure that the --client-ca-file argument is set as appropriate (Automated)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --client-ca-file path: '{.authentication.x509.clientCAFile}' remediation: | If using a Kubelet config file, edit the file to set `authentication.x509.clientCAFile` to the location of the client CA file. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_AUTHZ_ARGS variable. --client-ca-file= Based on your system, restart the kubelet service. For example, systemctl daemon-reload systemctl restart kubelet.service scored: true - id: 4.2.4 text: "Verify that the --read-only-port argument is set to 0 (Manual)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: bin_op: or test_items: - flag: "--read-only-port" path: '{.readOnlyPort}' compare: op: eq value: 0 - flag: "--read-only-port" path: '{.readOnlyPort}' set: false remediation: | If using a Kubelet config file, edit the file to set `readOnlyPort` to 0. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_SYSTEM_PODS_ARGS variable. --read-only-port=0 Based on your system, restart the kubelet service. For example, systemctl daemon-reload systemctl restart kubelet.service scored: false - id: 4.2.5 text: "Ensure that the --streaming-connection-idle-timeout argument is not set to 0 (Manual)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --streaming-connection-idle-timeout path: '{.streamingConnectionIdleTimeout}' compare: op: noteq value: 0 - flag: --streaming-connection-idle-timeout path: '{.streamingConnectionIdleTimeout}' set: false bin_op: or remediation: | If using a Kubelet config file, edit the file to set `streamingConnectionIdleTimeout` to a value other than 0. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_SYSTEM_PODS_ARGS variable. --streaming-connection-idle-timeout=5m Based on your system, restart the kubelet service. For example, systemctl daemon-reload systemctl restart kubelet.service scored: false - id: 4.2.6 text: "Ensure that the --make-iptables-util-chains argument is set to true (Automated)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --make-iptables-util-chains path: '{.makeIPTablesUtilChains}' compare: op: eq value: true - flag: --make-iptables-util-chains path: '{.makeIPTablesUtilChains}' set: false bin_op: or remediation: | If using a Kubelet config file, edit the file to set `makeIPTablesUtilChains` to `true`. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and remove the --make-iptables-util-chains argument from the KUBELET_SYSTEM_PODS_ARGS variable. Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: true - id: 4.2.7 text: "Ensure that the --hostname-override argument is not set (Manual)" # This is one of those properties that can only be set as a command line argument. # To check if the property is set as expected, we need to parse the kubelet command # instead reading the Kubelet Configuration file. audit: "/bin/ps -fC $kubeletbin " tests: test_items: - flag: --hostname-override set: false remediation: | Edit the kubelet service file $kubeletsvc on each worker node and remove the --hostname-override argument from the KUBELET_SYSTEM_PODS_ARGS variable. Based on your system, restart the kubelet service. For example, systemctl daemon-reload systemctl restart kubelet.service scored: false - id: 4.2.8 text: "Ensure that the eventRecordQPS argument is set to a level which ensures appropriate event capture (Manual)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --event-qps path: '{.eventRecordQPS}' compare: op: gte value: 0 - flag: --event-qps path: '{.eventRecordQPS}' set: false bin_op: or remediation: | If using a Kubelet config file, edit the file to set `eventRecordQPS` to an appropriate level. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_SYSTEM_PODS_ARGS variable. Based on your system, restart the kubelet service. For example, systemctl daemon-reload systemctl restart kubelet.service scored: false - id: 4.2.9 text: "Ensure that the --tls-cert-file and --tls-private-key-file arguments are set as appropriate (Manual)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --tls-cert-file path: '{.tlsCertFile}' - flag: --tls-private-key-file path: '{.tlsPrivateKeyFile}' remediation: | If using a Kubelet config file, edit the file to set `tlsCertFile` to the location of the certificate file to use to identify this Kubelet, and `tlsPrivateKeyFile` to the location of the corresponding private key file. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameters in KUBELET_CERTIFICATE_ARGS variable. --tls-cert-file= --tls-private-key-file= Based on your system, restart the kubelet service. For example, systemctl daemon-reload systemctl restart kubelet.service scored: false - id: 4.2.10 text: "Ensure that the --rotate-certificates argument is not set to false (Automated)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --rotate-certificates path: '{.rotateCertificates}' compare: op: eq value: true - flag: --rotate-certificates path: '{.rotateCertificates}' set: false bin_op: or remediation: | If using a Kubelet config file, edit the file to add the line `rotateCertificates` to `true` or remove it altogether to use the default value. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and remove --rotate-certificates=false argument from the KUBELET_CERTIFICATE_ARGS variable. Based on your system, restart the kubelet service. For example, systemctl daemon-reload systemctl restart kubelet.service scored: true - id: 4.2.11 text: "Verify that the RotateKubeletServerCertificate argument is set to true (Manual)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: bin_op: or test_items: - flag: RotateKubeletServerCertificate path: '{.featureGates.RotateKubeletServerCertificate}' compare: op: nothave value: false - flag: RotateKubeletServerCertificate path: '{.featureGates.RotateKubeletServerCertificate}' set: false remediation: | Edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_CERTIFICATE_ARGS variable. --feature-gates=RotateKubeletServerCertificate=true Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: false - id: 4.2.12 text: "Ensure that the Kubelet only makes use of Strong Cryptographic Ciphers (Manual)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --tls-cipher-suites path: '{range .tlsCipherSuites[:]}{}{'',''}{end}' compare: op: valid_elements value: 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 remediation: | If using a Kubelet config file, edit the file to set `tlsCipherSuites` to 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 or to a subset of these values. If using executable arguments, edit the kubelet service file $kubeletsvc on each worker node and set the --tls-cipher-suites parameter as follows, or to a subset of these values. --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 Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: false - id: 4.2.13 text: "Ensure that a limit is set on pod PIDs (Manual)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --pod-max-pids path: '{.podPidsLimit}' remediation: | Decide on an appropriate level for this parameter and set it, either via the --pod-max-pids command line parameter or the PodPidsLimit configuration file setting. scored: false ================================================ FILE: cfg/cis-1.8/policies.yaml ================================================ --- controls: version: "cis-1.8" id: 5 text: "Kubernetes Policies" type: "policies" groups: - id: 5.1 text: "RBAC and Service Accounts" checks: - id: 5.1.1 text: "Ensure that the cluster-admin role is only used where required (Manual)" type: "manual" remediation: | Identify all clusterrolebindings to the cluster-admin role. Check if they are used and if they need this role or if they could use a role with fewer privileges. Where possible, first bind users to a lower privileged role and then remove the clusterrolebinding to the cluster-admin role : kubectl delete clusterrolebinding [name] scored: false - id: 5.1.2 text: "Minimize access to secrets (Manual)" type: "manual" remediation: | Where possible, remove get, list and watch access to Secret objects in the cluster. scored: false - id: 5.1.3 text: "Minimize wildcard use in Roles and ClusterRoles (Manual)" type: "manual" remediation: | Where possible replace any use of wildcards in clusterroles and roles with specific objects or actions. scored: false - id: 5.1.4 text: "Minimize access to create pods (Manual)" type: "manual" remediation: | Where possible, remove create access to pod objects in the cluster. scored: false - id: 5.1.5 text: "Ensure that default service accounts are not actively used. (Manual)" type: "manual" remediation: | Create explicit service accounts wherever a Kubernetes workload requires specific access to the Kubernetes API server. Modify the configuration of each default service account to include this value automountServiceAccountToken: false scored: false - id: 5.1.6 text: "Ensure that Service Account Tokens are only mounted where necessary (Manual)" type: "manual" remediation: | Modify the definition of pods and service accounts which do not need to mount service account tokens to disable it. scored: false - id: 5.1.7 text: "Avoid use of system:masters group (Manual)" type: "manual" remediation: | Remove the system:masters group from all users in the cluster. scored: false - id: 5.1.8 text: "Limit use of the Bind, Impersonate and Escalate permissions in the Kubernetes cluster (Manual)" type: "manual" remediation: | Where possible, remove the impersonate, bind and escalate rights from subjects. scored: false - id: 5.1.9 text: "Minimize access to create persistent volumes (Manual)" type: "manual" remediation: | Where possible, remove create access to PersistentVolume objects in the cluster. scored: false - id: 5.1.10 text: "Minimize access to the proxy sub-resource of nodes (Manual)" type: "manual" remediation: | Where possible, remove access to the proxy sub-resource of node objects. scored: false - id: 5.1.11 text: "Minimize access to the approval sub-resource of certificatesigningrequests objects (Manual)" type: "manual" remediation: | Where possible, remove access to the approval sub-resource of certificatesigningrequest objects. scored: false - id: 5.1.12 text: "Minimize access to webhook configuration objects (Manual)" type: "manual" remediation: | Where possible, remove access to the validatingwebhookconfigurations or mutatingwebhookconfigurations objects scored: false - id: 5.1.13 text: "Minimize access to the service account token creation (Manual)" type: "manual" remediation: | Where possible, remove access to the token sub-resource of serviceaccount objects. scored: false - id: 5.2 text: "Pod Security Standards" checks: - id: 5.2.1 text: "Ensure that the cluster has at least one active policy control mechanism in place (Manual)" type: "manual" remediation: | Ensure that either Pod Security Admission or an external policy control system is in place for every namespace which contains user workloads. scored: false - id: 5.2.2 text: "Minimize the admission of privileged containers (Manual)" type: "manual" remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of privileged containers. scored: false - id: 5.2.3 text: "Minimize the admission of containers wishing to share the host process ID namespace (Automated)" type: "manual" remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of `hostPID` containers. scored: false - id: 5.2.4 text: "Minimize the admission of containers wishing to share the host IPC namespace (Automated)" type: "manual" remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of `hostIPC` containers. scored: false - id: 5.2.5 text: "Minimize the admission of containers wishing to share the host network namespace (Automated)" type: "manual" remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of `hostNetwork` containers. scored: false - id: 5.2.6 text: "Minimize the admission of containers with allowPrivilegeEscalation (Automated)" type: "manual" remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of containers with `.spec.allowPrivilegeEscalation` set to `true`. scored: false - id: 5.2.7 text: "Minimize the admission of root containers (Automated)" type: "manual" remediation: | Create a policy for each namespace in the cluster, ensuring that either `MustRunAsNonRoot` or `MustRunAs` with the range of UIDs not including 0, is set. scored: false - id: 5.2.8 text: "Minimize the admission of containers with the NET_RAW capability (Automated)" type: "manual" remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of containers with the `NET_RAW` capability. scored: false - id: 5.2.9 text: "Minimize the admission of containers with added capabilities (Automated)" type: "manual" remediation: | Ensure that `allowedCapabilities` is not present in policies for the cluster unless it is set to an empty array. scored: false - id: 5.2.10 text: "Minimize the admission of containers with capabilities assigned (Manual)" type: "manual" remediation: | Review the use of capabilites in applications running on your cluster. Where a namespace contains applications which do not require any Linux capabities to operate consider adding a PSP which forbids the admission of containers which do not drop all capabilities. scored: false - id: 5.2.11 text: "Minimize the admission of Windows HostProcess containers (Manual)" type: "manual" remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of containers that have `.securityContext.windowsOptions.hostProcess` set to `true`. scored: false - id: 5.2.12 text: "Minimize the admission of HostPath volumes (Manual)" type: "manual" remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of containers with `hostPath` volumes. scored: false - id: 5.2.13 text: "Minimize the admission of containers which use HostPorts (Manual)" type: "manual" remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of containers which use `hostPort` sections. scored: false - id: 5.3 text: "Network Policies and CNI" checks: - id: 5.3.1 text: "Ensure that the CNI in use supports NetworkPolicies (Manual)" type: "manual" remediation: | If the CNI plugin in use does not support network policies, consideration should be given to making use of a different plugin, or finding an alternate mechanism for restricting traffic in the Kubernetes cluster. scored: false - id: 5.3.2 text: "Ensure that all Namespaces have NetworkPolicies defined (Manual)" type: "manual" remediation: | Follow the documentation and create NetworkPolicy objects as you need them. scored: false - id: 5.4 text: "Secrets Management" checks: - id: 5.4.1 text: "Prefer using Secrets as files over Secrets as environment variables (Manual)" type: "manual" remediation: | If possible, rewrite application code to read Secrets from mounted secret files, rather than from environment variables. scored: false - id: 5.4.2 text: "Consider external secret storage (Manual)" type: "manual" remediation: | Refer to the Secrets management options offered by your cloud provider or a third-party secrets management solution. scored: false - id: 5.5 text: "Extensible Admission Control" checks: - id: 5.5.1 text: "Configure Image Provenance using ImagePolicyWebhook admission controller (Manual)" type: "manual" remediation: | Follow the Kubernetes documentation and setup image provenance. scored: false - id: 5.7 text: "General Policies" checks: - id: 5.7.1 text: "Create administrative boundaries between resources using namespaces (Manual)" type: "manual" remediation: | Follow the documentation and create namespaces for objects in your deployment as you need them. scored: false - id: 5.7.2 text: "Ensure that the seccomp profile is set to docker/default in your Pod definitions (Manual)" type: "manual" remediation: | Use `securityContext` to enable the docker/default seccomp profile in your pod definitions. An example is as below: securityContext: seccompProfile: type: RuntimeDefault scored: false - id: 5.7.3 text: "Apply SecurityContext to your Pods and Containers (Manual)" type: "manual" remediation: | Follow the Kubernetes documentation and apply SecurityContexts to your Pods. For a suggested list of SecurityContexts, you may refer to the CIS Security Benchmark for Docker Containers. scored: false - id: 5.7.4 text: "The default namespace should not be used (Manual)" type: "manual" remediation: | Ensure that namespaces are created to allow for appropriate segregation of Kubernetes resources and that all new resources are created in a specific namespace. scored: false ================================================ FILE: cfg/cis-1.9/config.yaml ================================================ --- ## Version-specific settings that override the values in cfg/config.yaml ================================================ FILE: cfg/cis-1.9/controlplane.yaml ================================================ --- controls: version: "cis-1.9" id: 3 text: "Control Plane Configuration" type: "controlplane" groups: - id: 3.1 text: "Authentication and Authorization" checks: - id: 3.1.1 text: "Client certificate authentication should not be used for users (Manual)" type: "manual" remediation: | Alternative mechanisms provided by Kubernetes such as the use of OIDC should be implemented in place of client certificates. scored: false - id: 3.1.2 text: "Service account token authentication should not be used for users (Manual)" type: "manual" remediation: | Alternative mechanisms provided by Kubernetes such as the use of OIDC should be implemented in place of service account tokens. scored: false - id: 3.1.3 text: "Bootstrap token authentication should not be used for users (Manual)" type: "manual" remediation: | Alternative mechanisms provided by Kubernetes such as the use of OIDC should be implemented in place of bootstrap tokens. scored: false - id: 3.2 text: "Logging" checks: - id: 3.2.1 text: "Ensure that a minimal audit policy is created (Manual)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--audit-policy-file" set: true remediation: | Create an audit policy file for your cluster. scored: false - id: 3.2.2 text: "Ensure that the audit policy covers key security concerns (Manual)" type: "manual" remediation: | Review the audit policy provided for the cluster and ensure that it covers at least the following areas, - Access to Secrets managed by the cluster. Care should be taken to only log Metadata for requests to Secrets, ConfigMaps, and TokenReviews, in order to avoid risk of logging sensitive data. - Modification of Pod and Deployment objects. - Use of `pods/exec`, `pods/portforward`, `pods/proxy` and `services/proxy`. For most requests, minimally logging at the Metadata level is recommended (the most basic level of logging). scored: false ================================================ FILE: cfg/cis-1.9/etcd.yaml ================================================ --- controls: version: "cis-1.9" id: 2 text: "Etcd Node Configuration" type: "etcd" groups: - id: 2 text: "Etcd Node Configuration" checks: - id: 2.1 text: "Ensure that the --cert-file and --key-file arguments are set as appropriate (Automated)" audit: "/bin/ps -ef | /bin/grep $etcdbin | /bin/grep -v grep" tests: bin_op: and test_items: - flag: "--cert-file" env: "ETCD_CERT_FILE" - flag: "--key-file" env: "ETCD_KEY_FILE" remediation: | Follow the etcd service documentation and configure TLS encryption. Then, edit the etcd pod specification file /etc/kubernetes/manifests/etcd.yaml on the master node and set the below parameters. --cert-file= --key-file= scored: true - id: 2.2 text: "Ensure that the --client-cert-auth argument is set to true (Automated)" audit: "/bin/ps -ef | /bin/grep $etcdbin | /bin/grep -v grep" tests: test_items: - flag: "--client-cert-auth" env: "ETCD_CLIENT_CERT_AUTH" compare: op: eq value: true remediation: | Edit the etcd pod specification file $etcdconf on the master node and set the below parameter. --client-cert-auth="true" scored: true - id: 2.3 text: "Ensure that the --auto-tls argument is not set to true (Automated)" audit: "/bin/ps -ef | /bin/grep $etcdbin | /bin/grep -v grep" tests: bin_op: or test_items: - flag: "--auto-tls" env: "ETCD_AUTO_TLS" set: false - flag: "--auto-tls" env: "ETCD_AUTO_TLS" compare: op: eq value: false remediation: | Edit the etcd pod specification file $etcdconf on the master node and either remove the --auto-tls parameter or set it to false. --auto-tls=false scored: true - id: 2.4 text: "Ensure that the --peer-cert-file and --peer-key-file arguments are set as appropriate (Automated)" audit: "/bin/ps -ef | /bin/grep $etcdbin | /bin/grep -v grep" tests: bin_op: and test_items: - flag: "--peer-cert-file" env: "ETCD_PEER_CERT_FILE" - flag: "--peer-key-file" env: "ETCD_PEER_KEY_FILE" remediation: | Follow the etcd service documentation and configure peer TLS encryption as appropriate for your etcd cluster. Then, edit the etcd pod specification file $etcdconf on the master node and set the below parameters. --peer-client-file= --peer-key-file= scored: true - id: 2.5 text: "Ensure that the --peer-client-cert-auth argument is set to true (Automated)" audit: "/bin/ps -ef | /bin/grep $etcdbin | /bin/grep -v grep" tests: test_items: - flag: "--peer-client-cert-auth" env: "ETCD_PEER_CLIENT_CERT_AUTH" compare: op: eq value: true remediation: | Edit the etcd pod specification file $etcdconf on the master node and set the below parameter. --peer-client-cert-auth=true scored: true - id: 2.6 text: "Ensure that the --peer-auto-tls argument is not set to true (Automated)" audit: "/bin/ps -ef | /bin/grep $etcdbin | /bin/grep -v grep" tests: bin_op: or test_items: - flag: "--peer-auto-tls" env: "ETCD_PEER_AUTO_TLS" set: false - flag: "--peer-auto-tls" env: "ETCD_PEER_AUTO_TLS" compare: op: eq value: false remediation: | Edit the etcd pod specification file $etcdconf on the master node and either remove the --peer-auto-tls parameter or set it to false. --peer-auto-tls=false scored: true - id: 2.7 text: "Ensure that a unique Certificate Authority is used for etcd (Manual)" audit: "/bin/ps -ef | /bin/grep $etcdbin | /bin/grep -v grep" tests: test_items: - flag: "--trusted-ca-file" env: "ETCD_TRUSTED_CA_FILE" remediation: | [Manual test] Follow the etcd documentation and create a dedicated certificate authority setup for the etcd service. Then, edit the etcd pod specification file $etcdconf on the master node and set the below parameter. --trusted-ca-file= scored: false ================================================ FILE: cfg/cis-1.9/master.yaml ================================================ --- controls: version: "cis-1.9" id: 1 text: "Control Plane Security Configuration" type: "master" groups: - id: 1.1 text: "Control Plane Node Configuration Files" checks: - id: 1.1.1 text: "Ensure that the API server pod specification file permissions are set to 600 or more restrictive (Automated)" audit: "/bin/sh -c 'if test -e $apiserverconf; then stat -c permissions=%a $apiserverconf; fi'" tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chmod 600 $apiserverconf scored: true - id: 1.1.2 text: "Ensure that the API server pod specification file ownership is set to root:root (Automated)" audit: "/bin/sh -c 'if test -e $apiserverconf; then stat -c %U:%G $apiserverconf; fi'" tests: test_items: - flag: "root:root" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chown root:root $apiserverconf scored: true - id: 1.1.3 text: "Ensure that the controller manager pod specification file permissions are set to 600 or more restrictive (Automated)" audit: "/bin/sh -c 'if test -e $controllermanagerconf; then stat -c permissions=%a $controllermanagerconf; fi'" tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chmod 600 $controllermanagerconf scored: true - id: 1.1.4 text: "Ensure that the controller manager pod specification file ownership is set to root:root (Automated)" audit: "/bin/sh -c 'if test -e $controllermanagerconf; then stat -c %U:%G $controllermanagerconf; fi'" tests: test_items: - flag: "root:root" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chown root:root $controllermanagerconf scored: true - id: 1.1.5 text: "Ensure that the scheduler pod specification file permissions are set to 600 or more restrictive (Automated)" audit: "/bin/sh -c 'if test -e $schedulerconf; then stat -c permissions=%a $schedulerconf; fi'" tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chmod 600 $schedulerconf scored: true - id: 1.1.6 text: "Ensure that the scheduler pod specification file ownership is set to root:root (Automated)" audit: "/bin/sh -c 'if test -e $schedulerconf; then stat -c %U:%G $schedulerconf; fi'" tests: test_items: - flag: "root:root" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chown root:root $schedulerconf scored: true - id: 1.1.7 text: "Ensure that the etcd pod specification file permissions are set to 600 or more restrictive (Automated)" audit: "/bin/sh -c 'if test -e $etcdconf; then find $etcdconf -name '*etcd*' | xargs stat -c permissions=%a; fi'" use_multiple_values: true tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chmod 600 $etcdconf scored: true - id: 1.1.8 text: "Ensure that the etcd pod specification file ownership is set to root:root (Automated)" audit: "/bin/sh -c 'if test -e $etcdconf; then find $etcdconf -name '*etcd*' | xargs stat -c %U:%G; fi'" use_multiple_values: true tests: test_items: - flag: "root:root" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chown root:root $etcdconf scored: true - id: 1.1.9 text: "Ensure that the Container Network Interface file permissions are set to 600 or more restrictive (Manual)" audit: | ps -ef | grep $kubeletbin | grep -- --cni-conf-dir | sed 's%.*cni-conf-dir[= ]\([^ ]*\).*%\1%' | xargs -I{} find {} -mindepth 1 | xargs --no-run-if-empty stat -c permissions=%a find /var/lib/cni/networks -type f 2> /dev/null | xargs --no-run-if-empty stat -c permissions=%a use_multiple_values: true tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chmod 600 scored: false - id: 1.1.10 text: "Ensure that the Container Network Interface file ownership is set to root:root (Manual)" audit: | ps -ef | grep $kubeletbin | grep -- --cni-conf-dir | sed 's%.*cni-conf-dir[= ]\([^ ]*\).*%\1%' | xargs -I{} find {} -mindepth 1 | xargs --no-run-if-empty stat -c %U:%G find /var/lib/cni/networks -type f 2> /dev/null | xargs --no-run-if-empty stat -c %U:%G use_multiple_values: true tests: test_items: - flag: "root:root" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chown root:root scored: false - id: 1.1.11 text: "Ensure that the etcd data directory permissions are set to 700 or more restrictive (Automated)" audit: | DATA_DIR='' for d in $(ps -ef | grep $etcdbin | grep -- --data-dir | sed 's%.*data-dir[= ]\([^ ]*\).*%\1%'); do if test -d "$d"; then DATA_DIR="$d"; fi done if ! test -d "$DATA_DIR"; then DATA_DIR=$etcddatadir; fi stat -c permissions=%a "$DATA_DIR" tests: test_items: - flag: "permissions" compare: op: bitmask value: "700" remediation: | On the etcd server node, get the etcd data directory, passed as an argument --data-dir, from the command 'ps -ef | grep etcd'. Run the below command (based on the etcd data directory found above). For example, chmod 700 /var/lib/etcd scored: true - id: 1.1.12 text: "Ensure that the etcd data directory ownership is set to etcd:etcd (Automated)" audit: | DATA_DIR='' for d in $(ps -ef | grep $etcdbin | grep -- --data-dir | sed 's%.*data-dir[= ]\([^ ]*\).*%\1%'); do if test -d "$d"; then DATA_DIR="$d"; fi done if ! test -d "$DATA_DIR"; then DATA_DIR=$etcddatadir; fi stat -c %U:%G "$DATA_DIR" tests: test_items: - flag: "etcd:etcd" remediation: | On the etcd server node, get the etcd data directory, passed as an argument --data-dir, from the command 'ps -ef | grep etcd'. Run the below command (based on the etcd data directory found above). For example, chown etcd:etcd /var/lib/etcd scored: true - id: 1.1.13 text: "Ensure that the default administrative credential file permissions are set to 600 (Automated)" audit: | for adminconf in /etc/kubernetes/admin.conf /etc/kubernetes/super-admin.conf; do if test -e $adminconf; then stat -c "permissions=%a %n" $adminconf; fi; done use_multiple_values: true tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chmod 600 /etc/kubernetes/admin.conf On Kubernetes 1.29+ the super-admin.conf file should also be modified, if present. For example, chmod 600 /etc/kubernetes/super-admin.conf scored: true - id: 1.1.14 text: "Ensure that the default administrative credential file ownership is set to root:root (Automated)" audit: | for adminconf in /etc/kubernetes/admin.conf /etc/kubernetes/super-admin.conf; do if test -e $adminconf; then stat -c "ownership=%U:%G %n" $adminconf; fi; done use_multiple_values: true tests: test_items: - flag: "ownership" compare: op: eq value: "root:root" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chown root:root /etc/kubernetes/admin.conf On Kubernetes 1.29+ the super-admin.conf file should also be modified, if present. For example, chown root:root /etc/kubernetes/super-admin.conf scored: true - id: 1.1.15 text: "Ensure that the scheduler.conf file permissions are set to 600 or more restrictive (Automated)" audit: "/bin/sh -c 'if test -e $schedulerkubeconfig; then stat -c permissions=%a $schedulerkubeconfig; fi'" tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chmod 600 $schedulerkubeconfig scored: true - id: 1.1.16 text: "Ensure that the scheduler.conf file ownership is set to root:root (Automated)" audit: "/bin/sh -c 'if test -e $schedulerkubeconfig; then stat -c %U:%G $schedulerkubeconfig; fi'" tests: test_items: - flag: "root:root" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chown root:root $schedulerkubeconfig scored: true - id: 1.1.17 text: "Ensure that the controller-manager.conf file permissions are set to 600 or more restrictive (Automated)" audit: "/bin/sh -c 'if test -e $controllermanagerkubeconfig; then stat -c permissions=%a $controllermanagerkubeconfig; fi'" tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chmod 600 $controllermanagerkubeconfig scored: true - id: 1.1.18 text: "Ensure that the controller-manager.conf file ownership is set to root:root (Automated)" audit: "/bin/sh -c 'if test -e $controllermanagerkubeconfig; then stat -c %U:%G $controllermanagerkubeconfig; fi'" tests: test_items: - flag: "root:root" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chown root:root $controllermanagerkubeconfig scored: true - id: 1.1.19 text: "Ensure that the Kubernetes PKI directory and file ownership is set to root:root (Automated)" audit: "find /etc/kubernetes/pki/ | xargs stat -c %U:%G" use_multiple_values: true tests: test_items: - flag: "root:root" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chown -R root:root /etc/kubernetes/pki/ scored: true - id: 1.1.20 text: "Ensure that the Kubernetes PKI certificate file permissions are set to 600 or more restrictive (Manual)" audit: "find /etc/kubernetes/pki/ -name '*.crt' | xargs stat -c permissions=%a" use_multiple_values: true tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chmod -R 600 /etc/kubernetes/pki/*.crt scored: false - id: 1.1.21 text: "Ensure that the Kubernetes PKI key file permissions are set to 600 (Manual)" audit: "find /etc/kubernetes/pki/ -name '*.key' | xargs stat -c permissions=%a" use_multiple_values: true tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chmod -R 600 /etc/kubernetes/pki/*.key scored: false - id: 1.2 text: "API Server" checks: - id: 1.2.1 text: "Ensure that the --anonymous-auth argument is set to false (Manual)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--anonymous-auth" compare: op: eq value: false remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the below parameter. --anonymous-auth=false scored: false - id: 1.2.2 text: "Ensure that the --token-auth-file parameter is not set (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--token-auth-file" set: false remediation: | Follow the documentation and configure alternate mechanisms for authentication. Then, edit the API server pod specification file $apiserverconf on the control plane node and remove the --token-auth-file= parameter. scored: true - id: 1.2.3 text: "Ensure that the --DenyServiceExternalIPs is set (Manual)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: bin_op: or test_items: - flag: "--enable-admission-plugins" compare: op: has value: "DenyServiceExternalIPs" remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and add the `DenyServiceExternalIPs` plugin to the enabled admission plugins, as such --enable-admission-plugin=DenyServiceExternalIPs. scored: false - id: 1.2.4 text: "Ensure that the --kubelet-client-certificate and --kubelet-client-key arguments are set as appropriate (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: bin_op: and test_items: - flag: "--kubelet-client-certificate" - flag: "--kubelet-client-key" remediation: | Follow the Kubernetes documentation and set up the TLS connection between the apiserver and kubelets. Then, edit API server pod specification file $apiserverconf on the control plane node and set the kubelet client certificate and key parameters as below. --kubelet-client-certificate= --kubelet-client-key= scored: true - id: 1.2.5 text: "Ensure that the --kubelet-certificate-authority argument is set as appropriate (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--kubelet-certificate-authority" remediation: | Follow the Kubernetes documentation and setup the TLS connection between the apiserver and kubelets. Then, edit the API server pod specification file $apiserverconf on the control plane node and set the --kubelet-certificate-authority parameter to the path to the cert file for the certificate authority. --kubelet-certificate-authority= scored: true - id: 1.2.6 text: "Ensure that the --authorization-mode argument is not set to AlwaysAllow (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--authorization-mode" compare: op: nothave value: "AlwaysAllow" remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the --authorization-mode parameter to values other than AlwaysAllow. One such example could be as below. --authorization-mode=RBAC scored: true - id: 1.2.7 text: "Ensure that the --authorization-mode argument includes Node (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--authorization-mode" compare: op: has value: "Node" remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the --authorization-mode parameter to a value that includes Node. --authorization-mode=Node,RBAC scored: true - id: 1.2.8 text: "Ensure that the --authorization-mode argument includes RBAC (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--authorization-mode" compare: op: has value: "RBAC" remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the --authorization-mode parameter to a value that includes RBAC, for example `--authorization-mode=Node,RBAC`. scored: true - id: 1.2.9 text: "Ensure that the admission control plugin EventRateLimit is set (Manual)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--enable-admission-plugins" compare: op: has value: "EventRateLimit" remediation: | Follow the Kubernetes documentation and set the desired limits in a configuration file. Then, edit the API server pod specification file $apiserverconf and set the below parameters. --enable-admission-plugins=...,EventRateLimit,... --admission-control-config-file= scored: false - id: 1.2.10 text: "Ensure that the admission control plugin AlwaysAdmit is not set (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: bin_op: or test_items: - flag: "--enable-admission-plugins" compare: op: nothave value: AlwaysAdmit - flag: "--enable-admission-plugins" set: false remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and either remove the --enable-admission-plugins parameter, or set it to a value that does not include AlwaysAdmit. scored: true - id: 1.2.11 text: "Ensure that the admission control plugin AlwaysPullImages is set (Manual)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--enable-admission-plugins" compare: op: has value: "AlwaysPullImages" remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the --enable-admission-plugins parameter to include AlwaysPullImages. --enable-admission-plugins=...,AlwaysPullImages,... scored: false - id: 1.2.12 text: "Ensure that the admission control plugin ServiceAccount is set (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: bin_op: or test_items: - flag: "--disable-admission-plugins" compare: op: nothave value: "ServiceAccount" - flag: "--disable-admission-plugins" set: false remediation: | Follow the documentation and create ServiceAccount objects as per your environment. Then, edit the API server pod specification file $apiserverconf on the control plane node and ensure that the --disable-admission-plugins parameter is set to a value that does not include ServiceAccount. scored: true - id: 1.2.13 text: "Ensure that the admission control plugin NamespaceLifecycle is set (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: bin_op: or test_items: - flag: "--disable-admission-plugins" compare: op: nothave value: "NamespaceLifecycle" - flag: "--disable-admission-plugins" set: false remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the --disable-admission-plugins parameter to ensure it does not include NamespaceLifecycle. scored: true - id: 1.2.14 text: "Ensure that the admission control plugin NodeRestriction is set (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--enable-admission-plugins" compare: op: has value: "NodeRestriction" remediation: | Follow the Kubernetes documentation and configure NodeRestriction plug-in on kubelets. Then, edit the API server pod specification file $apiserverconf on the control plane node and set the --enable-admission-plugins parameter to a value that includes NodeRestriction. --enable-admission-plugins=...,NodeRestriction,... scored: true - id: 1.2.15 text: "Ensure that the --profiling argument is set to false (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--profiling" compare: op: eq value: false remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the below parameter. --profiling=false scored: true - id: 1.2.16 text: "Ensure that the --audit-log-path argument is set (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--audit-log-path" remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the --audit-log-path parameter to a suitable path and file where you would like audit logs to be written, for example, --audit-log-path=/var/log/apiserver/audit.log scored: true - id: 1.2.17 text: "Ensure that the --audit-log-maxage argument is set to 30 or as appropriate (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--audit-log-maxage" compare: op: gte value: 30 remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the --audit-log-maxage parameter to 30 or as an appropriate number of days, for example, --audit-log-maxage=30 scored: true - id: 1.2.18 text: "Ensure that the --audit-log-maxbackup argument is set to 10 or as appropriate (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--audit-log-maxbackup" compare: op: gte value: 10 remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the --audit-log-maxbackup parameter to 10 or to an appropriate value. For example, --audit-log-maxbackup=10 scored: true - id: 1.2.19 text: "Ensure that the --audit-log-maxsize argument is set to 100 or as appropriate (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--audit-log-maxsize" compare: op: gte value: 100 remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the --audit-log-maxsize parameter to an appropriate size in MB. For example, to set it as 100 MB, --audit-log-maxsize=100 scored: true - id: 1.2.20 text: "Ensure that the --request-timeout argument is set as appropriate (Manual)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" type: manual remediation: | Edit the API server pod specification file $apiserverconf and set the below parameter as appropriate and if needed. For example, --request-timeout=300s scored: false - id: 1.2.21 text: "Ensure that the --service-account-lookup argument is set to true (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: bin_op: or test_items: - flag: "--service-account-lookup" set: false - flag: "--service-account-lookup" compare: op: eq value: true remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the below parameter. --service-account-lookup=true Alternatively, you can delete the --service-account-lookup parameter from this file so that the default takes effect. scored: true - id: 1.2.22 text: "Ensure that the --service-account-key-file argument is set as appropriate (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--service-account-key-file" remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the --service-account-key-file parameter to the public key file for service accounts. For example, --service-account-key-file= scored: true - id: 1.2.23 text: "Ensure that the --etcd-certfile and --etcd-keyfile arguments are set as appropriate (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: bin_op: and test_items: - flag: "--etcd-certfile" - flag: "--etcd-keyfile" remediation: | Follow the Kubernetes documentation and set up the TLS connection between the apiserver and etcd. Then, edit the API server pod specification file $apiserverconf on the control plane node and set the etcd certificate and key file parameters. --etcd-certfile= --etcd-keyfile= scored: true - id: 1.2.24 text: "Ensure that the --tls-cert-file and --tls-private-key-file arguments are set as appropriate (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: bin_op: and test_items: - flag: "--tls-cert-file" - flag: "--tls-private-key-file" remediation: | Follow the Kubernetes documentation and set up the TLS connection on the apiserver. Then, edit the API server pod specification file $apiserverconf on the control plane node and set the TLS certificate and private key file parameters. --tls-cert-file= --tls-private-key-file= scored: true - id: 1.2.25 text: "Ensure that the --client-ca-file argument is set as appropriate (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--client-ca-file" remediation: | Follow the Kubernetes documentation and set up the TLS connection on the apiserver. Then, edit the API server pod specification file $apiserverconf on the control plane node and set the client certificate authority file. --client-ca-file= scored: true - id: 1.2.26 text: "Ensure that the --etcd-cafile argument is set as appropriate (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--etcd-cafile" remediation: | Follow the Kubernetes documentation and set up the TLS connection between the apiserver and etcd. Then, edit the API server pod specification file $apiserverconf on the control plane node and set the etcd certificate authority file parameter. --etcd-cafile= scored: true - id: 1.2.27 text: "Ensure that the --encryption-provider-config argument is set as appropriate (Manual)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--encryption-provider-config" remediation: | Follow the Kubernetes documentation and configure a EncryptionConfig file. Then, edit the API server pod specification file $apiserverconf on the control plane node and set the --encryption-provider-config parameter to the path of that file. For example, --encryption-provider-config= scored: false - id: 1.2.28 text: "Ensure that encryption providers are appropriately configured (Manual)" audit: | ENCRYPTION_PROVIDER_CONFIG=$(ps -ef | grep $apiserverbin | grep -- --encryption-provider-config | sed 's%.*encryption-provider-config[= ]\([^ ]*\).*%\1%') if test -e $ENCRYPTION_PROVIDER_CONFIG; then grep -A1 'providers:' $ENCRYPTION_PROVIDER_CONFIG | tail -n1 | grep -o "[A-Za-z]*" | sed 's/^/provider=/'; fi tests: test_items: - flag: "provider" compare: op: valid_elements value: "aescbc,kms,secretbox" remediation: | Follow the Kubernetes documentation and configure a EncryptionConfig file. In this file, choose aescbc, kms or secretbox as the encryption provider. scored: false - id: 1.2.29 text: "Ensure that the API Server only makes use of Strong Cryptographic Ciphers (Manual)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--tls-cipher-suites" compare: op: valid_elements value: "TLS_AES_128_GCM_SHA256,TLS_AES_256_GCM_SHA384,TLS_CHACHA20_POLY1305_SHA256,TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA,TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,TLS_RSA_WITH_3DES_EDE_CBC_SHA,TLS_RSA_WITH_AES_128_CBC_SHA,TLS_RSA_WITH_AES_128_GCM_SHA256,TLS_RSA_WITH_AES_256_CBC_SHA,TLS_RSA_WITH_AES_256_GCM_SHA384" remediation: | Edit the API server pod specification file /etc/kubernetes/manifests/kube-apiserver.yaml on the control plane node and set the below parameter. --tls-cipher-suites=TLS_AES_128_GCM_SHA256,TLS_AES_256_GCM_SHA384,TLS_CHACHA20_POLY1305_SHA256, TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA,TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305, TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,TLS_RSA_WITH_3DES_EDE_CBC_SHA,TLS_RSA_WITH_AES_128_CBC_SHA, TLS_RSA_WITH_AES_128_GCM_SHA256,TLS_RSA_WITH_AES_256_CBC_SHA,TLS_RSA_WITH_AES_256_GCM_SHA384 scored: false - id: 1.3 text: "Controller Manager" checks: - id: 1.3.1 text: "Ensure that the --terminated-pod-gc-threshold argument is set as appropriate (Manual)" audit: "/bin/ps -ef | grep $controllermanagerbin | grep -v grep" tests: test_items: - flag: "--terminated-pod-gc-threshold" remediation: | Edit the Controller Manager pod specification file $controllermanagerconf on the control plane node and set the --terminated-pod-gc-threshold to an appropriate threshold, for example, --terminated-pod-gc-threshold=10 scored: false - id: 1.3.2 text: "Ensure that the --profiling argument is set to false (Automated)" audit: "/bin/ps -ef | grep $controllermanagerbin | grep -v grep" tests: test_items: - flag: "--profiling" compare: op: eq value: false remediation: | Edit the Controller Manager pod specification file $controllermanagerconf on the control plane node and set the below parameter. --profiling=false scored: true - id: 1.3.3 text: "Ensure that the --use-service-account-credentials argument is set to true (Automated)" audit: "/bin/ps -ef | grep $controllermanagerbin | grep -v grep" tests: test_items: - flag: "--use-service-account-credentials" compare: op: noteq value: false remediation: | Edit the Controller Manager pod specification file $controllermanagerconf on the control plane node to set the below parameter. --use-service-account-credentials=true scored: true - id: 1.3.4 text: "Ensure that the --service-account-private-key-file argument is set as appropriate (Automated)" audit: "/bin/ps -ef | grep $controllermanagerbin | grep -v grep" tests: test_items: - flag: "--service-account-private-key-file" remediation: | Edit the Controller Manager pod specification file $controllermanagerconf on the control plane node and set the --service-account-private-key-file parameter to the private key file for service accounts. --service-account-private-key-file= scored: true - id: 1.3.5 text: "Ensure that the --root-ca-file argument is set as appropriate (Automated)" audit: "/bin/ps -ef | grep $controllermanagerbin | grep -v grep" tests: test_items: - flag: "--root-ca-file" remediation: | Edit the Controller Manager pod specification file $controllermanagerconf on the control plane node and set the --root-ca-file parameter to the certificate bundle file`. --root-ca-file= scored: true - id: 1.3.6 text: "Ensure that the RotateKubeletServerCertificate argument is set to true (Automated)" audit: "/bin/ps -ef | grep $controllermanagerbin | grep -v grep" tests: bin_op: or test_items: - flag: "--feature-gates" compare: op: nothave value: "RotateKubeletServerCertificate=false" set: true - flag: "--feature-gates" set: false remediation: | Edit the Controller Manager pod specification file $controllermanagerconf on the control plane node and set the --feature-gates parameter to include RotateKubeletServerCertificate=true. --feature-gates=RotateKubeletServerCertificate=true scored: true - id: 1.3.7 text: "Ensure that the --bind-address argument is set to 127.0.0.1 (Automated)" audit: "/bin/ps -ef | grep $controllermanagerbin | grep -v grep" tests: bin_op: or test_items: - flag: "--bind-address" compare: op: eq value: "127.0.0.1" - flag: "--bind-address" set: false remediation: | Edit the Controller Manager pod specification file $controllermanagerconf on the control plane node and ensure the correct value for the --bind-address parameter scored: true - id: 1.4 text: "Scheduler" checks: - id: 1.4.1 text: "Ensure that the --profiling argument is set to false (Automated)" audit: "/bin/ps -ef | grep $schedulerbin | grep -v grep" tests: test_items: - flag: "--profiling" compare: op: eq value: false remediation: | Edit the Scheduler pod specification file $schedulerconf file on the control plane node and set the below parameter. --profiling=false scored: true - id: 1.4.2 text: "Ensure that the --bind-address argument is set to 127.0.0.1 (Automated)" audit: "/bin/ps -ef | grep $schedulerbin | grep -v grep" tests: bin_op: or test_items: - flag: "--bind-address" compare: op: eq value: "127.0.0.1" - flag: "--bind-address" set: false remediation: | Edit the Scheduler pod specification file $schedulerconf on the control plane node and ensure the correct value for the --bind-address parameter scored: true ================================================ FILE: cfg/cis-1.9/node.yaml ================================================ --- controls: version: "cis-1.9" id: 4 text: "Worker Node Security Configuration" type: "node" groups: - id: 4.1 text: "Worker Node Configuration Files" checks: - id: 4.1.1 text: "Ensure that the kubelet service file permissions are set to 600 or more restrictive (Automated)" audit: '/bin/sh -c ''if test -e $kubeletsvc; then stat -c permissions=%a $kubeletsvc; fi'' ' tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Run the below command (based on the file location on your system) on the each worker node. For example, chmod 600 $kubeletsvc scored: true - id: 4.1.2 text: "Ensure that the kubelet service file ownership is set to root:root (Automated)" audit: '/bin/sh -c "if test -e $kubeletsvc; then stat -c %U:%G $kubeletsvc; else echo \"File not found\"; fi"' tests: bin_op: or test_items: - flag: root:root - flag: "File not found" remediation: | Run the below command (based on the file location on your system) on the each worker node. For example, chown root:root $kubeletsvc scored: true - id: 4.1.3 text: "If proxy kubeconfig file exists ensure permissions are set to 600 or more restrictive (Manual)" audit: '/bin/sh -c ''if test -e $proxykubeconfig; then stat -c permissions=%a $proxykubeconfig; fi'' ' tests: bin_op: or test_items: - flag: "permissions" set: true compare: op: bitmask value: "600" remediation: | Run the below command (based on the file location on your system) on the each worker node. For example, chmod 600 $proxykubeconfig scored: false - id: 4.1.4 text: "If proxy kubeconfig file exists ensure ownership is set to root:root (Manual)" audit: '/bin/sh -c ''if test -e $proxykubeconfig; then stat -c %U:%G $proxykubeconfig; fi'' ' tests: bin_op: or test_items: - flag: root:root remediation: | Run the below command (based on the file location on your system) on the each worker node. For example, chown root:root $proxykubeconfig scored: false - id: 4.1.5 text: "Ensure that the --kubeconfig kubelet.conf file permissions are set to 600 or more restrictive (Automated)" audit: '/bin/sh -c ''if test -e $kubeletkubeconfig; then stat -c permissions=%a $kubeletkubeconfig; fi'' ' tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Run the below command (based on the file location on your system) on the each worker node. For example, chmod 600 $kubeletkubeconfig scored: true - id: 4.1.6 text: "Ensure that the --kubeconfig kubelet.conf file ownership is set to root:root (Automated)" audit: '/bin/sh -c ''if test -e $kubeletkubeconfig; then stat -c %U:%G $kubeletkubeconfig; fi'' ' tests: test_items: - flag: root:root remediation: | Run the below command (based on the file location on your system) on the each worker node. For example, chown root:root $kubeletkubeconfig scored: true - id: 4.1.7 text: "Ensure that the certificate authorities file permissions are set to 600 or more restrictive (Manual)" audit: | CAFILE=$(ps -ef | grep kubelet | grep -v apiserver | grep -- --client-ca-file= | awk -F '--client-ca-file=' '{print $2}' | awk '{print $1}' | uniq) if test -z $CAFILE; then CAFILE=$kubeletcafile; fi if test -e $CAFILE; then stat -c permissions=%a $CAFILE; fi tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Run the following command to modify the file permissions of the --client-ca-file chmod 600 scored: false - id: 4.1.8 text: "Ensure that the client certificate authorities file ownership is set to root:root (Manual)" audit: | CAFILE=$(ps -ef | grep kubelet | grep -v apiserver | grep -- --client-ca-file= | awk -F '--client-ca-file=' '{print $2}' | awk '{print $1}' | uniq) if test -z $CAFILE; then CAFILE=$kubeletcafile; fi if test -e $CAFILE; then stat -c %U:%G $CAFILE; fi tests: test_items: - flag: root:root compare: op: eq value: root:root remediation: | Run the following command to modify the ownership of the --client-ca-file. chown root:root scored: false - id: 4.1.9 text: "If the kubelet config.yaml configuration file is being used validate permissions set to 600 or more restrictive (Automated)" audit: '/bin/sh -c ''if test -e $kubeletconf; then stat -c permissions=%a $kubeletconf; fi'' ' tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Run the following command (using the config file location identified in the Audit step) chmod 600 $kubeletconf scored: true - id: 4.1.10 text: "If the kubelet config.yaml configuration file is being used validate file ownership is set to root:root (Automated)" audit: '/bin/sh -c ''if test -e $kubeletconf; then stat -c %U:%G $kubeletconf; fi'' ' tests: test_items: - flag: root:root remediation: | Run the following command (using the config file location identified in the Audit step) chown root:root $kubeletconf scored: true - id: 4.2 text: "Kubelet" checks: - id: 4.2.1 text: "Ensure that the --anonymous-auth argument is set to false (Automated)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: "--anonymous-auth" path: '{.authentication.anonymous.enabled}' compare: op: eq value: false remediation: | If using a Kubelet config file, edit the file to set `authentication: anonymous: enabled` to `false`. If using executable arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_SYSTEM_PODS_ARGS variable. `--anonymous-auth=false` Based on your system, restart the kubelet service. For example, systemctl daemon-reload systemctl restart kubelet.service scored: true - id: 4.2.2 text: "Ensure that the --authorization-mode argument is not set to AlwaysAllow (Automated)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --authorization-mode path: '{.authorization.mode}' compare: op: nothave value: AlwaysAllow remediation: | If using a Kubelet config file, edit the file to set `authorization.mode` to Webhook. If using executable arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_AUTHZ_ARGS variable. --authorization-mode=Webhook Based on your system, restart the kubelet service. For example, systemctl daemon-reload systemctl restart kubelet.service scored: true - id: 4.2.3 text: "Ensure that the --client-ca-file argument is set as appropriate (Automated)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --client-ca-file path: '{.authentication.x509.clientCAFile}' remediation: | If using a Kubelet config file, edit the file to set `authentication.x509.clientCAFile` to the location of the client CA file. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_AUTHZ_ARGS variable. --client-ca-file= Based on your system, restart the kubelet service. For example, systemctl daemon-reload systemctl restart kubelet.service scored: true - id: 4.2.4 text: "Verify that the --read-only-port argument is set to 0 (Manual)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: bin_op: or test_items: - flag: "--read-only-port" path: '{.readOnlyPort}' compare: op: eq value: 0 - flag: "--read-only-port" path: '{.readOnlyPort}' set: false remediation: | If using a Kubelet config file, edit the file to set `readOnlyPort` to 0. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_SYSTEM_PODS_ARGS variable. --read-only-port=0 Based on your system, restart the kubelet service. For example, systemctl daemon-reload systemctl restart kubelet.service scored: false - id: 4.2.5 text: "Ensure that the --streaming-connection-idle-timeout argument is not set to 0 (Manual)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --streaming-connection-idle-timeout path: '{.streamingConnectionIdleTimeout}' compare: op: noteq value: 0 - flag: --streaming-connection-idle-timeout path: '{.streamingConnectionIdleTimeout}' set: false bin_op: or remediation: | If using a Kubelet config file, edit the file to set `streamingConnectionIdleTimeout` to a value other than 0. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_SYSTEM_PODS_ARGS variable. --streaming-connection-idle-timeout=5m Based on your system, restart the kubelet service. For example, systemctl daemon-reload systemctl restart kubelet.service scored: false - id: 4.2.6 text: "Ensure that the --make-iptables-util-chains argument is set to true (Automated)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --make-iptables-util-chains path: '{.makeIPTablesUtilChains}' compare: op: eq value: true - flag: --make-iptables-util-chains path: '{.makeIPTablesUtilChains}' set: false bin_op: or remediation: | If using a Kubelet config file, edit the file to set `makeIPTablesUtilChains` to `true`. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and remove the --make-iptables-util-chains argument from the KUBELET_SYSTEM_PODS_ARGS variable. Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: true - id: 4.2.7 text: "Ensure that the --hostname-override argument is not set (Manual)" # This is one of those properties that can only be set as a command line argument. # To check if the property is set as expected, we need to parse the kubelet command # instead reading the Kubelet Configuration file. audit: "/bin/ps -fC $kubeletbin" tests: test_items: - flag: --hostname-override set: false remediation: | Edit the kubelet service file $kubeletsvc on each worker node and remove the --hostname-override argument from the KUBELET_SYSTEM_PODS_ARGS variable. Based on your system, restart the kubelet service. For example, systemctl daemon-reload systemctl restart kubelet.service scored: false - id: 4.2.8 text: "Ensure that the eventRecordQPS argument is set to a level which ensures appropriate event capture (Manual)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --event-qps path: '{.eventRecordQPS}' compare: op: gte value: 0 - flag: --event-qps path: '{.eventRecordQPS}' set: false bin_op: or remediation: | If using a Kubelet config file, edit the file to set `eventRecordQPS` to an appropriate level. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_SYSTEM_PODS_ARGS variable. Based on your system, restart the kubelet service. For example, systemctl daemon-reload systemctl restart kubelet.service scored: false - id: 4.2.9 text: "Ensure that the --tls-cert-file and --tls-private-key-file arguments are set as appropriate (Manual)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --tls-cert-file path: '{.tlsCertFile}' - flag: --tls-private-key-file path: '{.tlsPrivateKeyFile}' remediation: | If using a Kubelet config file, edit the file to set `tlsCertFile` to the location of the certificate file to use to identify this Kubelet, and `tlsPrivateKeyFile` to the location of the corresponding private key file. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameters in KUBELET_CERTIFICATE_ARGS variable. --tls-cert-file= --tls-private-key-file= Based on your system, restart the kubelet service. For example, systemctl daemon-reload systemctl restart kubelet.service scored: false - id: 4.2.10 text: "Ensure that the --rotate-certificates argument is not set to false (Automated)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --rotate-certificates path: '{.rotateCertificates}' compare: op: eq value: true - flag: --rotate-certificates path: '{.rotateCertificates}' set: false bin_op: or remediation: | If using a Kubelet config file, edit the file to add the line `rotateCertificates` to `true` or remove it altogether to use the default value. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and remove --rotate-certificates=false argument from the KUBELET_CERTIFICATE_ARGS variable. Based on your system, restart the kubelet service. For example, systemctl daemon-reload systemctl restart kubelet.service scored: true - id: 4.2.11 text: "Verify that the RotateKubeletServerCertificate argument is set to true (Manual)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: bin_op: or test_items: - flag: RotateKubeletServerCertificate path: '{.featureGates.RotateKubeletServerCertificate}' compare: op: nothave value: false - flag: RotateKubeletServerCertificate path: '{.featureGates.RotateKubeletServerCertificate}' set: false remediation: | Edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_CERTIFICATE_ARGS variable. --feature-gates=RotateKubeletServerCertificate=true Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: false - id: 4.2.12 text: "Ensure that the Kubelet only makes use of Strong Cryptographic Ciphers (Manual)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --tls-cipher-suites path: '{range .tlsCipherSuites[:]}{}{'',''}{end}' compare: op: valid_elements value: 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 remediation: | If using a Kubelet config file, edit the file to set `tlsCipherSuites` to 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 or to a subset of these values. If using executable arguments, edit the kubelet service file $kubeletsvc on each worker node and set the --tls-cipher-suites parameter as follows, or to a subset of these values. --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 Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: false - id: 4.2.13 text: "Ensure that a limit is set on pod PIDs (Manual)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --pod-max-pids path: '{.podPidsLimit}' remediation: | Decide on an appropriate level for this parameter and set it, either via the --pod-max-pids command line parameter or the PodPidsLimit configuration file setting. scored: false - id: 4.3 text: "kube-proxy" checks: - id: 4.3.1 text: "Ensure that the kube-proxy metrics service is bound to localhost (Automated)" audit: "/bin/ps -fC $proxybin" audit_config: "/bin/sh -c 'if test -e $proxykubeconfig; then cat $proxykubeconfig; fi'" tests: bin_op: or test_items: - flag: "--metrics-bind-address" path: '{.metricsBindAddress}' compare: op: has value: "127.0.0.1" - flag: "--metrics-bind-address" path: '{.metricsBindAddress}' set: false remediation: | Modify or remove any values which bind the metrics service to a non-localhost address. The default value is 127.0.0.1:10249. scored: true ================================================ FILE: cfg/cis-1.9/policies.yaml ================================================ --- controls: version: "cis-1.9" id: 5 text: "Kubernetes Policies" type: "policies" groups: - id: 5.1 text: "RBAC and Service Accounts" checks: - id: 5.1.1 text: "Ensure that the cluster-admin role is only used where required (Automated)" audit: | kubectl get clusterrolebindings -o=custom-columns=NAME:.metadata.name,ROLE:.roleRef.name,SUBJECT:.subjects[*].name --no-headers | while read -r role_name role_binding subject do if [[ "${role_name}" != "cluster-admin" && "${role_binding}" == "cluster-admin" ]]; then is_compliant="false" else is_compliant="true" fi; echo "**role_name: ${role_name} role_binding: ${role_binding} subject: ${subject} is_compliant: ${is_compliant}" done use_multiple_values: true tests: test_items: - flag: "is_compliant" compare: op: eq value: true remediation: | Identify all clusterrolebindings to the cluster-admin role. Check if they are used and if they need this role or if they could use a role with fewer privileges. Where possible, first bind users to a lower privileged role and then remove the clusterrolebinding to the cluster-admin role : kubectl delete clusterrolebinding [name] Condition: is_compliant is false if rolename is not cluster-admin and rolebinding is cluster-admin. scored: false - id: 5.1.2 text: "Minimize access to secrets (Automated)" audit: "echo \"canGetListWatchSecretsAsSystemAuthenticated: $(kubectl auth can-i get,list,watch secrets --all-namespaces --as=system:authenticated)\"" tests: test_items: - flag: "canGetListWatchSecretsAsSystemAuthenticated" compare: op: eq value: no remediation: | Where possible, remove get, list and watch access to Secret objects in the cluster. scored: false - id: 5.1.3 text: "Minimize wildcard use in Roles and ClusterRoles (Automated)" audit: | # Check Roles kubectl get roles --all-namespaces -o custom-columns=ROLE_NAMESPACE:.metadata.namespace,ROLE_NAME:.metadata.name --no-headers | while read -r role_namespace role_name do role_rules=$(kubectl get role -n "${role_namespace}" "${role_name}" -o=json | jq -c '.rules') if echo "${role_rules}" | grep -q "\[\"\*\"\]"; then role_is_compliant="false" else role_is_compliant="true" fi; echo "**role_name: ${role_name} role_namespace: ${role_namespace} role_rules: ${role_rules} role_is_compliant: ${role_is_compliant}" done # Check ClusterRoles kubectl get clusterroles -o custom-columns=CLUSTERROLE_NAME:.metadata.name --no-headers | while read -r clusterrole_name do clusterrole_rules=$(kubectl get clusterrole "${clusterrole_name}" -o=json | jq -c '.rules') if echo "${clusterrole_rules}" | grep -q "\[\"\*\"\]"; then clusterrole_is_compliant="false" else clusterrole_is_compliant="true" fi; echo "**clusterrole_name: ${clusterrole_name} clusterrole_rules: ${clusterrole_rules} clusterrole_is_compliant: ${clusterrole_is_compliant}" done use_multiple_values: true tests: bin_op: or test_items: - flag: "role_is_compliant" compare: op: eq value: true set: true - flag: "clusterrole_is_compliant" compare: op: eq value: true set: true remediation: | Where possible replace any use of wildcards ["*"] in roles and clusterroles with specific objects or actions. Condition: role_is_compliant is false if ["*"] is found in rules. Condition: clusterrole_is_compliant is false if ["*"] is found in rules. scored: false - id: 5.1.4 text: "Minimize access to create pods (Automated)" audit: | echo "canCreatePodsAsSystemAuthenticated: $(kubectl auth can-i create pods --all-namespaces --as=system:authenticated)" tests: test_items: - flag: "canCreatePodsAsSystemAuthenticated" compare: op: eq value: no remediation: | Where possible, remove create access to pod objects in the cluster. scored: false - id: 5.1.5 text: "Ensure that default service accounts are not actively used (Automated)" audit: | kubectl get serviceaccount --all-namespaces --field-selector metadata.name=default -o=json | jq -r '.items[] | " namespace: \(.metadata.namespace), kind: \(.kind), name: \(.metadata.name), automountServiceAccountToken: \(.automountServiceAccountToken | if . == null then "notset" else . end )"' | xargs -L 1 use_multiple_values: true tests: test_items: - flag: "automountServiceAccountToken" compare: op: eq value: false set: true remediation: | Create explicit service accounts wherever a Kubernetes workload requires specific access to the Kubernetes API server. Modify the configuration of each default service account to include this value `automountServiceAccountToken: false`. scored: false - id: 5.1.6 text: "Ensure that Service Account Tokens are only mounted where necessary (Automated)" audit: | kubectl get pods --all-namespaces -o custom-columns=POD_NAMESPACE:.metadata.namespace,POD_NAME:.metadata.name,POD_SERVICE_ACCOUNT:.spec.serviceAccount,POD_IS_AUTOMOUNTSERVICEACCOUNTTOKEN:.spec.automountServiceAccountToken --no-headers | while read -r pod_namespace pod_name pod_service_account pod_is_automountserviceaccounttoken do # Retrieve automountServiceAccountToken's value for ServiceAccount and Pod, set to notset if null or . svacc_is_automountserviceaccounttoken=$(kubectl get serviceaccount -n "${pod_namespace}" "${pod_service_account}" -o json | jq -r '.automountServiceAccountToken' | sed -e 's//notset/g' -e 's/null/notset/g') pod_is_automountserviceaccounttoken=$(echo "${pod_is_automountserviceaccounttoken}" | sed -e 's//notset/g' -e 's/null/notset/g') if [ "${svacc_is_automountserviceaccounttoken}" = "false" ] && ( [ "${pod_is_automountserviceaccounttoken}" = "false" ] || [ "${pod_is_automountserviceaccounttoken}" = "notset" ] ); then is_compliant="true" elif [ "${svacc_is_automountserviceaccounttoken}" = "true" ] && [ "${pod_is_automountserviceaccounttoken}" = "false" ]; then is_compliant="true" else is_compliant="false" fi echo "**namespace: ${pod_namespace} pod_name: ${pod_name} service_account: ${pod_service_account} pod_is_automountserviceaccounttoken: ${pod_is_automountserviceaccounttoken} svacc_is_automountServiceAccountToken: ${svacc_is_automountserviceaccounttoken} is_compliant: ${is_compliant}" done use_multiple_values: true tests: test_items: - flag: "is_compliant" compare: op: eq value: true remediation: | Modify the definition of ServiceAccounts and Pods which do not need to mount service account tokens to disable it, with `automountServiceAccountToken: false`. If both the ServiceAccount and the Pod's .spec specify a value for automountServiceAccountToken, the Pod spec takes precedence. Condition: Pod is_compliant to true when - ServiceAccount is automountServiceAccountToken: false and Pod is automountServiceAccountToken: false or notset - ServiceAccount is automountServiceAccountToken: true notset and Pod is automountServiceAccountToken: false scored: false - id: 5.1.7 text: "Avoid use of system:masters group (Manual)" type: "manual" remediation: | Remove the system:masters group from all users in the cluster. scored: false - id: 5.1.8 text: "Limit use of the Bind, Impersonate and Escalate permissions in the Kubernetes cluster (Manual)" type: "manual" remediation: | Where possible, remove the impersonate, bind and escalate rights from subjects. scored: false - id: 5.1.9 text: "Minimize access to create persistent volumes (Manual)" type: "manual" remediation: | Where possible, remove create access to PersistentVolume objects in the cluster. scored: false - id: 5.1.10 text: "Minimize access to the proxy sub-resource of nodes (Manual)" type: "manual" remediation: | Where possible, remove access to the proxy sub-resource of node objects. scored: false - id: 5.1.11 text: "Minimize access to the approval sub-resource of certificatesigningrequests objects (Manual)" type: "manual" remediation: | Where possible, remove access to the approval sub-resource of certificatesigningrequest objects. scored: false - id: 5.1.12 text: "Minimize access to webhook configuration objects (Manual)" type: "manual" remediation: | Where possible, remove access to the validatingwebhookconfigurations or mutatingwebhookconfigurations objects scored: false - id: 5.1.13 text: "Minimize access to the service account token creation (Manual)" type: "manual" remediation: | Where possible, remove access to the token sub-resource of serviceaccount objects. scored: false - id: 5.2 text: "Pod Security Standards" checks: - id: 5.2.1 text: "Ensure that the cluster has at least one active policy control mechanism in place (Manual)" type: "manual" remediation: | Ensure that either Pod Security Admission or an external policy control system is in place for every namespace which contains user workloads. scored: false - id: 5.2.2 text: "Minimize the admission of privileged containers (Manual)" type: "manual" remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of privileged containers. scored: false - id: 5.2.3 text: "Minimize the admission of containers wishing to share the host process ID namespace (Manual)" type: "manual" remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of `hostPID` containers. scored: false - id: 5.2.4 text: "Minimize the admission of containers wishing to share the host IPC namespace (Manual)" type: "manual" remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of `hostIPC` containers. scored: false - id: 5.2.5 text: "Minimize the admission of containers wishing to share the host network namespace (Manual)" type: "manual" remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of `hostNetwork` containers. scored: false - id: 5.2.6 text: "Minimize the admission of containers with allowPrivilegeEscalation (Manual)" type: "manual" remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of containers with `.spec.allowPrivilegeEscalation` set to `true`. scored: false - id: 5.2.7 text: "Minimize the admission of root containers (Manual)" type: "manual" remediation: | Create a policy for each namespace in the cluster, ensuring that either `MustRunAsNonRoot` or `MustRunAs` with the range of UIDs not including 0, is set. scored: false - id: 5.2.8 text: "Minimize the admission of containers with the NET_RAW capability (Manual)" type: "manual" remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of containers with the `NET_RAW` capability. scored: false - id: 5.2.9 text: "Minimize the admission of containers with added capabilities (Manual)" type: "manual" remediation: | Ensure that `allowedCapabilities` is not present in policies for the cluster unless it is set to an empty array. scored: false - id: 5.2.10 text: "Minimize the admission of containers with capabilities assigned (Manual)" type: "manual" remediation: | Review the use of capabilites in applications running on your cluster. Where a namespace contains applications which do not require any Linux capabities to operate consider adding a PSP which forbids the admission of containers which do not drop all capabilities. scored: false - id: 5.2.11 text: "Minimize the admission of Windows HostProcess containers (Manual)" type: "manual" remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of containers that have `.securityContext.windowsOptions.hostProcess` set to `true`. scored: false - id: 5.2.12 text: "Minimize the admission of HostPath volumes (Manual)" type: "manual" remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of containers with `hostPath` volumes. scored: false - id: 5.2.13 text: "Minimize the admission of containers which use HostPorts (Manual)" type: "manual" remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of containers which use `hostPort` sections. scored: false - id: 5.3 text: "Network Policies and CNI" checks: - id: 5.3.1 text: "Ensure that the CNI in use supports NetworkPolicies (Manual)" type: "manual" remediation: | If the CNI plugin in use does not support network policies, consideration should be given to making use of a different plugin, or finding an alternate mechanism for restricting traffic in the Kubernetes cluster. scored: false - id: 5.3.2 text: "Ensure that all Namespaces have NetworkPolicies defined (Manual)" type: "manual" remediation: | Follow the documentation and create NetworkPolicy objects as you need them. scored: false - id: 5.4 text: "Secrets Management" checks: - id: 5.4.1 text: "Prefer using Secrets as files over Secrets as environment variables (Manual)" type: "manual" remediation: | If possible, rewrite application code to read Secrets from mounted secret files, rather than from environment variables. scored: false - id: 5.4.2 text: "Consider external secret storage (Manual)" type: "manual" remediation: | Refer to the Secrets management options offered by your cloud provider or a third-party secrets management solution. scored: false - id: 5.5 text: "Extensible Admission Control" checks: - id: 5.5.1 text: "Configure Image Provenance using ImagePolicyWebhook admission controller (Manual)" type: "manual" remediation: | Follow the Kubernetes documentation and setup image provenance. scored: false - id: 5.7 text: "General Policies" checks: - id: 5.7.1 text: "Create administrative boundaries between resources using namespaces (Manual)" type: "manual" remediation: | Follow the documentation and create namespaces for objects in your deployment as you need them. scored: false - id: 5.7.2 text: "Ensure that the seccomp profile is set to docker/default in your Pod definitions (Manual)" type: "manual" remediation: | Use `securityContext` to enable the docker/default seccomp profile in your pod definitions. An example is as below: securityContext: seccompProfile: type: RuntimeDefault scored: false - id: 5.7.3 text: "Apply SecurityContext to your Pods and Containers (Manual)" type: "manual" remediation: | Follow the Kubernetes documentation and apply SecurityContexts to your Pods. For a suggested list of SecurityContexts, you may refer to the CIS Security Benchmark for Docker Containers. scored: false - id: 5.7.4 text: "The default namespace should not be used (Manual)" type: "manual" remediation: | Ensure that namespaces are created to allow for appropriate segregation of Kubernetes resources and that all new resources are created in a specific namespace. scored: false ================================================ FILE: cfg/config.yaml ================================================ --- ## Controls Files. # These are YAML files that hold all the details for running checks. # ## Uncomment to use different control file paths. # masterControls: ./cfg/master.yaml # nodeControls: ./cfg/node.yaml master: components: - apiserver - scheduler - controllermanager - etcd - flanneld # kubernetes is a component to cover the config file /etc/kubernetes/config that is referred to in the benchmark - kubernetes - kubelet kubernetes: defaultconf: /etc/kubernetes/config apiserver: bins: - "kube-apiserver" - "hyperkube apiserver" - "hyperkube kube-apiserver" - "apiserver" - "openshift start master api" - "hypershift openshift-kube-apiserver" confs: - /etc/kubernetes/manifests/kube-apiserver.yaml - /etc/kubernetes/manifests/kube-apiserver.yml - /etc/kubernetes/manifests/kube-apiserver.manifest - /var/snap/kube-apiserver/current/args - /var/snap/microk8s/current/args/kube-apiserver - /etc/origin/master/master-config.yaml - /etc/kubernetes/manifests/talos-kube-apiserver.yaml - /var/lib/rancher/rke2/agent/pod-manifests/kube-apiserver.yaml defaultconf: /etc/kubernetes/manifests/kube-apiserver.yaml scheduler: bins: - "kube-scheduler" - "hyperkube scheduler" - "hyperkube kube-scheduler" - "scheduler" - "openshift start master controllers" confs: - /etc/kubernetes/manifests/kube-scheduler.yaml - /etc/kubernetes/manifests/kube-scheduler.yml - /etc/kubernetes/manifests/kube-scheduler.manifest - /var/snap/kube-scheduler/current/args - /var/snap/microk8s/current/args/kube-scheduler - /etc/origin/master/scheduler.json - /etc/kubernetes/manifests/talos-kube-scheduler.yaml - /var/lib/rancher/rke2/agent/pod-manifests/kube-scheduler.yaml defaultconf: /etc/kubernetes/manifests/kube-scheduler.yaml kubeconfig: - /etc/kubernetes/scheduler.conf - /var/lib/kube-scheduler/kubeconfig - /var/lib/kube-scheduler/config.yaml - /var/lib/rancher/rke2/server/cred/scheduler.kubeconfig - /system/secrets/kubernetes/kube-scheduler/kubeconfig defaultkubeconfig: /etc/kubernetes/scheduler.conf controllermanager: bins: - "kube-controller-manager" - "kube-controller" - "hyperkube controller-manager" - "hyperkube kube-controller-manager" - "controller-manager" - "openshift start master controllers" - "hypershift openshift-controller-manager" confs: - /etc/kubernetes/manifests/kube-controller-manager.yaml - /etc/kubernetes/manifests/kube-controller-manager.yml - /etc/kubernetes/manifests/kube-controller-manager.manifest - /var/snap/kube-controller-manager/current/args - /var/snap/microk8s/current/args/kube-controller-manager - /etc/kubernetes/manifests/talos-kube-controller-manager.yaml - /var/lib/rancher/rke2/agent/pod-manifests/kube-controller-manager.yaml defaultconf: /etc/kubernetes/manifests/kube-controller-manager.yaml kubeconfig: - /etc/kubernetes/controller-manager.conf - /var/lib/kube-controller-manager/kubeconfig - /var/lib/rancher/rke2/server/cred/controller.kubeconfig - /system/secrets/kubernetes/kube-controller-manager/kubeconfig defaultkubeconfig: /etc/kubernetes/controller-manager.conf etcd: optional: true bins: - "etcd" - "openshift start etcd" datadirs: - /var/lib/etcd/default.etcd - /var/lib/etcd/data.etcd - /var/lib/rancher/k3s/server/db/etcd confs: - /etc/kubernetes/manifests/etcd.yaml - /etc/kubernetes/manifests/etcd.yml - /etc/kubernetes/manifests/etcd.manifest - /etc/etcd/etcd.conf - /var/snap/etcd/common/etcd.conf.yml - /var/snap/etcd/common/etcd.conf.yaml - /var/snap/microk8s/current/args/etcd - /usr/lib/systemd/system/etcd.service - /var/lib/rancher/rke2/server/db/etcd/config - /var/lib/rancher/k3s/server/db/etcd/config defaultconf: /etc/kubernetes/manifests/etcd.yaml defaultdatadir: /var/lib/etcd/default.etcd client_cert_file: /var/lib/rancher/rke2/server/tls/etcd/server-client.crt client_key_file: /var/lib/rancher/rke2/server/tls/etcd/server-client.key flanneld: optional: true bins: - flanneld defaultconf: /etc/sysconfig/flanneld kubelet: optional: true bins: - "hyperkube kubelet" - "kubelet" node: components: - kubelet - proxy # kubernetes is a component to cover the config file /etc/kubernetes/config that is referred to in the benchmark - kubernetes kubernetes: defaultconf: "/etc/kubernetes/config" kubelet: cafile: - "/var/lib/rancher/rke2/agent/client-ca.crt" - "/var/lib/rancher/rke2/server/tls/server-ca.crt" - "/etc/kubernetes/pki/ca.crt" - "/etc/kubernetes/certs/ca.crt" - "/etc/kubernetes/cert/ca.pem" - "/var/snap/microk8s/current/certs/ca.crt" - "/var/lib/rancher/rke2/agent/server.crt" - "/var/lib/rancher/k3s/agent/client-ca.crt" svc: # These paths must also be included # in the 'confs' property below - "/etc/systemd/system/kubelet.service.d/10-kubeadm.conf" - "/etc/systemd/system/kubelet.service" - "/lib/systemd/system/kubelet.service" - "/etc/systemd/system/snap.kubelet.daemon.service" - "/etc/systemd/system/snap.microk8s.daemon-kubelet.service" - "/etc/systemd/system/atomic-openshift-node.service" - "/etc/systemd/system/origin-node.service" bins: - "hyperkube kubelet" - "kubelet" kubeconfig: - "/etc/kubernetes/kubelet.conf" - "/etc/kubernetes/kubelet-kubeconfig.conf" - "/var/lib/kubelet/kubeconfig" - "/etc/kubernetes/kubelet-kubeconfig" - "/etc/kubernetes/kubelet/kubeconfig" - "/etc/kubernetes/ssl/kubecfg-kube-node.yaml" - "/var/snap/microk8s/current/credentials/kubelet.config" - "/etc/kubernetes/kubeconfig-kubelet" - "/var/lib/rancher/rke2/agent/kubelet.kubeconfig" - "/var/lib/rancher/k3s/agent/kubelet.kubeconfig" confs: - "/etc/kubernetes/kubelet-config.yaml" - "/etc/kubernetes/kubelet/kubelet-config.json" - "/var/lib/rancher/rke2/agent/etc/kubelet.conf.d" - "/var/lib/kubelet/config.yaml" - "/etc/kubernetes/azure.json" - "/var/lib/kubelet/config.yml" - "/etc/kubernetes/kubelet/kubelet-config.json" - "/etc/kubernetes/kubelet/config.json" - "/etc/kubernetes/kubelet/config" - "/home/kubernetes/kubelet-config.yaml" - "/home/kubernetes/kubelet-config.yml" - "/etc/default/kubeletconfig.json" - "/etc/default/kubelet" - "/var/lib/kubelet/kubeconfig" - "/var/snap/kubelet/current/args" - "/var/snap/microk8s/current/args/kubelet" ## Due to the fact that the kubelet might be configured ## without a kubelet-config file, we use a work-around ## of pointing to the systemd service file (which can also ## hold kubelet configuration). ## Note: The following paths must match the one under 'svc' - "/etc/systemd/system/kubelet.service.d/10-kubeadm.conf" - "/etc/systemd/system/kubelet.service" - "/lib/systemd/system/kubelet.service" - "/etc/systemd/system/snap.kubelet.daemon.service" - "/etc/systemd/system/snap.microk8s.daemon-kubelet.service" - "/etc/kubernetes/kubelet.yaml" defaultconf: "/var/lib/kubelet/config.yaml" defaultsvc: "/etc/systemd/system/kubelet.service.d/10-kubeadm.conf" defaultkubeconfig: "/etc/kubernetes/kubelet.conf" defaultcafile: "/etc/kubernetes/pki/ca.crt" proxy: optional: true bins: - "kube-proxy" - "hyperkube proxy" - "hyperkube kube-proxy" - "proxy" - "openshift start network" confs: - /etc/kubernetes/proxy - /etc/kubernetes/addons/kube-proxy-daemonset.yaml - /etc/kubernetes/addons/kube-proxy-daemonset.yml - /var/snap/kube-proxy/current/args - /var/snap/microk8s/current/args/kube-proxy kubeconfig: - "/etc/kubernetes/kubelet-kubeconfig" - "/etc/kubernetes/kubelet-kubeconfig.conf" - "/etc/kubernetes/kubelet/config" - "/etc/kubernetes/ssl/kubecfg-kube-proxy.yaml" - "/var/lib/kubelet/kubeconfig" - "/var/snap/microk8s/current/credentials/proxy.config" - "/var/lib/rancher/rke2/agent/kubeproxy.kubeconfig" - "/var/lib/rancher/k3s/agent/kubeproxy.kubeconfig" svc: - "/lib/systemd/system/kube-proxy.service" - "/etc/systemd/system/snap.microk8s.daemon-proxy.service" defaultconf: /etc/kubernetes/addons/kube-proxy-daemonset.yaml defaultkubeconfig: "/etc/kubernetes/proxy.conf" etcd: components: - etcd etcd: bins: - "etcd" datadirs: - /var/lib/etcd/default.etcd - /var/lib/etcd/data.etcd - /var/lib/rancher/k3s/server/db/etcd confs: - /var/lib/rancher/rke2/server/db/etcd/config - /var/lib/rancher/rke2/agent/pod-manifests/etcd.yaml - /etc/kubernetes/manifests/etcd.yaml - /etc/kubernetes/manifests/etcd.yml - /etc/kubernetes/manifests/etcd.manifest - /etc/etcd/etcd.conf - /var/snap/etcd/common/etcd.conf.yml - /var/snap/etcd/common/etcd.conf.yaml - /var/snap/microk8s/current/args/etcd - /usr/lib/systemd/system/etcd.service - /var/lib/rancher/k3s/server/db/etcd/config defaultconf: /etc/kubernetes/manifests/etcd.yaml defaultdatadir: /var/lib/etcd/default.etcd controlplane: components: - apiserver apiserver: bins: - "kube-apiserver" - "hyperkube apiserver" - "hyperkube kube-apiserver" - "apiserver" policies: components: [] managedservices: components: [] version_mapping: "1.15": "cis-1.5" "1.16": "cis-1.6" "1.17": "cis-1.6" "1.18": "cis-1.6" "1.19": "cis-1.20" "1.20": "cis-1.20" "1.21": "cis-1.20" "1.22": "cis-1.23" "1.23": "cis-1.23" "1.24": "cis-1.24" "1.25": "cis-1.7" "1.26": "cis-1.8" "1.27": "cis-1.9" "1.28": "cis-1.10" "1.29": "cis-1.11" "1.30": "cis-1.11" "1.31": "cis-1.11" "1.32": "cis-1.12" "1.33": "cis-1.12" "1.34": "cis-1.12" "eks-1.0.1": "eks-1.0.1" "eks-1.1.0": "eks-1.1.0" "eks-1.2.0": "eks-1.2.0" "eks-1.5.0": "eks-1.5.0" "eks-1.7.0": "eks-1.7.0" "eks-1.8.0": "eks-1.8.0" "gke-1.0": "gke-1.0" "gke-1.2.0": "gke-1.2.0" "gke-1.6.0": "gke-1.6.0" "gke-1.8.0": "gke-1.8.0" "ocp-3.10": "rh-0.7" "ocp-3.11": "rh-0.7" "ocp-4.0": "rh-1.0" "ocp-4.11": "rh-1.4" "ocp-4.13": "rh-1.8" "aks-1.0": "aks-1.0" "aks-1.7": "aks-1.7" "aks-1.8": "aks-1.8" "ack-1.0": "ack-1.0" "cis-1.6-k3s": "cis-1.6-k3s" "cis-1.24-microk8s": "cis-1.24-microk8s" "tkgi-1.2.53": "tkgi-1.2.53" "k3s-cis-1.7": "k3s-cis-1.7" "k3s-cis-1.23": "k3s-cis-1.23" "k3s-cis-1.24": "k3s-cis-1.24" "rke-cis-1.7": "rke-cis-1.7" "rke-cis-1.23": "rke-cis-1.23" "rke-cis-1.24": "rke-cis-1.24" "rke2-cis-1.7": "rke2-cis-1.7" "rke2-cis-1.8": "rke2-cis-1.8" "rke2-cis-1.23": "rke2-cis-1.23" "rke2-cis-1.24": "rke2-cis-1.24" target_mapping: "cis-1.5": - "master" - "node" - "controlplane" - "etcd" - "policies" "cis-1.6": - "master" - "node" - "controlplane" - "etcd" - "policies" "cis-1.6-k3s": - "master" - "node" - "controlplane" - "etcd" - "policies" "cis-1.20": - "master" - "node" - "controlplane" - "etcd" - "policies" "cis-1.23": - "master" - "node" - "controlplane" - "etcd" - "policies" "cis-1.24": - "master" - "node" - "controlplane" - "etcd" - "policies" "cis-1.24-microk8s": - "master" - "etcd" - "node" - "controlplane" - "policies" "cis-1.7": - "master" - "node" - "controlplane" - "etcd" - "policies" "cis-1.8": - "master" - "node" - "controlplane" - "etcd" - "policies" "cis-1.9": - "master" - "node" - "controlplane" - "etcd" - "policies" "cis-1.10": - "master" - "node" - "controlplane" - "etcd" - "policies" "cis-1.11": - "master" - "node" - "controlplane" - "etcd" - "policies" "cis-1.12": - "master" - "node" - "controlplane" - "etcd" - "policies" "gke-1.0": - "master" - "node" - "controlplane" - "etcd" - "policies" - "managedservices" "gke-1.2.0": - "master" - "node" - "controlplane" - "policies" - "managedservices" "gke-1.6.0": - "master" - "node" - "controlplane" - "policies" - "managedservices" "gke-1.8.0": - "master" - "node" - "controlplane" - "policies" - "managedservices" "eks-1.0.1": - "master" - "node" - "controlplane" - "policies" - "managedservices" "eks-1.1.0": - "master" - "node" - "controlplane" - "policies" - "managedservices" "eks-1.2.0": - "master" - "node" - "controlplane" - "policies" - "managedservices" "eks-1.5.0": - "master" - "node" - "controlplane" - "policies" - "managedservices" "eks-1.7.0": - "master" - "node" - "controlplane" - "policies" - "managedservices" "eks-1.8.0": - "master" - "node" - "controlplane" - "policies" - "managedservices" "rh-0.7": - "master" - "node" "aks-1.0": - "master" - "node" - "controlplane" - "policies" - "managedservices" "aks-1.7": - "master" - "node" - "controlplane" - "policies" - "managedservices" "aks-1.8": - "master" - "node" - "controlplane" - "policies" - "managedservices" "ack-1.0": - "master" - "node" - "controlplane" - "etcd" - "policies" - "managedservices" "rh-1.0": - "master" - "node" - "controlplane" - "policies" - "etcd" "rh-1.4": - "master" - "node" - "controlplane" - "policies" - "etcd" "rh-1.8": - "master" - "node" - "controlplane" - "policies" - "etcd" "eks-stig-kubernetes-v1r6": - "node" - "controlplane" - "policies" - "managedservices" "tkgi-1.2.53": - "master" - "etcd" - "controlplane" - "node" - "policies" "k3s-cis-1.7": - "master" - "etcd" - "controlplane" - "node" - "policies" "k3s-cis-1.8": - "master" - "etcd" - "controlplane" - "node" - "policies" "k3s-cis-1.23": - "master" - "etcd" - "controlplane" - "node" - "policies" "k3s-cis-1.24": - "master" - "etcd" - "controlplane" - "node" - "policies" "rke-cis-1.7": - "master" - "etcd" - "controlplane" - "node" - "policies" "rke-cis-1.23": - "master" - "etcd" - "controlplane" - "node" - "policies" "rke-cis-1.24": - "master" - "etcd" - "controlplane" - "node" - "policies" "rke2-cis-1.7": - "master" - "etcd" - "controlplane" - "node" - "policies" "rke2-cis-1.8": - "master" - "etcd" - "controlplane" - "node" - "policies" "rke2-cis-1.23": - "master" - "etcd" - "controlplane" - "node" - "policies" "rke2-cis-1.24": - "master" - "etcd" - "controlplane" - "node" - "policies" ================================================ FILE: cfg/eks-1.0.1/config.yaml ================================================ --- ## Version-specific settings that override the values in cfg/config.yaml ## These settings are required if you are using the --asff option to report findings to AWS Security Hub ## AWS account number is required. AWS_ACCOUNT: "" ## AWS region is required. AWS_REGION: "" ## EKS Cluster ARN is required. CLUSTER_ARN: "" ================================================ FILE: cfg/eks-1.0.1/controlplane.yaml ================================================ --- controls: version: "eks-1.0.1" id: 2 text: "Control Plane Configuration" type: "controlplane" groups: - id: 2.1 text: "Logging" checks: - id: 2.1.1 text: "Enable audit logs (Manual)" remediation: "Enable control plane logging for API Server, Audit, Authenticator, Controller Manager, and Scheduler." scored: false ================================================ FILE: cfg/eks-1.0.1/managedservices.yaml ================================================ --- controls: version: "eks-1.0.1" id: 5 text: "Managed Services" type: "managedservices" groups: - id: 5.1 text: "Image Registry and Image Scanning" checks: - id: 5.1.1 text: "Ensure Image Vulnerability Scanning using Amazon ECR image scanning or a third-party provider (Manual)" type: "manual" remediation: "No remediation" scored: false - id: 5.1.2 text: "Minimize user access to Amazon ECR (Manual)" type: "manual" remediation: "No remediation" scored: false - id: 5.1.3 text: "Minimize cluster access to read-only for Amazon ECR (Manual)" type: "manual" remediation: "No remediation" scored: false - id: 5.1.4 text: "Minimize Container Registries to only those approved (Manual)" type: "manual" remediation: "No remediation" scored: false - id: 5.2 text: "Identity and Access Management (IAM)" checks: - id: 5.2.1 text: "Prefer using dedicated Amazon EKS Service Accounts (Manual)" type: "manual" remediation: "No remediation" scored: false - id: 5.3 text: "AWS Key Management Service (KMS)" checks: - id: 5.3.1 text: "Ensure Kubernetes Secrets are encrypted using Customer Master Keys (CMKs) managed in AWS KMS (Manual)" type: "manual" remediation: "No remediation" scored: false - id: 5.4 text: "Cluster Networking" checks: - id: 5.4.1 text: "Restrict Access to the Control Plane Endpoint (Manual)" type: "manual" remediation: "No remediation" scored: false - id: 5.4.2 text: "Ensure clusters are created with Private Endpoint Enabled and Public Access Disabled (Manual)" type: "manual" remediation: "No remediation" scored: false - id: 5.4.3 text: "Ensure clusters are created with Private Nodes (Manual)" type: "manual" remediation: "No remediation" scored: false - id: 5.4.4 text: "Ensure Network Policy is Enabled and set as appropriate (Manual)" type: "manual" remediation: "No remediation" scored: false - id: 5.4.5 text: "Encrypt traffic to HTTPS load balancers with TLS certificates (Manual)" type: "manual" remediation: "No remediation" scored: false - id: 5.5 text: "Authentication and Authorization" checks: - id: 5.5.1 text: "Manage Kubernetes RBAC users with AWS IAM Authenticator for Kubernetes (Manual)" type: "manual" remediation: "No remediation" scored: false - id: 5.6 text: "Other Cluster Configurations" checks: - id: 5.6.1 text: "Consider Fargate for running untrusted workloads (Manual)" type: "manual" remediation: "No remediation" scored: false ================================================ FILE: cfg/eks-1.0.1/master.yaml ================================================ --- controls: version: "eks-1.0.1" id: 1 text: "Control Plane Components" type: "master" ================================================ FILE: cfg/eks-1.0.1/node.yaml ================================================ --- controls: version: "eks-1.0.1" id: 3 text: "Worker Node Security Configuration" type: "node" groups: - id: 3.1 text: "Worker Node Configuration Files" checks: - id: 3.1.1 text: "Ensure that the kubeconfig file permissions are set to 644 or more restrictive (Manual)" audit: '/bin/sh -c ''if test -e $kubeletkubeconfig; then stat -c permissions=%a $kubeletkubeconfig; fi'' ' tests: test_items: - flag: "permissions" compare: op: bitmask value: "644" remediation: | Run the below command (based on the file location on your system) on the each worker node. For example, chmod 644 $kubeletkubeconfig scored: false - id: 3.1.2 text: "Ensure that the kubelet kubeconfig file ownership is set to root:root (Manual)" audit: '/bin/sh -c ''if test -e $kubeletkubeconfig; then stat -c %U:%G $kubeletkubeconfig; fi'' ' tests: test_items: - flag: root:root remediation: | Run the below command (based on the file location on your system) on the each worker node. For example, chown root:root $kubeletkubeconfig scored: false - id: 3.1.3 text: "Ensure that the kubelet configuration file has permissions set to 644 or more restrictive (Manual)" audit: '/bin/sh -c ''if test -e $kubeletconf; then stat -c permissions=%a $kubeletconf; fi'' ' tests: test_items: - flag: "permissions" compare: op: bitmask value: "644" remediation: | Run the following command (using the config file location identified in the Audit step) chmod 644 $kubeletconf scored: false - id: 3.1.4 text: "Ensure that the kubelet configuration file ownership is set to root:root (Manual)" audit: '/bin/sh -c ''if test -e $kubeletconf; then stat -c %U:%G $kubeletconf; fi'' ' tests: test_items: - flag: root:root remediation: | Run the following command (using the config file location identified in the Audit step) chown root:root $kubeletconf scored: false - id: 3.2 text: "Kubelet" checks: - id: 3.2.1 text: "Ensure that the --anonymous-auth argument is set to false (Automated)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: "--anonymous-auth" path: '{.authentication.anonymous.enabled}' set: true compare: op: eq value: false remediation: | If using a Kubelet config file, edit the file to set authentication: anonymous: enabled to false. If using executable arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_SYSTEM_PODS_ARGS variable. --anonymous-auth=false Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: true - id: 3.2.2 text: "Ensure that the --authorization-mode argument is not set to AlwaysAllow (Automated)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --authorization-mode path: '{.authorization.mode}' set: true compare: op: nothave value: AlwaysAllow remediation: | If using a Kubelet config file, edit the file to set authorization: mode to Webhook. If using executable arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_AUTHZ_ARGS variable. --authorization-mode=Webhook Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: true - id: 3.2.3 text: "Ensure that the --client-ca-file argument is set as appropriate (Manual)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --client-ca-file path: '{.authentication.x509.clientCAFile}' set: true remediation: | If using a Kubelet config file, edit the file to set authentication: x509: clientCAFile to the location of the client CA file. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_AUTHZ_ARGS variable. --client-ca-file= Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: false - id: 3.2.4 text: "Ensure that the --read-only-port argument is set to 0 (Manual)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: "--read-only-port" path: '{.readOnlyPort}' set: true compare: op: eq value: 0 remediation: | If using a Kubelet config file, edit the file to set readOnlyPort to 0. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_SYSTEM_PODS_ARGS variable. --read-only-port=0 Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: false - id: 3.2.5 text: "Ensure that the --streaming-connection-idle-timeout argument is not set to 0 (Manual)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --streaming-connection-idle-timeout path: '{.streamingConnectionIdleTimeout}' set: true compare: op: noteq value: 0 - flag: --streaming-connection-idle-timeout path: '{.streamingConnectionIdleTimeout}' set: false bin_op: or remediation: | If using a Kubelet config file, edit the file to set streamingConnectionIdleTimeout to a value other than 0. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_SYSTEM_PODS_ARGS variable. --streaming-connection-idle-timeout=5m Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: false - id: 3.2.6 text: "Ensure that the --protect-kernel-defaults argument is set to true (Automated)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --protect-kernel-defaults path: '{.protectKernelDefaults}' set: true compare: op: eq value: true remediation: | If using a Kubelet config file, edit the file to set protectKernelDefaults: true. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_SYSTEM_PODS_ARGS variable. --protect-kernel-defaults=true Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: true - id: 3.2.7 text: "Ensure that the --make-iptables-util-chains argument is set to true (Automated) " audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --make-iptables-util-chains path: '{.makeIPTablesUtilChains}' set: true compare: op: eq value: true - flag: --make-iptables-util-chains path: '{.makeIPTablesUtilChains}' set: false bin_op: or remediation: | If using a Kubelet config file, edit the file to set makeIPTablesUtilChains: true. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and remove the --make-iptables-util-chains argument from the KUBELET_SYSTEM_PODS_ARGS variable. Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: true - id: 3.2.8 text: "Ensure that the --hostname-override argument is not set (Manual)" # This is one of those properties that can only be set as a command line argument. # To check if the property is set as expected, we need to parse the kubelet command # instead reading the Kubelet Configuration file. audit: "/bin/ps -fC $kubeletbin " tests: test_items: - flag: --hostname-override set: false remediation: | Edit the kubelet service file $kubeletsvc on each worker node and remove the --hostname-override argument from the KUBELET_SYSTEM_PODS_ARGS variable. Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: false - id: 3.2.9 text: "Ensure that the --eventRecordQPS argument is set to 0 or a level which ensures appropriate event capture (Automated)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --event-qps path: '{.eventRecordQPS}' set: true compare: op: eq value: 0 remediation: | If using a Kubelet config file, edit the file to set eventRecordQPS: to an appropriate level. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_SYSTEM_PODS_ARGS variable. Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: false - id: 3.2.10 text: "Ensure that the --rotate-certificates argument is not set to false (Manual)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --rotate-certificates path: '{.rotateCertificates}' set: true compare: op: eq value: true - flag: --rotate-certificates path: '{.rotateCertificates}' set: false bin_op: or remediation: | If using a Kubelet config file, edit the file to add the line rotateCertificates: true or remove it altogether to use the default value. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and remove --rotate-certificates=false argument from the KUBELET_CERTIFICATE_ARGS variable. Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: false - id: 3.2.11 text: "Ensure that the RotateKubeletServerCertificate argument is set to true (Manual)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: RotateKubeletServerCertificate path: '{.featureGates.RotateKubeletServerCertificate}' set: true compare: op: eq value: true remediation: | Edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_CERTIFICATE_ARGS variable. --feature-gates=RotateKubeletServerCertificate=true Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: false ================================================ FILE: cfg/eks-1.0.1/policies.yaml ================================================ --- controls: version: "eks-1.0.1" id: 4 text: "Policies" type: "policies" groups: - id: 4.1 text: "RBAC and Service Accounts" checks: - id: 4.1.1 text: "Ensure that the cluster-admin role is only used where required (Manual)" type: "manual" remediation: | Identify all clusterrolebindings to the cluster-admin role. Check if they are used and if they need this role or if they could use a role with fewer privileges. Where possible, first bind users to a lower privileged role and then remove the clusterrolebinding to the cluster-admin role : kubectl delete clusterrolebinding [name] scored: false - id: 4.1.2 text: "Minimize access to secrets (Manual)" type: "manual" remediation: | Where possible, remove get, list and watch access to secret objects in the cluster. scored: false - id: 4.1.3 text: "Minimize wildcard use in Roles and ClusterRoles (Manual)" type: "manual" remediation: | Where possible replace any use of wildcards in clusterroles and roles with specific objects or actions. scored: false - id: 4.1.4 text: "Minimize access to create pods (Manual)" type: "manual" remediation: | Where possible, remove create access to pod objects in the cluster. scored: false - id: 4.1.5 text: "Ensure that default service accounts are not actively used. (Manual)" type: "manual" remediation: | Create explicit service accounts wherever a Kubernetes workload requires specific access to the Kubernetes API server. Modify the configuration of each default service account to include this value automountServiceAccountToken: false scored: false - id: 4.1.6 text: "Ensure that Service Account Tokens are only mounted where necessary (Manual)" type: "manual" remediation: | Modify the definition of pods and service accounts which do not need to mount service account tokens to disable it. scored: false - id: 4.2 text: "Pod Security Policies" checks: - id: 4.2.1 text: "Minimize the admission of privileged containers (Automated)" type: "manual" remediation: | Create a PSP as described in the Kubernetes documentation, ensuring that the .spec.privileged field is omitted or set to false. scored: false - id: 4.2.2 text: "Minimize the admission of containers wishing to share the host process ID namespace (Automated)" type: "manual" remediation: | Create a PSP as described in the Kubernetes documentation, ensuring that the .spec.hostPID field is omitted or set to false. scored: false - id: 4.2.3 text: "Minimize the admission of containers wishing to share the host IPC namespace (Automated)" type: "manual" remediation: | Create a PSP as described in the Kubernetes documentation, ensuring that the .spec.hostIPC field is omitted or set to false. scored: false - id: 4.2.4 text: "Minimize the admission of containers wishing to share the host network namespace (Automated)" type: "manual" remediation: | Create a PSP as described in the Kubernetes documentation, ensuring that the .spec.hostNetwork field is omitted or set to false. scored: false - id: 4.2.5 text: "Minimize the admission of containers with allowPrivilegeEscalation (Automated)" type: "manual" remediation: | Create a PSP as described in the Kubernetes documentation, ensuring that the .spec.allowPrivilegeEscalation field is omitted or set to false. scored: false - id: 4.2.6 text: "Minimize the admission of root containers (Automated)" type: "manual" remediation: | Create a PSP as described in the Kubernetes documentation, ensuring that the .spec.runAsUser.rule is set to either MustRunAsNonRoot or MustRunAs with the range of UIDs not including 0. scored: false - id: 4.2.7 text: "Minimize the admission of containers with the NET_RAW capability (Automated)" type: "manual" remediation: | Create a PSP as described in the Kubernetes documentation, ensuring that the .spec.requiredDropCapabilities is set to include either NET_RAW or ALL. scored: false - id: 4.2.8 text: "Minimize the admission of containers with added capabilities (Automated)" type: "manual" remediation: | Ensure that allowedCapabilities is not present in PSPs for the cluster unless it is set to an empty array. scored: false - id: 4.2.9 text: "Minimize the admission of containers with capabilities assigned (Manual)" type: "manual" remediation: | Review the use of capabilities in applications running on your cluster. Where a namespace contains applications which do not require any Linux capabities to operate consider adding a PSP which forbids the admission of containers which do not drop all capabilities. scored: false - id: 4.3 text: "CNI Plugin" checks: - id: 4.3.1 text: "Ensure that the latest CNI version is used (Manual)" type: "manual" remediation: | Review the documentation of AWS CNI plugin, and ensure latest CNI version is used. scored: false - id: 4.3.2 text: "Ensure that all Namespaces have Network Policies defined (Automated)" type: "manual" remediation: | Follow the documentation and create NetworkPolicy objects as you need them. scored: false - id: 4.4 text: "Secrets Management" checks: - id: 4.4.1 text: "Prefer using secrets as files over secrets as environment variables (Manual)" type: "manual" remediation: | If possible, rewrite application code to read secrets from mounted secret files, rather than from environment variables. scored: false - id: 4.4.2 text: "Consider external secret storage (Manual)" type: "manual" remediation: | Refer to the secrets management options offered by your cloud provider or a third-party secrets management solution. scored: false - id: 4.5 text: "Extensible Admission Control" checks: - id: 4.5.1 text: "Configure Image Provenance using ImagePolicyWebhook admission controller (Manual)" type: "manual" remediation: | Follow the Kubernetes documentation and setup image provenance. scored: false - id: 4.6 text: "General Policies" checks: - id: 4.6.1 text: "Create administrative boundaries between resources using namespaces (Manual)" type: "manual" remediation: | Follow the documentation and create namespaces for objects in your deployment as you need them. scored: false - id: 4.6.2 text: "Apply Security Context to Your Pods and Containers (Manual)" type: "manual" remediation: | Follow the Kubernetes documentation and apply security contexts to your pods. For a suggested list of security contexts, you may refer to the CIS Security Benchmark for Docker Containers. scored: false - id: 4.6.3 text: "The default namespace should not be used (Automated)" type: "manual" remediation: | Ensure that namespaces are created to allow for appropriate segregation of Kubernetes resources and that all new resources are created in a specific namespace. scored: false ================================================ FILE: cfg/eks-1.1.0/config.yaml ================================================ --- ## Version-specific settings that override the values in cfg/config.yaml ## These settings are required if you are using the --asff option to report findings to AWS Security Hub ## AWS account number is required. AWS_ACCOUNT: "" ## AWS region is required. AWS_REGION: "" ## EKS Cluster ARN is required. CLUSTER_ARN: "" ================================================ FILE: cfg/eks-1.1.0/controlplane.yaml ================================================ --- controls: version: "eks-1.1.0" id: 2 text: "Control Plane Configuration" type: "controlplane" groups: - id: 2.1 text: "Logging" checks: - id: 2.1.1 text: "Enable audit logs (Manual)" remediation: "Enable control plane logging for API Server, Audit, Authenticator, Controller Manager, and Scheduler." scored: false ================================================ FILE: cfg/eks-1.1.0/managedservices.yaml ================================================ --- controls: version: "eks-1.1.0" id: 5 text: "Managed Services" type: "managedservices" groups: - id: 5.1 text: "Image Registry and Image Scanning" checks: - id: 5.1.1 text: "Ensure Image Vulnerability Scanning using Amazon ECR image scanning or a third-party provider (Manual)" type: "manual" remediation: | To utilize AWS ECR for Image scanning please follow the steps below: To create a repository configured for scan on push (AWS CLI): aws ecr create-repository --repository-name $REPO_NAME --image-scanning-configuration scanOnPush=true --region $REGION_CODE To edit the settings of an existing repository (AWS CLI): aws ecr put-image-scanning-configuration --repository-name $REPO_NAME --image-scanning-configuration scanOnPush=true --region $REGION_CODE Use the following steps to start a manual image scan using the AWS Management Console. Open the Amazon ECR console at https://console.aws.amazon.com/ecr/repositories. From the navigation bar, choose the Region to create your repository in. In the navigation pane, choose Repositories. On the Repositories page, choose the repository that contains the image to scan. On the Images page, select the image to scan and then choose Scan. scored: false - id: 5.1.2 text: "Minimize user access to Amazon ECR (Manual)" type: "manual" remediation: | Before you use IAM to manage access to Amazon ECR, you should understand what IAM features are available to use with Amazon ECR. To get a high-level view of how Amazon ECR and other AWS services work with IAM, see AWS Services That Work with IAM in the IAM User Guide. scored: false - id: 5.1.3 text: "Minimize cluster access to read-only for Amazon ECR (Manual)" type: "manual" remediation: | You can use your Amazon ECR images with Amazon EKS, but you need to satisfy the following prerequisites. The Amazon EKS worker node IAM role (NodeInstanceRole) that you use with your worker nodes must possess the following IAM policy permissions for Amazon ECR. { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "ecr:BatchCheckLayerAvailability", "ecr:BatchGetImage", "ecr:GetDownloadUrlForLayer", "ecr:GetAuthorizationToken" ], "Resource": "*" } ] } scored: false - id: 5.1.4 text: "Minimize Container Registries to only those approved (Manual)" type: "manual" remediation: "No remediation" scored: false - id: 5.2 text: "Identity and Access Management (IAM)" checks: - id: 5.2.1 text: "Prefer using dedicated Amazon EKS Service Accounts (Manual)" type: "manual" remediation: "No remediation" scored: false - id: 5.3 text: "AWS Key Management Service (KMS)" checks: - id: 5.3.1 text: "Ensure Kubernetes Secrets are encrypted using Customer Master Keys (CMKs) managed in AWS KMS (Manual)" type: "manual" remediation: | This process can only be performed during Cluster Creation. Enable 'Secrets Encryption' during Amazon EKS cluster creation as described in the links within the 'References' section. scored: false - id: 5.4 text: "Cluster Networking" checks: - id: 5.4.1 text: "Restrict Access to the Control Plane Endpoint (Manual)" type: "manual" remediation: "No remediation" scored: false - id: 5.4.2 text: "Ensure clusters are created with Private Endpoint Enabled and Public Access Disabled (Manual)" type: "manual" remediation: "No remediation" scored: false - id: 5.4.3 text: "Ensure clusters are created with Private Nodes (Manual)" type: "manual" remediation: "No remediation" scored: false - id: 5.4.4 text: "Ensure Network Policy is Enabled and set as appropriate (Manual)" type: "manual" remediation: "No remediation" scored: false - id: 5.4.5 text: "Encrypt traffic to HTTPS load balancers with TLS certificates (Manual)" type: "manual" remediation: "No remediation" scored: false - id: 5.5 text: "Authentication and Authorization" checks: - id: 5.5.1 text: "Manage Kubernetes RBAC users with AWS IAM Authenticator for Kubernetes (Manual)" type: "manual" remediation: | Refer to the 'Managing users or IAM roles for your cluster' in Amazon EKS documentation. scored: false - id: 5.6 text: "Other Cluster Configurations" checks: - id: 5.6.1 text: "Consider Fargate for running untrusted workloads (Manual)" type: "manual" remediation: | Create a Fargate profile for your cluster Before you can schedule pods running on Fargate in your cluster, you must define a Fargate profile that specifies which pods should use Fargate when they are launched. For more information, see AWS Fargate profile. Note: If you created your cluster with eksctl using the --fargate option, then a Fargate profile has already been created for your cluster with selectors for all pods in the kube-system and default namespaces. Use the following procedure to create Fargate profiles for any other namespaces you would like to use with Fargate. scored: false ================================================ FILE: cfg/eks-1.1.0/master.yaml ================================================ --- controls: version: "eks-1.1.0" id: 1 text: "Control Plane Components" type: "master" ================================================ FILE: cfg/eks-1.1.0/node.yaml ================================================ --- controls: version: "eks-1.1.0" id: 3 text: "Worker Node Security Configuration" type: "node" groups: - id: 3.1 text: "Worker Node Configuration Files" checks: - id: 3.1.1 text: "Ensure that the kubeconfig file permissions are set to 644 or more restrictive (Manual)" audit: '/bin/sh -c ''if test -e $kubeletkubeconfig; then stat -c permissions=%a $kubeletkubeconfig; fi'' ' tests: test_items: - flag: "permissions" compare: op: bitmask value: "644" remediation: | Run the below command (based on the file location on your system) on the each worker node. For example, chmod 644 $kubeletkubeconfig scored: false - id: 3.1.2 text: "Ensure that the kubelet kubeconfig file ownership is set to root:root (Manual)" audit: '/bin/sh -c ''if test -e $kubeletkubeconfig; then stat -c %U:%G $kubeletkubeconfig; fi'' ' tests: test_items: - flag: root:root remediation: | Run the below command (based on the file location on your system) on the each worker node. For example, chown root:root $kubeletkubeconfig scored: false - id: 3.1.3 text: "Ensure that the kubelet configuration file has permissions set to 644 or more restrictive (Manual)" audit: '/bin/sh -c ''if test -e $kubeletconf; then stat -c permissions=%a $kubeletconf; fi'' ' tests: test_items: - flag: "permissions" compare: op: bitmask value: "644" remediation: | Run the following command (using the config file location identified in the Audit step) chmod 644 $kubeletconf scored: false - id: 3.1.4 text: "Ensure that the kubelet configuration file ownership is set to root:root (Manual)" audit: '/bin/sh -c ''if test -e $kubeletconf; then stat -c %U:%G $kubeletconf; fi'' ' tests: test_items: - flag: root:root remediation: | Run the following command (using the config file location identified in the Audit step) chown root:root $kubeletconf scored: false - id: 3.2 text: "Kubelet" checks: - id: 3.2.1 text: "Ensure that the --anonymous-auth argument is set to false (Automated)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: "--anonymous-auth" path: '{.authentication.anonymous.enabled}' set: true compare: op: eq value: false remediation: | If using a Kubelet config file, edit the file to set authentication: anonymous: enabled to false. If using executable arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_SYSTEM_PODS_ARGS variable. --anonymous-auth=false Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: true - id: 3.2.2 text: "Ensure that the --authorization-mode argument is not set to AlwaysAllow (Automated)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --authorization-mode path: '{.authorization.mode}' set: true compare: op: nothave value: AlwaysAllow remediation: | If using a Kubelet config file, edit the file to set authorization: mode to Webhook. If using executable arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_AUTHZ_ARGS variable. --authorization-mode=Webhook Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: true - id: 3.2.3 text: "Ensure that the --client-ca-file argument is set as appropriate (Manual)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --client-ca-file path: '{.authentication.x509.clientCAFile}' set: true remediation: | If using a Kubelet config file, edit the file to set authentication: x509: clientCAFile to the location of the client CA file. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_AUTHZ_ARGS variable. --client-ca-file= Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: false - id: 3.2.4 text: "Ensure that the --read-only-port argument is set to 0 (Manual)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: "--read-only-port" path: '{.readOnlyPort}' set: true compare: op: eq value: 0 remediation: | If using a Kubelet config file, edit the file to set readOnlyPort to 0. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_SYSTEM_PODS_ARGS variable. --read-only-port=0 Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: false - id: 3.2.5 text: "Ensure that the --streaming-connection-idle-timeout argument is not set to 0 (Manual)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --streaming-connection-idle-timeout path: '{.streamingConnectionIdleTimeout}' set: true compare: op: noteq value: 0 - flag: --streaming-connection-idle-timeout path: '{.streamingConnectionIdleTimeout}' set: false bin_op: or remediation: | If using a Kubelet config file, edit the file to set streamingConnectionIdleTimeout to a value other than 0. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_SYSTEM_PODS_ARGS variable. --streaming-connection-idle-timeout=5m Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: false - id: 3.2.6 text: "Ensure that the --protect-kernel-defaults argument is set to true (Automated)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --protect-kernel-defaults path: '{.protectKernelDefaults}' set: true compare: op: eq value: true remediation: | If using a Kubelet config file, edit the file to set protectKernelDefaults: true. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_SYSTEM_PODS_ARGS variable. --protect-kernel-defaults=true Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: true - id: 3.2.7 text: "Ensure that the --make-iptables-util-chains argument is set to true (Automated) " audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --make-iptables-util-chains path: '{.makeIPTablesUtilChains}' set: true compare: op: eq value: true - flag: --make-iptables-util-chains path: '{.makeIPTablesUtilChains}' set: false bin_op: or remediation: | If using a Kubelet config file, edit the file to set makeIPTablesUtilChains: true. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and remove the --make-iptables-util-chains argument from the KUBELET_SYSTEM_PODS_ARGS variable. Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: true - id: 3.2.8 text: "Ensure that the --hostname-override argument is not set (Manual)" # This is one of those properties that can only be set as a command line argument. # To check if the property is set as expected, we need to parse the kubelet command # instead reading the Kubelet Configuration file. audit: "/bin/ps -fC $kubeletbin " tests: test_items: - flag: --hostname-override set: false remediation: | Edit the kubelet service file $kubeletsvc on each worker node and remove the --hostname-override argument from the KUBELET_SYSTEM_PODS_ARGS variable. Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: false - id: 3.2.9 text: "Ensure that the --eventRecordQPS argument is set to 0 or a level which ensures appropriate event capture (Automated)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --event-qps path: '{.eventRecordQPS}' set: true compare: op: eq value: 0 remediation: | If using a Kubelet config file, edit the file to set eventRecordQPS: to an appropriate level. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_SYSTEM_PODS_ARGS variable. Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: false - id: 3.2.10 text: "Ensure that the --rotate-certificates argument is not set to false (Manual)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --rotate-certificates path: '{.rotateCertificates}' set: true compare: op: eq value: true - flag: --rotate-certificates path: '{.rotateCertificates}' set: false bin_op: or remediation: | If using a Kubelet config file, edit the file to add the line rotateCertificates: true or remove it altogether to use the default value. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and remove --rotate-certificates=false argument from the KUBELET_CERTIFICATE_ARGS variable. Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: false - id: 3.2.11 text: "Ensure that the RotateKubeletServerCertificate argument is set to true (Manual)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: RotateKubeletServerCertificate path: '{.featureGates.RotateKubeletServerCertificate}' set: true compare: op: eq value: true remediation: | Edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_CERTIFICATE_ARGS variable. --feature-gates=RotateKubeletServerCertificate=true Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: false - id: 3.3 text: "Container Optimized OS" checks: - id: 3.3.1 text: "Prefer using Container-Optimized OS when possible (Manual)" remediation: "No remediation" scored: false ================================================ FILE: cfg/eks-1.1.0/policies.yaml ================================================ --- controls: version: "eks-1.1.0" id: 4 text: "Policies" type: "policies" groups: - id: 4.1 text: "RBAC and Service Accounts" checks: - id: 4.1.1 text: "Ensure that the cluster-admin role is only used where required (Manual)" type: "manual" remediation: | Identify all clusterrolebindings to the cluster-admin role. Check if they are used and if they need this role or if they could use a role with fewer privileges. Where possible, first bind users to a lower privileged role and then remove the clusterrolebinding to the cluster-admin role : kubectl delete clusterrolebinding [name] scored: false - id: 4.1.2 text: "Minimize access to secrets (Manual)" type: "manual" remediation: | Where possible, remove get, list and watch access to secret objects in the cluster. scored: false - id: 4.1.3 text: "Minimize wildcard use in Roles and ClusterRoles (Manual)" type: "manual" remediation: | Where possible replace any use of wildcards in clusterroles and roles with specific objects or actions. scored: false - id: 4.1.4 text: "Minimize access to create pods (Manual)" type: "manual" remediation: | Where possible, remove create access to pod objects in the cluster. scored: false - id: 4.1.5 text: "Ensure that default service accounts are not actively used. (Manual)" type: "manual" remediation: | Create explicit service accounts wherever a Kubernetes workload requires specific access to the Kubernetes API server. Modify the configuration of each default service account to include this value automountServiceAccountToken: false scored: false - id: 4.1.6 text: "Ensure that Service Account Tokens are only mounted where necessary (Manual)" type: "manual" remediation: | Modify the definition of pods and service accounts which do not need to mount service account tokens to disable it. scored: false - id: 4.2 text: "Pod Security Policies" checks: - id: 4.2.1 text: "Minimize the admission of privileged containers (Automated)" type: "manual" remediation: | Create a PSP as described in the Kubernetes documentation, ensuring that the .spec.privileged field is omitted or set to false. scored: false - id: 4.2.2 text: "Minimize the admission of containers wishing to share the host process ID namespace (Automated)" type: "manual" remediation: | Create a PSP as described in the Kubernetes documentation, ensuring that the .spec.hostPID field is omitted or set to false. scored: false - id: 4.2.3 text: "Minimize the admission of containers wishing to share the host IPC namespace (Automated)" type: "manual" remediation: | Create a PSP as described in the Kubernetes documentation, ensuring that the .spec.hostIPC field is omitted or set to false. scored: false - id: 4.2.4 text: "Minimize the admission of containers wishing to share the host network namespace (Automated)" type: "manual" remediation: | Create a PSP as described in the Kubernetes documentation, ensuring that the .spec.hostNetwork field is omitted or set to false. scored: false - id: 4.2.5 text: "Minimize the admission of containers with allowPrivilegeEscalation (Automated)" type: "manual" remediation: | Create a PSP as described in the Kubernetes documentation, ensuring that the .spec.allowPrivilegeEscalation field is omitted or set to false. scored: false - id: 4.2.6 text: "Minimize the admission of root containers (Automated)" type: "manual" remediation: | Create a PSP as described in the Kubernetes documentation, ensuring that the .spec.runAsUser.rule is set to either MustRunAsNonRoot or MustRunAs with the range of UIDs not including 0. scored: false - id: 4.2.7 text: "Minimize the admission of containers with the NET_RAW capability (Automated)" type: "manual" remediation: | Create a PSP as described in the Kubernetes documentation, ensuring that the .spec.requiredDropCapabilities is set to include either NET_RAW or ALL. scored: false - id: 4.2.8 text: "Minimize the admission of containers with added capabilities (Manual)" type: "manual" remediation: | Ensure that allowedCapabilities is not present in PSPs for the cluster unless it is set to an empty array. scored: false - id: 4.2.9 text: "Minimize the admission of containers with capabilities assigned (Manual)" type: "manual" remediation: | Review the use of capabilities in applications running on your cluster. Where a namespace contains applications which do not require any Linux capabities to operate consider adding a PSP which forbids the admission of containers which do not drop all capabilities. scored: false - id: 4.3 text: "CNI Plugin" checks: - id: 4.3.1 text: "Ensure that the latest CNI version is used (Manual)" type: "manual" remediation: | Review the documentation of AWS CNI plugin, and ensure latest CNI version is used. scored: false - id: 4.3.2 text: "Ensure that all Namespaces have Network Policies defined (Manual)" type: "manual" remediation: | Follow the documentation and create NetworkPolicy objects as you need them. scored: false - id: 4.4 text: "Secrets Management" checks: - id: 4.4.1 text: "Prefer using secrets as files over secrets as environment variables (Manual)" type: "manual" remediation: | If possible, rewrite application code to read secrets from mounted secret files, rather than from environment variables. scored: false - id: 4.4.2 text: "Consider external secret storage (Manual)" type: "manual" remediation: | Refer to the secrets management options offered by your cloud provider or a third-party secrets management solution. scored: false - id: 4.5 text: "Extensible Admission Control" checks: [] - id: 4.6 text: "General Policies" checks: - id: 4.6.1 text: "Create administrative boundaries between resources using namespaces (Manual)" type: "manual" remediation: | Follow the documentation and create namespaces for objects in your deployment as you need them. scored: false - id: 4.6.2 text: "Apply Security Context to Your Pods and Containers (Manual)" type: "manual" remediation: | Follow the Kubernetes documentation and apply security contexts to your pods. For a suggested list of security contexts, you may refer to the CIS Security Benchmark for Docker Containers. scored: false - id: 4.6.3 text: "The default namespace should not be used (Manual)" type: "manual" remediation: | Ensure that namespaces are created to allow for appropriate segregation of Kubernetes resources and that all new resources are created in a specific namespace. scored: false ================================================ FILE: cfg/eks-1.2.0/config.yaml ================================================ --- ## Version-specific settings that override the values in cfg/config.yaml ## These settings are required if you are using the --asff option to report findings to AWS Security Hub ## AWS account number is required. AWS_ACCOUNT: "" ## AWS region is required. AWS_REGION: "" ## EKS Cluster ARN is required. CLUSTER_ARN: "" ================================================ FILE: cfg/eks-1.2.0/controlplane.yaml ================================================ --- controls: version: "eks-1.2.0" id: 2 text: "Control Plane Configuration" type: "controlplane" groups: - id: 2.1 text: "Logging" checks: - id: 2.1.1 text: "Enable audit logs (Manual)" remediation: "Enable control plane logging for API Server, Audit, Authenticator, Controller Manager, and Scheduler." scored: false ================================================ FILE: cfg/eks-1.2.0/managedservices.yaml ================================================ --- controls: version: "eks-1.2.0" id: 5 text: "Managed Services" type: "managedservices" groups: - id: 5.1 text: "Image Registry and Image Scanning" checks: - id: 5.1.1 text: "Ensure Image Vulnerability Scanning using Amazon ECR image scanning or a third-party provider (Manual)" type: "manual" remediation: | To utilize AWS ECR for Image scanning please follow the steps below: To create a repository configured for scan on push (AWS CLI): aws ecr create-repository --repository-name $REPO_NAME --image-scanning-configuration scanOnPush=true --region $REGION_CODE To edit the settings of an existing repository (AWS CLI): aws ecr put-image-scanning-configuration --repository-name $REPO_NAME --image-scanning-configuration scanOnPush=true --region $REGION_CODE Use the following steps to start a manual image scan using the AWS Management Console. Open the Amazon ECR console at https://console.aws.amazon.com/ecr/repositories. From the navigation bar, choose the Region to create your repository in. In the navigation pane, choose Repositories. On the Repositories page, choose the repository that contains the image to scan. On the Images page, select the image to scan and then choose Scan. scored: false - id: 5.1.2 text: "Minimize user access to Amazon ECR (Manual)" type: "manual" remediation: | Before you use IAM to manage access to Amazon ECR, you should understand what IAM features are available to use with Amazon ECR. To get a high-level view of how Amazon ECR and other AWS services work with IAM, see AWS Services That Work with IAM in the IAM User Guide. scored: false - id: 5.1.3 text: "Minimize cluster access to read-only for Amazon ECR (Manual)" type: "manual" remediation: | You can use your Amazon ECR images with Amazon EKS, but you need to satisfy the following prerequisites. The Amazon EKS worker node IAM role (NodeInstanceRole) that you use with your worker nodes must possess the following IAM policy permissions for Amazon ECR. { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "ecr:BatchCheckLayerAvailability", "ecr:BatchGetImage", "ecr:GetDownloadUrlForLayer", "ecr:GetAuthorizationToken" ], "Resource": "*" } ] } scored: false - id: 5.1.4 text: "Minimize Container Registries to only those approved (Manual)" type: "manual" remediation: "No remediation" scored: false - id: 5.2 text: "Identity and Access Management (IAM)" checks: - id: 5.2.1 text: "Prefer using dedicated Amazon EKS Service Accounts (Manual)" type: "manual" remediation: "No remediation" scored: false - id: 5.3 text: "AWS Key Management Service (KMS)" checks: - id: 5.3.1 text: "Ensure Kubernetes Secrets are encrypted using Customer Master Keys (CMKs) managed in AWS KMS (Manual)" type: "manual" remediation: | This process can only be performed during Cluster Creation. Enable 'Secrets Encryption' during Amazon EKS cluster creation as described in the links within the 'References' section. scored: false - id: 5.4 text: "Cluster Networking" checks: - id: 5.4.1 text: "Restrict Access to the Control Plane Endpoint (Manual)" type: "manual" remediation: "No remediation" scored: false - id: 5.4.2 text: "Ensure clusters are created with Private Endpoint Enabled and Public Access Disabled (Manual)" type: "manual" remediation: "No remediation" scored: false - id: 5.4.3 text: "Ensure clusters are created with Private Nodes (Manual)" type: "manual" remediation: "No remediation" scored: false - id: 5.4.4 text: "Ensure Network Policy is Enabled and set as appropriate (Manual)" type: "manual" remediation: "No remediation" scored: false - id: 5.4.5 text: "Encrypt traffic to HTTPS load balancers with TLS certificates (Manual)" type: "manual" remediation: "No remediation" scored: false - id: 5.5 text: "Authentication and Authorization" checks: - id: 5.5.1 text: "Manage Kubernetes RBAC users with AWS IAM Authenticator for Kubernetes (Manual)" type: "manual" remediation: | Refer to the 'Managing users or IAM roles for your cluster' in Amazon EKS documentation. scored: false - id: 5.6 text: "Other Cluster Configurations" checks: - id: 5.6.1 text: "Consider Fargate for running untrusted workloads (Manual)" type: "manual" remediation: | Create a Fargate profile for your cluster Before you can schedule pods running on Fargate in your cluster, you must define a Fargate profile that specifies which pods should use Fargate when they are launched. For more information, see AWS Fargate profile. Note: If you created your cluster with eksctl using the --fargate option, then a Fargate profile has already been created for your cluster with selectors for all pods in the kube-system and default namespaces. Use the following procedure to create Fargate profiles for any other namespaces you would like to use with Fargate. scored: false ================================================ FILE: cfg/eks-1.2.0/master.yaml ================================================ --- controls: version: "eks-1.2.0" id: 1 text: "Control Plane Components" type: "master" ================================================ FILE: cfg/eks-1.2.0/node.yaml ================================================ --- controls: version: "eks-1.2.0" id: 3 text: "Worker Node Security Configuration" type: "node" groups: - id: 3.1 text: "Worker Node Configuration Files" checks: - id: 3.1.1 text: "Ensure that the kubeconfig file permissions are set to 644 or more restrictive (Manual)" audit: '/bin/sh -c ''if test -e $kubeletkubeconfig; then stat -c permissions=%a $kubeletkubeconfig; fi'' ' tests: test_items: - flag: "permissions" compare: op: bitmask value: "644" remediation: | Run the below command (based on the file location on your system) on the each worker node. For example, chmod 644 $kubeletkubeconfig scored: false - id: 3.1.2 text: "Ensure that the kubelet kubeconfig file ownership is set to root:root (Manual)" audit: '/bin/sh -c ''if test -e $kubeletkubeconfig; then stat -c %U:%G $kubeletkubeconfig; fi'' ' tests: test_items: - flag: root:root remediation: | Run the below command (based on the file location on your system) on the each worker node. For example, chown root:root $kubeletkubeconfig scored: false - id: 3.1.3 text: "Ensure that the kubelet configuration file has permissions set to 644 or more restrictive (Manual)" audit: '/bin/sh -c ''if test -e $kubeletconf; then stat -c permissions=%a $kubeletconf; fi'' ' tests: test_items: - flag: "permissions" compare: op: bitmask value: "644" remediation: | Run the following command (using the config file location identified in the Audit step) chmod 644 $kubeletconf scored: false - id: 3.1.4 text: "Ensure that the kubelet configuration file ownership is set to root:root (Manual)" audit: '/bin/sh -c ''if test -e $kubeletconf; then stat -c %U:%G $kubeletconf; fi'' ' tests: test_items: - flag: root:root remediation: | Run the following command (using the config file location identified in the Audit step) chown root:root $kubeletconf scored: false - id: 3.2 text: "Kubelet" checks: - id: 3.2.1 text: "Ensure that the Anonymous Auth is Not Enabled (Automated)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: "--anonymous-auth" path: '{.authentication.anonymous.enabled}' set: true compare: op: eq value: false remediation: | If using a Kubelet config file, edit the file to set authentication: anonymous: enabled to false. If using executable arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_SYSTEM_PODS_ARGS variable. --anonymous-auth=false Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: true - id: 3.2.2 text: "Ensure that the --authorization-mode argument is not set to AlwaysAllow (Automated)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --authorization-mode path: '{.authorization.mode}' set: true compare: op: nothave value: AlwaysAllow remediation: | If using a Kubelet config file, edit the file to set authorization: mode to Webhook. If using executable arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_AUTHZ_ARGS variable. --authorization-mode=Webhook Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: true - id: 3.2.3 text: "Ensure that a Client CA File is Configured (Manual)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --client-ca-file path: '{.authentication.x509.clientCAFile}' set: true remediation: | If using a Kubelet config file, edit the file to set authentication: x509: clientCAFile to the location of the client CA file. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_AUTHZ_ARGS variable. --client-ca-file= Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: false - id: 3.2.4 text: "Ensure that the --read-only-port is disabled (Manual)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: "--read-only-port" path: '{.readOnlyPort}' set: true compare: op: eq value: 0 remediation: | If using a Kubelet config file, edit the file to set readOnlyPort to 0. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_SYSTEM_PODS_ARGS variable. --read-only-port=0 Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: false - id: 3.2.5 text: "Ensure that the --streaming-connection-idle-timeout argument is not set to 0 (Automated)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --streaming-connection-idle-timeout path: '{.streamingConnectionIdleTimeout}' set: true compare: op: noteq value: 0 - flag: --streaming-connection-idle-timeout path: '{.streamingConnectionIdleTimeout}' set: false bin_op: or remediation: | If using a Kubelet config file, edit the file to set streamingConnectionIdleTimeout to a value other than 0. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_SYSTEM_PODS_ARGS variable. --streaming-connection-idle-timeout=5m Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: true - id: 3.2.6 text: "Ensure that the --protect-kernel-defaults argument is set to true (Automated)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --protect-kernel-defaults path: '{.protectKernelDefaults}' set: true compare: op: eq value: true remediation: | If using a Kubelet config file, edit the file to set protectKernelDefaults: true. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_SYSTEM_PODS_ARGS variable. --protect-kernel-defaults=true Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: true - id: 3.2.7 text: "Ensure that the --make-iptables-util-chains argument is set to true (Automated) " audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --make-iptables-util-chains path: '{.makeIPTablesUtilChains}' set: true compare: op: eq value: true - flag: --make-iptables-util-chains path: '{.makeIPTablesUtilChains}' set: false bin_op: or remediation: | If using a Kubelet config file, edit the file to set makeIPTablesUtilChains: true. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and remove the --make-iptables-util-chains argument from the KUBELET_SYSTEM_PODS_ARGS variable. Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: true - id: 3.2.8 text: "Ensure that the --hostname-override argument is not set (Manual)" # This is one of those properties that can only be set as a command line argument. # To check if the property is set as expected, we need to parse the kubelet command # instead reading the Kubelet Configuration file. audit: "/bin/ps -fC $kubeletbin " tests: test_items: - flag: --hostname-override set: false remediation: | Edit the kubelet service file $kubeletsvc on each worker node and remove the --hostname-override argument from the KUBELET_SYSTEM_PODS_ARGS variable. Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: false - id: 3.2.9 text: "Ensure that the --eventRecordQPS argument is set to 0 or a level which ensures appropriate event capture (Automated)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --event-qps path: '{.eventRecordQPS}' set: true compare: op: gte value: 0 remediation: | If using a Kubelet config file, edit the file to set eventRecordQPS: to an appropriate level. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_SYSTEM_PODS_ARGS variable. Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: false - id: 3.2.10 text: "Ensure that the --rotate-certificates argument is not present or is set to true (Manual)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --rotate-certificates path: '{.rotateCertificates}' set: true compare: op: eq value: true - flag: --rotate-certificates path: '{.rotateCertificates}' set: false bin_op: or remediation: | If using a Kubelet config file, edit the file to add the line rotateCertificates: true or remove it altogether to use the default value. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and remove --rotate-certificates=false argument from the KUBELET_CERTIFICATE_ARGS variable. Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: false - id: 3.2.11 text: "Ensure that the RotateKubeletServerCertificate argument is set to true (Manual)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: RotateKubeletServerCertificate path: '{.featureGates.RotateKubeletServerCertificate}' set: true compare: op: eq value: true remediation: | Edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_CERTIFICATE_ARGS variable. --feature-gates=RotateKubeletServerCertificate=true Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: false - id: 3.3 text: "Container Optimized OS" checks: - id: 3.3.1 text: "Prefer using a container-optimized OS when possible (Manual)" remediation: "No remediation" scored: false ================================================ FILE: cfg/eks-1.2.0/policies.yaml ================================================ --- controls: version: "eks-1.2.0" id: 4 text: "Policies" type: "policies" groups: - id: 4.1 text: "RBAC and Service Accounts" checks: - id: 4.1.1 text: "Ensure that the cluster-admin role is only used where required (Manual)" type: "manual" remediation: | Identify all clusterrolebindings to the cluster-admin role. Check if they are used and if they need this role or if they could use a role with fewer privileges. Where possible, first bind users to a lower privileged role and then remove the clusterrolebinding to the cluster-admin role : kubectl delete clusterrolebinding [name] scored: false - id: 4.1.2 text: "Minimize access to secrets (Manual)" type: "manual" remediation: | Where possible, remove get, list and watch access to secret objects in the cluster. scored: false - id: 4.1.3 text: "Minimize wildcard use in Roles and ClusterRoles (Manual)" type: "manual" remediation: | Where possible replace any use of wildcards in clusterroles and roles with specific objects or actions. scored: false - id: 4.1.4 text: "Minimize access to create pods (Manual)" type: "manual" remediation: | Where possible, remove create access to pod objects in the cluster. scored: false - id: 4.1.5 text: "Ensure that default service accounts are not actively used. (Manual)" type: "manual" remediation: | Create explicit service accounts wherever a Kubernetes workload requires specific access to the Kubernetes API server. Modify the configuration of each default service account to include this value automountServiceAccountToken: false scored: false - id: 4.1.6 text: "Ensure that Service Account Tokens are only mounted where necessary (Manual)" type: "manual" remediation: | Modify the definition of pods and service accounts which do not need to mount service account tokens to disable it. scored: false - id: 4.1.7 text: "Avoid use of system:masters group (Manual)" type: "manual" remediation: | Remove the system:masters group from all users in the cluster. scored: false - id: 4.1.8 text: "Limit use of the Bind, Impersonate and Escalate permissions in the Kubernetes cluster (Manual)" type: "manual" remediation: | Where possible, remove the impersonate, bind and escalate rights from subjects. scored: false - id: 4.2 text: "Pod Security Policies" checks: - id: 4.2.1 text: "Minimize the admission of privileged containers (Automated)" type: "manual" remediation: | Create a PSP as described in the Kubernetes documentation, ensuring that the .spec.privileged field is omitted or set to false. scored: false - id: 4.2.2 text: "Minimize the admission of containers wishing to share the host process ID namespace (Automated)" type: "manual" remediation: | Create a PSP as described in the Kubernetes documentation, ensuring that the .spec.hostPID field is omitted or set to false. scored: false - id: 4.2.3 text: "Minimize the admission of containers wishing to share the host IPC namespace (Automated)" type: "manual" remediation: | Create a PSP as described in the Kubernetes documentation, ensuring that the .spec.hostIPC field is omitted or set to false. scored: false - id: 4.2.4 text: "Minimize the admission of containers wishing to share the host network namespace (Automated)" type: "manual" remediation: | Create a PSP as described in the Kubernetes documentation, ensuring that the .spec.hostNetwork field is omitted or set to false. scored: false - id: 4.2.5 text: "Minimize the admission of containers with allowPrivilegeEscalation (Automated)" type: "manual" remediation: | Create a PSP as described in the Kubernetes documentation, ensuring that the .spec.allowPrivilegeEscalation field is omitted or set to false. scored: false - id: 4.2.6 text: "Minimize the admission of root containers (Automated)" type: "manual" remediation: | Create a PSP as described in the Kubernetes documentation, ensuring that the .spec.runAsUser.rule is set to either MustRunAsNonRoot or MustRunAs with the range of UIDs not including 0. scored: false - id: 4.2.7 text: "Minimize the admission of containers with added capabilities (Manual)" type: "manual" remediation: | Ensure that allowedCapabilities is not present in PSPs for the cluster unless it is set to an empty array. scored: false - id: 4.2.8 text: "Minimize the admission of containers with capabilities assigned (Manual)" type: "manual" remediation: | Review the use of capabilities in applications running on your cluster. Where a namespace contains applications which do not require any Linux capabities to operate consider adding a PSP which forbids the admission of containers which do not drop all capabilities. scored: false - id: 4.3 text: "CNI Plugin" checks: - id: 4.3.1 text: "Ensure CNI plugin supports network policies (Manual)" type: "manual" remediation: | As with RBAC policies, network policies should adhere to the policy of least privileged access. Start by creating a deny all policy that restricts all inbound and outbound traffic from a namespace or create a global policy using Calico. scored: false - id: 4.3.2 text: "Ensure that all Namespaces have Network Policies defined (Manual)" type: "manual" remediation: | Follow the documentation and create NetworkPolicy objects as you need them. scored: false - id: 4.4 text: "Secrets Management" checks: - id: 4.4.1 text: "Prefer using secrets as files over secrets as environment variables (Manual)" type: "manual" remediation: | If possible, rewrite application code to read secrets from mounted secret files, rather than from environment variables. scored: false - id: 4.4.2 text: "Consider external secret storage (Manual)" type: "manual" remediation: | Refer to the secrets management options offered by your cloud provider or a third-party secrets management solution. scored: false - id: 4.5 text: "Extensible Admission Control" checks: [] - id: 4.6 text: "General Policies" checks: - id: 4.6.1 text: "Create administrative boundaries between resources using namespaces (Manual)" type: "manual" remediation: | Follow the documentation and create namespaces for objects in your deployment as you need them. scored: false - id: 4.6.2 text: "Apply Security Context to Your Pods and Containers (Manual)" type: "manual" remediation: | Follow the Kubernetes documentation and apply security contexts to your pods. For a suggested list of security contexts, you may refer to the CIS Security Benchmark for Docker Containers. scored: false - id: 4.6.3 text: "The default namespace should not be used (Manual)" type: "manual" remediation: | Ensure that namespaces are created to allow for appropriate segregation of Kubernetes resources and that all new resources are created in a specific namespace. scored: false ================================================ FILE: cfg/eks-1.5.0/config.yaml ================================================ --- ## Version-specific settings that override the values in cfg/config.yaml ## These settings are required if you are using the --asff option to report findings to AWS Security Hub ## AWS account number is required. AWS_ACCOUNT: "" ## AWS region is required. AWS_REGION: "" ## EKS Cluster ARN is required. CLUSTER_ARN: "" ================================================ FILE: cfg/eks-1.5.0/controlplane.yaml ================================================ --- controls: version: "eks-1.5.0" id: 2 text: "Control Plane Configuration" type: "controlplane" groups: - id: 2.1 text: "Logging" checks: - id: 2.1.1 text: "Enable audit Logs (Automated)" remediation: | From Console: 1. For each EKS Cluster in each region; 2. Go to 'Amazon EKS' > 'Clusters' > '' > 'Configuration' > 'Logging'. 3. Click 'Manage logging'. 4. Ensure that all options are toggled to 'Enabled'. API server: Enabled Audit: Enabled Authenticator: Enabled Controller manager: Enabled Scheduler: Enabled 5. Click 'Save Changes'. From CLI: # For each EKS Cluster in each region; aws eks update-cluster-config \ --region '${REGION_CODE}' \ --name '${CLUSTER_NAME}' \ --logging '{"clusterLogging":[{"types":["api","audit","authenticator","controllerManager","scheduler"],"enabled":true}]}' scored: false ================================================ FILE: cfg/eks-1.5.0/managedservices.yaml ================================================ --- controls: version: "eks-1.5.0" id: 5 text: "Managed Services" type: "managedservices" groups: - id: 5.1 text: "Image Registry and Image Scanning" checks: - id: 5.1.1 text: "Ensure Image Vulnerability Scanning using Amazon ECR image scanning or a third party provider (Automated)" type: "manual" remediation: | To utilize AWS ECR for Image scanning please follow the steps below: To create a repository configured for scan on push (AWS CLI): aws ecr create-repository --repository-name $REPO_NAME --image-scanning-configuration scanOnPush=true --region $REGION_CODE To edit the settings of an existing repository (AWS CLI): aws ecr put-image-scanning-configuration --repository-name $REPO_NAME --image-scanning-configuration scanOnPush=true --region $REGION_CODE Use the following steps to start a manual image scan using the AWS Management Console. 1. Open the Amazon ECR console at https://console.aws.amazon.com/ecr/repositories. 2. From the navigation bar, choose the Region to create your repository in. 3. In the navigation pane, choose Repositories. 4. On the Repositories page, choose the repository that contains the image to scan. 5. On the Images page, select the image to scan and then choose Scan. scored: false - id: 5.1.2 text: "Minimize user access to Amazon ECR (Manual)" type: "manual" remediation: | Before you use IAM to manage access to Amazon ECR, you should understand what IAM features are available to use with Amazon ECR. To get a high-level view of how Amazon ECR and other AWS services work with IAM, see AWS Services That Work with IAM in the IAM User Guide. scored: false - id: 5.1.3 text: "Minimize cluster access to read-only for Amazon ECR (Manual)" type: "manual" remediation: | You can use your Amazon ECR images with Amazon EKS, but you need to satisfy the following prerequisites. The Amazon EKS worker node IAM role (NodeInstanceRole) that you use with your worker nodes must possess the following IAM policy permissions for Amazon ECR. { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "ecr:BatchCheckLayerAvailability", "ecr:BatchGetImage", "ecr:GetDownloadUrlForLayer", "ecr:GetAuthorizationToken" ], "Resource": "*" } ] } scored: false - id: 5.1.4 text: "Minimize Container Registries to only those approved (Manual)" type: "manual" remediation: | To minimize AWS ECR container registries to only those approved, you can follow these steps: 1. Define your approval criteria: Determine the criteria that containers must meet to be considered approved. This can include factors such as security, compliance, compatibility, and other requirements. 2. Identify all existing ECR registries: Identify all ECR registries that are currently being used in your organization. 3. Evaluate ECR registries against approval criteria: Evaluate each ECR registry against your approval criteria to determine whether it should be approved or not. This can be done by reviewing the registry settings and configuration, as well as conducting security assessments and vulnerability scans. 4. Establish policies and procedures: Establish policies and procedures that outline how ECR registries will be approved, maintained, and monitored. This should include guidelines for developers to follow when selecting a registry for their container images. 5. Implement access controls: Implement access controls to ensure that only approved ECR registries are used to store and distribute container images. This can be done by setting up IAM policies and roles that restrict access to unapproved registries or create a whitelist of approved registries. 6. Monitor and review: Continuously monitor and review the use of ECR registries to ensure that they continue to meet your approval criteria. This can include scored: false - id: 5.2 text: "Identity and Access Management (IAM)" checks: - id: 5.2.1 text: "Prefer using dedicated Amazon EKS Service Accounts (Automated)" type: "manual" remediation: | With IAM roles for service accounts on Amazon EKS clusters, you can associate an IAM role with a Kubernetes service account. This service account can then provide AWS permissions to the containers in any pod that uses that service account. With this feature, you no longer need to provide extended permissions to the worker node IAM role so that pods on that node can call AWS APIs. Applications must sign their AWS API requests with AWS credentials. This feature provides a strategy for managing credentials for your applications, similar to the way that Amazon EC2 instance profiles provide credentials to Amazon EC2 instances. Instead of creating and distributing your AWS credentials to the containers or using the Amazon EC2 instance’s role, you can associate an IAM role with a Kubernetes service account. The applications in the pod’s containers can then use an AWS SDK or the AWS CLI to make API requests to authorized AWS services. The IAM roles for service accounts feature provides the following benefits: - Least privilege - By using the IAM roles for service accounts feature, you no longer need to provide extended permissions to the worker node IAM role so that pods on that node can call AWS APIs. You can scope IAM permissions to a service account, and only pods that use that service account have access to those permissions. This feature also eliminates the need for third-party solutions such as kiam or kube2iam. - Credential isolation - A container can only retrieve credentials for the IAM role that is associated with the service account to which it belongs. A container never has access to credentials that are intended for another container that belongs to another pod. - Audit-ability - Access and event logging is available through CloudTrail to help ensure retrospective auditing. scored: false - id: 5.3 text: "AWS EKS Key Management Service" checks: - id: 5.3.1 text: "Ensure Kubernetes Secrets are encrypted using Customer Master Keys (CMKs) managed in AWS KMS (Manual)" type: "manual" remediation: | This process can only be performed during Cluster Creation. Enable 'Secrets Encryption' during Amazon EKS cluster creation as described in the links within the 'References' section. scored: false - id: 5.4 text: "Cluster Networking" checks: - id: 5.4.1 text: "Restrict Access to the Control Plane Endpoint (Automated)" type: "manual" remediation: | By enabling private endpoint access to the Kubernetes API server, all communication between your nodes and the API server stays within your VPC. You can also limit the IP addresses that can access your API server from the internet, or completely disable internet access to the API server. With this in mind, you can update your cluster accordingly using the AWS CLI to ensure that Private Endpoint Access is enabled. If you choose to also enable Public Endpoint Access then you should also configure a list of allowable CIDR blocks, resulting in restricted access from the internet. If you specify no CIDR blocks, then the public API server endpoint is able to receive and process requests from all IP addresses by defaulting to ['0.0.0.0/0']. For example, the following command would enable private access to the Kubernetes API as well as limited public access over the internet from a single IP address (noting the /32 CIDR suffix): aws eks update-cluster-config --region $AWS_REGION --name $CLUSTER_NAME --resources-vpc-config endpointPrivateAccess=true,endpointPrivateAccess=true,publicAccessCidrs="203.0.113.5/32" Note: The CIDR blocks specified cannot include reserved addresses. There is a maximum number of CIDR blocks that you can specify. For more information, see the EKS Service Quotas link in the references section. For more detailed information, see the EKS Cluster Endpoint documentation link in the references section. scored: false - id: 5.4.2 text: "Ensure clusters are created with Private Endpoint Enabled and Public Access Disabled (Automated)" type: "manual" remediation: | By enabling private endpoint access to the Kubernetes API server, all communication between your nodes and the API server stays within your VPC. With this in mind, you can update your cluster accordingly using the AWS CLI to ensure that Private Endpoint Access is enabled. For example, the following command would enable private access to the Kubernetes API and ensure that no public access is permitted: aws eks update-cluster-config --region $AWS_REGION --name $CLUSTER_NAME --resources-vpc-config endpointPrivateAccess=true,endpointPublicAccess=false Note: For more detailed information, see the EKS Cluster Endpoint documentation link in the references section. scored: false - id: 5.4.3 text: "Ensure clusters are created with Private Nodes (Automated)" type: "manual" remediation: | aws eks update-cluster-config \ --region region-code \ --name my-cluster \ --resources-vpc-config endpointPublicAccess=true,publicAccessCidrs="203.0.113.5/32",endpointPrivateAccess=true scored: false - id: 5.4.4 text: "Ensure Network Policy is Enabled and set as appropriate (Automated)" type: "manual" remediation: | Utilize Calico or other network policy engine to segment and isolate your traffic. scored: false - id: 5.4.5 text: "Encrypt traffic to HTTPS load balancers with TLS certificates (Manual)" type: "manual" remediation: | Your load balancer vendor can provide details on configuring HTTPS with TLS. scored: false - id: 5.5 text: "Authentication and Authorization" checks: - id: 5.5.1 text: "Manage Kubernetes RBAC users with AWS IAM Authenticator for Kubernetes or Upgrade to AWS CLI v1.16.156 or greater (Manual)" type: "manual" remediation: | Refer to the 'Managing users or IAM roles for your cluster' in Amazon EKS documentation. Note: If using AWS CLI version 1.16.156 or later there is no need to install the AWS IAM Authenticator anymore. The relevant AWS CLI commands, depending on the use case, are: aws eks update-kubeconfig aws eks get-token scored: false ================================================ FILE: cfg/eks-1.5.0/master.yaml ================================================ --- controls: version: "eks-1.5.0" id: 1 text: "Control Plane Components" type: "master" ================================================ FILE: cfg/eks-1.5.0/node.yaml ================================================ --- controls: version: "eks-1.5.0" id: 3 text: "Worker Node Security Configuration" type: "node" groups: - id: 3.1 text: "Worker Node Configuration Files" checks: - id: 3.1.1 text: "Ensure that the kubeconfig file permissions are set to 644 or more restrictive (Automated)" audit: '/bin/sh -c ''if test -e $kubeletkubeconfig; then stat -c permissions=%a $kubeletkubeconfig; fi'' ' tests: test_items: - flag: "permissions" compare: op: bitmask value: "644" remediation: | Run the below command (based on the file location on your system) on the each worker node. For example, chmod 644 $kubeletkubeconfig scored: true - id: 3.1.2 text: "Ensure that the kubelet kubeconfig file ownership is set to root:root (Automated)" audit: '/bin/sh -c ''if test -e $kubeletkubeconfig; then stat -c %U:%G $kubeletkubeconfig; fi'' ' tests: test_items: - flag: root:root remediation: | Run the below command (based on the file location on your system) on the each worker node. For example, chown root:root $kubeletkubeconfig scored: true - id: 3.1.3 text: "Ensure that the kubelet configuration file has permissions set to 644 or more restrictive (Automated)" audit: '/bin/sh -c ''if test -e $kubeletconf; then stat -c permissions=%a $kubeletconf; fi'' ' tests: test_items: - flag: "permissions" compare: op: bitmask value: "644" remediation: | Run the following command (using the config file location identified in the Audit step) chmod 644 $kubeletconf scored: true - id: 3.1.4 text: "Ensure that the kubelet configuration file ownership is set to root:root (Automated)" audit: '/bin/sh -c ''if test -e $kubeletconf; then stat -c %U:%G $kubeletconf; fi'' ' tests: test_items: - flag: root:root remediation: | Run the following command (using the config file location identified in the Audit step) chown root:root $kubeletconf scored: true - id: 3.2 text: "Kubelet" checks: - id: 3.2.1 text: "Ensure that the Anonymous Auth is Not Enabled (Automated)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: "--anonymous-auth" path: '{.authentication.anonymous.enabled}' set: true compare: op: eq value: false remediation: | Remediation Method 1: If configuring via the Kubelet config file, you first need to locate the file. To do this, SSH to each node and execute the following command to find the kubelet process: ps -ef | grep kubelet The output of the above command provides details of the active kubelet process, from which we can see the location of the configuration file provided to the kubelet service with the --config argument. The file can be viewed with a command such as more or less, like so: sudo less /path/to/kubelet-config.json Disable Anonymous Authentication by setting the following parameter: "authentication": { "anonymous": { "enabled": false } } Remediation Method 2. If using executable arguments, edit the kubelet service file on each worker node and ensure the below parameters are part of the KUBELET_ARGS variable string. For systems using systemd, such as the Amazon EKS Optimised Amazon Linux or Bottlerocket AMIs, then this file can be found at /etc/systemd/system/kubelet.service.d/10-kubelet-args.conf. Otherwise, you may need to look up documentation for your chosen operating system to determine which service manager is configured: --anonymous-auth=false For Both Remediation Steps: Based on your system, restart the kubelet service and check the service status. The following example is for operating systems using systemd, such as the Amazon EKS Optimised Amazon Linux or Bottlerocket AMIs, and invokes the systemctl command. If systemctl is not available then you will need to look up documentation for your chosen operating system to determine which service manager is configured: systemctl daemon-reload systemctl restart kubelet.service systemctl status kubelet -l scored: true - id: 3.2.2 text: "Ensure that the --authorization-mode argument is not set to AlwaysAllow (Automated)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --authorization-mode path: '{.authorization.mode}' set: true compare: op: nothave value: AlwaysAllow remediation: | Remediation Method 1: If configuring via the Kubelet config file, you first need to locate the file. To do this, SSH to each node and execute the following command to find the kubelet process: ps -ef | grep kubelet The output of the above command provides details of the active kubelet process, from which we can see the location of the configuration file provided to the kubelet service with the --config argument. The file can be viewed with a command such as more or less, like so: sudo less /path/to/kubelet-config.json Enable Webhook Authentication by setting the following parameter: "authentication": { "webhook": { "enabled": true } } Next, set the Authorization Mode to Webhook by setting the following parameter: "authorization": { "mode": "Webhook } Finer detail of the authentication and authorization fields can be found in the Kubelet Configuration documentation. Remediation Method 2: If using executable arguments, edit the kubelet service file on each worker node and ensure the below parameters are part of the KUBELET_ARGS variable string. For systems using systemd, such as the Amazon EKS Optimised Amazon Linux or Bottlerocket AMIs, then this file can be found at /etc/systemd/system/kubelet.service.d/10-kubelet-args.conf. Otherwise, you may need to look up documentation for your chosen operating system to determine which service manager is configured: --authentication-token-webhook --authorization-mode=Webhook For Both Remediation Steps: Based on your system, restart the kubelet service and check the service status. The following example is for operating systems using systemd, such as the Amazon EKS Optimised Amazon Linux or Bottlerocket AMIs, and invokes the systemctl command. If systemctl is not available then you will need to look up documentation for your chosen operating system to determine which service manager is configured: systemctl daemon-reload systemctl restart kubelet.service systemctl status kubelet -l scored: true - id: 3.2.3 text: "Ensure that a Client CA File is Configured (Automated)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --client-ca-file path: '{.authentication.x509.clientCAFile}' set: true remediation: | Remediation Method 1: If configuring via the Kubelet config file, you first need to locate the file. To do this, SSH to each node and execute the following command to find the kubelet process: ps -ef | grep kubelet The output of the above command provides details of the active kubelet process, from which we can see the location of the configuration file provided to the kubelet service with the --config argument. The file can be viewed with a command such as more or less, like so: sudo less /path/to/kubelet-config.json Configure the client certificate authority file by setting the following parameter appropriately: "authentication": { "x509": {"clientCAFile": } }" Remediation Method 2: If using executable arguments, edit the kubelet service file on each worker node and ensure the below parameters are part of the KUBELET_ARGS variable string. For systems using systemd, such as the Amazon EKS Optimised Amazon Linux or Bottlerocket AMIs, then this file can be found at /etc/systemd/system/kubelet.service.d/10-kubelet-args.conf. Otherwise, you may need to look up documentation for your chosen operating system to determine which service manager is configured: --client-ca-file= For Both Remediation Steps: Based on your system, restart the kubelet service and check the service status. The following example is for operating systems using systemd, such as the Amazon EKS Optimised Amazon Linux or Bottlerocket AMIs, and invokes the systemctl command. If systemctl is not available then you will need to look up documentation for your chosen operating system to determine which service manager is configured: systemctl daemon-reload systemctl restart kubelet.service systemctl status kubelet -l scored: true - id: 3.2.4 text: "Ensure that the --read-only-port is disabled (Automated)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: "--read-only-port" path: '{.readOnlyPort}' set: true compare: op: eq value: 0 remediation: | If modifying the Kubelet config file, edit the kubelet-config.json file /etc/kubernetes/kubelet/kubelet-config.json and set the below parameter to 0 "readOnlyPort": 0 If using executable arguments, edit the kubelet service file /etc/systemd/system/kubelet.service.d/10-kubelet-args.conf on each worker node and add the below parameter at the end of the KUBELET_ARGS variable string. --read-only-port=0 Based on your system, restart the kubelet service and check status systemctl daemon-reload systemctl restart kubelet.service systemctl status kubelet -l scored: true - id: 3.2.5 text: "Ensure that the --streaming-connection-idle-timeout argument is not set to 0 (Automated)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --streaming-connection-idle-timeout path: '{.streamingConnectionIdleTimeout}' set: true compare: op: noteq value: 0 - flag: --streaming-connection-idle-timeout path: '{.streamingConnectionIdleTimeout}' set: false bin_op: or remediation: | Remediation Method 1: If modifying the Kubelet config file, edit the kubelet-config.json file /etc/kubernetes/kubelet/kubelet-config.json and set the below parameter to a non-zero value in the format of #h#m#s "streamingConnectionIdleTimeout": "4h0m0s" You should ensure that the kubelet service file /etc/systemd/system/kubelet.service.d/10-kubelet-args.conf does not specify a --streaming-connection-idle-timeout argument because it would override the Kubelet config file. Remediation Method 2: If using executable arguments, edit the kubelet service file /etc/systemd/system/kubelet.service.d/10-kubelet-args.conf on each worker node and add the below parameter at the end of the KUBELET_ARGS variable string. --streaming-connection-idle-timeout=4h0m0s Remediation Method 3: If using the api configz endpoint consider searching for the status of "streamingConnectionIdleTimeout": by extracting the live configuration from the nodes running kubelet. **See detailed step-by-step configmap procedures in Reconfigure a Node's Kubelet in a Live Cluster, and then rerun the curl statement from audit process to check for kubelet configuration changes kubectl proxy --port=8001 & export HOSTNAME_PORT=localhost:8001 (example host and port number) export NODE_NAME=ip-192.168.31.226.ec2.internal (example node name from "kubectl get nodes") curl -sSL "http://${HOSTNAME_PORT}/api/v1/nodes/${NODE_NAME}/proxy/configz" For all three remediations: Based on your system, restart the kubelet service and check status systemctl daemon-reload systemctl restart kubelet.service systemctl status kubelet -l scored: true - id: 3.2.6 text: "Ensure that the --make-iptables-util-chains argument is set to true (Automated)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --make-iptables-util-chains path: '{.makeIPTablesUtilChains}' set: true compare: op: eq value: true - flag: --make-iptables-util-chains path: '{.makeIPTablesUtilChains}' set: false bin_op: or remediation: | Remediation Method 1: If modifying the Kubelet config file, edit the kubelet-config.json file /etc/kubernetes/kubelet/kubelet-config.json and set the below parameter to true "makeIPTablesUtilChains": true Ensure that /etc/systemd/system/kubelet.service.d/10-kubelet-args.conf does not set the --make-iptables-util-chains argument because that would override your Kubelet config file. Remediation Method 2: If using executable arguments, edit the kubelet service file /etc/systemd/system/kubelet.service.d/10-kubelet-args.conf on each worker node and add the below parameter at the end of the KUBELET_ARGS variable string. --make-iptables-util-chains:true Remediation Method 3: If using the api configz endpoint consider searching for the status of "makeIPTablesUtilChains.: true by extracting the live configuration from the nodes running kubelet. **See detailed step-by-step configmap procedures in Reconfigure a Node's Kubelet in a Live Cluster, and then rerun the curl statement from audit process to check for kubelet configuration changes kubectl proxy --port=8001 & export HOSTNAME_PORT=localhost:8001 (example host and port number) export NODE_NAME=ip-192.168.31.226.ec2.internal (example node name from "kubectl get nodes") curl -sSL "http://${HOSTNAME_PORT}/api/v1/nodes/${NODE_NAME}/proxy/configz" For all three remediations: Based on your system, restart the kubelet service and check status systemctl daemon-reload systemctl restart kubelet.service systemctl status kubelet -l scored: true - id: 3.2.7 text: "Ensure that the --eventRecordQPS argument is set to 0 or a level which ensures appropriate event capture (Automated)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --event-qps path: '{.eventRecordQPS}' set: true compare: op: gte value: 0 remediation: | If using a Kubelet config file, edit the file to set eventRecordQPS: to an appropriate level. If using command line arguments, edit the kubelet service file /etc/systemd/system/kubelet.service.d/10-kubeadm.conf on each worker node and set the below parameter in KUBELET_SYSTEM_PODS_ARGS variable. Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: true - id: 3.2.8 text: "Ensure that the --rotate-certificates argument is not present or is set to true (Automated)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --rotate-certificates path: '{.rotateCertificates}' set: true compare: op: eq value: true - flag: --rotate-certificates path: '{.rotateCertificates}' set: false bin_op: or remediation: | Remediation Method 1: If modifying the Kubelet config file, edit the kubelet-config.json file /etc/kubernetes/kubelet/kubelet-config.json and set the below parameter to true "RotateCertificate":true Additionally, ensure that the kubelet service file /etc/systemd/system/kubelet.service.d/10-kubelet-args.conf does not set the --RotateCertificate executable argument to false because this would override the Kubelet config file. Remediation Method 2: If using executable arguments, edit the kubelet service file /etc/systemd/system/kubelet.service.d/10-kubelet-args.conf on each worker node and add the below parameter at the end of the KUBELET_ARGS variable string. --RotateCertificate=true scored: true - id: 3.2.9 text: "Ensure that the RotateKubeletServerCertificate argument is set to true (Automated)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: RotateKubeletServerCertificate path: '{.featureGates.RotateKubeletServerCertificate}' set: true compare: op: eq value: true remediation: | Remediation Method 1: If modifying the Kubelet config file, edit the kubelet-config.json file /etc/kubernetes/kubelet/kubelet-config.json and set the below parameter to true "featureGates": { "RotateKubeletServerCertificate":true }, Additionally, ensure that the kubelet service file /etc/systemd/system/kubelet.service.d/10-kubelet-args.conf does not set the --rotate-kubelet-server-certificate executable argument to false because this would override the Kubelet config file. Remediation Method 2: If using executable arguments, edit the kubelet service file /etc/systemd/system/kubelet.service.d/10-kubelet-args.conf on each worker node and add the below parameter at the end of the KUBELET_ARGS variable string. --rotate-kubelet-server-certificate=true Remediation Method 3: If using the api configz endpoint consider searching for the status of "RotateKubeletServerCertificate": by extracting the live configuration from the nodes running kubelet. **See detailed step-by-step configmap procedures in Reconfigure a Node's Kubelet in a Live Cluster, and then rerun the curl statement from audit process to check for kubelet configuration changes kubectl proxy --port=8001 & export HOSTNAME_PORT=localhost:8001 (example host and port number) export NODE_NAME=ip-192.168.31.226.ec2.internal (example node name from "kubectl get nodes") curl -sSL "http://${HOSTNAME_PORT}/api/v1/nodes/${NODE_NAME}/proxy/configz" For all three remediation methods: Restart the kubelet service and check status. The example below is for when using systemctl to manage services: systemctl daemon-reload systemctl restart kubelet.service systemctl status kubelet -l scored: true ================================================ FILE: cfg/eks-1.5.0/policies.yaml ================================================ --- controls: version: "eks-1.5.0" id: 4 text: "Policies" type: "policies" groups: - id: 4.1 text: "RBAC and Service Accounts" checks: - id: 4.1.1 text: "Ensure that the cluster-admin role is only used where required (Automated)" type: "manual" remediation: | Identify all clusterrolebindings to the cluster-admin role. Check if they are used and if they need this role or if they could use a role with fewer privileges. Where possible, first bind users to a lower privileged role and then remove the clusterrolebinding to the cluster-admin role : kubectl delete clusterrolebinding [name] scored: false - id: 4.1.2 text: "Minimize access to secrets (Automated)" type: "manual" remediation: | Where possible, remove get, list and watch access to secret objects in the cluster. scored: false - id: 4.1.3 text: "Minimize wildcard use in Roles and ClusterRoles (Automated)" type: "manual" remediation: | Where possible replace any use of wildcards in clusterroles and roles with specific objects or actions. scored: false - id: 4.1.4 text: "Minimize access to create pods (Automated)" type: "manual" remediation: | Where possible, remove create access to pod objects in the cluster. scored: false - id: 4.1.5 text: "Ensure that default service accounts are not actively used. ((Automated)" type: "manual" remediation: | Create explicit service accounts wherever a Kubernetes workload requires specific access to the Kubernetes API server. Modify the configuration of each default service account to include this value automountServiceAccountToken: false Automatic remediation for the default account: kubectl patch serviceaccount default -p $'automountServiceAccountToken: false' scored: false - id: 4.1.6 text: "Ensure that Service Account Tokens are only mounted where necessary (Automated)" type: "manual" remediation: | Modify the definition of pods and service accounts which do not need to mount service account tokens to disable it. scored: false - id: 4.1.7 text: "Avoid use of system:masters group (Automated)" type: "manual" remediation: | Remove the system:masters group from all users in the cluster. scored: false - id: 4.1.8 text: "Limit use of the Bind, Impersonate and Escalate permissions in the Kubernetes cluster (Manual)" type: "manual" remediation: | Where possible, remove the impersonate, bind and escalate rights from subjects. scored: false - id: 4.2 text: "Pod Security Standards" checks: - id: 4.2.1 text: "Minimize the admission of privileged containers (Automated)" type: "manual" remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of privileged containers. To enable PSA for a namespace in your cluster, set the pod-security.kubernetes.io/enforce label with the policy value you want to enforce. kubectl label --overwrite ns NAMESPACE pod-security.kubernetes.io/enforce=restricted The above command enforces the restricted policy for the NAMESPACE namespace. You can also enable Pod Security Admission for all your namespaces. For example: kubectl label --overwrite ns --all pod-security.kubernetes.io/warn=baseline scored: false - id: 4.2.2 text: "Minimize the admission of containers wishing to share the host process ID namespace (Automated)" type: "manual" remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of hostPID containers. scored: false - id: 4.2.3 text: "Minimize the admission of containers wishing to share the host IPC namespace (Automated)" type: "manual" remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of hostIPC containers. scored: false - id: 4.2.4 text: "Minimize the admission of containers wishing to share the host network namespace (Automated)" type: "manual" remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of hostNetwork containers. scored: false - id: 4.2.5 text: "Minimize the admission of containers with allowPrivilegeEscalation (Automated)" type: "manual" remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of containers with .spec.allowPrivilegeEscalation set to true. scored: false - id: 4.3 text: "CNI Plugin" checks: - id: 4.3.1 text: "Ensure CNI plugin supports network policies (Manual)" type: "manual" remediation: | As with RBAC policies, network policies should adhere to the policy of least privileged access. Start by creating a deny all policy that restricts all inbound and outbound traffic from a namespace or create a global policy using Calico. scored: false - id: 4.3.2 text: "Ensure that all Namespaces have Network Policies defined (Automated)" type: "manual" remediation: | Follow the documentation and create NetworkPolicy objects as you need them. scored: false - id: 4.4 text: "Secrets Management" checks: - id: 4.4.1 text: "Prefer using secrets as files over secrets as environment variables (Automated)" type: "manual" remediation: | If possible, rewrite application code to read secrets from mounted secret files, rather than from environment variables. scored: false - id: 4.4.2 text: "Consider external secret storage (Manual)" type: "manual" remediation: | Refer to the secrets management options offered by your cloud provider or a third-party secrets management solution. scored: false - id: 4.5 text: "General Policies" checks: - id: 4.5.1 text: "Create administrative boundaries between resources using namespaces (Manual)" type: "manual" remediation: | Follow the documentation and create namespaces for objects in your deployment as you need them. scored: false - id: 4.5.2 text: "Apply Security Context to Your Pods and Containers (Manual)" type: "manual" remediation: | As a best practice we recommend that you scope the binding for privileged pods to service accounts within a particular namespace, e.g. kube-system, and limiting access to that namespace. For all other serviceaccounts/namespaces, we recommend implementing a more restrictive policy such as this: apiVersion: policy/v1beta1 kind: PodSecurityPolicy metadata: name: restricted annotations: seccomp.security.alpha.kubernetes.io/allowedProfileNames: 'docker/default,runtime/default' apparmor.security.beta.kubernetes.io/allowedProfileNames: 'runtime/default' seccomp.security.alpha.kubernetes.io/defaultProfileName: 'runtime/default' apparmor.security.beta.kubernetes.io/defaultProfileName: 'runtime/default' spec: privileged: false # Required to prevent escalations to root. allowPrivilegeEscalation: false # This is redundant with non-root + disallow privilege escalation, # but we can provide it for defense in depth. requiredDropCapabilities: - ALL # Allow core volume types. volumes: - 'configMap' - 'emptyDir' - 'projected' - 'secret' - 'downwardAPI' # Assume that persistentVolumes set up by the cluster admin are safe to use. - 'persistentVolumeClaim' hostNetwork: false hostIPC: false hostPID: false runAsUser: # Require the container to run without root privileges. rule: 'MustRunAsNonRoot' seLinux: # This policy assumes the nodes are using AppArmor rather than SELinux. rule: 'RunAsAny' supplementalGroups: rule: 'MustRunAs' ranges: # Forbid adding the root group. - min: 1 max: 65535 fsGroup: rule: 'MustRunAs' ranges: # Forbid adding the root group. - min: 1 max: 65535 readOnlyRootFilesystem: false This policy prevents pods from running as privileged or escalating privileges. It also restricts the types of volumes that can be mounted and the root supplemental groups that can be added. Another, albeit similar, approach is to start with policy that locks everything down and incrementally add exceptions for applications that need looser restrictions such as logging agents which need the ability to mount a host path. scored: false - id: 4.5.3 text: "The default namespace should not be used (Automated)" type: "manual" remediation: | Ensure that namespaces are created to allow for appropriate segregation of Kubernetes resources and that all new resources are created in a specific namespace. scored: false ================================================ FILE: cfg/eks-1.7.0/config.yaml ================================================ --- ## Version-specific settings that override the values in cfg/config.yaml ## These settings are required if you are using the --asff option to report findings to AWS Security Hub ## AWS account number is required. AWS_ACCOUNT: "" ## AWS region is required. AWS_REGION: "" ## EKS Cluster ARN is required. CLUSTER_ARN: "" ================================================ FILE: cfg/eks-1.7.0/controlplane.yaml ================================================ --- controls: version: "eks-1.7.0" id: 2 text: "Control Plane Configuration" type: "controlplane" groups: - id: 2.1 text: "Logging" checks: - id: 2.1.1 text: "Enable audit Logs (Manual)" type: manual remediation: | From Console: 1. For each EKS Cluster in each region; 2. Go to 'Amazon EKS' > 'Clusters' > '' > 'Configuration' > 'Logging'. 3. Click 'Manage logging'. 4. Ensure that all options are toggled to 'Enabled'. API server: Enabled Audit: Enabled Authenticator: Enabled Controller manager: Enabled Scheduler: Enabled 5. Click 'Save Changes'. From CLI: # For each EKS Cluster in each region; aws eks update-cluster-config \ --region '${REGION_CODE}' \ --name '${CLUSTER_NAME}' \ --logging '{"clusterLogging":[{"types":["api","audit","authenticator","controllerManager","scheduler"],"enabled":true}]}' scored: false - id: 2.1.2 text: "Ensure audit logs are collected and managed (Manual)" type: manual remediation: | Create or update the audit-policy.yaml to specify the audit logging configuration: apiVersion: audit.k8s.io/v1 kind: Policy rules: - level: Metadata resources: - group: "" resources: ["pods"] Apply the audit policy configuration to the cluster: kubectl apply -f .yaml Ensure audit logs are forwarded to a centralized logging system like CloudWatch, Elasticsearch, or another log management solution: kubectl create configmap cluster-audit-policy --from-file=audit-policy.yaml -n kube-system kubectl apply -f - < } }" Remediation Method 2: If using executable arguments, edit the kubelet service file on each worker node and ensure the below parameters are part of the KUBELET_ARGS variable string. For systems using systemd, such as the Amazon EKS Optimised Amazon Linux or Bottlerocket AMIs, then this file can be found at /etc/systemd/system/kubelet.service.d/10-kubelet-args.conf. Otherwise, you may need to look up documentation for your chosen operating system to determine which service manager is configured: --client-ca-file= For Both Remediation Steps: Based on your system, restart the kubelet service and check the service status. The following example is for operating systems using systemd, such as the Amazon EKS Optimised Amazon Linux or Bottlerocket AMIs, and invokes the systemctl command. If systemctl is not available then you will need to look up documentation for your chosen operating system to determine which service manager is configured: systemctl daemon-reload systemctl restart kubelet.service systemctl status kubelet -l scored: true - id: 3.2.4 text: "Ensure that the --read-only-port is disabled (Automated)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: "--read-only-port" path: '{.readOnlyPort}' set: true compare: op: eq value: 0 remediation: | If modifying the Kubelet config file, edit the kubelet-config.json file /etc/kubernetes/kubelet/kubelet-config.json and set the below parameter to 0 "readOnlyPort": 0 If using executable arguments, edit the kubelet service file /etc/systemd/system/kubelet.service.d/10-kubelet-args.conf on each worker node and add the below parameter at the end of the KUBELET_ARGS variable string. --read-only-port=0 Based on your system, restart the kubelet service and check status systemctl daemon-reload systemctl restart kubelet.service systemctl status kubelet -l scored: true - id: 3.2.5 text: "Ensure that the --streaming-connection-idle-timeout argument is not set to 0 (Automated)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --streaming-connection-idle-timeout path: '{.streamingConnectionIdleTimeout}' set: true compare: op: noteq value: 0 - flag: --streaming-connection-idle-timeout path: '{.streamingConnectionIdleTimeout}' set: false bin_op: or remediation: | Remediation Method 1: If modifying the Kubelet config file, edit the kubelet-config.json file /etc/kubernetes/kubelet/kubelet-config.json and set the below parameter to a non-zero value in the format of #h#m#s "streamingConnectionIdleTimeout": "4h0m0s" You should ensure that the kubelet service file /etc/systemd/system/kubelet.service.d/10-kubelet-args.conf does not specify a --streaming-connection-idle-timeout argument because it would override the Kubelet config file. Remediation Method 2: If using executable arguments, edit the kubelet service file /etc/systemd/system/kubelet.service.d/10-kubelet-args.conf on each worker node and add the below parameter at the end of the KUBELET_ARGS variable string. --streaming-connection-idle-timeout=4h0m0s Remediation Method 3: If using the api configz endpoint consider searching for the status of "streamingConnectionIdleTimeout": by extracting the live configuration from the nodes running kubelet. **See detailed step-by-step configmap procedures in Reconfigure a Node's Kubelet in a Live Cluster, and then rerun the curl statement from audit process to check for kubelet configuration changes kubectl proxy --port=8001 & export HOSTNAME_PORT=localhost:8001 (example host and port number) export NODE_NAME=ip-192.168.31.226.ec2.internal (example node name from "kubectl get nodes") curl -sSL "http://${HOSTNAME_PORT}/api/v1/nodes/${NODE_NAME}/proxy/configz" For all three remediations: Based on your system, restart the kubelet service and check status systemctl daemon-reload systemctl restart kubelet.service systemctl status kubelet -l scored: true - id: 3.2.6 text: "Ensure that the --make-iptables-util-chains argument is set to true (Automated)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --make-iptables-util-chains path: '{.makeIPTablesUtilChains}' set: true compare: op: eq value: true - flag: --make-iptables-util-chains path: '{.makeIPTablesUtilChains}' set: false bin_op: or remediation: | Remediation Method 1: If modifying the Kubelet config file, edit the kubelet-config.json file /etc/kubernetes/kubelet/kubelet-config.json and set the below parameter to true "makeIPTablesUtilChains": true Ensure that /etc/systemd/system/kubelet.service.d/10-kubelet-args.conf does not set the --make-iptables-util-chains argument because that would override your Kubelet config file. Remediation Method 2: If using executable arguments, edit the kubelet service file /etc/systemd/system/kubelet.service.d/10-kubelet-args.conf on each worker node and add the below parameter at the end of the KUBELET_ARGS variable string. --make-iptables-util-chains:true Remediation Method 3: If using the api configz endpoint consider searching for the status of "makeIPTablesUtilChains.: true by extracting the live configuration from the nodes running kubelet. **See detailed step-by-step configmap procedures in Reconfigure a Node's Kubelet in a Live Cluster, and then rerun the curl statement from audit process to check for kubelet configuration changes kubectl proxy --port=8001 & export HOSTNAME_PORT=localhost:8001 (example host and port number) export NODE_NAME=ip-192.168.31.226.ec2.internal (example node name from "kubectl get nodes") curl -sSL "http://${HOSTNAME_PORT}/api/v1/nodes/${NODE_NAME}/proxy/configz" For all three remediations: Based on your system, restart the kubelet service and check status systemctl daemon-reload systemctl restart kubelet.service systemctl status kubelet -l scored: true - id: 3.2.7 text: "Ensure that the --eventRecordQPS argument is set to 0 or a level which ensures appropriate event capture (Automated)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --event-qps path: '{.eventRecordQPS}' set: true compare: op: gte value: 0 - flag: --event-qps path: '{.eventRecordQPS}' set: false bin_op: or remediation: | If using a Kubelet config file, edit the file to set eventRecordQPS: to an appropriate level. If using command line arguments, edit the kubelet service file /etc/systemd/system/kubelet.service.d/10-kubeadm.conf on each worker node and set the below parameter in KUBELET_SYSTEM_PODS_ARGS variable. Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: true - id: 3.2.8 text: "Ensure that the --rotate-certificates argument is not present or is set to true (Automated)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --rotate-certificates path: '{.rotateCertificates}' compare: op: eq value: true - flag: --rotate-certificates path: '{.rotateCertificates}' set: false bin_op: or remediation: | Remediation Method 1: If modifying the Kubelet config file, edit the kubelet-config.json file /etc/kubernetes/kubelet/kubelet-config.json and set the below parameter to true "RotateCertificate":true Additionally, ensure that the kubelet service file /etc/systemd/system/kubelet.service.d/10-kubelet-args.conf does not set the --RotateCertificate executable argument to false because this would override the Kubelet config file. Remediation Method 2: If using executable arguments, edit the kubelet service file /etc/systemd/system/kubelet.service.d/10-kubelet-args.conf on each worker node and add the below parameter at the end of the KUBELET_ARGS variable string. --RotateCertificate=true scored: true - id: 3.2.9 text: "Ensure that the RotateKubeletServerCertificate argument is set to true (Automated)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: RotateKubeletServerCertificate path: '{.featureGates.RotateKubeletServerCertificate}' set: true compare: op: eq value: true remediation: | Remediation Method 1: If modifying the Kubelet config file, edit the kubelet-config.json file /etc/kubernetes/kubelet/kubelet-config.json and set the below parameter to true "featureGates": { "RotateKubeletServerCertificate":true }, Additionally, ensure that the kubelet service file /etc/systemd/system/kubelet.service.d/10-kubelet-args.conf does not set the --rotate-kubelet-server-certificate executable argument to false because this would override the Kubelet config file. Remediation Method 2: If using executable arguments, edit the kubelet service file /etc/systemd/system/kubelet.service.d/10-kubelet-args.conf on each worker node and add the below parameter at the end of the KUBELET_ARGS variable string. --rotate-kubelet-server-certificate=true Remediation Method 3: If using the api configz endpoint consider searching for the status of "RotateKubeletServerCertificate": by extracting the live configuration from the nodes running kubelet. **See detailed step-by-step configmap procedures in Reconfigure a Node's Kubelet in a Live Cluster, and then rerun the curl statement from audit process to check for kubelet configuration changes kubectl proxy --port=8001 & export HOSTNAME_PORT=localhost:8001 (example host and port number) export NODE_NAME=ip-192.168.31.226.ec2.internal (example node name from "kubectl get nodes") curl -sSL "http://${HOSTNAME_PORT}/api/v1/nodes/${NODE_NAME}/proxy/configz" For all three remediation methods: Restart the kubelet service and check status. The example below is for when using systemctl to manage services: systemctl daemon-reload systemctl restart kubelet.service systemctl status kubelet -l scored: true ================================================ FILE: cfg/eks-1.7.0/policies.yaml ================================================ --- controls: version: "eks-1.7.0" id: 4 text: "Policies" type: "policies" groups: - id: 4.1 text: "RBAC and Service Accounts" checks: - id: 4.1.1 text: "Ensure that the cluster-admin role is only used where required (Automated)" audit: | kubectl get clusterrolebindings -o json | jq -r ' .items[] | select(.roleRef.name == "cluster-admin") | .subjects[]? | select(.kind != "Group" or (.name != "system:masters" and .name != "system:nodes")) | "FOUND_CLUSTER_ADMIN_BINDING" ' || echo "NO_CLUSTER_ADMIN_BINDINGS" tests: test_items: - flag: "NO_CLUSTER_ADMIN_BINDINGS" set: true compare: op: eq value: "NO_CLUSTER_ADMIN_BINDINGS" remediation: | Identify all clusterrolebindings to the cluster-admin role. Check if they are used and if they need this role or if they could use a role with fewer privileges. Where possible, first bind users to a lower privileged role and then remove the clusterrolebinding to the cluster-admin role : kubectl delete clusterrolebinding [name] scored: true - id: 4.1.2 text: "Minimize access to secrets (Automated)" audit: | count=$(kubectl get roles --all-namespaces -o json | jq ' .items[] | select(.rules[]? | (.resources[]? == "secrets") and ((.verbs[]? == "get") or (.verbs[]? == "list") or (.verbs[]? == "watch")) )' | wc -l) if [ "$count" -gt 0 ]; then echo "SECRETS_ACCESS_FOUND" fi tests: test_items: - flag: "SECRETS_ACCESS_FOUND" set: false remediation: | Where possible, remove get, list and watch access to secret objects in the cluster. scored: true - id: 4.1.3 text: "Minimize wildcard use in Roles and ClusterRoles (Automated)" audit: | wildcards=$(kubectl get roles --all-namespaces -o json | jq ' .items[] | select( .rules[]? | (.verbs[]? == "*" or .resources[]? == "*" or .apiGroups[]? == "*") )' | wc -l) wildcards_clusterroles=$(kubectl get clusterroles -o json | jq ' .items[] | select( .rules[]? | (.verbs[]? == "*" or .resources[]? == "*" or .apiGroups[]? == "*") )' | wc -l) total=$((wildcards + wildcards_clusterroles)) if [ "$total" -gt 0 ]; then echo "wildcards_present" fi tests: test_items: - flag: wildcards_present set: false remediation: | Where possible replace any use of wildcards in clusterroles and roles with specific objects or actions. scored: true - id: 4.1.4 text: "Minimize access to create pods (Automated)" audit: | access=$(kubectl get roles,clusterroles -A -o json | jq ' [.items[] | select( .rules[]? | (.resources[]? == "pods" and .verbs[]? == "create") ) ] | length') if [ "$access" -gt 0 ]; then echo "pods_create_access" fi tests: test_items: - flag: pods_create_access set: false remediation: | Where possible, remove create access to pod objects in the cluster. scored: true - id: 4.1.5 text: "Ensure that default service accounts are not actively used. (Automated)" audit: | default_sa_count=$(kubectl get serviceaccounts --all-namespaces -o json | jq ' [.items[] | select(.metadata.name == "default" and (.automountServiceAccountToken != false))] | length') if [ "$default_sa_count" -gt 0 ]; then echo "default_sa_not_auto_mounted" fi pods_using_default_sa=$(kubectl get pods --all-namespaces -o json | jq ' [.items[] | select(.spec.serviceAccountName == "default")] | length') if [ "$pods_using_default_sa" -gt 0 ]; then echo "default_sa_used_in_pods" fi tests: test_items: - flag: default_sa_not_auto_mounted set: false - flag: default_sa_used_in_pods set: false remediation: | Create explicit service accounts wherever a Kubernetes workload requires specific access to the Kubernetes API server. Modify the configuration of each default service account to include this value automountServiceAccountToken: false Automatic remediation for the default account: kubectl patch serviceaccount default -p $'automountServiceAccountToken: false' scored: true - id: 4.1.6 text: "Ensure that Service Account Tokens are only mounted where necessary (Automated)" audit: | pods_with_token_mount=$(kubectl get pods --all-namespaces -o json | jq ' [.items[] | select(.spec.automountServiceAccountToken != false)] | length') if [ "$pods_with_token_mount" -gt 0 ]; then echo "automountServiceAccountToken" fi tests: test_items: - flag: automountServiceAccountToken set: false remediation: | Regularly review pod and service account objects in the cluster to ensure that the automountServiceAccountToken setting is false for pods and accounts that do not explicitly require API server access. scored: true - id: 4.1.7 text: "Cluster Access Manager API to streamline and enhance the management of access controls within EKS clusters (Manual)" type: "manual" remediation: | Log in to the AWS Management Console. Navigate to Amazon EKS and select your EKS cluster. Go to the Access tab and click on "Manage Access" in the "Access Configuration section". Under Cluster Authentication Mode for Cluster Access settings. Click EKS API to change cluster will source authenticated IAM principals only from EKS access entry APIs. Click ConfigMap to change cluster will source authenticated IAM principals only from the aws-auth ConfigMap. Note: EKS API and ConfigMap must be selected during Cluster creation and cannot be changed once the Cluster is provisioned. scored: false - id: 4.1.8 text: "Limit use of the Bind, Impersonate and Escalate permissions in the Kubernetes cluster (Manual)" type: "manual" remediation: | Where possible, remove the impersonate, bind and escalate rights from subjects. scored: false - id: 4.2 text: "Pod Security Standards" checks: - id: 4.2.1 text: "Minimize the admission of privileged containers (Automated)" audit: | kubectl get pods --all-namespaces -o json | \ jq -r 'if any(.items[]?.spec.containers[]?; .securityContext?.privileged == true) then "PRIVILEGED_FOUND" else "NO_PRIVILEGED" end' tests: test_items: - flag: "NO_PRIVILEGED" set: true compare: op: eq value: "NO_PRIVILEGED" remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of privileged containers. To enable PSA for a namespace in your cluster, set the pod-security.kubernetes.io/enforce label with the policy value you want to enforce. kubectl label --overwrite ns NAMESPACE pod-security.kubernetes.io/enforce=restricted The above command enforces the restricted policy for the NAMESPACE namespace. You can also enable Pod Security Admission for all your namespaces. For example: kubectl label --overwrite ns --all pod-security.kubernetes.io/warn=baseline scored: true - id: 4.2.2 text: "Minimize the admission of containers wishing to share the host process ID namespace (Automated)" audit: | kubectl get pods --all-namespaces -o json | \ jq -r 'if any(.items[]?; .spec.hostPID == true) then "HOSTPID_FOUND" else "NO_HOSTPID" end' tests: test_items: - flag: "NO_HOSTPID" set: true compare: op: eq value: "NO_HOSTPID" remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of hostPID containers. scored: true - id: 4.2.3 text: "Minimize the admission of containers wishing to share the host IPC namespace (Automated)" audit: | kubectl get pods --all-namespaces -o json | jq -r 'if any(.items[]?; .spec.hostIPC == true) then "HOSTIPC_FOUND" else "NO_HOSTIPC" end' tests: test_items: - flag: "NO_HOSTIPC" set: true compare: op: eq value: "NO_HOSTIPC" remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of hostIPC containers. scored: true - id: 4.2.4 text: "Minimize the admission of containers wishing to share the host network namespace (Automated)" audit: | kubectl get pods --all-namespaces -o json | jq -r 'if any(.items[]?; .spec.hostNetwork == true) then "HOSTNETWORK_FOUND" else "NO_HOSTNETWORK" end' tests: test_items: - flag: "NO_HOSTNETWORK" set: true compare: op: eq value: "NO_HOSTNETWORK" remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of hostNetwork containers. scored: true - id: 4.2.5 text: "Minimize the admission of containers with allowPrivilegeEscalation (Automated)" audit: | kubectl get pods --all-namespaces -o json | \ jq -r 'if any(.items[]?.spec.containers[]?; .securityContext?.allowPrivilegeEscalation == true) then "ALLOWPRIVILEGEESCALTION_FOUND" else "NO_ALLOWPRIVILEGEESCALTION" end' tests: test_items: - flag: "NO_ALLOWPRIVILEGEESCALTION" set: true compare: op: eq value: "NO_ALLOWPRIVILEGEESCALTION" remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of containers with .spec.allowPrivilegeEscalation set to true. scored: true - id: 4.3 text: "CNI Plugin" checks: - id: 4.3.1 text: "Ensure CNI plugin supports network policies (Manual)" type: "manual" remediation: | As with RBAC policies, network policies should adhere to the policy of least privileged access. Start by creating a deny all policy that restricts all inbound and outbound traffic from a namespace or create a global policy using Calico. scored: false - id: 4.3.2 text: "Ensure that all Namespaces have Network Policies defined (Automated)" audit: | ns_without_np=$(kubectl get namespaces -o json | jq -r '.items[].metadata.name' | while read ns; do count=$(kubectl get networkpolicy -n $ns --no-headers 2>/dev/null | wc -l) if [ "$count" -eq 0 ]; then echo $ns; fi done) if [ -z "$ns_without_np" ]; then echo "ALL_NAMESPACES_HAVE_NETWORK_POLICIES" else echo "NAMESPACES_WITHOUT_NETWORK_POLICIES: $ns_without_np" fi tests: test_items: - flag: "ALL_NAMESPACES_HAVE_NETWORK_POLICIES" set: true compare: op: eq value: "ALL_NAMESPACES_HAVE_NETWORK_POLICIES" remediation: | Create at least one NetworkPolicy in each namespace to control and restrict traffic between pods as needed. scored: true - id: 4.4 text: "Secrets Management" checks: - id: 4.4.1 text: "Prefer using secrets as files over secrets as environment variables (Automated)" audit: | result=$(kubectl get all --all-namespaces -o jsonpath='{range .items[?(@..secretKeyRef)]}{.metadata.namespace} {.kind} {.metadata.name}{"\n"}{end}') if [ -z "$result" ]; then echo "NO_SECRETS_AS_ENV_VARS" else echo "SECRETS_AS_ENV_VARS_FOUND: $result" fi tests: test_items: - flag: "NO_SECRETS_AS_ENV_VARS" set: true compare: op: eq value: "NO_SECRETS_AS_ENV_VARS" remediation: | If possible, rewrite application code to read secrets from mounted secret files, rather than from environment variables. scored: true - id: 4.4.2 text: "Consider external secret storage (Manual)" type: "manual" remediation: | Refer to the secrets management options offered by your cloud provider or a third-party secrets management solution. scored: false - id: 4.5 text: "General Policies" checks: - id: 4.5.1 text: "Create administrative boundaries between resources using namespaces (Manual)" type: "manual" remediation: | Follow the documentation and create namespaces for objects in your deployment as you need them. scored: false - id: 4.5.2 text: "The default namespace should not be used (Automated)" audit: | output=$(kubectl get $(kubectl api-resources --verbs=list --namespaced=true -o name | paste -sd, -) --ignore-not-found -n default 2>/dev/null | grep -v "^kubernetes ") if [ -z "$output" ]; then echo "NO_USER_RESOURCES_IN_DEFAULT" else echo "USER_RESOURCES_IN_DEFAULT_FOUND: $output" fi tests: test_items: - flag: "NO_USER_RESOURCES_IN_DEFAULT" set: true remediation: | Create and use dedicated namespaces for resources instead of the default namespace. Move any user-defined objects out of the default namespace to improve resource segregation and RBAC control. scored: true ================================================ FILE: cfg/eks-1.8.0/config.yaml ================================================ --- ## Version-specific settings that override the values in cfg/config.yaml ## These settings are required if you are using the --asff option to report findings to AWS Security Hub ## AWS account number is required. AWS_ACCOUNT: "" ## AWS region is required. AWS_REGION: "" ## EKS Cluster ARN is required. CLUSTER_ARN: "" ================================================ FILE: cfg/eks-1.8.0/controlplane.yaml ================================================ --- controls: version: "eks-1.8.0" id: 2 text: "Control Plane Configuration" type: "controlplane" groups: - id: 2.1 text: "Logging" checks: - id: 2.1.1 text: "Enable audit Logs (Manual)" type: manual remediation: | From Console: 1. For each EKS Cluster in each region; 2. Go to 'Amazon EKS' > 'Clusters' > '' > 'Configuration' > 'Logging'. 3. Click 'Manage logging'. 4. Ensure that all options are toggled to 'Enabled'. API server: Enabled Audit: Enabled Authenticator: Enabled Controller manager: Enabled Scheduler: Enabled 5. Click 'Save Changes'. From CLI: # For each EKS Cluster in each region; aws eks update-cluster-config \ --region '${REGION_CODE}' \ --name '${CLUSTER_NAME}' \ --logging '{"clusterLogging":[{"types":["api","audit","authenticator","controllerManager","scheduler"],"enabled":true}]}' scored: false - id: 2.1.2 text: "Ensure audit logs are collected and managed (Manual)" type: manual remediation: | Create or update the audit-policy.yaml to specify the audit logging configuration: apiVersion: audit.k8s.io/v1 kind: Policy rules: - level: Metadata resources: - group: "" resources: ["pods"] Apply the audit policy configuration to the cluster: kubectl apply -f .yaml Ensure audit logs are forwarded to a centralized logging system like CloudWatch, Elasticsearch, or another log management solution: kubectl create configmap cluster-audit-policy --from-file=audit-policy.yaml -n kube-system kubectl apply -f - < } }" Remediation Method 2: If using executable arguments, edit the kubelet service file on each worker node and ensure the below parameters are part of the KUBELET_ARGS variable string. For systems using systemd, such as the Amazon EKS Optimised Amazon Linux or Bottlerocket AMIs, then this file can be found at /etc/systemd/system/kubelet.service.d/10-kubelet-args.conf. Otherwise, you may need to look up documentation for your chosen operating system to determine which service manager is configured: --client-ca-file= For Both Remediation Steps: Based on your system, restart the kubelet service and check the service status. The following example is for operating systems using systemd, such as the Amazon EKS Optimised Amazon Linux or Bottlerocket AMIs, and invokes the systemctl command. If systemctl is not available then you will need to look up documentation for your chosen operating system to determine which service manager is configured: systemctl daemon-reload systemctl restart kubelet.service systemctl status kubelet -l scored: true - id: 3.2.4 text: "Ensure that the --read-only-port is disabled (Automated)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: "--read-only-port" path: '{.readOnlyPort}' set: true compare: op: eq value: 0 remediation: | If modifying the Kubelet config file, edit the kubelet-config.json file /etc/kubernetes/kubelet/kubelet-config.json and set the below parameter to 0 "readOnlyPort": 0 If using executable arguments, edit the kubelet service file /etc/systemd/system/kubelet.service.d/10-kubelet-args.conf on each worker node and add the below parameter at the end of the KUBELET_ARGS variable string. --read-only-port=0 Based on your system, restart the kubelet service and check status systemctl daemon-reload systemctl restart kubelet.service systemctl status kubelet -l scored: true - id: 3.2.5 text: "Ensure that the --streaming-connection-idle-timeout argument is not set to 0 (Automated)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --streaming-connection-idle-timeout path: '{.streamingConnectionIdleTimeout}' set: true compare: op: noteq value: 0 - flag: --streaming-connection-idle-timeout path: '{.streamingConnectionIdleTimeout}' set: false bin_op: or remediation: | Remediation Method 1: If modifying the Kubelet config file, edit the kubelet-config.json file /etc/kubernetes/kubelet/kubelet-config.json and set the below parameter to a non-zero value in the format of #h#m#s "streamingConnectionIdleTimeout": "4h0m0s" You should ensure that the kubelet service file /etc/systemd/system/kubelet.service.d/10-kubelet-args.conf does not specify a --streaming-connection-idle-timeout argument because it would override the Kubelet config file. Remediation Method 2: If using executable arguments, edit the kubelet service file /etc/systemd/system/kubelet.service.d/10-kubelet-args.conf on each worker node and add the below parameter at the end of the KUBELET_ARGS variable string. --streaming-connection-idle-timeout=4h0m0s Remediation Method 3: If using the api configz endpoint consider searching for the status of "streamingConnectionIdleTimeout": by extracting the live configuration from the nodes running kubelet. **See detailed step-by-step configmap procedures in Reconfigure a Node's Kubelet in a Live Cluster, and then rerun the curl statement from audit process to check for kubelet configuration changes kubectl proxy --port=8001 & export HOSTNAME_PORT=localhost:8001 (example host and port number) export NODE_NAME=ip-192.168.31.226.ec2.internal (example node name from "kubectl get nodes") curl -sSL "http://${HOSTNAME_PORT}/api/v1/nodes/${NODE_NAME}/proxy/configz" For all three remediations: Based on your system, restart the kubelet service and check status systemctl daemon-reload systemctl restart kubelet.service systemctl status kubelet -l scored: true - id: 3.2.6 text: "Ensure that the --make-iptables-util-chains argument is set to true (Automated)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --make-iptables-util-chains path: '{.makeIPTablesUtilChains}' set: true compare: op: eq value: true - flag: --make-iptables-util-chains path: '{.makeIPTablesUtilChains}' set: false bin_op: or remediation: | Remediation Method 1: If modifying the Kubelet config file, edit the kubelet-config.json file /etc/kubernetes/kubelet/kubelet-config.json and set the below parameter to true "makeIPTablesUtilChains": true Ensure that /etc/systemd/system/kubelet.service.d/10-kubelet-args.conf does not set the --make-iptables-util-chains argument because that would override your Kubelet config file. Remediation Method 2: If using executable arguments, edit the kubelet service file /etc/systemd/system/kubelet.service.d/10-kubelet-args.conf on each worker node and add the below parameter at the end of the KUBELET_ARGS variable string. --make-iptables-util-chains:true Remediation Method 3: If using the api configz endpoint consider searching for the status of "makeIPTablesUtilChains.: true by extracting the live configuration from the nodes running kubelet. **See detailed step-by-step configmap procedures in Reconfigure a Node's Kubelet in a Live Cluster, and then rerun the curl statement from audit process to check for kubelet configuration changes kubectl proxy --port=8001 & export HOSTNAME_PORT=localhost:8001 (example host and port number) export NODE_NAME=ip-192.168.31.226.ec2.internal (example node name from "kubectl get nodes") curl -sSL "http://${HOSTNAME_PORT}/api/v1/nodes/${NODE_NAME}/proxy/configz" For all three remediations: Based on your system, restart the kubelet service and check status systemctl daemon-reload systemctl restart kubelet.service systemctl status kubelet -l scored: true - id: 3.2.7 text: "Ensure that the --eventRecordQPS argument is set to 0 or a level which ensures appropriate event capture (Automated)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --event-qps path: '{.eventRecordQPS}' set: true compare: op: gte value: 0 - flag: --event-qps path: '{.eventRecordQPS}' set: false bin_op: or remediation: | If using a Kubelet config file, edit the file to set eventRecordQPS: to an appropriate level. If using command line arguments, edit the kubelet service file /etc/systemd/system/kubelet.service.d/10-kubeadm.conf on each worker node and set the below parameter in KUBELET_SYSTEM_PODS_ARGS variable. Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: true - id: 3.2.8 text: "Ensure that the --rotate-certificates argument is not present or is set to true (Automated)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --rotate-certificates path: '{.rotateCertificates}' compare: op: eq value: true - flag: --rotate-certificates path: '{.rotateCertificates}' set: false bin_op: or remediation: | Remediation Method 1: If modifying the Kubelet config file, edit the kubelet-config.json file /etc/kubernetes/kubelet/kubelet-config.json and set the below parameter to true "RotateCertificate":true Additionally, ensure that the kubelet service file /etc/systemd/system/kubelet.service.d/10-kubelet-args.conf does not set the --RotateCertificate executable argument to false because this would override the Kubelet config file. Remediation Method 2: If using executable arguments, edit the kubelet service file /etc/systemd/system/kubelet.service.d/10-kubelet-args.conf on each worker node and add the below parameter at the end of the KUBELET_ARGS variable string. --rotate-certificates=true scored: true - id: 3.2.9 text: "Ensure that the RotateKubeletServerCertificate argument is set to true (Automated)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: RotateKubeletServerCertificate path: '{.featureGates.RotateKubeletServerCertificate}' set: true compare: op: eq value: true remediation: | Remediation Method 1: If modifying the Kubelet config file, edit the kubelet-config.json file /etc/kubernetes/kubelet/kubelet-config.json and set the below parameter to true "featureGates": { "RotateKubeletServerCertificate":true }, Additionally, ensure that the kubelet service file /etc/systemd/system/kubelet.service.d/10-kubelet-args.conf does not set the --rotate-kubelet-server-certificate executable argument to false because this would override the Kubelet config file. Remediation Method 2: If using executable arguments, edit the kubelet service file /etc/systemd/system/kubelet.service.d/10-kubelet-args.conf on each worker node and add the below parameter at the end of the KUBELET_ARGS variable string. --rotate-kubelet-server-certificate=true Remediation Method 3: If using the api configz endpoint consider searching for the status of "RotateKubeletServerCertificate": by extracting the live configuration from the nodes running kubelet. **See detailed step-by-step configmap procedures in Reconfigure a Node's Kubelet in a Live Cluster, and then rerun the curl statement from audit process to check for kubelet configuration changes kubectl proxy --port=8001 & export HOSTNAME_PORT=localhost:8001 (example host and port number) export NODE_NAME=ip-192.168.31.226.ec2.internal (example node name from "kubectl get nodes") curl -sSL "http://${HOSTNAME_PORT}/api/v1/nodes/${NODE_NAME}/proxy/configz" For all three remediation methods: Restart the kubelet service and check status. The example below is for when using systemctl to manage services: systemctl daemon-reload systemctl restart kubelet.service systemctl status kubelet -l scored: true ================================================ FILE: cfg/eks-1.8.0/policies.yaml ================================================ --- controls: version: "eks-1.8.0" id: 4 text: "Policies" type: "policies" groups: - id: 4.1 text: "RBAC and Service Accounts" checks: - id: 4.1.1 text: "Ensure that the cluster-admin role is only used where required (Automated)" audit: | kubectl get clusterrolebindings -o json | jq -r ' .items[] | select(.roleRef.name == "cluster-admin") | .subjects[]? | select(.kind != "Group" or (.name != "system:masters" and .name != "system:nodes")) | "FOUND_CLUSTER_ADMIN_BINDING" ' || echo "NO_CLUSTER_ADMIN_BINDINGS" tests: test_items: - flag: "NO_CLUSTER_ADMIN_BINDINGS" set: true compare: op: eq value: "NO_CLUSTER_ADMIN_BINDINGS" remediation: | Identify all clusterrolebindings to the cluster-admin role. Check if they are used and if they need this role or if they could use a role with fewer privileges. Where possible, first bind users to a lower privileged role and then remove the clusterrolebinding to the cluster-admin role : kubectl delete clusterrolebinding [name] scored: true - id: 4.1.2 text: "Minimize access to secrets (Automated)" audit: | count=$(kubectl get roles --all-namespaces -o json | jq ' .items[] | select(.rules[]? | (.resources[]? == "secrets") and ((.verbs[]? == "get") or (.verbs[]? == "list") or (.verbs[]? == "watch")) )' | wc -l) if [ "$count" -gt 0 ]; then echo "SECRETS_ACCESS_FOUND" fi tests: test_items: - flag: "SECRETS_ACCESS_FOUND" set: false remediation: | Where possible, remove get, list and watch access to secret objects in the cluster. scored: true - id: 4.1.3 text: "Minimize wildcard use in Roles and ClusterRoles (Automated)" audit: | wildcards=$(kubectl get roles --all-namespaces -o json | jq ' .items[] | select( .rules[]? | (.verbs[]? == "*" or .resources[]? == "*" or .apiGroups[]? == "*") )' | wc -l) wildcards_clusterroles=$(kubectl get clusterroles -o json | jq ' .items[] | select( .rules[]? | (.verbs[]? == "*" or .resources[]? == "*" or .apiGroups[]? == "*") )' | wc -l) total=$((wildcards + wildcards_clusterroles)) if [ "$total" -gt 0 ]; then echo "wildcards_present" fi tests: test_items: - flag: wildcards_present set: false remediation: | Where possible replace any use of wildcards in clusterroles and roles with specific objects or actions. scored: true - id: 4.1.4 text: "Minimize access to create pods (Automated)" audit: | access=$(kubectl get roles,clusterroles -A -o json | jq ' [.items[] | select( .rules[]? | (.resources[]? == "pods" and .verbs[]? == "create") ) ] | length') if [ "$access" -gt 0 ]; then echo "pods_create_access" fi tests: test_items: - flag: pods_create_access set: false remediation: | Where possible, remove create access to pod objects in the cluster. scored: true - id: 4.1.5 text: "Ensure that default service accounts are not actively used. (Automated)" audit: | default_sa_count=$(kubectl get serviceaccounts --all-namespaces -o json | jq ' [.items[] | select(.metadata.name == "default" and (.automountServiceAccountToken != false))] | length') if [ "$default_sa_count" -gt 0 ]; then echo "default_sa_not_auto_mounted" fi pods_using_default_sa=$(kubectl get pods --all-namespaces -o json | jq ' [.items[] | select(.spec.serviceAccountName == "default")] | length') if [ "$pods_using_default_sa" -gt 0 ]; then echo "default_sa_used_in_pods" fi tests: test_items: - flag: default_sa_not_auto_mounted set: false - flag: default_sa_used_in_pods set: false remediation: | Create explicit service accounts wherever a Kubernetes workload requires specific access to the Kubernetes API server. Modify the configuration of each default service account to include this value automountServiceAccountToken: false Automatic remediation for the default account: kubectl patch serviceaccount default -p $'automountServiceAccountToken: false' scored: true - id: 4.1.6 text: "Ensure that Service Account Tokens are only mounted where necessary (Automated)" audit: | pods_with_token_mount=$(kubectl get pods --all-namespaces -o json | jq ' [.items[] | select(.spec.automountServiceAccountToken != false)] | length') if [ "$pods_with_token_mount" -gt 0 ]; then echo "automountServiceAccountToken" fi tests: test_items: - flag: automountServiceAccountToken set: false remediation: | Regularly review pod and service account objects in the cluster to ensure that the automountServiceAccountToken setting is false for pods and accounts that do not explicitly require API server access. scored: true - id: 4.1.7 text: "Cluster Access Manager API to streamline and enhance the management of access controls within EKS clusters (Manual)" type: "manual" remediation: | Log in to the AWS Management Console. Navigate to Amazon EKS and select your EKS cluster. Go to the Access tab and click on "Manage Access" in the "Access Configuration section". Under Cluster Authentication Mode for Cluster Access settings. Click EKS API to change cluster will source authenticated IAM principals only from EKS access entry APIs. Click ConfigMap to change cluster will source authenticated IAM principals only from the aws-auth ConfigMap. Note: EKS API and ConfigMap must be selected during Cluster creation and cannot be changed once the Cluster is provisioned. scored: false - id: 4.1.8 text: "Limit use of the Bind, Impersonate and Escalate permissions in the Kubernetes cluster (Manual)" type: "manual" remediation: | Where possible, remove the impersonate, bind and escalate rights from subjects. scored: false - id: 4.1.9 text: "Minimize access to create PersistentVolume objects (Manual)" type: "manual" remediation: | Review the RBAC rules in the cluster and identify users, groups, or service accounts with create permissions on PersistentVolume resources. Where possible, remove or restrict create access to PersistentVolume objects to trusted administrators only. scored: false - id: 4.1.10 text: "Minimize access to the proxy sub-resource of Node objects (Manual)" type: "manual" remediation: | Review RBAC roles and bindings in the cluster to identify users, groups, or service accounts with access to the proxy sub-resource of Node objects. Where possible, remove or restrict access to the node proxy sub-resource to trusted administrators only. scored: false - id: 4.1.11 text: "Minimize access to webhook configuration objects (Manual)" type: "manual" remediation: | Review RBAC roles and bindings in the cluster to identify users, groups, or service accounts with access to validatingwebhookconfigurations or mutatingwebhookconfigurations objects. Where possible, remove or restrict access to these webhook configuration objects to trusted administrators only. scored: false - id: 4.1.12 text: "Minimize access to the service account token creation (Manual)" type: "manual" remediation: | Review RBAC roles and bindings in the cluster to identify users, groups, or service accounts with access to create the token sub-resource of serviceaccount objects. Where possible, remove or restrict access to token creation to trusted administrators only. scored: false - id: 4.2 text: "Pod Security Standards" checks: - id: 4.2.1 text: "Minimize the admission of privileged containers (Automated)" audit: | kubectl get pods --all-namespaces -o json | \ jq -r 'if any(.items[]?.spec.containers[]?; .securityContext?.privileged == true) then "PRIVILEGED_FOUND" else "NO_PRIVILEGED" end' tests: test_items: - flag: "NO_PRIVILEGED" set: true compare: op: eq value: "NO_PRIVILEGED" remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of privileged containers. To enable PSA for a namespace in your cluster, set the pod-security.kubernetes.io/enforce label with the policy value you want to enforce. kubectl label --overwrite ns NAMESPACE pod-security.kubernetes.io/enforce=restricted The above command enforces the restricted policy for the NAMESPACE namespace. You can also enable Pod Security Admission for all your namespaces. For example: kubectl label --overwrite ns --all pod-security.kubernetes.io/warn=baseline scored: true - id: 4.2.2 text: "Minimize the admission of containers wishing to share the host process ID namespace (Automated)" audit: | kubectl get pods --all-namespaces -o json | \ jq -r 'if any(.items[]?; .spec.hostPID == true) then "HOSTPID_FOUND" else "NO_HOSTPID" end' tests: test_items: - flag: "NO_HOSTPID" set: true compare: op: eq value: "NO_HOSTPID" remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of hostPID containers. scored: true - id: 4.2.3 text: "Minimize the admission of containers wishing to share the host IPC namespace (Automated)" audit: | kubectl get pods --all-namespaces -o json | jq -r 'if any(.items[]?; .spec.hostIPC == true) then "HOSTIPC_FOUND" else "NO_HOSTIPC" end' tests: test_items: - flag: "NO_HOSTIPC" set: true compare: op: eq value: "NO_HOSTIPC" remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of hostIPC containers. scored: true - id: 4.2.4 text: "Minimize the admission of containers wishing to share the host network namespace (Automated)" audit: | kubectl get pods --all-namespaces -o json | jq -r 'if any(.items[]?; .spec.hostNetwork == true) then "HOSTNETWORK_FOUND" else "NO_HOSTNETWORK" end' tests: test_items: - flag: "NO_HOSTNETWORK" set: true compare: op: eq value: "NO_HOSTNETWORK" remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of hostNetwork containers. scored: true - id: 4.2.5 text: "Minimize the admission of containers with allowPrivilegeEscalation (Automated)" audit: | kubectl get pods --all-namespaces -o json | \ jq -r 'if any(.items[]?.spec.containers[]?; .securityContext?.allowPrivilegeEscalation == true) then "ALLOWPRIVILEGEESCALTION_FOUND" else "NO_ALLOWPRIVILEGEESCALATION" end' tests: test_items: - flag: "NO_ALLOWPRIVILEGEESCALATION" set: true compare: op: eq value: "NO_ALLOWPRIVILEGEESCALATION" remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of containers with .spec.allowPrivilegeEscalation set to true. scored: true - id: 4.3 text: "CNI Plugin" checks: - id: 4.3.1 text: "Ensure CNI plugin supports network policies (Manual)" type: "manual" remediation: | As with RBAC policies, network policies should adhere to the policy of least privileged access. Start by creating a deny all policy that restricts all inbound and outbound traffic from a namespace or create a global policy using Calico. scored: false - id: 4.3.2 text: "Ensure that all Namespaces have Network Policies defined (Automated)" audit: | ns_without_np=$(kubectl get namespaces -o json | jq -r '.items[].metadata.name' | while read ns; do count=$(kubectl get networkpolicy -n $ns --no-headers 2>/dev/null | wc -l) if [ "$count" -eq 0 ]; then echo $ns; fi done) if [ -z "$ns_without_np" ]; then echo "ALL_NAMESPACES_HAVE_NETWORK_POLICIES" else echo "NAMESPACES_WITHOUT_NETWORK_POLICIES: $ns_without_np" fi tests: test_items: - flag: "ALL_NAMESPACES_HAVE_NETWORK_POLICIES" set: true compare: op: eq value: "ALL_NAMESPACES_HAVE_NETWORK_POLICIES" remediation: | Create at least one NetworkPolicy in each namespace to control and restrict traffic between pods as needed. scored: true - id: 4.4 text: "Secrets Management" checks: - id: 4.4.1 text: "Prefer using secrets as files over secrets as environment variables (Automated)" audit: | result=$(kubectl get all --all-namespaces -o jsonpath='{range .items[?(@..secretKeyRef)]}{.metadata.namespace} {.kind} {.metadata.name}{"\n"}{end}') if [ -z "$result" ]; then echo "NO_SECRETS_AS_ENV_VARS" else echo "SECRETS_AS_ENV_VARS_FOUND: $result" fi tests: test_items: - flag: "NO_SECRETS_AS_ENV_VARS" set: true compare: op: eq value: "NO_SECRETS_AS_ENV_VARS" remediation: | If possible, rewrite application code to read secrets from mounted secret files, rather than from environment variables. scored: true - id: 4.4.2 text: "Consider external secret storage (Manual)" type: "manual" remediation: | Refer to the secrets management options offered by your cloud provider or a third-party secrets management solution. scored: false - id: 4.5 text: "General Policies" checks: - id: 4.5.1 text: "Create administrative boundaries between resources using namespaces (Manual)" type: "manual" remediation: | Follow the documentation and create namespaces for objects in your deployment as you need them. scored: false - id: 4.5.2 text: "The default namespace should not be used (Automated)" audit: | output=$(kubectl get $(kubectl api-resources --verbs=list --namespaced=true -o name | paste -sd, -) --ignore-not-found -n default 2>/dev/null | grep -v "^kubernetes ") if [ -z "$output" ]; then echo "NO_USER_RESOURCES_IN_DEFAULT" else echo "USER_RESOURCES_IN_DEFAULT_FOUND: $output" fi tests: test_items: - flag: "NO_USER_RESOURCES_IN_DEFAULT" set: true remediation: | Create and use dedicated namespaces for resources instead of the default namespace. Move any user-defined objects out of the default namespace to improve resource segregation and RBAC control. scored: true ================================================ FILE: cfg/eks-stig-kubernetes-v1r6/config.yaml ================================================ --- ## Version-specific settings that override the values in cfg/config.yaml ## These settings are required if you are using the --asff option to report findings to AWS Security Hub ## AWS account number is required. AWS_ACCOUNT: "" ## AWS region is required. AWS_REGION: "" ## EKS Cluster ARN is required. CLUSTER_ARN: "" ================================================ FILE: cfg/eks-stig-kubernetes-v1r6/controlplane.yaml ================================================ --- controls: version: "eks-stig-kubernetes-v1r6" id: 2 text: "Control Plane Configuration" type: "controlplane" groups: - id: 2.1 text: "DISA Category Code I" checks: - id: V-242390 text: "The Kubernetes API server must have anonymous authentication disabled (Automated)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: "--anonymous-auth" path: '{.authentication.anonymous.enabled}' set: true compare: op: eq value: false remediation: | If using a Kubelet config file, edit $kubeletconf to set authentication: anonymous: enabled to false. If using executable arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_SYSTEM_PODS_ARGS variable. --anonymous-auth=false Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: true - id: V-242400 text: "The Kubernetes API server must have Alpha APIs disabled (Automated)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: bin_op: or test_items: - flag: "--feature-gates" compare: op: nothave value: "AllAlpha=true" set: true - flag: "--feature-gates" set: false remediation: | Edit any manifest files or $kubeletconf that contain the feature-gates setting with AllAlpha set to "true". Set the flag to "false" or remove the "AllAlpha" setting completely. Restart the kubelet service if the kubelet config file if the kubelet config file is changed. scored: true - id: 2.2 text: "DISA Category Code II" checks: - id: V-242381 text: "The Kubernetes Controller Manager must create unique service accounts for each work payload. (Manual)" type: "manual" remediation: | Create explicit service accounts wherever a Kubernetes workload requires specific access to the Kubernetes API server. Modify the configuration of each default service account to include this value automountServiceAccountToken: false scored: false - id: V-242402 text: "The Kubernetes API Server must have an audit log path set (Manual)" type: "manual" remediation: | Enable control plane logging for API Server, Audit, Authenticator, Controller Manager, and Scheduler. Ref: https://docs.aws.amazon.com/eks/latest/userguide/control-plane-logs.html scored: false - id: V-242403 text: "Kubernetes API Server must generate audit records (Manual)" type: "manual" remediation: | Enable control plane logging for API Server, Audit, Authenticator, Controller Manager, and Scheduler. Ref: https://docs.aws.amazon.com/eks/latest/userguide/control-plane-logs.html scored: false - id: V-242461 text: "Kubernetes API Server audit logs must be enabled. (Manual)" type: "manual" remediation: | Enable control plane logging for API Server, Audit, Authenticator, Controller Manager, and Scheduler. Ref: https://docs.aws.amazon.com/eks/latest/userguide/control-plane-logs.html scored: false - id: V-242462 text: "The Kubernetes API Server must be set to audit log max size. (Manual)" type: "manual" remediation: | Enable control plane logging for API Server, Audit, Authenticator, Controller Manager, and Scheduler. Ref: https://docs.aws.amazon.com/eks/latest/userguide/control-plane-logs.html scored: false - id: V-242463 text: "The Kubernetes API Server must be set to audit log maximum backup. (Manual)" type: "manual" remediation: | Enable control plane logging for API Server, Audit, Authenticator, Controller Manager, and Scheduler. Ref: https://docs.aws.amazon.com/eks/latest/userguide/control-plane-logs.html scored: false - id: V-242464 text: "The Kubernetes API Server audit log retention must be set. (Manual)" type: "manual" remediation: | Enable control plane logging for API Server, Audit, Authenticator, Controller Manager, and Scheduler. Ref: https://docs.aws.amazon.com/eks/latest/userguide/control-plane-logs.html scored: false - id: V-242465 text: "The Kubernetes API Server audit log path must be set. (Manual)" type: "manual" remediation: | Enable control plane logging for API Server, Audit, Authenticator, Controller Manager, and Scheduler. Ref: https://docs.aws.amazon.com/eks/latest/userguide/control-plane-logs.html scored: false - id: 2.2 text: "DISA Category Code II" checks: - id: V-242443 text: " Kubernetes must contain the latest updates as authorized by IAVMs, CTOs, DTMs, and STIGs. (Manual)" type: "manual" remediation: | Upgrade Kubernetes to a supported version. Ref: https://docs.aws.amazon.com/eks/latest/userguide/update-cluster.html ================================================ FILE: cfg/eks-stig-kubernetes-v1r6/managedservices.yaml ================================================ --- controls: version: "eks-stig-kubernetes-v1r6" id: 5 text: "Managed Services" type: "managedservices" groups: - id: 5.1 text: "DISA Category Code I" checks: - id: V-242386 text: "The Kubernetes API server must have the insecure port flag disabled | Component of EKS Control Plane" type: "skip" - id: V-242388 text: "The Kubernetes API server must have the insecure bind address not set | Component of EKS Control Plane" type: "skip" - id: V-242436 text: "The Kubernetes API server must have the ValidatingAdmissionWebhook enabled (manual)" type: "manual" remediation: | Amazon EKS version 1.18 and later automatically enable ValidatingAdmissionWebhook Ref: https://docs.aws.amazon.com/eks/latest/userguide/platform-versions.html scored: false - id: V-245542 text: "Kubernetes API Server must disable basic authentication to protect information in transit | Component of EKS Control Plane" type: "skip" - id: 5.2 text: "DISA Category Code II" checks: - id: V-242376 text: "The Kubernetes Controller Manager must use TLS 1.2, at a minimum | Component of EKS Control Plane" type: "skip" - id: V-242377 text: "The Kubernetes Scheduler must use TLS 1.2, at a minimum | Component of EKS Control Plane" type: "skip" - id: V-242378 text: "The Kubernetes API Server must use TLS 1.2, at a minimum | Component of EKS Control Plane" type: "skip" - id: V-242379 text: "The Kubernetes etcd must use TLS to protect the confidentiality of sensitive data during electronic dissemination | Component of EKS Control Plane" type: "skip" - id: V-242380 text: "The Kubernetes etcd must use TLS to protect the confidentiality of sensitive data during electronic dissemination | Component of EKS Control Plane" type: "skip" - id: V-242382 text: "The Kubernetes API Server must enable Node,RBAC as the authorization mode | Component of EKS Control Plane" type: "skip" - id: V-242384 text: "The Kubernetes Scheduler must have secure binding | Component of EKS Control Plane" type: "skip" - id: V-242385 text: "The Kubernetes Controller Manager must have secure binding | Component of EKS Control Plane" type: "skip" - id: V-242389 text: "The Kubernetes API server must have the secure port set | Component of EKS Control Plane" type: "skip" - id: V-242401 text: "The Kubernetes API Server must have an audit policy set | Component of EKS Control Plane" type: "skip" - id: V-242402 text: "The Kubernetes API Server must have an audit log path set | Component of EKS Control Plane" type: "skip" - id: V-242403 text: "Kubernetes API Server must generate audit records | Component of EKS Control Plane" type: "skip" - id: V-242405 text: "The Kubernetes manifests must be owned by root | Component of EKS Control Plane" type: "skip" - id: V-242408 text: "The Kubernetes manifests must have least privileges | Component of EKS Control Plane" type: "skip" - id: V-242409 text: "Kubernetes Controller Manager must disable profiling | Component of EKS Control Plane" type: "skip" - id: V-242410 text: "The Kubernetes API Server must enforce PPS that adhere to PPSM CAL | Component of EKS Control Plane" type: "skip" - id: V-242411 text: "The Kubernetes Scheduler must enforce PPS that adhere to PPSM CAL | Component of EKS Control Plane" type: "skip" - id: V-242412 text: "The Kubernetes Controllers must enforce PPS that adhere to PPSM CAL | Component of EKS Control Plane" type: "skip" - id: V-242413 text: "The Kubernetes etcd must enforce PPS that adhere to PPSM CAL | Component of EKS Control Plane" type: "skip" - id: V-242418 text: "The Kubernetes API server must use approved cipher suites | Component of EKS Control Plane" type: "skip" - id: V-242419 text: "Kubernetes API Server must have the SSL Certificate Authority set | Component of EKS Control Plane" type: "skip" - id: V-242420 text: "Kubernetes Kubelet must have the SSL Certificate Authority set | Component of EKS Control Plane" type: "skip" - id: V-242421 text: "Kubernetes Controller Manager must have the SSL Certificate Authority set | Component of EKS Control Plane" type: "skip" - id: V-242422 text: "Kubernetes API Server must have a certificate for communication | Component of EKS Control Plane" type: "skip" - id: V-242423 text: "Kubernetes etcd must enable client authentication to secure service | Component of EKS Control Plane" type: "skip" - id: V-242424 text: "Kubernetes etcd must enable client authentication to secure service | Component of EKS Control Plane" type: "skip" - id: V-242425 text: "Kubernetes Kubelet must enable tls-cert-file for client authentication to secure service | Component of EKS Control Plane" type: "skip" - id: V-242426 text: "Kubernetes etcd must enable client authentication to secure service | Component of EKS Control Plane" type: "skip" - id: V-242427 text: "Kubernetes etcd must have a key file for secure communication | Component of EKS Control Plane" type: "skip" - id: V-242428 text: "Kubernetes etcd must have a certificate for communication | Component of EKS Control Plane" type: "skip" - id: V-242429 text: "Kubernetes etcd must have the SSL Certificate Authority set | Component of EKS Control Plane" type: "skip" - id: V-242430 text: "Kubernetes etcd must have a certificate for communication | Component of EKS Control Plane" type: "skip" - id: V-242431 text: "Kubernetes etcd must have a key file for secure communication | Component of EKS Control Plane" type: "skip" - id: V-242432 text: "Kubernetes etcd must have peer-cert-file set for secure communication | Component of EKS Control Plane" type: "skip" - id: V-242433 text: "Kubernetes etcd must have a peer-key-file set for secure communication | Component of EKS Control Plane" type: "skip" - id: V-242438 text: "Kubernetes API Server must configure timeouts to limit attack surface | Component of EKS Control Plane" type: "skip" - id: V-242444 text: "The Kubernetes component manifests must be owned by root | Component of EKS Control Plane" type: "skip" - id: V-242445 text: "The Kubernetes component etcd must be owned by etcd | Component of EKS Control Plane" type: "skip" - id: V-242446 text: "The Kubernetes conf files must be owned by root | Component of EKS Control Plane" type: "skip" - id: V-242447 text: "The Kubernetes Kube Proxy must have file permissions set to 644 or more restrictive | Component of EKS Control Plane" type: "skip" - id: V-242448 text: "The Kubernetes Kube Proxy must be owned by root | Component of EKS Control Plane" type: "skip" - id: V-242449 text: "The Kubernetes Kubelet certificate authority file must have file permissions set to 644 or more restrictive | Component of EKS Control Plane" type: "skip" - id: V-242450 text: "The Kubernetes Kubelet certificate authority must be owned by root | Component of EKS Control Plane" type: "skip" - id: V-242451 text: "The Kubernetes component PKI must be owned by root | Component of EKS Control Plane" type: "skip" - id: V-242452 text: "The Kubernetes kubelet config must have file permissions set to 644 or more restrictive | Component of EKS Control Plane" type: "skip" - id: V-242453 text: "The Kubernetes kubelet config must be owned by root | Component of EKS Control Plane" type: "skip" - id: V-242454 text: "The Kubernetes kubeadm.conf must be owned by root | Component of EKS Control Plane" type: "skip" - id: V-242455 text: "The Kubernetes kubeadm.conf must have file permissions set to 644 or more restrictive | Component of EKS Control Plane" type: "skip" - id: V-242456 text: "The Kubernetes kubelet config must have file permissions set to 644 or more restrictive | Component of EKS Control Plane" type: "skip" - id: V-242457 text: "The Kubernetes kubelet config must be owned by root | Component of EKS Control Plane" type: "skip" - id: V-242458 text: "The Kubernetes API Server must have file permissions set to 644 or more restrictive | Component of EKS Control Plane" type: "skip" - id: V-242459 text: "The Kubernetes etcd must have file permissions set to 644 or more restrictive | Component of EKS Control Plane" type: "skip" - id: V-242460 text: "The Kubernetes admin.conf must have file permissions set to 644 or more restrictive | Component of EKS Control Plane" type: "skip" - id: V-242466 text: "The Kubernetes PKI CRT must have file permissions set to 644 or more restrictive | Component of EKS Control Plane" type: "skip" - id: V-242467 text: "The Kubernetes PKI keys must have file permissions set to 600 or more restrictive | Component of EKS Control Plane" type: "skip" - id: V-242468 text: "The Kubernetes API Server must prohibit communication using TLS version 1.0 and 1.1, and SSL 2.0 and 3.0 | Component of EKS Control Plane" type: "skip" - id: V-245541 text: "Kubernetes Kubelet must not disable timeouts | Component of EKS Control Plane" type: "skip" - id: V-245543 text: "Kubernetes API Server must disable token authentication to protect information in transit | Component of EKS Control Plane" type: "skip" - id: V-245544 text: "Kubernetes endpoints must use approved organizational certificate and key pair to protect information in transit | Component of EKS Control Plane" type: "skip" ================================================ FILE: cfg/eks-stig-kubernetes-v1r6/master.yaml ================================================ --- controls: version: "eks-stig-kubernetes-v1r6" id: 1 text: "Control Plane Components" type: "master" ================================================ FILE: cfg/eks-stig-kubernetes-v1r6/node.yaml ================================================ --- controls: version: "eks-stig-kubernetes-v1r6" id: 3 text: "Worker Node Security Configuration" type: "node" groups: - id: 3.1 text: "DISA Category Code I" checks: - id: V-242387 # CIS 3.2.4 text: "The Kubernetes Kubelet must have the read-only port flag disabled (Manual)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: "--read-only-port" path: '{.readOnlyPort}' set: true compare: op: eq value: 0 remediation: | If using a Kubelet config file, edit $kubeletconf to set readOnlyPort to 0. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_SYSTEM_PODS_ARGS variable. --read-only-port=0 Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: false - id: V-242391 # CIS 3.2.1 text: "The Kubernetes Kubelet must have anonymous authentication disabled (Automated)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: "--anonymous-auth" path: '{.authentication.anonymous.enabled}' set: true compare: op: eq value: false remediation: | If using a Kubelet config file, edit $kubeletconf to set authentication: anonymous: enabled to false. If using executable arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_SYSTEM_PODS_ARGS variable. --anonymous-auth=false Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: true - id: V-242392 # CIS 3.2.2 text: "The Kubernetes kubelet must enable explicit authorization (Automated)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --authorization-mode path: '{.authorization.mode}' set: true compare: op: nothave value: AlwaysAllow remediation: | If using a Kubelet config file, edit $kubeletconf to set authorization: mode to Webhook. If using executable arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_AUTHZ_ARGS variable. --authorization-mode=Webhook Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: true - id: V-242397 text: "The Kubernetes kubelet static PodPath must not enable static pods (Automated)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - path: '{.staticPodPath}' set: false remediation: | Edit $kubeletconf on each node to to remove the staticPodPath Based on your system, restart the kubelet service. For example, systemctl daemon-reload systemctl restart kubelet.service scored: true - id: V-242415 text: "Secrets in Kubernetes must not be stored as environment variables.(Manual)" type: "manual" remediation: | Run the following command: kubectl get all -o jsonpath='{range .items[?(@..secretKeyRef)]} {.kind} {.metadata.name} {"\n"}{end}' -A If any of the values returned reference environment variables rewrite application code to read secrets from mounted secret files, rather than from environment variables. scored: false - id: V-242434 # CIS 3.2.6 text: "Kubernetes Kubelet must enable kernel protection (Automated)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --protect-kernel-defaults path: '{.protectKernelDefaults}' set: true compare: op: eq value: true remediation: | If using a Kubelet config file, edit $kubeletconf to set protectKernelDefaults: true. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_SYSTEM_PODS_ARGS variable. --protect-kernel-defaults=true Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: true - id: V-242435 text: "Kubernetes must prevent non-privileged users from executing privileged functions (Automated)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --authorization-mode path: '{.authorization.mode}' set: true compare: op: nothave value: AlwaysAllow remediation: | If using a Kubelet config file, edit $kubeletconf to set authorization: mode to Webhook. If using executable arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_AUTHZ_ARGS variable. --authorization-mode=Webhook Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: true - id: V-242393 text: "Kubernetes Worker Nodes must not have sshd service running. (Automated)" audit: '/bin/sh -c ''systemctl show -p ActiveState sshd'' ' tests: test_items: - flag: ActiveState compare: op: eq value: inactive remediation: | To stop the sshd service, run the command: systemctl stop sshd scored: true - id: V-242394 text: "Kubernetes Worker Nodes must not have the sshd service enabled. (Automated)" audit: "/bin/sh -c 'systemctl is-enabled sshd.service'" tests: test_items: - flag: "disabled" remediation: | To disable the sshd service, run the command: chkconfig sshd off scored: true - id: V-242395 text: "Kubernetes dashboard must not be enabled. (Manual)" type: "manual" remediation: | Run the command: kubectl get pods --all-namespaces -l k8s-app=kubernetes-dashboard If any resources are returned, this is a finding. Fix Text: Delete the Kubernetes dashboard deployment with the following command: kubectl delete deployment kubernetes-dashboard --namespace=kube-system scored: false - id: V-242398 text: "Kubernetes DynamicAuditing must not be enabled. (Automated)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: bin_op: or test_items: - flag: "--feature-gates" compare: op: nothave value: "DynamicAuditing=true" set: true - flag: "--feature-gates" set: false remediation: | Edit any manifest files or kubelet config files that contain the feature-gates setting with DynamicAuditing set to "true". Set the flag to "false" or remove the "DynamicAuditing" setting completely. Restart the kubelet service if the kubelet config file if the kubelet config file is changed. scored: true - id: V-242399 text: "Kubernetes DynamicKubeletConfig must not be enabled. (Automated)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: bin_op: or test_items: - flag: "--feature-gates" compare: op: nothave value: "DynamicKubeletConfig=true" set: true - flag: "--feature-gates" set: false remediation: | Edit any manifest files or $kubeletconf that contain the feature-gates setting with DynamicKubeletConfig set to "true". Set the flag to "false" or remove the "DynamicKubeletConfig" setting completely. Restart the kubelet service if the kubelet config file if the kubelet config file is changed. scored: true - id: V-242404 # CIS 3.2.8 text: "Kubernetes Kubelet must deny hostname override (Automated)" # This is one of those properties that can only be set as a command line argument. # To check if the property is set as expected, we need to parse the kubelet command # instead reading the Kubelet Configuration file. audit: "/bin/ps -fC $kubeletbin " tests: test_items: - flag: --hostname-override set: false remediation: | Edit the kubelet service file $kubeletbin on each worker node and remove the --hostname-override argument from the KUBELET_SYSTEM_PODS_ARGS variable. Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: true - id: V-242406 text: "The Kubernetes kubelet configuration file must be owned by root (Automated)" audit: '/bin/sh -c ''if test -e $kubeletkubeconfig; then stat -c %U:%G $kubeletkubeconfig; fi'' ' tests: test_items: - flag: root:root remediation: | Run the below command (based on the file location on your system) on the each worker node. For example, chown root:root $kubeletkubeconfig scored: true - id: V-242407 text: "The Kubernetes kubelet configuration files must have file permissions set to 644 or more restrictive (Automated)" audit: '/bin/sh -c ''if test -e $kubeletconf; then stat -c permissions=%a $kubeletconf; fi'' ' tests: test_items: - flag: "permissions" compare: op: bitmask value: "644" remediation: | Run the following command (using the config file location identified in the Audit step) chmod 644 $kubeletconf scored: true - id: V-242414 text: "The Kubernetes cluster must use non-privileged host ports for user pods. (Manual)" type: "manual" remediation: | For any of the pods that are using ports below 1024, reconfigure the pod to use a service to map a host non-privileged port to the pod port or reconfigure the image to use non-privileged ports. scored: false - id: V-242442 text: "Kubernetes must remove old components after updated versions have been installed. (Manual)" type: "manual" remediation: | To view all pods and the images used to create the pods, from the Master node, run the following command: kubectl get pods --all-namespaces -o jsonpath="{..image}" | \ tr -s '[[:space:]]' '\n' | \ sort | \ uniq -c Review the images used for pods running within Kubernetes. Remove any old pods that are using older images. scored: false - id: V-242396 text: "Kubernetes Kubectl cp command must give expected access and results. (Manual)" type: "manual" remediation: | If any Worker nodes are not using kubectl version 1.12.9 or newer, this is a finding. Upgrade the Master and Worker nodes to the latest version of kubectl. scored: false ================================================ FILE: cfg/eks-stig-kubernetes-v1r6/policies.yaml ================================================ --- controls: version: "eks-stig-kubernetes-v1r6" id: 4 text: "Policies" type: "policies" groups: - id: 4.1 text: "Policies - DISA Category Code I" checks: - id: V-242381 text: "The Kubernetes Controller Manager must create unique service accounts for each work payload. (Manual)" type: "manual" remediation: | Create explicit service accounts wherever a Kubernetes workload requires specific access to the Kubernetes API server. Modify the configuration of each default service account to include this value automountServiceAccountToken: false scored: false - id: V-242383 text: "User-managed resources must be created in dedicated namespaces. (Manual)" type: "manual" remediation: | Move any user-managed resources from the default, kube-public and kube-node-lease namespaces, to user namespaces. scored: false - id: V-242417 text: "Kubernetes must separate user functionality. (Manual)" type: "manual" remediation: | Move any user pods that are present in the Kubernetes system namespaces to user specific namespaces. scored: false ================================================ FILE: cfg/gke-1.0/config.yaml ================================================ --- ## Version-specific settings that override the values in cfg/config.yaml ================================================ FILE: cfg/gke-1.0/controlplane.yaml ================================================ --- controls: version: "gke-1.0" id: 3 text: "Control Plane Configuration" type: "controlplane" groups: - id: 3.1 text: "Authentication and Authorization" checks: - id: 3.1.1 text: "Client certificate authentication should not be used for users (Not Scored)" type: "manual" remediation: | Alternative mechanisms provided by Kubernetes such as the use of OIDC should be implemented in place of client certificates. You can remediate the availability of client certificates in your GKE cluster. See Recommendation 6.8.2. scored: false - id: 3.2 text: "Logging" type: skip checks: - id: 3.2.1 text: "Ensure that a minimal audit policy is created (Not Scored)" remediation: "This control cannot be modified in GKE." scored: false - id: 3.2.2 text: "Ensure that the audit policy covers key security concerns (Not Scored) " remediation: "This control cannot be modified in GKE." scored: false ================================================ FILE: cfg/gke-1.0/etcd.yaml ================================================ --- controls: version: "gke-1.0" id: 2 text: "Etcd Node Configuration" type: "etcd" groups: - id: 2 text: "Etcd Node Configuration Files" type: skip checks: - id: 2.1 text: "Ensure that the --cert-file and --key-file arguments are set as appropriate (Not Scored)" remediation: "This control cannot be modified in GKE." scored: false - id: 2.2 text: "Ensure that the --client-cert-auth argument is set to true (Not Scored)" remediation: "This control cannot be modified in GKE." scored: false - id: 2.3 text: "Ensure that the --auto-tls argument is not set to true (Not Scored)" remediation: "This control cannot be modified in GKE." scored: false - id: 2.4 text: "Ensure that the --peer-cert-file and --peer-key-file arguments are set as appropriate (Not Scored)" remediation: "This control cannot be modified in GKE." scored: false - id: 2.5 text: "Ensure that the --peer-client-cert-auth argument is set to true (Not Scored)" remediation: "This control cannot be modified in GKE." scored: false - id: 2.6 text: "Ensure that the --peer-auto-tls argument is not set to true (Not Scored)" remediation: "This control cannot be modified in GKE." scored: false - id: 2.7 text: "Ensure that a unique Certificate Authority is used for etcd (Not Scored)" remediation: "This control cannot be modified in GKE." scored: false ================================================ FILE: cfg/gke-1.0/managedservices.yaml ================================================ --- controls: version: "gke-1.0" id: 6 text: "Managed Services" type: "managedservices" groups: - id: 6.1 text: "Image Registry and Image Scanning" checks: - id: 6.1.1 text: "Ensure Image Vulnerability Scanning using GCR Container Analysis or a third-party provider (Scored)" type: "manual" remediation: | Using Command Line: gcloud services enable containerscanning.googleapis.com scored: true - id: 6.1.2 text: "Minimize user access to GCR (Scored)" type: "manual" remediation: | Using Command Line: To change roles at the GCR bucket level: Firstly, run the following if read permissions are required: gsutil iam ch [TYPE]:[EMAIL-ADDRESS]:objectViewer gs://artifacts.[PROJECT_ID].appspot.com Then remove the excessively privileged role (Storage Admin / Storage Object Admin / Storage Object Creator) using: gsutil iam ch -d [TYPE]:[EMAIL-ADDRESS]:[ROLE] gs://artifacts.[PROJECT_ID].appspot.com where: [TYPE] can be one of the following: o user, if the [EMAIL-ADDRESS] is a Google account o serviceAccount, if [EMAIL-ADDRESS] specifies a Service account [EMAIL-ADDRESS] can be one of the following: o a Google account (for example, someone@example.com) o a Cloud IAM service account To modify roles defined at the project level and subsequently inherited within the GCR bucket, or the Service Account User role, extract the IAM policy file, modify it accordingly and apply it using: gcloud projects set-iam-policy [PROJECT_ID] [POLICY_FILE] scored: true - id: 6.1.3 text: "Minimize cluster access to read-only for GCR (Scored)" type: "manual" remediation: | Using Command Line: For an account explicitly granted to the bucket. First, add read access to the Kubernetes Service Account gsutil iam ch [TYPE]:[EMAIL-ADDRESS]:objectViewer gs://artifacts.[PROJECT_ID].appspot.com where: [TYPE] can be one of the following: o user, if the [EMAIL-ADDRESS] is a Google account o serviceAccount, if [EMAIL-ADDRESS] specifies a Service account [EMAIL-ADDRESS] can be one of the following: o a Google account (for example, someone@example.com) o a Cloud IAM service account Then remove the excessively privileged role (Storage Admin / Storage Object Admin / Storage Object Creator) using: gsutil iam ch -d [TYPE]:[EMAIL-ADDRESS]:[ROLE] gs://artifacts.[PROJECT_ID].appspot.com For an account that inherits access to the GCR Bucket through Project level permissions, modify the Projects IAM policy file accordingly, then upload it using: gcloud projects set-iam-policy [PROJECT_ID] [POLICY_FILE] scored: true - id: 6.1.4 text: "Minimize Container Registries to only those approved (Not Scored)" type: "manual" remediation: | Using Command Line: First, update the cluster to enable Binary Authorization: gcloud container cluster update [CLUSTER_NAME] \ --enable-binauthz Create a Binary Authorization Policy using the Binary Authorization Policy Reference (https://cloud.google.com/binary-authorization/docs/policy-yaml-reference) for guidance. Import the policy file into Binary Authorization: gcloud container binauthz policy import [YAML_POLICY] scored: false - id: 6.2 text: "Identity and Access Management (IAM)" checks: - id: 6.2.1 text: "Ensure GKE clusters are not running using the Compute Engine default service account (Scored)" type: "manual" remediation: | Using Command Line: Firstly, create a minimally privileged service account: gcloud iam service-accounts create [SA_NAME] \ --display-name "GKE Node Service Account" export NODE_SA_EMAIL=`gcloud iam service-accounts list \ --format='value(email)' \ --filter='displayName:GKE Node Service Account'` Grant the following roles to the service account: export PROJECT_ID=`gcloud config get-value project` gcloud projects add-iam-policy-binding $PROJECT_ID \ --member serviceAccount:$NODE_SA_EMAIL \ --role roles/monitoring.metricWriter gcloud projects add-iam-policy-binding $PROJECT_ID \ --member serviceAccount:$NODE_SA_EMAIL \ --role roles/monitoring.viewer gcloud projects add-iam-policy-binding $PROJECT_ID \ --member serviceAccount:$NODE_SA_EMAIL \ --role roles/logging.logWriter To create a new Node pool using the Service account, run the following command: gcloud container node-pools create [NODE_POOL] \ --service-account=[SA_NAME]@[PROJECT_ID].iam.gserviceaccount.com \ --cluster=[CLUSTER_NAME] --zone [COMPUTE_ZONE] You will need to migrate your workloads to the new Node pool, and delete Node pools that use the default service account to complete the remediation. scored: true - id: 6.2.2 text: "Prefer using dedicated GCP Service Accounts and Workload Identity (Not Scored)" type: "manual" remediation: | Using Command Line: gcloud beta container clusters update [CLUSTER_NAME] --zone [CLUSTER_ZONE] \ --identity-namespace=[PROJECT_ID].svc.id.goog Note that existing Node pools are unaffected. New Node pools default to --workload- metadata-from-node=GKE_METADATA_SERVER . Then, modify existing Node pools to enable GKE_METADATA_SERVER: gcloud beta container node-pools update [NODEPOOL_NAME] \ --cluster=[CLUSTER_NAME] --zone [CLUSTER_ZONE] \ --workload-metadata-from-node=GKE_METADATA_SERVER You may also need to modify workloads in order for them to use Workload Identity as described within https://cloud.google.com/kubernetes-engine/docs/how-to/workload- identity. Also consider the effects on the availability of your hosted workloads as Node pools are updated, it may be more appropriate to create new Node Pools. scored: false - id: 6.3 text: "Cloud Key Management Service (Cloud KMS)" checks: - id: 6.3.1 text: "Ensure Kubernetes Secrets are encrypted using keys managed in Cloud KMS (Scored)" type: "manual" remediation: | Using Command Line: To create a key Create a key ring: gcloud kms keyrings create [RING_NAME] \ --location [LOCATION] \ --project [KEY_PROJECT_ID] Create a key: gcloud kms keys create [KEY_NAME] \ --location [LOCATION] \ --keyring [RING_NAME] \ --purpose encryption \ --project [KEY_PROJECT_ID] Grant the Kubernetes Engine Service Agent service account the Cloud KMS CryptoKey Encrypter/Decrypter role: gcloud kms keys add-iam-policy-binding [KEY_NAME] \ --location [LOCATION] \ --keyring [RING_NAME] \ --member serviceAccount:[SERVICE_ACCOUNT_NAME] \ --role roles/cloudkms.cryptoKeyEncrypterDecrypter \ --project [KEY_PROJECT_ID] To create a new cluster with Application-layer Secrets Encryption: gcloud container clusters create [CLUSTER_NAME] \ --cluster-version=latest \ --zone [ZONE] \ --database-encryption-key projects/[KEY_PROJECT_ID]/locations/[LOCATION]/keyRings/[RING_NAME]/cryptoKey s/[KEY_NAME] \ --project [CLUSTER_PROJECT_ID] To enable on an existing cluster: gcloud container clusters update [CLUSTER_NAME] \ --zone [ZONE] \ --database-encryption-key projects/[KEY_PROJECT_ID]/locations/[LOCATION]/keyRings/[RING_NAME]/cryptoKey s/[KEY_NAME] \ --project [CLUSTER_PROJECT_ID] scored: true - id: 6.4 text: "Node Metadata" checks: - id: 6.4.1 text: "Ensure legacy Compute Engine instance metadata APIs are Disabled (Scored)" type: "manual" remediation: | Using Command Line: To update an existing cluster, create a new Node pool with the legacy GCE metadata endpoint disabled: gcloud container node-pools create [POOL_NAME] \ --metadata disable-legacy-endpoints=true \ --cluster [CLUSTER_NAME] \ --zone [COMPUTE_ZONE] You will need to migrate workloads from any existing non-conforming Node pools, to the new Node pool, then delete non-conforming Node pools to complete the remediation. scored: true - id: 6.4.2 text: "Ensure the GKE Metadata Server is Enabled (Not Scored)" type: "manual" remediation: | Using Command Line: gcloud beta container clusters update [CLUSTER_NAME] \ --identity-namespace=[PROJECT_ID].svc.id.goog Note that existing Node pools are unaffected. New Node pools default to --workload- metadata-from-node=GKE_METADATA_SERVER . To modify an existing Node pool to enable GKE Metadata Server: gcloud beta container node-pools update [NODEPOOL_NAME] \ --cluster=[CLUSTER_NAME] \ --workload-metadata-from-node=GKE_METADATA_SERVER You may also need to modify workloads in order for them to use Workload Identity as described within https://cloud.google.com/kubernetes-engine/docs/how-to/workload- identity. scored: false - id: 6.5 text: "Node Configuration and Maintenance" checks: - id: 6.5.1 text: "Ensure Container-Optimized OS (COS) is used for GKE node images (Scored)" type: "manual" remediation: | Using Command Line: To set the node image to cos for an existing cluster's Node pool: gcloud container clusters upgrade [CLUSTER_NAME]\ --image-type cos \ --zone [COMPUTE_ZONE] --node-pool [POOL_NAME] scored: true - id: 6.5.2 text: "Ensure Node Auto-Repair is enabled for GKE nodes (Scored)" type: "manual" remediation: | Using Command Line: To enable node auto-repair for an existing cluster with Node pool, run the following command: gcloud container node-pools update [POOL_NAME] \ --cluster [CLUSTER_NAME] --zone [COMPUTE_ZONE] \ --enable-autorepair scored: true - id: 6.5.3 text: "Ensure Node Auto-Upgrade is enabled for GKE nodes (Scored)" type: "manual" remediation: | Using Command Line: To enable node auto-upgrade for an existing cluster's Node pool, run the following command: gcloud container node-pools update [NODE_POOL] \ --cluster [CLUSTER_NAME] --zone [COMPUTE_ZONE] \ --enable-autoupgrade scored: true - id: 6.5.4 text: "Automate GKE version management using Release Channels (Not Scored)" type: "manual" remediation: | Using Command Line: Create a new cluster by running the following command: gcloud beta container clusters create [CLUSTER_NAME] \ --zone [COMPUTE_ZONE] \ --release-channel [RELEASE_CHANNEL] where [RELEASE_CHANNEL] is stable or regular according to your needs. scored: false - id: 6.5.5 text: "Ensure Shielded GKE Nodes are Enabled (Not Scored)" type: "manual" remediation: | Using Command Line: To create a Node pool within the cluster with Integrity Monitoring enabled, run the following command: gcloud beta container node-pools create [NODEPOOL_NAME] \ --cluster [CLUSTER_NAME] --zone [COMPUTE_ZONE] \ --shielded-integrity-monitoring You will also need to migrate workloads from existing non-conforming Node pools to the newly created Node pool, then delete the non-conforming pools. scored: false - id: 6.5.6 text: "Ensure Shielded GKE Nodes are Enabled (Not Scored)" type: "manual" remediation: | Using Command Line: To migrate an existing cluster, you will need to specify the --enable-shielded-nodes flag on a cluster update command: gcloud beta container clusters update [CLUSTER_NAME] \ --zone [CLUSTER_ZONE] \ --enable-shielded-nodes scored: false - id: 6.5.7 text: "Ensure Secure Boot for Shielded GKE Nodes is Enabled (Not Scored)" type: "manual" remediation: | Using Command Line: To create a Node pool within the cluster with Secure Boot enabled, run the following command: gcloud beta container node-pools create [NODEPOOL_NAME] \ --cluster [CLUSTER_NAME] --zone [COMPUTE_ZONE] \ --shielded-secure-boot You will also need to migrate workloads from existing non-conforming Node pools to the newly created Node pool, then delete the non-conforming pools. scored: false - id: 6.6 text: "Cluster Networking" checks: - id: 6.6.1 text: "Enable VPC Flow Logs and Intranode Visibility (Not Scored)" type: "manual" remediation: | Using Command Line: To enable intranode visibility on an existing cluster, run the following command: gcloud beta container clusters update [CLUSTER_NAME] \ --enable-intra-node-visibility scored: false - id: 6.6.2 text: "Ensure use of VPC-native clusters (Scored)" type: "manual" remediation: | Using Command Line: To enable Alias IP on a new cluster, run the following command: gcloud container clusters create [CLUSTER_NAME] \ --zone [COMPUTE_ZONE] \ --enable-ip-alias scored: true - id: 6.6.3 text: "Ensure Master Authorized Networks is Enabled (Scored)" type: "manual" remediation: | Using Command Line: To check Master Authorized Networks status for an existing cluster, run the following command; gcloud container clusters describe [CLUSTER_NAME] \ --zone [COMPUTE_ZONE] \ --format json | jq '.masterAuthorizedNetworksConfig' The output should return { "enabled": true } if Master Authorized Networks is enabled. If Master Authorized Networks is disabled, the above command will return null ( { } ). scored: true - id: 6.6.4 text: "Ensure clusters are created with Private Endpoint Enabled and Public Access Disabled (Scored)" type: "manual" remediation: | Using Command Line: Create a cluster with a Private Endpoint enabled and Public Access disabled by including the --enable-private-endpoint flag within the cluster create command: gcloud container clusters create [CLUSTER_NAME] \ --enable-private-endpoint Setting this flag also requires the setting of --enable-private-nodes , --enable-ip-alias and --master-ipv4-cidr=[MASTER_CIDR_RANGE] . scored: true - id: 6.6.5 text: "Ensure clusters are created with Private Nodes (Scored)" type: "manual" remediation: | Using Command Line: To create a cluster with Private Nodes enabled, include the --enable-private-nodes flag within the cluster create command: gcloud container clusters create [CLUSTER_NAME] \ --enable-private-nodes Setting this flag also requires the setting of --enable-ip-alias and --master-ipv4- cidr=[MASTER_CIDR_RANGE] . scored: true - id: 6.6.6 text: "Consider firewalling GKE worker nodes (Not Scored)" type: "manual" remediation: | Using Command Line: Use the following command to generate firewall rules, setting the variables as appropriate. You may want to use the target [TAG] and [SERVICE_ACCOUNT] previously identified. gcloud compute firewall-rules create FIREWALL_RULE_NAME \ --network [NETWORK] \ --priority [PRIORITY] \ --direction [DIRECTION] \ --action [ACTION] \ --target-tags [TAG] \ --target-service-accounts [SERVICE_ACCOUNT] \ --source-ranges [SOURCE_CIDR-RANGE] \ --source-tags [SOURCE_TAGS] \ --source-service-accounts=[SOURCE_SERVICE_ACCOUNT] \ --destination-ranges [DESTINATION_CIDR_RANGE] \ --rules [RULES] scored: false - id: 6.6.7 text: "Ensure Network Policy is Enabled and set as appropriate (Not Scored)" type: "manual" remediation: | Using Command Line: To enable Network Policy for an existing cluster, firstly enable the Network Policy add-on: gcloud container clusters update [CLUSTER_NAME] \ --zone [COMPUTE_ZONE] \ --update-addons NetworkPolicy=ENABLED Then, enable Network Policy: gcloud container clusters update [CLUSTER_NAME] \ --zone [COMPUTE_ZONE] \ --enable-network-policy scored: false - id: 6.6.8 text: "Ensure use of Google-managed SSL Certificates (Not Scored)" type: "manual" remediation: | If services of type:LoadBalancer are discovered, consider replacing the Service with an Ingress. To configure the Ingress and use Google-managed SSL certificates, follow the instructions as listed at https://cloud.google.com/kubernetes-engine/docs/how-to/managed-certs. scored: false - id: 6.7 text: "Logging" checks: - id: 6.7.1 text: "Ensure Stackdriver Kubernetes Logging and Monitoring is Enabled (Scored)" type: "manual" remediation: | Using Command Line: STACKDRIVER KUBERNETES ENGINE MONITORING SUPPORT (PREFERRED): To enable Stackdriver Kubernetes Engine Monitoring for an existing cluster, run the following command: gcloud container clusters update [CLUSTER_NAME] \ --zone [COMPUTE_ZONE] \ --enable-stackdriver-kubernetes LEGACY STACKDRIVER SUPPORT: Both Logging and Monitoring support must be enabled. To enable Legacy Stackdriver Logging for an existing cluster, run the following command: gcloud container clusters update [CLUSTER_NAME] --zone [COMPUTE_ZONE] \ --logging-service logging.googleapis.com To enable Legacy Stackdriver Monitoring for an existing cluster, run the following command: gcloud container clusters update [CLUSTER_NAME] --zone [COMPUTE_ZONE] \ --monitoring-service monitoring.googleapis.com scored: true - id: 6.7.2 text: "Enable Linux auditd logging (Not Scored)" type: "manual" remediation: | Using Command Line: Download the example manifests: curl https://raw.githubusercontent.com/GoogleCloudPlatform/k8s-node-tools/master/os-audit/cos-auditd-logging.yaml \ > cos-auditd-logging.yaml Edit the example manifests if needed. Then, deploy them: kubectl apply -f cos-auditd-logging.yaml Verify that the logging Pods have started. If you defined a different Namespace in your manifests, replace cos-auditd with the name of the namespace you're using: kubectl get pods --namespace=cos-auditd scored: false - id: 6.8 text: "Authentication and Authorization" checks: - id: 6.8.1 text: "Ensure Basic Authentication using static passwords is Disabled (Scored)" type: "manual" remediation: | Using Command Line: To update an existing cluster and disable Basic Authentication by removing the static password: gcloud container clusters update [CLUSTER_NAME] \ --no-enable-basic-auth scored: true - id: 6.8.2 text: "Ensure authentication using Client Certificates is Disabled (Scored)" type: "manual" remediation: | Using Command Line: Create a new cluster without a Client Certificate: gcloud container clusters create [CLUSTER_NAME] \ --no-issue-client-certificate scored: true - id: 6.8.3 text: "Manage Kubernetes RBAC users with Google Groups for GKE (Not Scored)" type: "manual" remediation: | Using Command Line: Follow the G Suite Groups instructions at https://cloud.google.com/kubernetes- engine/docs/how-to/role-based-access-control#google-groups-for-gke. Then, create a cluster with gcloud beta container clusters create my-cluster \ --security-group="gke-security-groups@[yourdomain.com]" Finally create Roles, ClusterRoles, RoleBindings, and ClusterRoleBindings that reference your G Suite Groups. scored: false - id: 6.8.4 text: "Ensure Legacy Authorization (ABAC) is Disabled (Scored)" type: "manual" remediation: | Using Command Line: To disable Legacy Authorization for an existing cluster, run the following command: gcloud container clusters update [CLUSTER_NAME] \ --zone [COMPUTE_ZONE] \ --no-enable-legacy-authorization scored: true - id: 6.9 text: "Storage" checks: - id: 6.9.1 text: "Enable Customer-Managed Encryption Keys (CMEK) for GKE Persistent Disks (PD) (Not Scored)" type: "manual" remediation: | Using Command Line: FOR NODE BOOT DISKS: Create a new node pool using customer-managed encryption keys for the node boot disk, of [DISK_TYPE] either pd-standard or pd-ssd : gcloud beta container node-pools create [CLUSTER_NAME] \ --disk-type [DISK_TYPE] \ --boot-disk-kms-key \ projects/[KEY_PROJECT_ID]/locations/[LOCATION]/keyRings/[RING_NAME]/cryptoKeys/[KEY_NAME] Create a cluster using customer-managed encryption keys for the node boot disk, of [DISK_TYPE] either pd-standard or pd-ssd : gcloud beta container clusters create [CLUSTER_NAME] \ --disk-type [DISK_TYPE] \ --boot-disk-kms-key \ projects/[KEY_PROJECT_ID]/locations/[LOCATION]/keyRings/[RING_NAME]/cryptoKeys/[KEY_NAME] FOR ATTACHED DISKS: Follow the instructions detailed at https://cloud.google.com/kubernetes- engine/docs/how-to/using-cmek. scored: false - id: 6.10 text: "Other Cluster Configurations" checks: - id: 6.10.1 text: "Ensure Kubernetes Web UI is Disabled (Scored)" type: "manual" remediation: | Using Command Line: To disable the Kubernetes Dashboard on an existing cluster, run the following command: gcloud container clusters update [CLUSTER_NAME] \ --zone [ZONE] \ --update-addons=KubernetesDashboard=DISABLED scored: true - id: 6.10.2 text: "Ensure that Alpha clusters are not used for production workloads (Scored)" type: "manual" remediation: | Using Command Line: Upon creating a new cluster gcloud container clusters create [CLUSTER_NAME] \ --zone [COMPUTE_ZONE] Do not use the --enable-kubernetes-alpha argument. scored: true - id: 6.10.3 text: "Ensure Pod Security Policy is Enabled and set as appropriate (Not Scored)" type: "manual" remediation: | Using Command Line: To enable Pod Security Policy for an existing cluster, run the following command: gcloud beta container clusters update [CLUSTER_NAME] \ --zone [COMPUTE_ZONE] \ --enable-pod-security-policy scored: false - id: 6.10.4 text: "Consider GKE Sandbox for running untrusted workloads (Not Scored)" type: "manual" remediation: | Using Command Line: To enable GKE Sandbox on an existing cluster, a new Node pool must be created. gcloud container node-pools create [NODE_POOL_NAME] \ --zone=[COMPUTE-ZONE] \ --cluster=[CLUSTER_NAME] \ --image-type=cos_containerd \ --sandbox type=gvisor scored: false - id: 6.10.5 text: "Ensure use of Binary Authorization (Scored)" type: "manual" remediation: | Using Command Line: Firstly, update the cluster to enable Binary Authorization: gcloud container cluster update [CLUSTER_NAME] \ --zone [COMPUTE-ZONE] \ --enable-binauthz Create a Binary Authorization Policy using the Binary Authorization Policy Reference (https://cloud.google.com/binary-authorization/docs/policy-yaml-reference) for guidance. Import the policy file into Binary Authorization: gcloud container binauthz policy import [YAML_POLICY] scored: true - id: 6.10.6 text: "Enable Cloud Security Command Center (Cloud SCC) (Not Scored)" type: "manual" remediation: | Using Command Line: Follow the instructions at https://cloud.google.com/security-command- center/docs/quickstart-scc-setup. scored: false ================================================ FILE: cfg/gke-1.0/master.yaml ================================================ --- controls: version: "gke-1.0" id: 1 text: "Control Plane Components" type: "master" groups: - id: 1.1 text: "Master Node Configuration Files " type: skip checks: - id: 1.1.1 text: "Ensure that the API server pod specification file permissions are set to 644 or more restrictive (Not Scored)" remediation: "This control cannot be modified in GKE." scored: false - id: 1.1.2 text: "Ensure that the API server pod specification file ownership is set to root:root (Not Scored)" remediation: "This control cannot be modified in GKE." scored: false - id: 1.1.3 text: "Ensure that the controller manager pod specification file permissions are set to 644 or more restrictive (Not Scored)" remediation: "This control cannot be modified in GKE." scored: false - id: 1.1.4 text: "Ensure that the controller manager pod specification file ownership is set to root:root (Not Scored)" remediation: "This control cannot be modified in GKE." scored: false - id: 1.1.5 text: "Ensure that the scheduler pod specification file permissions are set to 644 or more restrictive (Not Scored)" remediation: "This control cannot be modified in GKE." scored: false - id: 1.1.6 text: "Ensure that the scheduler pod specification file ownership is set to root:root (Not Scored)" remediation: "This control cannot be modified in GKE." scored: false - id: 1.1.7 text: "Ensure that the etcd pod specification file permissions are set to 644 or more restrictive (Not Scored)" remediation: "This control cannot be modified in GKE." scored: false - id: 1.1.8 text: "Ensure that the etcd pod specification file ownership is set to root:root (Not Scored)" remediation: "This control cannot be modified in GKE." scored: false - id: 1.1.9 text: "Ensure that the Container Network Interface file permissions are set to 644 or more restrictive (Not Scored)" remediation: "This control cannot be modified in GKE." scored: false - id: 1.1.10 text: "Ensure that the Container Network Interface file ownership is set to root:root (Not Scored)" remediation: "This control cannot be modified in GKE." scored: false - id: 1.1.11 text: "Ensure that the etcd data directory permissions are set to 700 or more restrictive (Not Scored)" remediation: "This control cannot be modified in GKE." scored: false - id: 1.1.12 text: "Ensure that the etcd data directory ownership is set to etcd:etcd (Not Scored)" remediation: "This control cannot be modified in GKE." scored: false - id: 1.1.13 text: "Ensure that the admin.conf file permissions are set to 644 or more restrictive (Not Scored)" remediation: "This control cannot be modified in GKE." scored: false - id: 1.1.14 text: "Ensure that the admin.conf file ownership is set to root:root (Not Scored) " remediation: "This control cannot be modified in GKE." scored: false - id: 1.1.15 text: "Ensure that the scheduler.conf file permissions are set to 644 or more restrictive (Not Scored)" remediation: "This control cannot be modified in GKE." scored: true - id: 1.1.16 text: "Ensure that the scheduler.conf file ownership is set to root:root (Not Scored)" remediation: "This control cannot be modified in GKE." scored: false - id: 1.1.17 text: "Ensure that the controller-manager.conf file permissions are set to 644 or more restrictive (Not Scored)" remediation: "This control cannot be modified in GKE." scored: false - id: 1.1.18 text: "Ensure that the controller-manager.conf file ownership is set to root:root (Not Scored)" remediation: "This control cannot be modified in GKE." scored: false - id: 1.1.19 text: "Ensure that the Kubernetes PKI directory and file ownership is set to root:root (Not Scored)" remediation: "This control cannot be modified in GKE." scored: false - id: 1.1.20 text: "Ensure that the Kubernetes PKI certificate file permissions are set to 644 or more restrictive (Not Scored)" remediation: "This control cannot be modified in GKE." scored: false - id: 1.1.21 text: "Ensure that the Kubernetes PKI key file permissions are set to 600 (Not Scored)" remediation: "This control cannot be modified in GKE." scored: false - id: 1.2 text: "API Server" type: skip checks: - id: 1.2.1 text: "Ensure that the --anonymous-auth argument is set to false (Not Scored)" remediation: "This control cannot be modified in GKE." scored: false - id: 1.2.2 text: "Ensure that the --basic-auth-file argument is not set (Not Scored)" remediation: | Although the use of the --basic-auth-file argument cannot be audited on GKE, you can remediate the use of basic authentication. See Recommendation 6.8.1. scored: false - id: 1.2.3 text: "Ensure that the --token-auth-file parameter is not set (Not Scored)" remediation: "This control cannot be modified in GKE." scored: false - id: 1.2.4 text: "Ensure that the --kubelet-https argument is set to true (Not Scored)" remediation: "This control cannot be modified in GKE." scored: false - id: 1.2.5 text: "Ensure that the --kubelet-client-certificate and --kubelet-client-key arguments are set as appropriate (Not Scored)" remediation: "This control cannot be modified in GKE." scored: false - id: 1.2.6 text: "Ensure that the --kubelet-certificate-authority argument is set as appropriate (Not Scored)" remediation: "This control cannot be modified in GKE." scored: false - id: 1.2.7 text: "Ensure that the --authorization-mode argument is not set to AlwaysAllow (Not Scored)" remediation: "This control cannot be modified in GKE." scored: false - id: 1.2.8 text: "Ensure that the --authorization-mode argument includes Node (Not Scored)" remediation: "This control cannot be modified in GKE." scored: false - id: 1.2.9 text: "Ensure that the --authorization-mode argument includes RBAC (Not Scored)" remediation: "This control cannot be modified in GKE." scored: false - id: 1.2.10 text: "Ensure that the admission control plugin EventRateLimit is set (Not Scored)" remediation: "This control cannot be modified in GKE." scored: false - id: 1.2.11 text: "Ensure that the admission control plugin AlwaysAdmit is not set (Not Scored)" remediation: "This control cannot be modified in GKE." scored: false - id: 1.2.12 text: "Ensure that the admission control plugin AlwaysPullImages is set (Not Scored)" remediation: "This control cannot be modified in GKE." scored: false - id: 1.2.13 text: "Ensure that the admission control plugin SecurityContextDeny is set if PodSecurityPolicy is not used (Not Scored)" remediation: "This control cannot be modified in GKE." scored: false - id: 1.2.14 text: "Ensure that the admission control plugin ServiceAccount is set (Not Scored)" remediation: "This control cannot be modified in GKE." scored: false - id: 1.2.15 text: "Ensure that the admission control plugin NamespaceLifecycle is set (Not Scored)" remediation: "This control cannot be modified in GKE." scored: false - id: 1.2.16 text: "Ensure that the admission control plugin PodSecurityPolicy is set (Not Scored)" remediation: | To verify and remediate the use of Pod Security Policy on GKE, see Recommendation 6.10.3. scored: false - id: 1.2.17 text: "Ensure that the admission control plugin NodeRestriction is set (Not Scored)" remediation: "This control cannot be modified in GKE." scored: false - id: 1.2.18 text: "Ensure that the --insecure-bind-address argument is not set (Not Scored)" remediation: "This control cannot be modified in GKE." scored: false - id: 1.2.19 text: "Ensure that the --insecure-port argument is set to 0 (Not Scored)" remediation: "This control cannot be modified in GKE." scored: false - id: 1.2.20 text: "Ensure that the --secure-port argument is not set to 0 (Not Scored)" remediation: "This control cannot be modified in GKE." scored: false - id: 1.2.21 text: "Ensure that the --profiling argument is set to false (Not Scored)" remediation: "This control cannot be modified in GKE." scored: false - id: 1.2.22 text: "Ensure that the --audit-log-path argument is set (Not Scored)" remediation: "This control cannot be modified in GKE." scored: false - id: 1.2.23 text: "Ensure that the --audit-log-maxage argument is set to 30 or as appropriate (Not Scored)" remediation: "This control cannot be modified in GKE." scored: false - id: 1.2.24 text: "Ensure that the --audit-log-maxbackup argument is set to 10 or as appropriate (Not Scored)" remediation: "This control cannot be modified in GKE." scored: false - id: 1.2.25 text: "Ensure that the --audit-log-maxsize argument is set to 100 or as appropriate (Not Scored)" remediation: "This control cannot be modified in GKE." scored: false - id: 1.2.26 text: "Ensure that the --request-timeout argument is set as appropriate (Not Scored)" remediation: "This control cannot be modified in GKE." scored: false - id: 1.2.27 text: "Ensure that the --service-account-lookup argument is set to true (Not Scored)" remediation: "This control cannot be modified in GKE." scored: false - id: 1.2.28 text: "Ensure that the --service-account-key-file argument is set as appropriate (Not Scored)" remediation: "This control cannot be modified in GKE." scored: false - id: 1.2.29 text: "Ensure that the --etcd-certfile and --etcd-keyfile arguments are set as appropriate (Not Scored)" remediation: "This control cannot be modified in GKE." scored: false - id: 1.2.30 text: "Ensure that the --tls-cert-file and --tls-private-key-file arguments are set as appropriate (Not Scored)" remediation: "This control cannot be modified in GKE." scored: false - id: 1.2.31 text: "Ensure that the --client-ca-file argument is set as appropriate (Not Scored)" remediation: "This control cannot be modified in GKE." scored: false - id: 1.2.32 text: "Ensure that the --etcd-cafile argument is set as appropriate (Not Scored)" remediation: "This control cannot be modified in GKE." scored: false - id: 1.2.33 text: "Ensure that the --encryption-provider-config argument is set as appropriate (Not Scored)" remediation: | To verify and remediate the use of secret encryption on GKE, see Recommendation 6.3.1. scored: false - id: 1.2.34 text: "Ensure that encryption providers are appropriately configured (Not Scored)" remediation: | To verify and remediate the use of secret encryption on GKE, see Recommendation 6.3.1. scored: false - id: 1.2.35 text: "Ensure that the API Server only makes use of Strong Cryptographic Ciphers (Not Scored)" remediation: "This control cannot be modified in GKE." scored: false - id: 1.3 text: "Controller Manager" type: skip checks: - id: 1.3.1 text: "Ensure that the --terminated-pod-gc-threshold argument is set as appropriate (Not Scored)" remediation: "This control cannot be modified in GKE." scored: false - id: 1.3.2 text: "Ensure that the --profiling argument is set to false (Not Scored)" remediation: "This control cannot be modified in GKE." scored: false - id: 1.3.3 text: "Ensure that the --use-service-account-credentials argument is set to true (Not Scored)" remediation: "This control cannot be modified in GKE." scored: false - id: 1.3.4 text: "Ensure that the --service-account-private-key-file argument is set as appropriate (Not Scored)" remediation: "This control cannot be modified in GKE." scored: false - id: 1.3.5 text: "Ensure that the --root-ca-file argument is set as appropriate (Not Scored)" remediation: "This control cannot be modified in GKE." scored: false - id: 1.3.6 text: "Ensure that the RotateKubeletServerCertificate argument is set to true (Not Scored)" remediation: "This control cannot be modified in GKE." scored: false - id: 1.3.7 text: "Ensure that the --bind-address argument is set to 127.0.0.1 (Not Scored)" remediation: "This control cannot be modified in GKE." scored: false - id: 1.4 text: "Scheduler" type: skip checks: - id: 1.4.1 text: "Ensure that the --profiling argument is set to false (Not Scored)" remediation: "This control cannot be modified in GKE." scored: false - id: 1.4.2 text: "Ensure that the --bind-address argument is set to 127.0.0.1 (Not Scored) " remediation: "This control cannot be modified in GKE." scored: false ================================================ FILE: cfg/gke-1.0/node.yaml ================================================ --- controls: version: "gke-1.0" id: 4 text: "Worker Node Security Configuration" type: "node" groups: - id: 4.1 text: "Worker Node Configuration Files" checks: - id: 4.1.1 text: "Ensure that the kubelet service file permissions are set to 644 or more restrictive (Not Scored)" type: skip remediation: "This control cannot be modified in GKE." scored: false - id: 4.1.2 text: "Ensure that the kubelet service file ownership is set to root:root (Not Scored)" type: skip remediation: "This control cannot be modified in GKE." scored: false - id: 4.1.3 text: "Ensure that the proxy kubeconfig file permissions are set to 644 or more restrictive (Scored)" audit: '/bin/sh -c ''if test -e $proxykubeconfig; then stat -c permissions=%a $proxykubeconfig; fi'' ' tests: test_items: - flag: "permissions" compare: op: bitmask value: "644" remediation: | Run the below command (based on the file location on your system) on each worker node. For example, chmod 644 $proxykubeconfig scored: true - id: 4.1.4 text: "Ensure that the proxy kubeconfig file ownership is set to root:root (Scored)" audit: '/bin/sh -c ''if test -e $proxykubeconfig; then stat -c %U:%G $proxykubeconfig; fi'' ' tests: test_items: - flag: root:root remediation: | Run the below command (based on the file location on your system) on each worker node. For example, chown root:root $proxykubeconfig scored: true - id: 4.1.5 text: "Ensure that the kubelet.conf file permissions are set to 644 or more restrictive (Not Scored)" type: skip remediation: "This control cannot be modified in GKE." scored: false - id: 4.1.6 text: "Ensure that the kubelet.conf file ownership is set to root:root (Not Scored)" type: skip remediation: "This control cannot be modified in GKE." scored: false - id: 4.1.7 text: "Ensure that the certificate authorities file permissions are set to 644 or more restrictive (Not Scored)" type: skip remediation: "This control cannot be modified in GKE." scored: false - id: 4.1.8 text: "Ensure that the client certificate authorities file ownership is set to root:root (Not Scored)" type: skip remediation: "This control cannot be modified in GKE." scored: false - id: 4.1.9 text: "Ensure that the kubelet configuration file has permissions set to 644 or more restrictive (Scored)" audit: '/bin/sh -c ''if test -e $kubeletconf; then stat -c permissions=%a $kubeletconf; fi'' ' tests: test_items: - flag: "permissions" set: true compare: op: bitmask value: "644" remediation: | Run the following command (using the config file location identified in the Audit step) chmod 644 $kubeletconf scored: true - id: 4.1.10 text: "Ensure that the kubelet configuration file ownership is set to root:root (Scored)" audit: '/bin/sh -c ''if test -e $kubeletconf; then stat -c %U:%G $kubeletconf; fi'' ' tests: test_items: - flag: root:root remediation: | Run the following command (using the config file location identified in the Audit step) chown root:root $kubeletconf scored: true - id: 4.2 text: "Kubelet" checks: - id: 4.2.1 text: "Ensure that the --anonymous-auth argument is set to false (Scored)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: "--anonymous-auth" path: '{.authentication.anonymous.enabled}' compare: op: eq value: false remediation: | If using a Kubelet config file, edit the file to set authentication: anonymous: enabled to false. If using executable arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_SYSTEM_PODS_ARGS variable. --anonymous-auth=false Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: true - id: 4.2.2 text: "Ensure that the --authorization-mode argument is not set to AlwaysAllow (Scored)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --authorization-mode path: '{.authorization.mode}' compare: op: nothave value: AlwaysAllow remediation: | If using a Kubelet config file, edit the file to set authorization: mode to Webhook. If using executable arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_AUTHZ_ARGS variable. --authorization-mode=Webhook Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: true - id: 4.2.3 text: "Ensure that the --client-ca-file argument is set as appropriate (Scored)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --client-ca-file path: '{.authentication.x509.clientCAFile}' set: true remediation: | If using a Kubelet config file, edit the file to set authentication: x509: clientCAFile to the location of the client CA file. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_AUTHZ_ARGS variable. --client-ca-file= Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: true - id: 4.2.4 text: "Ensure that the --read-only-port argument is set to 0 (Scored)" type: skip remediation: "This control cannot be modified in GKE." scored: true - id: 4.2.5 text: "Ensure that the --streaming-connection-idle-timeout argument is not set to 0 (Scored)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --streaming-connection-idle-timeout path: '{.streamingConnectionIdleTimeout}' compare: op: noteq value: 0 - flag: --streaming-connection-idle-timeout path: '{.streamingConnectionIdleTimeout}' set: false bin_op: or remediation: | If using a Kubelet config file, edit the file to set streamingConnectionIdleTimeout to a value other than 0. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_SYSTEM_PODS_ARGS variable. --streaming-connection-idle-timeout=5m Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: true - id: 4.2.6 text: "Ensure that the --protect-kernel-defaults argument is set to true (Scored)" type: skip remediation: "This control cannot be modified in GKE." - id: 4.2.7 text: "Ensure that the --make-iptables-util-chains argument is set to true (Scored) " audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --make-iptables-util-chains path: '{.makeIPTablesUtilChains}' compare: op: eq value: true - flag: --make-iptables-util-chains path: '{.makeIPTablesUtilChains}' set: false bin_op: or remediation: | If using a Kubelet config file, edit the file to set makeIPTablesUtilChains: true. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and remove the --make-iptables-util-chains argument from the KUBELET_SYSTEM_PODS_ARGS variable. Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: true - id: 4.2.8 text: "Ensure that the --hostname-override argument is not set (Scored)" audit: "/bin/ps -fC $kubeletbin " tests: test_items: - flag: --hostname-override set: false remediation: | Edit the kubelet service file $kubeletsvc on each worker node and remove the --hostname-override argument from the KUBELET_SYSTEM_PODS_ARGS variable. Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: true - id: 4.2.9 text: "Ensure that the --event-qps argument is set to 0 or a level which ensures appropriate event capture (Scored)" type: skip remediation: "This control cannot be modified in GKE." - id: 4.2.10 text: "Ensure that the --tls-cert-file and --tls-private-key-file arguments are set as appropriate (Scored)" type: skip remediation: "This control cannot be modified in GKE." - id: 4.2.11 text: "Ensure that the --rotate-certificates argument is not set to false (Scored)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --rotate-certificates path: '{.rotateCertificates}' compare: op: eq value: true - flag: --rotate-certificates path: '{.rotateCertificates}' set: false bin_op: or remediation: | If using a Kubelet config file, edit the file to add the line rotateCertificates: true or remove it altogether to use the default value. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and remove --rotate-certificates=false argument from the KUBELET_CERTIFICATE_ARGS variable. Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: true - id: 4.2.12 text: "Ensure that the RotateKubeletServerCertificate argument is set to true (Scored)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: RotateKubeletServerCertificate path: '{.featureGates.RotateKubeletServerCertificate}' compare: op: eq value: true remediation: | Edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_CERTIFICATE_ARGS variable. --feature-gates=RotateKubeletServerCertificate=true Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: true - id: 4.2.13 text: "Ensure that the Kubelet only makes use of Strong Cryptographic Ciphers (Not Scored)" type: skip remediation: "This control cannot be modified in GKE." scored: false ================================================ FILE: cfg/gke-1.0/policies.yaml ================================================ --- controls: version: "gke-1.0" id: 5 text: "Kubernetes Policies" type: "policies" groups: - id: 5.1 text: "RBAC and Service Accounts" checks: - id: 5.1.1 text: "Ensure that the cluster-admin role is only used where required (Not Scored)" type: "manual" remediation: | Identify all clusterrolebindings to the cluster-admin role. Check if they are used and if they need this role or if they could use a role with fewer privileges. Where possible, first bind users to a lower privileged role and then remove the clusterrolebinding to the cluster-admin role : kubectl delete clusterrolebinding [name] scored: false - id: 5.1.2 text: "Minimize access to secrets (Not Scored)" type: "manual" remediation: | Where possible, remove get, list and watch access to secret objects in the cluster. scored: false - id: 5.1.3 text: "Minimize wildcard use in Roles and ClusterRoles (Not Scored)" type: "manual" remediation: | Where possible replace any use of wildcards in clusterroles and roles with specific objects or actions. scored: false - id: 5.1.4 text: "Minimize access to create pods (Not Scored)" type: "manual" remediation: | Where possible, remove create access to pod objects in the cluster. scored: false - id: 5.1.5 text: "Ensure that default service accounts are not actively used. (Scored)" type: "manual" remediation: | Create explicit service accounts wherever a Kubernetes workload requires specific access to the Kubernetes API server. Modify the configuration of each default service account to include this value automountServiceAccountToken: false scored: true - id: 5.1.6 text: "Ensure that Service Account Tokens are only mounted where necessary (Not Scored)" type: "manual" remediation: | Modify the definition of pods and service accounts which do not need to mount service account tokens to disable it. scored: false - id: 5.2 text: "Pod Security Policies" checks: - id: 5.2.1 text: "Minimize the admission of privileged containers (Not Scored)" type: "manual" remediation: | Create a PSP as described in the Kubernetes documentation, ensuring that the .spec.privileged field is omitted or set to false. scored: false - id: 5.2.2 text: "Minimize the admission of containers wishing to share the host process ID namespace (Scored)" type: "manual" remediation: | Create a PSP as described in the Kubernetes documentation, ensuring that the .spec.hostPID field is omitted or set to false. scored: true - id: 5.2.3 text: "Minimize the admission of containers wishing to share the host IPC namespace (Scored)" type: "manual" remediation: | Create a PSP as described in the Kubernetes documentation, ensuring that the .spec.hostIPC field is omitted or set to false. scored: true - id: 5.2.4 text: "Minimize the admission of containers wishing to share the host network namespace (Scored)" type: "manual" remediation: | Create a PSP as described in the Kubernetes documentation, ensuring that the .spec.hostNetwork field is omitted or set to false. scored: true - id: 5.2.5 text: "Minimize the admission of containers with allowPrivilegeEscalation (Scored)" type: "manual" remediation: | Create a PSP as described in the Kubernetes documentation, ensuring that the .spec.allowPrivilegeEscalation field is omitted or set to false. scored: true - id: 5.2.6 text: "Minimize the admission of root containers (Scored)" type: "manual" remediation: | Create a PSP as described in the Kubernetes documentation, ensuring that the .spec.runAsUser.rule is set to either MustRunAsNonRoot or MustRunAs with the range of UIDs not including 0. scored: true - id: 5.2.7 text: "Minimize the admission of containers with the NET_RAW capability (Scored)" type: "manual" remediation: | Create a PSP as described in the Kubernetes documentation, ensuring that the .spec.requiredDropCapabilities is set to include either NET_RAW or ALL. scored: true - id: 5.2.8 text: "Minimize the admission of containers with added capabilities (Scored)" type: "manual" remediation: | Ensure that allowedCapabilities is not present in PSPs for the cluster unless it is set to an empty array. scored: true - id: 5.2.9 text: "Minimize the admission of containers with capabilities assigned (Scored) " type: "manual" remediation: | Review the use of capabilites in applications running on your cluster. Where a namespace contains applications which do not require any Linux capabities to operate consider adding a PSP which forbids the admission of containers which do not drop all capabilities. scored: true - id: 5.3 text: "Network Policies and CNI" checks: - id: 5.3.1 text: "Ensure that the CNI in use supports Network Policies (Not Scored)" type: "manual" remediation: | To use a CNI plugin with Network Policy, enable Network Policy in GKE, and the CNI plugin will be updated. See Recommendation 6.6.7. scored: false - id: 5.3.2 text: "Ensure that all Namespaces have Network Policies defined (Scored)" type: "manual" remediation: | Follow the documentation and create NetworkPolicy objects as you need them. scored: true - id: 5.4 text: "Secrets Management" checks: - id: 5.4.1 text: "Prefer using secrets as files over secrets as environment variables (Not Scored)" type: "manual" remediation: | if possible, rewrite application code to read secrets from mounted secret files, rather than from environment variables. scored: false - id: 5.4.2 text: "Consider external secret storage (Not Scored)" type: "manual" remediation: | Refer to the secrets management options offered by your cloud provider or a third-party secrets management solution. scored: false - id: 5.5 text: "Extensible Admission Control" checks: - id: 5.5.1 text: "Configure Image Provenance using ImagePolicyWebhook admission controller (Not Scored)" type: "manual" remediation: | Follow the Kubernetes documentation and setup image provenance. See also Recommendation 6.10.5 for GKE specifically. scored: false - id: 5.6 text: "General Policies" checks: - id: 5.6.1 text: "Create administrative boundaries between resources using namespaces (Not Scored)" type: "manual" remediation: | Follow the documentation and create namespaces for objects in your deployment as you need them. scored: false - id: 5.6.2 text: "Ensure that the seccomp profile is set to docker/default in your pod definitions (Not Scored)" type: "manual" remediation: | Seccomp is an alpha feature currently. By default, all alpha features are disabled. So, you would need to enable alpha features in the apiserver by passing "--feature- gates=AllAlpha=true" argument. Edit the /etc/kubernetes/apiserver file on the master node and set the KUBE_API_ARGS parameter to "--feature-gates=AllAlpha=true" KUBE_API_ARGS="--feature-gates=AllAlpha=true" Based on your system, restart the kube-apiserver service. For example: systemctl restart kube-apiserver.service Use annotations to enable the docker/default seccomp profile in your pod definitions. An example is as below: apiVersion: v1 kind: Pod metadata: name: trustworthy-pod annotations: seccomp.security.alpha.kubernetes.io/pod: docker/default spec: containers: - name: trustworthy-container image: sotrustworthy:latest scored: false - id: 5.6.3 text: "Apply Security Context to Your Pods and Containers (Not Scored)" type: "manual" remediation: | Follow the Kubernetes documentation and apply security contexts to your pods. For a suggested list of security contexts, you may refer to the CIS Security Benchmark for Docker Containers. scored: false - id: 5.6.4 text: "The default namespace should not be used (Scored)" type: "manual" remediation: | Ensure that namespaces are created to allow for appropriate segregation of Kubernetes resources and that all new resources are created in a specific namespace. scored: true ================================================ FILE: cfg/gke-1.2.0/config.yaml ================================================ --- ## Version-specific settings that override the values in cfg/config.yaml ================================================ FILE: cfg/gke-1.2.0/controlplane.yaml ================================================ --- controls: version: "gke-1.2.0" id: 2 text: "Control Plane Configuration" type: "controlplane" groups: - id: 2.1 text: "Authentication and Authorization" checks: - id: 2.1.1 text: "Client certificate authentication should not be used for users (Manual)" type: "manual" remediation: | Alternative mechanisms provided by Kubernetes such as the use of OIDC should be implemented in place of client certificates. You can remediate the availability of client certificates in your GKE cluster. See Recommendation 6.8.2. scored: false - id: 2.2 text: "Logging" type: skip checks: - id: 2.2.1 text: "Ensure that a minimal audit policy is created (Manual)" type: "manual" remediation: "This control cannot be modified in GKE." scored: false - id: 2.2.2 text: "Ensure that the audit policy covers key security concerns (Manual)" type: "manual" remediation: "This control cannot be modified in GKE." scored: false ================================================ FILE: cfg/gke-1.2.0/managedservices.yaml ================================================ --- controls: version: "gke-1.2.0" id: 5 text: "Managed Services" type: "managedservices" groups: - id: 5.1 text: "Image Registry and Image Scanning" checks: - id: 5.1.1 text: "Ensure Image Vulnerability Scanning using GCR Container Analysis or a third-party provider (Manual)" type: "manual" remediation: | Using Command Line: gcloud services enable containerscanning.googleapis.com scored: false - id: 5.1.2 text: "Minimize user access to GCR (Manual)" type: "manual" remediation: | Using Command Line: To change roles at the GCR bucket level: Firstly, run the following if read permissions are required: gsutil iam ch [TYPE]:[EMAIL-ADDRESS]:objectViewer gs://artifacts.[PROJECT_ID].appspot.com Then remove the excessively privileged role (Storage Admin / Storage Object Admin / Storage Object Creator) using: gsutil iam ch -d [TYPE]:[EMAIL-ADDRESS]:[ROLE] gs://artifacts.[PROJECT_ID].appspot.com where: [TYPE] can be one of the following: o user, if the [EMAIL-ADDRESS] is a Google account o serviceAccount, if [EMAIL-ADDRESS] specifies a Service account [EMAIL-ADDRESS] can be one of the following: o a Google account (for example, someone@example.com) o a Cloud IAM service account To modify roles defined at the project level and subsequently inherited within the GCR bucket, or the Service Account User role, extract the IAM policy file, modify it accordingly and apply it using: gcloud projects set-iam-policy [PROJECT_ID] [POLICY_FILE] scored: false - id: 5.1.3 text: "Minimize cluster access to read-only for GCR (Manual)" type: "manual" remediation: | Using Command Line: For an account explicitly granted to the bucket. First, add read access to the Kubernetes Service Account gsutil iam ch [TYPE]:[EMAIL-ADDRESS]:objectViewer gs://artifacts.[PROJECT_ID].appspot.com where: [TYPE] can be one of the following: o user, if the [EMAIL-ADDRESS] is a Google account o serviceAccount, if [EMAIL-ADDRESS] specifies a Service account [EMAIL-ADDRESS] can be one of the following: o a Google account (for example, someone@example.com) o a Cloud IAM service account Then remove the excessively privileged role (Storage Admin / Storage Object Admin / Storage Object Creator) using: gsutil iam ch -d [TYPE]:[EMAIL-ADDRESS]:[ROLE] gs://artifacts.[PROJECT_ID].appspot.com For an account that inherits access to the GCR Bucket through Project level permissions, modify the Projects IAM policy file accordingly, then upload it using: gcloud projects set-iam-policy [PROJECT_ID] [POLICY_FILE] scored: false - id: 5.1.4 text: "Minimize Container Registries to only those approved (Manual)" type: "manual" remediation: | Using Command Line: First, update the cluster to enable Binary Authorization: gcloud container cluster update [CLUSTER_NAME] \ --enable-binauthz Create a Binary Authorization Policy using the Binary Authorization Policy Reference (https://cloud.google.com/binary-authorization/docs/policy-yaml-reference) for guidance. Import the policy file into Binary Authorization: gcloud container binauthz policy import [YAML_POLICY] scored: false - id: 5.2 text: "Identity and Access Management (IAM)" checks: - id: 5.2.1 text: "Ensure GKE clusters are not running using the Compute Engine default service account (Manual)" type: "manual" remediation: | Using Command Line: Firstly, create a minimally privileged service account: gcloud iam service-accounts create [SA_NAME] \ --display-name "GKE Node Service Account" export NODE_SA_EMAIL=`gcloud iam service-accounts list \ --format='value(email)' \ --filter='displayName:GKE Node Service Account'` Grant the following roles to the service account: export PROJECT_ID=`gcloud config get-value project` gcloud projects add-iam-policy-binding $PROJECT_ID \ --member serviceAccount:$NODE_SA_EMAIL \ --role roles/monitoring.metricWriter gcloud projects add-iam-policy-binding $PROJECT_ID \ --member serviceAccount:$NODE_SA_EMAIL \ --role roles/monitoring.viewer gcloud projects add-iam-policy-binding $PROJECT_ID \ --member serviceAccount:$NODE_SA_EMAIL \ --role roles/logging.logWriter To create a new Node pool using the Service account, run the following command: gcloud container node-pools create [NODE_POOL] \ --service-account=[SA_NAME]@[PROJECT_ID].iam.gserviceaccount.com \ --cluster=[CLUSTER_NAME] --zone [COMPUTE_ZONE] You will need to migrate your workloads to the new Node pool, and delete Node pools that use the default service account to complete the remediation. scored: false - id: 5.2.2 text: "Prefer using dedicated GCP Service Accounts and Workload Identity (Manual)" type: "manual" remediation: | Using Command Line: gcloud beta container clusters update [CLUSTER_NAME] --zone [CLUSTER_ZONE] \ --identity-namespace=[PROJECT_ID].svc.id.goog Note that existing Node pools are unaffected. New Node pools default to --workload- metadata-from-node=GKE_METADATA_SERVER . Then, modify existing Node pools to enable GKE_METADATA_SERVER: gcloud beta container node-pools update [NODEPOOL_NAME] \ --cluster=[CLUSTER_NAME] --zone [CLUSTER_ZONE] \ --workload-metadata-from-node=GKE_METADATA_SERVER You may also need to modify workloads in order for them to use Workload Identity as described within https://cloud.google.com/kubernetes-engine/docs/how-to/workload- identity. Also consider the effects on the availability of your hosted workloads as Node pools are updated, it may be more appropriate to create new Node Pools. scored: false - id: 5.3 text: "Cloud Key Management Service (Cloud KMS)" checks: - id: 5.3.1 text: "Ensure Kubernetes Secrets are encrypted using keys managed in Cloud KMS (Manual)" type: "manual" remediation: | Using Command Line: To create a key Create a key ring: gcloud kms keyrings create [RING_NAME] \ --location [LOCATION] \ --project [KEY_PROJECT_ID] Create a key: gcloud kms keys create [KEY_NAME] \ --location [LOCATION] \ --keyring [RING_NAME] \ --purpose encryption \ --project [KEY_PROJECT_ID] Grant the Kubernetes Engine Service Agent service account the Cloud KMS CryptoKey Encrypter/Decrypter role: gcloud kms keys add-iam-policy-binding [KEY_NAME] \ --location [LOCATION] \ --keyring [RING_NAME] \ --member serviceAccount:[SERVICE_ACCOUNT_NAME] \ --role roles/cloudkms.cryptoKeyEncrypterDecrypter \ --project [KEY_PROJECT_ID] To create a new cluster with Application-layer Secrets Encryption: gcloud container clusters create [CLUSTER_NAME] \ --cluster-version=latest \ --zone [ZONE] \ --database-encryption-key projects/[KEY_PROJECT_ID]/locations/[LOCATION]/keyRings/[RING_NAME]/cryptoKey s/[KEY_NAME] \ --project [CLUSTER_PROJECT_ID] To enable on an existing cluster: gcloud container clusters update [CLUSTER_NAME] \ --zone [ZONE] \ --database-encryption-key projects/[KEY_PROJECT_ID]/locations/[LOCATION]/keyRings/[RING_NAME]/cryptoKey s/[KEY_NAME] \ --project [CLUSTER_PROJECT_ID] scored: false - id: 5.4 text: "Node Metadata" checks: - id: 5.4.1 text: "Ensure legacy Compute Engine instance metadata APIs are Disabled (Automated)" type: "manual" remediation: | Using Command Line: To update an existing cluster, create a new Node pool with the legacy GCE metadata endpoint disabled: gcloud container node-pools create [POOL_NAME] \ --metadata disable-legacy-endpoints=true \ --cluster [CLUSTER_NAME] \ --zone [COMPUTE_ZONE] You will need to migrate workloads from any existing non-conforming Node pools, to the new Node pool, then delete non-conforming Node pools to complete the remediation. scored: false - id: 5.4.2 text: "Ensure the GKE Metadata Server is Enabled (Automated)" type: "manual" remediation: | Using Command Line: gcloud beta container clusters update [CLUSTER_NAME] \ --identity-namespace=[PROJECT_ID].svc.id.goog Note that existing Node pools are unaffected. New Node pools default to --workload- metadata-from-node=GKE_METADATA_SERVER . To modify an existing Node pool to enable GKE Metadata Server: gcloud beta container node-pools update [NODEPOOL_NAME] \ --cluster=[CLUSTER_NAME] \ --workload-metadata-from-node=GKE_METADATA_SERVER You may also need to modify workloads in order for them to use Workload Identity as described within https://cloud.google.com/kubernetes-engine/docs/how-to/workload- identity. scored: false - id: 5.5 text: "Node Configuration and Maintenance" checks: - id: 5.5.1 text: "Ensure Container-Optimized OS (COS) is used for GKE node images (Automated)" type: "manual" remediation: | Using Command Line: To set the node image to cos for an existing cluster's Node pool: gcloud container clusters upgrade [CLUSTER_NAME]\ --image-type cos \ --zone [COMPUTE_ZONE] --node-pool [POOL_NAME] scored: false - id: 5.5.2 text: "Ensure Node Auto-Repair is enabled for GKE nodes (Automated)" type: "manual" remediation: | Using Command Line: To enable node auto-repair for an existing cluster with Node pool, run the following command: gcloud container node-pools update [POOL_NAME] \ --cluster [CLUSTER_NAME] --zone [COMPUTE_ZONE] \ --enable-autorepair scored: false - id: 5.5.3 text: "Ensure Node Auto-Upgrade is enabled for GKE nodes (Automated)" type: "manual" remediation: | Using Command Line: To enable node auto-upgrade for an existing cluster's Node pool, run the following command: gcloud container node-pools update [NODE_POOL] \ --cluster [CLUSTER_NAME] --zone [COMPUTE_ZONE] \ --enable-autoupgrade scored: false - id: 5.5.4 text: "Automate GKE version management using Release Channels (Manual)" type: "manual" remediation: | Using Command Line: Create a new cluster by running the following command: gcloud beta container clusters create [CLUSTER_NAME] \ --zone [COMPUTE_ZONE] \ --release-channel [RELEASE_CHANNEL] where [RELEASE_CHANNEL] is stable or regular according to your needs. scored: false - id: 5.5.5 text: "Ensure Shielded GKE Nodes are Enabled (Manual)" type: "manual" remediation: | Using Command Line: To create a Node pool within the cluster with Integrity Monitoring enabled, run the following command: gcloud beta container node-pools create [NODEPOOL_NAME] \ --cluster [CLUSTER_NAME] --zone [COMPUTE_ZONE] \ --shielded-integrity-monitoring You will also need to migrate workloads from existing non-conforming Node pools to the newly created Node pool, then delete the non-conforming pools. scored: false - id: 5.5.6 text: "Ensure Integrity Monitoring for Shielded GKE Nodes is Enabled (Automated)" type: "manual" remediation: | Using Command Line: To create a Node pool within the cluster with Integrity Monitoring enabled, run the following command: gcloud beta container node-pools create [NODEPOOL_NAME] \ --cluster [CLUSTER_NAME] --zone [COMPUTE_ZONE] \ --shielded-integrity-monitoring You will also need to migrate workloads from existing non-conforming Node pools to the newly created Node pool, then delete the non-conforming pools. scored: false - id: 5.5.7 text: "Ensure Secure Boot for Shielded GKE Nodes is Enabled (Automated)" type: "manual" remediation: | Using Command Line: To create a Node pool within the cluster with Secure Boot enabled, run the following command: gcloud beta container node-pools create [NODEPOOL_NAME] \ --cluster [CLUSTER_NAME] --zone [COMPUTE_ZONE] \ --shielded-secure-boot You will also need to migrate workloads from existing non-conforming Node pools to the newly created Node pool, then delete the non-conforming pools. scored: false - id: 5.6 text: "Cluster Networking" checks: - id: 5.6.1 text: "Enable VPC Flow Logs and Intranode Visibility (Automated)" type: "manual" remediation: | Using Command Line: To enable intranode visibility on an existing cluster, run the following command: gcloud beta container clusters update [CLUSTER_NAME] \ --enable-intra-node-visibility scored: false - id: 5.6.2 text: "Ensure use of VPC-native clusters (Automated)" type: "manual" remediation: | Using Command Line: To enable Alias IP on a new cluster, run the following command: gcloud container clusters create [CLUSTER_NAME] \ --zone [COMPUTE_ZONE] \ --enable-ip-alias scored: false - id: 5.6.3 text: "Ensure Master Authorized Networks is Enabled (Manual)" type: "manual" remediation: | Using Command Line: To check Master Authorized Networks status for an existing cluster, run the following command; gcloud container clusters describe [CLUSTER_NAME] \ --zone [COMPUTE_ZONE] \ --format json | jq '.masterAuthorizedNetworksConfig' The output should return { "enabled": true } if Master Authorized Networks is enabled. If Master Authorized Networks is disabled, the above command will return null ( { } ). scored: false - id: 5.6.4 text: "Ensure clusters are created with Private Endpoint Enabled and Public Access Disabled (Manual)" type: "manual" remediation: | Using Command Line: Create a cluster with a Private Endpoint enabled and Public Access disabled by including the --enable-private-endpoint flag within the cluster create command: gcloud container clusters create [CLUSTER_NAME] \ --enable-private-endpoint Setting this flag also requires the setting of --enable-private-nodes , --enable-ip-alias and --master-ipv4-cidr=[MASTER_CIDR_RANGE] . scored: false - id: 5.6.5 text: "Ensure clusters are created with Private Nodes (Manual)" type: "manual" remediation: | Using Command Line: To create a cluster with Private Nodes enabled, include the --enable-private-nodes flag within the cluster create command: gcloud container clusters create [CLUSTER_NAME] \ --enable-private-nodes Setting this flag also requires the setting of --enable-ip-alias and --master-ipv4- cidr=[MASTER_CIDR_RANGE] . scored: false - id: 5.6.6 text: "Consider firewalling GKE worker nodes (Manual)" type: "manual" remediation: | Using Command Line: Use the following command to generate firewall rules, setting the variables as appropriate. You may want to use the target [TAG] and [SERVICE_ACCOUNT] previously identified. gcloud compute firewall-rules create FIREWALL_RULE_NAME \ --network [NETWORK] \ --priority [PRIORITY] \ --direction [DIRECTION] \ --action [ACTION] \ --target-tags [TAG] \ --target-service-accounts [SERVICE_ACCOUNT] \ --source-ranges [SOURCE_CIDR-RANGE] \ --source-tags [SOURCE_TAGS] \ --source-service-accounts=[SOURCE_SERVICE_ACCOUNT] \ --destination-ranges [DESTINATION_CIDR_RANGE] \ --rules [RULES] scored: false - id: 5.6.7 text: "Ensure Network Policy is Enabled and set as appropriate (Manual)" type: "manual" remediation: | Using Command Line: To enable Network Policy for an existing cluster, firstly enable the Network Policy add-on: gcloud container clusters update [CLUSTER_NAME] \ --zone [COMPUTE_ZONE] \ --update-addons NetworkPolicy=ENABLED Then, enable Network Policy: gcloud container clusters update [CLUSTER_NAME] \ --zone [COMPUTE_ZONE] \ --enable-network-policy scored: false - id: 5.6.8 text: "Ensure use of Google-managed SSL Certificates (Manual)" type: "manual" remediation: | If services of type:LoadBalancer are discovered, consider replacing the Service with an Ingress. To configure the Ingress and use Google-managed SSL certificates, follow the instructions as listed at https://cloud.google.com/kubernetes-engine/docs/how-to/managed-certs. scored: false - id: 5.7 text: "Logging" checks: - id: 5.7.1 text: "Ensure Stackdriver Kubernetes Logging and Monitoring is Enabled (Automated)" type: "manual" remediation: | Using Command Line: STACKDRIVER KUBERNETES ENGINE MONITORING SUPPORT (PREFERRED): To enable Stackdriver Kubernetes Engine Monitoring for an existing cluster, run the following command: gcloud container clusters update [CLUSTER_NAME] \ --zone [COMPUTE_ZONE] \ --enable-stackdriver-kubernetes LEGACY STACKDRIVER SUPPORT: Both Logging and Monitoring support must be enabled. To enable Legacy Stackdriver Logging for an existing cluster, run the following command: gcloud container clusters update [CLUSTER_NAME] --zone [COMPUTE_ZONE] \ --logging-service logging.googleapis.com To enable Legacy Stackdriver Monitoring for an existing cluster, run the following command: gcloud container clusters update [CLUSTER_NAME] --zone [COMPUTE_ZONE] \ --monitoring-service monitoring.googleapis.com scored: false - id: 5.7.2 text: "Enable Linux auditd logging (Manual)" type: "manual" remediation: | Using Command Line: Download the example manifests: curl https://raw.githubusercontent.com/GoogleCloudPlatform/k8s-node-tools/master/os-audit/cos-auditd-logging.yaml \ > cos-auditd-logging.yaml Edit the example manifests if needed. Then, deploy them: kubectl apply -f cos-auditd-logging.yaml Verify that the logging Pods have started. If you defined a different Namespace in your manifests, replace cos-auditd with the name of the namespace you're using: kubectl get pods --namespace=cos-auditd scored: false - id: 5.8 text: "Authentication and Authorization" checks: - id: 5.8.1 text: "Ensure Basic Authentication using static passwords is Disabled (Automated)" type: "manual" remediation: | Using Command Line: To update an existing cluster and disable Basic Authentication by removing the static password: gcloud container clusters update [CLUSTER_NAME] \ --no-enable-basic-auth scored: false - id: 5.8.2 text: "Ensure authentication using Client Certificates is Disabled (Automated)" type: "manual" remediation: | Using Command Line: Create a new cluster without a Client Certificate: gcloud container clusters create [CLUSTER_NAME] \ --no-issue-client-certificate scored: false - id: 5.8.3 text: "Manage Kubernetes RBAC users with Google Groups for GKE (Manual)" type: "manual" remediation: | Using Command Line: Follow the G Suite Groups instructions at https://cloud.google.com/kubernetes- engine/docs/how-to/role-based-access-control#google-groups-for-gke. Then, create a cluster with gcloud beta container clusters create my-cluster \ --security-group="gke-security-groups@[yourdomain.com]" Finally create Roles, ClusterRoles, RoleBindings, and ClusterRoleBindings that reference your G Suite Groups. scored: false - id: 5.8.4 text: "Ensure Legacy Authorization (ABAC) is Disabled (Automated)" type: "manual" remediation: | Using Command Line: To disable Legacy Authorization for an existing cluster, run the following command: gcloud container clusters update [CLUSTER_NAME] \ --zone [COMPUTE_ZONE] \ --no-enable-legacy-authorization scored: false - id: 5.9 text: "Storage" checks: - id: 5.9.1 text: "Enable Customer-Managed Encryption Keys (CMEK) for GKE Persistent Disks (PD) (Manual)" type: "manual" remediation: | Using Command Line: FOR NODE BOOT DISKS: Create a new node pool using customer-managed encryption keys for the node boot disk, of [DISK_TYPE] either pd-standard or pd-ssd : gcloud beta container node-pools create [CLUSTER_NAME] \ --disk-type [DISK_TYPE] \ --boot-disk-kms-key \ projects/[KEY_PROJECT_ID]/locations/[LOCATION]/keyRings/[RING_NAME]/cryptoKeys/[KEY_NAME] Create a cluster using customer-managed encryption keys for the node boot disk, of [DISK_TYPE] either pd-standard or pd-ssd : gcloud beta container clusters create [CLUSTER_NAME] \ --disk-type [DISK_TYPE] \ --boot-disk-kms-key \ projects/[KEY_PROJECT_ID]/locations/[LOCATION]/keyRings/[RING_NAME]/cryptoKeys/[KEY_NAME] FOR ATTACHED DISKS: Follow the instructions detailed at https://cloud.google.com/kubernetes- engine/docs/how-to/using-cmek. scored: false - id: 5.10 text: "Other Cluster Configurations" checks: - id: 5.10.1 text: "Ensure Kubernetes Web UI is Disabled (Automated)" type: "manual" remediation: | Using Command Line: To disable the Kubernetes Dashboard on an existing cluster, run the following command: gcloud container clusters update [CLUSTER_NAME] \ --zone [ZONE] \ --update-addons=KubernetesDashboard=DISABLED scored: false - id: 5.10.2 text: "Ensure that Alpha clusters are not used for production workloads (Automated)" type: "manual" remediation: | Using Command Line: Upon creating a new cluster gcloud container clusters create [CLUSTER_NAME] \ --zone [COMPUTE_ZONE] Do not use the --enable-kubernetes-alpha argument. scored: false - id: 5.10.3 text: "Ensure Pod Security Policy is Enabled and set as appropriate (Manual)" type: "manual" remediation: | Using Command Line: To enable Pod Security Policy for an existing cluster, run the following command: gcloud beta container clusters update [CLUSTER_NAME] \ --zone [COMPUTE_ZONE] \ --enable-pod-security-policy scored: false - id: 5.10.4 text: "Consider GKE Sandbox for running untrusted workloads (Manual)" type: "manual" remediation: | Using Command Line: To enable GKE Sandbox on an existing cluster, a new Node pool must be created. gcloud container node-pools create [NODE_POOL_NAME] \ --zone=[COMPUTE-ZONE] \ --cluster=[CLUSTER_NAME] \ --image-type=cos_containerd \ --sandbox type=gvisor scored: false - id: 5.10.5 text: "Ensure use of Binary Authorization (Automated)" type: "manual" remediation: | Using Command Line: Firstly, update the cluster to enable Binary Authorization: gcloud container cluster update [CLUSTER_NAME] \ --zone [COMPUTE-ZONE] \ --enable-binauthz Create a Binary Authorization Policy using the Binary Authorization Policy Reference (https://cloud.google.com/binary-authorization/docs/policy-yaml-reference) for guidance. Import the policy file into Binary Authorization: gcloud container binauthz policy import [YAML_POLICY] scored: false - id: 5.10.6 text: "Enable Cloud Security Command Center (Cloud SCC) (Manual)" type: "manual" remediation: | Using Command Line: Follow the instructions at https://cloud.google.com/security-command- center/docs/quickstart-scc-setup. scored: false ================================================ FILE: cfg/gke-1.2.0/master.yaml ================================================ --- controls: version: "gke-1.2.0" id: 1 text: "Control Plane Components" type: "master" ================================================ FILE: cfg/gke-1.2.0/node.yaml ================================================ --- controls: version: "gke-1.2.0" id: 3 text: "Worker Node Security Configuration" type: "node" groups: - id: 3.1 text: "Worker Node Configuration Files" checks: - id: 3.1.1 text: "Ensure that the proxy kubeconfig file permissions are set to 644 or more restrictive (Manual)" audit: '/bin/sh -c ''if test -e $proxykubeconfig; then stat -c permissions=%a $proxykubeconfig; fi'' ' tests: test_items: - flag: "permissions" compare: op: bitmask value: "644" remediation: | Run the below command (based on the file location on your system) on each worker node. For example, chmod 644 $proxykubeconfig scored: false - id: 3.1.2 text: "Ensure that the proxy kubeconfig file ownership is set to root:root (Manual)" audit: '/bin/sh -c ''if test -e $proxykubeconfig; then stat -c %U:%G $proxykubeconfig; fi'' ' tests: test_items: - flag: root:root remediation: | Run the below command (based on the file location on your system) on each worker node. For example, chown root:root $proxykubeconfig scored: false - id: 3.1.3 text: "Ensure that the kubelet configuration file permissions are set to 644 or more restrictive (Manual)" audit: '/bin/sh -c ''if test -e $kubeletconf; then stat -c permissions=%a $kubeletconf; fi'' ' tests: test_items: - flag: "permissions" compare: op: bitmask value: "644" remediation: | Run the following command (using the config file location identied in the Audit step) chmod 644 /var/lib/kubelet/config.yaml scored: false - id: 3.1.4 text: "Ensure that the kubelet configuration file ownership is set to root:root (Manual)" audit: '/bin/sh -c ''if test -e $kubeletconf; then stat -c %U:%G $kubeletconf; fi'' ' tests: test_items: - flag: root:root remediation: | Run the following command (using the config file location identied in the Audit step) chown root:root /etc/kubernetes/kubelet.conf scored: false - id: 3.2 text: "Kubelet" checks: - id: 3.2.1 text: "Ensure that the --anonymous-auth argument is set to false (Automated)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: "--anonymous-auth" path: '{.authentication.anonymous.enabled}' compare: op: eq value: false remediation: | If using a Kubelet config file, edit the file to set authentication: anonymous: enabled to false. If using executable arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_SYSTEM_PODS_ARGS variable. --anonymous-auth=false Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: true - id: 3.2.2 text: "Ensure that the --authorization-mode argument is not set to AlwaysAllow (Automated)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --authorization-mode path: '{.authorization.mode}' compare: op: nothave value: AlwaysAllow remediation: | If using a Kubelet config file, edit the file to set authorization: mode to Webhook. If using executable arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_AUTHZ_ARGS variable. --authorization-mode=Webhook Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: true - id: 3.2.3 text: "Ensure that the --client-ca-file argument is set as appropriate (Automated)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --client-ca-file path: '{.authentication.x509.clientCAFile}' set: true remediation: | If using a Kubelet config file, edit the file to set authentication: x509: clientCAFile to the location of the client CA file. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_AUTHZ_ARGS variable. --client-ca-file= Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: true - id: 3.2.4 text: "Ensure that the --read-only-port argument is set to 0 (Manual)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: "--read-only-port" path: '{.readOnlyPort}' set: true compare: op: eq value: 0 remediation: | If using a Kubelet config file, edit the file to set readOnlyPort to 0. If using command line arguments, edit the kubelet service file /etc/systemd/system/kubelet.service.d/10-kubeadm.conf on each worker node and set the below parameter in KUBELET_SYSTEM_PODS_ARGS variable. --read-only-port=0 Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: false - id: 3.2.5 text: "Ensure that the --streaming-connection-idle-timeout argument is not set to 0 (Automated)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --streaming-connection-idle-timeout path: '{.streamingConnectionIdleTimeout}' compare: op: noteq value: 0 - flag: --streaming-connection-idle-timeout path: '{.streamingConnectionIdleTimeout}' set: false bin_op: or remediation: | If using a Kubelet config file, edit the file to set streamingConnectionIdleTimeout to a value other than 0. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_SYSTEM_PODS_ARGS variable. --streaming-connection-idle-timeout=5m Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: true - id: 3.2.6 text: "Ensure that the --protect-kernel-defaults argument is set to true (Manual)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --protect-kernel-defaults path: '{.protectKernelDefaults}' set: true compare: op: eq value: true remediation: | If using a Kubelet config file, edit the file to set protectKernelDefaults: true. If using command line arguments, edit the kubelet service file /etc/systemd/system/kubelet.service.d/10-kubeadm.conf on each worker node and set the below parameter in KUBELET_SYSTEM_PODS_ARGS variable. --protect-kernel-defaults=true Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: true - id: 3.2.7 text: "Ensure that the --make-iptables-util-chains argument is set to true (Automated) " audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --make-iptables-util-chains path: '{.makeIPTablesUtilChains}' compare: op: eq value: true - flag: --make-iptables-util-chains path: '{.makeIPTablesUtilChains}' set: false bin_op: or remediation: | If using a Kubelet config file, edit the file to set makeIPTablesUtilChains: true. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and remove the --make-iptables-util-chains argument from the KUBELET_SYSTEM_PODS_ARGS variable. Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: true - id: 3.2.8 text: "Ensure that the --hostname-override argument is not set (Manual)" audit: "/bin/ps -fC $kubeletbin " tests: test_items: - flag: --hostname-override set: false remediation: | Edit the kubelet service file $kubeletsvc on each worker node and remove the --hostname-override argument from the KUBELET_SYSTEM_PODS_ARGS variable. Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: false - id: 3.2.9 text: "Ensure that the --event-qps argument is set to 0 or a level which ensures appropriate event capture (Automated)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --event-qps path: '{.eventRecordQPS}' set: true compare: op: eq value: 0 remediation: | If using a Kubelet config file, edit the file to set eventRecordQPS: to an appropriate level. If using command line arguments, edit the kubelet service file /etc/systemd/system/kubelet.service.d/10-kubeadm.conf on each worker node and set the below parameter in KUBELET_SYSTEM_PODS_ARGS variable. Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: true - id: 3.2.10 text: "Ensure that the --tls-cert-file and --tls-private-key-file arguments are set as appropriate (Manual)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --tls-cert-file path: '{.tlsCertFile}' - flag: --tls-private-key-file path: '{.tlsPrivateKeyFile}' remediation: | If using a Kubelet config file, edit the file to set tlsCertFile to the location of the certificate file to use to identify this Kubelet, and tlsPrivateKeyFile to the location of the corresponding private key file. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameters in KUBELET_CERTIFICATE_ARGS variable. --tls-cert-file= --tls-private-key-file= Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: false - id: 3.2.11 text: "Ensure that the --rotate-certificates argument is not set to false (Manual)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --rotate-certificates path: '{.rotateCertificates}' compare: op: eq value: true - flag: --rotate-certificates path: '{.rotateCertificates}' set: false bin_op: or remediation: | If using a Kubelet config file, edit the file to add the line rotateCertificates: true or remove it altogether to use the default value. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and remove --rotate-certificates=false argument from the KUBELET_CERTIFICATE_ARGS variable. Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: false - id: 3.2.12 text: "Ensure that the RotateKubeletServerCertificate argument is set to true (Automated)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: RotateKubeletServerCertificate path: '{.featureGates.RotateKubeletServerCertificate}' compare: op: eq value: true remediation: | Edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_CERTIFICATE_ARGS variable. --feature-gates=RotateKubeletServerCertificate=true Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: true ================================================ FILE: cfg/gke-1.2.0/policies.yaml ================================================ --- controls: version: "gke-1.2.0" id: 4 text: "Kubernetes Policies" type: "policies" groups: - id: 4.1 text: "RBAC and Service Accounts" checks: - id: 4.1.1 text: "Ensure that the cluster-admin role is only used where required (Manual)" type: "manual" remediation: | Identify all clusterrolebindings to the cluster-admin role. Check if they are used and if they need this role or if they could use a role with fewer privileges. Where possible, first bind users to a lower privileged role and then remove the clusterrolebinding to the cluster-admin role : kubectl delete clusterrolebinding [name] scored: false - id: 4.1.2 text: "Minimize access to secrets (Manual)" type: "manual" remediation: | Where possible, remove get, list and watch access to secret objects in the cluster. scored: false - id: 4.1.3 text: "Minimize wildcard use in Roles and ClusterRoles (Manual)" type: "manual" remediation: | Where possible replace any use of wildcards in clusterroles and roles with specific objects or actions. scored: false - id: 4.1.4 text: "Minimize access to create pods (Manual)" type: "manual" remediation: | Where possible, remove create access to pod objects in the cluster. scored: false - id: 4.1.5 text: "Ensure that default service accounts are not actively used. (Manual)" type: "manual" remediation: | Create explicit service accounts wherever a Kubernetes workload requires specific access to the Kubernetes API server. Modify the configuration of each default service account to include this value automountServiceAccountToken: false scored: true - id: 4.1.6 text: "Ensure that Service Account Tokens are only mounted where necessary (Manual)" type: "manual" remediation: | Modify the definition of pods and service accounts which do not need to mount service account tokens to disable it. scored: false - id: 4.2 text: "Pod Security Policies" checks: - id: 4.2.1 text: "Minimize the admission of privileged containers (Automated)" type: "manual" remediation: | Create a PSP as described in the Kubernetes documentation, ensuring that the .spec.privileged field is omitted or set to false. scored: false - id: 4.2.2 text: "Minimize the admission of containers wishing to share the host process ID namespace (Automated)" type: "manual" remediation: | Create a PSP as described in the Kubernetes documentation, ensuring that the .spec.hostPID field is omitted or set to false. scored: false - id: 4.2.3 text: "Minimize the admission of containers wishing to share the host IPC namespace (Automated)" type: "manual" remediation: | Create a PSP as described in the Kubernetes documentation, ensuring that the .spec.hostIPC field is omitted or set to false. scored: false - id: 4.2.4 text: "Minimize the admission of containers wishing to share the host network namespace (Automated)" type: "manual" remediation: | Create a PSP as described in the Kubernetes documentation, ensuring that the .spec.hostNetwork field is omitted or set to false. scored: false - id: 4.2.5 text: "Minimize the admission of containers with allowPrivilegeEscalation (Automated)" type: "manual" remediation: | Create a PSP as described in the Kubernetes documentation, ensuring that the .spec.allowPrivilegeEscalation field is omitted or set to false. scored: false - id: 4.2.6 text: "Minimize the admission of root containers (Automated)" type: "manual" remediation: | Create a PSP as described in the Kubernetes documentation, ensuring that the .spec.runAsUser.rule is set to either MustRunAsNonRoot or MustRunAs with the range of UIDs not including 0. scored: false - id: 4.2.7 text: "Minimize the admission of containers with the NET_RAW capability (Automated)" type: "manual" remediation: | Create a PSP as described in the Kubernetes documentation, ensuring that the .spec.requiredDropCapabilities is set to include either NET_RAW or ALL. scored: false - id: 4.2.8 text: "Minimize the admission of containers with added capabilities (Automated)" type: "manual" remediation: | Ensure that allowedCapabilities is not present in PSPs for the cluster unless it is set to an empty array. scored: false - id: 4.2.9 text: "Minimize the admission of containers with capabilities assigned (Manual) " type: "manual" remediation: | Review the use of capabilites in applications running on your cluster. Where a namespace contains applications which do not require any Linux capabities to operate consider adding a PSP which forbids the admission of containers which do not drop all capabilities. scored: false - id: 4.3 text: "Network Policies and CNI" checks: - id: 4.3.1 text: "Ensure that the CNI in use supports Network Policies (Manual)" type: "manual" remediation: | To use a CNI plugin with Network Policy, enable Network Policy in GKE, and the CNI plugin will be updated. See Recommendation 6.6.7. scored: false - id: 4.3.2 text: "Ensure that all Namespaces have Network Policies defined (Manual)" type: "manual" remediation: | Follow the documentation and create NetworkPolicy objects as you need them. scored: false - id: 4.4 text: "Secrets Management" checks: - id: 4.4.1 text: "Prefer using secrets as files over secrets as environment variables (Manual)" type: "manual" remediation: | if possible, rewrite application code to read secrets from mounted secret files, rather than from environment variables. scored: false - id: 4.4.2 text: "Consider external secret storage (Manual)" type: "manual" remediation: | Refer to the secrets management options offered by your cloud provider or a third-party secrets management solution. scored: false - id: 4.5 text: "Extensible Admission Control" checks: - id: 4.5.1 text: "Configure Image Provenance using ImagePolicyWebhook admission controller (Manual)" type: "manual" remediation: | Follow the Kubernetes documentation and setup image provenance. See also Recommendation 6.10.5 for GKE specifically. scored: false - id: 4.6 text: "General Policies" checks: - id: 4.6.1 text: "Create administrative boundaries between resources using namespaces (Manual)" type: "manual" remediation: | Follow the documentation and create namespaces for objects in your deployment as you need them. scored: false - id: 4.6.2 text: "Ensure that the seccomp profile is set to docker/default in your pod definitions (Manual)" type: "manual" remediation: | Seccomp is an alpha feature currently. By default, all alpha features are disabled. So, you would need to enable alpha features in the apiserver by passing "--feature- gates=AllAlpha=true" argument. Edit the /etc/kubernetes/apiserver file on the master node and set the KUBE_API_ARGS parameter to "--feature-gates=AllAlpha=true" KUBE_API_ARGS="--feature-gates=AllAlpha=true" Based on your system, restart the kube-apiserver service. For example: systemctl restart kube-apiserver.service Use annotations to enable the docker/default seccomp profile in your pod definitions. An example is as below: apiVersion: v1 kind: Pod metadata: name: trustworthy-pod annotations: seccomp.security.alpha.kubernetes.io/pod: docker/default spec: containers: - name: trustworthy-container image: sotrustworthy:latest scored: false - id: 4.6.3 text: "Apply Security Context to Your Pods and Containers (Manual)" type: "manual" remediation: | Follow the Kubernetes documentation and apply security contexts to your pods. For a suggested list of security contexts, you may refer to the CIS Security Benchmark for Docker Containers. scored: false - id: 4.6.4 text: "The default namespace should not be used (Manual)" type: "manual" remediation: | Ensure that namespaces are created to allow for appropriate segregation of Kubernetes resources and that all new resources are created in a specific namespace. scored: false ================================================ FILE: cfg/gke-1.6.0/config.yaml ================================================ --- ## Version-specific settings that override the values in cfg/config.yaml node: proxy: defaultkubeconfig: "/var/lib/kubelet/kubeconfig" kubelet: defaultconf: "/etc/kubernetes/kubelet/kubelet-config.yaml" ================================================ FILE: cfg/gke-1.6.0/controlplane.yaml ================================================ --- controls: version: "gke-1.6.0" id: 2 text: "Control Plane Configuration" type: "controlplane" groups: - id: 2.1 text: "Authentication and Authorization" checks: - id: 2.1.1 text: "Client certificate authentication should not be used for users (Manual)" type: "manual" remediation: | Alternative mechanisms provided by Kubernetes such as the use of OIDC should be implemented in place of client certificates. You can remediate the availability of client certificates in your GKE cluster. See Recommendation 5.8.1. scored: false ================================================ FILE: cfg/gke-1.6.0/managedservices.yaml ================================================ --- controls: version: "gke-1.6.0" id: 5 text: "Managed Services" type: "managedservices" groups: - id: 5.1 text: "Image Registry and Image Scanning" checks: - id: 5.1.1 text: "Ensure Image Vulnerability Scanning is enabled (Automated)" type: "manual" remediation: | For Images Hosted in GCR: Using Command Line: gcloud services enable containeranalysis.googleapis.com For Images Hosted in AR: Using Command Line: gcloud services enable containerscanning.googleapis.com scored: false - id: 5.1.2 text: "Minimize user access to Container Image repositories (Manual)" type: "manual" remediation: | For Images Hosted in AR: Using Command Line: gcloud artifacts repositories set-iam-policy \ --location To learn how to configure policy files see: https://cloud.google.com/artifact-registry/docs/access-control#grant For Images Hosted in GCR: Using Command Line: To change roles at the GCR bucket level: Firstly, run the following if read permissions are required: gsutil iam ch ::objectViewer gs://artifacts..appspot.com Then remove the excessively privileged role (Storage Admin / Storage Object Admin / Storage Object Creator) using: gsutil iam ch -d :: gs://artifacts..appspot.com where: can be one of the following: user, if the is a Google account. serviceAccount, if specifies a Service account. can be one of the following: a Google account (for example, someone@example.com). a Cloud IAM service account. To modify roles defined at the project level and subsequently inherited within the GCR bucket, or the Service Account User role, extract the IAM policy file, modify it accordingly and apply it using: gcloud projects set-iam-policy scored: false - id: 5.1.3 text: "Minimize cluster access to read-only for Container Image repositories (Manual)" type: "manual" remediation: | For Images Hosted in AR: Using Command Line: Add artifactregistry.reader role gcloud artifacts repositories add-iam-policy-binding \ --location= \ --member='serviceAccount:' \ --role='roles/artifactregistry.reader' Remove any roles other than artifactregistry.reader gcloud artifacts repositories remove-iam-policy-binding \ --location \ --member='serviceAccount:' \ --role='' For Images Hosted in GCR: For an account explicitly granted to the bucket: Firstly add read access to the Kubernetes Service Account: gsutil iam ch ::objectViewer gs://artifacts..appspot.com where: can be one of the following: user, if the is a Google account. serviceAccount, if specifies a Service account. can be one of the following: a Google account (for example, someone@example.com). a Cloud IAM service account. Then remove the excessively privileged role (Storage Admin / Storage Object Admin / Storage Object Creator) using: gsutil iam ch -d :: gs://artifacts..appspot.com For an account that inherits access to the GCR Bucket through Project level permissions, modify the Projects IAM policy file accordingly, then upload it using: gcloud projects set-iam-policy scored: false - id: 5.1.4 text: "Ensure only trusted container images are used (Manual)" type: "manual" remediation: | Using Command Line: Update the cluster to enable Binary Authorization: gcloud container cluster update --enable-binauthz Create a Binary Authorization Policy using the Binary Authorization Policy Reference: https://cloud.google.com/binary-authorization/docs/policy-yaml-reference for guidance. Import the policy file into Binary Authorization: gcloud container binauthz policy import scored: false - id: 5.2 text: "Identity and Access Management (IAM)" checks: - id: 5.2.1 text: "Ensure GKE clusters are not running using the Compute Engine default service account (Automated))" type: "manual" remediation: | Using Command Line: To create a minimally privileged service account: gcloud iam service-accounts create \ --display-name "GKE Node Service Account" export NODE_SA_EMAIL=gcloud iam service-accounts list \ --format='value(email)' --filter='displayName:GKE Node Service Account' Grant the following roles to the service account: export PROJECT_ID=gcloud config get-value project gcloud projects add-iam-policy-binding --member \ serviceAccount: --role roles/monitoring.metricWriter gcloud projects add-iam-policy-binding --member \ serviceAccount: --role roles/monitoring.viewer gcloud projects add-iam-policy-binding --member \ serviceAccount: --role roles/logging.logWriter To create a new Node pool using the Service account, run the following command: gcloud container node-pools create \ --service-account=@.iam.gserviceaccount.com \ --cluster= --zone Note: The workloads will need to be migrated to the new Node pool, and the old node pools that use the default service account should be deleted to complete the remediation. scored: false - id: 5.2.2 text: "Prefer using dedicated GCP Service Accounts and Workload Identity (Manual)" type: "manual" remediation: | Using Command Line: gcloud container clusters update --zone \ --workload-pool .svc.id.goog Note that existing Node pools are unaffected. New Node pools default to --workload- metadata-from-node=GKE_METADATA_SERVER. Then, modify existing Node pools to enable GKE_METADATA_SERVER: gcloud container node-pools update --cluster \ --zone --workload-metadata=GKE_METADATA Workloads may need to be modified in order for them to use Workload Identity as described within: https://cloud.google.com/kubernetes-engine/docs/how-to/workload-identity. Also consider the effects on the availability of hosted workloads as Node pools are updated. It may be more appropriate to create new Node Pools. scored: false - id: 5.3 text: "Cloud Key Management Service (Cloud KMS)" checks: - id: 5.3.1 text: "Ensure Kubernetes Secrets are encrypted using keys managed in Cloud KMS (Automated)" type: "manual" remediation: | To create a key: Create a key ring: gcloud kms keyrings create --location --project \ Create a key: gcloud kms keys create --location --keyring \ --purpose encryption --project Grant the Kubernetes Engine Service Agent service account the Cloud KMS CryptoKey Encrypter/Decrypter role: gcloud kms keys add-iam-policy-binding --location \ --keyring --member serviceAccount: \ --role roles/cloudkms.cryptoKeyEncrypterDecrypter --project To create a new cluster with Application-layer Secrets Encryption: gcloud container clusters create --cluster-version=latest \ --zone \ --database-encryption-key projects//locations//keyRings//cryptoKeys/ \ --project To enable on an existing cluster: gcloud container clusters update --zone \ --database-encryption-key projects//locations//keyRings//cryptoKeys/ \ --project scored: false - id: 5.4 text: "Node Metadata" checks: - id: 5.4.1 text: "Ensure the GKE Metadata Server is Enabled (Automated)" type: "manual" remediation: | Using Command Line: gcloud container clusters update --identity-namespace=.svc.id.goog Note that existing Node pools are unaffected. New Node pools default to --workload- metadata-from-node=GKE_METADATA_SERVER. To modify an existing Node pool to enable GKE Metadata Server: gcloud container node-pools update --cluster= \ --workload-metadata-from-node=GKE_METADATA_SERVER Workloads may need modification in order for them to use Workload Identity as described within: https://cloud.google.com/kubernetes-engine/docs/how-to/workload-identity. scored: false - id: 5.5 text: "Node Configuration and Maintenance" checks: - id: 5.5.1 text: "Ensure Container-Optimized OS (cos_containerd) is used for GKE node images (Automated)" type: "manual" remediation: | Using Command Line: To set the node image to cos for an existing cluster's Node pool: gcloud container clusters upgrade --image-type cos_containerd \ --zone --node-pool scored: false - id: 5.5.2 text: "Ensure Node Auto-Repair is enabled for GKE nodes (Automated)" type: "manual" remediation: | Using Command Line: To enable node auto-repair for an existing cluster's Node pool: gcloud container node-pools update --cluster \ --zone --enable-autorepair scored: false - id: 5.5.3 text: "Ensure Node Auto-Upgrade is enabled for GKE nodes (Automated)" type: "manual" remediation: | Using Command Line: To enable node auto-upgrade for an existing cluster's Node pool, run the following command: gcloud container node-pools update --cluster \ --zone --enable-autoupgrade scored: false - id: 5.5.4 text: "When creating New Clusters - Automate GKE version management using Release Channels (Automated)" type: "manual" remediation: | Using Command Line: Create a new cluster by running the following command: gcloud container clusters create --zone \ --release-channel where is stable or regular, according to requirements. scored: false - id: 5.5.5 text: "Ensure Shielded GKE Nodes are Enabled (Automated)" type: "manual" remediation: | Using Command Line: To migrate an existing cluster, the flag --enable-shielded-nodes needs to be specified in the cluster update command: gcloud container clusters update --zone \ --enable-shielded-nodes scored: false - id: 5.5.6 text: "Ensure Integrity Monitoring for Shielded GKE Nodes is Enabled (Automated)" type: "manual" remediation: | Using Command Line: To create a Node pool within the cluster with Integrity Monitoring enabled, run the following command: gcloud container node-pools create --cluster \ --zone --shielded-integrity-monitoring Workloads from existing non-conforming Node pools will need to be migrated to the newly created Node pool, then delete non-conforming Node pools to complete the remediation scored: false - id: 5.5.7 text: "Ensure Secure Boot for Shielded GKE Nodes is Enabled (Automated)" type: "manual" remediation: | Using Command Line: To create a Node pool within the cluster with Secure Boot enabled, run the following command: gcloud container node-pools create --cluster \ --zone --shielded-secure-boot Workloads will need to be migrated from existing non-conforming Node pools to the newly created Node pool, then delete the non-conforming pools. scored: false - id: 5.6 text: "Cluster Networking" checks: - id: 5.6.1 text: "Enable VPC Flow Logs and Intranode Visibility (Automated)" type: "manual" remediation: | Using Command Line: 1. Find the subnetwork name associated with the cluster. gcloud container clusters describe \ --region - -format json | jq '.subnetwork' 2. Update the subnetwork to enable VPC Flow Logs. gcloud compute networks subnets update --enable-flow-logs scored: false - id: 5.6.2 text: "Ensure use of VPC-native clusters (Automated)" type: "manual" remediation: | Using Command Line: To enable Alias IP on a new cluster, run the following command: gcloud container clusters create --zone \ --enable-ip-alias If using Autopilot configuration mode: gcloud container clusters create-auto \ --zone scored: false - id: 5.6.3 text: "Ensure Control Plane Authorized Networks is Enabled (Automated)" type: "manual" remediation: | Using Command Line: To enable Control Plane Authorized Networks for an existing cluster, run the following command: gcloud container clusters update --zone \ --enable-master-authorized-networks Along with this, you can list authorized networks using the --master-authorized-networks flag which contains a list of up to 20 external networks that are allowed to connect to your cluster's control plane through HTTPS. You provide these networks as a comma-separated list of addresses in CIDR notation (such as 90.90.100.0/24). scored: false - id: 5.6.4 text: "Ensure clusters are created with Private Endpoint Enabled and Public Access Disabled (Manual)" type: "manual" remediation: | Using Command Line: Create a cluster with a Private Endpoint enabled and Public Access disabled by including the --enable-private-endpoint flag within the cluster create command: gcloud container clusters create --enable-private-endpoint Setting this flag also requires the setting of --enable-private-nodes, --enable-ip-alias and --master-ipv4-cidr=. scored: false - id: 5.6.5 text: "Ensure clusters are created with Private Nodes (Manual)" type: "manual" remediation: | Using Command Line: To create a cluster with Private Nodes enabled, include the --enable-private-nodes flag within the cluster create command: gcloud container clusters create --enable-private-nodes Setting this flag also requires the setting of --enable-ip-alias and --master-ipv4-cidr=. scored: false - id: 5.6.6 text: "Consider firewalling GKE worker nodes (Manual)" type: "manual" remediation: | Using Command Line: Use the following command to generate firewall rules, setting the variables as appropriate: gcloud compute firewall-rules create \ --network --priority --direction \ --action --target-tags \ --target-service-accounts \ --source-ranges --source-tags \ --source-service-accounts \ --destination-ranges --rules scored: false - id: 5.6.7 text: "Ensure use of Google-managed SSL Certificates (Automated)" type: "manual" remediation: | If services of type:LoadBalancer are discovered, consider replacing the Service with an Ingress. To configure the Ingress and use Google-managed SSL certificates, follow the instructions as listed at: https://cloud.google.com/kubernetes-engine/docs/how- to/managed-certs. scored: false - id: 5.7 text: "Logging" checks: - id: 5.7.1 text: "Ensure Logging and Cloud Monitoring is Enabled (Automated)" type: "manual" remediation: | To enable Logging for an existing cluster, run the following command: gcloud container clusters update --zone \ --logging= See https://cloud.google.com/sdk/gcloud/reference/container/clusters/update#--logging for a list of available components for logging. To enable Cloud Monitoring for an existing cluster, run the following command: gcloud container clusters update --zone \ --monitoring= See https://cloud.google.com/sdk/gcloud/reference/container/clusters/update#-- monitoring for a list of available components for Cloud Monitoring. scored: false - id: 5.7.2 text: "Enable Linux auditd logging (Manual)" type: "manual" remediation: | Using Command Line: Download the example manifests: curl https://raw.githubusercontent.com/GoogleCloudPlatform/k8s-node-tools/master/os-audit/cos-auditd-logging.yaml > cos-auditd-logging.yaml Edit the example manifests if needed. Then, deploy them: kubectl apply -f cos-auditd-logging.yaml Verify that the logging Pods have started. If a different Namespace was defined in the manifests, replace cos-auditd with the name of the namespace being used: kubectl get pods --namespace=cos-auditd scored: false - id: 5.8 text: "Authentication and Authorization" checks: - id: 5.8.1 text: "Ensure authentication using Client Certificates is Disabled (Automated)" type: "manual" remediation: | Using Command Line: Create a new cluster without a Client Certificate: gcloud container clusters create [CLUSTER_NAME] \ --no-issue-client-certificate scored: false - id: 5.8.2 text: "Manage Kubernetes RBAC users with Google Groups for GKE (Manual)" type: "manual" remediation: | Using Command Line: Follow the G Suite Groups instructions at: https://cloud.google.com/kubernetes- engine/docs/how-to/role-based-access-control#google-groups-for-gke. Then, create a cluster with: gcloud container clusters create --security-group Finally create Roles, ClusterRoles, RoleBindings, and ClusterRoleBindings that reference the G Suite Groups. scored: false - id: 5.8.3 text: "Ensure Legacy Authorization (ABAC) is Disabled (Automated)" type: "manual" remediation: | Using Command Line: To disable Legacy Authorization for an existing cluster, run the following command: gcloud container clusters update --zone \ --no-enable-legacy-authorization scored: false - id: 5.9 text: "Storage" checks: - id: 5.9.1 text: "Enable Customer-Managed Encryption Keys (CMEK) for GKE Persistent Disks (PD) (Manual)" type: "manual" remediation: | Using Command Line: Follow the instructions detailed at: https://cloud.google.com/kubernetes-engine/docs/how-to/using-cmek. scored: false - id: 5.9.2 text: "Enable Customer-Managed Encryption Keys (CMEK) for Boot Disks (Automated)" type: "manual" remediation: | Using Command Line: Create a new node pool using customer-managed encryption keys for the node boot disk, of either pd-standard or pd-ssd: gcloud container node-pools create --disk-type \ --boot-disk-kms-key projects//locations//keyRings//cryptoKeys/ Create a cluster using customer-managed encryption keys for the node boot disk, of either pd-standard or pd-ssd: gcloud container clusters create --disk-type \ --boot-disk-kms-key projects//locations//keyRings//cryptoKeys/ scored: false - id: 5.10 text: "Other Cluster Configurations" checks: - id: 5.10.1 text: "Ensure Kubernetes Web UI is Disabled (Automated)" type: "manual" remediation: | Using Command Line: To disable the Kubernetes Dashboard on an existing cluster, run the following command: gcloud container clusters update --zone \ --update-addons=KubernetesDashboard=DISABLED scored: false - id: 5.10.2 text: "Ensure that Alpha clusters are not used for production workloads (Automated)" type: "manual" remediation: | Using Command Line: Upon creating a new cluster gcloud container clusters create [CLUSTER_NAME] \ --zone [COMPUTE_ZONE] Do not use the --enable-kubernetes-alpha argument. scored: false - id: 5.10.3 text: "Consider GKE Sandbox for running untrusted workloads (Manual)" type: "manual" remediation: | Using Command Line: To enable GKE Sandbox on an existing cluster, a new Node pool must be created, which can be done using: gcloud container node-pools create --zone \ --cluster --image-type=cos_containerd --sandbox="type=gvisor" scored: false - id: 5.10.4 text: "Ensure use of Binary Authorization (Automated)" type: "manual" remediation: | Using Command Line: Update the cluster to enable Binary Authorization: gcloud container cluster update --zone \ --binauthz-evaluation-mode= Example: gcloud container clusters update $CLUSTER_NAME --zone $COMPUTE_ZONE \ --binauthz-evaluation-mode=PROJECT_SINGLETON_POLICY_ENFORCE See: https://cloud.google.com/sdk/gcloud/reference/container/clusters/update#--binauthz-evaluation-mode for more details around the evaluation modes available. Create a Binary Authorization Policy using the Binary Authorization Policy Reference: https://cloud.google.com/binary-authorization/docs/policy-yaml-reference for guidance. Import the policy file into Binary Authorization: gcloud container binauthz policy import scored: false - id: 5.10.5 text: "Enable Security Posture (Manual)" type: "manual" remediation: | Enable security posture via the UI, gCloud or API. https://cloud.google.com/kubernetes-engine/docs/how-to/protect-workload-configuration scored: false ================================================ FILE: cfg/gke-1.6.0/master.yaml ================================================ --- controls: version: "gke-1.6.0" id: 1 text: "Control Plane Components" type: "master" ================================================ FILE: cfg/gke-1.6.0/node.yaml ================================================ --- controls: version: "gke-1.6.0" id: 3 text: "Worker Node Security Configuration" type: "node" groups: - id: 3.1 text: "Worker Node Configuration Files" checks: - id: 3.1.1 text: "Ensure that the proxy kubeconfig file permissions are set to 644 or more restrictive (Manual)" audit: '/bin/sh -c ''if test -e $proxykubeconfig; then stat -c permissions=%a $proxykubeconfig; fi'' ' tests: test_items: - flag: "permissions" compare: op: bitmask value: "644" remediation: | Run the below command (based on the file location on your system) on each worker node. For example, chmod 644 $proxykubeconfig scored: true - id: 3.1.2 text: "Ensure that the proxy kubeconfig file ownership is set to root:root (Manual)" audit: '/bin/sh -c ''if test -e $proxykubeconfig; then stat -c %U:%G $proxykubeconfig; fi'' ' tests: test_items: - flag: root:root remediation: | Run the below command (based on the file location on your system) on each worker node. For example: chown root:root $proxykubeconfig scored: true - id: 3.1.3 text: "Ensure that the kubelet configuration file has permissions set to 600 (Manual)" audit: '/bin/sh -c ''if test -e /home/kubernetes/kubelet-config.yaml; then stat -c permissions=%a /home/kubernetes/kubelet-config.yaml; fi'' ' tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Run the following command (using the kubelet config file location) chmod 644 /home/kubernetes/kubelet-config.yaml scored: true - id: 3.1.4 text: "Ensure that the kubelet configuration file ownership is set to root:root (Manual)" audit: '/bin/sh -c ''if test -e /home/kubernetes/kubelet-config.yaml; then stat -c %U:%G /home/kubernetes/kubelet-config.yaml; fi'' ' tests: test_items: - flag: root:root remediation: | Run the following command (using the config file location identied in the Audit step) chown root:root /home/kubernetes/kubelet-config.yaml scored: true - id: 3.2 text: "Kubelet" checks: - id: 3.2.1 text: "Ensure that the Anonymous Auth is Not Enabled (Automated)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat /home/kubernetes/kubelet-config.yaml" tests: test_items: - flag: "--anonymous-auth" path: '{.authentication.anonymous.enabled}' compare: op: eq value: false remediation: | Remediation Method 1: If configuring via the Kubelet config file, you first need to locate the file. To do this, SSH to each node and execute the following command to find the kubelet process: ps -ef | grep kubelet The output of the above command provides details of the active kubelet process, from which we can see the location of the configuration file provided to the kubelet service with the --config argument. The file can be viewed with a command such as more or less, like so: sudo less /home/kubernetes/kubelet-config.yaml Disable Anonymous Authentication by setting the following parameter: "authentication": { "anonymous": { "enabled": false } } Remediation Method 2: If using executable arguments, edit the kubelet service file on each worker node and ensure the below parameters are part of the KUBELET_ARGS variable string. For systems using systemd, such as the Amazon EKS Optimised Amazon Linux or Bottlerocket AMIs, then this file can be found at /etc/systemd/system/kubelet.service.d/10-kubelet-args.conf. Otherwise, you may need to look up documentation for your chosen operating system to determine which service manager is configured: --anonymous-auth=false For Both Remediation Steps: Based on your system, restart the kubelet service and check the service status. The following example is for operating systems using systemd, such as the Amazon EKS Optimised Amazon Linux or Bottlerocket AMIs, and invokes the systemctl command. If systemctl is not available then you will need to look up documentation for your chosen operating system to determine which service manager is configured: systemctl daemon-reload systemctl restart kubelet.service systemctl status kubelet -l scored: true - id: 3.2.2 text: "Ensure that the --authorization-mode argument is not set to AlwaysAllow (Automated)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat /home/kubernetes/kubelet-config.yaml" tests: test_items: - flag: --authorization-mode path: '{.authorization.mode}' compare: op: nothave value: AlwaysAllow remediation: | Remediation Method 1: If configuring via the Kubelet config file, you first need to locate the file. To do this, SSH to each node and execute the following command to find the kubelet process: ps -ef | grep kubelet The output of the above command provides details of the active kubelet process, from which we can see the location of the configuration file provided to the kubelet service with the --config argument. The file can be viewed with a command such as more or less, like so: sudo less /path/to/kubelet-config.json Enable Webhook Authentication by setting the following parameter: "authentication": { "webhook": { "enabled": true } } Next, set the Authorization Mode to Webhook by setting the following parameter: "authorization": { "mode": "Webhook } Finer detail of the authentication and authorization fields can be found in the Kubelet Configuration documentation (https://kubernetes.io/docs/reference/config-api/kubelet-config.v1beta1/). Remediation Method 2: If using executable arguments, edit the kubelet service file on each worker node and ensure the below parameters are part of the KUBELET_ARGS variable string. For systems using systemd, such as the Amazon EKS Optimised Amazon Linux or Bottlerocket AMIs, then this file can be found at /etc/systemd/system/kubelet.service.d/10-kubelet-args.conf. Otherwise, you may need to look up documentation for your chosen operating system to determine which service manager is configured: --authentication-token-webhook --authorization-mode=Webhook For Both Remediation Steps: Based on your system, restart the kubelet service and check the service status. The following example is for operating systems using systemd, such as the Amazon EKS Optimised Amazon Linux or Bottlerocket AMIs, and invokes the systemctl command. If systemctl is not available then you will need to look up documentation for your chosen operating system to determine which service manager is configured: systemctl daemon-reload systemctl restart kubelet.service systemctl status kubelet -l scored: true - id: 3.2.3 text: "Ensure that a Client CA File is Configured (Automated)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat /home/kubernetes/kubelet-config.yaml" tests: test_items: - flag: --client-ca-file path: '{.authentication.x509.clientCAFile}' set: true remediation: | Remediation Method 1: If configuring via the Kubelet config file, you first need to locate the file. To do this, SSH to each node and execute the following command to find the kubelet process: ps -ef | grep kubelet The output of the above command provides details of the active kubelet process, from which we can see the location of the configuration file provided to the kubelet service with the --config argument. The file can be viewed with a command such as more or less, like so: sudo less /path/to/kubelet-config.json Configure the client certificate authority file by setting the following parameter appropriately: "authentication": { "x509": {"clientCAFile": } }" Remediation Method 2: If using executable arguments, edit the kubelet service file on each worker node and ensure the below parameters are part of the KUBELET_ARGS variable string. For systems using systemd, such as the Amazon EKS Optimised Amazon Linux or Bottlerocket AMIs, then this file can be found at /etc/systemd/system/kubelet.service.d/10-kubelet-args.conf. Otherwise, you may need to look up documentation for your chosen operating system to determine which service manager is configured: --client-ca-file= For Both Remediation Steps: Based on your system, restart the kubelet service and check the service status. The following example is for operating systems using systemd, such as the Amazon EKS Optimised Amazon Linux or Bottlerocket AMIs, and invokes the systemctl command. If systemctl is not available then you will need to look up documentation for your chosen operating system to determine which service manager is configured: systemctl daemon-reload systemctl restart kubelet.service systemctl status kubelet -l scored: true - id: 3.2.4 text: "Ensure that the --read-only-port argument is disabled (Automated)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat /home/kubernetes/kubelet-config.yaml" tests: test_items: - flag: "--read-only-port" path: '{.readOnlyPort}' set: false - flag: "--read-only-port" path: '{.readOnlyPort}' compare: op: eq value: 0 bin_op: or remediation: | If modifying the Kubelet config file, edit the kubelet-config.json file /etc/kubernetes/kubelet/kubelet-config.json and set the below parameter to 0 "readOnlyPort": 0 If using executable arguments, edit the kubelet service file /etc/systemd/system/kubelet.service.d/10-kubelet-args.conf on each worker node and add the below parameter at the end of the KUBELET_ARGS variable string. --read-only-port=0 For each remediation: Based on your system, restart the kubelet service and check status systemctl daemon-reload systemctl restart kubelet.service systemctl status kubelet -l scored: true - id: 3.2.5 text: "Ensure that the --streaming-connection-idle-timeout argument is not set to 0 (Automated)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat /home/kubernetes/kubelet-config.yaml" tests: test_items: - flag: --streaming-connection-idle-timeout path: '{.streamingConnectionIdleTimeout}' compare: op: noteq value: 0 - flag: --streaming-connection-idle-timeout path: '{.streamingConnectionIdleTimeout}' set: false bin_op: or remediation: | Remediation Method 1: If modifying the Kubelet config file, edit the kubelet-config.json file /etc/kubernetes/kubelet-config.yaml and set the below parameter to a non-zero value in the format of #h#m#s "streamingConnectionIdleTimeout": "4h0m0s" You should ensure that the kubelet service file /etc/systemd/system/kubelet.service.d/10-kubelet-args.conf does not specify a --streaming-connection-idle-timeout argument because it would override the Kubelet config file. Remediation Method 2: If using executable arguments, edit the kubelet service file /etc/systemd/system/kubelet.service.d/10-kubelet-args.conf on each worker node and add the below parameter at the end of the KUBELET_ARGS variable string. --streaming-connection-idle-timeout=4h0m0s Remediation Method 3: If using the api configz endpoint consider searching for the status of "streamingConnectionIdleTimeout": by extracting the live configuration from the nodes running kubelet. **See detailed step-by-step configmap procedures in Reconfigure a Node's Kubelet in a Live Cluster (https://kubernetes.io/docs/tasks/administer-cluster/reconfigure-kubelet/), and then rerun the curl statement from audit process to check for kubelet configuration changes kubectl proxy --port=8001 & export HOSTNAME_PORT=localhost:8001 (example host and port number) export NODE_NAME=gke-cluster-1-pool1-5e572947-r2hg (example node name from "kubectl get nodes") curl -sSL "http://${HOSTNAME_PORT}/api/v1/nodes/${NODE_NAME}/proxy/configz" For all three remediations: Based on your system, restart the kubelet service and check status systemctl daemon-reload systemctl restart kubelet.service systemctl status kubelet -l scored: true - id: 3.2.6 text: "Ensure that the --make-iptables-util-chains argument is set to true (Automated)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat /home/kubernetes/kubelet-config.yaml" tests: test_items: - flag: --make-iptables-util-chains path: '{.makeIPTablesUtilChains}' compare: op: eq value: true - flag: --make-iptables-utils-chains path: '{.makeIPTablesUtilChains}' set: false bin_op: or remediation: | Remediation Method 1: If modifying the Kubelet config file, edit the kubelet-config.json file /etc/kubernetes/kubelet/kubelet-config.json and set the below parameter to true "makeIPTablesUtilChains": true Ensure that /etc/systemd/system/kubelet.service.d/10-kubelet-args.conf does not set the --make-iptables-util-chains argument because that would override your Kubelet config file. Remediation Method 2: If using executable arguments, edit the kubelet service file /etc/systemd/system/kubelet.service.d/10-kubelet-args.conf on each worker node and add the below parameter at the end of the KUBELET_ARGS variable string. --make-iptables-util-chains:true Remediation Method 3: If using the api configz endpoint consider searching for the status of "makeIPTablesUtilChains.: true by extracting the live configuration from the nodes running kubelet. **See detailed step-by-step configmap procedures in Reconfigure a Node's Kubelet in a Live Cluster (https://kubernetes.io/docs/tasks/administer-cluster/reconfigure-kubelet/), and then rerun the curl statement from audit process to check for kubelet configuration changes kubectl proxy --port=8001 & export HOSTNAME_PORT=localhost:8001 (example host and port number) export NODE_NAME=gke-cluster-1-pool1-5e572947-r2hg (example node name from "kubectl get nodes") curl -sSL "http://${HOSTNAME_PORT}/api/v1/nodes/${NODE_NAME}/proxy/configz" For all three remediations: Based on your system, restart the kubelet service and check status systemctl daemon-reload systemctl restart kubelet.service systemctl status kubelet -l scored: true - id: 3.2.7 text: "Ensure that the --eventRecordQPS argument is set to 0 or a level which ensures appropriate event capture (Automated)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat /etc/systemd/system/kubelet.service.d/10-kubeadm.conf" tests: test_items: - flag: --event-qps path: '{.eventRecordQPS}' set: true compare: op: eq value: 0 remediation: | If using a Kubelet config file, edit the file to set eventRecordQPS: to an appropriate level. If using command line arguments, edit the kubelet service file /etc/systemd/system/kubelet.service.d/10-kubeadm.conf on each worker node and set the below parameter in KUBELET_SYSTEM_PODS_ARGS variable. Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: true - id: 3.2.8 text: "Ensure that the --rotate-certificates argument is not present or is set to true (Automated)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat /home/kubernetes/kubelet-config.yaml" tests: test_items: - flag: --rotate-certificates path: '{.rotateCertificates}' compare: op: eq value: true - flag: --rotate-certificates path: '{.rotateCertificates}' set: false bin_op: or remediation: | Remediation Method 1: If modifying the Kubelet config file, edit the kubelet-config.yaml file /etc/kubernetes/kubelet/kubelet-config.yaml and set the below parameter to true "RotateCertificate":true Additionally, ensure that the kubelet service file /etc/systemd/system/kubelet.service.d/10-kubelet-args.conf does not set the --RotateCertificate executable argument to false because this would override the Kubelet config file. Remediation Method 2: If using executable arguments, edit the kubelet service file /etc/systemd/system/kubelet.service.d/10-kubelet-args.conf on each worker node and add the below parameter at the end of the KUBELET_ARGS variable string. --RotateCertificate=true scored: true - id: 3.2.9 text: "Ensure that the RotateKubeletServerCertificate argument is set to true (Automated)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat /home/kubernetes/kubelet-config.yaml" tests: test_items: - flag: RotateKubeletServerCertificate path: '{.featureGates.RotateKubeletServerCertificate}' compare: op: eq value: true remediation: | Remediation Method 1: If modifying the Kubelet config file, edit the kubelet-config.json file /etc/kubernetes/kubelet-config.yaml and set the below parameter to true "featureGates": { "RotateKubeletServerCertificate":true }, Additionally, ensure that the kubelet service file /etc/systemd/system/kubelet.service.d/10-kubelet-args.conf does not set the --rotate-kubelet-server-certificate executable argument to false because this would override the Kubelet config file. Remediation Method 2: If using executable arguments, edit the kubelet service file /etc/systemd/system/kubelet.service.d/10-kubelet-args.conf on each worker node and add the below parameter at the end of the KUBELET_ARGS variable string. --rotate-kubelet-server-certificate=true Remediation Method 3: If using the api configz endpoint consider searching for the status of "RotateKubeletServerCertificate": by extracting the live configuration from the nodes running kubelet. **See detailed step-by-step configmap procedures in Reconfigure a Node's Kubelet in a Live Cluster (https://kubernetes.io/docs/tasks/administer-cluster/reconfigure-kubelet/), and then rerun the curl statement from audit process to check for kubelet configuration changes kubectl proxy --port=8001 & export HOSTNAME_PORT=localhost:8001 (example host and port number) export NODE_NAME=gke-cluster-1-pool1-5e572947-r2hg (example node name from "kubectl get nodes") curl -sSL "http://${HOSTNAME_PORT}/api/v1/nodes/${NODE_NAME}/proxy/configz" For all three remediation methods: Restart the kubelet service and check status. The example below is for when using systemctl to manage services: systemctl daemon-reload systemctl restart kubelet.service systemctl status kubelet -l scored: true ================================================ FILE: cfg/gke-1.6.0/policies.yaml ================================================ --- controls: version: "gke-1.6.0" id: 4 text: "Kubernetes Policies" type: "policies" groups: - id: 4.1 text: "RBAC and Service Accounts" checks: - id: 4.1.1 text: "Ensure that the cluster-admin role is only used where required (Automated)" type: "manual" remediation: | Identify all clusterrolebindings to the cluster-admin role. Check if they are used and if they need this role or if they could use a role with fewer privileges. Where possible, first bind users to a lower privileged role and then remove the clusterrolebinding to the cluster-admin role : kubectl delete clusterrolebinding [name] scored: false - id: 4.1.2 text: "Minimize access to secrets (Automated)" type: "manual" remediation: | Where possible, remove get, list and watch access to secret objects in the cluster. scored: false - id: 4.1.3 text: "Minimize wildcard use in Roles and ClusterRoles (Automated)" type: "manual" remediation: | Where possible replace any use of wildcards in clusterroles and roles with specific objects or actions. scored: false - id: 4.1.4 text: "Ensure that default service accounts are not actively used (Automated)" type: "manual" remediation: | Create explicit service accounts wherever a Kubernetes workload requires specific access to the Kubernetes API server. Modify the configuration of each default service account to include this value automountServiceAccountToken: false scored: false - id: 4.1.5 text: "Ensure that Service Account Tokens are only mounted where necessary (Automated)" type: "manual" remediation: | Modify the definition of pods and service accounts which do not need to mount service account tokens to disable it. scored: false - id: 4.1.6 text: "Avoid use of system:masters group (Automated)" type: "manual" remediation: | Remove the system:masters group from all users in the cluster. scored: false - id: 4.1.7 text: "Limit use of the Bind, Impersonate and Escalate permissions in the Kubernetes cluster (Manual)" type: "manual" remediation: | Where possible, remove the impersonate, bind and escalate rights from subjects. scored: false - id: 4.1.8 text: "Avoid bindings to system:anonymous (Automated)" type: "manual" remediation: | Identify all clusterrolebindings and rolebindings to the user system:anonymous. Check if they are used and review the permissions associated with the binding using the commands in the Audit section above or refer to GKE documentation (https://cloud.google.com/kubernetes-engine/docs/best-practices/rbac#detect-prevent-default). Strongly consider replacing unsafe bindings with an authenticated, user-defined group. Where possible, bind to non-default, user-defined groups with least-privilege roles. If there are any unsafe bindings to the user system:anonymous, proceed to delete them after consideration for cluster operations with only necessary, safer bindings. kubectl delete clusterrolebinding [CLUSTER_ROLE_BINDING_NAME] kubectl delete rolebinding [ROLE_BINDING_NAME] --namespace [ROLE_BINDING_NAMESPACE] scored: false - id: 4.1.9 text: "Avoid non-default bindings to system:unauthenticated (Automated)" type: "manual" remediation: | Identify all non-default clusterrolebindings and rolebindings to the group system:unauthenticated. Check if they are used and review the permissions associated with the binding using the commands in the Audit section above or refer to GKE documentation (https://cloud.google.com/kubernetes-engine/docs/best-practices/rbac#detect-prevent-default). Strongly consider replacing non-default, unsafe bindings with an authenticated, user- defined group. Where possible, bind to non-default, user-defined groups with least- privilege roles. If there are any non-default, unsafe bindings to the group system:unauthenticated, proceed to delete them after consideration for cluster operations with only necessary, safer bindings. kubectl delete clusterrolebinding [CLUSTER_ROLE_BINDING_NAME] kubectl delete rolebinding [ROLE_BINDING_NAME] --namespace [ROLE_BINDING_NAMESPACE] scored: false - id: 4.1.10 text: "Avoid non-default bindings to system:authenticated (Automated)" type: "manual" remediation: | Identify all non-default clusterrolebindings and rolebindings to the group system:authenticated. Check if they are used and review the permissions associated with the binding using the commands in the Audit section above or refer to GKE documentation. Strongly consider replacing non-default, unsafe bindings with an authenticated, user- defined group. Where possible, bind to non-default, user-defined groups with least- privilege roles. If there are any non-default, unsafe bindings to the group system:authenticated, proceed to delete them after consideration for cluster operations with only necessary, safer bindings. kubectl delete clusterrolebinding [CLUSTER_ROLE_BINDING_NAME] kubectl delete rolebinding [ROLE_BINDING_NAME] --namespace [ROLE_BINDING_NAMESPACE] scored: false - id: 4.2 text: "Pod Security Standards" checks: - id: 4.2.1 text: "Ensure that the cluster enforces Pod Security Standard Baseline profile or stricter for all namespaces. (Manual)" type: "manual" remediation: | Ensure that Pod Security Admission is in place for every namespace which contains user workloads. Run the following command to enforce the Baseline profile in a namespace: kubectl label namespace pod-security.kubernetes.io/enforce=baseline scored: false - id: 4.3 text: "Network Policies and CNI" checks: - id: 4.3.1 text: "Ensure that the CNI in use supports Network Policies (Manual)" type: "manual" remediation: | To use a CNI plugin with Network Policy, enable Network Policy in GKE, and the CNI plugin will be updated. See Recommendation 5.6.7. scored: false - id: 4.3.2 text: "Ensure that all Namespaces have Network Policies defined (Automated)" type: "manual" remediation: | Follow the documentation and create NetworkPolicy objects as needed. See: https://cloud.google.com/kubernetes-engine/docs/how-to/network-policy#creating_a_network_policy for more information. scored: false - id: 4.4 text: "Secrets Management" checks: - id: 4.4.1 text: "Prefer using secrets as files over secrets as environment variables (Automated)" type: "manual" remediation: | if possible, rewrite application code to read secrets from mounted secret files, rather than from environment variables. scored: false - id: 4.4.2 text: "Consider external secret storage (Manual)" type: "manual" remediation: | Refer to the secrets management options offered by your cloud provider or a third-party secrets management solution. scored: false - id: 4.5 text: "Extensible Admission Control" checks: - id: 4.5.1 text: "Configure Image Provenance using ImagePolicyWebhook admission controller (Manual)" type: "manual" remediation: | Follow the Kubernetes documentation and setup image provenance. Also see recommendation 5.10.4. scored: false - id: 4.6 text: "General Policies" checks: - id: 4.6.1 text: "Create administrative boundaries between resources using namespaces (Manual)" type: "manual" remediation: | Follow the documentation and create namespaces for objects in your deployment as you need them. scored: false - id: 4.6.2 text: "Ensure that the seccomp profile is set to RuntimeDefault in your pod definitions (Automated)" type: "manual" remediation: | Use security context to enable the RuntimeDefault seccomp profile in your pod definitions. An example is as below: { "namespace": "kube-system", "name": "metrics-server-v0.7.0-dbcc8ddf6-gz7d4", "seccompProfile": "RuntimeDefault" } scored: false - id: 4.6.3 text: "Apply Security Context to Your Pods and Containers (Manual)" type: "manual" remediation: | Follow the Kubernetes documentation and apply security contexts to your pods. For a suggested list of security contexts, you may refer to the CIS Google Container- Optimized OS Benchmark. scored: false - id: 4.6.4 text: "The default namespace should not be used (Automated)" type: "manual" remediation: | Ensure that namespaces are created to allow for appropriate segregation of Kubernetes resources and that all new resources are created in a specific namespace. scored: false ================================================ FILE: cfg/gke-1.8.0/config.yaml ================================================ --- ## Version-specific settings that override the values in cfg/config.yaml node: proxy: defaultkubeconfig: "/var/lib/kubelet/kubeconfig" kubelet: defaultconf: "/etc/kubernetes/kubelet-config.yaml" ================================================ FILE: cfg/gke-1.8.0/controlplane.yaml ================================================ --- controls: version: "gke-1.8.0" id: 2 text: "Control Plane Configuration" type: "controlplane" ================================================ FILE: cfg/gke-1.8.0/managedservices.yaml ================================================ --- controls: version: "gke-1.8.0" id: 5 text: "Managed Services" type: "managedservices" groups: - id: 5.1 text: "Image Registry and Image Scanning" checks: - id: 5.1.1 text: "Ensure Image Vulnerability Scanning is enabled (Automated)" audit: "gcloud services list --enabled" type: "manual" remediation: | For Images Hosted in GCR: Using Google Cloud Console Go to GCR by visiting: https://console.cloud.google.com/gcr Select Settings and, under the Vulnerability Scanning heading, click the TURN ON button. Using Command Line gcloud services enable containeranalysis.googleapis.com For Images Hosted in AR: Using Google Cloud Console Go to GCR by visiting: https://console.cloud.google.com/artifacts Select Settings and, under the Vulnerability Scanning heading, click the ENABLE button. Using Command Line gcloud services enable containerscanning.googleapis.com scored: false - id: 5.1.2 text: "Minimize user access to Container Image repositories (Manual)" audit: | gcloud projects get-iam-policy \ --flatten="bindings[].members" \ --format='table(bindings.members,bindings.role)' \ --filter="bindings.role:roles/storage.admin OR bindings.role:roles/storage.objectAdmin OR bindings.role:roles/storage.objectCreator OR bindings.role:roles/storage.legacyBucketOwner OR bindings.role:roles/storage.legacyBucketWriter OR bindings.role:roles/storage.legacyObjectOwner" type: "manual" remediation: | For Images Hosted in AR: Using Command Line: gcloud artifacts repositories set-iam-policy \ --location To learn how to configure policy files see: https://cloud.google.com/artifact-registry/docs/access-control#grant For Images Hosted in GCR: Using Command Line: To change roles at the GCR bucket level: Firstly, run the following if read permissions are required: gsutil iam ch ::objectViewer gs://artifacts..appspot.com Then remove the excessively privileged role (Storage Admin / Storage Object Admin / Storage Object Creator) using: gsutil iam ch -d :: gs://artifacts..appspot.com where: can be one of the following: user, if the is a Google account. serviceAccount, if specifies a Service account. can be one of the following: a Google account (for example, someone@example.com). a Cloud IAM service account. To modify roles defined at the project level and subsequently inherited within the GCR bucket, or the Service Account User role, extract the IAM policy file, modify it accordingly and apply it using: gcloud projects set-iam-policy scored: false - id: 5.1.3 text: "Minimize cluster access to read-only for Container Image repositories (Manual)" audit: | gcloud projects get-iam-policy \ --flatten="bindings[].members" \ --format='table(bindings.members,bindings.role)' \ --filter="bindings.role:roles/storage.admin OR bindings.role:roles/storage.objectAdmin OR bindings.role:roles/storage.objectCreator OR bindings.role:roles/storage.legacyBucketOwner OR bindings.role:roles/storage.legacyBucketWriter OR bindings.role:roles/storage.legacyObjectOwner" type: "manual" remediation: | For Images Hosted in AR: Using Command Line: Add artifactregistry.reader role gcloud artifacts repositories add-iam-policy-binding \ --location= \ --member='serviceAccount:' \ --role='roles/artifactregistry.reader' Remove any roles other than artifactregistry.reader gcloud artifacts repositories remove-iam-policy-binding \ --location \ --member='serviceAccount:' \ --role='' For Images Hosted in GCR: For an account explicitly granted to the bucket: Firstly add read access to the Kubernetes Service Account: gsutil iam ch ::objectViewer gs://artifacts..appspot.com where: can be one of the following: user, if the is a Google account. serviceAccount, if specifies a Service account. can be one of the following: a Google account (for example, someone@example.com). a Cloud IAM service account. Then remove the excessively privileged role (Storage Admin / Storage Object Admin / Storage Object Creator) using: gsutil iam ch -d :: gs://artifacts..appspot.com For an account that inherits access to the GCR Bucket through Project level permissions, modify the Projects IAM policy file accordingly, then upload it using: gcloud projects set-iam-policy scored: false - id: 5.1.4 text: "Ensure only trusted container images are used (Manual)" audit: | gcloud container clusters describe $CLUSTER_NAME --zone $COMPUTE_ZONE --format json | jq .binaryAuthorization type: "manual" remediation: | Using Command Line: Update the cluster to enable Binary Authorization: gcloud container cluster update --enable-binauthz Create a Binary Authorization Policy using the Binary Authorization Policy Reference: https://cloud.google.com/binary-authorization/docs/policy-yaml-reference for guidance. Import the policy file into Binary Authorization: gcloud container binauthz policy import scored: false - id: 5.2 text: "Identity and Access Management (IAM)" checks: - id: 5.2.1 text: "Ensure GKE clusters are not running using the Compute Engine default service account (Automated))" audit: | gcloud container node-pools describe $NODE_POOL --cluster $CLUSTER_NAME --zone $COMPUTE_ZONE --format json | jq '.config.serviceAccount' type: "manual" remediation: | Using Command Line: To create a minimally privileged service account: gcloud iam service-accounts create \ --display-name "GKE Node Service Account" export NODE_SA_EMAIL=gcloud iam service-accounts list \ --format='value(email)' --filter='displayName:GKE Node Service Account' Grant the following roles to the service account: export PROJECT_ID=gcloud config get-value project gcloud projects add-iam-policy-binding --member \ serviceAccount: --role roles/monitoring.metricWriter gcloud projects add-iam-policy-binding --member \ serviceAccount: --role roles/monitoring.viewer gcloud projects add-iam-policy-binding --member \ serviceAccount: --role roles/logging.logWriter To create a new Node pool using the Service account, run the following command: gcloud container node-pools create \ --service-account=@.iam.gserviceaccount.com \ --cluster= --zone Note: The workloads will need to be migrated to the new Node pool, and the old node pools that use the default service account should be deleted to complete the remediation. scored: false - id: 5.2.2 text: "Prefer using dedicated GCP Service Accounts and Workload Identity (Manual)" audit: | gcloud container clusters describe $CLUSTER_NAME --zone $COMPUTE_ZONE --format json | jq .workloadIdentityConfig type: "manual" remediation: | Using Command Line: gcloud container clusters update --zone \ --workload-pool .svc.id.goog Note that existing Node pools are unaffected. New Node pools default to --workload- metadata-from-node=GKE_METADATA_SERVER. Then, modify existing Node pools to enable GKE_METADATA_SERVER: gcloud container node-pools update --cluster \ --zone --workload-metadata=GKE_METADATA Workloads may need to be modified in order for them to use Workload Identity as described within: https://cloud.google.com/kubernetes-engine/docs/how-to/workload-identity. Also consider the effects on the availability of hosted workloads as Node pools are updated. It may be more appropriate to create new Node Pools. scored: false - id: 5.3 text: "Cloud Key Management Service (Cloud KMS)" checks: - id: 5.3.1 text: "Ensure Kubernetes Secrets are encrypted using keys managed in Cloud KMS (Automated)" audit: | gcloud container clusters describe $CLUSTER_NAME --zone $COMPUTE_ZONE --format json | jq '.databaseEncryption' type: "manual" remediation: | To create a key: Create a key ring: gcloud kms keyrings create --location --project \ Create a key: gcloud kms keys create --location --keyring \ --purpose encryption --project Grant the Kubernetes Engine Service Agent service account the Cloud KMS CryptoKey Encrypter/Decrypter role: gcloud kms keys add-iam-policy-binding --location \ --keyring --member serviceAccount: \ --role roles/cloudkms.cryptoKeyEncrypterDecrypter --project To create a new cluster with Application-layer Secrets Encryption: gcloud container clusters create --cluster-version=latest \ --zone \ --database-encryption-key projects//locations//keyRings//cryptoKeys/ \ --project To enable on an existing cluster: gcloud container clusters update --zone \ --database-encryption-key projects//locations//keyRings//cryptoKeys/ \ --project scored: false - id: 5.4 text: "Node Metadata" checks: - id: 5.4.1 text: "Ensure the GKE Metadata Server is Enabled (Automated)" audit: | gcloud container clusters describe $CLUSTER_NAME --zone $COMPUTE_ZONE --format json | jq '.nodePools[].config.workloadMetadataConfig' type: "manual" remediation: | Using Command Line: gcloud container clusters update --identity-namespace=.svc.id.goog Note that existing Node pools are unaffected. New Node pools default to --workload- metadata-from-node=GKE_METADATA_SERVER. To modify an existing Node pool to enable GKE Metadata Server: gcloud container node-pools update --cluster= \ --workload-metadata-from-node=GKE_METADATA_SERVER Workloads may need modification in order for them to use Workload Identity as described within: https://cloud.google.com/kubernetes-engine/docs/how-to/workload-identity. scored: false - id: 5.5 text: "Node Configuration and Maintenance" checks: - id: 5.5.1 text: "Ensure Container-Optimized OS (cos_containerd) is used for GKE node images (Automated)" audit: | gcloud container node-pools describe $NODE_POOL --cluster $CLUSTER_NAME --zone $COMPUTE_ZONE --format json | jq '.config.imageType' type: "manual" remediation: | Using Command Line: To set the node image to cos for an existing cluster's Node pool: gcloud container clusters upgrade --image-type cos_containerd \ --zone --node-pool scored: false - id: 5.5.2 text: "Ensure Node Auto-Repair is enabled for GKE nodes (Automated)" audit: | gcloud container node-pools describe $POOL_NAME --cluster $CLUSTER_NAME --zone $COMPUTE_ZONE --format json | jq '.management' type: "manual" remediation: | Using Command Line: To enable node auto-repair for an existing cluster's Node pool: gcloud container node-pools update --cluster \ --zone --enable-autorepair scored: false - id: 5.5.3 text: "Ensure Node Auto-Upgrade is enabled for GKE nodes (Automated)" audit: | gcloud container node-pools describe $POOL_NAME --cluster $CLUSTER_NAME --zone $COMPUTE_ZONE --format json | jq '.management' type: "manual" remediation: | Using Command Line: To enable node auto-upgrade for an existing cluster's Node pool, run the following command: gcloud container node-pools update --cluster \ --zone --enable-autoupgrade scored: false - id: 5.5.4 text: "When creating New Clusters - Automate GKE version management using Release Channels (Automated)" audit: | gcloud container clusters describe $CLUSTER_NAME --zone $COMPUTE_ZONE --format json | jq .releaseChannel.channel type: "manual" remediation: | Using Command Line: Create a new cluster by running the following command: gcloud container clusters create --zone \ --release-channel where is stable or regular, according to requirements. scored: false - id: 5.5.5 text: "Ensure Shielded GKE Nodes are Enabled (Automated)" audit: | gcloud container clusters describe $CLUSTER_NAME --zone $COMPUTE_ZONE --format json | jq '.shieldedNodes' type: "manual" remediation: | Using Command Line: To migrate an existing cluster, the flag --enable-shielded-nodes needs to be specified in the cluster update command: gcloud container clusters update --zone \ --enable-shielded-nodes scored: false - id: 5.5.6 text: "Ensure Integrity Monitoring for Shielded GKE Nodes is Enabled (Automated)" audit: | gcloud container node-pools describe $POOL_NAME --cluster $CLUSTER_NAME --zone $COMPUTE_ZONE --format json | jq .config.shieldedInstanceConfig type: "manual" remediation: | Using Command Line: To create a Node pool within the cluster with Integrity Monitoring enabled, run the following command: gcloud container node-pools create --cluster \ --zone --shielded-integrity-monitoring Workloads from existing non-conforming Node pools will need to be migrated to the newly created Node pool, then delete non-conforming Node pools to complete the remediation scored: false - id: 5.5.7 text: "Ensure Secure Boot for Shielded GKE Nodes is Enabled (Automated)" audit: | gcloud container node-pools describe $POOL_NAME --cluster $CLUSTER_NAME --zone $COMPUTE_ZONE --format json | jq .config.shieldedInstanceConfig type: "manual" remediation: | Using Command Line: To create a Node pool within the cluster with Secure Boot enabled, run the following command: gcloud container node-pools create --cluster \ --zone --shielded-secure-boot Workloads will need to be migrated from existing non-conforming Node pools to the newly created Node pool, then delete the non-conforming pools. scored: false - id: 5.6 text: "Cluster Networking" checks: - id: 5.6.1 text: "Enable VPC Flow Logs and Intranode Visibility (Automated)" audit: | gcloud container clusters describe $CLUSTER_NAME --zone $COMPUTE_ZONE --format json | jq '.networkConfig.enableIntraNodeVisibility' type: "manual" remediation: | Using Command Line: 1. Find the subnetwork name associated with the cluster. gcloud container clusters describe \ --region - -format json | jq '.subnetwork' 2. Update the subnetwork to enable VPC Flow Logs. gcloud compute networks subnets update --enable-flow-logs scored: false - id: 5.6.2 text: "Ensure use of VPC-native clusters (Automated)" audit: | gcloud container clusters describe $CLUSTER_NAME --zone $COMPUTE_ZONE --format json | jq '.ipAllocationPolicy.useIpAliases' type: "manual" remediation: | Using Command Line: To enable Alias IP on a new cluster, run the following command: gcloud container clusters create --zone \ --enable-ip-alias scored: false - id: 5.6.3 text: "Ensure Control Plane Authorized Networks is Enabled (Automated)" audit: | gcloud container clusters describe $CLUSTER_NAME --zone $COMPUTE_ZONE --format json | jq '.masterAuthorizedNetworksConfig' type: "manual" remediation: | Using Command Line: To enable Control Plane Authorized Networks for an existing cluster, run the following command: gcloud container clusters update --zone \ --enable-master-authorized-networks Along with this, you can list authorized networks using the --master-authorized-networks flag which contains a list of up to 20 external networks that are allowed to connect to your cluster's control plane through HTTPS. You provide these networks as a comma-separated list of addresses in CIDR notation (such as 90.90.100.0/24). scored: false - id: 5.6.4 text: "Ensure clusters are created with Private Endpoint Enabled and Public Access Disabled (Automated)" audit: | gcloud container clusters describe $CLUSTER_NAME --zone $COMPUTE_ZONE --format json | jq '.privateClusterConfig.enablePrivateEndpoint' type: "manual" remediation: | Using Command Line: Create a cluster with a Private Endpoint enabled and Public Access disabled by including the --enable-private-endpoint flag within the cluster create command: gcloud container clusters create --enable-private-endpoint Setting this flag also requires the setting of --enable-private-nodes, --enable-ip-alias and --master-ipv4-cidr=. scored: false - id: 5.6.5 text: "Ensure clusters are created with Private Nodes (Automated)" audit: | gcloud container clusters describe $CLUSTER_NAME --zone $COMPUTE_ZONE --format json | jq '.privateClusterConfig.enablePrivateNodes' type: "manual" remediation: | Using Command Line: To create a cluster with Private Nodes enabled, include the --enable-private-nodes flag within the cluster create command: gcloud container clusters create --enable-private-nodes Setting this flag also requires the setting of --enable-ip-alias and --master-ipv4-cidr=. scored: false - id: 5.6.6 text: "Consider firewalling GKE worker nodes (Manual)" audit: | gcloud compute instances describe $INSTANCE_NAME --zone $COMPUTE_ZONE --format json | jq '{tags: .tags.items[], serviceaccount:.serviceAccounts[].email, network: .networkInterfaces[].network}' type: "manual" remediation: | Using Command Line: Use the following command to generate firewall rules, setting the variables as appropriate: gcloud compute firewall-rules create \ --network --priority --direction \ --action --target-tags \ --target-service-accounts \ --source-ranges --source-tags \ --source-service-accounts \ --destination-ranges --rules scored: false - id: 5.6.7 text: "Ensure use of Google-managed SSL Certificates (Automated)" audit: | svc_json="$(kubectl get svc -A -o json 2>/dev/null || echo '{"items":[],"__err":"SVC_FORBIDDEN"}')" ing_json="$(kubectl get ingress -A -o json 2>/dev/null || echo '{"items":[],"__err":"INGRESS_FORBIDDEN"}')" mc_json ="$(kubectl get managedcertificates -A -o json 2>/dev/null || echo '{"items":[],"__err":"MC_FORBIDDEN"}')" printf '%s\n%s\n%s\n' "$svc_json" "$ing_json" "$mc_json" \ | jq -rs ' (.[0] // {}) as $svcsRaw | (.[1] // {}) as $ingsRaw | (.[2] // {}) as $mcsRaw | # If any list failed, surface an error and DO NOT print the success string if ($svcsRaw.__err or $ingsRaw.__err or $mcsRaw.__err) then "ERROR_KUBECTL_LIST:" + ([ ($svcsRaw.__err // empty), ($ingsRaw.__err // empty), ($mcsRaw.__err // empty) ] | join(",")) else ($svcsRaw.items // []) as $svcs | ($ingsRaw.items // []) as $ings | ($mcsRaw.items // []) as $mcs | def trim: gsub("^\\s+|\\s+$";""); def hasmc($ns;$name): any($mcs[]?; .metadata.namespace==$ns and .metadata.name==$name); ([ # Public Services (not eligible for managed certs) $svcs[]? | select(.spec.type=="LoadBalancer") | "FOUND_PUBLIC_LB_SERVICE:\(.metadata.namespace // "default"):\(.metadata.name)" ] + [ # Ingresses missing managed-certs annotation $ings[]? as $i | ($i.metadata.annotations."networking.gke.io/managed-certificates" // "") as $ann | select($ann=="") | "FOUND_INGRESS_WITHOUT_MANAGED_CERT:\($i.metadata.namespace // "default"):\($i.metadata.name)" ] + [ # Ingresses referencing non-existent ManagedCertificate(s) $ings[]? as $i | ($i.metadata.annotations."networking.gke.io/managed-certificates" // "") as $ann | select($ann!="") | ($i.metadata.namespace // "default") as $ns | ($ann | split(",") | map(trim) | map(select(length>0)) | .[]) as $mc | select(hasmc($ns;$mc) | not) | "FOUND_MISSING_MANAGED_CERT_RESOURCE:\($ns):\($i.metadata.name):cert=\($mc)" ]) as $f | if ($f|length)>0 then $f[] else "ALL_INGRESSES_USE_MANAGED_CERTS_AND_NO_PUBLIC_LB_SERVICES" end end ' tests: test_items: - flag: "ALL_INGRESSES_USE_MANAGED_CERTS_AND_NO_PUBLIC_LB_SERVICES" set: true compare: op: eq value: "ALL_INGRESSES_USE_MANAGED_CERTS_AND_NO_PUBLIC_LB_SERVICES" remediation: | If services of type:LoadBalancer are discovered, consider replacing the Service with an Ingress. To configure the Ingress and use Google-managed SSL certificates, follow the instructions as listed at: https://cloud.google.com/kubernetes-engine/docs/how- to/managed-certs. scored: true - id: 5.7 text: "Logging" checks: - id: 5.7.1 text: "Ensure Logging and Cloud Monitoring is Enabled (Automated)" audit: | gcloud container clusters describe $CLUSTER_NAME --zone $COMPUTE_ZONE --format json | jq '.loggingService' type: "manual" remediation: | To enable Logging for an existing cluster, run the following command: gcloud container clusters update --zone \ --logging= See https://cloud.google.com/sdk/gcloud/reference/container/clusters/update#--logging for a list of available components for logging. To enable Cloud Monitoring for an existing cluster, run the following command: gcloud container clusters update --zone \ --monitoring= See https://cloud.google.com/sdk/gcloud/reference/container/clusters/update#-- monitoring for a list of available components for Cloud Monitoring. scored: false - id: 5.7.2 text: "Enable Linux auditd logging (Manual)" audit: | kubectl get daemonsets -A -o json | jq '.items[] | select (.spec.template.spec.containers[].image | contains ("gcr.io/stackdriver-agents/stackdriver-logging-agent"))'| jq '{name: .metadata.name, annotations: .metadata.annotations."kubernetes.io/description", namespace: .metadata.namespace, status: .status}' type: "manual" remediation: | Using Command Line: Download the example manifests: curl https://raw.githubusercontent.com/GoogleCloudPlatform/k8s-node-tools/master/os-audit/cos-auditd-logging.yaml > cos-auditd-logging.yaml Edit the example manifests if needed. Then, deploy them: kubectl apply -f cos-auditd-logging.yaml Verify that the logging Pods have started. If a different Namespace was defined in the manifests, replace cos-auditd with the name of the namespace being used: kubectl get pods --namespace=cos-auditd scored: false - id: 5.8 text: "Authentication and Authorization" checks: - id: 5.8.1 text: "Ensure authentication using Client Certificates is Disabled (Automated)" audit: | gcloud container clusters describe $CLUSTER_NAME --zone $COMPUTE_ZONE --format json | jq '.masterAuth.clientKey' type: "manual" remediation: | Using Command Line: Create a new cluster without a Client Certificate: gcloud container clusters create [CLUSTER_NAME] \ --no-issue-client-certificate scored: false - id: 5.8.2 text: "Manage Kubernetes RBAC users with Google Groups for GKE (Manual)" audit: | gcloud container clusters create --security-group type: "manual" remediation: | Using Command Line: Follow the G Suite Groups instructions at: https://cloud.google.com/kubernetes- engine/docs/how-to/role-based-access-control#google-groups-for-gke. Then, create a cluster with: gcloud container clusters create --security-group Finally create Roles, ClusterRoles, RoleBindings, and ClusterRoleBindings that reference the G Suite Groups. scored: false - id: 5.8.3 text: "Ensure Legacy Authorization (ABAC) is Disabled (Automated)" audit: | gcloud container clusters describe $CLUSTER_NAME --zone $COMPUTE_ZONE --format json | jq '.legacyAbac' type: "manual" remediation: | Using Command Line: To disable Legacy Authorization for an existing cluster, run the following command: gcloud container clusters update --zone \ --no-enable-legacy-authorization scored: false - id: 5.9 text: "Storage" checks: - id: 5.9.1 text: "Enable Customer-Managed Encryption Keys (CMEK) for GKE Persistent Disks (PD) (Manual)" audit: | gcloud compute disks describe $PV_NAME --zone $COMPUTE_ZONE --format json | jq '.diskEncryptionKey.kmsKeyName' type: "manual" remediation: | Using Command Line: Follow the instructions detailed at: https://cloud.google.com/kubernetes-engine/docs/how-to/using-cmek. scored: false - id: 5.9.2 text: "Enable Customer-Managed Encryption Keys (CMEK) for Boot Disks (Automated)" audit: | gcloud container node-pools describe $NODE_POOL --cluster $CLUSTER_NAME --zone $COMPUTE_ZONE type: "manual" remediation: | Using Command Line: Create a new node pool using customer-managed encryption keys for the node boot disk, of either pd-standard or pd-ssd: gcloud container node-pools create --disk-type \ --boot-disk-kms-key projects//locations//keyRings//cryptoKeys/ Create a cluster using customer-managed encryption keys for the node boot disk, of either pd-standard or pd-ssd: gcloud container clusters create --disk-type \ --boot-disk-kms-key projects//locations//keyRings//cryptoKeys/ scored: false - id: 5.10 text: "Other Cluster Configurations" checks: - id: 5.10.1 text: "Ensure Kubernetes Web UI is Disabled (Automated)" audit: | gcloud container clusters describe $CLUSTER_NAME --zone $COMPUTE_ZONE --format json | jq '.addonsConfig.kubernetesDashboard' type: "manual" remediation: | Using Command Line: To disable the Kubernetes Dashboard on an existing cluster, run the following command: gcloud container clusters update --zone \ --update-addons=KubernetesDashboard=DISABLED scored: false - id: 5.10.2 text: "Ensure that Alpha clusters are not used for production workloads (Automated)" audit: | gcloud container clusters describe $CLUSTER_NAME --zone $COMPUTE_ZONE --format json | jq '.enableKubernetesAlpha' type: "manual" remediation: | Using Command Line: Upon creating a new cluster gcloud container clusters create [CLUSTER_NAME] \ --zone [COMPUTE_ZONE] Do not use the --enable-kubernetes-alpha argument. scored: false - id: 5.10.3 text: "Consider GKE Sandbox for running untrusted workloads (Manual)" audit: | gcloud container node-pools describe $NODE_POOL --cluster $CLUSTER_NAME --zone $COMPUTE_ZONE --format json | jq '.config.sandboxConfig' type: "manual" remediation: | Using Command Line: To enable GKE Sandbox on an existing cluster, a new Node pool must be created, which can be done using: gcloud container node-pools create --zone \ --cluster --image-type=cos_containerd --sandbox="type=gvisor" scored: false - id: 5.10.5 text: "Enable Security Posture (Manual)" audit: "gcloud container clusters --location describe" type: "manual" remediation: | Enable security posture via the UI, gCloud or API. https://cloud.google.com/kubernetes-engine/docs/how-to/protect-workload-configuration scored: false ================================================ FILE: cfg/gke-1.8.0/master.yaml ================================================ --- controls: version: "gke-1.8.0" id: 1 text: "Control Plane Components" type: "master" ================================================ FILE: cfg/gke-1.8.0/node.yaml ================================================ --- controls: version: "gke-1.8.0" id: 3 text: "Worker Nodes" type: "node" groups: - id: 3.1 text: "Worker Node Configuration Files" checks: - id: 3.1.1 text: "Ensure that the kubeconfig file permissions are set to 644 or more restrictive (Automated)" audit: '/bin/sh -c ''if test -e $proxykubeconfig; then stat -c permissions=%a $proxykubeconfig; fi'' ' tests: test_items: - flag: "permissions" compare: op: bitmask value: "644" remediation: | Run the below command (based on the file location on your system) on each worker node. For example, chmod 644 $proxykubeconfig scored: true - id: 3.1.2 text: "Ensure that the kubelet kubeconfig file ownership is set to root:root (Automated)" audit: '/bin/sh -c ''if test -e $proxykubeconfig; then stat -c %U:%G $proxykubeconfig; fi'' ' tests: test_items: - flag: root:root remediation: | Run the below command (based on the file location on your system) on each worker node. For example: chown root:root $proxykubeconfig scored: true - id: 3.1.3 text: "Ensure that the kubelet configuration file has permissions set to 644 (Automated)" audit: '/bin/sh -c ''if test -e $kubeletconf; then stat -c permissions=%a $kubeletconf; fi'' ' tests: test_items: - flag: "permissions" compare: op: bitmask value: "644" remediation: | Run the following command (using the kubelet config file location) chmod 644 $kubeletconf scored: true - id: 3.1.4 text: "Ensure that the kubelet configuration file ownership is set to root:root (Automated)" audit: '/bin/sh -c ''if test -e $kubeletconf; then stat -c %U:%G $kubeletconf; fi'' ' tests: test_items: - flag: root:root remediation: | Run the following command (using the config file location identied in the Audit step) chown root:root $kubeletconf scored: true ================================================ FILE: cfg/gke-1.8.0/policies.yaml ================================================ --- controls: version: "gke-1.8.0" id: 4 text: "Kubernetes Policies" type: "policies" groups: - id: 4.1 text: "RBAC and Service Accounts" checks: - id: 4.1.1 text: "Ensure that the cluster-admin role is only used where required (Automated)" audit: | kubectl get clusterrolebindings -o json | jq -r ' [ .items[] | select(.roleRef.name == "cluster-admin") | .subjects[]? | select(.kind != "Group" or .name != "system:masters") ] | if length == 0 then "NO_CLUSTER_ADMIN_BINDINGS" else "FOUND_CLUSTER_ADMIN_BINDING" end ' tests: test_items: - flag: "NO_CLUSTER_ADMIN_BINDINGS" set: true compare: op: eq value: "NO_CLUSTER_ADMIN_BINDINGS" remediation: | Identify all ClusterRoleBindings to the "cluster-admin" role and review their subjects: kubectl get clusterrolebindings -o=custom-columns=NAME:.metadata.name,ROLE:.roleRef.name,SUBJECTS:.subjects[*].name | grep cluster-admin If non-system principals (users, groups, or service accounts) do not strictly require cluster-admin, rebind them to a least-privileged (Cluster)Role and then remove the excessive binding: kubectl delete clusterrolebinding Notes: - Do not modify bindings with the "system:" prefix that are required for core components. - Prefer assigning narrowly scoped Roles/ClusterRoles that grant only the permissions needed. scored: true - id: 4.1.2 text: "Minimize access to secrets (Automated)" audit: | count=$(kubectl get roles --all-namespaces -o json | jq ' .items[] | select(.rules[]? | (.resources[]? == "secrets") and ((.verbs[]? == "get") or (.verbs[]? == "list") or (.verbs[]? == "watch")) )' | wc -l) if [ "$count" -gt 0 ]; then echo "SECRETS_ACCESS_FOUND" fi tests: test_items: - flag: "SECRETS_ACCESS_FOUND" set: false remediation: | Where possible, remove get, list and watch access to Secret objects in the cluster. scored: true - id: 4.1.3 text: "Minimize wildcard use in Roles and ClusterRoles (Automated)" audit: | wildcards=$(kubectl get roles --all-namespaces -o json | jq ' .items[] | select( .rules[]? | (.verbs[]? == "*" or .resources[]? == "*" or .apiGroups[]? == "*") )' | wc -l) wildcards_clusterroles=$(kubectl get clusterroles -o json | jq ' .items[] | select( .rules[]? | (.verbs[]? == "*" or .resources[]? == "*" or .apiGroups[]? == "*") )' | wc -l) total=$((wildcards + wildcards_clusterroles)) if [ "$total" -gt 0 ]; then echo "wildcards_present" fi tests: test_items: - flag: wildcards_present set: false remediation: | Where possible replace any use of wildcards in clusterroles and roles with specific objects or actions. scored: true - id: 4.1.4 text: "Ensure that default service accounts are not actively used (Automated)" audit: | echo "🔹 Default Service Accounts with automountServiceAccountToken enabled:" default_sa_count=$(kubectl get serviceaccounts --all-namespaces -o json | jq ' [.items[] | select(.metadata.name == "default" and (.automountServiceAccountToken != false))] | length') if [ "$default_sa_count" -gt 0 ]; then echo "default_sa_not_auto_mounted" fi echo "\n🔹 Pods using default ServiceAccount:" pods_using_default_sa=$(kubectl get pods --all-namespaces -o json | jq ' [.items[] | select(.spec.serviceAccountName == "default")] | length') if [ "$pods_using_default_sa" -gt 0 ]; then echo "default_sa_used_in_pods" fi tests: test_items: - flag: default_sa_not_auto_mounted set: false - flag: default_sa_used_in_pods set: false remediation: | Create explicit service accounts wherever a Kubernetes workload requires specific access to the Kubernetes API server. Modify the configuration of each default service account to include this value automountServiceAccountToken: false scored: true - id: 4.1.5 text: "Ensure that Service Account Tokens are only mounted where necessary (Automated)" audit: | echo "🔹 Pods with automountServiceAccountToken enabled:" pods_with_token_mount=$(kubectl get pods --all-namespaces -o json | jq ' [.items[] | select(.spec.automountServiceAccountToken != false)] | length') if [ "$pods_with_token_mount" -gt 0 ]; then echo "automountServiceAccountToken" fi tests: test_items: - flag: automountServiceAccountToken set: false remediation: | Modify the definition of pods and service accounts which do not need to mount service account tokens to disable it. scored: true - id: 4.1.6 text: "Avoid use of system:masters group (Automated)" audit: | found=0 for csr in $(kubectl get csr -o name 2>/dev/null | sed 's|^.*/||'); do req=$(kubectl get csr "$csr" -o jsonpath='{.spec.request}' 2>/dev/null) [ -z "$req" ] && continue if echo "$req" | base64 -d 2>/dev/null | openssl req -noout -text 2>/dev/null | grep -q 'O = system:masters'; then conds=$(kubectl get csr "$csr" -o json | jq -r '[.status.conditions[]?.type] | join(",")') echo "FOUND_SYSTEM_MASTERS_CSR:${csr}:${conds:-NONE}" found=1 fi done if [ "$found" -eq 0 ]; then echo "NO_SYSTEM_MASTERS_CREDENTIALS_FOUND" fi tests: test_items: - flag: "NO_SYSTEM_MASTERS_CREDENTIALS_FOUND" set: true compare: op: eq value: "NO_SYSTEM_MASTERS_CREDENTIALS_FOUND" remediation: | Remove the system:masters group from all users in the cluster. scored: true - id: 4.1.7 text: "Limit use of the Bind, Impersonate and Escalate permissions in the Kubernetes cluster (Manual)" type: "manual" remediation: | Where possible, remove the impersonate, bind and escalate rights from subjects. scored: false - id: 4.1.8 text: "Avoid bindings to system:anonymous (Automated)" audit: | # Flags any ClusterRoleBinding/RoleBinding that targets the user "system:anonymous". # Prints "NO_ANONYMOUS_BINDINGS" when none are found. ( kubectl get clusterrolebindings -o json | jq -r ' .items[] | select((.subjects | length) > 0) | select(any(.subjects[]?; .kind=="User" and .name=="system:anonymous" )) | "FOUND_ANONYMOUS:ClusterRoleBinding:\(.metadata.name):ROLE=\(.roleRef.kind)/\(.roleRef.name)" '; kubectl get rolebindings -A -o json | jq -r ' .items[] | select((.subjects | length) > 0) | select(any(.subjects[]?; .kind=="User" and .name=="system:anonymous" )) | "FOUND_ANONYMOUS:RoleBinding:\(.metadata.namespace):\(.metadata.name):ROLE=\(.roleRef.kind)/\(.roleRef.name)" ' ) | (grep -q '^FOUND_ANONYMOUS:' && cat || echo 'NO_ANONYMOUS_BINDINGS') tests: test_items: - flag: "NO_ANONYMOUS_BINDINGS" set: true compare: op: eq value: "NO_ANONYMOUS_BINDINGS" remediation: | Identify all clusterrolebindings and rolebindings to the user system:anonymous. Check if they are used and review the permissions associated with the binding using the commands in the Audit section above or refer to GKE documentation (https://cloud.google.com/kubernetes-engine/docs/best-practices/rbac#detect-prevent-default). Strongly consider replacing unsafe bindings with an authenticated, user-defined group. Where possible, bind to non-default, user-defined groups with least-privilege roles. If there are any unsafe bindings to the user system:anonymous, proceed to delete them after consideration for cluster operations with only necessary, safer bindings. kubectl delete clusterrolebinding [CLUSTER_ROLE_BINDING_NAME] kubectl delete rolebinding [ROLE_BINDING_NAME] --namespace [ROLE_BINDING_NAMESPACE] scored: true - id: 4.1.9 text: "Avoid non-default bindings to system:unauthenticated (Automated)" audit: | # Flags any non-default binding to the group "system:unauthenticated". # Prints "NO_NON_DEFAULT_UNAUTH_BINDINGS" when none are found. ( kubectl get clusterrolebindings -o json | jq -r ' .items[] | select(.metadata.name != "system:public-info-viewer") | select((.subjects | length) > 0) | select(any(.subjects[]?; .kind=="Group" and .name=="system:unauthenticated" )) | "FOUND_UNAUTH:ClusterRoleBinding:\(.metadata.name):ROLE=\(.roleRef.kind)/\(.roleRef.name)" '; kubectl get rolebindings -A -o json | jq -r ' .items[] | select((.subjects | length) > 0) | select(any(.subjects[]?; .kind=="Group" and .name=="system:unauthenticated" )) | "FOUND_UNAUTH:RoleBinding:\(.metadata.namespace):\(.metadata.name):ROLE=\(.roleRef.kind)/\(.roleRef.name)" ' ) | (grep -q "^FOUND_UNAUTH:" && cat || echo "NO_NON_DEFAULT_UNAUTH_BINDINGS") tests: test_items: - flag: "NO_NON_DEFAULT_UNAUTH_BINDINGS" set: true compare: op: eq value: "NO_NON_DEFAULT_UNAUTH_BINDINGS" remediation: | Identify all non-default clusterrolebindings and rolebindings to the group system:unauthenticated. Check if they are used and review the permissions associated with the binding using the commands in the Audit section above or refer to GKE documentation (https://cloud.google.com/kubernetes-engine/docs/best-practices/rbac#detect-prevent-default). Strongly consider replacing non-default, unsafe bindings with an authenticated, user- defined group. Where possible, bind to non-default, user-defined groups with least- privilege roles. If there are any non-default, unsafe bindings to the group system:unauthenticated, proceed to delete them after consideration for cluster operations with only necessary, safer bindings. kubectl delete clusterrolebinding [CLUSTER_ROLE_BINDING_NAME] kubectl delete rolebinding [ROLE_BINDING_NAME] --namespace [ROLE_BINDING_NAMESPACE] scored: true - id: 4.1.10 text: "Avoid non-default bindings to system:authenticated (Automated)" audit: | # Flags any non-default binding to the group "system:authenticated". # Allowed defaults (CRB): system:basic-user, system:discovery # Prints "NO_NON_DEFAULT_AUTH_BINDINGS" when none are found. ( kubectl get clusterrolebindings -o json | jq -r ' .items[] | select((.metadata.name != "system:basic-user") and (.metadata.name != "system:discovery")) | select((.subjects | length) > 0) | select(any(.subjects[]?; .kind=="Group" and .name=="system:authenticated" )) | "FOUND_AUTH:ClusterRoleBinding:\(.metadata.name):ROLE=\(.roleRef.kind)/\(.roleRef.name)" '; kubectl get rolebindings -A -o json | jq -r ' .items[] | select((.subjects | length) > 0) | select(any(.subjects[]?; .kind=="Group" and .name=="system:authenticated" )) | "FOUND_AUTH:RoleBinding:\(.metadata.namespace):\(.metadata.name):ROLE=\(.roleRef.kind)/\(.roleRef.name)" ' ) | (grep -q "^FOUND_AUTH:" && cat || echo "NO_NON_DEFAULT_AUTH_BINDINGS") tests: test_items: - flag: "NO_NON_DEFAULT_AUTH_BINDINGS" set: true compare: op: eq value: "NO_NON_DEFAULT_AUTH_BINDINGS" remediation: | Identify all non-default clusterrolebindings and rolebindings to the group system:authenticated. Check if they are used and review the permissions associated with the binding using the commands in the Audit section above or refer to GKE documentation. Strongly consider replacing non-default, unsafe bindings with an authenticated, user- defined group. Where possible, bind to non-default, user-defined groups with least- privilege roles. If there are any non-default, unsafe bindings to the group system:authenticated, proceed to delete them after consideration for cluster operations with only necessary, safer bindings. kubectl delete clusterrolebinding [CLUSTER_ROLE_BINDING_NAME] kubectl delete rolebinding [ROLE_BINDING_NAME] --namespace [ROLE_BINDING_NAMESPACE] scored: true - id: 4.2 text: "Pod Security Standards" checks: - id: 4.2.1 text: "Ensure that the cluster enforces Pod Security Standard Baseline profile or stricter for all namespaces. (Manual)" type: "manual" remediation: | Ensure that Pod Security Admission is in place for every namespace which contains user workloads. Run the following command to enforce the Baseline profile in a namespace: kubectl label namespace pod-security.kubernetes.io/enforce=baseline scored: false - id: 4.3 text: "Network Policies and CNI" checks: - id: 4.3.1 text: "Ensure that the CNI in use supports Network Policies (Manual)" type: "manual" remediation: | To use a CNI plugin with Network Policy, enable Network Policy in GKE, and the CNI plugin will be updated. See Recommendation 5.6.7. scored: false - id: 4.3.2 text: "Ensure that all Namespaces have Network Policies defined (Automated)" audit: | (kubectl get ns -o json; kubectl get networkpolicy -A -o json) \ | jq -rs ' (.[0].items | map(.metadata.name) | map(select(.!="kube-system" and .!="kube-public" and .!="kube-node-lease"))) as $ns | ( (.[1].items // []) | sort_by(.metadata.namespace) | group_by(.metadata.namespace) | map({key: .[0].metadata.namespace, value: length}) | from_entries ) as $np | [ $ns[] | select( ($np[.] // 0) == 0 ) ] as $missing | if ($missing|length)>0 then ($missing[] | "FOUND_NAMESPACE_WITHOUT_NETWORKPOLICY:"+.) else "ALL_NAMESPACES_HAVE_NETWORK_POLICIES" end ' tests: test_items: - flag: "ALL_NAMESPACES_HAVE_NETWORK_POLICIES" set: true compare: op: eq value: "ALL_NAMESPACES_HAVE_NETWORK_POLICIES" remediation: | Follow the documentation and create NetworkPolicy objects as needed. See: https://cloud.google.com/kubernetes-engine/docs/how-to/network-policy#creating_a_network_policy for more information. scored: true - id: 4.4 text: "Secrets Management" checks: - id: 4.4.1 text: "Prefer using secrets as files over secrets as environment variables (Automated)" audit: | output=$(kubectl get all --all-namespaces -o jsonpath='{range .items[?(@..secretKeyRef)]} {.kind} {.metadata.name} {"\n"}{end}') if [ -z "$output" ]; then echo "NO_ENV_SECRET_REFERENCES"; else echo "ENV_SECRET_REFERENCES_FOUND"; fi tests: test_items: - flag: "NO_ENV_SECRET_REFERENCES" set: true compare: op: eq value: "NO_ENV_SECRET_REFERENCES" remediation: | if possible, rewrite application code to read secrets from mounted secret files, rather than from environment variables. scored: true - id: 4.4.2 text: "Consider external secret storage (Manual)" type: "manual" remediation: | Refer to the secrets management options offered by your cloud provider or a third-party secrets management solution. scored: false - id: 4.5 text: "Extensible Admission Control" checks: - id: 4.5.1 text: "Configure Image Provenance using ImagePolicyWebhook admission controller (Manual)" type: "manual" remediation: | Follow the Kubernetes documentation and setup image provenance. Also see recommendation 5.10.4. scored: false - id: 4.6 text: "General Policies" checks: - id: 4.6.1 text: "Create administrative boundaries between resources using namespaces (Manual)" type: "manual" remediation: | Follow the documentation and create namespaces for objects in your deployment as you need them. scored: false - id: 4.6.2 text: "Ensure that the seccomp profile is set to RuntimeDefault in your pod definitions (Automated)" type: "manual" remediation: | Use security context to enable the RuntimeDefault seccomp profile in your pod definitions. An example is as below: { "namespace": "kube-system", "name": "metrics-server-v0.7.0-dbcc8ddf6-gz7d4", "seccompProfile": "RuntimeDefault" } scored: false - id: 4.6.3 text: "Apply Security Context to Your Pods and Containers (Manual)" type: "manual" remediation: | Follow the Kubernetes documentation and apply security contexts to your pods. For a suggested list of security contexts, you may refer to the CIS Google Container- Optimized OS Benchmark. scored: false - id: 4.6.4 text: "The default namespace should not be used (Automated)" audit: | output=$(kubectl get all -n default --no-headers 2>/dev/null | grep -v '^service\s\+kubernetes\s' || true) if [ -z "$output" ]; then echo "DEFAULT_NAMESPACE_UNUSED"; else echo "DEFAULT_NAMESPACE_IN_USE"; fi tests: test_items: - flag: "DEFAULT_NAMESPACE_UNUSED" set: true compare: op: eq value: "DEFAULT_NAMESPACE_UNUSED" remediation: | Ensure that namespaces are created to allow for appropriate segregation of Kubernetes resources and that all new resources are created in a specific namespace. scored: true ================================================ FILE: cfg/k3s-cis-1.23/config.yaml ================================================ --- ## Version-specific settings that override the values in cfg/config.yaml master: components: - apiserver - scheduler - controllermanager - etcd - policies apiserver: bins: - containerd scheduler: bins: - containerd controllermanager: bins: - containerd etcd: bins: - containerd datadirs: - /var/lib/rancher/k3s/server/db/etcd node: components: - kubelet - proxy kubelet: bins: - containerd defaultkubeconfig: /var/lib/rancher/k3s/agent/kubelet.kubeconfig defaultcafile: /var/lib/rancher/k3s/agent/client-ca.crt proxy: bins: - containerd defaultkubeconfig: /var/lib/rancher/k3s/agent/kubeproxy.kubeconfig policies: components: - policies ================================================ FILE: cfg/k3s-cis-1.23/controlplane.yaml ================================================ --- controls: version: "k3s-cis-1.23" id: 3 text: "Control Plane Configuration" type: "controlplane" groups: - id: 3.1 text: "Authentication and Authorization" checks: - id: 3.1.1 text: "Client certificate authentication should not be used for users (Manual)" type: "manual" remediation: | Alternative mechanisms provided by Kubernetes such as the use of OIDC should be implemented in place of client certificates. scored: false - id: 3.2 text: "Logging" checks: - id: 3.2.1 text: "Ensure that a minimal audit policy is created (Manual)" audit: "journalctl -m -u k3s | grep 'Running kube-apiserver' | tail -n1 | grep 'audit-policy-file'" type: "manual" tests: test_items: - flag: "--audit-policy-file" set: true remediation: | Create an audit policy file for your cluster. scored: false - id: 3.2.2 text: "Ensure that the audit policy covers key security concerns (Manual)" type: "manual" remediation: | Review the audit policy provided for the cluster and ensure that it covers at least the following areas, - Access to Secrets managed by the cluster. Care should be taken to only log Metadata for requests to Secrets, ConfigMaps, and TokenReviews, in order to avoid risk of logging sensitive data. - Modification of Pod and Deployment objects. - Use of `pods/exec`, `pods/portforward`, `pods/proxy` and `services/proxy`. For most requests, minimally logging at the Metadata level is recommended (the most basic level of logging). scored: false ================================================ FILE: cfg/k3s-cis-1.23/etcd.yaml ================================================ --- controls: version: "k3s-cis-1.23" id: 2 text: "Etcd Node Configuration" type: "etcd" groups: - id: 2 text: "Etcd Node Configuration" checks: - id: 2.1 text: "Ensure that the --cert-file and --key-file arguments are set as appropriate (Automated)" audit: "grep -A 4 'client-transport-security' $etcdconf | grep -E 'cert-file|key-file'" tests: bin_op: and test_items: - flag: "cert-file" set: true - flag: "key-file" set: true remediation: | Follow the etcd service documentation and configure TLS encryption. Then, edit the etcd pod specification file /etc/kubernetes/manifests/etcd.yaml on the master node and set the below parameters. --cert-file= --key-file= scored: true - id: 2.2 text: "Ensure that the --client-cert-auth argument is set to true (Automated)" audit: "grep -A 4 'client-transport-security' $etcdconf | grep 'client-cert-auth'" tests: bin_op: or test_items: - flag: "--client-cert-auth" set: true - flag: "client-cert-auth" compare: op: eq value: true set: true remediation: | Edit the etcd pod specification file $etcdconf on the master node and set the below parameter. --client-cert-auth="true" scored: true - id: 2.3 text: "Ensure that the --auto-tls argument is not set to true (Automated)" audit: "if grep -q '^auto-tls' $etcdconf;then grep '^auto-tls' $etcdconf;else echo 'notset';fi" tests: bin_op: or test_items: - flag: "--auto-tls" set: false - flag: "--auto-tls" compare: op: eq value: false remediation: | Edit the etcd pod specification file $etcdconf on the master node and either remove the --auto-tls parameter or set it to false. --auto-tls=false scored: true - id: 2.4 text: "Ensure that the --peer-cert-file and --peer-key-file arguments are set as appropriate (Automated)" audit: "grep -A 4 'peer-transport-security' $etcdconf | grep -E 'cert-file|key-file'" tests: bin_op: and test_items: - flag: "cert-file" set: true - flag: "key-file" set: true remediation: | Follow the etcd service documentation and configure peer TLS encryption as appropriate for your etcd cluster. Then, edit the etcd pod specification file $etcdconf on the master node and set the below parameters. --peer-client-file= --peer-key-file= scored: true - id: 2.5 text: "Ensure that the --peer-client-cert-auth argument is set to true (Automated)" audit: "grep -A 4 'peer-transport-security' $etcdconf | grep 'client-cert-auth'" tests: bin_op: or test_items: - flag: "--client-cert-auth" set: true - flag: "client-cert-auth" compare: op: eq value: true set: true remediation: | Edit the etcd pod specification file $etcdconf on the master node and set the below parameter. --peer-client-cert-auth=true scored: true - id: 2.6 text: "Ensure that the --peer-auto-tls argument is not set to true (Automated)" audit: "if grep -q '^peer-auto-tls' $etcdconf;then grep '^peer-auto-tls' $etcdconf;else echo 'notset';fi" tests: bin_op: or test_items: - flag: "--peer-auto-tls" set: false - flag: "--peer-auto-tls" compare: op: eq value: false set: true remediation: | Edit the etcd pod specification file $etcdconf on the master node and either remove the --peer-auto-tls parameter or set it to false. --peer-auto-tls=false scored: true - id: 2.7 text: "Ensure that a unique Certificate Authority is used for etcd (Manual)" audit: "if grep -q 'trusted-ca-file' $etcdconf;then grep 'trusted-ca-file' $etcdconf;else echo 'notset';fi" tests: test_items: - flag: "trusted-ca-file" set: true remediation: | [Manual test] Follow the etcd documentation and create a dedicated certificate authority setup for the etcd service. Then, edit the etcd pod specification file $etcdconf on the master node and set the below parameter. --trusted-ca-file= scored: false ================================================ FILE: cfg/k3s-cis-1.23/master.yaml ================================================ --- controls: version: "k3s-cis-1.23" id: 1 text: "Control Plane Security Configuration" type: "master" groups: - id: 1.1 text: "Control Plane Node Configuration Files" checks: - id: 1.1.1 text: "Ensure that the API server pod specification file permissions are set to 644 or more restrictive (Automated)" audit: "/bin/sh -c 'if test -e $apiserverconf; then stat -c permissions=%a $apiserverconf; fi'" type: "skip" tests: test_items: - flag: "permissions" compare: op: bitmask value: "644" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chmod 644 $apiserverconf scored: true - id: 1.1.2 text: "Ensure that the API server pod specification file ownership is set to root:root (Automated)" audit: "/bin/sh -c 'if test -e $apiserverconf; then stat -c %U:%G $apiserverconf; fi'" type: "skip" tests: test_items: - flag: "root:root" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chown root:root $apiserverconf scored: true - id: 1.1.3 text: "Ensure that the controller manager pod specification file permissions are set to 644 or more restrictive (Automated)" audit: "/bin/sh -c 'if test -e $controllermanagerconf; then stat -c permissions=%a $controllermanagerconf; fi'" type: "skip" tests: test_items: - flag: "permissions" compare: op: bitmask value: "644" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chmod 644 $controllermanagerconf scored: true - id: 1.1.4 text: "Ensure that the controller manager pod specification file ownership is set to root:root (Automated)" audit: "/bin/sh -c 'if test -e $controllermanagerconf; then stat -c %U:%G $controllermanagerconf; fi'" type: "skip" tests: test_items: - flag: "root:root" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chown root:root $controllermanagerconf scored: true - id: 1.1.5 text: "Ensure that the scheduler pod specification file permissions are set to 644 or more restrictive (Automated)" audit: "/bin/sh -c 'if test -e $schedulerconf; then stat -c permissions=%a $schedulerconf; fi'" type: "skip" tests: test_items: - flag: "permissions" compare: op: bitmask value: "644" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chmod 644 $schedulerconf scored: true - id: 1.1.6 text: "Ensure that the scheduler pod specification file ownership is set to root:root (Automated)" audit: "/bin/sh -c 'if test -e $schedulerconf; then stat -c %U:%G $schedulerconf; fi'" type: "skip" tests: test_items: - flag: "root:root" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chown root:root $schedulerconf scored: true - id: 1.1.7 text: "Ensure that the etcd pod specification file permissions are set to 644 or more restrictive (Automated)" audit: "/bin/sh -c 'if test -e $etcdconf; then stat -c permissions=%a $etcdconf; fi'" type: "skip" tests: test_items: - flag: "permissions" compare: op: bitmask value: "644" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chmod 644 $etcdconf scored: true - id: 1.1.8 text: "Ensure that the etcd pod specification file ownership is set to root:root (Automated)" audit: "/bin/sh -c 'if test -e $etcdconf; then stat -c %U:%G $etcdconf; fi'" type: "skip" tests: test_items: - flag: "root:root" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chown root:root $etcdconf scored: true - id: 1.1.9 text: "Ensure that the Container Network Interface file permissions are set to 644 or more restrictive (Manual)" type: "skip" audit: | ps -ef | grep $kubeletbin | grep -- --cni-conf-dir | sed 's%.*cni-conf-dir[= ]\([^ ]*\).*%\1%' | xargs -I{} find {} -mindepth 1 | xargs --no-run-if-empty stat -c permissions=%a find /var/lib/cni/networks -type f 2> /dev/null | xargs --no-run-if-empty stat -c permissions=%a use_multiple_values: true tests: test_items: - flag: "permissions" compare: op: bitmask value: "644" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chmod 644 scored: false - id: 1.1.10 text: "Ensure that the Container Network Interface file ownership is set to root:root (Manual)" type: "skip" audit: | ps -ef | grep $kubeletbin | grep -- --cni-conf-dir | sed 's%.*cni-conf-dir[= ]\([^ ]*\).*%\1%' | xargs -I{} find {} -mindepth 1 | xargs --no-run-if-empty stat -c %U:%G find /var/lib/cni/networks -type f 2> /dev/null | xargs --no-run-if-empty stat -c %U:%G use_multiple_values: true tests: test_items: - flag: "root:root" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chown root:root scored: false - id: 1.1.11 text: "Ensure that the etcd data directory permissions are set to 700 or more restrictive (Automated)" audit: "stat -c %a $etcddatadir" tests: test_items: - flag: "700" compare: op: eq value: "700" set: true remediation: | On the etcd server node, get the etcd data directory, passed as an argument --data-dir, from the command 'ps -ef | grep etcd'. Run the below command (based on the etcd data directory found above). For example, chmod 700 /var/lib/etcd scored: true - id: 1.1.12 text: "Ensure that the etcd data directory ownership is set to etcd:etcd (Automated)" audit: ps -ef | grep $etcdbin | grep -- --data-dir | sed 's%.*data-dir[= ]\([^ ]*\).*%\1%' | xargs stat -c %U:%G type: "skip" tests: test_items: - flag: "etcd:etcd" remediation: | On the etcd server node, get the etcd data directory, passed as an argument --data-dir, from the command 'ps -ef | grep etcd'. Run the below command (based on the etcd data directory found above). For example, chown etcd:etcd /var/lib/etcd scored: true - id: 1.1.13 text: "Ensure that the admin.conf file permissions are set to 600 or more restrictive (Automated)" audit: "/bin/sh -c 'if test -e /var/lib/rancher/k3s/server/cred/admin.kubeconfig; then stat -c permissions=%a /var/lib/rancher/k3s/server/cred/admin.kubeconfig'" type: "skip" tests: test_items: - flag: "600" compare: op: eq value: "600" set: true remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chmod 600 /var/lib/rancher/k3s/server/cred/admin.kubeconfig scored: true - id: 1.1.14 text: "Ensure that the admin.conf file ownership is set to root:root (Automated)" audit: "/bin/sh -c 'if test -e /var/lib/rancher/k3s/server/cred/admin.kubeconfig; then stat -c %U:%G /var/lib/rancher/k3s/server/cred/admin.kubeconfig; fi'" tests: test_items: - flag: "root:root" compare: op: eq value: "root:root" set: true remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chown root:root /etc/kubernetes/admin.conf scored: true - id: 1.1.15 text: "Ensure that the scheduler.conf file permissions are set to 644 or more restrictive (Automated)" audit: "/bin/sh -c 'if test -e /var/lib/rancher/k3s/server/cred/scheduler.kubeconfig; then stat -c permissions=%a /var/lib/rancher/k3s/server/cred/scheduler.kubeconfig; fi'" tests: test_items: - flag: "permissions" compare: op: bitmask value: "644" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chmod 644 $schedulerkubeconfig scored: true - id: 1.1.16 text: "Ensure that the scheduler.conf file ownership is set to root:root (Automated)" audit: "/bin/sh -c 'if test -e /var/lib/rancher/k3s/server/cred/scheduler.kubeconfig; then stat -c %U:%G /var/lib/rancher/k3s/server/cred/scheduler.kubeconfig; fi'" tests: test_items: - flag: "root:root" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chown root:root $schedulerkubeconfig scored: true - id: 1.1.17 text: "Ensure that the controller-manager.conf file permissions are set to 644 or more restrictive (Automated)" audit: "/bin/sh -c 'if test -e /var/lib/rancher/k3s/server/cred/controller.kubeconfig; then stat -c permissions=%a /var/lib/rancher/k3s/server/cred/controller.kubeconfig; fi'" tests: test_items: - flag: "permissions" compare: op: bitmask value: "644" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chmod 644 $controllermanagerkubeconfig scored: true - id: 1.1.18 text: "Ensure that the controller-manager.conf file ownership is set to root:root (Automated)" audit: "stat -c %U:%G /var/lib/rancher/k3s/server/tls" tests: test_items: - flag: "root:root" compare: op: eq value: "root:root" set: true remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chown root:root $controllermanagerkubeconfig scored: true - id: 1.1.19 text: "Ensure that the Kubernetes PKI directory and file ownership is set to root:root (Automated)" audit: "find /var/lib/rancher/k3s/server/tls | xargs stat -c %U:%G" use_multiple_values: true tests: test_items: - flag: "root:root" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chown -R root:root /etc/kubernetes/pki/ scored: true - id: 1.1.20 text: "Ensure that the Kubernetes PKI certificate file permissions are set to 644 or more restrictive (Manual)" audit: "stat -c %n\ %a /var/lib/rancher/k3s/server/tls/*.crt" use_multiple_values: true tests: test_items: - flag: "permissions" compare: op: bitmask value: "644" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chmod -R 644 /etc/kubernetes/pki/*.crt scored: false - id: 1.1.21 text: "Ensure that the Kubernetes PKI key file permissions are set to 600 (Manual)" audit: "stat -c %n\ %a /var/lib/rancher/k3s/server/tls/*.key" use_multiple_values: true tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chmod -R 600 /etc/kubernetes/pki/*.key scored: false - id: 1.2 text: "API Server" checks: - id: 1.2.1 text: "Ensure that the --anonymous-auth argument is set to false (Manual)" audit: "journalctl -m -u k3s | grep 'Running kube-apiserver' | tail -n1 | grep 'anonymous-auth'" type: manual tests: test_items: - flag: "--anonymous-auth" compare: op: eq value: false remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the below parameter. --anonymous-auth=false scored: false - id: 1.2.2 text: "Ensure that the --token-auth-file parameter is not set (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--token-auth-file" set: false remediation: | Follow the documentation and configure alternate mechanisms for authentication. Then, edit the API server pod specification file $apiserverconf on the control plane node and remove the --token-auth-file= parameter. scored: true - id: 1.2.3 text: "Ensure that the --DenyServiceExternalIPs is not set (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: bin_op: or test_items: - flag: "--enable-admission-plugins" compare: op: nothave value: "DenyServiceExternalIPs" set: true - flag: "--enable-admission-plugins" set: false remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and remove the `DenyServiceExternalIPs` from enabled admission plugins. scored: true - id: 1.2.4 text: "Ensure that the --kubelet-https argument is set to true (Automated)" audit: "journalctl -m -u k3s | grep 'Running kube-apiserver' | tail -n1 | grep 'kubelet-https'" type: "skip" tests: bin_op: or test_items: - flag: "--kubelet-https" compare: op: eq value: true - flag: "--kubelet-https" set: false remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and remove the --kubelet-https parameter. scored: true - id: 1.2.5 text: "Ensure that the --kubelet-client-certificate and --kubelet-client-key arguments are set as appropriate (Automated)" audit: "journalctl -m -u k3s | grep 'Running kube-apiserver' | tail -n1 | grep 'kubelet-certificate-authority'" tests: bin_op: and test_items: - flag: "--kubelet-client-certificate" - flag: "--kubelet-client-key" remediation: | Follow the Kubernetes documentation and set up the TLS connection between the apiserver and kubelets. Then, edit API server pod specification file $apiserverconf on the control plane node and set the kubelet client certificate and key parameters as below. --kubelet-client-certificate= --kubelet-client-key= scored: true - id: 1.2.6 text: "Ensure that the --kubelet-certificate-authority argument is set as appropriate (Automated)" audit: "journalctl -m -u k3s | grep 'Running kube-apiserver' | tail -n1 | grep 'kubelet-certificate-authority'" tests: test_items: - flag: "--kubelet-certificate-authority" remediation: | Follow the Kubernetes documentation and setup the TLS connection between the apiserver and kubelets. Then, edit the API server pod specification file $apiserverconf on the control plane node and set the --kubelet-certificate-authority parameter to the path to the cert file for the certificate authority. --kubelet-certificate-authority= scored: true - id: 1.2.7 text: "Ensure that the --authorization-mode argument is not set to AlwaysAllow (Automated)" audit: "journalctl -m -u k3s | grep 'Running kube-apiserver' | tail -n1 | grep 'authorization-mode'" tests: test_items: - flag: "--authorization-mode" compare: op: nothave value: "AlwaysAllow" remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the --authorization-mode parameter to values other than AlwaysAllow. One such example could be as below. --authorization-mode=RBAC scored: true - id: 1.2.8 text: "Ensure that the --authorization-mode argument includes Node (Automated)" audit: "journalctl -m -u k3s | grep 'Running kube-apiserver' | tail -n1 | grep 'authorization-mode'" tests: test_items: - flag: "--authorization-mode" compare: op: has value: "Node" remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the --authorization-mode parameter to a value that includes Node. --authorization-mode=Node,RBAC scored: true - id: 1.2.9 text: "Ensure that the --authorization-mode argument includes RBAC (Automated)" audit: "journalctl -m -u k3s | grep 'Running kube-apiserver' | tail -n1 | grep 'authorization-mode'" tests: test_items: - flag: "--authorization-mode" compare: op: has value: "RBAC" remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the --authorization-mode parameter to a value that includes RBAC, for example `--authorization-mode=Node,RBAC`. scored: true - id: 1.2.10 text: "Ensure that the admission control plugin EventRateLimit is set (Manual)" audit: "journalctl -m -u k3s | grep 'Running kube-apiserver' | tail -n1 | grep 'enable-admission-plugins'" tests: test_items: - flag: "--enable-admission-plugins" compare: op: has value: "EventRateLimit" remediation: | Follow the Kubernetes documentation and set the desired limits in a configuration file. Then, edit the API server pod specification file $apiserverconf and set the below parameters. --enable-admission-plugins=...,EventRateLimit,... --admission-control-config-file= scored: false - id: 1.2.11 text: "Ensure that the admission control plugin AlwaysAdmit is not set (Automated)" audit: "journalctl -m -u k3s | grep 'Running kube-apiserver' | tail -n1 | grep 'enable-admission-plugins'" tests: bin_op: or test_items: - flag: "--enable-admission-plugins" compare: op: nothave value: AlwaysAdmit - flag: "--enable-admission-plugins" set: false remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and either remove the --enable-admission-plugins parameter, or set it to a value that does not include AlwaysAdmit. scored: true - id: 1.2.12 text: "Ensure that the admission control plugin AlwaysPullImages is set (Manual)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--enable-admission-plugins" compare: op: has value: "AlwaysPullImages" remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the --enable-admission-plugins parameter to include AlwaysPullImages. --enable-admission-plugins=...,AlwaysPullImages,... scored: false - id: 1.2.13 text: "Ensure that the admission control plugin SecurityContextDeny is set if PodSecurityPolicy is not used (Manual)" audit: "journalctl -m -u k3s | grep 'Running kube-apiserver' | tail -n1 | grep 'enable-admission-plugins'" tests: bin_op: or test_items: - flag: "--enable-admission-plugins" compare: op: has value: "SecurityContextDeny" - flag: "--enable-admission-plugins" compare: op: has value: "PodSecurityPolicy" remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the --enable-admission-plugins parameter to include SecurityContextDeny, unless PodSecurityPolicy is already in place. --enable-admission-plugins=...,SecurityContextDeny,... scored: false - id: 1.2.14 text: "Ensure that the admission control plugin ServiceAccount is set (Automated)" audit: "journalctl -m -u k3s | grep 'Running kube-apiserver' | tail -n1 | grep -v grep" tests: bin_op: or test_items: - flag: "--disable-admission-plugins" compare: op: nothave value: "ServiceAccount" - flag: "--disable-admission-plugins" set: false remediation: | Follow the documentation and create ServiceAccount objects as per your environment. Then, edit the API server pod specification file $apiserverconf on the control plane node and ensure that the --disable-admission-plugins parameter is set to a value that does not include ServiceAccount. scored: true - id: 1.2.15 text: "Ensure that the admission control plugin NamespaceLifecycle is set (Automated)" audit: "journalctl -m -u k3s | grep 'Running kube-apiserver' | tail -n1 | grep -v grep" tests: bin_op: or test_items: - flag: "--disable-admission-plugins" compare: op: nothave value: "NamespaceLifecycle" - flag: "--disable-admission-plugins" set: false remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the --disable-admission-plugins parameter to ensure it does not include NamespaceLifecycle. scored: true - id: 1.2.16 text: "Ensure that the admission control plugin NodeRestriction is set (Automated)" audit: "journalctl -m -u k3s | grep 'Running kube-apiserver' | tail -n1 | grep 'enable-admission-plugins'" tests: test_items: - flag: "--enable-admission-plugins" compare: op: has value: "NodeRestriction" remediation: | Follow the Kubernetes documentation and configure NodeRestriction plug-in on kubelets. Then, edit the API server pod specification file $apiserverconf on the control plane node and set the --enable-admission-plugins parameter to a value that includes NodeRestriction. --enable-admission-plugins=...,NodeRestriction,... scored: true - id: 1.2.17 text: "Ensure that the --secure-port argument is not set to 0 (Automated)" audit: "journalctl -m -u k3s | grep 'Running kube-apiserver' | tail -n1 | grep 'secure-port'" tests: bin_op: or test_items: - flag: "--secure-port" compare: op: gt value: 0 - flag: "--secure-port" set: false remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and either remove the --secure-port parameter or set it to a different (non-zero) desired port. scored: true - id: 1.2.18 text: "Ensure that the --profiling argument is set to false (Automated)" audit: "journalctl -m -u k3s | grep 'Running kube-apiserver' | tail -n1 | grep 'profiling'" tests: test_items: - flag: "--profiling" compare: op: eq value: false remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the below parameter. --profiling=false scored: true - id: 1.2.19 text: "Ensure that the --audit-log-path argument is set (Automated)" audit: "journalctl -m -u k3s | grep 'Running kube-apiserver' | tail -n1 | grep -v grep" type: "skip" tests: test_items: - flag: "--audit-log-path" remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the --audit-log-path parameter to a suitable path and file where you would like audit logs to be written, for example, --audit-log-path=/var/log/apiserver/audit.log scored: true - id: 1.2.20 text: "Ensure that the --audit-log-maxage argument is set to 30 or as appropriate (Automated)" audit: "journalctl -m -u k3s | grep 'Running kube-apiserver' | tail -n1 | grep -v grep" type: "skip" tests: test_items: - flag: "--audit-log-maxage" compare: op: gte value: 30 remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the --audit-log-maxage parameter to 30 or as an appropriate number of days, for example, --audit-log-maxage=30 scored: true - id: 1.2.21 text: "Ensure that the --audit-log-maxbackup argument is set to 10 or as appropriate (Automated)" audit: "journalctl -m -u k3s | grep 'Running kube-apiserver' | tail -n1 | grep -v grep" type: "skip" tests: test_items: - flag: "--audit-log-maxbackup" compare: op: gte value: 10 remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the --audit-log-maxbackup parameter to 10 or to an appropriate value. For example, --audit-log-maxbackup=10 scored: true - id: 1.2.22 text: "Ensure that the --audit-log-maxsize argument is set to 100 or as appropriate (Automated)" audit: "journalctl -m -u k3s | grep 'Running kube-apiserver' | tail -n1 | grep -v grep" type: "skip" tests: test_items: - flag: "--audit-log-maxsize" compare: op: gte value: 100 remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the --audit-log-maxsize parameter to an appropriate size in MB. For example, to set it as 100 MB, --audit-log-maxsize=100 scored: true - id: 1.2.23 text: "Ensure that the --request-timeout argument is set as appropriate (Automated)" audit: "journalctl -m -u k3s | grep 'Running kube-apiserver' | tail -n1 | grep -v grep" type: "skip" tests: test_items: - flag: "--request-timeout" remediation: | Edit the API server pod specification file $apiserverconf and set the below parameter as appropriate and if needed. For example, --request-timeout=300s scored: true - id: 1.2.24 text: "Ensure that the --service-account-lookup argument is set to true (Automated)" audit: "journalctl -m -u k3s | grep 'Running kube-apiserver' | tail -n1 | grep -v grep" tests: bin_op: or test_items: - flag: "--service-account-lookup" set: false - flag: "--service-account-lookup" compare: op: eq value: true remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the below parameter. --service-account-lookup=true Alternatively, you can delete the --service-account-lookup parameter from this file so that the default takes effect. scored: true - id: 1.2.25 text: "Ensure that the --service-account-key-file argument is set as appropriate (Automated)" audit: "journalctl -m -u k3s | grep 'Running kube-apiserver' | tail -n1 | grep -v grep" type: "skip" tests: test_items: - flag: "--service-account-key-file" remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the --service-account-key-file parameter to the public key file for service accounts. For example, --service-account-key-file= scored: true - id: 1.2.26 text: "Ensure that the --etcd-certfile and --etcd-keyfile arguments are set as appropriate (Automated)" audit: "journalctl -m -u k3s | grep -m1 'Running kube-apiserver'" tests: bin_op: and test_items: - flag: "--etcd-certfile" set: true - flag: "--etcd-keyfile" set: true remediation: | Follow the Kubernetes documentation and set up the TLS connection between the apiserver and etcd. Then, edit the API server pod specification file $apiserverconf on the control plane node and set the etcd certificate and key file parameters. --etcd-certfile= --etcd-keyfile= scored: true - id: 1.2.27 text: "Ensure that the --tls-cert-file and --tls-private-key-file arguments are set as appropriate (Automated)" audit: "journalctl -m -u k3s | grep -A1 'Running kube-apiserver' | tail -n2" tests: bin_op: and test_items: - flag: "--tls-cert-file" set: true - flag: "--tls-private-key-file" set: true remediation: | Follow the Kubernetes documentation and set up the TLS connection on the apiserver. Then, edit the API server pod specification file $apiserverconf on the control plane node and set the TLS certificate and private key file parameters. --tls-cert-file= --tls-private-key-file= scored: true - id: 1.2.28 text: "Ensure that the --client-ca-file argument is set as appropriate (Automated)" audit: "journalctl -m -u k3s | grep 'Running kube-apiserver' | tail -n1 | grep 'client-ca-file'" tests: test_items: - flag: "--client-ca-file" remediation: | Follow the Kubernetes documentation and set up the TLS connection on the apiserver. Then, edit the API server pod specification file $apiserverconf on the control plane node and set the client certificate authority file. --client-ca-file= scored: true - id: 1.2.29 text: "Ensure that the --etcd-cafile argument is set as appropriate (Automated)" audit: "journalctl -m -u k3s | grep 'Running kube-apiserver' | tail -n1 | grep 'etcd-cafile'" tests: test_items: - flag: "--etcd-cafile" remediation: | Follow the Kubernetes documentation and set up the TLS connection between the apiserver and etcd. Then, edit the API server pod specification file $apiserverconf on the control plane node and set the etcd certificate authority file parameter. --etcd-cafile= scored: true - id: 1.2.30 text: "Ensure that the --encryption-provider-config argument is set as appropriate (Manual)" audit: "journalctl -m -u k3s | grep 'Running kube-apiserver' | tail -n1 | grep 'encryption-provider-config'" tests: test_items: - flag: "--encryption-provider-config" remediation: | Follow the Kubernetes documentation and configure a EncryptionConfig file. Then, edit the API server pod specification file $apiserverconf on the control plane node and set the --encryption-provider-config parameter to the path of that file. For example, --encryption-provider-config= scored: false - id: 1.2.31 text: "Ensure that encryption providers are appropriately configured (Manual)" audit: "grep aescbc /path/to/encryption-config.json" type: "manual" remediation: | Follow the Kubernetes documentation and configure a EncryptionConfig file. In this file, choose aescbc, kms or secretbox as the encryption provider. scored: false - id: 1.2.32 text: "Ensure that the API Server only makes use of Strong Cryptographic Ciphers (Manual)" audit: "journalctl -m -u k3s | grep 'Running kube-apiserver' | tail -n1 | grep 'tls-cipher-suites'" tests: test_items: - flag: "--tls-cipher-suites" compare: op: valid_elements value: "TLS_AES_128_GCM_SHA256,TLS_AES_256_GCM_SHA384,TLS_CHACHA20_POLY1305_SHA256,TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA,TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,TLS_RSA_WITH_3DES_EDE_CBC_SHA,TLS_RSA_WITH_AES_128_CBC_SHA,TLS_RSA_WITH_AES_128_GCM_SHA256,TLS_RSA_WITH_AES_256_CBC_SHA,TLS_RSA_WITH_AES_256_GCM_SHA384" remediation: | Edit the API server pod specification file /etc/kubernetes/manifests/kube-apiserver.yaml on the control plane node and set the below parameter. --tls-cipher-suites=TLS_AES_128_GCM_SHA256,TLS_AES_256_GCM_SHA384,TLS_CHACHA20_POLY1305_SHA256, TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA,TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305, TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,TLS_RSA_WITH_3DES_EDE_CBC_SHA,TLS_RSA_WITH_AES_128_CBC_SHA, TLS_RSA_WITH_AES_128_GCM_SHA256,TLS_RSA_WITH_AES_256_CBC_SHA,TLS_RSA_WITH_AES_256_GCM_SHA384 scored: false - id: 1.3 text: "Controller Manager" checks: - id: 1.3.1 text: "Ensure that the --terminated-pod-gc-threshold argument is set as appropriate (Manual)" audit: "journalctl -m -u k3s | grep 'Running kube-controller-manager' | tail -n1 | grep 'terminated-pod-gc-threshold'" tests: test_items: - flag: "--terminated-pod-gc-threshold" remediation: | Edit the Controller Manager pod specification file $controllermanagerconf on the control plane node and set the --terminated-pod-gc-threshold to an appropriate threshold, for example, --terminated-pod-gc-threshold=10 scored: false - id: 1.3.2 text: "Ensure that the --profiling argument is set to false (Automated)" audit: "journalctl -m -u k3s | grep 'Running kube-controller-manager' | tail -n1 | grep 'profiling'" tests: test_items: - flag: "--profiling" compare: op: eq value: false remediation: | Edit the Controller Manager pod specification file $controllermanagerconf on the control plane node and set the below parameter. --profiling=false scored: true - id: 1.3.3 text: "Ensure that the --use-service-account-credentials argument is set to true (Automated)" audit: "journalctl -m -u k3s | grep 'Running kube-controller-manager' | tail -n1 | grep 'use-service-account-credentials'" tests: test_items: - flag: "--use-service-account-credentials" compare: op: noteq value: false remediation: | Edit the Controller Manager pod specification file $controllermanagerconf on the control plane node to set the below parameter. --use-service-account-credentials=true scored: true - id: 1.3.4 text: "Ensure that the --service-account-private-key-file argument is set as appropriate (Automated)" audit: "journalctl -m -u k3s | grep 'Running kube-controller-manager' | tail -n1 | grep 'service-account-private-key-file'" tests: test_items: - flag: "--service-account-private-key-file" remediation: | Edit the Controller Manager pod specification file $controllermanagerconf on the control plane node and set the --service-account-private-key-file parameter to the private key file for service accounts. --service-account-private-key-file= scored: true - id: 1.3.5 text: "Ensure that the --root-ca-file argument is set as appropriate (Automated)" audit: "journalctl -m -u k3s | grep 'Running kube-controller-manager' | tail -n1 | grep 'root-ca-file'" tests: test_items: - flag: "--root-ca-file" remediation: | Edit the Controller Manager pod specification file $controllermanagerconf on the control plane node and set the --root-ca-file parameter to the certificate bundle file`. --root-ca-file= scored: true - id: 1.3.6 text: "Ensure that the RotateKubeletServerCertificate argument is set to true (Automated)" audit: "journalctl -m -u k3s | grep 'Running kube-controller-manager' | tail -n1 | grep 'RotateKubeletServerCertificate'" type: "skip" tests: bin_op: or test_items: - flag: "--feature-gates" compare: op: nothave value: "RotateKubeletServerCertificate=false" set: true - flag: "--feature-gates" set: false remediation: | Edit the Controller Manager pod specification file $controllermanagerconf on the control plane node and set the --feature-gates parameter to include RotateKubeletServerCertificate=true. --feature-gates=RotateKubeletServerCertificate=true scored: true - id: 1.3.7 text: "Ensure that the --bind-address argument is set to 127.0.0.1 (Automated)" audit: "/bin/ps -ef | grep $controllermanagerbin | grep -v grep" tests: bin_op: or test_items: - flag: "--bind-address" compare: op: eq value: "127.0.0.1" set: true - flag: "--bind-address" set: false remediation: | Edit the Controller Manager pod specification file $controllermanagerconf on the control plane node and ensure the correct value for the --bind-address parameter scored: true - id: 1.4 text: "Scheduler" checks: - id: 1.4.1 text: "Ensure that the --profiling argument is set to false (Automated)" audit: "journalctl -m -u k3s | grep 'Running kube-scheduler' | tail -n1" tests: test_items: - flag: "--profiling" compare: op: eq value: false set: true remediation: | Edit the Scheduler pod specification file $schedulerconf file on the control plane node and set the below parameter. --profiling=false scored: true - id: 1.4.2 text: "Ensure that the --bind-address argument is set to 127.0.0.1 (Automated)" audit: "journalctl -m -u k3s | grep 'Running kube-scheduler' | tail -n1 | grep 'bind-address'" tests: bin_op: or test_items: - flag: "--bind-address" compare: op: eq value: "127.0.0.1" set: true - flag: "--bind-address" set: false remediation: | Edit the Scheduler pod specification file $schedulerconf on the control plane node and ensure the correct value for the --bind-address parameter scored: true ================================================ FILE: cfg/k3s-cis-1.23/node.yaml ================================================ --- controls: version: "k3s-cis-1.23" id: 4 text: "Worker Node Security Configuration" type: "node" groups: - id: 4.1 text: "Worker Node Configuration Files" checks: - id: 4.1.1 text: "Ensure that the kubelet service file permissions are set to 644 or more restrictive (Automated)" audit: '/bin/sh -c ''if test -e $kubeletsvc; then stat -c permissions=%a $kubeletsvc; fi'' ' type: "skip" tests: test_items: - flag: "permissions" compare: op: bitmask value: "644" remediation: | Run the below command (based on the file location on your system) on the each worker node. For example, chmod 644 $kubeletsvc scored: true - id: 4.1.2 text: "Ensure that the kubelet service file ownership is set to root:root (Automated)" audit: '/bin/sh -c ''if test -e $kubeletsvc; then stat -c %U:%G $kubeletsvc; fi'' ' type: "skip" tests: test_items: - flag: root:root remediation: | Run the below command (based on the file location on your system) on the each worker node. For example, chown root:root $kubeletsvc scored: true - id: 4.1.3 text: "If proxy kubeconfig file exists ensure permissions are set to 644 or more restrictive (Manual)" audit: 'stat -c %a /var/lib/rancher/k3s/agent/kubeproxy.kubeconfig' tests: bin_op: or test_items: - flag: "permissions" set: true compare: op: bitmask value: "644" remediation: | Run the below command (based on the file location on your system) on the each worker node. For example, chmod 644 $proxykubeconfig scored: false - id: 4.1.4 text: "If proxy kubeconfig file exists ensure ownership is set to root:root (Manual)" audit: '/bin/sh -c ''if test -e /var/lib/rancher/k3s/agent/kubeproxy.kubeconfig; then stat -c %U:%G /var/lib/rancher/k3s/agent/kubeproxy.kubeconfig; fi'' ' tests: bin_op: or test_items: - flag: root:root remediation: | Run the below command (based on the file location on your system) on the each worker node. For example, chown root:root $proxykubeconfig scored: false - id: 4.1.5 text: "Ensure that the --kubeconfig kubelet.conf file permissions are set to 644 or more restrictive (Automated)" audit: 'stat -c %a /var/lib/rancher/k3s/agent/kubelet.kubeconfig ' tests: test_items: - flag: "644" compare: op: eq value: "644" set: true remediation: | Run the below command (based on the file location on your system) on the each worker node. For example, chmod 644 $kubeletkubeconfig scored: true - id: 4.1.6 text: "Ensure that the --kubeconfig kubelet.conf file ownership is set to root:root (Automated)" audit: 'stat -c %U:%G /var/lib/rancher/k3s/agent/kubelet.kubeconfig' tests: test_items: - flag: "root:root" compare: op: eq value: "root:root" set: true remediation: | Run the below command (based on the file location on your system) on the each worker node. For example, chown root:root $kubeletkubeconfig scored: true - id: 4.1.7 text: "Ensure that the certificate authorities file permissions are set to 644 or more restrictive (Manual)" audit: "stat -c %a /var/lib/rancher/k3s/server/tls/server-ca.crt" tests: test_items: - flag: "644" compare: op: eq value: "644" set: true - flag: "640" compare: op: eq value: "640" set: true - flag: "600" compare: op: eq value: "600" set: true - flag: "444" compare: op: eq value: "444" set: true - flag: "440" compare: op: eq value: "440" set: true - flag: "400" compare: op: eq value: "400" set: true - flag: "000" compare: op: eq value: "000" set: true bin_op: or remediation: | Run the following command to modify the file permissions of the --client-ca-file chmod 644 scored: true - id: 4.1.8 text: "Ensure that the client certificate authorities file ownership is set to root:root (Manual)" audit: "stat -c %U:%G /var/lib/rancher/k3s/server/tls/client-ca.crt" tests: test_items: - flag: root:root remediation: | Run the following command to modify the ownership of the --client-ca-file. chown root:root scored: false - id: 4.1.9 text: "Ensure that the kubelet --config configuration file has permissions set to 644 or more restrictive (Automated)" audit: '/bin/sh -c ''if test -e $kubeletconf; then stat -c permissions=%a $kubeletconf; fi'' ' type: "skip" tests: test_items: - flag: "permissions" compare: op: bitmask value: "644" remediation: | Run the following command (using the config file location identified in the Audit step) chmod 644 $kubeletconf scored: true - id: 4.1.10 text: "Ensure that the kubelet --config configuration file ownership is set to root:root (Automated)" audit: '/bin/sh -c ''if test -e $kubeletconf; then stat -c %U:%G $kubeletconf; fi'' ' type: "skip" tests: test_items: - flag: root:root remediation: | Run the following command (using the config file location identified in the Audit step) chown root:root $kubeletconf scored: true - id: 4.2 text: "Kubelet" checks: - id: 4.2.1 text: "Ensure that the --anonymous-auth argument is set to false (Automated)" audit: '/bin/sh -c ''if test $(journalctl -m -u k3s -u k3s-agent | grep "Running kubelet" | wc -l) -gt 0; then journalctl -m -u k3s -u k3s-agent | grep "Running kubelet" | tail -n1 | grep "anonymous-auth" | grep -v grep; else echo "--anonymous-auth=false"; fi'' ' tests: test_items: - flag: "--anonymous-auth" path: '{.authentication.anonymous.enabled}' compare: op: eq value: false set: true remediation: | If using a Kubelet config file, edit the file to set `authentication: anonymous: enabled` to `false`. If using executable arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_SYSTEM_PODS_ARGS variable. `--anonymous-auth=false` Based on your system, restart the kubelet service. For example, systemctl daemon-reload systemctl restart kubelet.service scored: true - id: 4.2.2 text: "Ensure that the --authorization-mode argument is not set to AlwaysAllow (Automated)" audit: '/bin/sh -c ''if test $(journalctl -m -u k3s -u k3s-agent | grep "Running kubelet" | wc -l) -gt 0; then journalctl -m -u k3s -u k3s-agent | grep "Running kubelet" | tail -n1 | grep "authorization-mode" | grep -v grep; else echo "--authorization-mode=Webhook"; fi'' ' tests: test_items: - flag: --authorization-mode path: '{.authorization.mode}' compare: op: nothave value: AlwaysAllow set: true remediation: | If using a Kubelet config file, edit the file to set `authorization.mode` to Webhook. If using executable arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_AUTHZ_ARGS variable. --authorization-mode=Webhook Based on your system, restart the kubelet service. For example, systemctl daemon-reload systemctl restart kubelet.service scored: true - id: 4.2.3 text: "Ensure that the --client-ca-file argument is set as appropriate (Automated)" audit: '/bin/sh -c ''if test $(journalctl -m -u k3s -u k3s-agent | grep "Running kubelet" | wc -l) -gt 0; then journalctl -m -u k3s -u k3s-agent | grep "Running kubelet" | tail -n1 | grep "client-ca-file" | grep -v grep; else echo "--client-ca-file=/var/lib/rancher/k3s/server/tls/request-header-ca.crt"; fi'' ' tests: test_items: - flag: --client-ca-file path: '{.authentication.x509.clientCAFile}' set: true remediation: | If using a Kubelet config file, edit the file to set `authentication.x509.clientCAFile` to the location of the client CA file. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_AUTHZ_ARGS variable. --client-ca-file= Based on your system, restart the kubelet service. For example, systemctl daemon-reload systemctl restart kubelet.service scored: true - id: 4.2.4 text: "Ensure that the --read-only-port argument is set to 0 (Manual)" audit: "journalctl -m -u k3s | grep 'Running kubelet' | tail -n1 | grep 'read-only-port' " tests: bin_op: or test_items: - flag: "--read-only-port" path: '{.readOnlyPort}' compare: op: eq value: 0 - flag: "--read-only-port" path: '{.readOnlyPort}' set: false remediation: | If using a Kubelet config file, edit the file to set `readOnlyPort` to 0. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_SYSTEM_PODS_ARGS variable. --read-only-port=0 Based on your system, restart the kubelet service. For example, systemctl daemon-reload systemctl restart kubelet.service scored: false - id: 4.2.5 text: "Ensure that the --streaming-connection-idle-timeout argument is not set to 0 (Manual)" audit: "journalctl -m -u k3s | grep 'Running kubelet' | tail -n1 | grep 'streaming-connection-idle-timeout'" tests: test_items: - flag: --streaming-connection-idle-timeout path: '{.streamingConnectionIdleTimeout}' compare: op: noteq value: 0 - flag: --streaming-connection-idle-timeout path: '{.streamingConnectionIdleTimeout}' set: false bin_op: or remediation: | If using a Kubelet config file, edit the file to set `streamingConnectionIdleTimeout` to a value other than 0. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_SYSTEM_PODS_ARGS variable. --streaming-connection-idle-timeout=5m Based on your system, restart the kubelet service. For example, systemctl daemon-reload systemctl restart kubelet.service scored: false - id: 4.2.6 text: "Ensure that the --protect-kernel-defaults argument is set to true (Automated)" audit: "journalctl -m -u k3s | grep 'Running kubelet' | tail -n1 | grep 'protect-kernel-defaults'" type: "skip" tests: test_items: - flag: --protect-kernel-defaults path: '{.protectKernelDefaults}' compare: op: eq value: true set: true remediation: | If using a Kubelet config file, edit the file to set `protectKernelDefaults` to `true`. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_SYSTEM_PODS_ARGS variable. --protect-kernel-defaults=true Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: true - id: 4.2.7 text: "Ensure that the --make-iptables-util-chains argument is set to true (Automated)" audit: "journalctl -m -u k3s | grep 'Running kubelet' | tail -n1 | grep 'make-iptables-util-chains'" type: "skip" tests: test_items: - flag: --make-iptables-util-chains path: '{.makeIPTablesUtilChains}' compare: op: eq value: true set: true - flag: --make-iptables-util-chains path: '{.makeIPTablesUtilChains}' set: false bin_op: or remediation: | If using a Kubelet config file, edit the file to set `makeIPTablesUtilChains` to `true`. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and remove the --make-iptables-util-chains argument from the KUBELET_SYSTEM_PODS_ARGS variable. Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: true - id: 4.2.8 text: "Ensure that the --hostname-override argument is not set (Manual)" # This is one of those properties that can only be set as a command line argument. # To check if the property is set as expected, we need to parse the kubelet command # instead reading the Kubelet Configuration file. audit: "/bin/ps -fC $kubeletbin " type: "skip" tests: test_items: - flag: --hostname-override set: false remediation: | Edit the kubelet service file $kubeletsvc on each worker node and remove the --hostname-override argument from the KUBELET_SYSTEM_PODS_ARGS variable. Based on your system, restart the kubelet service. For example, systemctl daemon-reload systemctl restart kubelet.service scored: false - id: 4.2.9 text: "Ensure that the --event-qps argument is set to 0 or a level which ensures appropriate event capture (Manual)" audit: "/bin/ps -fC $kubeletbin" type: "manual" tests: test_items: - flag: --event-qps path: '{.eventRecordQPS}' compare: op: eq value: 0 remediation: | If using a Kubelet config file, edit the file to set `eventRecordQPS` to an appropriate level. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_SYSTEM_PODS_ARGS variable. Based on your system, restart the kubelet service. For example, systemctl daemon-reload systemctl restart kubelet.service scored: false - id: 4.2.10 text: "Ensure that the --tls-cert-file and --tls-private-key-file arguments are set as appropriate (Manual)" audit: "journalctl -m -u k3s | grep 'Running kubelet' | tail -n1" tests: test_items: - flag: --tls-cert-file path: '/var/lib/rancher/k3s/agent/serving-kubelet.crt' - flag: --tls-private-key-file path: '/var/lib/rancher/k3s/agent/serving-kubelet.key' remediation: | If using a Kubelet config file, edit the file to set `tlsCertFile` to the location of the certificate file to use to identify this Kubelet, and `tlsPrivateKeyFile` to the location of the corresponding private key file. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameters in KUBELET_CERTIFICATE_ARGS variable. --tls-cert-file= --tls-private-key-file= Based on your system, restart the kubelet service. For example, systemctl daemon-reload systemctl restart kubelet.service scored: false - id: 4.2.11 text: "Ensure that the --rotate-certificates argument is not set to false (Automated)" audit: "/bin/ps -fC $kubeletbin" type: "skip" tests: test_items: - flag: --rotate-certificates path: '{.rotateCertificates}' compare: op: eq value: true - flag: --rotate-certificates path: '{.rotateCertificates}' set: false bin_op: or remediation: | If using a Kubelet config file, edit the file to add the line `rotateCertificates` to `true` or remove it altogether to use the default value. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and remove --rotate-certificates=false argument from the KUBELET_CERTIFICATE_ARGS variable. Based on your system, restart the kubelet service. For example, systemctl daemon-reload systemctl restart kubelet.service scored: true - id: 4.2.12 text: "Verify that the RotateKubeletServerCertificate argument is set to true (Manual)" audit: "/bin/ps -fC $kubeletbin" type: "skip" tests: bin_op: or test_items: - flag: RotateKubeletServerCertificate path: '{.featureGates.RotateKubeletServerCertificate}' compare: op: nothave value: false - flag: RotateKubeletServerCertificate path: '{.featureGates.RotateKubeletServerCertificate}' set: false remediation: | Edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_CERTIFICATE_ARGS variable. --feature-gates=RotateKubeletServerCertificate=true Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: false - id: 4.2.13 text: "Ensure that the Kubelet only makes use of Strong Cryptographic Ciphers (Manual)" audit: "/bin/ps -fC $kubeletbin" type: "manual" tests: test_items: - flag: --tls-cipher-suites path: '{range .tlsCipherSuites[:]}{}{'',''}{end}' compare: op: valid_elements value: 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 remediation: | If using a Kubelet config file, edit the file to set `tlsCipherSuites` to 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 or to a subset of these values. If using executable arguments, edit the kubelet service file $kubeletsvc on each worker node and set the --tls-cipher-suites parameter as follows, or to a subset of these values. --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 Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: false ================================================ FILE: cfg/k3s-cis-1.23/policies.yaml ================================================ --- controls: version: "k3s-cis-1.23" id: 5 text: "Kubernetes Policies" type: "policies" groups: - id: 5.1 text: "RBAC and Service Accounts" checks: - id: 5.1.1 text: "Ensure that the cluster-admin role is only used where required (Manual)" type: "manual" remediation: | Identify all clusterrolebindings to the cluster-admin role. Check if they are used and if they need this role or if they could use a role with fewer privileges. Where possible, first bind users to a lower privileged role and then remove the clusterrolebinding to the cluster-admin role : kubectl delete clusterrolebinding [name] scored: false - id: 5.1.2 text: "Minimize access to secrets (Manual)" type: "manual" remediation: | Where possible, remove get, list and watch access to Secret objects in the cluster. scored: false - id: 5.1.3 text: "Minimize wildcard use in Roles and ClusterRoles (Manual)" type: "manual" remediation: | Where possible replace any use of wildcards in clusterroles and roles with specific objects or actions. scored: false - id: 5.1.4 text: "Minimize access to create pods (Manual)" type: "manual" remediation: | Where possible, remove create access to pod objects in the cluster. scored: false - id: 5.1.5 text: "Ensure that default service accounts are not actively used. (Manual)" type: "manual" remediation: | Create explicit service accounts wherever a Kubernetes workload requires specific access to the Kubernetes API server. Modify the configuration of each default service account to include this value automountServiceAccountToken: false scored: false - id: 5.1.6 text: "Ensure that Service Account Tokens are only mounted where necessary (Manual)" type: "manual" remediation: | Modify the definition of pods and service accounts which do not need to mount service account tokens to disable it. scored: false - id: 5.1.7 text: "Avoid use of system:masters group (Manual)" type: "manual" remediation: | Remove the system:masters group from all users in the cluster. scored: false - id: 5.1.8 text: "Limit use of the Bind, Impersonate and Escalate permissions in the Kubernetes cluster (Manual)" type: "manual" remediation: | Where possible, remove the impersonate, bind and escalate rights from subjects. scored: false - id: 5.2 text: "Pod Security Standards" checks: - id: 5.2.1 text: "Ensure that the cluster has at least one active policy control mechanism in place (Manual)" type: "manual" remediation: | Ensure that either Pod Security Admission or an external policy control system is in place for every namespace which contains user workloads. scored: false - id: 5.2.2 text: "Minimize the admission of privileged containers (Automated)" type: "manual" remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of privileged containers. scored: false - id: 5.2.3 text: "Minimize the admission of containers wishing to share the host process ID namespace (Automated)" type: "manual" remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of `hostPID` containers. scored: false - id: 5.2.4 text: "Minimize the admission of containers wishing to share the host IPC namespace (Automated)" type: "manual" remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of `hostIPC` containers. scored: false - id: 5.2.5 text: "Minimize the admission of containers wishing to share the host network namespace (Automated)" type: "manual" remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of `hostNetwork` containers. scored: false - id: 5.2.6 text: "Minimize the admission of containers with allowPrivilegeEscalation (Automated)" type: "manual" remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of containers with `.spec.allowPrivilegeEscalation` set to `true`. scored: false - id: 5.2.7 text: "Minimize the admission of root containers (Automated)" type: "manual" remediation: | Create a policy for each namespace in the cluster, ensuring that either `MustRunAsNonRoot` or `MustRunAs` with the range of UIDs not including 0, is set. scored: false - id: 5.2.8 text: "Minimize the admission of containers with the NET_RAW capability (Automated)" type: "manual" remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of containers with the `NET_RAW` capability. scored: false - id: 5.2.9 text: "Minimize the admission of containers with added capabilities (Automated)" type: "manual" remediation: | Ensure that `allowedCapabilities` is not present in policies for the cluster unless it is set to an empty array. scored: false - id: 5.2.10 text: "Minimize the admission of containers with capabilities assigned (Manual)" type: "manual" remediation: | Review the use of capabilites in applications running on your cluster. Where a namespace contains applications which do not require any Linux capabities to operate consider adding a PSP which forbids the admission of containers which do not drop all capabilities. scored: false - id: 5.2.11 text: "Minimize the admission of Windows HostProcess containers (Manual)" type: "manual" remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of containers that have `.securityContext.windowsOptions.hostProcess` set to `true`. scored: false - id: 5.2.12 text: "Minimize the admission of HostPath volumes (Manual)" type: "manual" remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of containers with `hostPath` volumes. scored: false - id: 5.2.13 text: "Minimize the admission of containers which use HostPorts (Manual)" type: "manual" remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of containers which use `hostPort` sections. scored: false - id: 5.3 text: "Network Policies and CNI" checks: - id: 5.3.1 text: "Ensure that the CNI in use supports NetworkPolicies (Manual)" type: "manual" remediation: | If the CNI plugin in use does not support network policies, consideration should be given to making use of a different plugin, or finding an alternate mechanism for restricting traffic in the Kubernetes cluster. scored: false - id: 5.3.2 text: "Ensure that all Namespaces have NetworkPolicies defined (Manual)" type: "manual" remediation: | Follow the documentation and create NetworkPolicy objects as you need them. scored: false - id: 5.4 text: "Secrets Management" checks: - id: 5.4.1 text: "Prefer using Secrets as files over Secrets as environment variables (Manual)" type: "manual" remediation: | If possible, rewrite application code to read Secrets from mounted secret files, rather than from environment variables. scored: false - id: 5.4.2 text: "Consider external secret storage (Manual)" type: "manual" remediation: | Refer to the Secrets management options offered by your cloud provider or a third-party secrets management solution. scored: false - id: 5.5 text: "Extensible Admission Control" checks: - id: 5.5.1 text: "Configure Image Provenance using ImagePolicyWebhook admission controller (Manual)" type: "manual" remediation: | Follow the Kubernetes documentation and setup image provenance. scored: false - id: 5.7 text: "General Policies" checks: - id: 5.7.1 text: "Create administrative boundaries between resources using namespaces (Manual)" type: "manual" remediation: | Follow the documentation and create namespaces for objects in your deployment as you need them. scored: false - id: 5.7.2 text: "Ensure that the seccomp profile is set to docker/default in your Pod definitions (Manual)" type: "manual" remediation: | Use `securityContext` to enable the docker/default seccomp profile in your pod definitions. An example is as below: securityContext: seccompProfile: type: RuntimeDefault scored: false - id: 5.7.3 text: "Apply SecurityContext to your Pods and Containers (Manual)" type: "manual" remediation: | Follow the Kubernetes documentation and apply SecurityContexts to your Pods. For a suggested list of SecurityContexts, you may refer to the CIS Security Benchmark for Docker Containers. scored: false - id: 5.7.4 text: "The default namespace should not be used (Manual)" type: "manual" remediation: | Ensure that namespaces are created to allow for appropriate segregation of Kubernetes resources and that all new resources are created in a specific namespace. scored: false ================================================ FILE: cfg/k3s-cis-1.24/config.yaml ================================================ --- ## Version-specific settings that override the values in cfg/config.yaml master: components: - apiserver - scheduler - controllermanager - etcd - policies apiserver: bins: - containerd scheduler: bins: - containerd kubeconfig: - /var/lib/rancher/k3s/server/cred/scheduler.kubeconfig controllermanager: bins: - containerd kubeconfig: - /var/lib/rancher/k3s/server/cred/controller.kubeconfig etcd: bins: - containerd etcd: components: - etcd etcd: confs: /var/lib/rancher/k3s/server/db/etcd/config node: components: - kubelet - proxy kubelet: bins: - containerd defaultkubeconfig: /var/lib/rancher/k3s/agent/kubelet.kubeconfig defaultcafile: /var/lib/rancher/k3s/agent/client-ca.crt proxy: bins: - containerd defaultkubeconfig: /var/lib/rancher/k3s/agent/kubeproxy.kubeconfig policies: components: - policies ================================================ FILE: cfg/k3s-cis-1.24/controlplane.yaml ================================================ --- controls: version: "k3s-cis-1.24" id: 3 text: "Control Plane Configuration" type: "controlplane" groups: - id: 3.1 text: "Authentication and Authorization" checks: - id: 3.1.1 text: "Client certificate authentication should not be used for users (Manual)" type: "manual" remediation: | Alternative mechanisms provided by Kubernetes such as the use of OIDC should be implemented in place of client certificates. scored: false - id: 3.2 text: "Logging" checks: - id: 3.2.1 text: "Ensure that a minimal audit policy is created (Automated)" audit: "journalctl -m -u k3s | grep 'Running kube-apiserver' | tail -n1 | grep 'audit-policy-file'" tests: test_items: - flag: "--audit-policy-file" set: true remediation: | Create an audit policy file for your cluster. scored: false - id: 3.2.2 text: "Ensure that the audit policy covers key security concerns (Manual)" type: "manual" remediation: | Review the audit policy provided for the cluster and ensure that it covers at least the following areas, - Access to Secrets managed by the cluster. Care should be taken to only log Metadata for requests to Secrets, ConfigMaps, and TokenReviews, in order to avoid risk of logging sensitive data. - Modification of Pod and Deployment objects. - Use of `pods/exec`, `pods/portforward`, `pods/proxy` and `services/proxy`. For most requests, minimally logging at the Metadata level is recommended (the most basic level of logging). scored: false ================================================ FILE: cfg/k3s-cis-1.24/etcd.yaml ================================================ --- controls: version: "k3s-cis-1.24" id: 2 text: "Etcd Node Configuration" type: "etcd" groups: - id: 2 text: "Etcd Node Configuration" checks: - id: 2.1 text: "Ensure that the --cert-file and --key-file arguments are set as appropriate (Automated)" audit_config: "cat $etcdconf" tests: bin_op: and test_items: - path: "{.client-transport-security.cert-file}" compare: op: eq value: "/var/lib/rancher/k3s/server/tls/etcd/server-client.crt" - path: "{.client-transport-security.key-file}" compare: op: eq value: "/var/lib/rancher/k3s/server/tls/etcd/server-client.key" remediation: | If running on with sqlite or a external DB, etcd checks are Not Applicable. When running with embedded-etcd, K3s generates cert and key files for etcd. These are located in /var/lib/rancher/k3s/server/tls/etcd/. If this check fails, ensure that the configuration file $etcdconf has not been modified to use custom cert and key files. scored: false - id: 2.2 text: "Ensure that the --client-cert-auth argument is set to true (Automated)" audit_config: "cat $etcdconf" tests: test_items: - path: "{.client-transport-security.client-cert-auth}" compare: op: eq value: true remediation: | If running on with sqlite or a external DB, etcd checks are Not Applicable. When running with embedded-etcd, K3s sets the --client-cert-auth parameter to true. If this check fails, ensure that the configuration file $etcdconf has not been modified to disable client certificate authentication. scored: false - id: 2.3 text: "Ensure that the --auto-tls argument is not set to true (Automated)" audit_config: "cat $etcdconf" tests: bin_op: or test_items: - path: "{.client-transport-security.auto-tls}" compare: op: eq value: false - path: "{.client-transport-security.auto-tls}" set: false remediation: | If running on with sqlite or a external DB, etcd checks are Not Applicable. When running with embedded-etcd, K3s does not set the --auto-tls parameter. If this check fails, edit the etcd pod specification file $etcdconf on the master node and either remove the --auto-tls parameter or set it to false. client-transport-security: auto-tls: false scored: false - id: 2.4 text: "Ensure that the --peer-cert-file and --peer-key-file arguments are set as appropriate (Automated)" audit_config: "cat $etcdconf" tests: bin_op: and test_items: - path: "{.peer-transport-security.cert-file}" compare: op: eq value: "/var/lib/rancher/k3s/server/tls/etcd/peer-server-client.crt" - path: "{.peer-transport-security.key-file}" compare: op: eq value: "/var/lib/rancher/k3s/server/tls/etcd/peer-server-client.key" remediation: | If running on with sqlite or a external DB, etcd checks are Not Applicable. When running with embedded-etcd, K3s generates peer cert and key files for etcd. These are located in /var/lib/rancher/k3s/server/tls/etcd/. If this check fails, ensure that the configuration file $etcdconf has not been modified to use custom peer cert and key files. scored: false - id: 2.5 text: "Ensure that the --peer-client-cert-auth argument is set to true (Automated)" audit_config: "cat $etcdconf" tests: test_items: - path: "{.peer-transport-security.client-cert-auth}" compare: op: eq value: true remediation: | If running on with sqlite or a external DB, etcd checks are Not Applicable. When running with embedded-etcd, K3s sets the --peer-cert-auth parameter to true. If this check fails, ensure that the configuration file $etcdconf has not been modified to disable peer client certificate authentication. scored: false - id: 2.6 text: "Ensure that the --peer-auto-tls argument is not set to true (Automated)" audit_config: "cat $etcdconf" tests: bin_op: or test_items: - path: "{.peer-transport-security.auto-tls}" compare: op: eq value: false - path: "{.peer-transport-security.auto-tls}" set: false remediation: | If running on with sqlite or a external DB, etcd checks are Not Applicable. When running with embedded-etcd, K3s does not set the --peer-auto-tls parameter. If this check fails, edit the etcd pod specification file $etcdconf on the master node and either remove the --peer-auto-tls parameter or set it to false. peer-transport-security: auto-tls: false scored: false - id: 2.7 text: "Ensure that a unique Certificate Authority is used for etcd (Automated)" audit_config: "cat $etcdconf" tests: test_items: - path: "{.peer-transport-security.trusted-ca-file}" compare: op: eq value: "/var/lib/rancher/k3s/server/tls/etcd/peer-ca.crt" remediation: | If running on with sqlite or a external DB, etcd checks are Not Applicable. When running with embedded-etcd, K3s generates a unique certificate authority for etcd. This is located at /var/lib/rancher/k3s/server/tls/etcd/peer-ca.crt. If this check fails, ensure that the configuration file $etcdconf has not been modified to use a shared certificate authority. scored: false ================================================ FILE: cfg/k3s-cis-1.24/master.yaml ================================================ --- controls: version: "k3s-cis-1.24" id: 1 text: "Control Plane Security Configuration" type: "master" groups: - id: 1.1 text: "Control Plane Node Configuration Files" checks: - id: 1.1.1 text: "Ensure that the API server pod specification file permissions are set to 644 or more restrictive (Automated)" audit: "/bin/sh -c 'if test -e $apiserverconf; then stat -c permissions=%a $apiserverconf; fi'" type: "skip" tests: test_items: - flag: "permissions" compare: op: bitmask value: "644" remediation: | Not Applicable. By default, K3s embeds the api server within the k3s process. There is no API server pod specification file. scored: true - id: 1.1.2 text: "Ensure that the API server pod specification file ownership is set to root:root (Automated)" audit: "/bin/sh -c 'if test -e $apiserverconf; then stat -c %U:%G $apiserverconf; fi'" type: "skip" tests: test_items: - flag: "root:root" remediation: | Not Applicable. By default, K3s embeds the api server within the k3s process. There is no API server pod specification file. scored: true - id: 1.1.3 text: "Ensure that the controller manager pod specification file permissions are set to 600 or more restrictive (Automated)" audit: "/bin/sh -c 'if test -e $controllermanagerconf; then stat -c permissions=%a $controllermanagerconf; fi'" type: "skip" tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Not Applicable. By default, K3s embeds the controller manager within the k3s process. There is no controller manager pod specification file. scored: true - id: 1.1.4 text: "Ensure that the controller manager pod specification file ownership is set to root:root (Automated)" audit: "/bin/sh -c 'if test -e $controllermanagerconf; then stat -c %U:%G $controllermanagerconf; fi'" type: "skip" tests: test_items: - flag: "root:root" remediation: | Not Applicable. By default, K3s embeds the controller manager within the k3s process. There is no controller manager pod specification file. scored: true - id: 1.1.5 text: "Ensure that the scheduler pod specification file permissions are set to 600 or more restrictive (Automated)" audit: "/bin/sh -c 'if test -e $schedulerconf; then stat -c permissions=%a $schedulerconf; fi'" type: "skip" tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Not Applicable. By default, K3s embeds the scheduler within the k3s process. There is no scheduler pod specification file. scored: true - id: 1.1.6 text: "Ensure that the scheduler pod specification file ownership is set to root:root (Automated)" audit: "/bin/sh -c 'if test -e $schedulerconf; then stat -c %U:%G $schedulerconf; fi'" type: "skip" tests: test_items: - flag: "root:root" remediation: | Not Applicable. By default, K3s embeds the scheduler within the k3s process. There is no scheduler pod specification file. scored: true - id: 1.1.7 text: "Ensure that the etcd pod specification file permissions are set to 600 or more restrictive (Automated)" audit: "/bin/sh -c 'if test -e $etcdconf; then stat -c permissions=%a $etcdconf; fi'" type: "skip" tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Not Applicable. By default, K3s embeds etcd within the k3s process. There is no etcd pod specification file. scored: true - id: 1.1.8 text: "Ensure that the etcd pod specification file ownership is set to root:root (Automated)" audit: "/bin/sh -c 'if test -e $etcdconf; then stat -c %U:%G $etcdconf; fi'" type: "skip" tests: test_items: - flag: "root:root" remediation: | Not Applicable. By default, K3s embeds etcd within the k3s process. There is no etcd pod specification file. scored: true - id: 1.1.9 text: "Ensure that the Container Network Interface file permissions are set to 600 or more restrictive (Automated)" audit: find /var/lib/cni/networks -type f ! -name lock 2> /dev/null | xargs --no-run-if-empty stat -c permissions=%a type: "skip" use_multiple_values: true tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Not Applicable. The default K3s CNI, flannel, does not create any files in /var/lib/cni/networks. scored: false - id: 1.1.10 text: "Ensure that the Container Network Interface file ownership is set to root:root (Automated)" audit: find /var/lib/cni/networks -type f 2> /dev/null | xargs --no-run-if-empty stat -c %U:%G\ type: "skip" use_multiple_values: true tests: test_items: - flag: "root:root" remediation: | Not Applicable. The default K3s CNI, flannel, does not create any files in /var/lib/cni/networks. scored: true - id: 1.1.11 text: "Ensure that the etcd data directory permissions are set to 700 or more restrictive (Automated)" audit: | if [ "$(journalctl -m -u k3s | grep -m1 'Managed etcd cluster' | wc -l)" -gt 0 ]; then stat -c permissions=%a /var/lib/rancher/k3s/server/db/etcd else echo "permissions=700" fi tests: test_items: - flag: "permissions" compare: op: bitmask value: "700" remediation: | On the etcd server node, get the etcd data directory, passed as an argument --data-dir, from the command 'ps -ef | grep etcd'. Run the below command (based on the etcd data directory found above). For example, chmod 700 /var/lib/etcd scored: true - id: 1.1.12 text: "Ensure that the etcd data directory ownership is set to etcd:etcd (Automated)" audit: ps -ef | grep $etcdbin | grep -- --data-dir | sed 's%.*data-dir[= ]\([^ ]*\).*%\1%' | xargs stat -c %U:%G type: "skip" tests: test_items: - flag: "etcd:etcd" remediation: | Not Applicable. For K3s, etcd is embedded within the k3s process. There is no separate etcd process. Therefore the etcd data directory ownership is managed by the k3s process and should be root:root. scored: true - id: 1.1.13 text: "Ensure that the admin.conf file permissions are set to 600 or more restrictive (Automated)" audit: "/bin/sh -c 'if test -e /var/lib/rancher/k3s/server/cred/admin.kubeconfig; then stat -c permissions=%a /var/lib/rancher/k3s/server/cred/admin.kubeconfig; fi'" type: "skip" tests: test_items: - flag: "600" compare: op: eq value: "600" set: true remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chmod 600 /var/lib/rancher/k3s/server/cred/admin.kubeconfig scored: true - id: 1.1.14 text: "Ensure that the admin.conf file ownership is set to root:root (Automated)" audit: "/bin/sh -c 'if test -e /var/lib/rancher/k3s/server/cred/admin.kubeconfig; then stat -c %U:%G /var/lib/rancher/k3s/server/cred/admin.kubeconfig; fi'" tests: test_items: - flag: "root:root" compare: op: eq value: "root:root" set: true remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chown root:root /var/lib/rancher/k3s/server/cred/admin.kubeconfig scored: true - id: 1.1.15 text: "Ensure that the scheduler.conf file permissions are set to 600 or more restrictive (Automated)" audit: "/bin/sh -c 'if test -e $schedulerkubeconfig; then stat -c permissions=%a $schedulerkubeconfig; fi'" tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chmod 600 $schedulerkubeconfig scored: true - id: 1.1.16 text: "Ensure that the scheduler.conf file ownership is set to root:root (Automated)" audit: "/bin/sh -c 'if test -e $schedulerkubeconfig; then stat -c %U:%G $schedulerkubeconfig; fi'" tests: test_items: - flag: "root:root" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chown root:root $schedulerkubeconfig scored: true - id: 1.1.17 text: "Ensure that the controller-manager.conf file permissions are set to 600 or more restrictive (Automated)" audit: "/bin/sh -c 'if test -e $controllermanagerkubeconfig; then stat -c permissions=%a $controllermanagerkubeconfig; fi'" tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chmod 600 $controllermanagerkubeconfig scored: true - id: 1.1.18 text: "Ensure that the controller-manager.conf file ownership is set to root:root (Automated)" audit: "stat -c %U:%G /var/lib/rancher/k3s/server/tls" tests: test_items: - flag: "root:root" compare: op: eq value: "root:root" set: true remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chown root:root $controllermanagerkubeconfig scored: true - id: 1.1.19 text: "Ensure that the Kubernetes PKI directory and file ownership is set to root:root (Automated)" audit: "find /var/lib/rancher/k3s/server/tls | xargs stat -c %U:%G" use_multiple_values: true tests: test_items: - flag: "root:root" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chown -R root:root /etc/kubernetes/pki/ scored: true - id: 1.1.20 text: "Ensure that the Kubernetes PKI certificate file permissions are set to 600 or more restrictive (Manual)" audit: "/bin/sh -c 'stat -c permissions=%a /var/lib/rancher/k3s/server/tls/*.crt'" use_multiple_values: true tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Run the below command (based on the file location on your system) on the master node. For example, chmod -R 600 /var/lib/rancher/k3s/server/tls/*.crt scored: false - id: 1.1.21 text: "Ensure that the Kubernetes PKI key file permissions are set to 600 (Automated)" audit: "/bin/sh -c 'stat -c permissions=%a /var/lib/rancher/k3s/server/tls/*.key'" use_multiple_values: true tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Run the below command (based on the file location on your system) on the master node. For example, chmod -R 600 /var/lib/rancher/k3s/server/tls/*.key scored: true - id: 1.2 text: "API Server" checks: - id: 1.2.1 text: "Ensure that the --anonymous-auth argument is set to false (Automated)" audit: "journalctl -m -u k3s | grep 'Running kube-apiserver' | tail -n1 | grep 'anonymous-auth'" tests: test_items: - flag: "--anonymous-auth" compare: op: eq value: false remediation: | By default, K3s sets the --anonymous-auth argument to false. If it is set to true, edit the K3s config file /etc/rancher/k3s/config.yaml and remove anything similar to below. kube-apiserver-arg: - "anonymous-auth=true" scored: true - id: 1.2.2 text: "Ensure that the --token-auth-file parameter is not set (Automated)" audit: "journalctl -m -u k3s | grep 'Running kube-apiserver' | tail -n1" tests: test_items: - flag: "--token-auth-file" set: false remediation: | Follow the documentation and configure alternate mechanisms for authentication. If this check fails, edit the K3s config file /etc/rancher/k3s/config.yaml and remove anything similar to below. kube-apiserver-arg: - "token-auth-file=" scored: true - id: 1.2.3 text: "Ensure that the --DenyServiceExternalIPs is not set (Automated)" audit: "journalctl -m -u k3s | grep 'Running kube-apiserver' | tail -n1" tests: bin_op: or test_items: - flag: "--enable-admission-plugins" compare: op: nothave value: "DenyServiceExternalIPs" - flag: "--enable-admission-plugins" set: false remediation: | By default, K3s does not set DenyServiceExternalIPs. If this check fails, edit the K3s config file /etc/rancher/k3s/config.yaml, remove any lines like below. kube-apiserver-arg: - "enable-admission-plugins=DenyServiceExternalIPs" scored: true - id: 1.2.4 text: "Ensure that the --kubelet-https argument is set to true (Automated)" audit: "journalctl -m -u k3s | grep 'Running kube-apiserver' | tail -n1" type: "skip" tests: bin_op: or test_items: - flag: "--kubelet-https" compare: op: eq value: true - flag: "--kubelet-https" set: false remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and remove the --kubelet-https parameter. scored: true - id: 1.2.5 text: "Ensure that the --kubelet-client-certificate and --kubelet-client-key arguments are set as appropriate (Automated)" audit: "journalctl -m -u k3s | grep 'Running kube-apiserver' | tail -n1 | grep 'kubelet-certificate-authority'" tests: bin_op: and test_items: - flag: "--kubelet-client-certificate" - flag: "--kubelet-client-key" remediation: | By default, K3s automatically provides the kubelet client certificate and key. They are generated and located at /var/lib/rancher/k3s/server/tls/client-kube-apiserver.crt and /var/lib/rancher/k3s/server/tls/client-kube-apiserver.key If for some reason you need to provide your own certificate and key, you can set the below parameters in the K3s config file /etc/rancher/k3s/config.yaml. kube-apiserver-arg: - "kubelet-client-certificate=" - "kubelet-client-key=" scored: true - id: 1.2.6 text: "Ensure that the --kubelet-certificate-authority argument is set as appropriate (Automated)" audit: "journalctl -m -u k3s | grep 'Running kube-apiserver' | tail -n1 | grep 'kubelet-certificate-authority'" tests: test_items: - flag: "--kubelet-certificate-authority" remediation: | Follow the Kubernetes documentation and setup the TLS connection between the apiserver and kubelets. Then, edit the API server pod specification file $apiserverconf on the control plane node and set the --kubelet-certificate-authority parameter to the path to the cert file for the certificate authority. --kubelet-certificate-authority= scored: true - id: 1.2.7 text: "Ensure that the --authorization-mode argument is not set to AlwaysAllow (Automated)" audit: "journalctl -m -u k3s | grep 'Running kube-apiserver' | tail -n1 | grep 'authorization-mode'" tests: test_items: - flag: "--authorization-mode" compare: op: nothave value: "AlwaysAllow" remediation: | By default, K3s does not set the --authorization-mode to AlwaysAllow. If this check fails, edit K3s config file /etc/rancher/k3s/config.yaml, remove any lines like below. kube-apiserver-arg: - "authorization-mode=AlwaysAllow" scored: true - id: 1.2.8 text: "Ensure that the --authorization-mode argument includes Node (Automated)" audit: "journalctl -m -u k3s | grep 'Running kube-apiserver' | tail -n1 | grep 'authorization-mode'" tests: test_items: - flag: "--authorization-mode" compare: op: has value: "Node" remediation: | By default, K3s sets the --authorization-mode to Node and RBAC. If this check fails, edit the K3s config file /etc/rancher/k3s/config.yaml, ensure that you are not overriding authorization-mode. scored: true - id: 1.2.9 text: "Ensure that the --authorization-mode argument includes RBAC (Automated)" audit: "journalctl -m -u k3s | grep 'Running kube-apiserver' | tail -n1 | grep 'authorization-mode'" tests: test_items: - flag: "--authorization-mode" compare: op: has value: "RBAC" remediation: | By default, K3s sets the --authorization-mode to Node and RBAC. If this check fails, edit the K3s config file /etc/rancher/k3s/config.yaml, ensure that you are not overriding authorization-mode. scored: true - id: 1.2.10 text: "Ensure that the admission control plugin EventRateLimit is set (Manual)" audit: "journalctl -m -u k3s | grep 'Running kube-apiserver' | tail -n1 | grep 'enable-admission-plugins'" tests: test_items: - flag: "--enable-admission-plugins" compare: op: has value: "EventRateLimit" remediation: | Follow the Kubernetes documentation and set the desired limits in a configuration file. Then, edit the K3s config file /etc/rancher/k3s/config.yaml and set the below parameters. kube-apiserver-arg: - "enable-admission-plugins=...,EventRateLimit,..." - "admission-control-config-file=" scored: false - id: 1.2.11 text: "Ensure that the admission control plugin AlwaysAdmit is not set (Automated)" audit: "journalctl -m -u k3s | grep 'Running kube-apiserver' | tail -n1 | grep 'enable-admission-plugins'" tests: bin_op: or test_items: - flag: "--enable-admission-plugins" compare: op: nothave value: AlwaysAdmit - flag: "--enable-admission-plugins" set: false remediation: | By default, K3s does not set the --enable-admission-plugins to AlwaysAdmit. If this check fails, edit K3s config file /etc/rancher/k3s/config.yaml, remove any lines like below. kube-apiserver-arg: - "enable-admission-plugins=AlwaysAdmit" scored: true - id: 1.2.12 text: "Ensure that the admission control plugin AlwaysPullImages is set (Manual)" audit: "journalctl -m -u k3s | grep 'Running kube-apiserver' | tail -n1 | grep 'enable-admission-plugins'" tests: test_items: - flag: "--enable-admission-plugins" compare: op: has value: "AlwaysPullImages" remediation: | Permissive, per CIS guidelines, "This setting could impact offline or isolated clusters, which have images pre-loaded and do not have access to a registry to pull in-use images. This setting is not appropriate for clusters which use this configuration." Edit the K3s config file /etc/rancher/k3s/config.yaml and set the below parameter. kube-apiserver-arg: - "enable-admission-plugins=...,AlwaysPullImages,..." scored: false - id: 1.2.13 text: "Ensure that the admission control plugin SecurityContextDeny is set if PodSecurityPolicy is not used (Manual)" audit: "journalctl -m -u k3s | grep 'Running kube-apiserver' | tail -n1 | grep 'enable-admission-plugins'" tests: bin_op: or test_items: - flag: "--enable-admission-plugins" compare: op: has value: "SecurityContextDeny" - flag: "--enable-admission-plugins" compare: op: has value: "PodSecurityPolicy" remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the --enable-admission-plugins parameter to include SecurityContextDeny, unless PodSecurityPolicy is already in place. --enable-admission-plugins=...,SecurityContextDeny,... scored: false - id: 1.2.14 text: "Ensure that the admission control plugin ServiceAccount is set (Automated)" audit: "journalctl -m -u k3s | grep 'Running kube-apiserver' | tail -n1 | grep 'ServiceAccount'" tests: bin_op: or test_items: - flag: "--disable-admission-plugins" compare: op: nothave value: "ServiceAccount" - flag: "--disable-admission-plugins" set: false remediation: | By default, K3s does not set the --disable-admission-plugins to anything. Follow the documentation and create ServiceAccount objects as per your environment. If this check fails, edit the K3s config file /etc/rancher/k3s/config.yaml and remove any lines like below. kube-apiserver-arg: - "disable-admission-plugins=ServiceAccount" scored: true - id: 1.2.15 text: "Ensure that the admission control plugin NamespaceLifecycle is set (Automated)" audit: "journalctl -m -u k3s | grep 'Running kube-apiserver' | tail -n1" tests: bin_op: or test_items: - flag: "--disable-admission-plugins" compare: op: nothave value: "NamespaceLifecycle" - flag: "--disable-admission-plugins" set: false remediation: | By default, K3s does not set the --disable-admission-plugins to anything. If this check fails, edit the K3s config file /etc/rancher/k3s/config.yaml and remove any lines like below. kube-apiserver-arg: - "disable-admission-plugins=...,NamespaceLifecycle,..." scored: true - id: 1.2.16 text: "Ensure that the admission control plugin NodeRestriction is set (Automated)" audit: "journalctl -m -u k3s | grep 'Running kube-apiserver' | tail -n1 | grep 'enable-admission-plugins'" tests: test_items: - flag: "--enable-admission-plugins" compare: op: has value: "NodeRestriction" remediation: | By default, K3s sets the --enable-admission-plugins to NodeRestriction. If using the K3s config file /etc/rancher/k3s/config.yaml, check that you are not overriding the admission plugins. If you are, include NodeRestriction in the list. kube-apiserver-arg: - "enable-admission-plugins=...,NodeRestriction,..." scored: true - id: 1.2.17 text: "Ensure that the --secure-port argument is not set to 0 (Automated)" audit: "journalctl -m -u k3s | grep 'Running kube-apiserver' | tail -n1 | grep 'secure-port'" tests: bin_op: or test_items: - flag: "--secure-port" compare: op: gt value: 0 - flag: "--secure-port" set: false remediation: | By default, K3s sets the secure port to 6444. If this check fails, edit the K3s config file /etc/rancher/k3s/config.yaml and remove any lines like below. kube-apiserver-arg: - "secure-port=" scored: true - id: 1.2.18 text: "Ensure that the --profiling argument is set to false (Automated)" audit: "journalctl -m -u k3s | grep 'Running kube-apiserver' | tail -n1 | grep 'profiling'" tests: test_items: - flag: "--profiling" compare: op: eq value: false remediation: | By default, K3s sets the --profiling argument to false. If this check fails, edit the K3s config file /etc/rancher/k3s/config.yaml and remove any lines like below. kube-apiserver-arg: - "profiling=true" scored: true - id: 1.2.19 text: "Ensure that the --audit-log-path argument is set (Manual)" audit: "journalctl -m -u k3s | grep 'Running kube-apiserver' | tail -n1 | grep 'audit-log-path'" tests: test_items: - flag: "--audit-log-path" remediation: | Edit the K3s config file /etc/rancher/k3s/config.yaml and set the audit-log-path parameter to a suitable path and file where you would like audit logs to be written, for example, kube-apiserver-arg: - "audit-log-path=/var/lib/rancher/k3s/server/logs/audit.log" scored: false - id: 1.2.20 text: "Ensure that the --audit-log-maxage argument is set to 30 or as appropriate (Manual)" audit: "journalctl -m -u k3s | grep 'Running kube-apiserver' | tail -n1 | grep 'audit-log-maxage'" tests: test_items: - flag: "--audit-log-maxage" compare: op: gte value: 30 remediation: | Edit the K3s config file /etc/rancher/k3s/config.yaml on the control plane node and set the audit-log-maxage parameter to 30 or as an appropriate number of days, for example, kube-apiserver-arg: - "audit-log-maxage=30" scored: false - id: 1.2.21 text: "Ensure that the --audit-log-maxbackup argument is set to 10 or as appropriate (Manual)" audit: "journalctl -m -u k3s | grep 'Running kube-apiserver' | tail -n1 | grep 'audit-log-maxbackup'" tests: test_items: - flag: "--audit-log-maxbackup" compare: op: gte value: 10 remediation: | Edit the K3s config file /etc/rancher/k3s/config.yaml on the control plane node and set the audit-log-maxbackup parameter to 10 or to an appropriate value. For example, kube-apiserver-arg: - "audit-log-maxbackup=10" scored: false - id: 1.2.22 text: "Ensure that the --audit-log-maxsize argument is set to 100 or as appropriate (Manual)" audit: "journalctl -m -u k3s | grep 'Running kube-apiserver' | tail -n1 | grep 'audit-log-maxsize'" tests: test_items: - flag: "--audit-log-maxsize" compare: op: gte value: 100 remediation: | Edit the K3s config file /etc/rancher/k3s/config.yaml on the control plane node and set the audit-log-maxsize parameter to an appropriate size in MB. For example, kube-apiserver-arg: - "audit-log-maxsize=100" scored: false - id: 1.2.23 text: "Ensure that the --request-timeout argument is set as appropriate (Manual)" audit: "journalctl -m -u k3s | grep 'Running kube-apiserver' | tail -n1 | grep 'request-timeout'" tests: bin_op: or test_items: - flag: "--request-timeout" set: false - flag: "--request-timeout" remediation: | Permissive, per CIS guidelines, "it is recommended to set this limit as appropriate and change the default limit of 60 seconds only if needed". Edit the K3s config file /etc/rancher/k3s/config.yaml and set the below parameter if needed. For example, kube-apiserver-arg: - "request-timeout=300s" scored: false - id: 1.2.24 text: "Ensure that the --service-account-lookup argument is set to true (Automated)" audit: "journalctl -m -u k3s | grep 'Running kube-apiserver' | tail -n1" tests: bin_op: or test_items: - flag: "--service-account-lookup" set: false - flag: "--service-account-lookup" compare: op: eq value: true remediation: | By default, K3s does not set the --service-account-lookup argument. Edit the K3s config file /etc/rancher/k3s/config.yaml and set the service-account-lookup. For example, kube-apiserver-arg: - "service-account-lookup=true" Alternatively, you can delete the service-account-lookup parameter from this file so that the default takes effect. scored: true - id: 1.2.25 text: "Ensure that the --service-account-key-file argument is set as appropriate (Automated)" audit: "journalctl -m -u k3s | grep 'Running kube-apiserver' | tail -n1 | grep 'service-account-key-file'" tests: test_items: - flag: "--service-account-key-file" remediation: | K3s automatically generates and sets the service account key file. It is located at /var/lib/rancher/k3s/server/tls/service.key. If this check fails, edit K3s config file /etc/rancher/k3s/config.yaml and remove any lines like below. kube-apiserver-arg: - "service-account-key-file=" scored: true - id: 1.2.26 text: "Ensure that the --etcd-certfile and --etcd-keyfile arguments are set as appropriate (Automated)" audit: | if [ "$(journalctl -m -u k3s | grep -m1 'Managed etcd cluster' | wc -l)" -gt 0 ]; then journalctl -m -u k3s | grep -m1 'Running kube-apiserver' | tail -n1 else echo "--etcd-certfile AND --etcd-keyfile" fi tests: bin_op: and test_items: - flag: "--etcd-certfile" set: true - flag: "--etcd-keyfile" set: true remediation: | K3s automatically generates and sets the etcd certificate and key files. They are located at /var/lib/rancher/k3s/server/tls/etcd/client.crt and /var/lib/rancher/k3s/server/tls/etcd/client.key. If this check fails, edit the K3s config file /etc/rancher/k3s/config.yaml and remove any lines like below. kube-apiserver-arg: - "etcd-certfile=" - "etcd-keyfile=" scored: true - id: 1.2.27 text: "Ensure that the --tls-cert-file and --tls-private-key-file arguments are set as appropriate (Automated)" audit: "journalctl -m -u k3s | grep -A1 'Running kube-apiserver' | tail -n2" tests: bin_op: and test_items: - flag: "--tls-cert-file" set: true - flag: "--tls-private-key-file" set: true remediation: | By default, K3s automatically generates and provides the TLS certificate and private key for the apiserver. They are generated and located at /var/lib/rancher/k3s/server/tls/serving-kube-apiserver.crt and /var/lib/rancher/k3s/server/tls/serving-kube-apiserver.key If this check fails, edit the K3s config file /etc/rancher/k3s/config.yaml and remove any lines like below. kube-apiserver-arg: - "tls-cert-file=" - "tls-private-key-file=" scored: true - id: 1.2.28 text: "Ensure that the --client-ca-file argument is set as appropriate (Automated)" audit: "journalctl -m -u k3s | grep 'Running kube-apiserver' | tail -n1 | grep 'client-ca-file'" tests: test_items: - flag: "--client-ca-file" remediation: | By default, K3s automatically provides the client certificate authority file. It is generated and located at /var/lib/rancher/k3s/server/tls/client-ca.crt. If this check fails, edit the K3s config file /etc/rancher/k3s/config.yaml and remove any lines like below. kube-apiserver-arg: - "client-ca-file=" scored: true - id: 1.2.29 text: "Ensure that the --etcd-cafile argument is set as appropriate (Automated)" audit: "journalctl -m -u k3s | grep 'Running kube-apiserver' | tail -n1 | grep 'etcd-cafile'" tests: test_items: - flag: "--etcd-cafile" remediation: | By default, K3s automatically provides the etcd certificate authority file. It is generated and located at /var/lib/rancher/k3s/server/tls/client-ca.crt. If this check fails, edit the K3s config file /etc/rancher/k3s/config.yaml and remove any lines like below. kube-apiserver-arg: - "etcd-cafile=" scored: true - id: 1.2.30 text: "Ensure that the --encryption-provider-config argument is set as appropriate (Manual)" audit: "journalctl -m -u k3s | grep 'Running kube-apiserver' | tail -n1 | grep 'encryption-provider-config'" tests: test_items: - flag: "--encryption-provider-config" remediation: | K3s can be configured to use encryption providers to encrypt secrets at rest. Edit the K3s config file /etc/rancher/k3s/config.yaml on the control plane node and set the below parameter. secrets-encryption: true Secrets encryption can then be managed with the k3s secrets-encrypt command line tool. If needed, you can find the generated encryption config at /var/lib/rancher/k3s/server/cred/encryption-config.json. scored: false - id: 1.2.31 text: "Ensure that encryption providers are appropriately configured (Manual)" audit: | ENCRYPTION_PROVIDER_CONFIG=$(journalctl -m -u k3s | grep 'Running kube-apiserver' | tail -n1 | grep -- --encryption-provider-config | sed 's%.*encryption-provider-config[= ]\([^ ]*\).*%\1%') if test -e $ENCRYPTION_PROVIDER_CONFIG; then grep -o 'providers\"\:\[.*\]' $ENCRYPTION_PROVIDER_CONFIG | grep -o "[A-Za-z]*" | head -2 | tail -1 | sed 's/^/provider=/'; fi tests: test_items: - flag: "provider" compare: op: valid_elements value: "aescbc,kms,secretbox" remediation: | K3s can be configured to use encryption providers to encrypt secrets at rest. K3s will utilize the aescbc provider. Edit the K3s config file /etc/rancher/k3s/config.yaml on the control plane node and set the below parameter. secrets-encryption: true Secrets encryption can then be managed with the k3s secrets-encrypt command line tool. If needed, you can find the generated encryption config at /var/lib/rancher/k3s/server/cred/encryption-config.json scored: false - id: 1.2.32 text: "Ensure that the API Server only makes use of Strong Cryptographic Ciphers (Automated)" audit: "journalctl -m -u k3s | grep 'Running kube-apiserver' | tail -n1 | grep 'tls-cipher-suites'" tests: test_items: - flag: "--tls-cipher-suites" compare: op: valid_elements value: "TLS_AES_128_GCM_SHA256,TLS_AES_256_GCM_SHA384,TLS_CHACHA20_POLY1305_SHA256,TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA,TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,TLS_RSA_WITH_3DES_EDE_CBC_SHA,TLS_RSA_WITH_AES_128_CBC_SHA,TLS_RSA_WITH_AES_128_GCM_SHA256,TLS_RSA_WITH_AES_256_CBC_SHA,TLS_RSA_WITH_AES_256_GCM_SHA384" remediation: | By default, the K3s kube-apiserver complies with this test. Changes to these values may cause regression, therefore ensure that all apiserver clients support the new TLS configuration before applying it in production deployments. If a custom TLS configuration is required, consider also creating a custom version of this rule that aligns with your requirements. If this check fails, remove any custom configuration around `tls-cipher-suites` or update the /etc/rancher/k3s/config.yaml file to match the default by adding the following: kube-apiserver-arg: - "tls-cipher-suites=TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,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_CHACHA20_POLY1305" scored: true - id: 1.3 text: "Controller Manager" checks: - id: 1.3.1 text: "Ensure that the --terminated-pod-gc-threshold argument is set as appropriate (Manual)" audit: "journalctl -m -u k3s | grep 'Running kube-controller-manager' | tail -n1 | grep 'terminated-pod-gc-threshold'" tests: test_items: - flag: "--terminated-pod-gc-threshold" remediation: | Edit the K3s config file /etc/rancher/k3s/config.yaml on the control plane node and set the --terminated-pod-gc-threshold to an appropriate threshold, kube-controller-manager-arg: - "terminated-pod-gc-threshold=10" scored: false - id: 1.3.2 text: "Ensure that the --profiling argument is set to false (Automated)" audit: "journalctl -m -u k3s | grep 'Running kube-controller-manager' | tail -n1 | grep 'profiling'" tests: test_items: - flag: "--profiling" compare: op: eq value: false remediation: | By default, K3s sets the --profiling argument to false. If this check fails, edit the K3s config file /etc/rancher/k3s/config.yaml and remove any lines like below. kube-controller-manager-arg: - "profiling=true" scored: true - id: 1.3.3 text: "Ensure that the --use-service-account-credentials argument is set to true (Automated)" audit: "journalctl -m -u k3s | grep 'Running kube-controller-manager' | tail -n1 | grep 'use-service-account-credentials'" tests: test_items: - flag: "--use-service-account-credentials" compare: op: noteq value: false remediation: | By default, K3s sets the --use-service-account-credentials argument to true. If this check fails, edit the K3s config file /etc/rancher/k3s/config.yaml and remove any lines like below. kube-controller-manager-arg: - "use-service-account-credentials=false" scored: true - id: 1.3.4 text: "Ensure that the --service-account-private-key-file argument is set as appropriate (Automated)" audit: "journalctl -m -u k3s | grep 'Running kube-controller-manager' | tail -n1 | grep 'service-account-private-key-file'" tests: test_items: - flag: "--service-account-private-key-file" remediation: | By default, K3s automatically provides the service account private key file. It is generated and located at /var/lib/rancher/k3s/server/tls/service.current.key. If this check fails, edit the K3s config file /etc/rancher/k3s/config.yaml and remove any lines like below. kube-controller-manager-arg: - "service-account-private-key-file=" scored: true - id: 1.3.5 text: "Ensure that the --root-ca-file argument is set as appropriate (Automated)" audit: "journalctl -m -u k3s | grep 'Running kube-controller-manager' | tail -n1 | grep 'root-ca-file'" tests: test_items: - flag: "--root-ca-file" remediation: | By default, K3s automatically provides the root CA file. It is generated and located at /var/lib/rancher/k3s/server/tls/server-ca.crt. If this check fails, edit the K3s config file /etc/rancher/k3s/config.yaml and remove any lines like below. kube-controller-manager-arg: - "root-ca-file=" scored: true - id: 1.3.6 text: "Ensure that the RotateKubeletServerCertificate argument is set to true (Automated)" audit: "journalctl -m -u k3s | grep 'Running kube-controller-manager' | tail -n1" tests: bin_op: or test_items: - flag: "--feature-gates" compare: op: nothave value: "RotateKubeletServerCertificate=false" set: true - flag: "--feature-gates" set: false remediation: | By default, K3s does not set the RotateKubeletServerCertificate feature gate. If you have enabled this feature gate, you should remove it. If this check fails, edit the K3s config file /etc/rancher/k3s/config.yaml, remove any lines like below. kube-controller-manager-arg: - "feature-gate=RotateKubeletServerCertificate" scored: true - id: 1.3.7 text: "Ensure that the --bind-address argument is set to 127.0.0.1 (Automated)" audit: "journalctl -m -u k3s | grep 'Running kube-controller-manager' | tail -n1 | grep 'bind-address'" tests: bin_op: or test_items: - flag: "--bind-address" compare: op: eq value: "127.0.0.1" set: true - flag: "--bind-address" set: false remediation: | By default, K3s sets the --bind-address argument to 127.0.0.1 If this check fails, edit the K3s config file /etc/rancher/k3s/config.yaml and remove any lines like below. kube-controller-manager-arg: - "bind-address=" scored: true - id: 1.4 text: "Scheduler" checks: - id: 1.4.1 text: "Ensure that the --profiling argument is set to false (Automated)" audit: "journalctl -m -u k3s | grep 'Running kube-scheduler' | tail -n1" tests: test_items: - flag: "--profiling" compare: op: eq value: false set: true remediation: | By default, K3s sets the --profiling argument to false. If this check fails, edit the K3s config file /etc/rancher/k3s/config.yaml and remove any lines like below. kube-scheduler-arg: - "profiling=true" scored: true - id: 1.4.2 text: "Ensure that the --bind-address argument is set to 127.0.0.1 (Automated)" audit: "journalctl -m -u k3s | grep 'Running kube-scheduler' | tail -n1 | grep 'bind-address'" tests: bin_op: or test_items: - flag: "--bind-address" compare: op: eq value: "127.0.0.1" set: true - flag: "--bind-address" set: false remediation: | By default, K3s sets the --bind-address argument to 127.0.0.1 If this check fails, edit the K3s config file /etc/rancher/k3s/config.yaml and remove any lines like below. kube-scheduler-arg: - "bind-address=" scored: true ================================================ FILE: cfg/k3s-cis-1.24/node.yaml ================================================ --- controls: version: "k3s-cis-1.24" id: 4 text: "Worker Node Security Configuration" type: "node" groups: - id: 4.1 text: "Worker Node Configuration Files" checks: - id: 4.1.1 text: "Ensure that the kubelet service file permissions are set to 600 or more restrictive (Automated)" audit: '/bin/sh -c ''if test -e $kubeletsvc; then stat -c permissions=%a $kubeletsvc; fi'' ' type: "skip" tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Not Applicable. The kubelet is embedded in the k3s process. There is no kubelet service file, all configuration is passed in as arguments at runtime. scored: true - id: 4.1.2 text: "Ensure that the kubelet service file ownership is set to root:root (Automated)" audit: '/bin/sh -c ''if test -e $kubeletsvc; then stat -c %U:%G $kubeletsvc; fi'' ' type: "skip" tests: test_items: - flag: root:root remediation: | Not Applicable. The kubelet is embedded in the k3s process. There is no kubelet service file, all configuration is passed in as arguments at runtime. scored: true - id: 4.1.3 text: "If proxy kubeconfig file exists ensure permissions are set to 600 or more restrictive (Automated)" audit: '/bin/sh -c ''if test -e $proxykubeconfig; then stat -c permissions=%a $proxykubeconfig; fi'' ' tests: bin_op: or test_items: - flag: "permissions" set: true compare: op: bitmask value: "600" remediation: | Run the below command (based on the file location on your system) on the each worker node. For example, chmod 600 $proxykubeconfig scored: true - id: 4.1.4 text: "If proxy kubeconfig file exists ensure ownership is set to root:root (Automated)" audit: 'stat -c %U:%G $proxykubeconfig' tests: bin_op: or test_items: - flag: root:root remediation: | Run the below command (based on the file location on your system) on the each worker node. For example, chown root:root $proxykubeconfig scored: true - id: 4.1.5 text: "Ensure that the --kubeconfig kubelet.conf file permissions are set to 600 or more restrictive (Automated)" audit: '/bin/sh -c ''if test -e $kubeletkubeconfig; then stat -c permissions=%a $kubeletkubeconfig; fi'' ' tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Run the below command (based on the file location on your system) on the each worker node. For example, chmod 600 $kubeletkubeconfig scored: true - id: 4.1.6 text: "Ensure that the --kubeconfig kubelet.conf file ownership is set to root:root (Automated)" audit: 'stat -c %U:%G $kubeletkubeconfig' tests: test_items: - flag: "root:root" compare: op: eq value: "root:root" set: true remediation: | Run the below command (based on the file location on your system) on the each worker node. For example, chown root:root $kubeletkubeconfig scored: true - id: 4.1.7 text: "Ensure that the certificate authorities file permissions are set to 600 or more restrictive (Automated)" audit: "stat -c permissions=%a $kubeletcafile" tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" set: true remediation: | Run the following command to modify the file permissions of the --client-ca-file chmod 600 $kubeletcafile scored: true - id: 4.1.8 text: "Ensure that the client certificate authorities file ownership is set to root:root (Automated)" audit: "stat -c %U:%G $kubeletcafile" tests: test_items: - flag: root:root compare: op: eq value: root:root remediation: | Run the following command to modify the ownership of the --client-ca-file. chown root:root $kubeletcafile scored: true - id: 4.1.9 text: "If the kubelet config.yaml configuration file is being used validate permissions set to 600 or more restrictive (Automated)" audit: '/bin/sh -c ''if test -e $kubeletconf; then stat -c permissions=%a $kubeletconf; fi'' ' type: "skip" tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Not Applicable. The kubelet is embedded in the k3s process. There is no kubelet config file, all configuration is passed in as arguments at runtime. scored: true - id: 4.1.10 text: "If the kubelet config.yaml configuration file is being used validate file ownership is set to root:root (Automated)" audit: '/bin/sh -c ''if test -e $kubeletconf; then stat -c %U:%G $kubeletconf; fi'' ' type: "skip" tests: test_items: - flag: root:root remediation: | Not Applicable. The kubelet is embedded in the k3s process. There is no kubelet config file, all configuration is passed in as arguments at runtime. scored: true - id: 4.2 text: "Kubelet" checks: - id: 4.2.1 text: "Ensure that the --anonymous-auth argument is set to false (Automated)" audit: '/bin/sh -c ''if test $(journalctl -m -u k3s -u k3s-agent | grep "Running kubelet" | wc -l) -gt 0; then journalctl -m -u k3s -u k3s-agent | grep "Running kubelet" | tail -n1 | grep "anonymous-auth" | grep -v grep; else echo "--anonymous-auth=false"; fi'' ' tests: test_items: - flag: "--anonymous-auth" path: '{.authentication.anonymous.enabled}' compare: op: eq value: false set: true remediation: | By default, K3s sets the --anonymous-auth to false. If you have set this to a different value, you should set it back to false. If using the K3s config file /etc/rancher/k3s/config.yaml, remove any lines similar to below. kubelet-arg: - "anonymous-auth=true" If using the command line, edit the K3s service file and remove the below argument. --kubelet-arg="anonymous-auth=true" Based on your system, restart the k3s service. For example, systemctl daemon-reload systemctl restart k3s.service scored: true - id: 4.2.2 text: "Ensure that the --authorization-mode argument is not set to AlwaysAllow (Automated)" audit: '/bin/sh -c ''if test $(journalctl -m -u k3s -u k3s-agent | grep "Running kubelet" | wc -l) -gt 0; then journalctl -m -u k3s -u k3s-agent | grep "Running kubelet" | tail -n1 | grep "authorization-mode"; else echo "--authorization-mode=Webhook"; fi'' ' tests: test_items: - flag: --authorization-mode path: '{.authorization.mode}' compare: op: nothave value: AlwaysAllow set: true remediation: | By default, K3s does not set the --authorization-mode to AlwaysAllow. If using the K3s config file /etc/rancher/k3s/config.yaml, remove any lines similar to below. kubelet-arg: - "authorization-mode=AlwaysAllow" If using the command line, edit the K3s service file and remove the below argument. --kubelet-arg="authorization-mode=AlwaysAllow" Based on your system, restart the k3s service. For example, systemctl daemon-reload systemctl restart k3s.service scored: true - id: 4.2.3 text: "Ensure that the --client-ca-file argument is set as appropriate (Automated)" audit: '/bin/sh -c ''if test $(journalctl -m -u k3s -u k3s-agent | grep "Running kubelet" | wc -l) -gt 0; then journalctl -m -u k3s -u k3s-agent | grep "Running kubelet" | tail -n1 | grep "client-ca-file"; else echo "--client-ca-file=/var/lib/rancher/k3s/server/tls/request-header-ca.crt"; fi'' ' tests: test_items: - flag: --client-ca-file path: '{.authentication.x509.clientCAFile}' set: true remediation: | By default, K3s automatically provides the client ca certificate for the Kubelet. It is generated and located at /var/lib/rancher/k3s/agent/client-ca.crt scored: true - id: 4.2.4 text: "Verify that the --read-only-port argument is set to 0 (Automated)" audit: "journalctl -m -u k3s -u k3s-agent | grep 'Running kubelet' | tail -n1" tests: bin_op: or test_items: - flag: "--read-only-port" path: '{.readOnlyPort}' compare: op: eq value: 0 - flag: "--read-only-port" path: '{.readOnlyPort}' set: false remediation: | By default, K3s sets the --read-only-port to 0. If you have set this to a different value, you should set it back to 0. If using the K3s config file /etc/rancher/k3s/config.yaml, remove any lines similar to below. kubelet-arg: - "read-only-port=XXXX" If using the command line, edit the K3s service file and remove the below argument. --kubelet-arg="read-only-port=XXXX" Based on your system, restart the k3s service. For example, systemctl daemon-reload systemctl restart k3s.service scored: true - id: 4.2.5 text: "Ensure that the --streaming-connection-idle-timeout argument is not set to 0 (Manual)" audit: "journalctl -m -u k3s -u k3s-agent | grep 'Running kubelet' | tail -n1" tests: test_items: - flag: --streaming-connection-idle-timeout path: '{.streamingConnectionIdleTimeout}' compare: op: noteq value: 0 - flag: --streaming-connection-idle-timeout path: '{.streamingConnectionIdleTimeout}' set: false bin_op: or remediation: | If using the K3s config file /etc/rancher/k3s/config.yaml, set the following parameter to an appropriate value. kubelet-arg: - "streaming-connection-idle-timeout=5m" If using the command line, run K3s with --kubelet-arg="streaming-connection-idle-timeout=5m". Based on your system, restart the k3s service. For example, systemctl restart k3s.service scored: false - id: 4.2.6 text: "Ensure that the --protect-kernel-defaults argument is set to true (Automated)" audit: "journalctl -m -u k3s -u k3s-agent | grep 'Running kubelet' | tail -n1" tests: test_items: - flag: --protect-kernel-defaults path: '{.protectKernelDefaults}' compare: op: eq value: true set: true remediation: | If using the K3s config file /etc/rancher/k3s/config.yaml, set the following parameter. protect-kernel-defaults: true If using the command line, run K3s with --protect-kernel-defaults=true. Based on your system, restart the k3s service. For example, systemctl restart k3s.service scored: true - id: 4.2.7 text: "Ensure that the --make-iptables-util-chains argument is set to true (Automated)" audit: "journalctl -m -u k3s -u k3s-agent | grep 'Running kubelet' | tail -n1" tests: test_items: - flag: --make-iptables-util-chains path: '{.makeIPTablesUtilChains}' compare: op: eq value: true set: true - flag: --make-iptables-util-chains path: '{.makeIPTablesUtilChains}' set: false bin_op: or remediation: | If using the K3s config file /etc/rancher/k3s/config.yaml, set the following parameter. kubelet-arg: - "make-iptables-util-chains=true" If using the command line, run K3s with --kubelet-arg="make-iptables-util-chains=true". Based on your system, restart the k3s service. For example, systemctl restart k3s.service scored: true - id: 4.2.8 text: "Ensure that the --hostname-override argument is not set (Automated)" type: "skip" audit: "journalctl -m -u k3s -u k3s-agent | grep 'Running kubelet' | tail -n1" tests: test_items: - flag: --hostname-override set: false remediation: | Not Applicable. By default, K3s does set the --hostname-override argument. Per CIS guidelines, this is to comply with cloud providers that require this flag to ensure that hostname matches node names. scored: true - id: 4.2.9 text: "Ensure that the eventRecordQPS argument is set to a level which ensures appropriate event capture (Manual)" audit: "journalctl -m -u k3s -u k3s-agent | grep 'Running kubelet' | tail -n1" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --event-qps path: '{.eventRecordQPS}' compare: op: eq value: 0 remediation: | By default, K3s sets the event-qps to 0. Should you wish to change this, If using the K3s config file /etc/rancher/k3s/config.yaml, set the following parameter to an appropriate value. kubelet-arg: - "event-qps=" If using the command line, run K3s with --kubelet-arg="event-qps=". Based on your system, restart the k3s service. For example, systemctl restart k3s.service scored: false - id: 4.2.10 text: "Ensure that the --tls-cert-file and --tls-private-key-file arguments are set as appropriate (Automated)" audit: "journalctl -m -u k3s -u k3s-agent | grep 'Running kubelet' | tail -n1" tests: test_items: - flag: --tls-cert-file path: '/var/lib/rancher/k3s/agent/serving-kubelet.crt' - flag: --tls-private-key-file path: '/var/lib/rancher/k3s/agent/serving-kubelet.key' remediation: | By default, K3s automatically provides the TLS certificate and private key for the Kubelet. They are generated and located at /var/lib/rancher/k3s/agent/serving-kubelet.crt and /var/lib/rancher/k3s/agent/serving-kubelet.key If for some reason you need to provide your own certificate and key, you can set the below parameters in the K3s config file /etc/rancher/k3s/config.yaml. kubelet-arg: - "tls-cert-file=" - "tls-private-key-file=" scored: true - id: 4.2.11 text: "Ensure that the --rotate-certificates argument is not set to false (Automated)" audit: "journalctl -m -u k3s -u k3s-agent | grep 'Running kubelet' | tail -n1" audit_config: "/bin/sh -c 'if test -e $kubeletconf; then /bin/cat $kubeletconf; fi' " tests: test_items: - flag: --rotate-certificates path: '{.rotateCertificates}' compare: op: eq value: true - flag: --rotate-certificates path: '{.rotateCertificates}' set: false bin_op: or remediation: | By default, K3s does not set the --rotate-certificates argument. If you have set this flag with a value of `false`, you should either set it to `true` or completely remove the flag. If using the K3s config file /etc/rancher/k3s/config.yaml, remove any rotate-certificates parameter. If using the command line, remove the K3s flag --kubelet-arg="rotate-certificates". Based on your system, restart the k3s service. For example, systemctl restart k3s.service scored: true - id: 4.2.12 text: "Verify that the RotateKubeletServerCertificate argument is set to true (Automated)" audit: "journalctl -m -u k3s -u k3s-agent | grep 'Running kubelet' | tail -n1" tests: bin_op: or test_items: - flag: RotateKubeletServerCertificate path: '{.featureGates.RotateKubeletServerCertificate}' compare: op: nothave value: false - flag: RotateKubeletServerCertificate path: '{.featureGates.RotateKubeletServerCertificate}' set: false remediation: | By default, K3s does not set the RotateKubeletServerCertificate feature gate. If you have enabled this feature gate, you should remove it. If using the K3s config file /etc/rancher/k3s/config.yaml, remove any feature-gate=RotateKubeletServerCertificate parameter. If using the command line, remove the K3s flag --kubelet-arg="feature-gate=RotateKubeletServerCertificate". Based on your system, restart the k3s service. For example, systemctl restart k3s.service scored: true - id: 4.2.13 text: "Ensure that the Kubelet only makes use of Strong Cryptographic Ciphers (Manual)" audit: "journalctl -m -u k3s -u k3s-agent | grep 'Running kubelet' | tail -n1" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --tls-cipher-suites path: '{range .tlsCipherSuites[:]}{}{'',''}{end}' compare: op: valid_elements value: 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 remediation: | If using a K3s config file /etc/rancher/k3s/config.yaml, edit the file to set `tlsCipherSuites` to kubelet-arg: - "tls-cipher-suites=TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,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_CHACHA20_POLY1305" or to a subset of these values. If using the command line, add the K3s flag --kubelet-arg="tls-cipher-suites=" Based on your system, restart the k3s service. For example, systemctl restart k3s.service scored: false ================================================ FILE: cfg/k3s-cis-1.24/policies.yaml ================================================ --- controls: version: "k3s-cis-1.24" id: 5 text: "Kubernetes Policies" type: "policies" groups: - id: 5.1 text: "RBAC and Service Accounts" checks: - id: 5.1.1 text: "Ensure that the cluster-admin role is only used where required (Manual)" type: "manual" remediation: | Identify all clusterrolebindings to the cluster-admin role. Check if they are used and if they need this role or if they could use a role with fewer privileges. Where possible, first bind users to a lower privileged role and then remove the clusterrolebinding to the cluster-admin role : kubectl delete clusterrolebinding [name] scored: false - id: 5.1.2 text: "Minimize access to secrets (Manual)" type: "manual" remediation: | Where possible, remove get, list and watch access to Secret objects in the cluster. scored: false - id: 5.1.3 text: "Minimize wildcard use in Roles and ClusterRoles (Manual)" type: "manual" remediation: | Where possible replace any use of wildcards in clusterroles and roles with specific objects or actions. scored: false - id: 5.1.4 text: "Minimize access to create pods (Manual)" type: "manual" remediation: | Where possible, remove create access to pod objects in the cluster. scored: false - id: 5.1.5 text: "Ensure that default service accounts are not actively used. (Manual)" type: "manual" remediation: | Create explicit service accounts wherever a Kubernetes workload requires specific access to the Kubernetes API server. Modify the configuration of each default service account to include this value automountServiceAccountToken: false scored: false - id: 5.1.6 text: "Ensure that Service Account Tokens are only mounted where necessary (Manual)" type: "manual" remediation: | Modify the definition of pods and service accounts which do not need to mount service account tokens to disable it. scored: false - id: 5.1.7 text: "Avoid use of system:masters group (Manual)" type: "manual" remediation: | Remove the system:masters group from all users in the cluster. scored: false - id: 5.1.8 text: "Limit use of the Bind, Impersonate and Escalate permissions in the Kubernetes cluster (Manual)" type: "manual" remediation: | Where possible, remove the impersonate, bind and escalate rights from subjects. scored: false - id: 5.2 text: "Pod Security Standards" checks: - id: 5.2.1 text: "Ensure that the cluster has at least one active policy control mechanism in place (Manual)" type: "manual" remediation: | Ensure that either Pod Security Admission or an external policy control system is in place for every namespace which contains user workloads. scored: false - id: 5.2.2 text: "Minimize the admission of privileged containers (Manual)" type: "manual" remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of privileged containers. scored: false - id: 5.2.3 text: "Minimize the admission of containers wishing to share the host process ID namespace (Automated)" type: "manual" remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of `hostPID` containers. scored: false - id: 5.2.4 text: "Minimize the admission of containers wishing to share the host IPC namespace (Automated)" type: "manual" remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of `hostIPC` containers. scored: false - id: 5.2.5 text: "Minimize the admission of containers wishing to share the host network namespace (Automated)" type: "manual" remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of `hostNetwork` containers. scored: false - id: 5.2.6 text: "Minimize the admission of containers with allowPrivilegeEscalation (Automated)" type: "manual" remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of containers with `.spec.allowPrivilegeEscalation` set to `true`. scored: false - id: 5.2.7 text: "Minimize the admission of root containers (Automated)" type: "manual" remediation: | Create a policy for each namespace in the cluster, ensuring that either `MustRunAsNonRoot` or `MustRunAs` with the range of UIDs not including 0, is set. scored: false - id: 5.2.8 text: "Minimize the admission of containers with the NET_RAW capability (Automated)" type: "manual" remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of containers with the `NET_RAW` capability. scored: false - id: 5.2.9 text: "Minimize the admission of containers with added capabilities (Automated)" type: "manual" remediation: | Ensure that `allowedCapabilities` is not present in policies for the cluster unless it is set to an empty array. scored: false - id: 5.2.10 text: "Minimize the admission of containers with capabilities assigned (Manual)" type: "manual" remediation: | Review the use of capabilities in applications running on your cluster. Where a namespace contains applications which do not require any Linux capabities to operate consider adding a PSP which forbids the admission of containers which do not drop all capabilities. scored: false - id: 5.2.11 text: "Minimize the admission of Windows HostProcess containers (Manual)" type: "manual" remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of containers that have `.securityContext.windowsOptions.hostProcess` set to `true`. scored: false - id: 5.2.12 text: "Minimize the admission of HostPath volumes (Manual)" type: "manual" remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of containers with `hostPath` volumes. scored: false - id: 5.2.13 text: "Minimize the admission of containers which use HostPorts (Manual)" type: "manual" remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of containers which use `hostPort` sections. scored: false - id: 5.3 text: "Network Policies and CNI" checks: - id: 5.3.1 text: "Ensure that the CNI in use supports NetworkPolicies (Manual)" type: "manual" remediation: | If the CNI plugin in use does not support network policies, consideration should be given to making use of a different plugin, or finding an alternate mechanism for restricting traffic in the Kubernetes cluster. scored: false - id: 5.3.2 text: "Ensure that all Namespaces have NetworkPolicies defined (Manual)" type: "manual" remediation: | Follow the documentation and create NetworkPolicy objects as you need them. scored: false - id: 5.4 text: "Secrets Management" checks: - id: 5.4.1 text: "Prefer using Secrets as files over Secrets as environment variables (Manual)" type: "manual" remediation: | If possible, rewrite application code to read Secrets from mounted secret files, rather than from environment variables. scored: false - id: 5.4.2 text: "Consider external secret storage (Manual)" type: "manual" remediation: | Refer to the Secrets management options offered by your cloud provider or a third-party secrets management solution. scored: false - id: 5.5 text: "Extensible Admission Control" checks: - id: 5.5.1 text: "Configure Image Provenance using ImagePolicyWebhook admission controller (Manual)" type: "manual" remediation: | Follow the Kubernetes documentation and setup image provenance. scored: false - id: 5.7 text: "General Policies" checks: - id: 5.7.1 text: "Create administrative boundaries between resources using namespaces (Manual)" type: "manual" remediation: | Follow the documentation and create namespaces for objects in your deployment as you need them. scored: false - id: 5.7.2 text: "Ensure that the seccomp profile is set to docker/default in your Pod definitions (Manual)" type: "manual" remediation: | Use `securityContext` to enable the docker/default seccomp profile in your pod definitions. An example is as below: securityContext: seccompProfile: type: RuntimeDefault scored: false - id: 5.7.3 text: "Apply SecurityContext to your Pods and Containers (Manual)" type: "manual" remediation: | Follow the Kubernetes documentation and apply SecurityContexts to your Pods. For a suggested list of SecurityContexts, you may refer to the CIS Security Benchmark for Docker Containers. scored: false - id: 5.7.4 text: "The default namespace should not be used (Manual)" type: "manual" remediation: | Ensure that namespaces are created to allow for appropriate segregation of Kubernetes resources and that all new resources are created in a specific namespace. scored: false ================================================ FILE: cfg/k3s-cis-1.7/config.yaml ================================================ --- ## Version-specific settings that override the values in cfg/config.yaml master: components: - apiserver - kubelet - scheduler - controllermanager - etcd - policies apiserver: bins: - containerd kubelet: bins: - containerd defaultkubeconfig: /var/lib/rancher/k3s/agent/kubelet.kubeconfig defaultcafile: /var/lib/rancher/k3s/agent/client-ca.crt scheduler: bins: - containerd kubeconfig: - /var/lib/rancher/k3s/server/cred/scheduler.kubeconfig controllermanager: bins: - containerd kubeconfig: - /var/lib/rancher/k3s/server/cred/controller.kubeconfig etcd: bins: - containerd etcd: components: - etcd etcd: confs: /var/lib/rancher/k3s/server/db/etcd/config node: components: - kubelet - proxy kubelet: bins: - containerd defaultkubeconfig: /var/lib/rancher/k3s/agent/kubelet.kubeconfig defaultcafile: /var/lib/rancher/k3s/agent/client-ca.crt proxy: bins: - containerd defaultkubeconfig: /var/lib/rancher/k3s/agent/kubeproxy.kubeconfig policies: components: - policies ================================================ FILE: cfg/k3s-cis-1.7/controlplane.yaml ================================================ --- controls: version: "k3s-cis-1.7" id: 3 text: "Control Plane Configuration" type: "controlplane" groups: - id: 3.1 text: "Authentication and Authorization" checks: - id: 3.1.1 text: "Client certificate authentication should not be used for users (Manual)" type: "manual" remediation: | Alternative mechanisms provided by Kubernetes such as the use of OIDC should be implemented in place of client certificates. scored: false - id: 3.1.2 text: "Service account token authentication should not be used for users (Manual)" type: "manual" remediation: | Alternative mechanisms provided by Kubernetes such as the use of OIDC should be implemented in place of service account tokens. scored: false - id: 3.1.3 text: "Bootstrap token authentication should not be used for users (Manual)" type: "manual" remediation: | Alternative mechanisms provided by Kubernetes such as the use of OIDC should be implemented in place of bootstrap tokens. scored: false - id: 3.2 text: "Logging" checks: - id: 3.2.1 text: "Ensure that a minimal audit policy is created (Manual)" audit: "journalctl -m -u k3s | grep 'Running kube-apiserver' | tail -n1 | grep 'audit-policy-file'" tests: test_items: - flag: "--audit-policy-file" set: true remediation: | Create an audit policy file for your cluster. scored: false - id: 3.2.2 text: "Ensure that the audit policy covers key security concerns (Manual)" type: "manual" remediation: | Review the audit policy provided for the cluster and ensure that it covers at least the following areas, - Access to Secrets managed by the cluster. Care should be taken to only log Metadata for requests to Secrets, ConfigMaps, and TokenReviews, in order to avoid risk of logging sensitive data. - Modification of Pod and Deployment objects. - Use of `pods/exec`, `pods/portforward`, `pods/proxy` and `services/proxy`. For most requests, minimally logging at the Metadata level is recommended (the most basic level of logging). scored: false ================================================ FILE: cfg/k3s-cis-1.7/etcd.yaml ================================================ --- controls: version: "k3s-cis-1.7" id: 2 text: "Etcd Node Configuration" type: "etcd" groups: - id: 2 text: "Etcd Node Configuration" checks: - id: 2.1 text: "Ensure that the --cert-file and --key-file arguments are set as appropriate (Automated)" audit_config: "cat $etcdconf" tests: bin_op: and test_items: - path: "{.client-transport-security.cert-file}" compare: op: eq value: "/var/lib/rancher/k3s/server/tls/etcd/server-client.crt" - path: "{.client-transport-security.key-file}" compare: op: eq value: "/var/lib/rancher/k3s/server/tls/etcd/server-client.key" remediation: | If running on with sqlite or a external DB, etcd checks are Not Applicable. When running with embedded-etcd, K3s generates cert and key files for etcd. These are located in /var/lib/rancher/k3s/server/tls/etcd/. If this check fails, ensure that the configuration file $etcdconf has not been modified to use custom cert and key files. scored: false - id: 2.2 text: "Ensure that the --client-cert-auth argument is set to true (Automated)" audit_config: "cat $etcdconf" tests: test_items: - path: "{.client-transport-security.client-cert-auth}" compare: op: eq value: true remediation: | If running on with sqlite or a external DB, etcd checks are Not Applicable. When running with embedded-etcd, K3s sets the --client-cert-auth parameter to true. If this check fails, ensure that the configuration file $etcdconf has not been modified to disable client certificate authentication. scored: false - id: 2.3 text: "Ensure that the --auto-tls argument is not set to true (Automated)" audit_config: "cat $etcdconf" tests: bin_op: or test_items: - path: "{.client-transport-security.auto-tls}" compare: op: eq value: false - path: "{.client-transport-security.auto-tls}" set: false remediation: | If running on with sqlite or a external DB, etcd checks are Not Applicable. When running with embedded-etcd, K3s does not set the --auto-tls parameter. If this check fails, edit the etcd pod specification file $etcdconf on the master node and either remove the --auto-tls parameter or set it to false. client-transport-security: auto-tls: false scored: false - id: 2.4 text: "Ensure that the --peer-cert-file and --peer-key-file arguments are set as appropriate (Automated)" audit_config: "cat $etcdconf" tests: bin_op: and test_items: - path: "{.peer-transport-security.cert-file}" compare: op: eq value: "/var/lib/rancher/k3s/server/tls/etcd/peer-server-client.crt" - path: "{.peer-transport-security.key-file}" compare: op: eq value: "/var/lib/rancher/k3s/server/tls/etcd/peer-server-client.key" remediation: | If running on with sqlite or a external DB, etcd checks are Not Applicable. When running with embedded-etcd, K3s generates peer cert and key files for etcd. These are located in /var/lib/rancher/k3s/server/tls/etcd/. If this check fails, ensure that the configuration file $etcdconf has not been modified to use custom peer cert and key files. scored: false - id: 2.5 text: "Ensure that the --peer-client-cert-auth argument is set to true (Automated)" audit_config: "cat $etcdconf" tests: test_items: - path: "{.peer-transport-security.client-cert-auth}" compare: op: eq value: true remediation: | If running on with sqlite or a external DB, etcd checks are Not Applicable. When running with embedded-etcd, K3s sets the --peer-cert-auth parameter to true. If this check fails, ensure that the configuration file $etcdconf has not been modified to disable peer client certificate authentication. scored: false - id: 2.6 text: "Ensure that the --peer-auto-tls argument is not set to true (Automated)" audit_config: "cat $etcdconf" tests: bin_op: or test_items: - path: "{.peer-transport-security.auto-tls}" compare: op: eq value: false - path: "{.peer-transport-security.auto-tls}" set: false remediation: | If running on with sqlite or a external DB, etcd checks are Not Applicable. When running with embedded-etcd, K3s does not set the --peer-auto-tls parameter. If this check fails, edit the etcd pod specification file $etcdconf on the master node and either remove the --peer-auto-tls parameter or set it to false. peer-transport-security: auto-tls: false scored: false - id: 2.7 text: "Ensure that a unique Certificate Authority is used for etcd (Automated)" audit_config: "cat $etcdconf" tests: test_items: - path: "{.peer-transport-security.trusted-ca-file}" compare: op: eq value: "/var/lib/rancher/k3s/server/tls/etcd/peer-ca.crt" remediation: | If running on with sqlite or a external DB, etcd checks are Not Applicable. When running with embedded-etcd, K3s generates a unique certificate authority for etcd. This is located at /var/lib/rancher/k3s/server/tls/etcd/peer-ca.crt. If this check fails, ensure that the configuration file $etcdconf has not been modified to use a shared certificate authority. scored: false ================================================ FILE: cfg/k3s-cis-1.7/master.yaml ================================================ --- controls: version: "k3s-cis-1.7" id: 1 text: "Control Plane Security Configuration" type: "master" groups: - id: 1.1 text: "Control Plane Node Configuration Files" checks: - id: 1.1.1 text: "Ensure that the API server pod specification file permissions are set to 600 or more restrictive (Automated)" type: "skip" audit: "/bin/sh -c 'if test -e $apiserverconf; then stat -c permissions=%a $apiserverconf; fi'" tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Not Applicable. By default, K3s embeds the api server within the k3s process. There is no API server pod specification file. scored: true - id: 1.1.2 text: "Ensure that the API server pod specification file ownership is set to root:root (Automated)" type: "skip" audit: "/bin/sh -c 'if test -e $apiserverconf; then stat -c %U:%G $apiserverconf; fi'" tests: test_items: - flag: "root:root" remediation: | Not Applicable. By default, K3s embeds the api server within the k3s process. There is no API server pod specification file. scored: true - id: 1.1.3 text: "Ensure that the controller manager pod specification file permissions are set to 600 or more restrictive (Automated)" type: "skip" audit: "/bin/sh -c 'if test -e $controllermanagerconf; then stat -c permissions=%a $controllermanagerconf; fi'" tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Not Applicable. By default, K3s embeds the controller manager within the k3s process. There is no controller manager pod specification file. scored: true - id: 1.1.4 text: "Ensure that the controller manager pod specification file ownership is set to root:root (Automated)" type: "skip" audit: "/bin/sh -c 'if test -e $controllermanagerconf; then stat -c %U:%G $controllermanagerconf; fi'" tests: test_items: - flag: "root:root" remediation: | Not Applicable. By default, K3s embeds the controller manager within the k3s process. There is no controller manager pod specification file. scored: true - id: 1.1.5 text: "Ensure that the scheduler pod specification file permissions are set to 600 or more restrictive (Automated)" type: "skip" audit: "/bin/sh -c 'if test -e $schedulerconf; then stat -c permissions=%a $schedulerconf; fi'" tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Not Applicable. By default, K3s embeds the scheduler within the k3s process. There is no scheduler pod specification file. scored: true - id: 1.1.6 text: "Ensure that the scheduler pod specification file ownership is set to root:root (Automated)" type: "skip" audit: "/bin/sh -c 'if test -e $schedulerconf; then stat -c %U:%G $schedulerconf; fi'" tests: test_items: - flag: "root:root" remediation: | Not Applicable. By default, K3s embeds the scheduler within the k3s process. There is no scheduler pod specification file. scored: true - id: 1.1.7 text: "Ensure that the etcd pod specification file permissions are set to 600 or more restrictive (Automated)" type: "skip" audit: "/bin/sh -c 'if test -e $etcdconf; then find $etcdconf -name '*etcd*' | xargs stat -c permissions=%a; fi'" use_multiple_values: true tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Not Applicable. By default, K3s embeds etcd within the k3s process. There is no etcd pod specification file. scored: true - id: 1.1.8 text: "Ensure that the etcd pod specification file ownership is set to root:root (Automated)" type: "skip" audit: "/bin/sh -c 'if test -e $etcdconf; then find $etcdconf -name '*etcd*' | xargs stat -c %U:%G; fi'" use_multiple_values: true tests: test_items: - flag: "root:root" remediation: | Not Applicable. By default, K3s embeds etcd within the k3s process. There is no etcd pod specification file. scored: true - id: 1.1.9 text: "Ensure that the Container Network Interface file permissions are set to 600 or more restrictive (Manual)" audit: find /var/lib/cni/networks -type f ! -name lock 2> /dev/null | xargs --no-run-if-empty stat -c permissions=%a use_multiple_values: true tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | By default, K3s sets the CNI file permissions to 644. Note that for many CNIs, a lock file is created with permissions 750. This is expected and can be ignored. If you modify your CNI configuration, ensure that the permissions are set to 600. For example, chmod 600 /var/lib/cni/networks/ scored: false - id: 1.1.10 text: "Ensure that the Container Network Interface file ownership is set to root:root (Automated)" audit: find /var/lib/cni/networks -type f 2> /dev/null | xargs --no-run-if-empty stat -c %U:%G use_multiple_values: true tests: test_items: - flag: "root:root" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chown root:root /var/lib/cni/networks/ scored: true - id: 1.1.11 text: "Ensure that the etcd data directory permissions are set to 700 or more restrictive (Automated)" audit: | if [ "$(journalctl -m -u k3s | grep -m1 'Managed etcd cluster' | wc -l)" -gt 0 ]; then stat -c permissions=%a /var/lib/rancher/k3s/server/db/etcd else echo "permissions=700" fi tests: test_items: - flag: "permissions" compare: op: bitmask value: "700" remediation: | On the etcd server node, get the etcd data directory, passed as an argument --data-dir, from the command 'ps -ef | grep etcd'. Run the below command (based on the etcd data directory found above). For example, chmod 700 $etcddatadir scored: true - id: 1.1.12 text: "Ensure that the etcd data directory ownership is set to etcd:etcd (Automated)" audit: ps -ef | grep $etcdbin | grep -- --data-dir | sed 's%.*data-dir[= ]\([^ ]*\).*%\1%' | xargs stat -c %U:%G type: "skip" tests: test_items: - flag: "etcd:etcd" remediation: | Not Applicable. For K3s, etcd is embedded within the k3s process. There is no separate etcd process. Therefore the etcd data directory ownership is managed by the k3s process and should be root:root. scored: true - id: 1.1.13 text: "Ensure that the admin.conf file permissions are set to 600 or more restrictive (Automated)" audit: "/bin/sh -c 'if test -e /var/lib/rancher/k3s/server/cred/admin.kubeconfig; then stat -c permissions=%a /var/lib/rancher/k3s/server/cred/admin.kubeconfig; fi'" tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chmod 600 /var/lib/rancher/k3s/server/cred/admin.kubeconfig scored: true - id: 1.1.14 text: "Ensure that the admin.conf file ownership is set to root:root (Automated)" audit: "/bin/sh -c 'if test -e /var/lib/rancher/k3s/server/cred/admin.kubeconfig; then stat -c %U:%G /var/lib/rancher/k3s/server/cred/admin.kubeconfig; fi'" tests: test_items: - flag: "root:root" compare: op: eq value: "root:root" set: true remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chown root:root /var/lib/rancher/k3s/server/cred/admin.kubeconfig scored: true - id: 1.1.15 text: "Ensure that the scheduler.conf file permissions are set to 600 or more restrictive (Automated)" audit: "/bin/sh -c 'if test -e $schedulerkubeconfig; then stat -c permissions=%a $schedulerkubeconfig; fi'" tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chmod 600 $schedulerkubeconfig scored: true - id: 1.1.16 text: "Ensure that the scheduler.conf file ownership is set to root:root (Automated)" audit: "/bin/sh -c 'if test -e $schedulerkubeconfig; then stat -c %U:%G $schedulerkubeconfig; fi'" tests: test_items: - flag: "root:root" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chown root:root $schedulerkubeconfig scored: true - id: 1.1.17 text: "Ensure that the controller-manager.conf file permissions are set to 600 or more restrictive (Automated)" audit: "/bin/sh -c 'if test -e $controllermanagerkubeconfig; then stat -c permissions=%a $controllermanagerkubeconfig; fi'" tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chmod 600 $controllermanagerkubeconfig scored: true - id: 1.1.18 text: "Ensure that the controller-manager.conf file ownership is set to root:root (Automated)" audit: "stat -c %U:%G $controllermanagerkubeconfig" tests: test_items: - flag: "root:root" compare: op: eq value: "root:root" set: true remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chown root:root $controllermanagerkubeconfig scored: true - id: 1.1.19 text: "Ensure that the Kubernetes PKI directory and file ownership is set to root:root (Automated)" audit: "stat -c %U:%G /var/lib/rancher/k3s/server/tls" use_multiple_values: true tests: test_items: - flag: "root:root" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chown -R root:root /var/lib/rancher/k3s/server/tls scored: true - id: 1.1.20 text: "Ensure that the Kubernetes PKI certificate file permissions are set to 600 or more restrictive (Manual)" audit: "/bin/sh -c 'stat -c permissions=%a /var/lib/rancher/k3s/server/tls/*.crt'" use_multiple_values: true tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Run the below command (based on the file location on your system) on the master node. For example, chmod -R 600 /var/lib/rancher/k3s/server/tls/*.crt scored: false - id: 1.1.21 text: "Ensure that the Kubernetes PKI key file permissions are set to 600 (Automated)" audit: "/bin/sh -c 'stat -c permissions=%a /var/lib/rancher/k3s/server/tls/*.key'" use_multiple_values: true tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Run the below command (based on the file location on your system) on the master node. For example, chmod -R 600 /var/lib/rancher/k3s/server/tls/*.key scored: true - id: 1.2 text: "API Server" checks: - id: 1.2.1 text: "Ensure that the --anonymous-auth argument is set to false (Automated)" audit: "journalctl -m -u k3s | grep 'Running kube-apiserver' | tail -n1 | grep 'anonymous-auth'" tests: test_items: - flag: "--anonymous-auth" compare: op: eq value: false remediation: | By default, K3s sets the --anonymous-auth argument to false. If it is set to true, edit the K3s config file /etc/rancher/k3s/config.yaml and remove anything similar to below. kube-apiserver-arg: - "anonymous-auth=true" scored: true - id: 1.2.2 text: "Ensure that the --token-auth-file parameter is not set (Automated)" audit: "journalctl -m -u k3s | grep 'Running kube-apiserver' | tail -n1" tests: test_items: - flag: "--token-auth-file" set: false remediation: | Follow the documentation and configure alternate mechanisms for authentication. If this check fails, edit the K3s config file /etc/rancher/k3s/config.yaml and remove anything similar to below. kube-apiserver-arg: - "token-auth-file=" scored: true - id: 1.2.3 text: "Ensure that the --DenyServiceExternalIPs is not set (Automated)" audit: "journalctl -m -u k3s | grep 'Running kube-apiserver' | tail -n1" tests: bin_op: or test_items: - flag: "--enable-admission-plugins" compare: op: nothave value: "DenyServiceExternalIPs" set: true - flag: "--enable-admission-plugins" set: false remediation: | By default, K3s does not set DenyServiceExternalIPs. If this check fails, edit the K3s config file /etc/rancher/k3s/config.yaml, remove any lines like below. kube-apiserver-arg: - "enable-admission-plugins=DenyServiceExternalIPs" scored: true - id: 1.2.4 text: "Ensure that the --kubelet-client-certificate and --kubelet-client-key arguments are set as appropriate (Automated)" audit: "journalctl -m -u k3s | grep 'Running kube-apiserver' | tail -n1 | grep 'kubelet-certificate-authority'" tests: bin_op: and test_items: - flag: "--kubelet-client-certificate" - flag: "--kubelet-client-key" remediation: | By default, K3s automatically provides the kubelet client certificate and key. They are generated and located at /var/lib/rancher/k3s/server/tls/client-kube-apiserver.crt and /var/lib/rancher/k3s/server/tls/client-kube-apiserver.key If for some reason you need to provide your own certificate and key, you can set the below parameters in the K3s config file /etc/rancher/k3s/config.yaml. kube-apiserver-arg: - "kubelet-client-certificate=" - "kubelet-client-key=" scored: true - id: 1.2.5 text: "Ensure that the --kubelet-certificate-authority argument is set as appropriate (Automated)" audit: "journalctl -m -u k3s | grep 'Running kube-apiserver' | tail -n1 | grep 'kubelet-certificate-authority'" tests: test_items: - flag: "--kubelet-certificate-authority" remediation: | By default, K3s automatically provides the kubelet CA cert file, at /var/lib/rancher/k3s/server/tls/server-ca.crt. If this check fails, edit the K3s config file /etc/rancher/k3s/config.yaml and remove any lines like below. kube-apiserver-arg: - "kubelet-certificate-authority=" scored: true - id: 1.2.6 text: "Ensure that the --authorization-mode argument is not set to AlwaysAllow (Automated)" audit: "journalctl -m -u k3s | grep 'Running kube-apiserver' | tail -n1 | grep 'authorization-mode'" tests: test_items: - flag: "--authorization-mode" compare: op: nothave value: "AlwaysAllow" remediation: | By default, K3s does not set the --authorization-mode to AlwaysAllow. If this check fails, edit K3s config file /etc/rancher/k3s/config.yaml, remove any lines like below. kube-apiserver-arg: - "authorization-mode=AlwaysAllow" scored: true - id: 1.2.7 text: "Ensure that the --authorization-mode argument includes Node (Automated)" audit: "journalctl -m -u k3s | grep 'Running kube-apiserver' | tail -n1 | grep 'authorization-mode'" tests: test_items: - flag: "--authorization-mode" compare: op: has value: "Node" remediation: | By default, K3s sets the --authorization-mode to Node and RBAC. If this check fails, edit the K3s config file /etc/rancher/k3s/config.yaml, ensure that you are not overriding authorization-mode. scored: true - id: 1.2.8 text: "Ensure that the --authorization-mode argument includes RBAC (Automated)" audit: "journalctl -m -u k3s | grep 'Running kube-apiserver' | tail -n1 | grep 'authorization-mode'" tests: test_items: - flag: "--authorization-mode" compare: op: has value: "RBAC" remediation: | By default, K3s sets the --authorization-mode to Node and RBAC. If this check fails, edit the K3s config file /etc/rancher/k3s/config.yaml, ensure that you are not overriding authorization-mode. scored: true - id: 1.2.9 text: "Ensure that the admission control plugin EventRateLimit is set (Manual)" audit: "journalctl -m -u k3s | grep 'Running kube-apiserver' | tail -n1 | grep 'enable-admission-plugins'" tests: test_items: - flag: "--enable-admission-plugins" compare: op: has value: "EventRateLimit" remediation: | Follow the Kubernetes documentation and set the desired limits in a configuration file. Then, edit the K3s config file /etc/rancher/k3s/config.yaml and set the below parameters. kube-apiserver-arg: - "enable-admission-plugins=...,EventRateLimit,..." - "admission-control-config-file=" scored: false - id: 1.2.10 text: "Ensure that the admission control plugin AlwaysAdmit is not set (Automated)" audit: "journalctl -m -u k3s | grep 'Running kube-apiserver' | tail -n1 | grep 'enable-admission-plugins'" tests: bin_op: or test_items: - flag: "--enable-admission-plugins" compare: op: nothave value: AlwaysAdmit - flag: "--enable-admission-plugins" set: false remediation: | By default, K3s does not set the --enable-admission-plugins to AlwaysAdmit. If this check fails, edit K3s config file /etc/rancher/k3s/config.yaml, remove any lines like below. kube-apiserver-arg: - "enable-admission-plugins=AlwaysAdmit" scored: true - id: 1.2.11 text: "Ensure that the admission control plugin AlwaysPullImages is set (Manual)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--enable-admission-plugins" compare: op: has value: "AlwaysPullImages" remediation: | Permissive, per CIS guidelines, "This setting could impact offline or isolated clusters, which have images pre-loaded and do not have access to a registry to pull in-use images. This setting is not appropriate for clusters which use this configuration." Edit the K3s config file /etc/rancher/k3s/config.yaml and set the below parameter. kube-apiserver-arg: - "enable-admission-plugins=...,AlwaysPullImages,..." scored: false - id: 1.2.12 text: "Ensure that the admission control plugin SecurityContextDeny is set if PodSecurityPolicy is not used (Manual)" type: "skip" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: bin_op: or test_items: - flag: "--enable-admission-plugins" compare: op: has value: "SecurityContextDeny" - flag: "--enable-admission-plugins" compare: op: has value: "PodSecurityPolicy" remediation: | Not Applicable. Enabling Pod Security Policy is no longer supported on K3s v1.25+ and will cause applications to unexpectedly fail. scored: false - id: 1.2.13 text: "Ensure that the admission control plugin ServiceAccount is set (Automated)" audit: "journalctl -m -u k3s | grep 'Running kube-apiserver' | tail -n1" tests: bin_op: or test_items: - flag: "--disable-admission-plugins" compare: op: nothave value: "ServiceAccount" - flag: "--disable-admission-plugins" set: false remediation: | By default, K3s does not set the --disable-admission-plugins to anything. Follow the documentation and create ServiceAccount objects as per your environment. If this check fails, edit the K3s config file /etc/rancher/k3s/config.yaml and remove any lines like below. kube-apiserver-arg: - "disable-admission-plugins=ServiceAccount" scored: true - id: 1.2.14 text: "Ensure that the admission control plugin NamespaceLifecycle is set (Automated)" audit: "journalctl -m -u k3s | grep 'Running kube-apiserver' | tail -n1" tests: bin_op: or test_items: - flag: "--disable-admission-plugins" compare: op: nothave value: "NamespaceLifecycle" - flag: "--disable-admission-plugins" set: false remediation: | By default, K3s does not set the --disable-admission-plugins to anything. If this check fails, edit the K3s config file /etc/rancher/k3s/config.yaml and remove any lines like below. kube-apiserver-arg: - "disable-admission-plugins=...,NamespaceLifecycle,..." scored: true - id: 1.2.15 text: "Ensure that the admission control plugin NodeRestriction is set (Automated)" audit: "journalctl -m -u k3s | grep 'Running kube-apiserver' | tail -n1 | grep 'enable-admission-plugins'" tests: test_items: - flag: "--enable-admission-plugins" compare: op: has value: "NodeRestriction" remediation: | By default, K3s sets the --enable-admission-plugins to NodeRestriction. If using the K3s config file /etc/rancher/k3s/config.yaml, check that you are not overriding the admission plugins. If you are, include NodeRestriction in the list. kube-apiserver-arg: - "enable-admission-plugins=...,NodeRestriction,..." scored: true - id: 1.2.16 text: "Ensure that the --secure-port argument is not set to 0 - NoteThis recommendation is obsolete and will be deleted per the consensus process (Automated)" audit: "journalctl -m -u k3s | grep 'Running kube-apiserver' | tail -n1 | grep 'secure-port'" tests: bin_op: or test_items: - flag: "--secure-port" compare: op: gt value: 0 - flag: "--secure-port" set: false remediation: | By default, K3s sets the secure port to 6444. If this check fails, edit the K3s config file /etc/rancher/k3s/config.yaml and remove any lines like below. kube-apiserver-arg: - "secure-port=" scored: true - id: 1.2.17 text: "Ensure that the --profiling argument is set to false (Automated)" audit: "journalctl -m -u k3s | grep 'Running kube-apiserver' | tail -n1 | grep 'profiling'" tests: test_items: - flag: "--profiling" compare: op: eq value: false remediation: | By default, K3s sets the --profiling argument to false. If this check fails, edit the K3s config file /etc/rancher/k3s/config.yaml and remove any lines like below. kube-apiserver-arg: - "profiling=true" scored: true - id: 1.2.18 text: "Ensure that the --audit-log-path argument is set (Manual)" audit: "journalctl -m -u k3s | grep 'Running kube-apiserver' | tail -n1" tests: test_items: - flag: "--audit-log-path" remediation: | Edit the K3s config file /etc/rancher/k3s/config.yaml and set the audit-log-path parameter to a suitable path and file where you would like audit logs to be written, for example, kube-apiserver-arg: - "audit-log-path=/var/lib/rancher/k3s/server/logs/audit.log" scored: false - id: 1.2.19 text: "Ensure that the --audit-log-maxage argument is set to 30 or as appropriate (Manual)" audit: "journalctl -m -u k3s | grep 'Running kube-apiserver' | tail -n1" tests: test_items: - flag: "--audit-log-maxage" compare: op: gte value: 30 remediation: | Edit the K3s config file /etc/rancher/k3s/config.yaml on the control plane node and set the audit-log-maxage parameter to 30 or as an appropriate number of days, for example, kube-apiserver-arg: - "audit-log-maxage=30" scored: false - id: 1.2.20 text: "Ensure that the --audit-log-maxbackup argument is set to 10 or as appropriate (Manual)" audit: "journalctl -m -u k3s | grep 'Running kube-apiserver' | tail -n1" tests: test_items: - flag: "--audit-log-maxbackup" compare: op: gte value: 10 remediation: | Edit the K3s config file /etc/rancher/k3s/config.yaml on the control plane node and set the audit-log-maxbackup parameter to 10 or to an appropriate value. For example, kube-apiserver-arg: - "audit-log-maxbackup=10" scored: false - id: 1.2.21 text: "Ensure that the --audit-log-maxsize argument is set to 100 or as appropriate (Manual)" audit: "journalctl -m -u k3s | grep 'Running kube-apiserver' | tail -n1" tests: test_items: - flag: "--audit-log-maxsize" compare: op: gte value: 100 remediation: | Edit the K3s config file /etc/rancher/k3s/config.yaml on the control plane node and set the audit-log-maxsize parameter to an appropriate size in MB. For example, kube-apiserver-arg: - "audit-log-maxsize=100" scored: false - id: 1.2.22 text: "Ensure that the --request-timeout argument is set as appropriate (Manual)" audit: "journalctl -m -u k3s | grep 'Running kube-apiserver' | tail -n1" tests: test_items: - flag: "--request-timeout" remediation: | Permissive, per CIS guidelines, "it is recommended to set this limit as appropriate and change the default limit of 60 seconds only if needed". Edit the K3s config file /etc/rancher/k3s/config.yaml and set the below parameter if needed. For example, kube-apiserver-arg: - "request-timeout=300s" scored: false - id: 1.2.23 text: "Ensure that the --service-account-lookup argument is set to true (Automated)" audit: "journalctl -m -u k3s | grep 'Running kube-apiserver' | tail -n1" tests: bin_op: or test_items: - flag: "--service-account-lookup" set: false - flag: "--service-account-lookup" compare: op: eq value: true remediation: | By default, K3s does not set the --service-account-lookup argument. Edit the K3s config file /etc/rancher/k3s/config.yaml and set the service-account-lookup. For example, kube-apiserver-arg: - "service-account-lookup=true" Alternatively, you can delete the service-account-lookup parameter from this file so that the default takes effect. scored: true - id: 1.2.24 text: "Ensure that the --service-account-key-file argument is set as appropriate (Automated)" audit: "journalctl -m -u k3s | grep 'Running kube-apiserver' | tail -n1" tests: test_items: - flag: "--service-account-key-file" remediation: | K3s automatically generates and sets the service account key file. It is located at /var/lib/rancher/k3s/server/tls/service.key. If this check fails, edit K3s config file /etc/rancher/k3s/config.yaml and remove any lines like below. kube-apiserver-arg: - "service-account-key-file=" scored: true - id: 1.2.25 text: "Ensure that the --etcd-certfile and --etcd-keyfile arguments are set as appropriate (Automated)" audit: | if [ "$(journalctl -m -u k3s | grep -m1 'Managed etcd cluster' | wc -l)" -gt 0 ]; then journalctl -m -u k3s | grep -m1 'Running kube-apiserver' | tail -n1 else echo "--etcd-certfile AND --etcd-keyfile" fi tests: bin_op: and test_items: - flag: "--etcd-certfile" set: true - flag: "--etcd-keyfile" set: true remediation: | K3s automatically generates and sets the etcd certificate and key files. They are located at /var/lib/rancher/k3s/server/tls/etcd/client.crt and /var/lib/rancher/k3s/server/tls/etcd/client.key. If this check fails, edit the K3s config file /etc/rancher/k3s/config.yaml and remove any lines like below. kube-apiserver-arg: - "etcd-certfile=" - "etcd-keyfile=" scored: true - id: 1.2.26 text: "Ensure that the --tls-cert-file and --tls-private-key-file arguments are set as appropriate (Automated)" audit: "journalctl -m -u k3s | grep -A1 'Running kube-apiserver' | tail -n2" tests: bin_op: and test_items: - flag: "--tls-cert-file" set: true - flag: "--tls-private-key-file" set: true remediation: | By default, K3s automatically generates and provides the TLS certificate and private key for the apiserver. They are generated and located at /var/lib/rancher/k3s/server/tls/serving-kube-apiserver.crt and /var/lib/rancher/k3s/server/tls/serving-kube-apiserver.key If this check fails, edit the K3s config file /etc/rancher/k3s/config.yaml and remove any lines like below. kube-apiserver-arg: - "tls-cert-file=" - "tls-private-key-file=" scored: true - id: 1.2.27 text: "Ensure that the --client-ca-file argument is set as appropriate (Automated)" audit: "journalctl -m -u k3s | grep 'Running kube-apiserver' | tail -n1 | grep 'client-ca-file'" tests: test_items: - flag: "--client-ca-file" remediation: | By default, K3s automatically provides the client certificate authority file. It is generated and located at /var/lib/rancher/k3s/server/tls/client-ca.crt. If this check fails, edit the K3s config file /etc/rancher/k3s/config.yaml and remove any lines like below. kube-apiserver-arg: - "client-ca-file=" scored: true - id: 1.2.28 text: "Ensure that the --etcd-cafile argument is set as appropriate (Automated)" audit: "journalctl -m -u k3s | grep 'Running kube-apiserver' | tail -n1 | grep 'etcd-cafile'" tests: test_items: - flag: "--etcd-cafile" remediation: | By default, K3s automatically provides the etcd certificate authority file. It is generated and located at /var/lib/rancher/k3s/server/tls/client-ca.crt. If this check fails, edit the K3s config file /etc/rancher/k3s/config.yaml and remove any lines like below. kube-apiserver-arg: - "etcd-cafile=" scored: true - id: 1.2.29 text: "Ensure that the --encryption-provider-config argument is set as appropriate (Manual)" audit: "journalctl -m -u k3s | grep 'Running kube-apiserver' | tail -n1 | grep 'encryption-provider-config'" tests: test_items: - flag: "--encryption-provider-config" remediation: | K3s can be configured to use encryption providers to encrypt secrets at rest. Edit the K3s config file /etc/rancher/k3s/config.yaml on the control plane node and set the below parameter. secrets-encryption: true Secrets encryption can then be managed with the k3s secrets-encrypt command line tool. If needed, you can find the generated encryption config at /var/lib/rancher/k3s/server/cred/encryption-config.json. scored: false - id: 1.2.30 text: "Ensure that encryption providers are appropriately configured (Manual)" audit: | ENCRYPTION_PROVIDER_CONFIG=$(journalctl -m -u k3s | grep 'Running kube-apiserver' | tail -n1 | grep -- --encryption-provider-config | sed 's%.*encryption-provider-config[= ]\([^ ]*\).*%\1%') if test -e $ENCRYPTION_PROVIDER_CONFIG; then grep -o 'providers\"\:\[.*\]' $ENCRYPTION_PROVIDER_CONFIG | grep -o "[A-Za-z]*" | head -2 | tail -1 | sed 's/^/provider=/'; fi tests: test_items: - flag: "provider" compare: op: valid_elements value: "aescbc,kms,secretbox" remediation: | K3s can be configured to use encryption providers to encrypt secrets at rest. K3s will utilize the aescbc provider. Edit the K3s config file /etc/rancher/k3s/config.yaml on the control plane node and set the below parameter. secrets-encryption: true Secrets encryption can then be managed with the k3s secrets-encrypt command line tool. If needed, you can find the generated encryption config at /var/lib/rancher/k3s/server/cred/encryption-config.json scored: false - id: 1.2.31 text: "Ensure that the API Server only makes use of Strong Cryptographic Ciphers (Automated)" audit: "journalctl -m -u k3s | grep 'Running kube-apiserver' | tail -n1 | grep 'tls-cipher-suites'" tests: test_items: - flag: "--tls-cipher-suites" compare: op: valid_elements value: "TLS_AES_128_GCM_SHA256,TLS_AES_256_GCM_SHA384,TLS_CHACHA20_POLY1305_SHA256,TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA,TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,TLS_RSA_WITH_3DES_EDE_CBC_SHA,TLS_RSA_WITH_AES_128_CBC_SHA,TLS_RSA_WITH_AES_128_GCM_SHA256,TLS_RSA_WITH_AES_256_CBC_SHA,TLS_RSA_WITH_AES_256_GCM_SHA384" remediation: | By default, the K3s kube-apiserver complies with this test. Changes to these values may cause regression, therefore ensure that all apiserver clients support the new TLS configuration before applying it in production deployments. If a custom TLS configuration is required, consider also creating a custom version of this rule that aligns with your requirements. If this check fails, remove any custom configuration around `tls-cipher-suites` or update the /etc/rancher/k3s/config.yaml file to match the default by adding the following: kube-apiserver-arg: - "tls-cipher-suites=TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,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_CHACHA20_POLY1305" scored: true - id: 1.3 text: "Controller Manager" checks: - id: 1.3.1 text: "Ensure that the --terminated-pod-gc-threshold argument is set as appropriate (Manual)" audit: "journalctl -m -u k3s | grep 'Running kube-controller-manager' | tail -n1 | grep 'terminated-pod-gc-threshold'" tests: test_items: - flag: "--terminated-pod-gc-threshold" remediation: | Edit the K3s config file /etc/rancher/k3s/config.yaml on the control plane node and set the --terminated-pod-gc-threshold to an appropriate threshold, kube-controller-manager-arg: - "terminated-pod-gc-threshold=10" scored: false - id: 1.3.2 text: "Ensure that the --profiling argument is set to false (Automated)" audit: "journalctl -m -u k3s | grep 'Running kube-controller-manager' | tail -n1 | grep 'profiling'" tests: test_items: - flag: "--profiling" compare: op: eq value: false remediation: | By default, K3s sets the --profiling argument to false. If this check fails, edit the K3s config file /etc/rancher/k3s/config.yaml and remove any lines like below. kube-controller-manager-arg: - "profiling=true" scored: true - id: 1.3.3 text: "Ensure that the --use-service-account-credentials argument is set to true (Automated)" audit: "journalctl -m -u k3s | grep 'Running kube-controller-manager' | tail -n1 | grep 'use-service-account-credentials'" tests: test_items: - flag: "--use-service-account-credentials" compare: op: noteq value: false remediation: | By default, K3s sets the --use-service-account-credentials argument to true. If this check fails, edit the K3s config file /etc/rancher/k3s/config.yaml and remove any lines like below. kube-controller-manager-arg: - "use-service-account-credentials=false" scored: true - id: 1.3.4 text: "Ensure that the --service-account-private-key-file argument is set as appropriate (Automated)" audit: "journalctl -m -u k3s | grep 'Running kube-controller-manager' | tail -n1 | grep 'service-account-private-key-file'" tests: test_items: - flag: "--service-account-private-key-file" remediation: | By default, K3s automatically provides the service account private key file. It is generated and located at /var/lib/rancher/k3s/server/tls/service.current.key. If this check fails, edit the K3s config file /etc/rancher/k3s/config.yaml and remove any lines like below. kube-controller-manager-arg: - "service-account-private-key-file=" scored: true - id: 1.3.5 text: "Ensure that the --root-ca-file argument is set as appropriate (Automated)" audit: "journalctl -m -u k3s | grep 'Running kube-controller-manager' | tail -n1 | grep 'root-ca-file'" tests: test_items: - flag: "--root-ca-file" remediation: | By default, K3s automatically provides the root CA file. It is generated and located at /var/lib/rancher/k3s/server/tls/server-ca.crt. If this check fails, edit the K3s config file /etc/rancher/k3s/config.yaml and remove any lines like below. kube-controller-manager-arg: - "root-ca-file=" scored: true - id: 1.3.6 text: "Ensure that the RotateKubeletServerCertificate argument is set to true (Automated)" audit: "journalctl -m -u k3s | grep 'Running kube-controller-manager' | tail -n1" tests: bin_op: or test_items: - flag: "--feature-gates" compare: op: nothave value: "RotateKubeletServerCertificate=false" set: true - flag: "--feature-gates" set: false remediation: | By default, K3s does not set the RotateKubeletServerCertificate feature gate. If you have enabled this feature gate, you should remove it. If this check fails, edit the K3s config file /etc/rancher/k3s/config.yaml, remove any lines like below. kube-controller-manager-arg: - "feature-gate=RotateKubeletServerCertificate" scored: true - id: 1.3.7 text: "Ensure that the --bind-address argument is set to 127.0.0.1 (Automated)" audit: "/bin/ps -ef | grep $controllermanagerbin | grep -v grep" tests: bin_op: or test_items: - flag: "--bind-address" compare: op: eq value: "127.0.0.1" set: true - flag: "--bind-address" set: false remediation: | By default, K3s sets the --bind-address argument to 127.0.0.1 If this check fails, edit the K3s config file /etc/rancher/k3s/config.yaml and remove any lines like below. kube-controller-manager-arg: - "bind-address=" scored: true - id: 1.4 text: "Scheduler" checks: - id: 1.4.1 text: "Ensure that the --profiling argument is set to false (Automated)" audit: "journalctl -m -u k3s | grep 'Running kube-scheduler' | tail -n1" tests: test_items: - flag: "--profiling" compare: op: eq value: false set: true remediation: | By default, K3s sets the --profiling argument to false. If this check fails, edit the K3s config file /etc/rancher/k3s/config.yaml and remove any lines like below. kube-scheduler-arg: - "profiling=true" scored: true - id: 1.4.2 text: "Ensure that the --bind-address argument is set to 127.0.0.1 (Automated)" audit: "journalctl -m -u k3s | grep 'Running kube-scheduler' | tail -n1 | grep 'bind-address'" tests: bin_op: or test_items: - flag: "--bind-address" compare: op: eq value: "127.0.0.1" set: true - flag: "--bind-address" set: false remediation: | By default, K3s sets the --bind-address argument to 127.0.0.1 If this check fails, edit the K3s config file /etc/rancher/k3s/config.yaml and remove any lines like below. kube-scheduler-arg: - "bind-address=" scored: true ================================================ FILE: cfg/k3s-cis-1.7/node.yaml ================================================ --- controls: version: "k3s-cis-1.7" id: 4 text: "Worker Node Security Configuration" type: "node" groups: - id: 4.1 text: "Worker Node Configuration Files" checks: - id: 4.1.1 text: "Ensure that the kubelet service file permissions are set to 600 or more restrictive (Automated)" type: "skip" audit: '/bin/sh -c ''if test -e $kubeletsvc; then stat -c permissions=%a $kubeletsvc; fi'' ' tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Not Applicable. The kubelet is embedded in the k3s process. There is no kubelet service file, all configuration is passed in as arguments at runtime. scored: true - id: 4.1.2 text: "Ensure that the kubelet service file ownership is set to root:root (Automated)" type: "skip" audit: '/bin/sh -c ''if test -e $kubeletsvc; then stat -c %U:%G $kubeletsvc; fi'' ' tests: test_items: - flag: root:root remediation: | Not Applicable. The kubelet is embedded in the k3s process. There is no kubelet service file, all configuration is passed in as arguments at runtime. Not Applicable. All configuration is passed in as arguments at container run time. scored: true - id: 4.1.3 text: "If proxy kubeconfig file exists ensure permissions are set to 600 or more restrictive (Automated)" audit: '/bin/sh -c ''if test -e $proxykubeconfig; then stat -c permissions=%a $proxykubeconfig; fi'' ' tests: bin_op: or test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Run the below command (based on the file location on your system) on the each worker node. For example, chmod 600 $proxykubeconfig scored: true - id: 4.1.4 text: "If proxy kubeconfig file exists ensure ownership is set to root:root (Automated)" audit: '/bin/sh -c ''if test -e $proxykubeconfig; then stat -c %U:%G $proxykubeconfig; fi'' ' tests: bin_op: or test_items: - flag: root:root remediation: | Run the below command (based on the file location on your system) on the each worker node. For example, chown root:root $proxykubeconfig scored: true - id: 4.1.5 text: "Ensure that the --kubeconfig kubelet.conf file permissions are set to 600 or more restrictive (Automated)" audit: '/bin/sh -c ''if test -e $kubeletkubeconfig; then stat -c permissions=%a $kubeletkubeconfig; fi'' ' tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Run the below command (based on the file location on your system) on the each worker node. For example, chmod 600 $kubeletkubeconfig scored: true - id: 4.1.6 text: "Ensure that the --kubeconfig kubelet.conf file ownership is set to root:root (Automated)" audit: 'stat -c %U:%G $kubeletkubeconfig' tests: test_items: - flag: root:root remediation: | Run the below command (based on the file location on your system) on the each worker node. For example, chown root:root $kubeletkubeconfig scored: true - id: 4.1.7 text: "Ensure that the certificate authorities file permissions are set to 600 or more restrictive (Automated)" audit: "stat -c permissions=%a $kubeletcafile" tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Run the following command to modify the file permissions of the --client-ca-file chmod 600 $kubeletcafile scored: true - id: 4.1.8 text: "Ensure that the client certificate authorities file ownership is set to root:root (Automated)" audit: "stat -c %U:%G $kubeletcafile" tests: test_items: - flag: root:root compare: op: eq value: root:root remediation: | Run the following command to modify the ownership of the --client-ca-file. chown root:root $kubeletcafile scored: true - id: 4.1.9 text: "Ensure that the kubelet --config configuration file has permissions set to 600 or more restrictive (Automated)" type: "skip" audit: '/bin/sh -c ''if test -e $kubeletconf; then stat -c permissions=%a $kubeletconf; fi'' ' tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Not Applicable. The kubelet is embedded in the k3s process. There is no kubelet config file, all configuration is passed in as arguments at runtime. scored: true - id: 4.1.10 text: "Ensure that the kubelet --config configuration file ownership is set to root:root (Automated)" type: "skip" audit: '/bin/sh -c ''if test -e $kubeletconf; then stat -c %U:%G $kubeletconf; fi'' ' tests: test_items: - flag: root:root remediation: | Not Applicable. The kubelet is embedded in the k3s process. There is no kubelet config file, all configuration is passed in as arguments at runtime. scored: true - id: 4.2 text: "Kubelet" checks: - id: 4.2.1 text: "Ensure that the --anonymous-auth argument is set to false (Automated)" audit: '/bin/sh -c ''if test $(journalctl -m -u k3s | grep "Running kubelet" | wc -l) -gt 0; then journalctl -m -u k3s -u k3s-agent | grep "Running kubelet" | tail -n1 | grep "anonymous-auth" | grep -v grep; else echo "--anonymous-auth=false"; fi'' ' tests: test_items: - flag: "--anonymous-auth" path: '{.authentication.anonymous.enabled}' compare: op: eq value: false remediation: | By default, K3s sets the --anonymous-auth to false. If you have set this to a different value, you should set it back to false. If using the K3s config file /etc/rancher/k3s/config.yaml, remove any lines similar to below. kubelet-arg: - "anonymous-auth=true" If using the command line, edit the K3s service file and remove the below argument. --kubelet-arg="anonymous-auth=true" Based on your system, restart the k3s service. For example, systemctl daemon-reload systemctl restart k3s.service scored: true - id: 4.2.2 text: "Ensure that the --authorization-mode argument is not set to AlwaysAllow (Automated)" audit: '/bin/sh -c ''if test $(journalctl -m -u k3s -u k3s-agent | grep "Running kubelet" | wc -l) -gt 0; then journalctl -m -u k3s -u k3s-agent | grep "Running kubelet" | tail -n1 | grep "authorization-mode"; else echo "--authorization-mode=Webhook"; fi'' ' audit_config: "/bin/sh -c 'if test -e $kubeletconf; then /bin/cat $kubeletconf; fi' " tests: test_items: - flag: --authorization-mode path: '{.authorization.mode}' compare: op: nothave value: AlwaysAllow remediation: | By default, K3s does not set the --authorization-mode to AlwaysAllow. If using the K3s config file /etc/rancher/k3s/config.yaml, remove any lines similar to below. kubelet-arg: - "authorization-mode=AlwaysAllow" If using the command line, edit the K3s service file and remove the below argument. --kubelet-arg="authorization-mode=AlwaysAllow" Based on your system, restart the k3s service. For example, systemctl daemon-reload systemctl restart k3s.service scored: true - id: 4.2.3 text: "Ensure that the --client-ca-file argument is set as appropriate (Automated)" audit: '/bin/sh -c ''if test $(journalctl -m -u k3s -u k3s-agent | grep "Running kubelet" | wc -l) -gt 0; then journalctl -m -u k3s -u k3s-agent | grep "Running kubelet" | tail -n1 | grep "client-ca-file"; else echo "--client-ca-file=/var/lib/rancher/k3s/server/tls/request-header-ca.crt"; fi'' ' tests: test_items: - flag: --client-ca-file path: '{.authentication.x509.clientCAFile}' remediation: | By default, K3s automatically provides the client ca certificate for the Kubelet. It is generated and located at /var/lib/rancher/k3s/agent/client-ca.crt scored: true - id: 4.2.4 text: "Verify that the --read-only-port argument is set to 0 (Automated)" audit: "journalctl -m -u k3s -u k3s-agent | grep 'Running kubelet' | tail -n1" audit_config: "/bin/sh -c 'if test -e $kubeletconf; then /bin/cat $kubeletconf; fi' " tests: bin_op: or test_items: - flag: "--read-only-port" path: '{.readOnlyPort}' compare: op: eq value: 0 - flag: "--read-only-port" path: '{.readOnlyPort}' set: false remediation: | By default, K3s sets the --read-only-port to 0. If you have set this to a different value, you should set it back to 0. If using the K3s config file /etc/rancher/k3s/config.yaml, remove any lines similar to below. kubelet-arg: - "read-only-port=XXXX" If using the command line, edit the K3s service file and remove the below argument. --kubelet-arg="read-only-port=XXXX" Based on your system, restart the k3s service. For example, systemctl daemon-reload systemctl restart k3s.service scored: true - id: 4.2.5 text: "Ensure that the --streaming-connection-idle-timeout argument is not set to 0 (Manual)" audit: "journalctl -m -u k3s -u k3s-agent | grep 'Running kubelet' | tail -n1" tests: test_items: - flag: --streaming-connection-idle-timeout path: '{.streamingConnectionIdleTimeout}' compare: op: noteq value: 0 - flag: --streaming-connection-idle-timeout path: '{.streamingConnectionIdleTimeout}' set: false bin_op: or remediation: | If using the K3s config file /etc/rancher/k3s/config.yaml, set the following parameter to an appropriate value. kubelet-arg: - "streaming-connection-idle-timeout=5m" If using the command line, run K3s with --kubelet-arg="streaming-connection-idle-timeout=5m". Based on your system, restart the k3s service. For example, systemctl restart k3s.service scored: false - id: 4.2.6 text: "Ensure that the --make-iptables-util-chains argument is set to true (Automated)" audit: "journalctl -m -u k3s -u k3s-agent | grep 'Running kubelet' | tail -n1" tests: test_items: - flag: --make-iptables-util-chains path: '{.makeIPTablesUtilChains}' compare: op: eq value: true - flag: --make-iptables-util-chains path: '{.makeIPTablesUtilChains}' set: false bin_op: or remediation: | If using the K3s config file /etc/rancher/k3s/config.yaml, set the following parameter. kubelet-arg: - "make-iptables-util-chains=true" If using the command line, run K3s with --kubelet-arg="make-iptables-util-chains=true". Based on your system, restart the k3s service. For example, systemctl restart k3s.service scored: true - id: 4.2.7 text: "Ensure that the --hostname-override argument is not set (Automated)" audit: "journalctl -m -u k3s -u k3s-agent | grep 'Running kubelet' | tail -n1" type: "skip" tests: test_items: - flag: --hostname-override set: false remediation: | Not Applicable. By default, K3s does set the --hostname-override argument. Per CIS guidelines, this is to comply with cloud providers that require this flag to ensure that hostname matches node names. scored: true - id: 4.2.8 text: "Ensure that the eventRecordQPS argument is set to a level which ensures appropriate event capture (Manual)" audit: "journalctl -m -u k3s -u k3s-agent | grep 'Running kubelet' | tail -n1" audit_config: "/bin/sh -c 'if test -e $kubeletconf; then /bin/cat $kubeletconf; fi' " tests: test_items: - flag: --event-qps path: '{.eventRecordQPS}' compare: op: gte value: 0 - flag: --event-qps path: '{.eventRecordQPS}' set: false bin_op: or remediation: | By default, K3s sets the event-qps to 0. Should you wish to change this, If using the K3s config file /etc/rancher/k3s/config.yaml, set the following parameter to an appropriate value. kubelet-arg: - "event-qps=" If using the command line, run K3s with --kubelet-arg="event-qps=". Based on your system, restart the k3s service. For example, systemctl restart k3s.service scored: false - id: 4.2.9 text: "Ensure that the --tls-cert-file and --tls-private-key-file arguments are set as appropriate (Automated)" audit: "journalctl -m -u k3s -u k3s-agent | grep 'Running kubelet' | tail -n1" tests: test_items: - flag: --tls-cert-file path: '/var/lib/rancher/k3s/agent/serving-kubelet.crt' - flag: --tls-private-key-file path: '/var/lib/rancher/k3s/agent/serving-kubelet.key' remediation: | By default, K3s automatically provides the TLS certificate and private key for the Kubelet. They are generated and located at /var/lib/rancher/k3s/agent/serving-kubelet.crt and /var/lib/rancher/k3s/agent/serving-kubelet.key If for some reason you need to provide your own certificate and key, you can set the the below parameters in the K3s config file /etc/rancher/k3s/config.yaml. kubelet-arg: - "tls-cert-file=" - "tls-private-key-file=" scored: true - id: 4.2.10 text: "Ensure that the --rotate-certificates argument is not set to false (Automated)" audit: "journalctl -m -u k3s -u k3s-agent | grep 'Running kubelet' | tail -n1" audit_config: "/bin/sh -c 'if test -e $kubeletconf; then /bin/cat $kubeletconf; fi' " tests: test_items: - flag: --rotate-certificates path: '{.rotateCertificates}' compare: op: eq value: true - flag: --rotate-certificates path: '{.rotateCertificates}' set: false bin_op: or remediation: | By default, K3s does not set the --rotate-certificates argument. If you have set this flag with a value of `false`, you should either set it to `true` or completely remove the flag. If using the K3s config file /etc/rancher/k3s/config.yaml, remove any rotate-certificates parameter. If using the command line, remove the K3s flag --kubelet-arg="rotate-certificates". Based on your system, restart the k3s service. For example, systemctl restart k3s.service scored: true - id: 4.2.11 text: "Verify that the RotateKubeletServerCertificate argument is set to true (Automated)" audit: "journalctl -m -u k3s -u k3s-agent | grep 'Running kubelet' | tail -n1" audit_config: "/bin/cat $kubeletconf" tests: bin_op: or test_items: - flag: RotateKubeletServerCertificate path: '{.featureGates.RotateKubeletServerCertificate}' compare: op: nothave value: false - flag: RotateKubeletServerCertificate path: '{.featureGates.RotateKubeletServerCertificate}' set: false remediation: | By default, K3s does not set the RotateKubeletServerCertificate feature gate. If you have enabled this feature gate, you should remove it. If using the K3s config file /etc/rancher/k3s/config.yaml, remove any feature-gate=RotateKubeletServerCertificate parameter. If using the command line, remove the K3s flag --kubelet-arg="feature-gate=RotateKubeletServerCertificate". Based on your system, restart the k3s service. For example, systemctl restart k3s.service scored: true - id: 4.2.12 text: "Ensure that the Kubelet only makes use of Strong Cryptographic Ciphers (Manual)" audit: "journalctl -m -u k3s -u k3s-agent | grep 'Running kubelet' | tail -n1" audit_config: "/bin/sh -c 'if test -e $kubeletconf; then /bin/cat $kubeletconf; fi' " tests: test_items: - flag: --tls-cipher-suites path: '{range .tlsCipherSuites[:]}{}{'',''}{end}' compare: op: valid_elements value: 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 remediation: | If using a K3s config file /etc/rancher/k3s/config.yaml, edit the file to set `tlsCipherSuites` to kubelet-arg: - "tls-cipher-suites=TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,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_CHACHA20_POLY1305" or to a subset of these values. If using the command line, add the K3s flag --kubelet-arg="tls-cipher-suites=" Based on your system, restart the k3s service. For example, systemctl restart k3s.service scored: false - id: 4.2.13 text: "Ensure that a limit is set on pod PIDs (Manual)" audit: "journalctl -m -u k3s -u k3s-agent | grep 'Running kubelet' | tail -n1" audit_config: "/bin/sh -c 'if test -e $kubeletconf; then /bin/cat $kubeletconf; fi' " tests: test_items: - flag: --pod-max-pids path: '{.podPidsLimit}' remediation: | Decide on an appropriate level for this parameter and set it, If using a K3s config file /etc/rancher/k3s/config.yaml, edit the file to set `podPidsLimit` to kubelet-arg: - "pod-max-pids=" scored: false ================================================ FILE: cfg/k3s-cis-1.7/policies.yaml ================================================ --- controls: version: "k3s-cis-1.7" id: 5 text: "Kubernetes Policies" type: "policies" groups: - id: 5.1 text: "RBAC and Service Accounts" checks: - id: 5.1.1 text: "Ensure that the cluster-admin role is only used where required (Manual)" type: "manual" remediation: | Identify all clusterrolebindings to the cluster-admin role. Check if they are used and if they need this role or if they could use a role with fewer privileges. Where possible, first bind users to a lower privileged role and then remove the clusterrolebinding to the cluster-admin role : kubectl delete clusterrolebinding [name] scored: false - id: 5.1.2 text: "Minimize access to secrets (Manual)" type: "manual" remediation: | Where possible, remove get, list and watch access to Secret objects in the cluster. scored: false - id: 5.1.3 text: "Minimize wildcard use in Roles and ClusterRoles (Manual)" type: "manual" remediation: | Where possible replace any use of wildcards in clusterroles and roles with specific objects or actions. scored: false - id: 5.1.4 text: "Minimize access to create pods (Manual)" type: "manual" remediation: | Where possible, remove create access to pod objects in the cluster. scored: false - id: 5.1.5 text: "Ensure that default service accounts are not actively used. (Manual)" type: "manual" remediation: | Create explicit service accounts wherever a Kubernetes workload requires specific access to the Kubernetes API server. Modify the configuration of each default service account to include this value automountServiceAccountToken: false scored: false - id: 5.1.6 text: "Ensure that Service Account Tokens are only mounted where necessary (Manual)" type: "manual" remediation: | Modify the definition of pods and service accounts which do not need to mount service account tokens to disable it. scored: false - id: 5.1.7 text: "Avoid use of system:masters group (Manual)" type: "manual" remediation: | Remove the system:masters group from all users in the cluster. scored: false - id: 5.1.8 text: "Limit use of the Bind, Impersonate and Escalate permissions in the Kubernetes cluster (Manual)" type: "manual" remediation: | Where possible, remove the impersonate, bind and escalate rights from subjects. scored: false - id: 5.1.9 text: "Minimize access to create persistent volumes (Manual)" type: "manual" remediation: | Where possible, remove create access to PersistentVolume objects in the cluster. scored: false - id: 5.1.10 text: "Minimize access to the proxy sub-resource of nodes (Manual)" type: "manual" remediation: | Where possible, remove access to the proxy sub-resource of node objects. scored: false - id: 5.1.11 text: "Minimize access to the approval sub-resource of certificatesigningrequests objects (Manual)" type: "manual" remediation: | Where possible, remove access to the approval sub-resource of certificatesigningrequest objects. scored: false - id: 5.1.12 text: "Minimize access to webhook configuration objects (Manual)" type: "manual" remediation: | Where possible, remove access to the validatingwebhookconfigurations or mutatingwebhookconfigurations objects scored: false - id: 5.1.13 text: "Minimize access to the service account token creation (Manual)" type: "manual" remediation: | Where possible, remove access to the token sub-resource of serviceaccount objects. scored: false - id: 5.2 text: "Pod Security Standards" checks: - id: 5.2.1 text: "Ensure that the cluster has at least one active policy control mechanism in place (Manual)" type: "manual" remediation: | Ensure that either Pod Security Admission or an external policy control system is in place for every namespace which contains user workloads. scored: false - id: 5.2.2 text: "Minimize the admission of privileged containers (Manual)" type: "manual" remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of privileged containers. scored: false - id: 5.2.3 text: "Minimize the admission of containers wishing to share the host process ID namespace (Automated)" remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of `hostPID` containers. scored: false - id: 5.2.4 text: "Minimize the admission of containers wishing to share the host IPC namespace (Automated)" remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of `hostIPC` containers. scored: false - id: 5.2.5 text: "Minimize the admission of containers wishing to share the host network namespace (Automated)" remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of `hostNetwork` containers. scored: false - id: 5.2.6 text: "Minimize the admission of containers with allowPrivilegeEscalation (Automated)" type: "manual" remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of containers with `.spec.allowPrivilegeEscalation` set to `true`. scored: true - id: 5.2.7 text: "Minimize the admission of root containers (Automated)" type: "manual" remediation: | Create a policy for each namespace in the cluster, ensuring that either `MustRunAsNonRoot` or `MustRunAs` with the range of UIDs not including 0, is set. scored: false - id: 5.2.8 text: "Minimize the admission of containers with the NET_RAW capability (Automated)" type: "manual" remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of containers with the `NET_RAW` capability. scored: false - id: 5.2.9 text: "Minimize the admission of containers with added capabilities (Automated)" type: "manual" remediation: | Ensure that `allowedCapabilities` is not present in policies for the cluster unless it is set to an empty array. scored: false - id: 5.2.10 text: "Minimize the admission of containers with capabilities assigned (Manual)" type: "manual" remediation: | Review the use of capabilities in applications running on your cluster. Where a namespace contains applications which do not require any Linux capabities to operate consider adding a PSP which forbids the admission of containers which do not drop all capabilities. scored: false - id: 5.2.11 text: "Minimize the admission of Windows HostProcess containers (Manual)" type: "manual" remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of containers that have `.securityContext.windowsOptions.hostProcess` set to `true`. scored: false - id: 5.2.12 text: "Minimize the admission of HostPath volumes (Manual)" type: "manual" remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of containers with `hostPath` volumes. scored: false - id: 5.2.13 text: "Minimize the admission of containers which use HostPorts (Manual)" type: "manual" remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of containers which use `hostPort` sections. scored: false - id: 5.3 text: "Network Policies and CNI" checks: - id: 5.3.1 text: "Ensure that the CNI in use supports NetworkPolicies (Manual)" type: "manual" remediation: | If the CNI plugin in use does not support network policies, consideration should be given to making use of a different plugin, or finding an alternate mechanism for restricting traffic in the Kubernetes cluster. scored: false - id: 5.3.2 text: "Ensure that all Namespaces have NetworkPolicies defined (Manual)" remediation: | Follow the documentation and create NetworkPolicy objects as you need them. scored: false - id: 5.4 text: "Secrets Management" checks: - id: 5.4.1 text: "Prefer using Secrets as files over Secrets as environment variables (Manual)" type: "manual" remediation: | If possible, rewrite application code to read Secrets from mounted secret files, rather than from environment variables. scored: false - id: 5.4.2 text: "Consider external secret storage (Manual)" type: "manual" remediation: | Refer to the Secrets management options offered by your cloud provider or a third-party secrets management solution. scored: false - id: 5.5 text: "Extensible Admission Control" checks: - id: 5.5.1 text: "Configure Image Provenance using ImagePolicyWebhook admission controller (Manual)" type: "manual" remediation: | Follow the Kubernetes documentation and setup image provenance. scored: false - id: 5.7 text: "General Policies" checks: - id: 5.7.1 text: "Create administrative boundaries between resources using namespaces (Manual)" type: "manual" remediation: | Follow the documentation and create namespaces for objects in your deployment as you need them. scored: false - id: 5.7.2 text: "Ensure that the seccomp profile is set to docker/default in your Pod definitions (Manual)" type: "manual" remediation: | Use `securityContext` to enable the docker/default seccomp profile in your pod definitions. An example is as below: securityContext: seccompProfile: type: RuntimeDefault scored: false - id: 5.7.3 text: "Apply SecurityContext to your Pods and Containers (Manual)" type: "manual" remediation: | Follow the Kubernetes documentation and apply SecurityContexts to your Pods. For a suggested list of SecurityContexts, you may refer to the CIS Security Benchmark for Docker Containers. scored: false - id: 5.7.4 text: "The default namespace should not be used (Manual)" remediation: | Ensure that namespaces are created to allow for appropriate segregation of Kubernetes resources and that all new resources are created in a specific namespace. scored: false ================================================ FILE: cfg/k3s-cis-1.8/config.yaml ================================================ --- ## Version-specific settings that override the values in cfg/config.yaml master: components: - apiserver - kubelet - scheduler - controllermanager - etcd - policies apiserver: bins: - containerd kubelet: bins: - containerd defaultkubeconfig: /var/lib/rancher/k3s/agent/kubelet.kubeconfig defaultcafile: /var/lib/rancher/k3s/agent/client-ca.crt scheduler: bins: - containerd kubeconfig: - /var/lib/rancher/k3s/server/cred/scheduler.kubeconfig controllermanager: bins: - containerd kubeconfig: - /var/lib/rancher/k3s/server/cred/controller.kubeconfig etcd: bins: - containerd etcd: confs: /var/lib/rancher/k3s/server/db/etcd/config node: components: - kubelet - proxy kubelet: bins: - containerd confs: - /var/lib/rancher/k3s/agent/kubelet.kubeconfig defaultkubeconfig: /var/lib/rancher/k3s/agent/kubelet.kubeconfig defaultcafile: /var/lib/rancher/k3s/agent/client-ca.crt proxy: bins: - containerd defaultkubeconfig: /var/lib/rancher/k3s/agent/kubeproxy.kubeconfig policies: components: - policies ================================================ FILE: cfg/k3s-cis-1.8/controlplane.yaml ================================================ --- controls: version: "k3s-cis-1.8" id: 3 text: "Control Plane Configuration" type: "controlplane" groups: - id: 3.1 text: "Authentication and Authorization" checks: - id: 3.1.1 text: "Client certificate authentication should not be used for users (Manual)" type: "manual" remediation: | Alternative mechanisms provided by Kubernetes such as the use of OIDC should be implemented in place of client certificates. scored: false - id: 3.1.2 text: "Service account token authentication should not be used for users (Manual)" type: "manual" remediation: | Alternative mechanisms provided by Kubernetes such as the use of OIDC should be implemented in place of service account tokens. scored: false - id: 3.1.3 text: "Bootstrap token authentication should not be used for users (Manual)" type: "manual" remediation: | Alternative mechanisms provided by Kubernetes such as the use of OIDC should be implemented in place of bootstrap tokens. scored: false - id: 3.2 text: "Logging" checks: - id: 3.2.1 text: "Ensure that a minimal audit policy is created (Manual)" audit: "journalctl -m -u k3s | grep 'Running kube-apiserver' | tail -n1 | grep 'audit-policy-file'" tests: test_items: - flag: "--audit-policy-file" set: true remediation: | Create an audit policy file for your cluster. scored: false - id: 3.2.2 text: "Ensure that the audit policy covers key security concerns (Manual)" type: "manual" remediation: | Review the audit policy provided for the cluster and ensure that it covers at least the following areas, - Access to Secrets managed by the cluster. Care should be taken to only log Metadata for requests to Secrets, ConfigMaps, and TokenReviews, in order to avoid risk of logging sensitive data. - Modification of Pod and Deployment objects. - Use of `pods/exec`, `pods/portforward`, `pods/proxy` and `services/proxy`. For most requests, minimally logging at the Metadata level is recommended (the most basic level of logging). scored: false ================================================ FILE: cfg/k3s-cis-1.8/etcd.yaml ================================================ --- controls: version: "k3s-cis-1.8" id: 2 text: "Etcd Node Configuration" type: "etcd" groups: - id: 2 text: "Etcd Node Configuration" checks: - id: 2.1 text: "Ensure that the --cert-file and --key-file arguments are set as appropriate (Automated)" audit_config: "cat $etcdconf" tests: bin_op: and test_items: - path: "{.client-transport-security.cert-file}" compare: op: eq value: "/var/lib/rancher/k3s/server/tls/etcd/server-client.crt" - path: "{.client-transport-security.key-file}" compare: op: eq value: "/var/lib/rancher/k3s/server/tls/etcd/server-client.key" remediation: | If running on with sqlite or a external DB, etcd checks are Not Applicable. When running with embedded-etcd, K3s generates cert and key files for etcd. These are located in /var/lib/rancher/k3s/server/tls/etcd/. If this check fails, ensure that the configuration file $etcdconf has not been modified to use custom cert and key files. scored: false - id: 2.2 text: "Ensure that the --client-cert-auth argument is set to true (Automated)" audit_config: "cat $etcdconf" tests: test_items: - path: "{.client-transport-security.client-cert-auth}" compare: op: eq value: true remediation: | If running on with sqlite or a external DB, etcd checks are Not Applicable. When running with embedded-etcd, K3s sets the --client-cert-auth parameter to true. If this check fails, ensure that the configuration file $etcdconf has not been modified to disable client certificate authentication. scored: false - id: 2.3 text: "Ensure that the --auto-tls argument is not set to true (Automated)" audit_config: "cat $etcdconf" tests: bin_op: or test_items: - path: "{.client-transport-security.auto-tls}" compare: op: eq value: false - path: "{.client-transport-security.auto-tls}" set: false remediation: | If running on with sqlite or a external DB, etcd checks are Not Applicable. When running with embedded-etcd, K3s does not set the --auto-tls parameter. If this check fails, edit the etcd pod specification file $etcdconf on the master node and either remove the --auto-tls parameter or set it to false. client-transport-security: auto-tls: false scored: false - id: 2.4 text: "Ensure that the --peer-cert-file and --peer-key-file arguments are set as appropriate (Automated)" audit_config: "cat $etcdconf" tests: bin_op: and test_items: - path: "{.peer-transport-security.cert-file}" compare: op: eq value: "/var/lib/rancher/k3s/server/tls/etcd/peer-server-client.crt" - path: "{.peer-transport-security.key-file}" compare: op: eq value: "/var/lib/rancher/k3s/server/tls/etcd/peer-server-client.key" remediation: | If running on with sqlite or a external DB, etcd checks are Not Applicable. When running with embedded-etcd, K3s generates peer cert and key files for etcd. These are located in /var/lib/rancher/k3s/server/tls/etcd/. If this check fails, ensure that the configuration file $etcdconf has not been modified to use custom peer cert and key files. scored: false - id: 2.5 text: "Ensure that the --peer-client-cert-auth argument is set to true (Automated)" audit_config: "cat $etcdconf" tests: test_items: - path: "{.peer-transport-security.client-cert-auth}" compare: op: eq value: true remediation: | If running on with sqlite or a external DB, etcd checks are Not Applicable. When running with embedded-etcd, K3s sets the --peer-cert-auth parameter to true. If this check fails, ensure that the configuration file $etcdconf has not been modified to disable peer client certificate authentication. scored: false - id: 2.6 text: "Ensure that the --peer-auto-tls argument is not set to true (Automated)" audit_config: "cat $etcdconf" tests: bin_op: or test_items: - path: "{.peer-transport-security.auto-tls}" compare: op: eq value: false - path: "{.peer-transport-security.auto-tls}" set: false remediation: | If running on with sqlite or a external DB, etcd checks are Not Applicable. When running with embedded-etcd, K3s does not set the --peer-auto-tls parameter. If this check fails, edit the etcd pod specification file $etcdconf on the master node and either remove the --peer-auto-tls parameter or set it to false. peer-transport-security: auto-tls: false scored: false - id: 2.7 text: "Ensure that a unique Certificate Authority is used for etcd (Automated)" audit_config: "cat $etcdconf" tests: test_items: - path: "{.peer-transport-security.trusted-ca-file}" compare: op: eq value: "/var/lib/rancher/k3s/server/tls/etcd/peer-ca.crt" remediation: | If running on with sqlite or a external DB, etcd checks are Not Applicable. When running with embedded-etcd, K3s generates a unique certificate authority for etcd. This is located at /var/lib/rancher/k3s/server/tls/etcd/peer-ca.crt. If this check fails, ensure that the configuration file $etcdconf has not been modified to use a shared certificate authority. scored: false ================================================ FILE: cfg/k3s-cis-1.8/master.yaml ================================================ --- controls: version: "k3s-cis-1.8" id: 1 text: "Control Plane Security Configuration" type: "master" groups: - id: 1.1 text: "Control Plane Node Configuration Files" checks: - id: 1.1.1 text: "Ensure that the API server pod specification file permissions are set to 600 or more restrictive (Automated)" type: "skip" audit: "/bin/sh -c 'if test -e $apiserverconf; then stat -c permissions=%a $apiserverconf; fi'" tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Not Applicable. By default, K3s embeds the api server within the k3s process. There is no API server pod specification file. scored: true - id: 1.1.2 text: "Ensure that the API server pod specification file ownership is set to root:root (Automated)" type: "skip" audit: "/bin/sh -c 'if test -e $apiserverconf; then stat -c %U:%G $apiserverconf; fi'" tests: test_items: - flag: "root:root" remediation: | Not Applicable. By default, K3s embeds the api server within the k3s process. There is no API server pod specification file. scored: true - id: 1.1.3 text: "Ensure that the controller manager pod specification file permissions are set to 600 or more restrictive (Automated)" type: "skip" audit: "/bin/sh -c 'if test -e $controllermanagerconf; then stat -c permissions=%a $controllermanagerconf; fi'" tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Not Applicable. By default, K3s embeds the controller manager within the k3s process. There is no controller manager pod specification file. scored: true - id: 1.1.4 text: "Ensure that the controller manager pod specification file ownership is set to root:root (Automated)" type: "skip" audit: "/bin/sh -c 'if test -e $controllermanagerconf; then stat -c %U:%G $controllermanagerconf; fi'" tests: test_items: - flag: "root:root" remediation: | Not Applicable. By default, K3s embeds the controller manager within the k3s process. There is no controller manager pod specification file. scored: true - id: 1.1.5 text: "Ensure that the scheduler pod specification file permissions are set to 600 or more restrictive (Automated)" type: "skip" audit: "/bin/sh -c 'if test -e $schedulerconf; then stat -c permissions=%a $schedulerconf; fi'" tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Not Applicable. By default, K3s embeds the scheduler within the k3s process. There is no scheduler pod specification file. scored: true - id: 1.1.6 text: "Ensure that the scheduler pod specification file ownership is set to root:root (Automated)" type: "skip" audit: "/bin/sh -c 'if test -e $schedulerconf; then stat -c %U:%G $schedulerconf; fi'" tests: test_items: - flag: "root:root" remediation: | Not Applicable. By default, K3s embeds the scheduler within the k3s process. There is no scheduler pod specification file. scored: true - id: 1.1.7 text: "Ensure that the etcd pod specification file permissions are set to 600 or more restrictive (Automated)" type: "skip" audit: "/bin/sh -c 'if test -e $etcdconf; then find $etcdconf -name '*etcd*' | xargs stat -c permissions=%a; fi'" use_multiple_values: true tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Not Applicable. By default, K3s embeds etcd within the k3s process. There is no etcd pod specification file. scored: true - id: 1.1.8 text: "Ensure that the etcd pod specification file ownership is set to root:root (Automated)" type: "skip" audit: "/bin/sh -c 'if test -e $etcdconf; then find $etcdconf -name '*etcd*' | xargs stat -c %U:%G; fi'" use_multiple_values: true tests: test_items: - flag: "root:root" remediation: | Not Applicable. By default, K3s embeds etcd within the k3s process. There is no etcd pod specification file. scored: true - id: 1.1.9 text: "Ensure that the Container Network Interface file permissions are set to 600 or more restrictive (Automated)" audit: find /var/lib/cni/networks -type f ! -name lock 2> /dev/null | xargs --no-run-if-empty stat -c permissions=%a use_multiple_values: true tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | By default, K3s sets the CNI file permissions to 600. Note that for many CNIs, a lock file is created with permissions 750. This is expected and can be ignored. If you modify your CNI configuration, ensure that the permissions are set to 600. For example, chmod 600 /var/lib/cni/networks/ scored: true - id: 1.1.10 text: "Ensure that the Container Network Interface file ownership is set to root:root (Automated)" audit: find /var/lib/cni/networks -type f 2> /dev/null | xargs --no-run-if-empty stat -c %U:%G use_multiple_values: true tests: test_items: - flag: "root:root" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chown root:root /var/lib/cni/networks/ scored: true - id: 1.1.11 text: "Ensure that the etcd data directory permissions are set to 700 or more restrictive (Automated)" audit: | if [ "$(journalctl -m -u k3s | grep -m1 'Managed etcd cluster' | wc -l)" -gt 0 ]; then stat -c permissions=%a /var/lib/rancher/k3s/server/db/etcd else echo "permissions=700" fi tests: test_items: - flag: "permissions" compare: op: bitmask value: "700" remediation: | On the etcd server node, get the etcd data directory, passed as an argument --data-dir, from the command 'ps -ef | grep etcd'. Run the below command (based on the etcd data directory found above). For example, chmod 700 /var/lib/etcd scored: true - id: 1.1.12 text: "Ensure that the etcd data directory ownership is set to etcd:etcd (Automated)" audit: ps -ef | grep $etcdbin | grep -- --data-dir | sed 's%.*data-dir[= ]\([^ ]*\).*%\1%' | xargs stat -c %U:%G type: "skip" tests: test_items: - flag: "etcd:etcd" remediation: | Not Applicable. For K3s, etcd is embedded within the k3s process. There is no separate etcd process. Therefore the etcd data directory ownership is managed by the k3s process and should be root:root. scored: true - id: 1.1.13 text: "Ensure that the admin.conf file permissions are set to 600 or more restrictive (Automated)" audit: "/bin/sh -c 'if test -e /var/lib/rancher/k3s/server/cred/admin.kubeconfig; then stat -c permissions=%a /var/lib/rancher/k3s/server/cred/admin.kubeconfig; fi'" tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chmod 600 /var/lib/rancher/k3s/server/cred/admin.kubeconfig scored: true - id: 1.1.14 text: "Ensure that the admin.conf file ownership is set to root:root (Automated)" audit: "/bin/sh -c 'if test -e /var/lib/rancher/k3s/server/cred/admin.kubeconfig; then stat -c %U:%G /var/lib/rancher/k3s/server/cred/admin.kubeconfig; fi'" tests: test_items: - flag: "root:root" compare: op: eq value: "root:root" set: true remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chown root:root /var/lib/rancher/k3s/server/cred/admin.kubeconfig scored: true - id: 1.1.15 text: "Ensure that the scheduler.conf file permissions are set to 600 or more restrictive (Automated)" audit: "/bin/sh -c 'if test -e $schedulerkubeconfig; then stat -c permissions=%a $schedulerkubeconfig; fi'" tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chmod 600 $schedulerkubeconfig scored: true - id: 1.1.16 text: "Ensure that the scheduler.conf file ownership is set to root:root (Automated)" audit: "/bin/sh -c 'if test -e $schedulerkubeconfig; then stat -c %U:%G $schedulerkubeconfig; fi'" tests: test_items: - flag: "root:root" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chown root:root $schedulerkubeconfig scored: true - id: 1.1.17 text: "Ensure that the controller-manager.conf file permissions are set to 600 or more restrictive (Automated)" audit: "/bin/sh -c 'if test -e $controllermanagerkubeconfig; then stat -c permissions=%a $controllermanagerkubeconfig; fi'" tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chmod 600 $controllermanagerkubeconfig scored: true - id: 1.1.18 text: "Ensure that the controller-manager.conf file ownership is set to root:root (Automated)" audit: "stat -c %U:%G $controllermanagerkubeconfig" tests: test_items: - flag: "root:root" compare: op: eq value: "root:root" set: true remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chown root:root $controllermanagerkubeconfig scored: true - id: 1.1.19 text: "Ensure that the Kubernetes PKI directory and file ownership is set to root:root (Automated)" audit: "stat -c %U:%G /var/lib/rancher/k3s/server/tls" use_multiple_values: true tests: test_items: - flag: "root:root" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chown -R root:root /var/lib/rancher/k3s/server/tls scored: true - id: 1.1.20 text: "Ensure that the Kubernetes PKI certificate file permissions are set to 600 or more restrictive (Manual)" audit: "/bin/sh -c 'stat -c permissions=%a /var/lib/rancher/k3s/server/tls/*.crt'" use_multiple_values: true tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Run the below command (based on the file location on your system) on the master node. For example, chmod -R 600 /var/lib/rancher/k3s/server/tls/*.crt scored: false - id: 1.1.21 text: "Ensure that the Kubernetes PKI key file permissions are set to 600 (Automated)" audit: "/bin/sh -c 'stat -c permissions=%a /var/lib/rancher/k3s/server/tls/*.key'" use_multiple_values: true tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Run the below command (based on the file location on your system) on the master node. For example, chmod -R 600 /var/lib/rancher/k3s/server/tls/*.key scored: true - id: 1.2 text: "API Server" checks: - id: 1.2.1 text: "Ensure that the --anonymous-auth argument is set to false (Automated)" audit: "journalctl -m -u k3s | grep 'Running kube-apiserver' | tail -n1 | grep 'anonymous-auth'" tests: test_items: - flag: "--anonymous-auth" compare: op: eq value: false remediation: | By default, K3s sets the --anonymous-auth argument to false. If this check fails, edit the K3s config file /etc/rancher/k3s/config.yaml and remove anything similar to below. kube-apiserver-arg: - "anonymous-auth=true" scored: true - id: 1.2.2 text: "Ensure that the --token-auth-file parameter is not set (Automated)" audit: "journalctl -m -u k3s | grep 'Running kube-apiserver' | tail -n1" tests: test_items: - flag: "--token-auth-file" set: false remediation: | Follow the documentation and configure alternate mechanisms for authentication. If this check fails, edit the K3s config file /etc/rancher/k3s/config.yaml and remove anything similar to below. kube-apiserver-arg: - "token-auth-file=" scored: true - id: 1.2.3 text: "Ensure that the --DenyServiceExternalIPs is not set (Automated)" audit: "journalctl -m -u k3s | grep 'Running kube-apiserver' | tail -n1" tests: bin_op: or test_items: - flag: "--enable-admission-plugins" compare: op: nothave value: "DenyServiceExternalIPs" set: true - flag: "--enable-admission-plugins" set: false remediation: | By default, K3s does not set DenyServiceExternalIPs. If this check fails, edit the K3s config file /etc/rancher/k3s/config.yaml, remove any lines like below. kube-apiserver-arg: - "enable-admission-plugins=DenyServiceExternalIPs" scored: true - id: 1.2.4 text: "Ensure that the --kubelet-client-certificate and --kubelet-client-key arguments are set as appropriate (Automated)" audit: "journalctl -m -u k3s | grep 'Running kube-apiserver' | tail -n1" tests: bin_op: and test_items: - flag: "--kubelet-client-certificate" - flag: "--kubelet-client-key" remediation: | By default, K3s automatically provides the kubelet client certificate and key. They are generated and located at /var/lib/rancher/k3s/server/tls/client-kube-apiserver.crt and /var/lib/rancher/k3s/server/tls/client-kube-apiserver.key If for some reason you need to provide your own certificate and key, you can set the below parameters in the K3s config file /etc/rancher/k3s/config.yaml. kube-apiserver-arg: - "kubelet-client-certificate=" - "kubelet-client-key=" scored: true - id: 1.2.5 text: "Ensure that the --kubelet-certificate-authority argument is set as appropriate (Automated)" audit: "journalctl -m -u k3s | grep 'Running kube-apiserver' | tail -n1 | grep 'kubelet-certificate-authority'" tests: test_items: - flag: "--kubelet-certificate-authority" remediation: | By default, K3s automatically provides the kubelet CA cert file, at /var/lib/rancher/k3s/server/tls/server-ca.crt. If for some reason you need to provide your own ca certificate, look at using the k3s certificate command line tool. If this check fails, edit the K3s config file /etc/rancher/k3s/config.yaml and remove any lines like below. kube-apiserver-arg: - "kubelet-certificate-authority=" scored: true - id: 1.2.6 text: "Ensure that the --authorization-mode argument is not set to AlwaysAllow (Automated)" audit: "journalctl -m -u k3s | grep 'Running kube-apiserver' | tail -n1 | grep 'authorization-mode'" tests: test_items: - flag: "--authorization-mode" compare: op: nothave value: "AlwaysAllow" remediation: | By default, K3s does not set the --authorization-mode to AlwaysAllow. If this check fails, edit K3s config file /etc/rancher/k3s/config.yaml, remove any lines like below. kube-apiserver-arg: - "authorization-mode=AlwaysAllow" scored: true - id: 1.2.7 text: "Ensure that the --authorization-mode argument includes Node (Automated)" audit: "journalctl -m -u k3s | grep 'Running kube-apiserver' | tail -n1 | grep 'authorization-mode'" tests: test_items: - flag: "--authorization-mode" compare: op: has value: "Node" remediation: | By default, K3s sets the --authorization-mode to Node and RBAC. If this check fails, edit the K3s config file /etc/rancher/k3s/config.yaml, ensure that you are not overriding authorization-mode. scored: true - id: 1.2.8 text: "Ensure that the --authorization-mode argument includes RBAC (Automated)" audit: "journalctl -m -u k3s | grep 'Running kube-apiserver' | tail -n1 | grep 'authorization-mode'" tests: test_items: - flag: "--authorization-mode" compare: op: has value: "RBAC" remediation: | By default, K3s sets the --authorization-mode to Node and RBAC. If this check fails, edit the K3s config file /etc/rancher/k3s/config.yaml, ensure that you are not overriding authorization-mode. scored: true - id: 1.2.9 text: "Ensure that the admission control plugin EventRateLimit is set (Manual)" audit: "journalctl -m -u k3s | grep 'Running kube-apiserver' | tail -n1 | grep 'enable-admission-plugins'" tests: test_items: - flag: "--enable-admission-plugins" compare: op: has value: "EventRateLimit" remediation: | Follow the Kubernetes documentation and set the desired limits in a configuration file. Then, edit the K3s config file /etc/rancher/k3s/config.yaml and set the below parameters. kube-apiserver-arg: - "enable-admission-plugins=...,EventRateLimit,..." - "admission-control-config-file=" scored: false - id: 1.2.10 text: "Ensure that the admission control plugin AlwaysAdmit is not set (Automated)" audit: "journalctl -m -u k3s | grep 'Running kube-apiserver' | tail -n1 | grep 'enable-admission-plugins'" tests: bin_op: or test_items: - flag: "--enable-admission-plugins" compare: op: nothave value: AlwaysAdmit - flag: "--enable-admission-plugins" set: false remediation: | By default, K3s does not set the --enable-admission-plugins to AlwaysAdmit. If this check fails, edit K3s config file /etc/rancher/k3s/config.yaml, remove any lines like below. kube-apiserver-arg: - "enable-admission-plugins=AlwaysAdmit" scored: true - id: 1.2.11 text: "Ensure that the admission control plugin AlwaysPullImages is set (Manual)" audit: "journalctl -m -u k3s | grep 'Running kube-apiserver' | tail -n1" tests: test_items: - flag: "--enable-admission-plugins" compare: op: has value: "AlwaysPullImages" remediation: | Permissive, per CIS guidelines, "This setting could impact offline or isolated clusters, which have images pre-loaded and do not have access to a registry to pull in-use images. This setting is not appropriate for clusters which use this configuration." Edit the K3s config file /etc/rancher/k3s/config.yaml and set the below parameter. kube-apiserver-arg: - "enable-admission-plugins=...,AlwaysPullImages,..." scored: false - id: 1.2.12 text: "Ensure that the admission control plugin SecurityContextDeny is set if PodSecurityPolicy is not used (Manual)" type: "skip" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: bin_op: or test_items: - flag: "--enable-admission-plugins" compare: op: has value: "SecurityContextDeny" - flag: "--enable-admission-plugins" compare: op: has value: "PodSecurityPolicy" remediation: | Not Applicable. Enabling Pod Security Policy is no longer supported on K3s v1.25+ and will cause applications to unexpectedly fail. scored: false - id: 1.2.13 text: "Ensure that the admission control plugin ServiceAccount is set (Automated)" audit: "journalctl -m -u k3s | grep 'Running kube-apiserver' | tail -n1" tests: bin_op: or test_items: - flag: "--disable-admission-plugins" compare: op: nothave value: "ServiceAccount" - flag: "--disable-admission-plugins" set: false remediation: | By default, K3s does not set the --disable-admission-plugins to anything. Follow the documentation and create ServiceAccount objects as per your environment. If this check fails, edit the K3s config file /etc/rancher/k3s/config.yaml and remove any lines like below. kube-apiserver-arg: - "disable-admission-plugins=ServiceAccount" scored: true - id: 1.2.14 text: "Ensure that the admission control plugin NamespaceLifecycle is set (Automated)" audit: "journalctl -m -u k3s | grep 'Running kube-apiserver' | tail -n1" tests: bin_op: or test_items: - flag: "--disable-admission-plugins" compare: op: nothave value: "NamespaceLifecycle" - flag: "--disable-admission-plugins" set: false remediation: | By default, K3s does not set the --disable-admission-plugins to anything. If this check fails, edit the K3s config file /etc/rancher/k3s/config.yaml and remove any lines like below. kube-apiserver-arg: - "disable-admission-plugins=...,NamespaceLifecycle,..." scored: true - id: 1.2.15 text: "Ensure that the admission control plugin NodeRestriction is set (Automated)" audit: "journalctl -m -u k3s | grep 'Running kube-apiserver' | tail -n1 | grep 'enable-admission-plugins'" tests: test_items: - flag: "--enable-admission-plugins" compare: op: has value: "NodeRestriction" remediation: | By default, K3s sets the --enable-admission-plugins to NodeRestriction. If using the K3s config file /etc/rancher/k3s/config.yaml, check that you are not overriding the admission plugins. If you are, include NodeRestriction in the list. kube-apiserver-arg: - "enable-admission-plugins=...,NodeRestriction,..." scored: true - id: 1.2.16 text: "Ensure that the --profiling argument is set to false (Automated)" audit: "journalctl -m -u k3s | grep 'Running kube-apiserver' | tail -n1 | grep 'profiling'" tests: test_items: - flag: "--profiling" compare: op: eq value: false remediation: | By default, K3s sets the --profiling argument to false. If this check fails, edit the K3s config file /etc/rancher/k3s/config.yaml and remove any lines like below. kube-apiserver-arg: - "profiling=true" scored: true - id: 1.2.17 text: "Ensure that the --audit-log-path argument is set (Manual)" audit: "journalctl -m -u k3s | grep 'Running kube-apiserver' | tail -n1" tests: test_items: - flag: "--audit-log-path" remediation: | Edit the K3s config file /etc/rancher/k3s/config.yaml and set the audit-log-path parameter to a suitable path and file where you would like audit logs to be written, for example, kube-apiserver-arg: - "audit-log-path=/var/lib/rancher/k3s/server/logs/audit.log" scored: false - id: 1.2.18 text: "Ensure that the --audit-log-maxage argument is set to 30 or as appropriate (Manual)" audit: "journalctl -m -u k3s | grep 'Running kube-apiserver' | tail -n1" tests: test_items: - flag: "--audit-log-maxage" compare: op: gte value: 30 remediation: | Edit the K3s config file /etc/rancher/k3s/config.yaml on the control plane node and set the audit-log-maxage parameter to 30 or as an appropriate number of days, for example, kube-apiserver-arg: - "audit-log-maxage=30" scored: false - id: 1.2.19 text: "Ensure that the --audit-log-maxbackup argument is set to 10 or as appropriate (Manual)" audit: "journalctl -m -u k3s | grep 'Running kube-apiserver' | tail -n1" tests: test_items: - flag: "--audit-log-maxbackup" compare: op: gte value: 10 remediation: | Edit the K3s config file /etc/rancher/k3s/config.yaml on the control plane node and set the audit-log-maxbackup parameter to 10 or to an appropriate value. For example, kube-apiserver-arg: - "audit-log-maxbackup=10" scored: false - id: 1.2.20 text: "Ensure that the --audit-log-maxsize argument is set to 100 or as appropriate (Manual)" audit: "journalctl -m -u k3s | grep 'Running kube-apiserver' | tail -n1" tests: test_items: - flag: "--audit-log-maxsize" compare: op: gte value: 100 remediation: | Edit the K3s config file /etc/rancher/k3s/config.yaml on the control plane node and set the audit-log-maxsize parameter to an appropriate size in MB. For example, kube-apiserver-arg: - "audit-log-maxsize=100" scored: false - id: 1.2.21 text: "Ensure that the --request-timeout argument is set as appropriate (Manual)" audit: "journalctl -m -u k3s | grep 'Running kube-apiserver' | tail -n1" tests: test_items: - flag: "--request-timeout" remediation: | Permissive, per CIS guidelines, "it is recommended to set this limit as appropriate and change the default limit of 60 seconds only if needed". Edit the K3s config file /etc/rancher/k3s/config.yaml and set the below parameter if needed. For example, kube-apiserver-arg: - "request-timeout=300s" scored: false - id: 1.2.22 text: "Ensure that the --service-account-lookup argument is set to true (Automated)" audit: "journalctl -m -u k3s | grep 'Running kube-apiserver' | tail -n1" tests: bin_op: or test_items: - flag: "--service-account-lookup" set: false - flag: "--service-account-lookup" compare: op: eq value: true remediation: | By default, K3s does not set the --service-account-lookup argument. Edit the K3s config file /etc/rancher/k3s/config.yaml and set the service-account-lookup. For example, kube-apiserver-arg: - "service-account-lookup=true" Alternatively, you can delete the service-account-lookup parameter from this file so that the default takes effect. scored: true - id: 1.2.23 text: "Ensure that the --service-account-key-file argument is set as appropriate (Automated)" audit: "journalctl -m -u k3s | grep 'Running kube-apiserver' | tail -n1" tests: test_items: - flag: "--service-account-key-file" remediation: | K3s automatically generates and sets the service account key file. It is located at /var/lib/rancher/k3s/server/tls/service.key. If this check fails, edit K3s config file /etc/rancher/k3s/config.yaml and remove any lines like below. kube-apiserver-arg: - "service-account-key-file=" scored: true - id: 1.2.24 text: "Ensure that the --etcd-certfile and --etcd-keyfile arguments are set as appropriate (Automated)" audit: | if [ "$(journalctl -m -u k3s | grep -m1 'Managed etcd cluster' | wc -l)" -gt 0 ]; then journalctl -m -u k3s | grep -m1 'Running kube-apiserver' | tail -n1 else echo "--etcd-certfile AND --etcd-keyfile" fi tests: bin_op: and test_items: - flag: "--etcd-certfile" set: true - flag: "--etcd-keyfile" set: true remediation: | K3s automatically generates and sets the etcd certificate and key files. They are located at /var/lib/rancher/k3s/server/tls/etcd/client.crt and /var/lib/rancher/k3s/server/tls/etcd/client.key. If this check fails, edit the K3s config file /etc/rancher/k3s/config.yaml and remove any lines like below. kube-apiserver-arg: - "etcd-certfile=" - "etcd-keyfile=" scored: true - id: 1.2.25 text: "Ensure that the --tls-cert-file and --tls-private-key-file arguments are set as appropriate (Automated)" audit: "journalctl -m -u k3s | grep -A1 'Running kube-apiserver' | tail -n2" tests: bin_op: and test_items: - flag: "--tls-cert-file" set: true - flag: "--tls-private-key-file" set: true remediation: | By default, K3s automatically generates and provides the TLS certificate and private key for the apiserver. They are generated and located at /var/lib/rancher/k3s/server/tls/serving-kube-apiserver.crt and /var/lib/rancher/k3s/server/tls/serving-kube-apiserver.key If this check fails, edit the K3s config file /etc/rancher/k3s/config.yaml and remove any lines like below. kube-apiserver-arg: - "tls-cert-file=" - "tls-private-key-file=" scored: true - id: 1.2.26 text: "Ensure that the --client-ca-file argument is set as appropriate (Automated)" audit: "journalctl -m -u k3s | grep 'Running kube-apiserver' | tail -n1 | grep 'client-ca-file'" tests: test_items: - flag: "--client-ca-file" remediation: | By default, K3s automatically provides the client certificate authority file. It is generated and located at /var/lib/rancher/k3s/server/tls/client-ca.crt. If for some reason you need to provide your own ca certificate, look at using the k3s certificate command line tool. If this check fails, edit the K3s config file /etc/rancher/k3s/config.yaml and remove any lines like below. kube-apiserver-arg: - "client-ca-file=" scored: true - id: 1.2.27 text: "Ensure that the --etcd-cafile argument is set as appropriate (Automated)" audit: "journalctl -m -u k3s | grep 'Running kube-apiserver' | tail -n1 | grep 'etcd-cafile'" tests: test_items: - flag: "--etcd-cafile" remediation: | By default, K3s automatically provides the etcd certificate authority file. It is generated and located at /var/lib/rancher/k3s/server/tls/client-ca.crt. If for some reason you need to provide your own ca certificate, look at using the k3s certificate command line tool. If this check fails, edit the K3s config file /etc/rancher/k3s/config.yaml and remove any lines like below. kube-apiserver-arg: - "etcd-cafile=" scored: true - id: 1.2.28 text: "Ensure that the --encryption-provider-config argument is set as appropriate (Manual)" audit: "journalctl -m -u k3s | grep 'Running kube-apiserver' | tail -n1 | grep 'encryption-provider-config'" tests: test_items: - flag: "--encryption-provider-config" remediation: | K3s can be configured to use encryption providers to encrypt secrets at rest. Edit the K3s config file /etc/rancher/k3s/config.yaml on the control plane node and set the below parameter. secrets-encryption: true Secrets encryption can then be managed with the k3s secrets-encrypt command line tool. If needed, you can find the generated encryption config at /var/lib/rancher/k3s/server/cred/encryption-config.json. scored: false - id: 1.2.29 text: "Ensure that encryption providers are appropriately configured (Manual)" audit: | ENCRYPTION_PROVIDER_CONFIG=$(journalctl -m -u k3s | grep 'Running kube-apiserver' | tail -n1 | grep -- --encryption-provider-config | sed 's%.*encryption-provider-config[= ]\([^ ]*\).*%\1%') if test -e $ENCRYPTION_PROVIDER_CONFIG; then grep -o 'providers\"\:\[.*\]' $ENCRYPTION_PROVIDER_CONFIG | grep -o "[A-Za-z]*" | head -2 | tail -1 | sed 's/^/provider=/'; fi tests: test_items: - flag: "provider" compare: op: valid_elements value: "aescbc,kms,secretbox" remediation: | K3s can be configured to use encryption providers to encrypt secrets at rest. K3s will utilize the aescbc provider. Edit the K3s config file /etc/rancher/k3s/config.yaml on the control plane node and set the below parameter. secrets-encryption: true Secrets encryption can then be managed with the k3s secrets-encrypt command line tool. If needed, you can find the generated encryption config at /var/lib/rancher/k3s/server/cred/encryption-config.json scored: false - id: 1.2.30 text: "Ensure that the API Server only makes use of Strong Cryptographic Ciphers (Automated)" audit: "journalctl -m -u k3s | grep 'Running kube-apiserver' | tail -n1 | grep 'tls-cipher-suites'" tests: test_items: - flag: "--tls-cipher-suites" compare: op: valid_elements value: "TLS_AES_128_GCM_SHA256,TLS_AES_256_GCM_SHA384,TLS_CHACHA20_POLY1305_SHA256,TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA,TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,TLS_RSA_WITH_3DES_EDE_CBC_SHA,TLS_RSA_WITH_AES_128_CBC_SHA,TLS_RSA_WITH_AES_128_GCM_SHA256,TLS_RSA_WITH_AES_256_CBC_SHA,TLS_RSA_WITH_AES_256_GCM_SHA384" remediation: | By default, the K3s kube-apiserver complies with this test. Changes to these values may cause regression, therefore ensure that all apiserver clients support the new TLS configuration before applying it in production deployments. If a custom TLS configuration is required, consider also creating a custom version of this rule that aligns with your requirements. If this check fails, remove any custom configuration around `tls-cipher-suites` or update the /etc/rancher/k3s/config.yaml file to match the default by adding the following: kube-apiserver-arg: - "tls-cipher-suites=TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,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_CHACHA20_POLY1305" scored: true - id: 1.3 text: "Controller Manager" checks: - id: 1.3.1 text: "Ensure that the --terminated-pod-gc-threshold argument is set as appropriate (Manual)" audit: "journalctl -m -u k3s | grep 'Running kube-controller-manager' | tail -n1 | grep 'terminated-pod-gc-threshold'" tests: test_items: - flag: "--terminated-pod-gc-threshold" remediation: | Edit the K3s config file /etc/rancher/k3s/config.yaml on the control plane node and set the --terminated-pod-gc-threshold to an appropriate threshold, kube-controller-manager-arg: - "terminated-pod-gc-threshold=10" scored: false - id: 1.3.2 text: "Ensure that the --profiling argument is set to false (Automated)" audit: "journalctl -m -u k3s | grep 'Running kube-controller-manager' | tail -n1 | grep 'profiling'" tests: test_items: - flag: "--profiling" compare: op: eq value: false remediation: | By default, K3s sets the --profiling argument to false. If this check fails, edit the K3s config file /etc/rancher/k3s/config.yaml and remove any lines like below. kube-controller-manager-arg: - "profiling=true" scored: true - id: 1.3.3 text: "Ensure that the --use-service-account-credentials argument is set to true (Automated)" audit: "journalctl -m -u k3s | grep 'Running kube-controller-manager' | tail -n1 | grep 'use-service-account-credentials'" tests: test_items: - flag: "--use-service-account-credentials" compare: op: noteq value: false remediation: | By default, K3s sets the --use-service-account-credentials argument to true. If this check fails, edit the K3s config file /etc/rancher/k3s/config.yaml and remove any lines like below. kube-controller-manager-arg: - "use-service-account-credentials=false" scored: true - id: 1.3.4 text: "Ensure that the --service-account-private-key-file argument is set as appropriate (Automated)" audit: "journalctl -m -u k3s | grep 'Running kube-controller-manager' | tail -n1 | grep 'service-account-private-key-file'" tests: test_items: - flag: "--service-account-private-key-file" remediation: | By default, K3s automatically provides the service account private key file. It is generated and located at /var/lib/rancher/k3s/server/tls/service.current.key. If this check fails, edit the K3s config file /etc/rancher/k3s/config.yaml and remove any lines like below. kube-controller-manager-arg: - "service-account-private-key-file=" scored: true - id: 1.3.5 text: "Ensure that the --root-ca-file argument is set as appropriate (Automated)" audit: "journalctl -m -u k3s | grep 'Running kube-controller-manager' | tail -n1 | grep 'root-ca-file'" tests: test_items: - flag: "--root-ca-file" remediation: | By default, K3s automatically provides the root CA file. It is generated and located at /var/lib/rancher/k3s/server/tls/server-ca.crt. If for some reason you need to provide your own ca certificate, look at using the k3s certificate command line tool. If this check fails, edit the K3s config file /etc/rancher/k3s/config.yaml and remove any lines like below. kube-controller-manager-arg: - "root-ca-file=" scored: true - id: 1.3.6 text: "Ensure that the RotateKubeletServerCertificate argument is set to true (Automated)" audit: "journalctl -m -u k3s | grep 'Running kube-controller-manager' | tail -n1" tests: bin_op: or test_items: - flag: "--feature-gates" compare: op: nothave value: "RotateKubeletServerCertificate=false" set: true - flag: "--feature-gates" set: false remediation: | By default, K3s does not set the RotateKubeletServerCertificate feature gate. If you have enabled this feature gate, you should remove it. If this check fails, edit the K3s config file /etc/rancher/k3s/config.yaml, remove any lines like below. kube-controller-manager-arg: - "feature-gate=RotateKubeletServerCertificate" scored: true - id: 1.3.7 text: "Ensure that the --bind-address argument is set to 127.0.0.1 (Automated)" audit: "journalctl -m -u k3s | grep 'Running kube-controller-manager' | tail -n1" tests: bin_op: or test_items: - flag: "--bind-address" compare: op: eq value: "127.0.0.1" set: true - flag: "--bind-address" set: false remediation: | By default, K3s sets the --bind-address argument to 127.0.0.1 If this check fails, edit the K3s config file /etc/rancher/k3s/config.yaml and remove any lines like below. kube-controller-manager-arg: - "bind-address=" scored: true - id: 1.4 text: "Scheduler" checks: - id: 1.4.1 text: "Ensure that the --profiling argument is set to false (Automated)" audit: "journalctl -m -u k3s | grep 'Running kube-scheduler' | tail -n1 | grep 'profiling'" tests: test_items: - flag: "--profiling" compare: op: eq value: false set: true remediation: | By default, K3s sets the --profiling argument to false. If this check fails, edit the K3s config file /etc/rancher/k3s/config.yaml and remove any lines like below. kube-scheduler-arg: - "profiling=true" scored: true - id: 1.4.2 text: "Ensure that the --bind-address argument is set to 127.0.0.1 (Automated)" audit: "journalctl -m -u k3s | grep 'Running kube-scheduler' | tail -n1 | grep 'bind-address'" tests: bin_op: or test_items: - flag: "--bind-address" compare: op: eq value: "127.0.0.1" set: true - flag: "--bind-address" set: false remediation: | By default, K3s sets the --bind-address argument to 127.0.0.1 If this check fails, edit the K3s config file /etc/rancher/k3s/config.yaml and remove any lines like below. kube-scheduler-arg: - "bind-address=" scored: true ================================================ FILE: cfg/k3s-cis-1.8/node.yaml ================================================ --- controls: version: "k3s-cis-1.8" id: 4 text: "Worker Node Security Configuration" type: "node" groups: - id: 4.1 text: "Worker Node Configuration Files" checks: - id: 4.1.1 text: "Ensure that the kubelet service file permissions are set to 600 or more restrictive (Automated)" type: "skip" audit: '/bin/sh -c ''if test -e $kubeletsvc; then stat -c permissions=%a $kubeletsvc; fi'' ' tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Not Applicable. The kubelet is embedded in the k3s process. There is no kubelet service file, all configuration is passed in as arguments at runtime. scored: true - id: 4.1.2 text: "Ensure that the kubelet service file ownership is set to root:root (Automated)" type: "skip" audit: '/bin/sh -c ''if test -e $kubeletsvc; then stat -c %U:%G $kubeletsvc; fi'' ' tests: test_items: - flag: root:root remediation: | Not Applicable. The kubelet is embedded in the k3s process. There is no kubelet service file, all configuration is passed in as arguments at runtime. Not Applicable. All configuration is passed in as arguments at container run time. scored: true - id: 4.1.3 text: "If proxy kubeconfig file exists ensure permissions are set to 600 or more restrictive (Automated)" audit: '/bin/sh -c ''if test -e $proxykubeconfig; then stat -c permissions=%a $proxykubeconfig; fi'' ' tests: bin_op: or test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Run the below command (based on the file location on your system) on the each worker node. For example, chmod 600 $proxykubeconfig scored: true - id: 4.1.4 text: "If proxy kubeconfig file exists ensure ownership is set to root:root (Automated)" audit: '/bin/sh -c ''if test -e $proxykubeconfig; then stat -c %U:%G $proxykubeconfig; fi'' ' tests: bin_op: or test_items: - flag: root:root remediation: | Run the below command (based on the file location on your system) on the each worker node. For example, chown root:root $proxykubeconfig scored: true - id: 4.1.5 text: "Ensure that the --kubeconfig kubelet.conf file permissions are set to 600 or more restrictive (Automated)" audit: '/bin/sh -c ''if test -e $kubeletkubeconfig; then stat -c permissions=%a $kubeletkubeconfig; fi'' ' tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Run the below command (based on the file location on your system) on the each worker node. For example, chmod 600 $kubeletkubeconfig scored: true - id: 4.1.6 text: "Ensure that the --kubeconfig kubelet.conf file ownership is set to root:root (Automated)" audit: 'stat -c %U:%G $kubeletkubeconfig' tests: test_items: - flag: root:root remediation: | Run the below command (based on the file location on your system) on the each worker node. For example, chown root:root $kubeletkubeconfig scored: true - id: 4.1.7 text: "Ensure that the certificate authorities file permissions are set to 600 or more restrictive (Automated)" audit: "stat -c permissions=%a $kubeletcafile" tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Run the following command to modify the file permissions of the --client-ca-file chmod 600 $kubeletcafile scored: true - id: 4.1.8 text: "Ensure that the client certificate authorities file ownership is set to root:root (Automated)" audit: "stat -c %U:%G $kubeletcafile" tests: test_items: - flag: root:root compare: op: eq value: root:root remediation: | Run the following command to modify the ownership of the --client-ca-file. chown root:root $kubeletcafile scored: true - id: 4.1.9 text: "Ensure that the kubelet --config configuration file has permissions set to 600 or more restrictive (Automated)" audit: '/bin/sh -c ''if test -e $kubeletconf; then stat -c permissions=%a $kubeletconf; fi'' ' type: "skip" tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Not Applicable. The kubelet is embedded in the k3s process. There is no kubelet config file, all configuration is passed in as arguments at runtime. scored: true - id: 4.1.10 text: "Ensure that the kubelet --config configuration file ownership is set to root:root (Automated)" audit: '/bin/sh -c ''if test -e $kubeletconf; then stat -c %U:%G $kubeletconf; fi'' ' type: "skip" tests: test_items: - flag: root:root remediation: | Not Applicable. The kubelet is embedded in the k3s process. There is no kubelet config file, all configuration is passed in as arguments at runtime. scored: true - id: 4.2 text: "Kubelet" checks: - id: 4.2.1 text: "Ensure that the --anonymous-auth argument is set to false (Automated)" audit: '/bin/sh -c ''if test $(journalctl -m -u k3s -u k3s-agent | grep "Running kubelet" | wc -l) -gt 0; then journalctl -m -u k3s -u k3s-agent | grep "Running kubelet" | tail -n1 | grep "anonymous-auth" | grep -v grep; else echo "--anonymous-auth=false"; fi'' ' tests: test_items: - flag: "--anonymous-auth" path: '{.authentication.anonymous.enabled}' compare: op: eq value: false remediation: | By default, K3s sets the --anonymous-auth to false. If you have set this to a different value, you should set it back to false. If using the K3s config file /etc/rancher/k3s/config.yaml, remove any lines similar to below. kubelet-arg: - "anonymous-auth=true" If using the command line, edit the K3s service file and remove the below argument. --kubelet-arg="anonymous-auth=true" Based on your system, restart the k3s service. For example, systemctl daemon-reload systemctl restart k3s.service scored: true - id: 4.2.2 text: "Ensure that the --authorization-mode argument is not set to AlwaysAllow (Automated)" audit: '/bin/sh -c ''if test $(journalctl -m -u k3s -u k3s-agent | grep "Running kubelet" | wc -l) -gt 0; then journalctl -m -u k3s -u k3s-agent | grep "Running kubelet" | tail -n1 | grep "authorization-mode"; else echo "--authorization-mode=Webhook"; fi'' ' audit_config: "/bin/sh -c 'if test -e $kubeletconf; then /bin/cat $kubeletconf; fi' " tests: test_items: - flag: --authorization-mode path: '{.authorization.mode}' compare: op: nothave value: AlwaysAllow remediation: | By default, K3s does not set the --authorization-mode to AlwaysAllow. If using the K3s config file /etc/rancher/k3s/config.yaml, remove any lines similar to below. kubelet-arg: - "authorization-mode=AlwaysAllow" If using the command line, edit the K3s service file and remove the below argument. --kubelet-arg="authorization-mode=AlwaysAllow" Based on your system, restart the k3s service. For example, systemctl daemon-reload systemctl restart k3s.service scored: true - id: 4.2.3 text: "Ensure that the --client-ca-file argument is set as appropriate (Automated)" audit: '/bin/sh -c ''if test $(journalctl -m -u k3s -u k3s-agent | grep "Running kubelet" | wc -l) -gt 0; then journalctl -m -u k3s -u k3s-agent | grep "Running kubelet" | tail -n1 | grep "client-ca-file"; else echo "--client-ca-file=/var/lib/rancher/k3s/server/tls/request-header-ca.crt"; fi'' ' tests: test_items: - flag: --client-ca-file path: '{.authentication.x509.clientCAFile}' remediation: | By default, K3s automatically provides the client ca certificate for the Kubelet. It is generated and located at /var/lib/rancher/k3s/agent/client-ca.crt scored: true - id: 4.2.4 text: "Verify that the --read-only-port argument is set to 0 (Automated)" audit: "journalctl -m -u k3s -u k3s-agent | grep 'Running kubelet' | tail -n1" audit_config: "/bin/sh -c 'if test -e $kubeletconf; then /bin/cat $kubeletconf; fi' " tests: bin_op: or test_items: - flag: "--read-only-port" path: '{.readOnlyPort}' compare: op: eq value: 0 - flag: "--read-only-port" path: '{.readOnlyPort}' set: false remediation: | By default, K3s sets the --read-only-port to 0. If you have set this to a different value, you should set it back to 0. If using the K3s config file /etc/rancher/k3s/config.yaml, remove any lines similar to below. kubelet-arg: - "read-only-port=XXXX" If using the command line, edit the K3s service file and remove the below argument. --kubelet-arg="read-only-port=XXXX" Based on your system, restart the k3s service. For example, systemctl daemon-reload systemctl restart k3s.service scored: true - id: 4.2.5 text: "Ensure that the --streaming-connection-idle-timeout argument is not set to 0 (Manual)" audit: "journalctl -m -u k3s -u k3s-agent | grep 'Running kubelet' | tail -n1" tests: test_items: - flag: --streaming-connection-idle-timeout path: '{.streamingConnectionIdleTimeout}' compare: op: noteq value: 0 - flag: --streaming-connection-idle-timeout path: '{.streamingConnectionIdleTimeout}' set: false bin_op: or remediation: | If using the K3s config file /etc/rancher/k3s/config.yaml, set the following parameter to an appropriate value. kubelet-arg: - "streaming-connection-idle-timeout=5m" If using the command line, run K3s with --kubelet-arg="streaming-connection-idle-timeout=5m". Based on your system, restart the k3s service. For example, systemctl restart k3s.service scored: false - id: 4.2.6 text: "Ensure that the --make-iptables-util-chains argument is set to true (Automated)" audit: "journalctl -m -u k3s -u k3s-agent | grep 'Running kubelet' | tail -n1" tests: test_items: - flag: --make-iptables-util-chains path: '{.makeIPTablesUtilChains}' compare: op: eq value: true - flag: --make-iptables-util-chains path: '{.makeIPTablesUtilChains}' set: false bin_op: or remediation: | If using the K3s config file /etc/rancher/k3s/config.yaml, set the following parameter. kubelet-arg: - "make-iptables-util-chains=true" If using the command line, run K3s with --kubelet-arg="make-iptables-util-chains=true". Based on your system, restart the k3s service. For example, systemctl restart k3s.service scored: true - id: 4.2.7 text: "Ensure that the --hostname-override argument is not set (Automated)" audit: "journalctl -m -u k3s -u k3s-agent | grep 'Running kubelet' | tail -n1" type: "skip" tests: test_items: - flag: --hostname-override set: false remediation: | Not Applicable. By default, K3s does set the --hostname-override argument. Per CIS guidelines, this is to comply with cloud providers that require this flag to ensure that hostname matches node names. scored: true - id: 4.2.8 text: "Ensure that the eventRecordQPS argument is set to a level which ensures appropriate event capture (Manual)" audit: "journalctl -m -u k3s -u k3s-agent | grep 'Running kubelet' | tail -n1" audit_config: "/bin/sh -c 'if test -e $kubeletconf; then /bin/cat $kubeletconf; fi' " tests: test_items: - flag: --event-qps path: '{.eventRecordQPS}' compare: op: gte value: 0 - flag: --event-qps path: '{.eventRecordQPS}' set: false bin_op: or remediation: | By default, K3s sets the event-qps to 0. Should you wish to change this, If using the K3s config file /etc/rancher/k3s/config.yaml, set the following parameter to an appropriate value. kubelet-arg: - "event-qps=" If using the command line, run K3s with --kubelet-arg="event-qps=". Based on your system, restart the k3s service. For example, systemctl restart k3s.service scored: false - id: 4.2.9 text: "Ensure that the --tls-cert-file and --tls-private-key-file arguments are set as appropriate (Automated)" audit: "journalctl -m -u k3s -u k3s-agent | grep 'Running kubelet' | tail -n1" tests: test_items: - flag: --tls-cert-file path: '/var/lib/rancher/k3s/agent/serving-kubelet.crt' - flag: --tls-private-key-file path: '/var/lib/rancher/k3s/agent/serving-kubelet.key' remediation: | By default, K3s automatically provides the TLS certificate and private key for the Kubelet. They are generated and located at /var/lib/rancher/k3s/agent/serving-kubelet.crt and /var/lib/rancher/k3s/agent/serving-kubelet.key If for some reason you need to provide your own certificate and key, you can set the below parameters in the K3s config file /etc/rancher/k3s/config.yaml. kubelet-arg: - "tls-cert-file=" - "tls-private-key-file=" scored: true - id: 4.2.10 text: "Ensure that the --rotate-certificates argument is not set to false (Automated)" audit: "journalctl -m -u k3s -u k3s-agent | grep 'Running kubelet' | tail -n1" audit_config: "/bin/sh -c 'if test -e $kubeletconf; then /bin/cat $kubeletconf; fi' " tests: test_items: - flag: --rotate-certificates path: '{.rotateCertificates}' compare: op: eq value: true - flag: --rotate-certificates path: '{.rotateCertificates}' set: false bin_op: or remediation: | By default, K3s does not set the --rotate-certificates argument. If you have set this flag with a value of `false`, you should either set it to `true` or completely remove the flag. If using the K3s config file /etc/rancher/k3s/config.yaml, remove any rotate-certificates parameter. If using the command line, remove the K3s flag --kubelet-arg="rotate-certificates". Based on your system, restart the k3s service. For example, systemctl restart k3s.service scored: true - id: 4.2.11 text: "Verify that the RotateKubeletServerCertificate argument is set to true (Automated)" audit: "journalctl -m -u k3s -u k3s-agent | grep 'Running kubelet' | tail -n1" audit_config: "/bin/cat $kubeletconf" tests: bin_op: or test_items: - flag: RotateKubeletServerCertificate path: '{.featureGates.RotateKubeletServerCertificate}' compare: op: nothave value: false - flag: RotateKubeletServerCertificate path: '{.featureGates.RotateKubeletServerCertificate}' set: false remediation: | By default, K3s does not set the RotateKubeletServerCertificate feature gate. If you have enabled this feature gate, you should remove it. If using the K3s config file /etc/rancher/k3s/config.yaml, remove any feature-gate=RotateKubeletServerCertificate parameter. If using the command line, remove the K3s flag --kubelet-arg="feature-gate=RotateKubeletServerCertificate". Based on your system, restart the k3s service. For example, systemctl restart k3s.service scored: true - id: 4.2.12 text: "Ensure that the Kubelet only makes use of Strong Cryptographic Ciphers (Manual)" audit: "journalctl -m -u k3s -u k3s-agent | grep 'Running kubelet' | tail -n1" audit_config: "/bin/sh -c 'if test -e $kubeletconf; then /bin/cat $kubeletconf; fi' " tests: test_items: - flag: --tls-cipher-suites path: '{range .tlsCipherSuites[:]}{}{'',''}{end}' compare: op: valid_elements value: 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 remediation: | If using a K3s config file /etc/rancher/k3s/config.yaml, edit the file to set `tlsCipherSuites` to kubelet-arg: - "tls-cipher-suites=TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,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_CHACHA20_POLY1305" or to a subset of these values. If using the command line, add the K3s flag --kubelet-arg="tls-cipher-suites=" Based on your system, restart the k3s service. For example, systemctl restart k3s.service scored: false - id: 4.2.13 text: "Ensure that a limit is set on pod PIDs (Manual)" audit: "journalctl -m -u k3s -u k3s-agent | grep 'Running kubelet' | tail -n1" audit_config: "/bin/sh -c 'if test -e $kubeletconf; then /bin/cat $kubeletconf; fi' " tests: test_items: - flag: --pod-max-pids path: '{.podPidsLimit}' remediation: | Decide on an appropriate level for this parameter and set it, If using a K3s config file /etc/rancher/k3s/config.yaml, edit the file to set `podPidsLimit` to kubelet-arg: - "pod-max-pids=" scored: false ================================================ FILE: cfg/k3s-cis-1.8/policies.yaml ================================================ --- controls: version: "k3s-cis-1.8" id: 5 text: "Kubernetes Policies" type: "policies" groups: - id: 5.1 text: "RBAC and Service Accounts" checks: - id: 5.1.1 text: "Ensure that the cluster-admin role is only used where required (Manual)" type: "manual" remediation: | Identify all clusterrolebindings to the cluster-admin role. Check if they are used and if they need this role or if they could use a role with fewer privileges. Where possible, first bind users to a lower privileged role and then remove the clusterrolebinding to the cluster-admin role : kubectl delete clusterrolebinding [name] scored: false - id: 5.1.2 text: "Minimize access to secrets (Manual)" type: "manual" remediation: | Where possible, remove get, list and watch access to Secret objects in the cluster. scored: false - id: 5.1.3 text: "Minimize wildcard use in Roles and ClusterRoles (Manual)" type: "manual" remediation: | Where possible replace any use of wildcards in clusterroles and roles with specific objects or actions. scored: false - id: 5.1.4 text: "Minimize access to create pods (Manual)" type: "manual" remediation: | Where possible, remove create access to pod objects in the cluster. scored: false - id: 5.1.5 text: "Ensure that default service accounts are not actively used. (Manual)" type: "manual" remediation: | Create explicit service accounts wherever a Kubernetes workload requires specific access to the Kubernetes API server. Modify the configuration of each default service account to include this value automountServiceAccountToken: false scored: false - id: 5.1.6 text: "Ensure that Service Account Tokens are only mounted where necessary (Manual)" type: "manual" remediation: | Modify the definition of pods and service accounts which do not need to mount service account tokens to disable it. scored: false - id: 5.1.7 text: "Avoid use of system:masters group (Manual)" type: "manual" remediation: | Remove the system:masters group from all users in the cluster. scored: false - id: 5.1.8 text: "Limit use of the Bind, Impersonate and Escalate permissions in the Kubernetes cluster (Manual)" type: "manual" remediation: | Where possible, remove the impersonate, bind and escalate rights from subjects. scored: false - id: 5.1.9 text: "Minimize access to create persistent volumes (Manual)" type: "manual" remediation: | Where possible, remove create access to PersistentVolume objects in the cluster. scored: false - id: 5.1.10 text: "Minimize access to the proxy sub-resource of nodes (Manual)" type: "manual" remediation: | Where possible, remove access to the proxy sub-resource of node objects. scored: false - id: 5.1.11 text: "Minimize access to the approval sub-resource of certificatesigningrequests objects (Manual)" type: "manual" remediation: | Where possible, remove access to the approval sub-resource of certificatesigningrequest objects. scored: false - id: 5.1.12 text: "Minimize access to webhook configuration objects (Manual)" type: "manual" remediation: | Where possible, remove access to the validatingwebhookconfigurations or mutatingwebhookconfigurations objects scored: false - id: 5.1.13 text: "Minimize access to the service account token creation (Manual)" type: "manual" remediation: | Where possible, remove access to the token sub-resource of serviceaccount objects. scored: false - id: 5.2 text: "Pod Security Standards" checks: - id: 5.2.1 text: "Ensure that the cluster has at least one active policy control mechanism in place (Manual)" type: "manual" remediation: | Ensure that either Pod Security Admission or an external policy control system is in place for every namespace which contains user workloads. scored: false - id: 5.2.2 text: "Minimize the admission of privileged containers (Manual)" type: "manual" remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of privileged containers. scored: false - id: 5.2.3 text: "Minimize the admission of containers wishing to share the host process ID namespace (Automated)" remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of `hostPID` containers. scored: false - id: 5.2.4 text: "Minimize the admission of containers wishing to share the host IPC namespace (Automated)" remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of `hostIPC` containers. scored: false - id: 5.2.5 text: "Minimize the admission of containers wishing to share the host network namespace (Automated)" remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of `hostNetwork` containers. scored: false - id: 5.2.6 text: "Minimize the admission of containers with allowPrivilegeEscalation (Automated)" type: "manual" remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of containers with `.spec.allowPrivilegeEscalation` set to `true`. scored: true - id: 5.2.7 text: "Minimize the admission of root containers (Automated)" type: "manual" remediation: | Create a policy for each namespace in the cluster, ensuring that either `MustRunAsNonRoot` or `MustRunAs` with the range of UIDs not including 0, is set. scored: false - id: 5.2.8 text: "Minimize the admission of containers with the NET_RAW capability (Automated)" type: "manual" remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of containers with the `NET_RAW` capability. scored: false - id: 5.2.9 text: "Minimize the admission of containers with added capabilities (Automated)" type: "manual" remediation: | Ensure that `allowedCapabilities` is not present in policies for the cluster unless it is set to an empty array. scored: false - id: 5.2.10 text: "Minimize the admission of containers with capabilities assigned (Manual)" type: "manual" remediation: | Review the use of capabilities in applications running on your cluster. Where a namespace contains applications which do not require any Linux capabities to operate consider adding a PSP which forbids the admission of containers which do not drop all capabilities. scored: false - id: 5.2.11 text: "Minimize the admission of Windows HostProcess containers (Manual)" type: "manual" remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of containers that have `.securityContext.windowsOptions.hostProcess` set to `true`. scored: false - id: 5.2.12 text: "Minimize the admission of HostPath volumes (Manual)" type: "manual" remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of containers with `hostPath` volumes. scored: false - id: 5.2.13 text: "Minimize the admission of containers which use HostPorts (Manual)" type: "manual" remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of containers which use `hostPort` sections. scored: false - id: 5.3 text: "Network Policies and CNI" checks: - id: 5.3.1 text: "Ensure that the CNI in use supports NetworkPolicies (Manual)" type: "manual" remediation: | If the CNI plugin in use does not support network policies, consideration should be given to making use of a different plugin, or finding an alternate mechanism for restricting traffic in the Kubernetes cluster. scored: false - id: 5.3.2 text: "Ensure that all Namespaces have NetworkPolicies defined (Manual)" remediation: | Follow the documentation and create NetworkPolicy objects as you need them. scored: false - id: 5.4 text: "Secrets Management" checks: - id: 5.4.1 text: "Prefer using Secrets as files over Secrets as environment variables (Manual)" type: "manual" remediation: | If possible, rewrite application code to read Secrets from mounted secret files, rather than from environment variables. scored: false - id: 5.4.2 text: "Consider external secret storage (Manual)" type: "manual" remediation: | Refer to the Secrets management options offered by your cloud provider or a third-party secrets management solution. scored: false - id: 5.5 text: "Extensible Admission Control" checks: - id: 5.5.1 text: "Configure Image Provenance using ImagePolicyWebhook admission controller (Manual)" type: "manual" remediation: | Follow the Kubernetes documentation and setup image provenance. scored: false - id: 5.7 text: "General Policies" checks: - id: 5.7.1 text: "Create administrative boundaries between resources using namespaces (Manual)" type: "manual" remediation: | Follow the documentation and create namespaces for objects in your deployment as you need them. scored: false - id: 5.7.2 text: "Ensure that the seccomp profile is set to docker/default in your Pod definitions (Manual)" type: "manual" remediation: | Use `securityContext` to enable the docker/default seccomp profile in your pod definitions. An example is as below: securityContext: seccompProfile: type: RuntimeDefault scored: false - id: 5.7.3 text: "Apply SecurityContext to your Pods and Containers (Manual)" type: "manual" remediation: | Follow the Kubernetes documentation and apply SecurityContexts to your Pods. For a suggested list of SecurityContexts, you may refer to the CIS Security Benchmark for Docker Containers. scored: false - id: 5.7.4 text: "The default namespace should not be used (Manual)" remediation: | Ensure that namespaces are created to allow for appropriate segregation of Kubernetes resources and that all new resources are created in a specific namespace. scored: false ================================================ FILE: cfg/rh-0.7/config.yaml ================================================ --- ## Version-specific settings that override the values in cfg/config.yaml ================================================ FILE: cfg/rh-0.7/master.yaml ================================================ --- controls: version: "rh-0.7" id: 1 text: "Securing the OpenShift Master" type: "master" groups: - id: 1 text: "Protecting the API Server" checks: - id: 1.1 text: "Maintain default behavior for anonymous access" type: "skip" scored: true - id: 1.2 text: "Verify that the basic-auth-file method is not enabled" audit_config: "cat /etc/origin/master/master-config.yaml" tests: test_items: - path: "{.kubernetesMasterConfig.apiServerArguments.basic-auth-file}" set: false remediation: | Edit the kubernetes master config file /etc/origin/master/master-config.yaml and remove the basic-auth-file entry. kubernetesMasterConfig:  apiServerArguments:    basic-auth-file:    - /path/to/any/file scored: true - id: 1.3 text: "Insecure Tokens" type: "skip" scored: true - id: 1.4 text: "Secure communications between the API server and master nodes" audit_config: "cat /etc/origin/master/master-config.yaml" tests: bin_op: and test_items: - path: "{.kubeletClientInfo.ca}" compare: op: has value: "ca-bundle.crt" - path: "{.kubeletClientInfo.certFile}" compare: op: has value: "master.kubelet-client.crt" - path: "{.kubeletClientInfo.keyFile}" compare: op: has value: "master.kubelet-client.key" set: true - path: "{.kubeletClientInfo.port}" compare: op: eq value: "10250" remediation: | Edit the kubernetes master config file /etc/origin/master/master-config.yaml and change it to match the below. kubeletClientInfo:  ca: ca-bundle.crt  certFile: master.kubelet-client.crt  keyFile: master.kubelet-client.key  port: 10250 scored: true - id: 1.5 text: "Prevent insecure bindings" audit_config: "cat /etc/origin/master/master-config.yaml" tests: test_items: - path: "{.kubernetesMasterConfig.insecure-bind-address}" set: false remediation: | Edit the kubernetes master config file /etc/origin/master/master-config.yaml and remove the insecure-bind-address entry. kubernetesMasterConfig:  apiServerArguments:    insecure-bind-address:    - 127.0.0.1 scored: true - id: 1.6 text: "Prevent insecure port access" audit_config: "cat /etc/origin/master/master-config.yaml" tests: test_items: - path: "{.kubernetesMasterConfig.apiServerArguments.insecure-port}" set: false remediation: | Edit the kubernetes master config file /etc/origin/master/master-config.yaml and remove the insecure-port entry. kubernetesMasterConfig:  apiServerArguments:   insecure-port:  - 0 scored: true - id: 1.7 text: "Use Secure Ports for API Server Traffic" audit_config: "cat /etc/origin/master/master-config.yaml" tests: bin_op: or test_items: - path: "{.kubernetesMasterConfig.apiServerArguments.secure-port}" set: false - path: "{.kubernetesMasterConfig.apiServerArguments.secure-port}" compare: op: noteq value: "0" remediation: | Edit the kubernetes master config file /etc/origin/master/master-config.yaml and either remove the secure-port parameter or set it to a different (non-zero) desired port. kubernetesMasterConfig:  apiServerArguments:   secure-port:  - 8443 scored: true - id: 1.8 text: "Do not expose API server profiling data" type: "skip" scored: true - id: 1.9 text: "Verify repair-malformed-updates argument for API compatibility" audit_config: "cat /etc/origin/master/master-config.yaml" tests: bin_op: or test_items: - path: "{.repair-malformed-updates}" set: false - path: "{.repair-malformed-updates}" compare: op: eq value: "true" remediation: | Edit the kubernetes master config file /etc/origin/master/master-config.yaml and remove the repair-malformed-updates entry or set repair-malformed-updates=true. scored: true - id: 1.10 text: "Verify that the AlwaysAdmit admission controller is disabled" audit_config: "cat /etc/origin/master/master-config.yaml" tests: test_items: - path: "{.AlwaysAdmit}" set: false remediation: | Edit the kubernetes master config file /etc/origin/master/master-config.yaml and remove the entry below. AlwaysAdmit: configuration: kind: DefaultAdmissionConfig apiVersion: v1 disable: false scored: true - id: 1.11 text: "Manage the AlwaysPullImages admission controller" audit_config: "cat /etc/origin/master/master-config.yaml" tests: test_items: - path: "{.admissionConfig.pluginConfig.configuration.disable}" compare: op: eq value: "false" remediation: | Edit the kubernetes master config file /etc/origin/master/master-config.yaml and add the entry below. admissionConfig: pluginConfig: AlwaysPullImages: configuration: kind: DefaultAdmissionConfig apiVersion: v1 disable: false scored: true - id: 1.12 text: "Use Security Context Constraints instead of DenyEscalatingExec admission" type: "skip" scored: true - id: 1.13 text: "Use Security Context Constraints instead of the SecurityContextDeny admission controller" type: "skip" scored: true - id: 1.14 text: "Manage the NamespaceLifecycle admission controller" audit_config: "cat /etc/origin/master/master-config.yaml" tests: test_items: - path: "{.NamespaceLifecycle}" set: false remediation: | Edit the kubernetes master config file /etc/origin/master/master-config.yaml and remove the following entry. NamespaceLifecycle: configuration: kind: DefaultAdmissionConfig apiVersion: v1 disable: true scored: true - id: 1.15 text: "Configure API server auditing - audit log file path" audit_config: "cat /etc/origin/master/master-config.yaml" tests: test_items: - path: "{.auditConfig.enabled}" compare: op: eq value: "true" remediation: | Edit the Openshift master config file /etc/origin/master/master-config.yaml, update the following entry and restart the API server. auditConfig: auditFilePath: ""/etc/origin/master/audit-ocp.log"" enabled: true maximumFileRetentionDays: 30 maximumFileSizeMegabytes: 10 maximumRetainedFiles: 10 Make the same changes in the inventory/ansible variables so the changes are not lost when an upgrade occurs. scored: true - id: 1.16 text: "Configure API server auditing - audit log retention" audit_config: "cat /etc/origin/master/master-config.yaml" tests: test_items: - path: "{.auditConfig.maximumFileRetentionDays}" compare: op: gte value: 30 remediation: | Edit the Openshift master config file /etc/origin/master/master-config.yaml, update the maximumFileRetentionDays entry and restart the API server. auditConfig: auditFilePath: ""/etc/origin/master/audit-ocp.log"" enabled: true maximumFileRetentionDays: 30 maximumFileSizeMegabytes: 10 maximumRetainedFiles: 10 Make the same changes in the inventory/ansible variables so the changes are not lost when an upgrade occurs. scored: true - id: 1.17 text: "Configure API server auditing - audit log backup retention" audit_config: "cat /etc/origin/master/master-config.yaml" tests: test_items: - path: "{.auditConfig.maximumRetainedFiles}" compare: op: eq value: "10" remediation: | Edit the Openshift master config file /etc/origin/master/master-config.yaml, update the maximumRetainedFiles entry, set enabled to true and restart the API server. auditConfig: auditFilePath: ""/etc/origin/master/audit-ocp.log"" enabled: true maximumFileRetentionDays: 30 maximumFileSizeMegabytes: 10 maximumRetainedFiles: 10 Make the same changes in the inventory/ansible variables so the changes are not lost when an upgrade occurs. scored: true - id: 1.18 text: "Configure audit log file size" audit_config: "cat /etc/origin/master/master-config.yaml" tests: test_items: - path: "{.auditConfig.maximumFileSizeMegabytes}" compare: op: eq value: "30" remediation: | Edit the Openshift master config file /etc/origin/master/master-config.yaml, update the maximumFileSizeMegabytes entry, set enabled to true and restart the API server. auditConfig: auditFilePath: ""/etc/origin/master/audit-ocp.log"" enabled: true maximumFileRetentionDays: 30 maximumFileSizeMegabytes: 10 maximumRetainedFiles: 10 Make the same changes in the inventory/ansible variables so the changes are not lost when an upgrade occurs. scored: true - id: 1.19 text: "Verify that authorization-mode is not set to AlwaysAllow" audit_config: "cat /etc/origin/master/master-config.yaml" tests: test_items: - path: "{.kubernetesMasterConfig.apiServerArguments.authorization-mode}" set: false remediation: | Edit the Openshift master config file /etc/origin/master/master-config.yaml and remove the authorization-mode entry. kubernetesMasterConfig:  apiServerArguments:    authorization-mode:    - AllowAll scored: true - id: 1.20 text: "Verify that the token-auth-file flag is not set" audit_config: "cat /etc/origin/master/master-config.yaml" tests: test_items: - path: "{.kubernetesMasterConfig.apiServerArguments.token-auth-file}" set: false remediation: | Edit the Openshift master config file /etc/origin/master/master-config.yaml and remove the token-auth-file entry under apiserverArguments section. kubernetesMasterConfig:  apiServerArguments:    token-auth-file:    - /path/to/file scored: true - id: 1.21 text: "Verify the API server certificate authority" audit_config: "cat /etc/origin/master/master-config.yaml" tests: test_items: - path: "{.kubernetesMasterConfig.apiServerArguments.kubelet-certificat-authority}" set: false remediation: | Edit the Openshift master config file /etc/origin/master/master-config.yaml and remove the following configuration under apiserverArguments section. kubernetesMasterConfig:  apiServerArguments:    kubelet-certificat-authority:    - /path/to/ca scored: true - id: 1.22 text: "Verify the API server client certificate and client key" audit_config: "cat /etc/origin/master/master-config.yaml" tests: bin_op: and test_items: - path: "{.kubeletClientInfo.keyFile}" compare: op: eq value: "master.kubelet-client.key" - path: "{.kubeletClientInfo.certFile}" compare: op: eq value: "master.kubelet-client.crt" remediation: | Edit the Openshift master config file /etc/origin/master/master-config.yaml and add the following configuration under kubeletClientInfo kubeletClientInfo:  ca: ca-bundle.crt  certFile: master.kubelet-client.crt  keyFile: master.kubelet-client.key port: 10250 scored: true - id: 1.23 text: "Verify that the service account lookup flag is not set" type: "skip" scored: true - id: 1.24 text: "Verify the PodSecurityPolicy is disabled to ensure use of SecurityContextConstraints" type: "skip" scored: true - id: 1.25 text: "Verify that the service account key file argument is not set" audit_config: "cat /etc/origin/master/master-config.yaml" tests: bin_op: and test_items: - path: "{.serviceAccountConfig.privateKeyFile}" compare: op: eq value: "serviceaccounts.private.key" - path: "{.serviceAccountConfig.publicKeyFiles}" compare: op: has value: "serviceaccounts.public.key" remediation: | OpenShift API server does not use the service-account-key-file argument. Even if value is set in master-config.yaml, it will not be used to verify service account tokens, as it is in upstream Kubernetes. The ServiceAccount token authenticator is configured with serviceAccountConfig.publicKeyFiles in the master-config.yaml. OpenShift does not reuse the apiserver TLS key. Edit the Openshift master config file /etc/origin/master/master-config.yaml and set the privateKeyFile and publicKeyFile configuration under serviceAccountConfig. serviceAccountConfig:  limitSecretReferences: false  managedNames: - default  - builder  - deployer  masterCA: ca-bundle.crt   privateKeyFile: serviceaccounts.private.key  publicKeyFiles:  - serviceaccounts.public.key Verify that privateKeyFile and publicKeyFile exist and set. scored: true - id: 1.26 text: "Verify the certificate and key used for communication with etcd" audit_config: "cat /etc/origin/master/master-config.yaml" tests: bin_op: and test_items: - path: "{.etcdClientInfo.certFile}" compare: op: eq value: "master.etcd-client.crt" - path: "{.etcdClientInfo.keyFile}" compare: op: eq value: "master.etcd-client.key" remediation: | Edit the Openshift master config file /etc/origin/master/master-config.yaml and set keyFile and certFile under etcdClientInfo like below. etcdClientInfo:  ca: master.etcd-ca.crt certFile: master.etcd-client.crt keyFile: master.etcd-client.key scored: true - id: 1.27 text: "Verify that the ServiceAccount admission controller is enabled" audit_config: "cat /etc/origin/master/master-config.yaml" tests: bin_op: or test_items: - path: "{.ServiceAccount.configuration.disable}" set: false - path: "{.ServiceAccount.configuration.disable}" compare: op: eq value: "false" remediation: | Edit the Openshift master config file /etc/origin/master/master-config.yaml and enable ServiceAccount admission control policy. ServiceAccount: configuration: kind: DefaultAdmissionConfig apiVersion: v1 disable: false scored: true - id: 1.28 text: "Verify the certificate and key used to encrypt API server traffic" audit_config: "cat /etc/origin/master/master-config.yaml" tests: bin_op: and test_items: - path: "{.servingInfo.certFile}" compare: op: eq value: "master.server.crt" - path: "{.servingInfo.keyFile}" compare: op: eq value: "master.server.key" remediation: | Edit the Openshift master config file /etc/origin/master/master-config.yaml and set keyFile and certFile under servingInfo. servingInfo:  bindAddress: 0.0.0.0:8443   bindNetwork: tcp4 certFile: master.server.crt clientCA: ca.crt keyFile: master.server.key maxRequestsInFlight: 500 requestTimeoutSeconds: 3600 scored: true - id: 1.29 text: "Verify that the --client-ca-file argument is not set" audit_config: "cat /etc/origin/master/master-config.yaml" tests: test_items: - path: "{.servingInfo.clientCA}" set: false remediation: | Edit the Openshift master config file /etc/origin/master/master-config.yaml and set clientCA under servingInfo. servingInfo:  bindAddress: 0.0.0.0:8443   bindNetwork: tcp4 certFile: master.server.crt clientCA: ca.crt keyFile: master.server.key maxRequestsInFlight: 500 requestTimeoutSeconds: 3600 scored: true - id: 1.30 text: "Verify the CA used for communication with etcd" audit_config: "cat /etc/origin/master/master-config.yaml" tests: test_items: - path: "{.etcdClientInfo.ca}" compare: op: eq value: "master.etcd-ca.crt" remediation: | Edit the Openshift master config file /etc/origin/master/master-config.yaml and set ca under etcdClientInfo. etcdClientInfo:   ca: master.etcd-ca.crt certFile: master.etcd-client.crt keyFile: master.etcd-client.key scored: true - id: 1.31 text: "Verify that the authorization-mode argument is not set" type: "skip" scored: true - id: 1.32 text: "Verify that the NodeRestriction admission controller is enabled" audit_config: "cat /etc/origin/master/master-config.yaml" tests: bin_op: or test_items: - path: "{.NodeRestriction.configuration.disable}" set: false - path: "{.NodeRestriction.configuration.disable}" compare: op: eq value: "false" remediation: | Edit the Openshift master config file /etc/origin/master/master-config.yaml and enable NodeRestriction ca under etcdClientInfo. NodeRestriction: configuration: kind: DefaultAdmissionConfig apiVersion: v1 disable: false scored: true - id: 1.33 text: "Configure encryption of data at rest in etcd datastore" audit_config: "cat /etc/origin/master/master-config.yaml" tests: test_items: - path: "{.kubernetesMasterConfig.apiServerArguments.experimental-encryption-provider-config}" remediation: | Follow the instructions in the documentation to configure encryption. https://docs.openshift.com/container-platform/3.10/admin_guide/encrypting_data.html scored: true - id: 1.34 text: "Set the encryption provider to aescbc for etcd data at rest" audit_config: "grep -A1 experimental-encryption-provider-config /etc/origin/master/master-config.yaml | sed -n '2p' | awk '{ print $2 }' | xargs cat" tests: bin_op: and test_items: - path: "{.resources[*].providers[*].aescbc.keys[*]}}" compare: op: has value: "secret" - path: "{.resources[*].providers[*].aescbc.keys[*]}}" compare: op: has value: "name" remediation: | Edit the Openshift master config file /etc/origin/master/master-config.yaml and set aescbc as the first provider in encryption provider config. See https://docs.openshift.com/container-platform/3.10/admin_guide/encrypting_data.html. scored: true - id: 1.35 text: "Enable the EventRateLimit plugin" audit_config: "cat /etc/origin/master/master-config.yaml" tests: test_items: - path: "{.admissionConfig.pluginConfig.AlwaysPullImages.configuration.disable}" compare: op: eq value: "false" remediation: | Follow the documentation to enable the EventRateLimit plugin. https://docs.openshift.com/container-platform/3.10/architecture/additional_concepts/admission_controllers.html#admission-controllers-general-admission-rules scored: true - id: 1.36 text: "Configure advanced auditing" audit_config: "cat /etc/origin/master/master-config.yaml" tests: bin_op: or test_items: - path: "{.kubernetesMasterConfig.apiServerArguments.feature-gates.AdvancedAuditing}" compare: op: eq value: "true" - path: "{.kubernetesMasterConfig.apiServerArguments.feature-gates.AdvancedAuditing}" set: false remediation: | Edit the Openshift master config file /etc/origin/master/master-config.yaml and enable AdvancedAuditing, kubernetesMasterConfig:  apiServerArguments: feature-gates: - AdvancedAuditing=true scored: true # Review 1.1.37 in Aquasec shared doc, the tests are net zero. - id: 1.37 text: "Adjust the request timeout argument for your cluster resources" audit: "grep request-timeout /etc/origin/master/master-config.yaml" type: "manual" remediation: | [Manual test] change the request-timeout value in the  /etc/origin/master/master-config.yaml scored: true - id: 2 text: "Scheduler" checks: - id: 2.1 text: "Verify that Scheduler profiling is not exposed to the web" type: "skip" scored: true - id: 3 text: "Controller Manager" checks: - id: 3.1 text: "Adjust the terminated-pod-gc-threshold argument as needed" audit_config: "cat /etc/origin/master/master-config.yaml" tests: test_items: - path: "{.kubernetesMasterConfig.controllerArguments.terminated-pod-gc-threshold}" compare: op: eq value: "12500" remediation: | Edit the Openshift master config file /etc/origin/master/master-config.yaml and enable terminated-pod-gc-threshold. kubernetesMasterConfig:  controllerArguments:     terminated-pod-gc-threshold:    - true Enabling the "terminated-pod-gc-threshold" settings is optional. scored: true - id: 3.2 text: "Verify that Controller profiling is not exposed to the web" type: "skip" scored: true - id: 3.3 text: "Verify that the --use-service-account-credentials argument is set to true" audit_config: "cat /etc/origin/master/master-config.yaml" tests: bin_op: or test_items: - path: "{.kubernetesMasterConfig.controllerArguments.use-service-account-credentials}" set: false - path: "{.kubernetesMasterConfig.controllerArguments.use-service-account-credentials}" compare: op: eq value: "true" remediation: | Edit the Openshift master config file /etc/origin/master/master-config.yaml and set use-service-account-credentials to true under controllerArguments section. kubernetesMasterConfig:  controllerArguments:     use-service-account-credentials:     - true scored: true - id: 3.4 text: "Verify that the --service-account-private-key-file argument is set as appropriate" audit: | grep -A9 serviceAccountConfig /etc/origin/master/master-config.yaml | grep privateKeyFile; grep -A2 service-account-private-key-file /etc/origin/master/master-config.yaml audit_config: "cat /etc/origin/master/master-config.yaml" tests: bin_op: and test_items: - flag: "privateKeyFile: serviceaccounts.private.key" path: "{.serviceAccountConfig.privateKeyFile}" compare: op: eq value: "serviceaccounts.private.key" - flag: "service-account-private-key-file" set: false remediation: | Edit the Openshift master config file /etc/origin/master/master-config.yaml and remove service-account-private-key-file scored: true - id: 3.5 text: "Verify that the --root-ca-file argument is set as appropriate" audit_config: "cat /etc/origin/master/master-config.yaml" tests: bin_op: and test_items: - path: "{.serviceAccountConfig.root-ca-file}" compare: op: eq value: "/etc/origin/master/ca-bundle.crt" - path: "{.serviceAccountConfig.masterCA}" compare: op: eq value: "ca-bundle.crt" remediation: | Reset to OpenShift defaults OpenShift starts kube-controller-manager with root-ca-file=/etc/origin/master/ca-bundle.crt by default. OpenShift Advanced Installation creates this certificate authority and configuration without any configuration required. https://docs.openshift.com/container-platform/3.10/admin_guide/service_accounts.html" scored: true - id: 3.6 text: "Verify that Security Context Constraints are applied to Your Pods and Containers" type: "skip" scored: false - id: 3.7 text: "Manage certificate rotation" audit_config: "cat /etc/origin/master/master-config.yaml" tests: test_items: - path: "{.kubeletArguments.feature-gates.RotateKubeletServerCertificate}" compare: op: eq value: "true" remediation: | If you decide not to enable the RotateKubeletServerCertificate feature, be sure to use the Ansible playbooks provided with the OpenShift installer to automate re-deploying certificates. scored: true - id: 4 text: "Configuration Files" checks: - id: 4.1 text: "Verify the OpenShift default permissions for the API server pod specification file" audit: "stat -c permissions=%a /etc/origin/node/pods/apiserver.yaml" tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Run the below command. chmod 600 /etc/origin/node/pods/apiserver.yaml scored: true - id: 4.2 text: "Verify the OpenShift default file ownership for the API server pod specification file" audit: "stat -c %U:%G /etc/origin/node/pods/apiserver.yaml" tests: test_items: - flag: "root:root" remediation: | Run the below command on the master node. chown root:root /etc/origin/node/pods/apiserver.yaml scored: true - id: 4.3 text: "Verify the OpenShift default file permissions for the controller manager pod specification file" audit: "stat -c permissions=%a /etc/origin/node/pods/controller.yaml" tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Run the below command on the master node. chmod 600 /etc/origin/node/pods/controller.yaml scored: true - id: 4.4 text: "Verify the OpenShift default ownership for the controller manager pod specification file" audit: "stat -c %U:%G /etc/origin/node/pods/controller.yaml" tests: test_items: - flag: "root:root" remediation: | Run the below command on the master node. chown root:root /etc/origin/node/pods/controller.yaml scored: true - id: 4.5 text: "Verify the OpenShift default permissions for the scheduler pod specification file" audit: "stat -c permissions=%a /etc/origin/node/pods/controller.yaml" tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Run the below command. chmod 600 stat -c permissions=%a /etc/origin/node/pods/controller.yaml scored: true - id: 4.6 text: "Verify the scheduler pod specification file ownership set by OpenShift" audit: "stat -c %U:%G /etc/origin/node/pods/controller.yaml" tests: test_items: - flag: "root:root" remediation: | Run the below command on the master node. chown root:root /etc/origin/node/pods/controller.yaml scored: true - id: 4.7 text: "Verify the OpenShift default etcd pod specification file permissions" audit: "stat -c permissions=%a /etc/origin/node/pods/etcd.yaml" tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Run the below command. chmod 600 /etc/origin/node/pods/etcd.yaml scored: true - id: 4.8 text: "Verify the OpenShift default etcd pod specification file ownership" audit: "stat -c %U:%G /etc/origin/node/pods/etcd.yaml" tests: test_items: - flag: "root:root" remediation: | Run the below command on the master node. chown root:root /etc/origin/node/pods/etcd.yaml scored: true - id: 4.9 text: "Verify the default OpenShift Container Network Interface file permissions" audit: "stat -c permissions=%a /etc/origin/openvswitch/ /etc/cni/net.d/" use_multiple_values: true tests: test_items: - flag: "permissions" compare: op: bitmask value: "644" remediation: | Run the below command. chmod 644 -R /etc/origin/openvswitch/ /etc/cni/net.d/ scored: true - id: 4.10 text: "Verify the default OpenShift Container Network Interface file ownership" audit: "stat -c %U:%G /etc/origin/openvswitch/ /etc/cni/net.d/" use_multiple_values: true tests: test_items: - flag: "root:root" remediation: | Run the below command on the master node. chown root:root /etc/origin/openvswitch/ /etc/cni/net.d/ scored: true - id: 4.11 text: "Verify the default OpenShift etcd data directory permissions" audit: "stat -c permissions=%a /var/lib/etcd" tests: test_items: - flag: "permissions" compare: op: bitmask value: "700" remediation: | On the etcd server node, get the etcd data directory, passed as an argument --data-dir , from the below command: ps -ef | grep etcd Run the below command (based on the etcd data directory found above). For example, chmod 700 /var/lib/etcd scored: true - id: 4.12 text: "Verify the default OpenShift etcd data directory ownership" audit: "stat -c %U:%G /var/lib/etcd" tests: test_items: - flag: "etcd:etcd" remediation: | Run the below command on the master node. chown etcd:etcd /var/lib/etcd scored: true - id: 4.13 text: "Verify the default OpenShift admin.conf file permissions" audit: "stat -c permissions=%a /etc/origin/master/admin.kubeconfig" tests: test_items: - flag: "permissions" compare: op: bitmask value: "644" remediation: | Run the below command. chmod 644 /etc/origin/master/admin.kubeconfig" scored: true - id: 4.14 text: "Verify the default OpenShift admin.conf file ownership" audit: "stat -c %U:%G /etc/origin/master/admin.kubeconfig" tests: test_items: - flag: "root:root" remediation: | Run the below command on the master node. chown root:root /etc/origin/master/admin.kubeconfig scored: true - id: 4.15 text: "Verify the default OpenShift scheduler.conf file permissions" audit: "stat -c permissions=%a /etc/origin/master/openshift-master.kubeconfig" tests: test_items: - flag: "permissions" compare: op: bitmask value: "644" remediation: | Run the below command. chmod 644 /etc/origin/master/openshift-master.kubeconfig scored: true - id: 4.16 text: "Verify the default OpenShift scheduler.conf file ownership" audit: "stat -c %U:%G /etc/origin/master/openshift-master.kubeconfig" tests: test_items: - flag: "root:root" remediation: | Run the below command on the master node. chown root:root /etc/origin/master/openshift-master.kubeconfig scored: true - id: 4.17 text: "Verify the default Openshift controller-manager.conf file permissions" audit: "stat -c permissions=%a /etc/origin/master/openshift-master.kubeconfig" tests: test_items: - flag: "permissions" compare: op: bitmask value: "644" remediation: | Run the below command. chmod 644 /etc/origin/master/openshift-master.kubeconfig scored: true - id: 4.18 text: "Ensure that the controller-manager.conf file ownership is set to root:root (Scored)" audit: "stat -c %U:%G /etc/origin/master/openshift-master.kubeconfig" tests: test_items: - flag: "root:root" compare: op: eq value: "root:root" set: true remediation: | Run the below command on the master node. chown root:root /etc/origin/master/openshift-master.kubeconfig scored: true - id: 5 text: "Etcd" checks: - id: 5.1 text: "Verify the default OpenShift cert-file and key-file configuration" audit: "/bin/sh -c '/usr/local/bin/master-exec etcd etcd grep ETCD_CERT_FILE=/etc/etcd/server.crt /proc/1/environ; /usr/local/bin/master-exec etcd etcd grep ETCD_KEY_FILE=/etc/etcd/server.key /proc/1/environ; grep ETCD_CERT_FILE=/etc/etcd/server.crt /etc/etcd/etcd.conf; grep ETCD_KEY_FILE=/etc/etcd/server.key /etc/etcd/etcd.conf'" tests: bin_op: and test_items: - flag: "Binary file /proc/1/environ matches" - flag: "ETCD_CERT_FILE" compare: op: eq value: "/etc/etcd/server.crt" - flag: "ETCD_KEY_FILE" compare: op: eq value: "/etc/etcd/server.key" remediation: | Reset to the OpenShift default configuration. scored: true - id: 5.2 text: "Verify the default OpenShift setting for the client-cert-auth argument" audit: "/bin/sh -c '/usr/local/bin/master-exec etcd etcd grep ETCD_CLIENT_CERT_AUTH=true /proc/1/environ; grep ETCD_CLIENT_CERT_AUTH /etc/etcd/etcd.conf'" tests: bin_op: and test_items: - flag: "Binary file /proc/1/environ matches" - flag: "ETCD_CLIENT_CERT_AUTH" compare: op: eq value: "true" remediation: | Reset to the OpenShift default configuration. scored: true - id: 5.3 text: "Verify the OpenShift default values for etcd_auto_tls" audit: "/bin/sh -c '/usr/local/bin/master-exec etcd etcd grep ETCD_AUTO_TLS /proc/1/environ; grep ETCD_AUTO_TLS /etc/etcd/etcd.conf'" tests: bin_op: or test_items: - flag: "ETCD_AUTO_TLS" compare: op: eq value: "false" remediation: | Reset to the OpenShift default configuration. scored: true - id: 5.4 text: "Verify the OpenShift default peer-cert-file and peer-key-file arguments for etcd" audit: "/bin/sh -c '/usr/local/bin/master-exec etcd etcd grep ETCD_PEER_CERT_FILE=/etc/etcd/peer.crt /proc/1/environ; /usr/local/bin/master-exec etcd etcd grep ETCD_PEER_KEY_FILE=/etc/etcd/peer.key /proc/1/environ; grep ETCD_PEER_CERT_FILE /etc/etcd/etcd.conf; grep ETCD_PEER_KEY_FILE /etc/etcd/etcd.conf'" tests: bin_op: and test_items: - flag: "Binary file /proc/1/environ matches" - flag: "ETCD_PEER_CERT_FILE" compare: op: eq value: "/etc/etcd/peer.crt" - flag: "ETCD_PEER_KEY_FILE" compare: op: eq value: "/etc/etcd/peer.key" remediation: | Reset to the OpenShift default configuration. scored: true - id: 5.5 text: "Verify the OpenShift default configuration for the peer-client-cert-auth" audit: "/bin/sh -c '/usr/local/bin/master-exec etcd etcd grep ETCD_PEER_CLIENT_CERT_AUTH=true /proc/1/environ; grep ETCD_PEER_CLIENT_CERT_AUTH /etc/etcd/etcd.conf'" tests: bin_op: and test_items: - flag: "Binary file /proc/1/environ matches" - flag: "ETCD_PEER_CLIENT_CERT_AUTH" compare: op: eq value: "true" remediation: | Reset to the OpenShift default configuration. scored: true - id: 5.6 text: "Verify the OpenShift default configuration for the peer-auto-tls argument" audit: "/bin/sh -c '/usr/local/bin/master-exec etcd etcd grep ETCD_PEER_AUTO_TLS /proc/1/environ; grep ETCD_PEER_AUTO_TLS /etc/etcd/etcd.conf'" tests: test_items: - flag: "ETCD_PEER_AUTO_TLS" compare: op: noteq value: "true" remediation: | Reset to the OpenShift default configuration. scored: true - id: 5.7 text: "Optionally modify the wal-dir argument" type: "skip" scored: true - id: 5.8 text: "Optionally modify the max-wals argument" type: "skip" scored: true - id: 5.9 text: "Verify the OpenShift default configuration for the etcd Certificate Authority" audit: "openssl x509 -in /etc/origin/master/master.etcd-ca.crt -subject -issuer -noout | sed 's/@/ /'" tests: test_items: - flag: "issuer= /CN=etcd-signer" remediation: | Reset to the OpenShift default configuration. scored: false - id: 6 text: "General Security Primitives" checks: - id: 6.1 text: "Ensure that the cluster-admin role is only used where required" type: "manual" remediation: | [Manual test] Review users, groups, serviceaccounts bound to cluster-admin: oc get clusterrolebindings | grep cluster-admin Review users and groups bound to cluster-admin and decide whether they require such access. Consider creating least-privilege roles for users and service accounts scored: false - id: 6.2 text: "Verify Security Context Constraints as in use" type: "manual" remediation: | [Manual test] Review Security Context Constraints: oc get scc Use OpenShift's Security Context Constraint feature, which has been contributed to Kubernetes as Pod Security Policies. PSPs are still beta in Kubernetes 1.10. OpenShift ships with two SCCs: restricted and privileged. The two default SCCs will be created when the master is started. The restricted SCC is granted to all authenticated users by default. https://docs.openshift.com/container-platform/3.10/admin_guide/manage_scc.html" scored: false - id: 6.3 text: "Use OpenShift projects to maintain boundaries between resources" type: "manual" remediation: | [Manual test] Review projects: oc get projects scored: false - id: 6.4 text: "Create network segmentation using the Multi-tenant plugin or Network Policies" type: "manual" remediation: | [Manual test] Verify on masters the plugin being used: grep networkPluginName /etc/origin/master/master-config.yaml OpenShift provides multi-tenant networking isolation (using Open vSwich and vXLAN), to segregate network traffic between containers belonging to different tenants (users or applications) while running on a shared cluster. Red Hat also works with 3rd-party SDN vendors to provide the same level of capabilities integrated with OpenShift. OpenShift SDN is included a part of OpenShift subscription. OpenShift supports Kubernetes NetworkPolicy. Administrator must configure NetworkPolicies if desired. https://docs.openshift.com/container-platform/3.10/architecture/networking/sdn.html#architecture-additional-concepts-sdn Ansible Inventory variable: os_sdn_network_plugin_name: https://docs.openshift.com/container-platform/3.10/install/configuring_inventory_file.html scored: false - id: 6.5 text: "Enable seccomp and configure custom Security Context Constraints" type: "manual" remediation: | [Manual test] Verify SCCs that have been configured with seccomp: oc get scc -ocustom-columns=NAME:.metadata.name,SECCOMP-PROFILES:.seccompProfiles OpenShift does not enable seccomp by default. To configure seccomp profiles that are applied to pods run by the SCC, follow the instructions in the documentation: https://docs.openshift.com/container-platform/3.9/admin_guide/seccomp.html#admin-guide-seccomp scored: false - id: 6.6 text: "Review Security Context Constraints" type: "manual" remediation: | [Manual test] Review SCCs: oc describe scc Use OpenShift's Security Context Constraint feature, which has been contributed to Kubernetes as Pod Security Policies. PSPs are still beta in Kubernetes 1.10. OpenShift ships with two SCCs: restricted and privileged. The two default SCCs will be created when the master is started. The restricted SCC is granted to all authenticated users by default. All pods are run under the restricted SCC by default. Running a pod under any other SCC requires an account with cluster admin capabilities to grant access for the service account. SecurityContextConstraints limit what securityContext is applied to pods and containers. https://docs.openshift.com/container-platform/3.10/admin_guide/manage_scc.html scored: false - id: 6.7 text: "Manage Image Provenance using ImagePolicyWebhook admission controller" type: "manual" remediation: | [Manual test] Review imagePolicyConfig in /etc/origin/master/master-config.yaml. scored: false - id: 6.8 text: "Configure Network policies as appropriate" type: "manual" remediation: | [Manual test] If ovs-networkplugin is used, review network policies: oc get networkpolicies OpenShift supports Kubernetes NetworkPolicy via ovs-networkpolicy plugin. If choosing ovs-multitenant plugin, each namespace is isolated in its own netnamespace by default. scored: false - id: 6.9 text: "Use Security Context Constraints as compensating controls for privileged containers" type: "manual" remediation: | [Manual test] 1) Determine all sccs allowing privileged containers: oc get scc -ocustom-columns=NAME:.metadata.name,ALLOWS_PRIVILEGED:.allowPrivilegedContainer 2) Review users and groups assigned to sccs allowing priviliged containers: oc describe sccs Use OpenShift's Security Context Constraint feature, which has been contributed to Kubernetes as Pod Security Policies. PSPs are still beta in Kubernetes 1.10. OpenShift ships with two SCCs: restricted and privileged. The two default SCCs will be created when the master is started. The restricted SCC is granted to all authenticated users by default. Similar scenarios are documented in the SCC documentation, which outlines granting SCC access to specific serviceaccounts. Administrators may create least-restrictive SCCs based on individual container needs. For example, if a container only requires running as the root user, the anyuid SCC can be used, which will not expose additional access granted by running privileged containers. https://docs.openshift.com/container-platform/3.10/admin_guide/manage_scc.html scored: false ================================================ FILE: cfg/rh-0.7/node.yaml ================================================ --- controls: version: "rh-0.7" id: 2 text: "Worker Node Security Configuration" type: "node" groups: - id: 7 text: "Kubelet" checks: - id: 7.1 text: "Use Security Context Constraints to manage privileged containers as needed" type: "skip" scored: true - id: 7.2 text: "Ensure anonymous-auth is not disabled" type: "skip" scored: true - id: 7.3 text: "Verify that the --authorization-mode argument is set to WebHook" audit_config: "cat /etc/origin/node/node-config.yaml" tests: bin_op: or test_items: - path: "{.kubeletArguments.authorization-mode}" set: false - path: "{.kubeletArguments.authorization-mode}" compare: op: has value: "Webhook" remediation: | Edit the Openshift node config file /etc/origin/node/node-config.yaml and remove authorization-mode under kubeletArguments in /etc/origin/node/node-config.yaml or set it to "Webhook". scored: true - id: 7.4 text: "Verify the OpenShift default for the client-ca-file argument" audit_config: "cat /etc/origin/node/node-config.yaml" tests: test_items: - path: "{.PodManifestConfig.client-ca-file}" set: false remediation: | Edit the Openshift node config file /etc/origin/node/node-config.yaml and remove any configuration returned by the following: grep -A1 client-ca-file /etc/origin/node/node-config.yaml Reset to the OpenShift default. See https://github.com/openshift/openshift-ansible/blob/release-3.10/roles/openshift_node_group/templates/node-config.yaml.j2#L65 The config file does not have this defined in kubeletArgument, but in PodManifestConfig. scored: true - id: 7.5 text: "Verify the OpenShift default setting for the read-only-port argument" audit_config: "cat /etc/origin/node/node-config.yaml" tests: bin_op: or test_items: - path: "{.kubeletArguments.read-only-port}" set: false - path: "{.kubeletArguments.read-only-port}" compare: op: eq value: "0" remediation: | Edit the Openshift node config file /etc/origin/node/node-config.yaml and removed so that the OpenShift default is applied. scored: true - id: 7.6 text: "Adjust the streaming-connection-idle-timeout argument" audit_config: "cat /etc/origin/node/node-config.yaml" tests: bin_op: or test_items: - path: "{.kubeletArguments.streaming-connection-idle-timeout}" set: false - path: "{.kubeletArguments.streaming-connection-idle-timeout}" compare: op: eq value: "5m" remediation: | Edit the Openshift node config file /etc/origin/node/node-config.yaml and set the streaming-connection-timeout value like the following in node-config.yaml. kubeletArguments:  streaming-connection-idle-timeout:    - "5m" scored: true - id: 7.7 text: "Verify the OpenShift defaults for the protect-kernel-defaults argument" type: "skip" scored: true - id: 7.8 text: "Verify the OpenShift default value of true for the make-iptables-util-chains argument" audit_config: "cat /etc/origin/node/node-config.yaml" tests: bin_op: or test_items: - path: "{.kubeletArguments.make-iptables-util-chains}" set: false - path: "{.kubeletArguments.make-iptables-util-chains}" compare: op: eq value: "true" remediation: | Edit the Openshift node config file /etc/origin/node/node-config.yaml and reset make-iptables-util-chains to the OpenShift default value of true. scored: true - id: 7.9 text: "Verify that the --keep-terminated-pod-volumes argument is set to false" audit_config: "cat /etc/origin/node/node-config.yaml" tests: bin_op: or test_items: - path: "{.kubeletArguments.keep-terminated-pod-volumes}" set: false - path: "{.kubeletArguments.keep-terminated-pod-volumes}" compare: op: eq value: "false" remediation: | Reset to the OpenShift defaults scored: true - id: 7.10 text: "Verify the OpenShift defaults for the hostname-override argument" type: "skip" scored: true - id: 7.11 text: "Set the --event-qps argument to 0" audit_config: "cat /etc/origin/node/node-config.yaml" tests: bin_op: or test_items: - path: "{.kubeletArguments.event-qps}" set: false - path: "{.kubeletArguments.event-qps}" compare: op: eq value: "0" remediation: | Edit the Openshift node config file /etc/origin/node/node-config.yaml set the event-qps argument to 0 in the kubeletArguments section of. scored: true - id: 7.12 text: "Verify the OpenShift cert-dir flag for HTTPS traffic" audit_config: "cat /etc/origin/node/node-config.yaml" tests: test_items: - path: "{.kubeletArguments.cert-dir}" compare: op: has value: "/etc/origin/node/certificates" remediation: | Reset to the OpenShift default values. scored: true - id: 7.13 text: "Verify the OpenShift default of 0 for the cadvisor-port argument" audit_config: "cat /etc/origin/node/node-config.yaml" tests: bin_op: or test_items: - path: "{.kubeletArguments.cadvisor-port}" set: false - path: "{.kubeletArguments.cadvisor-port}" compare: op: eq value: "0" remediation: | Edit the Openshift node config file /etc/origin/node/node-config.yaml and remove the cadvisor-port flag if it is set in the kubeletArguments section. scored: true - id: 7.14 text: "Verify that the RotateKubeletClientCertificate argument is set to true" audit_config: "cat /etc/origin/node/node-config.yaml" tests: test_items: - path: "{.kubeletArguments.feature-gates}" compare: op: has value: "RotateKubeletClientCertificate=true" remediation: | Edit the Openshift node config file /etc/origin/node/node-config.yaml and set RotateKubeletClientCertificate to true. scored: true - id: 7.15 text: "Verify that the RotateKubeletServerCertificate argument is set to true" audit_config: "cat /etc/origin/node/node-config.yaml" tests: test_items: - path: "{.kubeletArguments.feature-gates}" compare: op: has value: "RotateKubeletServerCertificate=true" remediation: | Edit the Openshift node config file /etc/origin/node/node-config.yaml and set RotateKubeletServerCertificate to true. scored: true - id: 8 text: "Configuration Files" checks: - id: 8.1 text: "Verify the OpenShift default permissions for the kubelet.conf file" audit: "stat -c permissions=%a /etc/origin/node/node.kubeconfig" tests: test_items: - flag: "permissions" compare: op: bitmask value: "644" remediation: | Run the below command on each worker node. chmod 644 /etc/origin/node/node.kubeconfig scored: true - id: 8.2 text: "Verify the kubeconfig file ownership of root:root" audit: "stat -c %U:%G /etc/origin/node/node.kubeconfig" tests: test_items: - flag: "root:root" remediation: | Run the below command on each worker node. chown root:root /etc/origin/node/node.kubeconfig scored: true - id: 8.3 text: "Verify the kubelet service file permissions of 644" audit: "stat -c permissions=%a $kubeletsvc" tests: test_items: - flag: "permissions" compare: op: bitmask value: "644" remediation: | Run the below command on each worker node. chmod 644 $kubeletsvc scored: true - id: 8.4 text: "Verify the kubelet service file ownership of root:root" audit: "stat -c %U:%G $kubeletsvc" tests: test_items: - flag: "root:root" remediation: | Run the below command on each worker node. chown root:root $kubeletsvc scored: true - id: 8.5 text: "Verify the OpenShift default permissions for the proxy kubeconfig file" audit: "stat -c permissions=%a /etc/origin/node/node.kubeconfig" tests: test_items: - flag: "permissions" compare: op: bitmask value: "644" remediation: | Run the below command on each worker node. chmod 644 /etc/origin/node/node.kubeconfig scored: true - id: 8.6 text: "Verify the proxy kubeconfig file ownership of root:root" audit: "stat -c %U:%G /etc/origin/node/node.kubeconfig" tests: test_items: - flag: "root:root" remediation: | Run the below command on each worker node. chown root:root /etc/origin/node/node.kubeconfig scored: true - id: 8.7 text: "Verify the OpenShift default permissions for the certificate authorities file." audit: "stat -c permissions=%a /etc/origin/node/client-ca.crt" tests: test_items: - flag: "permissions" compare: op: bitmask value: "644" remediation: | Run the below command on each worker node. chmod 644 /etc/origin/node/client-ca.crt scored: true - id: 8.8 text: "Verify the client certificate authorities file ownership of root:root" audit: "stat -c %U:%G /etc/origin/node/client-ca.crt" tests: test_items: - flag: "root:root" remediation: | Run the below command on each worker node. chown root:root /etc/origin/node/client-ca.crt scored: true ================================================ FILE: cfg/rh-1.0/config.yaml ================================================ --- ## Version-specific settings that override the values in cfg/config.yaml ================================================ FILE: cfg/rh-1.0/controlplane.yaml ================================================ --- controls: version: rh-1.0 id: 3 text: "Control Plane Configuration" type: "controlplane" groups: - id: 3.1 text: "Authentication and Authorization" checks: - id: 3.1.1 text: "Client certificate authentication should not be used for users (Manual)" audit: | # To verify user authentication is enabled oc describe authentication # To verify that an identity provider is configured oc get identity # To verify that a custom cluster-admin user exists oc get clusterrolebindings -o=custom-columns=NAME:.metadata.name,ROLE:.roleRef.name,SUBJECT:.subjects[*].kind | grep cluster-admin | grep User # To verity that kbueadmin is removed, no results should be returned oc get secrets kubeadmin -n kube-system type: manual remediation: | Configure an identity provider for the OpenShift cluster. Understanding identity provider configuration | Authentication | OpenShift Container Platform 4.5. Once an identity provider has been defined, you can use RBAC to define and apply permissions. After you define an identity provider and create a new cluster-admin user, remove the kubeadmin user to improve cluster security. scored: false - id: 3.2 text: "Logging" checks: - id: 3.2.1 text: "Ensure that a minimal audit policy is created (Manual)" audit: | #To view kube apiserver log files oc adm node-logs --role=master --path=kube-apiserver/ #To view openshift apiserver log files oc adm node-logs --role=master --path=openshift-apiserver/ #To verify kube apiserver audit config oc get configmap config -n openshift-kube-apiserver -ojson | jq -r '.data["config.yaml"]' | jq '.auditConfig[]?' #To verify openshift apiserver audit config oc get configmap config -n openshift-apiserver -ojson | jq -r '.data["config.yaml"]' | jq '.auditConfig[]?' type: manual remediation: | No remediation required. scored: false - id: 3.2.2 text: "Ensure that the audit policy covers key security concerns (Manual)" audit: | #To verify openshift apiserver audit config oc get configmap config -n openshift-kube-apiserver -ojson | jq -r '.data["config.yaml"]' | jq '.auditConfig.policyConfiguration.rules[]?' #To verify kube apiserver audit config oc get configmap config -n openshift-apiserver -ojson | jq -r '.data["config.yaml"]' | jq '.auditConfig.policyConfiguration.rules[]?' type: manual remediation: | In OpenShift 4.6 and higher, if appropriate for your needs, modify the audit policy. scored: false ================================================ FILE: cfg/rh-1.0/etcd.yaml ================================================ --- controls: version: rh-1.0 id: 2 text: "Etcd Node Configuration" type: "etcd" groups: - id: 2 text: "Etcd Node Configuration Files" checks: - id: 2.1 text: "Ensure that the --cert-file and --key-file arguments are set as appropriate (Manual)" audit: | # Get the node name where the pod is running NODE_NAME=$(oc get pod "$HOSTNAME" -o=jsonpath='{.spec.nodeName}') # Get the pod name in the openshift-etcd namespace POD_NAME=$(oc get pods -n openshift-etcd -l app=etcd --field-selector spec.nodeName="$NODE_NAME" -o jsonpath='{.items[0].metadata.name}' 2>/dev/null) if [ -z "$POD_NAME" ]; then echo "No matching file found on the current node." else # Execute the stat command oc exec -n openshift-etcd -c etcd "$POD_NAME" -- ps -o command= -C etcd | sed 's/.*\(--cert-file=[^ ]*\).*/\1/' oc exec -n openshift-etcd -c etcd "$POD_NAME" -- ps -o command= -C etcd | sed 's/.*\(--key-file=[^ ]*\).*/\1/' fi use_multiple_values: true tests: test_items: - flag: "file" compare: op: regex value: '\/etc\/kubernetes\/static-pod-certs\/secrets\/etcd-all-serving\/etcd-serving-.*\.(?:crt|key)' remediation: | OpenShift does not use the etcd-certfile or etcd-keyfile flags. Certificates for etcd are managed by the etcd cluster operator. scored: false - id: 2.2 text: "Ensure that the --client-cert-auth argument is set to true (Manual)" audit: | # Get the node name where the pod is running NODE_NAME=$(oc get pod "$HOSTNAME" -o=jsonpath='{.spec.nodeName}') # Get the pod name in the openshift-etcd namespace POD_NAME=$(oc get pods -n openshift-etcd -l app=etcd --field-selector spec.nodeName="$NODE_NAME" -o jsonpath='{.items[0].metadata.name}' 2>/dev/null) if [ -z "$POD_NAME" ]; then echo "No matching file found on the current node." else # Execute the stat command oc exec -n openshift-etcd -c etcd "$POD_NAME" -- ps -o command= -C etcd | sed 's/.*\(--client-cert-auth=[^ ]*\).*/\1/' fi use_multiple_values: true tests: test_items: - flag: "--client-cert-auth" compare: op: eq value: true remediation: | This setting is managed by the cluster etcd operator. No remediation required." scored: false - id: 2.3 text: "Ensure that the --auto-tls argument is not set to true (Manual)" audit: | # Returns 0 if found, 1 if not found # Get the node name where the pod is running NODE_NAME=$(oc get pod "$HOSTNAME" -o=jsonpath='{.spec.nodeName}') # Get the pod name in the openshift-etcd namespace POD_NAME=$(oc get pods -n openshift-etcd -l app=etcd --field-selector spec.nodeName="$NODE_NAME" -o jsonpath='{.items[0].metadata.name}' 2>/dev/null) if [ -z "$POD_NAME" ]; then echo "No matching file found on the current node." else # Execute the stat command oc exec -n openshift-etcd -c etcd "$POD_NAME" -- ps -o command= -C etcd | grep -- --auto-tls=true 2>/dev/null ; echo exit_code=$? fi use_multiple_values: true tests: test_items: - flag: "exit_code" compare: op: eq value: "1" remediation: | This setting is managed by the cluster etcd operator. No remediation required. scored: false - id: 2.4 text: "Ensure that the --peer-cert-file and --peer-key-file arguments are set as appropriate (Manual)" audit: | # Get the node name where the pod is running NODE_NAME=$(oc get pod "$HOSTNAME" -o=jsonpath='{.spec.nodeName}') # Get the pod name in the openshift-etcd namespace POD_NAME=$(oc get pods -n openshift-etcd -l app=etcd --field-selector spec.nodeName="$NODE_NAME" -o jsonpath='{.items[0].metadata.name}' 2>/dev/null) if [ -z "$POD_NAME" ]; then echo "No matching file found on the current node." else # Execute the stat command oc exec -n openshift-etcd -c etcd "$POD_NAME" -- ps -o command= -C etcd | sed 's/.*\(--peer-cert-file=[^ ]*\).*/\1/' oc exec -n openshift-etcd -c etcd "$POD_NAME" -- ps -o command= -C etcd | sed 's/.*\(--peer-key-file=[^ ]*\).*/\1/' fi use_multiple_values: true tests: test_items: - flag: "file" compare: op: regex value: '\/etc\/kubernetes\/static-pod-certs\/secrets\/etcd-all-peer\/etcd-peer-.*\.(?:crt|key)' remediation: | None. This configuration is managed by the etcd operator. scored: false - id: 2.5 text: "Ensure that the --peer-client-cert-auth argument is set to true (Manual)" audit: | # Get the node name where the pod is running NODE_NAME=$(oc get pod "$HOSTNAME" -o=jsonpath='{.spec.nodeName}') # Get the pod name in the openshift-etcd namespace POD_NAME=$(oc get pods -n openshift-etcd -l app=etcd --field-selector spec.nodeName="$NODE_NAME" -o jsonpath='{.items[0].metadata.name}' 2>/dev/null) if [ -z "$POD_NAME" ]; then echo "No matching file found on the current node." else # Execute the stat command oc exec -n openshift-etcd -c etcd "$POD_NAME" -- ps -o command= -C etcd | sed 's/.*\(--peer-client-cert-auth=[^ ]*\).*/\1/' fi use_multiple_values: true tests: test_items: - flag: "--peer-client-cert-auth" compare: op: eq value: true remediation: | This setting is managed by the cluster etcd operator. No remediation required. scored: false - id: 2.6 text: "Ensure that the --peer-auto-tls argument is not set to true (Manual)" audit: | # Returns 0 if found, 1 if not found # Get the node name where the pod is running NODE_NAME=$(oc get pod "$HOSTNAME" -o=jsonpath='{.spec.nodeName}') # Get the pod name in the openshift-etcd namespace POD_NAME=$(oc get pods -n openshift-etcd -l app=etcd --field-selector spec.nodeName="$NODE_NAME" -o jsonpath='{.items[0].metadata.name}' 2>/dev/null) if [ -z "$POD_NAME" ]; then echo "No matching file found on the current node." else # Execute the stat command oc exec -n openshift-etcd -c etcd "$POD_NAME" -- ps -o command= -C etcd | grep -- --peer-auto-tls=true 2>/dev/null ; echo exit_code=$? fi use_multiple_values: true tests: test_items: - flag: "exit_code" compare: op: eq value: "1" remediation: | This setting is managed by the cluster etcd operator. No remediation required. scored: false - id: 2.7 text: "Ensure that a unique Certificate Authority is used for etcd (Manual)" audit: | # Get the node name where the pod is running NODE_NAME=$(oc get pod "$HOSTNAME" -o=jsonpath='{.spec.nodeName}') # Get the pod name in the openshift-etcd namespace POD_NAME=$(oc get pods -n openshift-etcd -l app=etcd --field-selector spec.nodeName="$NODE_NAME" -o jsonpath='{.items[0].metadata.name}' 2>/dev/null) if [ -z "$POD_NAME" ]; then echo "No matching file found on the current node." else # Execute the stat command oc exec -n openshift-etcd -c etcd "$POD_NAME" -- ps -o command= -C etcd | sed 's/.*\(--trusted-ca-file=[^ ]*\).*/\1/' oc exec -n openshift-etcd -c etcd "$POD_NAME" -- ps -o command= -C etcd | sed 's/.*\(--peer-trusted-ca-file=[^ ]*\).*/\1/' fi use_multiple_values: true tests: test_items: - flag: "file" compare: op: regex value: '\/etc\/kubernetes\/static-pod-certs\/configmaps\/etcd-(?:serving|peer-client)-ca\/ca-bundle\.(?:crt|key)' remediation: | None required. Certificates for etcd are managed by the OpenShift cluster etcd operator. scored: false ================================================ FILE: cfg/rh-1.0/master.yaml ================================================ --- controls: version: rh-1.0 id: 1 text: "Master Node Security Configuration" type: "master" groups: - id: 1.1 text: "Master Node Configuration Files" checks: - id: 1.1.1 text: "Ensure that the API server pod specification file permissions are set to 644 or more restrictive (Manual)" audit: | # Get the node name where the pod is running NODE_NAME=$(oc get pod "$HOSTNAME" -o=jsonpath='{.spec.nodeName}') # Get the pod name in the openshift-kube-apiserver namespace POD_NAME=$(oc get pods -n openshift-kube-apiserver -l app=openshift-kube-apiserver --field-selector spec.nodeName="$NODE_NAME" -o jsonpath='{.items[0].metadata.name}' 2>/dev/null) if [ -z "$POD_NAME" ]; then echo "No matching pods found on the current node." else # Execute the stat command oc exec -n openshift-kube-apiserver "$POD_NAME" -- stat -c "$POD_NAME %n permissions=%a" /etc/kubernetes/static-pod-resources/kube-apiserver-pod.yaml fi use_multiple_values: true tests: test_items: - flag: "permissions" compare: op: bitmask value: "644" remediation: | No remediation required; file permissions are managed by the operator. scored: false - id: 1.1.2 text: "Ensure that the API server pod specification file ownership is set to root:root (Manual)" audit: | # Get the node name where the pod is running NODE_NAME=$(oc get pod "$HOSTNAME" -o=jsonpath='{.spec.nodeName}') # Get the pod name in the openshift-kube-apiserver namespace POD_NAME=$(oc get pods -n openshift-kube-apiserver -l app=openshift-kube-apiserver --field-selector spec.nodeName="$NODE_NAME" -o jsonpath='{.items[0].metadata.name}' 2>/dev/null) if [ -z "$POD_NAME" ]; then echo "No matching pods found on the current node." else # Execute the stat command oc exec -n openshift-kube-apiserver "$POD_NAME" -- stat -c "$POD_NAME %n %U:%G" /etc/kubernetes/static-pod-resources/kube-apiserver-pod.yaml fi use_multiple_values: true tests: test_items: - flag: "root:root" remediation: | No remediation required; file permissions are managed by the operator. scored: false - id: 1.1.3 text: "Ensure that the controller manager pod specification file permissions are set to 644 or more restrictive (Manual)" audit: | # Get the node name where the pod is running NODE_NAME=$(oc get pod "$HOSTNAME" -o=jsonpath='{.spec.nodeName}') # Get the pod name in the openshift-kube-controller-manager namespace POD_NAME=$(oc get pods -n openshift-kube-controller-manager -l app=kube-controller-manager --field-selector spec.nodeName="$NODE_NAME" -o jsonpath='{.items[0].metadata.name}' 2>/dev/null) if [ -z "$POD_NAME" ]; then echo "No matching pods found on the current node." else # Execute the stat command oc exec -n openshift-kube-controller-manager "$POD_NAME" -- stat -c "$POD_NAME %n permissions=%a" /etc/kubernetes/static-pod-resources/kube-controller-manager-pod.yaml fi use_multiple_values: true tests: test_items: - flag: "permissions" compare: op: bitmask value: "644" remediation: | No remediation required; file permissions are managed by the operator. scored: false - id: 1.1.4 text: "Ensure that the controller manager pod specification file ownership is set to root:root (Manual)" audit: | # Get the node name where the pod is running NODE_NAME=$(oc get pod "$HOSTNAME" -o=jsonpath='{.spec.nodeName}') # Get the pod name in the openshift-kube-controller-manager namespace POD_NAME=$(oc get pods -n openshift-kube-controller-manager -l app=kube-controller-manager --field-selector spec.nodeName="$NODE_NAME" -o jsonpath='{.items[0].metadata.name}' 2>/dev/null) if [ -z "$POD_NAME" ]; then echo "No matching pods found on the current node." else # Execute the stat command oc exec -n openshift-kube-controller-manager "$POD_NAME" -- stat -c "$POD_NAME %n %U:%G" /etc/kubernetes/static-pod-resources/kube-controller-manager-pod.yaml fi use_multiple_values: true tests: test_items: - flag: "root:root" remediation: | No remediation required; file permissions are managed by the operator. scored: false - id: 1.1.5 text: "Ensure that the scheduler pod specification file permissions are set to 644 or more restrictive (Manual)" audit: | # Get the node name where the pod is running NODE_NAME=$(oc get pod "$HOSTNAME" -o=jsonpath='{.spec.nodeName}') # Get the pod name in the openshift-kube-scheduler namespace POD_NAME=$(oc get pods -n openshift-kube-scheduler -l app=openshift-kube-scheduler --field-selector spec.nodeName="$NODE_NAME" -o jsonpath='{.items[0].metadata.name}' 2>/dev/null) if [ -z "$POD_NAME" ]; then echo "No matching pods found on the current node." else # Execute the stat command oc exec -n openshift-kube-scheduler "$POD_NAME" -- stat -c "$POD_NAME %n permissions=%a" /etc/kubernetes/static-pod-resources/kube-scheduler-pod.yaml fi use_multiple_values: true tests: test_items: - flag: "permissions" compare: op: bitmask value: "644" remediation: | No remediation required; file permissions are managed by the operator. scored: false - id: 1.1.6 text: "Ensure that the scheduler pod specification file ownership is set to root:root (Manual))" audit: | # Get the node name where the pod is running NODE_NAME=$(oc get pod "$HOSTNAME" -o=jsonpath='{.spec.nodeName}') # Get the pod name in the openshift-kube-scheduler namespace POD_NAME=$(oc get pods -n openshift-kube-scheduler -l app=openshift-kube-scheduler --field-selector spec.nodeName="$NODE_NAME" -o jsonpath='{.items[0].metadata.name}' 2>/dev/null) if [ -z "$POD_NAME" ]; then echo "No matching pods found on the current node." else # Execute the stat command oc exec -n openshift-kube-scheduler "$POD_NAME" -- stat -c "$POD_NAME %n %U:%G" /etc/kubernetes/static-pod-resources/kube-scheduler-pod.yaml fi use_multiple_values: true tests: test_items: - flag: "root:root" remediation: | No remediation required; file permissions are managed by the operator. scored: false - id: 1.1.7 text: "Ensure that the etcd pod specification file permissions are set to 644 or more restrictive (Manual))" audit: | # Get the node name where the pod is running NODE_NAME=$(oc get pod "$HOSTNAME" -o=jsonpath='{.spec.nodeName}') # Get the pod name in the openshift-etcd namespace POD_NAME=$(oc get pods -n openshift-etcd -l app=etcd --field-selector spec.nodeName="$NODE_NAME" -o jsonpath='{.items[0].metadata.name}' 2>/dev/null) if [ -z "$POD_NAME" ]; then echo "No matching pods found on the current node." else # Execute the stat command oc rsh -n openshift-etcd "$POD_NAME" stat -c "$POD_NAME %n permissions=%a" /etc/kubernetes/manifests/etcd-pod.yaml fi use_multiple_values: true tests: test_items: - flag: "permissions" compare: op: bitmask value: "644" remediation: | No remediation required; file permissions are managed by the operator. scored: false - id: 1.1.8 text: "Ensure that the etcd pod specification file ownership is set to root:root (Manual)" audit: | # Get the node name where the pod is running NODE_NAME=$(oc get pod "$HOSTNAME" -o=jsonpath='{.spec.nodeName}') # Get the pod name in the openshift-etcd namespace POD_NAME=$(oc get pods -n openshift-etcd -l app=etcd --field-selector spec.nodeName="$NODE_NAME" -o jsonpath='{.items[0].metadata.name}' 2>/dev/null) if [ -z "$POD_NAME" ]; then echo "No matching pods found on the current node." else # Execute the stat command oc rsh -n openshift-etcd "$POD_NAME" stat -c "$POD_NAME %n %U:%G" /etc/kubernetes/manifests/etcd-pod.yaml fi use_multiple_values: true tests: test_items: - flag: "root:root" remediation: | No remediation required; file permissions are managed by the operator. scored: false - id: 1.1.9 text: "Ensure that the Container Network Interface file permissions are set to 644 or more restrictive (Manual)" audit: | # Get the node name where the pod is running NODE_NAME=$(oc get pod "$HOSTNAME" -o=jsonpath='{.spec.nodeName}') # For CNI multus # Get the pod name in the openshift-multus namespace POD_NAME=$(oc get pods -n openshift-multus -l app=multus --field-selector spec.nodeName="$NODE_NAME" -o jsonpath='{.items[0].metadata.name}' 2>/dev/null) if [ -z "$POD_NAME" ]; then echo "No matching pods found on the current node." else # Execute the stat command oc exec -n openshift-multus "$POD_NAME" -- /bin/bash -c "stat -c \"$i %n permissions=%a\" /host/etc/cni/net.d/*.conf"; 2>/dev/null oc exec -n openshift-multus "$POD_NAME" -- /bin/bash -c "stat -c \"$i %n permissions=%a\" /host/var/run/multus/cni/net.d/*.conf"; 2>/dev/null fi # For SDN pods POD_NAME=$(oc get pods -n openshift-sdn -l app=sdn --field-selector spec.nodeName="$NODE_NAME" -o jsonpath='{.items[0].metadata.name}' 2>/dev/null) if [ -z "$POD_NAME" ]; then echo "No matching pods found on the current node." else # Execute the stat command oc exec -n openshift-sdn "$POD_NAME" -- find /var/lib/cni/networks/openshift-sdn -type f -exec stat -c "$i %n permissions=%a" {} \; 2>/dev/null oc exec -n openshift-sdn "$POD_NAME" -- find /var/run/openshift-sdn -type f -exec stat -c "$i %n permissions=%a" {} \; 2>/dev/null fi # For OVS pods POD_NAME=$(oc get pods -n openshift-sdn -l app=ovs --field-selector spec.nodeName="$NODE_NAME" -o jsonpath='{.items[0].metadata.name}' 2>/dev/null) if [ -z "$POD_NAME" ]; then echo "No matching pods found on the current node." else # Execute the stat command oc exec -n openshift-sdn "$POD_NAME" -- find /var/run/openvswitch -type f -exec stat -c "$i %n permissions=%a" {} \; 2>/dev/null oc exec -n openshift-sdn "$POD_NAME" -- find /etc/openvswitch -type f -exec stat -c "$i %n permissions=%a" {} \; 2>/dev/null oc exec -n openshift-sdn "$POD_NAME" -- find /run/openvswitch -type f -exec stat -c "$i %n permissions=%a" {} \; 2>/dev/null fi use_multiple_values: true tests: test_items: - flag: "permissions" compare: op: bitmask value: "644" remediation: | No remediation required; file permissions are managed by the operator. scored: false - id: 1.1.10 text: "Ensure that the Container Network Interface file ownership is set to root:root (Manual)" audit: | # Get the node name where the pod is running NODE_NAME=$(oc get pod "$HOSTNAME" -o=jsonpath='{.spec.nodeName}') # For CNI multus # Get the pod name in the openshift-multus namespace POD_NAME=$(oc get pods -n openshift-multus -l app=multus --field-selector spec.nodeName="$NODE_NAME" -o jsonpath='{.items[0].metadata.name}' 2>/dev/null) if [ -z "$POD_NAME" ]; then echo "No matching pods found on the current node." else # Execute the stat command oc exec -n openshift-multus "$POD_NAME" -- /bin/bash -c "stat -c '$i %n %U:%G' /host/etc/cni/net.d/*.conf" 2>/dev/null oc exec -n openshift-multus $i -- /bin/bash -c "stat -c '$i %n %U:%G' /host/var/run/multus/cni/net.d/*.conf" 2>/dev/null fi # For SDN pods POD_NAME=$(oc get pods -n openshift-sdn -l app=sdn --field-selector spec.nodeName="$NODE_NAME" -o jsonpath='{.items[0].metadata.name}' 2>/dev/null) if [ -z "$POD_NAME" ]; then echo "No matching pods found on the current node." else # Execute the stat command oc exec -n openshift-sdn "$POD_NAME" -- find /var/lib/cni/networks/openshift-sdn -type f -exec stat -c "$i %n %U:%G" {} \; 2>/dev/null oc exec -n openshift-sdn "$POD_NAME" -- find /var/run/openshift-sdn -type f -exec stat -c "$i %n %U:%G" {} \; 2>/dev/null fi # For OVS pods in 4.5 POD_NAME=$(oc get pods -n openshift-sdn -l app=ovs --field-selector spec.nodeName="$NODE_NAME" -o jsonpath='{.items[0].metadata.name}' 2>/dev/null) if [ -z "$POD_NAME" ]; then echo "No matching pods found on the current node." else # Execute the stat command oc exec -n openshift-sdn "$POD_NAME" -- find /var/run/openvswitch -type f -exec stat -c "$i %n %U:%G" {} \; 2>/dev/null oc exec -n openshift-sdn "$POD_NAME" -- find /etc/openvswitch -type f -exec stat -c "$i %n %U:%G" {} \; 2>/dev/null oc exec -n openshift-sdn "$POD_NAME" -- find /run/openvswitch -type f -exec stat -c "$i %n %U:%G" {} \; 2>/dev/null fi use_multiple_values: true tests: test_items: - flag: "root:root" remediation: | No remediation required; file permissions are managed by the operator. scored: false - id: 1.1.11 text: "Ensure that the etcd data directory permissions are set to 700 or more restrictive (Manual)" audit: | # Get the node name where the pod is running NODE_NAME=$(oc get pod "$HOSTNAME" -o=jsonpath='{.spec.nodeName}') # Get the pod name in the openshift-etcd namespace POD_NAME=$(oc get pods -n openshift-etcd -l app=etcd --field-selector spec.nodeName="$NODE_NAME" -o jsonpath='{.items[0].metadata.name}' 2>/dev/null) if [ -z "$POD_NAME" ]; then echo "No matching pods found on the current node." else # Execute the stat command oc exec -n openshift-etcd "$POD_NAME" -- stat -c "$POD_NAME %n permissions=%a" /var/lib/etcd/member fi use_multiple_values: true tests: test_items: - flag: "permissions" compare: op: bitmask value: "700" remediation: | No remediation required; file permissions are managed by the operator. scored: false - id: 1.1.12 text: "Ensure that the etcd data directory ownership is set to etcd:etcd (Manual)" audit: | # Get the node name where the pod is running NODE_NAME=$(oc get pod "$HOSTNAME" -o=jsonpath='{.spec.nodeName}') # Get the pod name in the openshift-etcd namespace POD_NAME=$(oc get pods -n openshift-etcd -l app=etcd --field-selector spec.nodeName="$NODE_NAME" -o jsonpath='{.items[0].metadata.name}' 2>/dev/null) if [ -z "$POD_NAME" ]; then echo "No matching pods found on the current node." else # Execute the stat command oc exec -n openshift-etcd "$POD_NAME" -- stat -c "$POD_NAME %n %U:%G" /var/lib/etcd/member fi use_multiple_values: true tests: test_items: - flag: "root:root" remediation: | No remediation required; file permissions are managed by the operator. scored: false - id: 1.1.13 text: "Ensure that the admin.conf file permissions are set to 644 or more restrictive (Manual))" audit: | NODE_NAME=$(oc get pod $HOSTNAME -o=jsonpath='{.spec.nodeName}') oc debug node/$NODE_NAME -- chroot /host stat -c "$NODE_NAME %n permissions=%a" /etc/kubernetes/kubeconfig 2> /dev/null use_multiple_values: true tests: test_items: - flag: "permissions" compare: op: bitmask value: "644" remediation: | No remediation required; file permissions are managed by the operator. scored: false - id: 1.1.14 text: "Ensure that the admin.conf file ownership is set to root:root (Manual)" audit: | NODE_NAME=$(oc get pod $HOSTNAME -o=jsonpath='{.spec.nodeName}') oc debug node/$NODE_NAME -- chroot /host stat -c "$NODE_NAME %n %U:%G" /etc/kubernetes/kubeconfig 2> /dev/null use_multiple_values: true tests: test_items: - flag: "root:root" remediation: | No remediation required; file permissions are managed by the operator. scored: false - id: 1.1.15 text: "Ensure that the scheduler.conf file permissions are set to 644 or more restrictive (Manual)" audit: | # Get the node name where the pod is running NODE_NAME=$(oc get pod "$HOSTNAME" -o=jsonpath='{.spec.nodeName}') # Get the pod name in the openshift-kube-scheduler namespace POD_NAME=$(oc get pods -n openshift-kube-scheduler -l app=openshift-kube-scheduler --field-selector spec.nodeName="$NODE_NAME" -o jsonpath='{.items[0].metadata.name}' 2>/dev/null) if [ -z "$POD_NAME" ]; then echo "No matching pods found on the current node." else # Execute the stat command oc exec -n openshift-kube-scheduler "$POD_NAME" -- stat -c "$POD_NAME %n permissions=%a" /etc/kubernetes/static-pod-resources/configmaps/scheduler-kubeconfig/kubeconfig fi use_multiple_values: true tests: test_items: - flag: "permissions" compare: op: bitmask value: "644" remediation: | No remediation required; file permissions are managed by the operator. scored: false - id: 1.1.16 text: "Ensure that the scheduler.conf file ownership is set to root:root (Manual)" audit: | # Get the node name where the pod is running NODE_NAME=$(oc get pod "$HOSTNAME" -o=jsonpath='{.spec.nodeName}') # Get the pod name in the openshift-kube-scheduler namespace POD_NAME=$(oc get pods -n openshift-kube-scheduler -l app=openshift-kube-scheduler --field-selector spec.nodeName="$NODE_NAME" -o jsonpath='{.items[0].metadata.name}' 2>/dev/null) if [ -z "$POD_NAME" ]; then echo "No matching pods found on the current node." else # Execute the stat command oc exec -n openshift-kube-scheduler "$POD_NAME" -- stat -c "$POD_NAME %n %U:%G" /etc/kubernetes/static-pod-resources/configmaps/scheduler-kubeconfig/kubeconfig fi use_multiple_values: true tests: test_items: - flag: "root:root" remediation: | No remediation required; file permissions are managed by the operator. scored: false - id: 1.1.17 text: "Ensure that the controller-manager.conf file permissions are set to 644 or more restrictive (Manual)" audit: | # Get the node name where the pod is running NODE_NAME=$(oc get pod "$HOSTNAME" -o=jsonpath='{.spec.nodeName}') # Get the pod name in the openshift-kube-controller-manager namespace POD_NAME=$(oc get pods -n openshift-kube-controller-manager -l app=kube-controller-manager --field-selector spec.nodeName="$NODE_NAME" -o jsonpath='{.items[0].metadata.name}' 2>/dev/null) if [ -z "$POD_NAME" ]; then echo "No matching pods found on the current node." else # Execute the stat command oc exec -n openshift-kube-controller-manager "$POD_NAME" -- stat -c "$POD_NAME %n permissions=%a" /etc/kubernetes/static-pod-resources/configmaps/controller-manager-kubeconfig/kubeconfig fi use_multiple_values: true tests: test_items: - flag: "permissions" compare: op: bitmask value: "644" remediation: | No remediation required; file permissions are managed by the operator. scored: false - id: 1.1.18 text: "Ensure that the controller-manager.conf file ownership is set to root:root (Manual)" audit: | # Get the node name where the pod is running NODE_NAME=$(oc get pod "$HOSTNAME" -o=jsonpath='{.spec.nodeName}') # Get the pod name in the openshift-kube-controller-manager namespace POD_NAME=$(oc get pods -n openshift-kube-controller-manager -l app=kube-controller-manager --field-selector spec.nodeName="$NODE_NAME" -o jsonpath='{.items[0].metadata.name}' 2>/dev/null) if [ -z "$POD_NAME" ]; then echo "No matching pods found on the current node." else # Execute the stat command oc exec -n openshift-kube-controller-manager "$POD_NAME" -- stat -c "$POD_NAME %n %U:%G" /etc/kubernetes/static-pod-resources/configmaps/controller-manager-kubeconfig/kubeconfig fi use_multiple_values: true tests: test_items: - flag: "root:root" remediation: | No remediation required; file permissions are managed by the operator. scored: false - id: 1.1.19 text: "Ensure that the Kubernetes PKI directory and file ownership is set to root:root (Manual)" audit: | # Should return root:root for all files and directories # Get the node name where the pod is running NODE_NAME=$(oc get pod "$HOSTNAME" -o=jsonpath='{.spec.nodeName}') # Get the pod name in the openshift-kube-controller-manager namespace POD_NAME=$(oc get pods -n openshift-kube-apiserver -l app=openshift-kube-apiserver --field-selector spec.nodeName="$NODE_NAME" -o jsonpath='{.items[0].metadata.name}' 2>/dev/null) if [ -z "$POD_NAME" ]; then echo "No matching pods found on the current node." else # echo $i static-pod-certs oc exec -n openshift-kube-apiserver "$POD_NAME" -c kube-apiserver -- find /etc/kubernetes/static-pod-certs -type d -wholename '*/secrets*' -exec stat -c "$i %n %U:%G" {} \; oc exec -n openshift-kube-apiserver "$POD_NAME" -c kube-apiserver -- find /etc/kubernetes/static-pod-certs -type f -wholename '*/secrets*' -exec stat -c "$i %n %U:%G" {} \; # echo $i static-pod-resources oc exec -n openshift-kube-apiserver "$POD_NAME" -c kube-apiserver -- find /etc/kubernetes/static-pod-resources -type d -wholename '*/secrets*' -exec stat -c "$i %n %U:%G" {} \; oc exec -n openshift-kube-apiserver "$POD_NAME" -c kube-apiserver -- find /etc/kubernetes/static-pod-resources -type f -wholename '*/secrets*' -exec stat -c "$i %n %U:%G" {} \; fi use_multiple_values: true tests: test_items: - flag: "root:root" remediation: | No remediation required; file permissions are managed by the operator. scored: false - id: 1.1.20 text: "Ensure that the OpenShift PKI certificate file permissions are set to 644 or more restrictive (Manual)" audit: | # Get the node name where the pod is running NODE_NAME=$(oc get pod "$HOSTNAME" -o=jsonpath='{.spec.nodeName}') # Get the pod name in the openshift-kube-apiserver namespace POD_NAME=$(oc get pods -n openshift-kube-apiserver -l app=openshift-kube-apiserver --field-selector spec.nodeName="$NODE_NAME" -o jsonpath='{.items[0].metadata.name}' 2>/dev/null) if [ -z "$POD_NAME" ]; then echo "No matching pods found on the current node." else # Execute the stat command oc exec -n openshift-kube-apiserver "$POD_NAME" -c kube-apiserver -- find /etc/kubernetes/static-pod-certs -type f -wholename '*/secrets/*.crt' -exec stat -c "$POD_NAME %n permissions=%a" {} \; fi use_multiple_values: true tests: test_items: - flag: "permissions" compare: op: bitmask value: "644" remediation: | No remediation required; file permissions are managed by the operator. scored: false - id: 1.1.21 text: "Ensure that the OpenShift PKI key file permissions are set to 600 (Manual)" audit: | # Get the node name where the pod is running NODE_NAME=$(oc get pod "$HOSTNAME" -o=jsonpath='{.spec.nodeName}') # Get the pod name in the openshift-kube-apiserver namespace POD_NAME=$(oc get pods -n openshift-kube-apiserver -l app=openshift-kube-apiserver --field-selector spec.nodeName="$NODE_NAME" -o jsonpath='{.items[0].metadata.name}' 2>/dev/null) if [ -z "$POD_NAME" ]; then echo "No matching pods found on the current node." else # Execute the stat command oc exec -n openshift-kube-apiserver "$POD_NAME" -c kube-apiserver -- find /etc/kubernetes/static-pod-certs -type f -wholename '*/secrets/*.key' -exec stat -c "$POD_NAME %n permissions=%a" {} \; fi use_multiple_values: true tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | No remediation required; file permissions are managed by the operator. scored: false - id: 1.2 text: "API Server" checks: - id: 1.2.1 text: "Ensure that anonymous requests are authorized (Manual)" audit: | # To verify that userGroups include system:unauthenticated oc get configmap config -n openshift-kube-apiserver -ojson | jq -r '.data["config.yaml"]' | jq '.auditConfig.policyConfiguration.rules[]?' # To verify that userGroups include system:unauthenticated oc get configmap config -n openshift-apiserver -ojson | jq -r '.data["config.yaml"]' | jq '.auditConfig.policyConfiguration.rules[]?.userGroups' # To verify RBAC is enabled oc get clusterrolebinding oc get clusterrole oc get rolebinding oc get role tests: test_items: - flag: "system:unauthenticated" remediation: | None required. The default configuration should not be modified. scored: false - id: 1.2.2 text: "Ensure that the --basic-auth-file argument is not set (Manual)" audit: | oc -n openshift-kube-apiserver get cm config -o yaml | grep --color "basic-auth" oc -n openshift-apiserver get cm config -o yaml | grep --color "basic-auth" # Add | awk '$3 != "AVAILABLE" { if ($3){print "available=true"}else{print "available=false"} }; to create AVAILABLE = true/false form oc get clusteroperator authentication | awk '$3 != "AVAILABLE" { if ($3){print "available=true"}else{print "available=false"} }' tests: bin_op: and test_items: - flag: "basic-auth-file" set: false - flag: "available" compare: op: eq value: true remediation: | None required. --basic-auth-file cannot be configured on OpenShift. scored: false - id: 1.2.3 text: "Ensure that the --token-auth-file parameter is not set (Manual)" audit: | # Verify that the token-auth-file flag is not present oc get configmap config -n openshift-kube-apiserver -ojson | jq -r '.data["config.yaml"]' | jq '.apiServerArguments' oc get configmap config -n openshift-apiserver -ojson | jq -r '.data["config.yaml"]' | jq '.apiServerArguments' oc get kubeapiservers.operator.openshift.io cluster -o json | jq '.spec.observedConfig.apiServerArguments' #Verify that the authentication operator is running oc get clusteroperator authentication | awk '$3 != "AVAILABLE" { if ($3){print "available=true"}else{print "available=false"} }' tests: bin_op: and test_items: - flag: "token-auth-file" set: false - flag: "available" compare: op: eq value: true remediation: | None is required. scored: false - id: 1.2.4 text: "Use https for kubelet connections (Manual)" audit: | #for 4.5 oc get configmap config -n openshift-kube-apiserver -ojson | jq -r '.data["config.yaml"]' | jq '.kubeletClientInfo' #for 4.6 oc get configmap config -n openshift-kube-apiserver -ojson | jq -r '.data["config.yaml"]' | jq '.apiServerArguments' #for both 4.5 and 4.6 oc -n openshift-apiserver describe secret serving-cert tests: bin_op: and test_items: - flag: "/etc/kubernetes/static-pod-resources/secrets/kubelet-client/tls.crt" - flag: "/etc/kubernetes/static-pod-resources/secrets/kubelet-client/tls.key" remediation: | No remediation is required. OpenShift platform components use X.509 certificates for authentication. OpenShift manages the CAs and certificates for platform components. This is not configurable. scored: false - id: 1.2.5 text: "Ensure that the kubelet uses certificates to authenticate (Manual)" audit: | #for 4.5 oc get configmap config -n openshift-kube-apiserver -ojson | jq -r '.data["config.yaml"]' | jq '.kubeletClientInfo' #for 4.6 oc get configmap config -n openshift-kube-apiserver -ojson | jq -r '.data["config.yaml"]' | jq '.apiServerArguments' #for both 4.5 and 4.6 oc -n openshift-apiserver describe secret serving-cert tests: bin_op: and test_items: - flag: "/etc/kubernetes/static-pod-resources/secrets/kubelet-client/tls.crt" - flag: "/etc/kubernetes/static-pod-resources/secrets/kubelet-client/tls.key" remediation: | No remediation is required. OpenShift platform components use X.509 certificates for authentication. OpenShift manages the CAs and certificates for platform components. This is not configurable. scored: false - id: 1.2.6 text: "Verify that the kubelet certificate authority is set as appropriate (Manual)" audit: | # for 4.5 oc get configmap config -n openshift-kube-apiserver -ojson | jq -r '.data["config.yaml"]' | jq '.kubeletClientInfo' # for 4.6 oc get configmap config -n openshift-kube-apiserver -ojson | jq -r '.data["config.yaml"]' | jq '.apiServerArguments' tests: test_items: - flag: "/etc/kubernetes/static-pod-resources/configmaps/kubelet-serving-ca/ca-bundle.crt" remediation: | No remediation is required. OpenShift platform components use X.509 certificates for authentication. OpenShift manages the CAs and certificates for platform components. This is not configurable. scored: false - id: 1.2.7 text: "Ensure that the --authorization-mode argument is not set to AlwaysAllow (Manual)" audit: | # To verify that the authorization-mode argument is not used oc get configmap config -n openshift-kube-apiserver -ojson | jq -r '.data["config.yaml"]' | jq '.apiServerArguments' oc get configmap config -n openshift-apiserver -ojson | jq -r '.data["config.yaml"]' | jq '.apiServerArguments' #Check that no overrides are configured oc get kubeapiservers.operator.openshift.io cluster -o json | jq -r '.spec.unsupportedConfigOverrides' # To verify RBAC is configured: oc get clusterrolebinding oc get clusterrole oc get rolebinding oc get role audit_config: | oc get configmap config -n openshift-kube-apiserver -ojson | jq -r '.data["config.yaml"]' | jq '.apiServerArguments' tests: bin_op: or test_items: - path: "{.authorization-mode}" compare: op: nothave value: "AlwaysAllow" - path: "{.authorization-mode}" flag: "authorization-mode" set: false remediation: | None. RBAC is always on and the OpenShift API server does not use the values assigned to the flag authorization-mode. scored: false - id: 1.2.8 text: "Verify that the Node authorizer is enabled (Manual)" audit: | # For OCP 4.5 and earlier verify that authorization-mode is not used oc get configmap config -n openshift-kube-apiserver -ojson | jq -r '.data["config.yaml"]' | jq '.apiServerArguments' oc get kubeapiservers.operator.openshift.io cluster -o json | jq '.spec.observedConfig.apiServerArguments' # For OCP 4.5 and earlier verify that authorization-mode is not used NODE_NAME=$(oc get pod $HOSTNAME -o=jsonpath='{.spec.nodeName}') oc debug node/$NODE_NAME -- chroot /host cat /etc/kubernetes/kubelet.conf | grep authorization-mode 2> /dev/null oc debug node/$NODE_NAME -- chroot /host ps -aux | grep kubelet | grep authorization-mode 2> /dev/null #Check that no overrides are configured oc get kubeapiservers.operator.openshift.io cluster -o json | jq -r '.spec.unsupportedConfigOverrides' audit_config: | oc get configmap config -n openshift-kube-apiserver -ojson | jq -r '.data["config.yaml"]' | jq '.apiServerArguments' tests: bin_op: or test_items: - path: "{.authorization-mode}" compare: op: has value: "Node" - path: "{.authorization-mode}" flag: "authorization-mode" set: false remediation: | No remediation is required. scored: false - id: 1.2.9 text: "Verify that RBAC is enabled (Manual)" audit: | # For 4.5 To verify that the authorization-mode argument is not used oc get configmap config -n openshift-kube-apiserver -ojson | jq -r '.data["config.yaml"]' | jq '.apiServerArguments' oc get configmap config -n openshift-apiserver -ojson | jq -r '.data["config.yaml"]' | jq '.apiServerArguments' #Check that no overrides are configured oc get kubeapiservers.operator.openshift.io cluster -o json | jq -r '.spec.unsupportedConfigOverrides' # To verify RBAC is used oc get clusterrolebinding oc get clusterrole oc get rolebinding oc get role # For 4.6, verify that the authorization-mode argument includes RBAC audit_config: | oc get configmap config -n openshift-kube-apiserver -ojson | jq -r '.data["config.yaml"]' | jq '.apiServerArguments' tests: bin_op: or test_items: - path: "{.authorization-mode}" compare: op: has value: "RBAC" - path: "{.authorization-mode}" flag: "authorization-mode" set: false remediation: | None. It is not possible to disable RBAC. scored: false - id: 1.2.10 text: "Ensure that the APIPriorityAndFairness feature gate is enabled (Manual)" audit: | #Verify the APIPriorityAndFairness feature-gate oc get kubeapiservers.operator.openshift.io cluster -o json | jq '.spec.observedConfig.apiServerArguments' #Verify the set of admission-plugins for OCP 4.6 and higher oc -n openshift-kube-apiserver get configmap config -o json | jq -r '.data."config.yaml"' | jq '.apiServerArguments."enable-admission-plugins"' #Check that no overrides are configured oc get kubeapiservers.operator.openshift.io cluster -o json | jq -r '.spec.unsupportedConfigOverrides' tests: bin_op: and test_items: - flag: "APIPriorityAndFairness=true" - flag: "EventRateLimit" set: false remediation: | No remediation is required scored: false - id: 1.2.11 text: "Ensure that the admission control plugin AlwaysAdmit is not set (Manual)" audit: | #Verify the set of admission-plugins for OCP 4.6 and higher oc -n openshift-kube-apiserver get configmap config -o json | jq -r '.data."config.yaml"' | jq '.apiServerArguments."enable-admission-plugins"' #Check that no overrides are configured oc get kubeapiservers.operator.openshift.io cluster -o json | jq -r '.spec.unsupportedConfigOverrides' tests: test_items: - flag: "AlwaysAdmit" set: false remediation: | No remediation is required. The AlwaysAdmit admission controller cannot be enabled in OpenShift. scored: false - id: 1.2.12 text: "Ensure that the admission control plugin AlwaysPullImages is set (Manual)" audit: | #Verify the set of admissi on-plugins for OCP 4.6 and higher oc -n openshift-kube-apiserver get configmap config -o json | jq -r '.data."config.yaml"' | jq '.apiServerArguments."enable-admission-plugins"' #Check that no overrides are configured oc get kubeapiservers.operator.openshift.io cluster -o json | jq -r '.spec.unsupportedConfigOverrides' tests: test_items: - flag: "AlwaysPullImages" set: false remediation: | None required. scored: false - id: 1.2.13 text: "Ensure that the admission control plugin SecurityContextDeny is not set (Manual)" audit: | #Verify the set of admission-plugins for OCP 4.6 and higher oc -n openshift-kube-apiserver get configmap config -o json | jq -r '.data."config.yaml"' | jq '.apiServerArguments."enable-admission-plugins"' output=$(oc -n openshift-kube-apiserver get configmap config -o json | jq -r '.data."config.yaml"' | jq '.apiServerArguments."enable-admission-plugins"') [ "$output" == "null" ] && echo "ocp 4.5 has SecurityContextDeny and SecurityContextConstraint compiled" || echo $output #Check that no overrides are configured oc get kubeapiservers.operator.openshift.io cluster -o json | jq -r '.spec.unsupportedConfigOverrides' #Verify that SecurityContextConstraints are deployed oc get scc oc describe scc restricted tests: bin_op: and test_items: - flag: "SecurityContextConstraint" set: true - flag: "anyuid" - flag: "hostaccess" - flag: "hostmount-anyuid" - flag: "hostnetwork" - flag: "node-exporter" - flag: "nonroot" - flag: "privileged" - flag: "restricted" remediation: | None required. The Security Context Constraint admission controller cannot be disabled in OpenShift 4. scored: false - id: 1.2.14 text: "Ensure that the admission control plugin ServiceAccount is set (Manual)" audit: | #Verify the list of admission controllers for 4.6 and higher oc -n openshift-kube-apiserver get configmap config -o json | jq -r '.data."config.yaml"' | jq '.apiServerArguments."enable-admission-plugins"' output=$(oc -n openshift-kube-apiserver get configmap config -o json | jq -r '.data."config.yaml"' | jq '.apiServerArguments."enable-admission-plugins"') [ "$output" == "null" ] && echo "ocp 4.5 has ServiceAccount compiled" || echo $output #Check that no overrides are configured oc get kubeapiservers.operator.openshift.io cluster -o json | jq -r '.spec.unsupportedConfigOverrides' #Verify that Service Accounts are present oc get sa -A tests: test_items: - flag: "ServiceAccount" set: true remediation: | None required. OpenShift is configured to use service accounts by default. scored: false - id: 1.2.15 text: "Ensure that the admission control plugin NamespaceLifecycle is set (Manual)" audit: | #Verify the list of admission controllers for 4.6 and higher oc -n openshift-kube-apiserver get configmap config -o json | jq -r '.data."config.yaml"' | jq '.apiServerArguments."enable-admission-plugins"' output=$(oc -n openshift-kube-apiserver get configmap config -o json | jq -r '.data."config.yaml"' | jq '.apiServerArguments."enable-admission-plugins"') [ "$output" == "null" ] && echo "ocp 4.5 has NamespaceLifecycle compiled" || echo $output #Check that no overrides are configured oc get kubeapiservers.operator.openshift.io cluster -o json | jq -r '.spec.unsupportedConfigOverrides' tests: test_items: - flag: "NamespaceLifecycle" remediation: | Ensure that the --disable-admission-plugins parameter does not include NamespaceLifecycle. scored: false - id: 1.2.16 text: "Ensure that the admission control plugin SecurityContextConstraint is set (Manual)" audit: | #Verify the set of admission-plugins for OCP 4.6 and higher oc -n openshift-kube-apiserver get configmap config -o json | jq -r '.data."config.yaml"' | jq '.apiServerArguments."enable-admission-plugins"' output=$(oc -n openshift-kube-apiserver get configmap config -o json | jq -r '.data."config.yaml"' | jq '.apiServerArguments."enable-admission-plugins"') [ "$output" == "null" ] && echo "ocp 4.5 has SecurityContextConstraint compiled" || echo $output #Check that no overrides are configured oc get kubeapiservers.operator.openshift.io cluster -o json | jq -r '.spec.unsupportedConfigOverrides' #Verify that SecurityContextConstraints are deployed oc get scc oc describe scc restricted tests: bin_op: and test_items: - flag: "SecurityContextConstraint" - flag: "anyuid" - flag: "hostaccess" - flag: "hostmount-anyuid" - flag: "hostnetwork" - flag: "node-exporter" - flag: "nonroot" - flag: "privileged" - flag: "restricted" remediation: | None required. Security Context Constraints are enabled by default in OpenShift and cannot be disabled. scored: false - id: 1.2.17 text: "Ensure that the admission control plugin NodeRestriction is set (Manual)" audit: | # For 4.5, review the control plane manifest https://github.com/openshift/origin/blob/release-4.5/vendor/k8s.io/kubernetes/cmd/kubeadm/app/phases/controlplane/manifests.go#L132 #Verify the set of admission-plugins for OCP 4.6 and higher oc -n openshift-kube-apiserver get configmap config -o json | jq -r '.data."config.yaml"' | jq '.apiServerArguments."enable-admission-plugins"' output=$(oc -n openshift-kube-apiserver get configmap config -o json | jq -r '.data."config.yaml"' | jq '.apiServerArguments."enable-admission-plugins"') [ "$output" == "null" ] && echo "ocp 4.5 has NodeRestriction compiled" || echo $output #Check that no overrides are configured oc get kubeapiservers.operator.openshift.io cluster -o json | jq -r '.spec.unsupportedConfigOverrides' tests: test_items: - flag: "NodeRestriction" remediation: | The NodeRestriction plugin cannot be disabled. scored: false - id: 1.2.18 text: "Ensure that the --insecure-bind-address argument is not set (Manual)" audit: | # InsecureBindAddress=true should not be in the results oc get kubeapiservers.operator.openshift.io cluster -o jsonpath='{range .spec.observedConfig.apiServerArguments.feature-gates[*]}{@}{"\n"}{end}' # Result should be only 6443 oc -n openshift-kube-apiserver get endpoints -o jsonpath='{.items[*].subsets[*].ports[*].port}' # Result should be only 8443 oc -n openshift-apiserver get endpoints -o jsonpath='{.items[*].subsets[*].ports[*].port}' tests: bin_op: and test_items: - flag: "insecure-bind-address" set: false - flag: 6443 - flag: 8443 remediation: | None required. scored: false - id: 1.2.19 text: "Ensure that the --insecure-port argument is set to 0 (Manual)" audit: | # Should return 6443 oc -n openshift-kube-apiserver get endpoints -o jsonpath='{.items[*].subsets[*].ports[*].port}' # For OCP 4.6 and above oc get configmap config -n openshift-kube-apiserver -ojson | jq -r '.data["config.yaml"]' | jq '.apiServerArguments["insecure-port"]' output=$(oc get configmap config -n openshift-kube-apiserver -ojson | jq -r '.data["config.yaml"]' | jq '.apiServerArguments["insecure-port"]') [ "$output" == "null" ] && echo "ocp 4.5 has insecure-port set to \"0\" compiled" || echo $output tests: bin_op: and test_items: - flag: "\"0\"" - flag: "6443" remediation: | None required. The configuration is managed by the API server operator. scored: false - id: 1.2.20 text: "Ensure that the --secure-port argument is not set to 0 (Manual)" audit: | oc get kubeapiservers.operator.openshift.io cluster -o json | jq '.spec.observedConfig' # Should return only 6443 echo ports=`oc get pods -n openshift-kube-apiserver -l app=openshift-kube-apiserver -o jsonpath='{.items[*].spec.containers[?(@.name=="kube-apiserver")].ports[*].containerPort}'` tests: bin_op: and test_items: - flag: '"bindAddress": "0.0.0.0:6443"' - flag: "ports" compare: op: regex value: '\s*(?:6443\s*){1,}$' remediation: | None required. scored: false - id: 1.2.21 text: "Ensure that the healthz endpoint is protected by RBAC (Manual)" type: manual audit: | # Verify endpoints oc -n openshift-kube-apiserver describe endpoints # Check config for ports, livenessProbe, readinessProbe, healthz oc -n openshift-kube-apiserver get cm kube-apiserver-pod -o json | jq -r '.data."pod.yaml"' | jq '.spec.containers' # Test to validate RBAC enabled on the apiserver endpoint; check with non-admin role oc project openshift-kube-apiserver POD=$(oc get pods -n openshift-kube-apiserver -l app=openshift-kube-apiserver -o jsonpath='{.items[0].metadata.name}') PORT=$(oc get pods -n openshift-kube-apiserver -l app=openshift-kube-apiserver -o jsonpath='{.items[0].spec.containers[0].ports[0].hostPort}') # Following should return 403 Forbidden oc rsh -n openshift-kube-apiserver ${POD} curl https://localhost:${PORT}/metrics -k # Create a service account to test RBAC oc create -n openshift-kube-apiserver sa permission-test-sa # Should return 403 Forbidden SA_TOKEN=$(oc sa -n openshift-kube-apiserver get-token permission-test-sa) oc rsh -n openshift-kube-apiserver ${POD} curl https://localhost:${PORT}/metrics -H "Authorization: Bearer $SA_TOKEN" -k # Cleanup oc delete -n openshift-kube-apiserver sa permission-test-sa # As cluster admin, should succeed CLUSTER_ADMIN_TOKEN=$(oc whoami -t) oc rsh -n openshift-kube-apiserver ${POD} curl https://localhost:${PORT}/metrics -H "Authorization: Bearer $CLUSTER_ADMIN_TOKEN" -k remediation: | None required as profiling data is protected by RBAC. scored: false - id: 1.2.22 text: "Ensure that the --audit-log-path argument is set (Manual)" audit: | # Should return “/var/log/kube-apiserver/audit.log" output=$(oc get configmap config -n openshift-kube-apiserver -o jsonpath="{['.data.config\.yaml']}" | jq '.auditConfig.auditFilePath') [ "$output" != "" ] && [ "$output" != "null" ] && echo "$output" || true output=$(oc get configmap config -n openshift-kube-apiserver -o json | jq -r '.data["config.yaml"]' | jq -r '.apiServerArguments["audit-log-path"][]?') [ "$output" != "" ] && [ "$output" != "null" ] && echo "$output" || true POD=$(oc get pods -n openshift-kube-apiserver -l app=openshift-kube-apiserver -o jsonpath='{.items[0].metadata.name}') oc rsh -n openshift-kube-apiserver -c kube-apiserver $POD ls /var/log/kube-apiserver/audit.log 2>/dev/null # Should return 0 echo exit_code=$? # Should return "/var/log/openshift-apiserver/audit.log" output=$(oc get configmap config -n openshift-apiserver -o jsonpath="{['.data.config\.yaml']}" | jq '.auditConfig.auditFilePath') [ "$output" != "" ] && [ "$output" != "null" ] && echo "$output" || true output=$(oc get configmap config -n openshift-apiserver -o json | jq -r '.data["config.yaml"]' | jq -r '.apiServerArguments["audit-log-path"][]?') [ "$output" != "" ] && [ "$output" != "null" ] && echo "$output" || true POD=$(oc get pods -n openshift-apiserver -l apiserver=true -o jsonpath='{.items[0].metadata.name}') oc rsh -n openshift-apiserver $POD ls /var/log/openshift-apiserver/audit.log 2>/dev/null # Should return 0 echo exit_code=$? use_multiple_values: true tests: bin_op: or test_items: - flag: "/var/log/kube-apiserver/audit.log" - flag: "/var/log/openshift-apiserver/audit.log" - flag: "exit_code=0" - flag: "null" remediation: | None required. This is managed by the cluster apiserver operator. scored: false - id: 1.2.23 text: "Ensure that the audit logs are forwarded off the cluster for retention (Manual)" type: "manual" remediation: | Follow the documentation for log forwarding. Forwarding logs to third party systems https://docs.openshift.com/container-platform/4.5/logging/cluster-logging-external.html scored: false - id: 1.2.24 text: "Ensure that the maximumRetainedFiles argument is set to 10 or as appropriate (Manual)" audit: | #NOTICE output=$(oc get configmap config -n openshift-kube-apiserver -o json | jq -r '.data["config.yaml"]' | jq -r .auditConfig.maximumRetainedFiles) [ "$output" != "" ] && [ "$output" != "null" ] && echo "maximumRetainedFiles=$output" || true output=$(oc get configmap config -n openshift-apiserver -o json | jq -r '.data["config.yaml"]' | jq -r .auditConfig.maximumRetainedFiles) [ "$output" != "" ] && [ "$output" != "null" ] && echo "maximumRetainedFiles=$output" || true output=$(oc get configmap config -n openshift-kube-apiserver -o json | jq -r '.data["config.yaml"]' | jq -r '.apiServerArguments["audit-log-maxbackup"][]?') [ "$output" != "" ] && [ "$output" != "null" ] && echo "audit-log-maxbackup=$output" || true output=$(oc get configmap config -n openshift-apiserver -o json | jq -r '.data["config.yaml"]' | jq -r '.apiServerArguments["audit-log-maxbackup"][]?') [ "$output" != "" ] && [ "$output" != "null" ] && echo "audit-log-maxbackup=$output" || true use_multiple_values: true tests: bin_op: or test_items: - flag: "maximumRetainedFiles" compare: op: gte value: 10 - flag: "audit-log-maxbackup" compare: op: gte value: 10 remediation: | Set the maximumRetainedFiles parameter to 10 or as an appropriate number of files. maximumRetainedFiles: 10 scored: false - id: 1.2.25 text: "Ensure that the maximumFileSizeMegabytes argument is set to 100 or as appropriate (Manual)" audit: | #NOTICE output=$(oc get configmap config -n openshift-kube-apiserver -o json | jq -r '.data["config.yaml"]' | jq -r .auditConfig.maximumFileSizeMegabytes) [ "$output" != "" ] && [ "$output" != "null" ] && echo "maximumFileSizeMegabytes=$output" || true output=$(oc get configmap config -n openshift-apiserver -o json | jq -r '.data["config.yaml"]' | jq -r .auditConfig.maximumFileSizeMegabytes) [ "$output" != "" ] && [ "$output" != "null" ] && echo "maximumFileSizeMegabytes=$output" || true output=$(oc get configmap config -n openshift-kube-apiserver -o json | jq -r '.data["config.yaml"]' | jq -r '.apiServerArguments["audit-log-maxsize"][]?') [ "$output" != "" ] && [ "$output" != "null" ] && echo "audit-log-maxsize=$output" || true output=$(oc get configmap config -n openshift-apiserver -o json | jq -r '.data["config.yaml"]' | jq -r '.apiServerArguments["audit-log-maxsize"][]?') [ "$output" != "" ] && [ "$output" != "null" ] && echo "audit-log-maxsize=$output" || true use_multiple_values: true tests: bin_op: or test_items: - flag: "maximumFileSizeMegabytes" compare: op: gte value: 100 - flag: "audit-log-maxsize" compare: op: gte value: 100 remediation: | Set the audit-log-maxsize parameter to 100 or as an appropriate number. maximumFileSizeMegabytes: 100 scored: false - id: 1.2.26 text: "Ensure that the --request-timeout argument is set as appropriate (Manual)" audit: | echo requestTimeoutSeconds=`oc get configmap config -n openshift-kube-apiserver -ojson | jq -r '.data["config.yaml"]' | jq -r .servingInfo.requestTimeoutSeconds` tests: test_items: - flag: "requestTimeoutSeconds" remediation: | TBD scored: false - id: 1.2.27 text: "Ensure that the --service-account-lookup argument is set to true (Manual)" audit: | # For OCP 4.5 oc get configmap config -n openshift-kube-apiserver -ojson | jq -r '.data["config.yaml"]' | jq '.apiServerArguments' | grep service-account-lookup # For OCP 4.6 and above oc get configmap config -n openshift-kube-apiserver -ojson | jq -r '.data["config.yaml"]' | jq -r '.apiServerArguments["service-account-lookup"]' output=$(oc get configmap config -n openshift-kube-apiserver -ojson | jq -r '.data["config.yaml"]' | jq -r '.apiServerArguments["service-account-lookup"][0]') [ "$output" == "null" ] && echo "ocp 4.5 has service-account-lookup=true compiled" || echo service-account-lookup=$output tests: test_items: - flag: "service-account-lookup=true" remediation: | TBD scored: false - id: 1.2.28 text: "Ensure that the --service-account-key-file argument is set as appropriate (Manual)" audit: | oc get configmap config -n openshift-kube-apiserver -ojson | jq -r '.data["config.yaml"]' | jq -r .serviceAccountPublicKeyFiles[] tests: bin_op: and test_items: - flag: "/etc/kubernetes/static-pod-resources/configmaps/sa-token-signing-certs" - flag: "/etc/kubernetes/static-pod-resources/configmaps/bound-sa-token-signing-certs" remediation: | The OpenShift API server does not use the service-account-key-file argument. The ServiceAccount token authenticator is configured with serviceAccountConfig.publicKeyFiles. OpenShift does not reuse the apiserver TLS key. This is not configurable. scored: false - id: 1.2.29 text: "Ensure that the --etcd-certfile and --etcd-keyfile arguments are set as appropriate (Manual)" audit: | # etcd Certificate File oc get configmap config -n openshift-kube-apiserver -ojson | jq -r '.data["config.yaml"]' | jq -r .storageConfig.certFile # etcd Key File oc get configmap config -n openshift-kube-apiserver -ojson | jq -r '.data["config.yaml"]' | jq -r .storageConfig.keyFile # NOTICE 4.6 extention oc get configmap config -n openshift-kube-apiserver -ojson | jq -r '.data["config.yaml"]' | jq -r '.apiServerArguments["etcd-certfile"]' oc get configmap config -n openshift-kube-apiserver -ojson | jq -r '.data["config.yaml"]' | jq -r '.apiServerArguments["etcd-keyfile"]' tests: bin_op: and test_items: - flag: "/etc/kubernetes/static-pod-resources/secrets/etcd-client/tls.crt" - flag: "/etc/kubernetes/static-pod-resources/secrets/etcd-client/tls.key" remediation: | OpenShift automatically manages TLS and client certificate authentication for etcd. This is not configurable. scored: false - id: 1.2.30 text: "Ensure that the --tls-cert-file and --tls-private-key-file arguments are set as appropriate (Manual)" audit: | # TLS Cert File - openshift-kube-apiserver oc get configmap config -n openshift-kube-apiserver -ojson | jq -r '.data["config.yaml"]' | jq -r .servingInfo.certFile # TLS Key File oc get configmap config -n openshift-kube-apiserver -ojson | jq -r '.data["config.yaml"]' | jq -r '.servingInfo.keyFile' # NOTECI 4.6 extention oc get configmap config -n openshift-kube-apiserver -ojson | jq -r '.data["config.yaml"]' | jq -r '.apiServerArguments["tls-cert-file"]' oc get configmap config -n openshift-kube-apiserver -ojson | jq -r '.data["config.yaml"]' | jq -r '.apiServerArguments["tls-private-key-file"]' tests: bin_op: and test_items: - flag: "/etc/kubernetes/static-pod-certs/secrets/service-network-serving-certkey/tls.crt" - flag: "/etc/kubernetes/static-pod-certs/secrets/service-network-serving-certkey/tls.key" remediation: | OpenShift automatically manages TLS authentication for the API server communication with the node/kublet. This is not configurable. You may optionally set a custom default certificate to be used by the API server when serving content in order to enable clients to access the API server at a different host name or without the need to distribute the cluster-managed certificate authority (CA) certificates to the clients. Follow the directions in the OpenShift documentation User-provided certificates for the API server scored: false - id: 1.2.31 text: "Ensure that the --client-ca-file argument is set as appropriate (Manual)" audit: | oc get configmap config -n openshift-kube-apiserver -ojson | jq -r '.data["config.yaml"]' | jq -r .servingInfo.clientCA oc get configmap config -n openshift-kube-apiserver -ojson | jq -r '.data["config.yaml"]' | jq -r '.apiServerArguments["client-ca-file"]' tests: test_items: - flag: "/etc/kubernetes/static-pod-certs/configmaps/client-ca/ca-bundle.crt" remediation: | OpenShift automatically manages TLS authentication for the API server communication with the node/kublet. This is not configurable. You may optionally set a custom default certificate to be used by the API server when serving content in order to enable clients to access the API server at a different host name or without the need to distribute the cluster-managed certificate authority (CA) certificates to the clients. User-provided certificates must be provided in a kubernetes.io/tls type Secret in the openshift-config namespace. Update the API server cluster configuration, the apiserver/cluster resource, to enable the use of the user-provided certificate. scored: false - id: 1.2.32 text: "Ensure that the --etcd-cafile argument is set as appropriate (Manual)" audit: | #etcd CA File oc get configmap config -n openshift-kube-apiserver -ojson | jq -r '.data["config.yaml"]' | jq -r .storageConfig.ca oc get configmap config -n openshift-kube-apiserver -ojson | jq -r '.data["config.yaml"]' | jq -r '.apiServerArguments["etcd-cafile"]' tests: test_items: - flag: "/etc/kubernetes/static-pod-resources/configmaps/etcd-serving-ca/ca-bundle.crt" remediation: | None required. OpenShift generates the etcd-cafile and sets the arguments appropriately in the API server. Communication with etcd is secured by the etcd serving CA. scored: false - id: 1.2.33 text: "Ensure that the --encryption-provider-config argument is set as appropriate (Manual)" audit: | # encrypt the etcd datastore oc get openshiftapiserver -o=jsonpath='{range.items[0].status.conditions[?(@.type=="Encrypted")]}{.reason}{"\n"}{.message}{"\n"}' tests: test_items: - flag: "EncryptionCompleted" remediation: | Follow the OpenShift documentation for Encrypting etcd data | Authentication | OpenShift Container Platform 4.5 https://docs.openshift.com/container-platform/4.5/security/encrypting-etcd.html scored: false - id: 1.2.34 text: "Ensure that encryption providers are appropriately configured (Manual)" audit: | # encrypt the etcd datastore oc get openshiftapiserver -o=jsonpath='{range.items[0].status.conditions[?(@.type=="Encrypted")]}{.reason}{"\n"}{.message}{"\n"}' tests: test_items: - flag: "EncryptionCompleted" remediation: | Follow the Kubernetes documentation and configure a EncryptionConfig file. In this file, choose aescbc, kms or secretbox as the encryption provider. scored: false - id: 1.2.35 text: "Ensure that the API Server only makes use of Strong Cryptographic Ciphers (Manual)" type: manual audit: | # verify cipher suites oc get cm -n openshift-authentication v4-0-config-system-cliconfig -o jsonpath='{.data.v4\-0\-config\-system\-cliconfig}' | jq .servingInfo oc get kubeapiservers.operator.openshift.io cluster -o json |jq.spec.observedConfig.servingInfo oc get openshiftapiservers.operator.openshift.io cluster -o json |jq.spec.observedConfig.servingInfo oc describe --namespace=openshift-ingress-operator ingresscontroller/default remediation: | Verify that the tlsSecurityProfile is set to the value you chose. Note: The HAProxy Ingress controller image does not support TLS 1.3 and because the Modern profile requires TLS 1.3, it is not supported. The Ingress Operator converts the Modern profile to Intermediate. The Ingress Operator also converts the TLS 1.0 of an Old or Custom profile to 1.1, and TLS 1.3 of a Custom profile to 1.2. scored: false - id: 1.3 text: "Controller Manager" checks: - id: 1.3.1 text: "Ensure that garbage collection is configured as appropriate (Manual)" type: manual remediation: | To configure, follow the directions in Configuring garbage collection for containers and images https://docs.openshift.com/container-platform/4.5/nodes/nodes/nodes-nodes-garbage-collection.html#nodes-nodes-garbage-collection-configuring_nodes-nodes-configuring scored: false - id: 1.3.2 text: "Ensure that controller manager healthz endpoints are protected by RBAC (Manual)" type: manual audit: | # Verify configuration for ports, livenessProbe, readinessProbe, healthz oc -n openshift-kube-controller-manager get cm kube-controller-manager-pod -o json | jq -r '.data."pod.yaml"' | jq '.spec.containers' # Verify endpoints oc -n openshift-kube-controller-manager describe endpoints # Test to validate RBAC enabled on the controller endpoint; check with non-admin role oc project openshift-kube-controller-manage POD=$(oc get pods -n openshift-kube-controller-manager -l app=kube-controller-manager -o jsonpath='{.items[0].metadata.name}') PORT=$(oc get pods -n openshift-kube-controller-manager -l app=kube-controller-manager -o jsonpath='{.items[0].spec.containers[0].ports[0].hostPort}') # Following should return 403 Forbidden oc rsh -n openshift-kube-controller-manager ${POD} curl https://localhost:${PORT}/metrics -k # Create a service account to test RBAC oc create -n openshift-kube-controller-manager sa permission-test-sa # Should return 403 Forbidden SA_TOKEN=$(oc sa -n openshift-kube-controller-manager get-token permission-test-sa) oc rsh -n openshift-kube-controller-manager ${POD} curl https://localhost:${PORT}/metrics -H "Authorization: Bearer $SA_TOKEN" -k # Cleanup oc delete -n openshift-kube-controller-manager sa permission-test-sa # As cluster admin, should succeed CLUSTER_ADMIN_TOKEN=$(oc whoami -t) oc rsh -n openshift-kube-controller-manager ${POD} curl https://localhost:${PORT}/metrics -H "Authorization: Bearer $CLUSTER_ADMIN_TOKEN" -k remediation: | None required; profiling is protected by RBAC. scored: false - id: 1.3.3 text: "Ensure that the --use-service-account-credentials argument is set to true (Manual)" audit: | echo use-service-account-credentials=`oc get configmaps config -n openshift-kube-controller-manager -ojson | jq -r '.data["config.yaml"]' | jq -r '.extendedArguments["use-service-account-credentials"][]'` tests: test_items: - flag: "use-service-account-credentials" compare: op: eq value: true remediation: | The OpenShift Controller Manager operator manages and updates the OpenShift Controller Manager. The Kubernetes Controller Manager operator manages and updates the Kubernetes Controller Manager deployed on top of OpenShift. This operator is configured via KubeControllerManager custom resource. scored: false - id: 1.3.4 text: "Ensure that the --service-account-private-key-file argument is set as appropriate (Manual)" audit: | oc get configmaps config -n openshift-kube-controller-manager -ojson | jq -r '.data["config.yaml"]' | jq -r '.extendedArguments["service-account-private-key-file"][]' tests: test_items: - flag: "/etc/kubernetes/static-pod-resources/secrets/service-account-private-key/service-account.key" remediation: | None required. OpenShift manages the service account credentials for the scheduler automatically. scored: false - id: 1.3.5 text: "Ensure that the --root-ca-file argument is set as appropriate (Manual)" audit: | oc get configmaps config -n openshift-kube-controller-manager -ojson | jq -r '.data["config.yaml"]' | jq -r '.extendedArguments["root-ca-file"][]' tests: test_items: - flag: "/etc/kubernetes/static-pod-resources/configmaps/serviceaccount-ca/ca-bundle.crt" remediation: | None required. Certificates for OpenShift platform components are automatically created and rotated by the OpenShift Container Platform. scored: false - id: 1.3.6 text: "Ensure that the RotateKubeletServerCertificate argument is set to true (Manual)" audit: | oc get configmaps config -n openshift-kube-controller-manager -ojson | jq -r '.data["config.yaml"]' | jq -r '.extendedArguments["feature-gates"][]' tests: test_items: - flag: "RotateKubeletServerCertificate" compare: op: eq value: "true" remediation: | None required. Certificates for OpenShift platform components are automatically created and rotated by the OpenShift Container Platform. scored: false - id: 1.3.7 text: "Ensure that the --bind-address argument is set to 127.0.0.1 (Manual)" audit: | echo port=`oc get configmaps config -n openshift-kube-controller-manager -ojson | jq -r '.data["config.yaml"]' | jq '.extendedArguments["port"][]'` echo secure-port=`oc get configmaps config -n openshift-kube-controller-manager -ojson | jq -r '.data["config.yaml"]' | jq '.extendedArguments["secure-port"][]'` #Following should fail with a http code 403 POD=$(oc get pods -n openshift-kube-controller-manager -l app=kube-controller-manager -o jsonpath='{.items[0].metadata.name}') oc rsh -n openshift-kube-controller-manager -c kube-controller-manager $POD curl https://localhost:10257/metrics -k tests: bin_op: and test_items: - flag: "secure-port" compare: op: eq value: "\"10257\"" - flag: "port" compare: op: eq value: "\"0\"" - flag: "\"code\": 403" remediation: | Edit the Controller Manager pod specification file $controllermanagerconf on the master node and ensure the correct value for the --bind-address parameter scored: false - id: 1.4 text: "Scheduler" checks: - id: 1.4.1 text: "Ensure that the healthz endpoints for the scheduler are protected by RBAC (Manual)" type: manual audit: | # check configuration for ports, livenessProbe, readinessProbe, healthz oc -n openshift-kube-scheduler get cm kube-scheduler-pod -o json | jq -r '.data."pod.yaml"' | jq '.spec.containers' # Test to verify endpoints oc -n openshift-kube-scheduler describe endpoints # Test to validate RBAC enabled on the scheduler endpoint; check with non-admin role oc project openshift-kube-scheduler POD=$(oc get pods -l app=openshift-kube-scheduler -o jsonpath='{.items[0].metadata.name}') PORT=$(oc get pod $POD -o jsonpath='{.spec.containers[0].livenessProbe.httpGet.port}') # Should return 403 Forbidden oc rsh ${POD} curl http://localhost:${PORT}/metrics -k # Create a service account to test RBAC oc create sa permission-test-sa # Should return 403 Forbidden SA_TOKEN=$(oc sa get-token permission-test-sa) oc rsh ${POD} curl http://localhost:${PORT}/metrics -H "Authorization: Bearer $SA_TOKEN" -k # Cleanup oc delete sa permission-test-sa # As cluster admin, should succeed CLUSTER_ADMIN_TOKEN=$(oc whoami -t) oc rsh ${POD} curl http://localhost:${PORT}/metrics -H "Authorization: Bearer $CLUSTER_ADMIN_TOKEN" -k remediation: | A fix to this issue: https://bugzilla.redhat.com/show_bug.cgi?id=1889488 None required. Profiling is protected by RBAC and cannot be disabled. scored: false - id: 1.4.2 text: "Verify that the scheduler API service is protected by authentication and authorization (Manual)" type: manual audit: | # To verify endpoints oc -n openshift-kube-scheduler describe endpoints # To verify that bind-adress is not used in the configuration and that port is set to 0 oc -n openshift-kube-scheduler get cm kube-scheduler-pod -o json | jq -r '.data."pod.yaml"' | jq '.spec.containers' # To test for RBAC: oc project openshift-kube-scheduler POD=$(oc get pods -l app=openshift-kube-scheduler -o jsonpath='{.items[0].metadata.name}') POD_IP=$(oc get pods -l app=openshift-kube-scheduler -o jsonpath='{.items[0].status.podIP}') PORT=$(oc get pod $POD -o jsonpath='{.spec.containers[0].livenessProbe.httpGet.port}') # Should return a 403 oc rsh ${POD} curl http://${POD_IP}:${PORT}/metrics # Create a service account to test RBAC oc create sa permission-test-sa # Should return 403 Forbidden SA_TOKEN=$(oc sa get-token permission-test-sa) oc rsh ${POD} curl http://localhost:${PORT}/metrics -H "Authorization: Bearer $SA_TOKEN" -k # Cleanup oc delete sa permission-test-sa # As cluster admin, should succeed CLUSTER_ADMIN_TOKEN=$(oc whoami -t) oc rsh ${POD} curl http://localhost:${PORT}/metrics -H "Authorization: Bearer $CLUSTER_ADMIN_TOKEN" -k remediation: | By default, the --bind-address argument is not present, the readinessProbe and livenessProbe arguments are set to 10251 and the port argument is set to 0. Check the status of this issue: https://bugzilla.redhat.com/show_bug.cgi?id=1889488 scored: false ================================================ FILE: cfg/rh-1.0/node.yaml ================================================ --- controls: version: rh-1.0 id: 4 text: "Worker Node Security Configuration" type: "node" groups: - id: 4.1 text: "Worker Node Configuration Files" checks: - id: 4.1.1 text: "Ensure that the kubelet service file permissions are set to 644 or more restrictive (Automated)" audit: | NODE_NAME=$(oc get pod $HOSTNAME -o=jsonpath='{.spec.nodeName}') oc debug node/$NODE_NAME -- chroot /host stat -c "$NODE_NAME %n permissions=%a" /etc/systemd/system/kubelet.service 2> /dev/null tests: test_items: - flag: "permissions" compare: op: bitmask value: "644" remediation: | By default, the kubelet service file has permissions of 644. scored: true - id: 4.1.2 text: "Ensure that the kubelet service file ownership is set to root:root (Automated)" audit: | # Should return root:root for each node NODE_NAME=$(oc get pod $HOSTNAME -o=jsonpath='{.spec.nodeName}') oc debug node/$NODE_NAME -- chroot /host stat -c "$NODE_NAME %n %U:%G" /etc/systemd/system/kubelet.service 2> /dev/null tests: test_items: - flag: root:root remediation: | By default, the kubelet service file has ownership of root:root. scored: true - id: 4.1.3 text: "If proxy kubeconfig file exists ensure permissions are set to 644 or more restrictive (Manual)" audit: | # Get the node name where the pod is running NODE_NAME=$(oc get pod "$HOSTNAME" -o=jsonpath='{.spec.nodeName}') # Get the pod name in the openshift-sdn namespace POD_NAME=$(oc get pods -n openshift-sdn -l app=sdn --field-selector spec.nodeName="$NODE_NAME" -o jsonpath='{.items[0].metadata.name}' 2>/dev/null) if [ -z "$POD_NAME" ]; then echo "No matching pods found on the current node." else # Execute the stat command oc exec -n openshift-sdn "$POD_NAME" -- stat -Lc "$i %n permissions=%a" /config/kube-proxy-config.yaml 2>/dev/null fi tests: bin_op: or test_items: - flag: "permissions" set: true compare: op: bitmask value: "644" remediation: | None needed. scored: false - id: 4.1.4 text: "Ensure that the proxy kubeconfig file ownership is set to root:root (Manual)" audit: | # Get the node name where the pod is running NODE_NAME=$(oc get pod "$HOSTNAME" -o=jsonpath='{.spec.nodeName}') # Get the pod name in the openshift-sdn namespace POD_NAME=$(oc get pods -n openshift-sdn -l app=sdn --field-selector spec.nodeName="$NODE_NAME" -o jsonpath='{.items[0].metadata.name}' 2>/dev/null) if [ -z "$POD_NAME" ]; then echo "No matching pods found on the current node." else # Execute the stat command oc exec -n openshift-sdn "$POD_NAME" -- stat -Lc "$i %n %U:%G" /config/kube-proxy-config.yaml 2>/dev/null fi use_multiple_values: true tests: bin_op: or test_items: - flag: root:root remediation: | None required. The configuration is managed by OpenShift operators. scored: false - id: 4.1.5 text: "Ensure that the --kubeconfig kubelet.conf file permissions are set to 644 or more restrictive (Manual)" audit: | # Check permissions NODE_NAME=$(oc get pod $HOSTNAME -o=jsonpath='{.spec.nodeName}') oc debug node/$NODE_NAME -- chroot /host stat -c "$NODE_NAME %n permissions=%a" /etc/kubernetes/kubelet.conf 2> /dev/null use_multiple_values: true tests: test_items: - flag: "permissions" compare: op: bitmask value: "644" remediation: | None required. scored: false - id: 4.1.6 text: "Ensure that the --kubeconfig kubelet.conf file ownership is set to root:root (Manual)" audit: | NODE_NAME=$(oc get pod $HOSTNAME -o=jsonpath='{.spec.nodeName}') oc debug node/$NODE_NAME -- chroot /host stat -c "$NODE_NAME %n %U:%G" /etc/kubernetes/kubelet.conf 2> /dev/null use_multiple_values: true tests: test_items: - flag: root:root remediation: | None required. scored: false - id: 4.1.7 text: "Ensure that the certificate authorities file permissions are set to 644 or more restrictive (Automated)" audit: | NODE_NAME=$(oc get pod $HOSTNAME -o=jsonpath='{.spec.nodeName}') oc debug node/$NODE_NAME -- chroot /host stat -c "$NODE_NAME %n permissions=%a" /etc/kubernetes/kubelet-ca.crt 2> /dev/null use_multiple_values: true tests: test_items: - flag: "permissions" compare: op: bitmask value: "644" remediation: | None required. scored: true - id: 4.1.8 text: "Ensure that the client certificate authorities file ownership is set to root:root (Automated)" audit: | NODE_NAME=$(oc get pod $HOSTNAME -o=jsonpath='{.spec.nodeName}') oc debug node/$NODE_NAME -- chroot /host stat -c "$NODE_NAME %n %U:%G" /etc/kubernetes/kubelet-ca.crt 2> /dev/null use_multiple_values: true tests: test_items: - flag: root:root remediation: | None required. scored: true - id: 4.1.9 text: "Ensure that the kubelet --config configuration file has permissions set to 644 or more restrictive (Automated)" audit: | NODE_NAME=$(oc get pod $HOSTNAME -o=jsonpath='{.spec.nodeName}') oc debug node/$NODE_NAME -- chroot /host stat -c "$NODE_NAME %n permissions=%a" /var/lib/kubelet/kubeconfig 2> /dev/null use_multiple_values: true tests: test_items: - flag: "permissions" compare: op: bitmask value: "644" remediation: | None required. scored: true - id: 4.1.10 text: "Ensure that the kubelet configuration file ownership is set to root:root (Automated)" audit: | NODE_NAME=$(oc get pod $HOSTNAME -o=jsonpath='{.spec.nodeName}') oc debug node/$NODE_NAME -- chroot /host stat -c "$NODE_NAME %n %U:%G" /var/lib/kubelet/kubeconfig 2> /dev/null use_multiple_values: true tests: test_items: - flag: root:root remediation: | None required. scored: true - id: 4.2 text: "Kubelet" checks: - id: 4.2.1 text: "Ensure that the --anonymous-auth argument is set to false (Automated)" audit: | NODE_NAME=$(oc get pod $HOSTNAME -o=jsonpath='{.spec.nodeName}') oc debug node/$NODE_NAME -- chroot /host grep -B4 -A1 anonymous /etc/kubernetes/kubelet.conf 2> /dev/null use_multiple_values: true tests: test_items: - flag: "enabled: true" set: false remediation: | Follow the instructions in the documentation to create a Kubelet config CRD and set the anonymous-auth is set to false. scored: true - id: 4.2.2 text: "Ensure that the --authorization-mode argument is not set to AlwaysAllow (Manual)" type: manual # Takes a lot of time for connection to fail and audit: | POD=$(oc -n openshift-kube-apiserver get pod -l app=openshift-kube-apiserver -o jsonpath='{.items[0].metadata.name}') TOKEN=$(oc whoami -t) NODE_NAME=$(oc get pod $HOSTNAME -o=jsonpath='{.spec.nodeName}') oc exec -n openshift-kube-apiserver $POD -- curl -sS https://172.25.0.1/api/v1/nodes/$NODE_NAME/proxy/configz -k -H "Authorization:Bearer $TOKEN" | jq -r '.kubeletconfig.authorization.mode' 2> /dev/null use_multiple_values: true tests: test_items: - flag: "Connection timed out" remediation: | None required. Unauthenticated/Unauthorized users have no access to OpenShift nodes. scored: false - id: 4.2.3 text: "Ensure that the --client-ca-file argument is set as appropriate (Automated)" audit: | NODE_NAME=$(oc get pod $HOSTNAME -o=jsonpath='{.spec.nodeName}') oc debug node/$NODE_NAME -- chroot /host grep clientCAFile /etc/kubernetes/kubelet.conf 2> /dev/null use_multiple_values: true tests: test_items: - flag: '"clientCAFile": "/etc/kubernetes/kubelet-ca.crt"' remediation: | None required. Changing the clientCAFile value is unsupported. scored: true - id: 4.2.4 text: "Verify that the read only port is not used or is set to 0 (Automated)" audit: | echo `oc -n openshift-kube-apiserver get cm kube-apiserver-pod -o yaml | grep --color read-only-port` 2> /dev/null echo `oc -n openshift-kube-apiserver get cm config -o yaml | grep --color "read-only-port"` 2> /dev/null tests: bin_op: or test_items: - flag: "read-only-port" compare: op: has value: "[\"0\"]" - flag: "read-only-port" set: false remediation: | In earlier versions of OpenShift 4, the read-only-port argument is not used. Follow the instructions in the documentation to create a Kubelet config CRD and set the --read-only-port is set to 0. scored: true - id: 4.2.5 text: "Ensure that the --streaming-connection-idle-timeout argument is not set to 0 (Automated)" audit: | # Should return 1 for node NODE_NAME=$(oc get pod $HOSTNAME -o=jsonpath='{.spec.nodeName}') oc debug node/${NODE_NAME} -- chroot /host ps -ef | grep kubelet | grep streaming-connection-idle-timeout 2> /dev/null echo exit_code=$? # Should return 1 for node oc debug node/${NODE_NAME} -- chroot /host grep streamingConnectionIdleTimeout /etc/kubernetes/kubelet.conf 2> /dev/null echo exit_code=$? use_multiple_values: true tests: bin_op: or test_items: - flag: --streaming-connection-idle-timeout compare: op: noteq value: 0 - flag: streamingConnectionIdleTimeout compare: op: noteq value: 0s - flag: "exit_code" compare: op: eq value: 1 remediation: | Follow the instructions in the documentation to create a Kubelet config CRD and set the --streaming-connection-idle-timeout to the desired value. Do not set the value to 0. scored: true - id: 4.2.6 text: "Ensure that the --protect-kernel-defaults argument is not set (Manual)" audit: | NODE_NAME=$(oc get pod $HOSTNAME -o=jsonpath='{.spec.nodeName}') oc debug node/$NODE_NAME -- chroot /host more /etc/kubernetes/kubelet.conf 2> /dev/null tests: test_items: - flag: protectKernelDefaults set: false remediation: | None required. The OpenShift 4 kubelet modifies the system tunable; using the protect-kernel-defaults flag will cause the kubelet to fail on start if the tunables don't match the kubelet configuration and the OpenShift node will fail to start. scored: false - id: 4.2.7 text: "Ensure that the --make-iptables-util-chains argument is set to true (Manual)" audit: | /bin/bash flag=make-iptables-util-chains opt=makeIPTablesUtilChains # look at each machineconfigpool while read -r pool nodeconfig; do # true by default value='true' # first look for the flag oc get machineconfig $nodeconfig -o json | jq -r '.spec.config.systemd[][] | select(.name=="kubelet.service") | .contents' | sed -n "/^ExecStart=/,/^\$/ { /^\\s*--$flag=false/ q 100 }" # if the above command exited with 100, the flag was false [ $? == 100 ] && value='false' # now look in the yaml KubeletConfig yamlconfig=$(oc get machineconfig $nodeconfig -o json | jq -r '.spec.config.storage.files[] | select(.path=="/etc/kubernetes/kubelet.conf") | .contents.source ' | sed 's/^data:,//' | while read; do echo -e ${REPLY//%/\\x}; done) echo "$yamlconfig" | sed -n "/^$opt:\\s*false\\s*$/ q 100" [ $? == 100 ] && value='false' echo "Pool $pool has $flag ($opt) set to $value" done < <(oc get machineconfigpools -o json | jq -r '.items[] | select(.status.machineCount>0) | .metadata.name + " " + .spec.configuration.name') use_multiple_values: true tests: test_items: - flag: "set to true" remediation: | None required. The --make-iptables-util-chains argument is set to true by default. scored: false - id: 4.2.8 text: "Ensure that the --hostname-override argument is not set (Manual)" audit: | echo `oc get machineconfig 01-worker-kubelet -o yaml | grep hostname-override` echo `oc get machineconfig 01-master-kubelet -o yaml | grep hostname-override` tests: test_items: - flag: hostname-override set: false remediation: | By default, --hostname-override argument is not set. scored: false - id: 4.2.9 text: "Ensure that the kubeAPIQPS [--event-qps] argument is set to 0 or a level which ensures appropriate event capture (Manual)" audit: | NODE_NAME=$(oc get pod $HOSTNAME -o=jsonpath='{.spec.nodeName}') oc debug node/${NODE_NAME} -- chroot /host cat /etc/kubernetes/kubelet.conf; oc get machineconfig 01-worker-kubelet -o yaml | grep --color kubeAPIQPS%3A%2050 oc get machineconfig 01-master-kubelet -o yaml | grep --color kubeAPIQPS%3A%2050 type: "manual" remediation: | Follow the documentation to edit kubelet parameters https://docs.openshift.com/container-platform/4.5/scalability_and_performance/recommended-host-practices.html#create-a-kubeletconfig-crd-to-edit-kubelet-parameters KubeAPIQPS: scored: false - id: 4.2.10 text: "Ensure that the --tls-cert-file and --tls-private-key-file arguments are set as appropriate (Automated)" audit: | oc get configmap config -n openshift-kube-apiserver -o json \ | jq -r '.data["config.yaml"]' \ | jq -r '.apiServerArguments | .["kubelet-client-certificate"][0], .["kubelet-client-key"][0] ' tests: bin_op: and test_items: - flag: "/etc/kubernetes/static-pod-certs/secrets/kubelet-client/tls.crt" - flag: "/etc/kubernetes/static-pod-certs/secrets/kubelet-client/tls.key" remediation: | OpenShift automatically manages TLS authentication for the API server communication with the node/kublet. This is not configurable. scored: true - id: 4.2.11 text: "Ensure that the --rotate-certificates argument is not set to false (Manual)" audit: | #Verify the rotateKubeletClientCertificate feature gate is not set to false NODE_NAME=$(oc get pod $HOSTNAME -o=jsonpath='{.spec.nodeName}') oc debug node/${NODE_NAME} -- chroot /host cat /etc/kubernetes/kubelet.conf | grep RotateKubeletClientCertificate 2> /dev/null # Verify the rotateCertificates argument is set to true oc debug node/${NODE_NAME} -- chroot host grep rotate /etc/kubernetes/kubelet.conf 2> /dev/null use_multiple_values: true tests: bin_op: or test_items: - flag: rotateCertificates compare: op: eq value: true - flag: rotateKubeletClientCertificates compare: op: noteq value: false - flag: rotateKubeletClientCertificates set: false remediation: | None required. scored: false - id: 4.2.12 text: "Verify that the RotateKubeletServerCertificate argument is set to true (Manual)" audit: | #Verify the rotateKubeletServerCertificate feature gate is on NODE_NAME=$(oc get pod $HOSTNAME -o=jsonpath='{.spec.nodeName}') oc debug node/${NODE_NAME} -- chroot /host grep RotateKubeletServerCertificate /etc/kubernetes/kubelet.conf 2> /dev/null # Verify the rotateCertificates argument is set to true oc debug node/${NODE_NAME} -- chroot host grep rotate /etc/kubernetes/kubelet.conf 2> /dev/null use_multiple_values: true tests: bin_op: or test_items: - flag: rotateCertificates compare: op: eq value: true - flag: RotateKubeletServerCertificate compare: op: eq value: true remediation: | By default, kubelet server certificate rotation is disabled. scored: false - id: 4.2.13 text: "Ensure that the Kubelet only makes use of Strong Cryptographic Ciphers (Manual)" audit: | # needs verification # verify cipher suites oc describe --namespace=openshift-ingress-operator ingresscontroller/default oc get kubeapiservers.operator.openshift.io cluster -o json |jq .spec.observedConfig.servingInfo oc get openshiftapiservers.operator.openshift.io cluster -o json |jq .spec.observedConfig.servingInfo oc get cm -n openshift-authentication v4-0-config-system-cliconfig -o jsonpath='{.data.v4\-0\-config\-system\-cliconfig}' | jq .servingInfo #check value for tlsSecurityProfile; null is returned if default is used oc get kubeapiservers.operator.openshift.io cluster -o json |jq .spec.tlsSecurityProfile type: manual remediation: | Follow the directions above and in the OpenShift documentation to configure the tlsSecurityProfile. Configuring Ingress scored: false ================================================ FILE: cfg/rh-1.0/policies.yaml ================================================ --- controls: version: rh-1.0 id: 5 text: "Kubernetes Policies" type: "policies" groups: - id: 5.1 text: "RBAC and Service Accounts" checks: - id: 5.1.1 text: "Ensure that the cluster-admin role is only used where required (Manual)" type: "manual" audit: | #To get a list of users and service accounts with the cluster-admin role oc get clusterrolebindings -o=customcolumns=NAME:.metadata.name,ROLE:.roleRef.name,SUBJECT:.subjects[*].kind | grep cluster-admin #To verity that kbueadmin is removed, no results should be returned oc get secrets kubeadmin -n kube-system remediation: | Identify all clusterrolebindings to the cluster-admin role. Check if they are used and if they need this role or if they could use a role with fewer privileges. Where possible, first bind users to a lower privileged role and then remove the clusterrolebinding to the cluster-admin role : kubectl delete clusterrolebinding [name] scored: false - id: 5.1.2 text: "Minimize access to secrets (Manual)" type: "manual" remediation: | Where possible, remove get, list and watch access to secret objects in the cluster. scored: false - id: 5.1.3 text: "Minimize wildcard use in Roles and ClusterRoles (Manual)" type: "manual" audit: | #needs verification oc get roles --all-namespaces -o yaml for i in $(oc get roles -A -o jsonpath='{.items[*].metadata.name}'); do oc describe clusterrole ${i}; done #Retrieve the cluster roles defined in the cluster and review for wildcards oc get clusterroles -o yaml for i in $(oc get clusterroles -o jsonpath='{.items[*].metadata.name}'); do oc describe clusterrole ${i}; done remediation: | Where possible replace any use of wildcards in clusterroles and roles with specific objects or actions. scored: false - id: 5.1.4 text: "Minimize access to create pods (Manual)" type: "manual" remediation: | Where possible, remove create access to pod objects in the cluster. scored: false - id: 5.1.5 text: "Ensure that default service accounts are not actively used. (Manual)" type: "manual" remediation: | None required. scored: false - id: 5.1.6 text: "Ensure that Service Account Tokens are only mounted where necessary (Manual)" type: "manual" remediation: | Modify the definition of pods and service accounts which do not need to mount service account tokens to disable it. scored: false - id: 5.2 text: "Pod Security Policies" checks: - id: 5.2.1 text: "Minimize the admission of privileged containers (Manual)" audit: | # needs verification oc get scc -o=custom-columns=NAME:.metadata.name,allowPrivilegedContainer:.allowPrivilegedContainer tests: test_items: - flag: "false" remediation: | Create a SCC as described in the OpenShift documentation, ensuring that the Allow Privileged field is set to false. scored: false - id: 5.2.2 text: "Minimize the admission of containers wishing to share the host process ID namespace (Manual)" audit: | oc get scc -o=custom-columns=NAME:.metadata.name,allowHostPID:.allowHostPID tests: test_items: - flag: "false" remediation: | Create a SCC as described in the OpenShift documentation, ensuring that the Allow Host PID field is set to false. scored: false - id: 5.2.3 text: "Minimize the admission of containers wishing to share the host IPC namespace (Manual)" audit: | oc get scc -o=custom-columns=NAME:.metadata.name,allowHostIPC:.allowHostIPC tests: test_items: - flag: "false" remediation: | Create a SCC as described in the OpenShift documentation, ensuring that the Allow Host IPC field is set to false. scored: false - id: 5.2.4 text: "Minimize the admission of containers wishing to share the host network namespace (Manual)" audit: | oc get scc -o=custom-columns=NAME:.metadata.name,allowHostNetwork:.allowHostNetwork tests: test_items: - flag: "false" remediation: | Create a SCC as described in the OpenShift documentation, ensuring that the Allow Host Network field is omitted or set to false. scored: false - id: 5.2.5 text: "Minimize the admission of containers with allowPrivilegeEscalation (Manual)" audit: | oc get scc -o=custom-columns=NAME:.metadata.name,allowPrivilegeEscalation:.allowPrivilegeEscalation tests: test_items: - flag: "false" remediation: | Create a SCC as described in the OpenShift documentation, ensuring that the Allow Privilege Escalation field is omitted or set to false. scored: false - id: 5.2.6 text: "Minimize the admission of root containers (Manual)" audit: | # needs verification # | awk 'NR>1 {gsub("map\\[type:", "", $2); gsub("\\]$", "", $2); print $1 ":" $2}' oc get scc -o=custom-columns=NAME:.metadata.name,runAsUser:.runAsUser.type #For SCCs with MustRunAs verify that the range of UIDs does not include 0 oc get scc -o=custom-columns=NAME:.metadata.name,uidRangeMin:.runAsUser.uidRangeMin,uidRangeMax:.runAsUser.uidRangeMax tests: bin_op: or test_items: - flag: "MustRunAsNonRoot" - flag: "MustRunAs" compare: op: nothave value: 0 remediation: | None required. By default, OpenShift includes the non-root SCC with the the Run As User Strategy is set to either MustRunAsNonRoot. If additional SCCs are appropriate, follow the OpenShift documentation to create custom SCCs. scored: false - id: 5.2.7 text: "Minimize the admission of containers with the NET_RAW capability (Manual)" audit: | # needs verification oc get scc -o=custom-columns=NAME:.metadata.name,requiredDropCapabilities:.requiredDropCapabilities tests: bin_op: or test_items: - flag: "ALL" - flag: "NET_RAW" remediation: | Create a SCC as described in the OpenShift documentation, ensuring that the Required Drop Capabilities is set to include either NET_RAW or ALL. scored: false - id: 5.2.8 text: "Minimize the admission of containers with added capabilities (Manual)" type: "manual" remediation: | Ensure that Allowed Capabilities is set to an empty array for every SCC in the cluster except for the privileged SCC. scored: false - id: 5.2.9 text: "Minimize the admission of containers with capabilities assigned (Manual)" type: "manual" remediation: | Review the use of capabilites in applications running on your cluster. Where a namespace contains applications which do not require any Linux capabities to operate consider adding a SCC which forbids the admission of containers which do not drop all capabilities. scored: false - id: 5.3 text: "Network Policies and CNI" checks: - id: 5.3.1 text: "Ensure that the CNI in use supports Network Policies (Manual)" type: "manual" remediation: | None required. scored: false - id: 5.3.2 text: "Ensure that all Namespaces have Network Policies defined (Manual)" type: "manual" audit: | #Run the following command and review the NetworkPolicy objects created in the cluster. oc -n all get networkpolicy remediation: | Follow the documentation and create NetworkPolicy objects as you need them. scored: false - id: 5.4 text: "Secrets Management" checks: - id: 5.4.1 text: "Prefer using secrets as files over secrets as environment variables (Manual)" type: "manual" audit: | #Run the following command to find references to objects which use environment variables defined from secrets. oc get all -o jsonpath='{range .items[?(@..secretKeyRef)]} {.kind} {.metadata.name} {"\n"}{end}' -A remediation: | If possible, rewrite application code to read secrets from mounted secret files, rather than from environment variables. scored: false - id: 5.4.2 text: "Consider external secret storage (Manual)" type: "manual" remediation: | Refer to the secrets management options offered by your cloud provider or a third-party secrets management solution. scored: false - id: 5.5 text: "Extensible Admission Control" checks: - id: 5.5.1 text: "Configure Image Provenance using ImagePolicyWebhook admission controller (Manual)" type: "manual" remediation: | Follow the OpenShift documentation: [Image configuration resources](https://docs.openshift.com/container-platform/4.5/openshift_images/image-configuration.html scored: false - id: 5.7 text: "General Policies" checks: - id: 5.7.1 text: "Create administrative boundaries between resources using namespaces (Manual)" type: "manual" audit: | #Run the following command and review the namespaces created in the cluster. oc get namespaces #Ensure that these namespaces are the ones you need and are adequately administered as per your requirements. remediation: | Follow the documentation and create namespaces for objects in your deployment as you need them. scored: false - id: 5.7.2 text: "Ensure that the seccomp profile is set to docker/default in your pod definitions (Manual)" type: "manual" remediation: | To enable the default seccomp profile, use the reserved value /runtime/default that will make sure that the pod uses the default policy available on the host. scored: false - id: 5.7.3 text: "Apply Security Context to Your Pods and Containers (Manual)" type: "manual" remediation: | Follow the Kubernetes documentation and apply security contexts to your pods. For a suggested list of security contexts, you may refer to the CIS Security Benchmark for Docker Containers. scored: false - id: 5.7.4 text: "The default namespace should not be used (Manual)" type: "manual" audit: | #Run this command to list objects in default namespace oc project default oc get all #The only entries there should be system managed resources such as the kubernetes and openshift service remediation: | Ensure that namespaces are created to allow for appropriate segregation of Kubernetes resources and that all new resources are created in a specific namespace. scored: false ================================================ FILE: cfg/rh-1.4/config.yaml ================================================ --- ## Version-specific settings that override the values in cfg/config.yaml ================================================ FILE: cfg/rh-1.4/controlplane.yaml ================================================ --- controls: version: rh-1.4 id: 3 text: "Control Plane Configuration" type: "controlplane" groups: - id: 3.1 text: "Authentication and Authorization" checks: - id: 3.1.1 text: "Client certificate authentication should not be used for users (Manual)" audit: | # To verify user authentication is enabled oc describe authentication # To verify that an identity provider is configured oc get identity # To verify that a custom cluster-admin user exists oc get clusterrolebindings -o=custom-columns=NAME:.metadata.name,ROLE:.roleRef.name,SUBJECT:.subjects[*].kind | grep cluster-admin | grep User # To verity that kbueadmin is removed, no results should be returned oc get secrets kubeadmin -n kube-system type: manual remediation: | Configure an identity provider for the OpenShift cluster. Understanding identity provider configuration | Authentication | OpenShift Container Platform 4.5. Once an identity provider has been defined, you can use RBAC to define and apply permissions. After you define an identity provider and create a new cluster-admin user, remove the kubeadmin user to improve cluster security. scored: false - id: 3.2 text: "Logging" checks: - id: 3.2.1 text: "Ensure that a minimal audit policy is created (Manual)" audit: | #To view kube apiserver log files oc adm node-logs --role=master --path=kube-apiserver/ #To view openshift apiserver log files oc adm node-logs --role=master --path=openshift-apiserver/ #To verify kube apiserver audit config oc get configmap config -n openshift-kube-apiserver -ojson | jq -r '.data["config.yaml"]' | jq '.auditConfig[]?' #To verify openshift apiserver audit config oc get configmap config -n openshift-apiserver -ojson | jq -r '.data["config.yaml"]' | jq '.auditConfig[]?' type: manual remediation: | No remediation required. scored: false - id: 3.2.2 text: "Ensure that the audit policy covers key security concerns (Manual)" audit: | #To verify openshift apiserver audit config oc get configmap config -n openshift-kube-apiserver -ojson | jq -r '.data["config.yaml"]' | jq '.auditConfig.policyConfiguration.rules[]?' #To verify kube apiserver audit config oc get configmap config -n openshift-apiserver -ojson | jq -r '.data["config.yaml"]' | jq '.auditConfig.policyConfiguration.rules[]?' type: manual remediation: | In OpenShift 4.6 and higher, if appropriate for your needs, modify the audit policy. scored: false ================================================ FILE: cfg/rh-1.4/etcd.yaml ================================================ --- controls: version: rh-1.4 id: 2 text: "Etcd" type: "etcd" groups: - id: 2 text: "Etcd" checks: - id: 2.1 text: "Ensure that the --cert-file and --key-file arguments are set as appropriate (Manual)" audit: | # Get the node name where the pod is running NODE_NAME=$(oc get pod "$HOSTNAME" -o=jsonpath='{.spec.nodeName}') # Get the pod name in the openshift-etcd namespace POD_NAME=$(oc get pods -n openshift-etcd -l app=etcd --field-selector spec.nodeName="$NODE_NAME" -o jsonpath='{.items[0].metadata.name}' 2>/dev/null) if [ -z "$POD_NAME" ]; then echo "No matching file found on the current node." else # Execute the stat command oc exec -n openshift-etcd -c etcd "$POD_NAME" -- ps -o command= -C etcd | sed 's/.*\(--cert-file=[^ ]*\).*/\1/' oc exec -n openshift-etcd -c etcd "$POD_NAME" -- ps -o command= -C etcd | sed 's/.*\(--key-file=[^ ]*\).*/\1/' fi use_multiple_values: true tests: test_items: - flag: "file" compare: op: regex value: '\/etc\/kubernetes\/static-pod-certs\/secrets\/etcd-all-(serving|certs)\/etcd-serving-.*\.(?:crt|key)' remediation: | OpenShift does not use the etcd-certfile or etcd-keyfile flags. Certificates for etcd are managed by the etcd cluster operator. scored: true - id: 2.2 text: "Ensure that the --client-cert-auth argument is set to true (Manual)" audit: | # Get the node name where the pod is running NODE_NAME=$(oc get pod "$HOSTNAME" -o=jsonpath='{.spec.nodeName}') # Get the pod name in the openshift-etcd namespace POD_NAME=$(oc get pods -n openshift-etcd -l app=etcd --field-selector spec.nodeName="$NODE_NAME" -o jsonpath='{.items[0].metadata.name}' 2>/dev/null) if [ -z "$POD_NAME" ]; then echo "No matching file found on the current node." else # Execute the stat command oc exec -n openshift-etcd -c etcd "$POD_NAME" -- ps -o command= -C etcd | sed 's/.*\(--client-cert-auth=[^ ]*\).*/\1/' fi use_multiple_values: true tests: test_items: - flag: "--client-cert-auth" compare: op: eq value: true remediation: | This setting is managed by the cluster etcd operator. No remediation required." scored: true - id: 2.3 text: "Ensure that the --auto-tls argument is not set to true (Manual)" audit: | # Returns 0 if found, 1 if not found # Get the node name where the pod is running NODE_NAME=$(oc get pod "$HOSTNAME" -o=jsonpath='{.spec.nodeName}') # Get the pod name in the openshift-etcd namespace POD_NAME=$(oc get pods -n openshift-etcd -l app=etcd --field-selector spec.nodeName="$NODE_NAME" -o jsonpath='{.items[0].metadata.name}' 2>/dev/null) if [ -z "$POD_NAME" ]; then echo "No matching file found on the current node." else # Execute the stat command oc exec -n openshift-etcd -c etcd "$POD_NAME" -- ps -o command= -C etcd | grep -- --auto-tls=true 2>/dev/null ; echo exit_code=$? fi use_multiple_values: true tests: test_items: - flag: "exit_code" compare: op: eq value: "1" remediation: | This setting is managed by the cluster etcd operator. No remediation required. scored: true - id: 2.4 text: "Ensure that the --peer-cert-file and --peer-key-file arguments are set as appropriate (Manual)" audit: | # Get the node name where the pod is running NODE_NAME=$(oc get pod "$HOSTNAME" -o=jsonpath='{.spec.nodeName}') # Get the pod name in the openshift-etcd namespace POD_NAME=$(oc get pods -n openshift-etcd -l app=etcd --field-selector spec.nodeName="$NODE_NAME" -o jsonpath='{.items[0].metadata.name}' 2>/dev/null) if [ -z "$POD_NAME" ]; then echo "No matching file found on the current node." else # Execute the stat command oc exec -n openshift-etcd -c etcd "$POD_NAME" -- ps -o command= -C etcd | sed 's/.*\(--peer-cert-file=[^ ]*\).*/\1/' oc exec -n openshift-etcd -c etcd "$POD_NAME" -- ps -o command= -C etcd | sed 's/.*\(--peer-key-file=[^ ]*\).*/\1/' fi use_multiple_values: true tests: test_items: - flag: "file" compare: op: regex value: '\/etc\/kubernetes\/static-pod-certs\/secrets\/etcd-all-(peer|certs)\/etcd-peer-.*\.(?:crt|key)' remediation: | None. This configuration is managed by the etcd operator. scored: true - id: 2.5 text: "Ensure that the --peer-client-cert-auth argument is set to true (Manual)" audit: | # Get the node name where the pod is running NODE_NAME=$(oc get pod "$HOSTNAME" -o=jsonpath='{.spec.nodeName}') # Get the pod name in the openshift-etcd namespace POD_NAME=$(oc get pods -n openshift-etcd -l app=etcd --field-selector spec.nodeName="$NODE_NAME" -o jsonpath='{.items[0].metadata.name}' 2>/dev/null) if [ -z "$POD_NAME" ]; then echo "No matching file found on the current node." else # Execute the stat command oc exec -n openshift-etcd -c etcd "$POD_NAME" -- ps -o command= -C etcd | sed 's/.*\(--peer-client-cert-auth=[^ ]*\).*/\1/' fi use_multiple_values: true tests: test_items: - flag: "--peer-client-cert-auth" compare: op: eq value: true remediation: | This setting is managed by the cluster etcd operator. No remediation required. scored: true - id: 2.6 text: "Ensure that the --peer-auto-tls argument is not set to true (Manual)" audit: | # Returns 0 if found, 1 if not found # Get the node name where the pod is running NODE_NAME=$(oc get pod "$HOSTNAME" -o=jsonpath='{.spec.nodeName}') # Get the pod name in the openshift-etcd namespace POD_NAME=$(oc get pods -n openshift-etcd -l app=etcd --field-selector spec.nodeName="$NODE_NAME" -o jsonpath='{.items[0].metadata.name}' 2>/dev/null) if [ -z "$POD_NAME" ]; then echo "No matching file found on the current node." else # Execute the stat command oc exec -n openshift-etcd -c etcd "$POD_NAME" -- ps -o command= -C etcd | grep -- --peer-auto-tls=true 2>/dev/null ; echo exit_code=$? fi use_multiple_values: true tests: test_items: - flag: "exit_code" compare: op: eq value: "1" remediation: | This setting is managed by the cluster etcd operator. No remediation required. scored: true - id: 2.7 text: "Ensure that a unique Certificate Authority is used for etcd (Manual)" audit: | # Get the node name where the pod is running NODE_NAME=$(oc get pod "$HOSTNAME" -o=jsonpath='{.spec.nodeName}') # Get the pod name in the openshift-etcd namespace POD_NAME=$(oc get pods -n openshift-etcd -l app=etcd --field-selector spec.nodeName="$NODE_NAME" -o jsonpath='{.items[0].metadata.name}' 2>/dev/null) if [ -z "$POD_NAME" ]; then echo "No matching file found on the current node." else # Execute the stat command oc exec -n openshift-etcd -c etcd "$POD_NAME" -- ps -o command= -C etcd | sed 's/.*\(--trusted-ca-file=[^ ]*\).*/\1/' oc exec -n openshift-etcd -c etcd "$POD_NAME" -- ps -o command= -C etcd | sed 's/.*\(--peer-trusted-ca-file=[^ ]*\).*/\1/' fi use_multiple_values: true tests: test_items: - flag: "file" compare: op: regex value: '\/etc\/kubernetes\/static-pod-certs\/configmaps\/(?:etcd-(?:serving|peer-client)-ca\/ca-bundle\.crt|etcd-all-bundles\/server-ca-bundle\.crt)' remediation: | None required. Certificates for etcd are managed by the OpenShift cluster etcd operator. scored: true ================================================ FILE: cfg/rh-1.4/master.yaml ================================================ --- controls: version: rh-1.4 id: 1 text: "Control Plane Components" type: "master" groups: - id: 1.1 text: "Master Node Configuration Files" checks: - id: 1.1.1 text: "Ensure that the API server pod specification file permissions are set to 600 or more restrictive (Manual)" audit: | # Get the node name where the pod is running NODE_NAME=$(oc get pod "$HOSTNAME" -o=jsonpath='{.spec.nodeName}') # Get the pod name in the openshift-kube-apiserver namespace POD_NAME=$(oc get pods -n openshift-kube-apiserver -l app=openshift-kube-apiserver --field-selector spec.nodeName="$NODE_NAME" -o jsonpath='{.items[0].metadata.name}' 2>/dev/null) if [ -z "$POD_NAME" ]; then echo "No matching pods found on the current node." else # Execute the stat command oc exec -n openshift-kube-apiserver "$POD_NAME" -- stat -c "$POD_NAME %n permissions=%a" /etc/kubernetes/static-pod-resources/kube-apiserver-pod.yaml fi use_multiple_values: true tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | No remediation required; file permissions are managed by the operator. scored: true - id: 1.1.2 text: "Ensure that the API server pod specification file ownership is set to root:root (Manual)" audit: | # Get the node name where the pod is running NODE_NAME=$(oc get pod "$HOSTNAME" -o=jsonpath='{.spec.nodeName}') # Get the pod name in the openshift-kube-apiserver namespace POD_NAME=$(oc get pods -n openshift-kube-apiserver -l app=openshift-kube-apiserver --field-selector spec.nodeName="$NODE_NAME" -o jsonpath='{.items[0].metadata.name}' 2>/dev/null) if [ -z "$POD_NAME" ]; then echo "No matching pods found on the current node." else # Execute the stat command oc exec -n openshift-kube-apiserver "$POD_NAME" -- stat -c "$POD_NAME %n %U:%G" /etc/kubernetes/static-pod-resources/kube-apiserver-pod.yaml fi use_multiple_values: true tests: test_items: - flag: "root:root" remediation: | No remediation required; file permissions are managed by the operator. scored: true - id: 1.1.3 text: "Ensure that the controller manager pod specification file permissions are set to 600 or more restrictive (Manual)" audit: | # Get the node name where the pod is running NODE_NAME=$(oc get pod "$HOSTNAME" -o=jsonpath='{.spec.nodeName}') # Get the pod name in the openshift-kube-controller-manager namespace POD_NAME=$(oc get pods -n openshift-kube-controller-manager -l app=kube-controller-manager --field-selector spec.nodeName="$NODE_NAME" -o jsonpath='{.items[0].metadata.name}' 2>/dev/null) if [ -z "$POD_NAME" ]; then echo "No matching pods found on the current node." else # Execute the stat command oc exec -n openshift-kube-controller-manager "$POD_NAME" -- stat -c "$POD_NAME %n permissions=%a" /etc/kubernetes/static-pod-resources/kube-controller-manager-pod.yaml fi use_multiple_values: true tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | No remediation required; file permissions are managed by the operator. scored: true - id: 1.1.4 text: "Ensure that the controller manager pod specification file ownership is set to root:root (Manual)" audit: | # Get the node name where the pod is running NODE_NAME=$(oc get pod "$HOSTNAME" -o=jsonpath='{.spec.nodeName}') # Get the pod name in the openshift-kube-controller-manager namespace POD_NAME=$(oc get pods -n openshift-kube-controller-manager -l app=kube-controller-manager --field-selector spec.nodeName="$NODE_NAME" -o jsonpath='{.items[0].metadata.name}' 2>/dev/null) if [ -z "$POD_NAME" ]; then echo "No matching pods found on the current node." else # Execute the stat command oc exec -n openshift-kube-controller-manager "$POD_NAME" -- stat -c "$POD_NAME %n %U:%G" /etc/kubernetes/static-pod-resources/kube-controller-manager-pod.yaml fi use_multiple_values: true tests: test_items: - flag: "root:root" remediation: | No remediation required; file permissions are managed by the operator. scored: true - id: 1.1.5 text: "Ensure that the scheduler pod specification file permissions are set to 600 or more restrictive (Manual)" audit: | # Get the node name where the pod is running NODE_NAME=$(oc get pod "$HOSTNAME" -o=jsonpath='{.spec.nodeName}') # Get the pod name in the openshift-kube-scheduler namespace POD_NAME=$(oc get pods -n openshift-kube-scheduler -l app=openshift-kube-scheduler --field-selector spec.nodeName="$NODE_NAME" -o jsonpath='{.items[0].metadata.name}' 2>/dev/null) if [ -z "$POD_NAME" ]; then echo "No matching pods found on the current node." else # Execute the stat command oc exec -n openshift-kube-scheduler "$POD_NAME" -- stat -c "$POD_NAME %n permissions=%a" /etc/kubernetes/static-pod-resources/kube-scheduler-pod.yaml fi use_multiple_values: true tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | No remediation required; file permissions are managed by the operator. scored: true - id: 1.1.6 text: "Ensure that the scheduler pod specification file ownership is set to root:root (Manual))" audit: | # Get the node name where the pod is running NODE_NAME=$(oc get pod "$HOSTNAME" -o=jsonpath='{.spec.nodeName}') # Get the pod name in the openshift-kube-scheduler namespace POD_NAME=$(oc get pods -n openshift-kube-scheduler -l app=openshift-kube-scheduler --field-selector spec.nodeName="$NODE_NAME" -o jsonpath='{.items[0].metadata.name}' 2>/dev/null) if [ -z "$POD_NAME" ]; then echo "No matching pods found on the current node." else # Execute the stat command oc exec -n openshift-kube-scheduler "$POD_NAME" -- stat -c "$POD_NAME %n %U:%G" /etc/kubernetes/static-pod-resources/kube-scheduler-pod.yaml fi use_multiple_values: true tests: test_items: - flag: "root:root" remediation: | No remediation required; file permissions are managed by the operator. scored: true - id: 1.1.7 text: "Ensure that the etcd pod specification file permissions are set to 600 or more restrictive (Manual))" audit: | # Get the node name where the pod is running NODE_NAME=$(oc get pod "$HOSTNAME" -o=jsonpath='{.spec.nodeName}') # Get the pod name in the openshift-etcd namespace POD_NAME=$(oc get pods -n openshift-etcd -l app=etcd --field-selector spec.nodeName="$NODE_NAME" -o jsonpath='{.items[0].metadata.name}' 2>/dev/null) if [ -z "$POD_NAME" ]; then echo "No matching pods found on the current node." else # Execute the stat command oc rsh -n openshift-etcd "$POD_NAME" stat -c "$POD_NAME %n permissions=%a" /etc/kubernetes/manifests/etcd-pod.yaml fi use_multiple_values: true tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | No remediation required; file permissions are managed by the operator. scored: true - id: 1.1.8 text: "Ensure that the etcd pod specification file ownership is set to root:root (Manual)" audit: | # Get the node name where the pod is running NODE_NAME=$(oc get pod "$HOSTNAME" -o=jsonpath='{.spec.nodeName}') # Get the pod name in the openshift-etcd namespace POD_NAME=$(oc get pods -n openshift-etcd -l app=etcd --field-selector spec.nodeName="$NODE_NAME" -o jsonpath='{.items[0].metadata.name}' 2>/dev/null) if [ -z "$POD_NAME" ]; then echo "No matching pods found on the current node." else # Execute the stat command oc rsh -n openshift-etcd "$POD_NAME" stat -c "$POD_NAME %n %U:%G" /etc/kubernetes/manifests/etcd-pod.yaml fi use_multiple_values: true tests: test_items: - flag: "root:root" remediation: | No remediation required; file permissions are managed by the operator. scored: true - id: 1.1.9 text: "Ensure that the Container Network Interface file permissions are set to 600 or more restrictive (Manual)" audit: | # Get the node name where the pod is running NODE_NAME=$(oc get pod "$HOSTNAME" -o=jsonpath='{.spec.nodeName}') # For CNI multus # Get the pod name in the openshift-multus namespace POD_NAME=$(oc get pods -n openshift-multus -l app=multus --field-selector spec.nodeName="$NODE_NAME" -o jsonpath='{.items[0].metadata.name}' 2>/dev/null) if [ -z "$POD_NAME" ]; then echo "No matching pods found on the current node." else # Execute the stat command oc exec -n openshift-multus "$POD_NAME" -- /bin/bash -c "stat -c \"$i %n permissions=%a\" /host/etc/cni/net.d/*.conf"; 2>/dev/null oc exec -n openshift-multus "$POD_NAME" -- /bin/bash -c "stat -c \"$i %n permissions=%a\" /host/var/run/multus/cni/net.d/*.conf"; 2>/dev/null fi # For SDN pods POD_NAME=$(oc get pods -n openshift-sdn -l app=sdn --field-selector spec.nodeName="$NODE_NAME" -o jsonpath='{.items[0].metadata.name}' 2>/dev/null) if [ -z "$POD_NAME" ]; then echo "No matching pods found on the current node." else # Execute the stat command oc exec -n openshift-sdn "$POD_NAME" -- find /var/lib/cni/networks/openshift-sdn -type f -exec stat -c "$i %n permissions=%a" {} \; 2>/dev/null oc exec -n openshift-sdn "$POD_NAME" -- find /var/run/openshift-sdn -type f -exec stat -c "$i %n permissions=%a" {} \; 2>/dev/null fi # For OVS pods POD_NAME=$(oc get pods -n openshift-sdn -l app=ovs --field-selector spec.nodeName="$NODE_NAME" -o jsonpath='{.items[0].metadata.name}' 2>/dev/null) if [ -z "$POD_NAME" ]; then echo "No matching pods found on the current node." else # Execute the stat command oc exec -n openshift-sdn "$POD_NAME" -- find /var/run/openvswitch -type f -exec stat -c "$i %n permissions=%a" {} \; 2>/dev/null oc exec -n openshift-sdn "$POD_NAME" -- find /etc/openvswitch -type f -exec stat -c "$i %n permissions=%a" {} \; 2>/dev/null oc exec -n openshift-sdn "$POD_NAME" -- find /run/openvswitch -type f -exec stat -c "$i %n permissions=%a" {} \; 2>/dev/null fi use_multiple_values: true tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | No remediation required; file permissions are managed by the operator. scored: true - id: 1.1.10 text: "Ensure that the Container Network Interface file ownership is set to root:root (Manual)" audit: | # Get the node name where the pod is running NODE_NAME=$(oc get pod "$HOSTNAME" -o=jsonpath='{.spec.nodeName}') # For CNI multus # Get the pod name in the openshift-multus namespace POD_NAME=$(oc get pods -n openshift-multus -l app=multus --field-selector spec.nodeName="$NODE_NAME" -o jsonpath='{.items[0].metadata.name}' 2>/dev/null) if [ -z "$POD_NAME" ]; then echo "No matching pods found on the current node." else # Execute the stat command oc exec -n openshift-multus "$POD_NAME" -- /bin/bash -c "stat -c '$i %n %U:%G' /host/etc/cni/net.d/*.conf" 2>/dev/null oc exec -n openshift-multus $i -- /bin/bash -c "stat -c '$i %n %U:%G' /host/var/run/multus/cni/net.d/*.conf" 2>/dev/null fi # For SDN pods POD_NAME=$(oc get pods -n openshift-sdn -l app=sdn --field-selector spec.nodeName="$NODE_NAME" -o jsonpath='{.items[0].metadata.name}' 2>/dev/null) if [ -z "$POD_NAME" ]; then echo "No matching pods found on the current node." else # Execute the stat command oc exec -n openshift-sdn "$POD_NAME" -- find /var/lib/cni/networks/openshift-sdn -type f -exec stat -c "$i %n %U:%G" {} \; 2>/dev/null oc exec -n openshift-sdn "$POD_NAME" -- find /var/run/openshift-sdn -type f -exec stat -c "$i %n %U:%G" {} \; 2>/dev/null fi # For OVS pods in 4.5 POD_NAME=$(oc get pods -n openshift-sdn -l app=ovs --field-selector spec.nodeName="$NODE_NAME" -o jsonpath='{.items[0].metadata.name}' 2>/dev/null) if [ -z "$POD_NAME" ]; then echo "No matching pods found on the current node." else # Execute the stat command oc exec -n openshift-sdn "$POD_NAME" -- find /var/run/openvswitch -type f -exec stat -c "$i %n %U:%G" {} \; 2>/dev/null oc exec -n openshift-sdn "$POD_NAME" -- find /etc/openvswitch -type f -exec stat -c "$i %n %U:%G" {} \; 2>/dev/null oc exec -n openshift-sdn "$POD_NAME" -- find /run/openvswitch -type f -exec stat -c "$i %n %U:%G" {} \; 2>/dev/null fi use_multiple_values: true tests: test_items: - flag: "root:root" remediation: | No remediation required; file permissions are managed by the operator. scored: true - id: 1.1.11 text: "Ensure that the etcd data directory permissions are set to 700 or more restrictive (Manual)" audit: | # Get the node name where the pod is running NODE_NAME=$(oc get pod "$HOSTNAME" -o=jsonpath='{.spec.nodeName}') # Get the pod name in the openshift-etcd namespace POD_NAME=$(oc get pods -n openshift-etcd -l app=etcd --field-selector spec.nodeName="$NODE_NAME" -o jsonpath='{.items[0].metadata.name}' 2>/dev/null) if [ -z "$POD_NAME" ]; then echo "No matching pods found on the current node." else # Execute the stat command oc exec -n openshift-etcd "$POD_NAME" -- stat -c "$POD_NAME %n permissions=%a" /var/lib/etcd/member fi use_multiple_values: true tests: test_items: - flag: "permissions" compare: op: bitmask value: "700" remediation: | No remediation required; file permissions are managed by the operator. scored: true - id: 1.1.12 text: "Ensure that the etcd data directory ownership is set to etcd:etcd (Manual)" audit: | # Get the node name where the pod is running NODE_NAME=$(oc get pod "$HOSTNAME" -o=jsonpath='{.spec.nodeName}') # Get the pod name in the openshift-etcd namespace POD_NAME=$(oc get pods -n openshift-etcd -l app=etcd --field-selector spec.nodeName="$NODE_NAME" -o jsonpath='{.items[0].metadata.name}' 2>/dev/null) if [ -z "$POD_NAME" ]; then echo "No matching pods found on the current node." else # Execute the stat command oc exec -n openshift-etcd "$POD_NAME" -- stat -c "$POD_NAME %n %U:%G" /var/lib/etcd/member fi use_multiple_values: true tests: test_items: - flag: "root:root" remediation: | No remediation required; file permissions are managed by the operator. scored: true - id: 1.1.13 text: "Ensure that the kubeconfig file permissions are set to 600 or more restrictive (Manual)" audit: | NODE_NAME=$(oc get pod $HOSTNAME -o=jsonpath='{.spec.nodeName}') oc debug node/$NODE_NAME -- chroot /host stat -c "$NODE_NAME %n permissions=%a" /etc/kubernetes/kubeconfig 2> /dev/null use_multiple_values: true tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | No remediation required; file permissions are managed by the operator. scored: true - id: 1.1.14 text: "Ensure that the kubeconfig file ownership is set to root:root (Manual)" audit: | NODE_NAME=$(oc get pod $HOSTNAME -o=jsonpath='{.spec.nodeName}') oc debug node/$NODE_NAME -- chroot /host stat -c "$NODE_NAME %n %U:%G" /etc/kubernetes/kubeconfig 2> /dev/null use_multiple_values: true tests: test_items: - flag: "root:root" remediation: | No remediation required; file permissions are managed by the operator. scored: true - id: 1.1.15 text: "Ensure that the scheduler kubeconfig file permissions are set to 600 or more restrictive (Manual)" audit: | # Get the node name where the pod is running NODE_NAME=$(oc get pod "$HOSTNAME" -o=jsonpath='{.spec.nodeName}') # Get the pod name in the openshift-kube-scheduler namespace POD_NAME=$(oc get pods -n openshift-kube-scheduler -l app=openshift-kube-scheduler --field-selector spec.nodeName="$NODE_NAME" -o jsonpath='{.items[0].metadata.name}' 2>/dev/null) if [ -z "$POD_NAME" ]; then echo "No matching pods found on the current node." else # Execute the stat command oc exec -n openshift-kube-scheduler "$POD_NAME" -- stat -c "$POD_NAME %n permissions=%a" /etc/kubernetes/static-pod-resources/configmaps/scheduler-kubeconfig/kubeconfig fi use_multiple_values: true tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | No remediation required; file permissions are managed by the operator. scored: true - id: 1.1.16 text: "Ensure that the scheduler kubeconfig file ownership is set to root:root (Manual)" audit: | # Get the node name where the pod is running NODE_NAME=$(oc get pod "$HOSTNAME" -o=jsonpath='{.spec.nodeName}') # Get the pod name in the openshift-kube-scheduler namespace POD_NAME=$(oc get pods -n openshift-kube-scheduler -l app=openshift-kube-scheduler --field-selector spec.nodeName="$NODE_NAME" -o jsonpath='{.items[0].metadata.name}' 2>/dev/null) if [ -z "$POD_NAME" ]; then echo "No matching pods found on the current node." else # Execute the stat command oc exec -n openshift-kube-scheduler "$POD_NAME" -- stat -c "$POD_NAME %n %U:%G" /etc/kubernetes/static-pod-resources/configmaps/scheduler-kubeconfig/kubeconfig fi use_multiple_values: true tests: test_items: - flag: "root:root" remediation: | No remediation required; file permissions are managed by the operator. scored: true - id: 1.1.17 text: "Ensure that the controller-manager kubeconfig file permissions are set to 600 or more restrictive (Manual)" audit: | # Get the node name where the pod is running NODE_NAME=$(oc get pod "$HOSTNAME" -o=jsonpath='{.spec.nodeName}') # Get the pod name in the openshift-kube-controller-manager namespace POD_NAME=$(oc get pods -n openshift-kube-controller-manager -l app=kube-controller-manager --field-selector spec.nodeName="$NODE_NAME" -o jsonpath='{.items[0].metadata.name}' 2>/dev/null) if [ -z "$POD_NAME" ]; then echo "No matching pods found on the current node." else # Execute the stat command oc exec -n openshift-kube-controller-manager "$POD_NAME" -- stat -c "$POD_NAME %n permissions=%a" /etc/kubernetes/static-pod-resources/configmaps/controller-manager-kubeconfig/kubeconfig fi use_multiple_values: true tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | No remediation required; file permissions are managed by the operator. scored: true - id: 1.1.18 text: "Ensure that the controller-manager kubeconfig file ownership is set to root:root (Manual)" audit: | # Get the node name where the pod is running NODE_NAME=$(oc get pod "$HOSTNAME" -o=jsonpath='{.spec.nodeName}') # Get the pod name in the openshift-kube-controller-manager namespace POD_NAME=$(oc get pods -n openshift-kube-controller-manager -l app=kube-controller-manager --field-selector spec.nodeName="$NODE_NAME" -o jsonpath='{.items[0].metadata.name}' 2>/dev/null) if [ -z "$POD_NAME" ]; then echo "No matching pods found on the current node." else # Execute the stat command oc exec -n openshift-kube-controller-manager "$POD_NAME" -- stat -c "$POD_NAME %n %U:%G" /etc/kubernetes/static-pod-resources/configmaps/controller-manager-kubeconfig/kubeconfig fi use_multiple_values: true tests: test_items: - flag: "root:root" remediation: | No remediation required; file permissions are managed by the operator. scored: true - id: 1.1.19 text: "Ensure that the OpenShift PKI directory and file ownership is set to root:root (Manual)" audit: | # Should return root:root for all files and directories # Get the node name where the pod is running NODE_NAME=$(oc get pod "$HOSTNAME" -o=jsonpath='{.spec.nodeName}') # Get the pod name in the openshift-kube-controller-manager namespace POD_NAME=$(oc get pods -n openshift-kube-apiserver -l app=openshift-kube-apiserver --field-selector spec.nodeName="$NODE_NAME" -o jsonpath='{.items[0].metadata.name}' 2>/dev/null) if [ -z "$POD_NAME" ]; then echo "No matching pods found on the current node." else # echo $i static-pod-certs oc exec -n openshift-kube-apiserver "$POD_NAME" -c kube-apiserver -- find /etc/kubernetes/static-pod-certs -type d -wholename '*/secrets*' -exec stat -c "$i %n %U:%G" {} \; oc exec -n openshift-kube-apiserver "$POD_NAME" -c kube-apiserver -- find /etc/kubernetes/static-pod-certs -type f -wholename '*/secrets*' -exec stat -c "$i %n %U:%G" {} \; # echo $i static-pod-resources oc exec -n openshift-kube-apiserver "$POD_NAME" -c kube-apiserver -- find /etc/kubernetes/static-pod-resources -type d -wholename '*/secrets*' -exec stat -c "$i %n %U:%G" {} \; oc exec -n openshift-kube-apiserver "$POD_NAME" -c kube-apiserver -- find /etc/kubernetes/static-pod-resources -type f -wholename '*/secrets*' -exec stat -c "$i %n %U:%G" {} \; fi use_multiple_values: true tests: test_items: - flag: "root:root" remediation: | No remediation required; file permissions are managed by the operator. scored: true - id: 1.1.20 text: "Ensure that the OpenShift PKI certificate file permissions are set to 600 or more restrictive (Manual)" audit: | # Get the node name where the pod is running NODE_NAME=$(oc get pod "$HOSTNAME" -o=jsonpath='{.spec.nodeName}') # Get the pod name in the openshift-kube-apiserver namespace POD_NAME=$(oc get pods -n openshift-kube-apiserver -l app=openshift-kube-apiserver --field-selector spec.nodeName="$NODE_NAME" -o jsonpath='{.items[0].metadata.name}' 2>/dev/null) if [ -z "$POD_NAME" ]; then echo "No matching pods found on the current node." else # Execute the stat command oc exec -n openshift-kube-apiserver "$POD_NAME" -c kube-apiserver -- find /etc/kubernetes/static-pod-certs -type f -wholename '*/secrets/*.crt' -exec stat -c "$POD_NAME %n permissions=%a" {} \; fi use_multiple_values: true tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | No remediation required; file permissions are managed by the operator. scored: true - id: 1.1.21 text: "Ensure that the OpenShift PKI key file permissions are set to 600 (Manual)" audit: | # Get the node name where the pod is running NODE_NAME=$(oc get pod "$HOSTNAME" -o=jsonpath='{.spec.nodeName}') # Get the pod name in the openshift-kube-apiserver namespace POD_NAME=$(oc get pods -n openshift-kube-apiserver -l app=openshift-kube-apiserver --field-selector spec.nodeName="$NODE_NAME" -o jsonpath='{.items[0].metadata.name}' 2>/dev/null) if [ -z "$POD_NAME" ]; then echo "No matching pods found on the current node." else # Execute the stat command oc exec -n openshift-kube-apiserver "$POD_NAME" -c kube-apiserver -- find /etc/kubernetes/static-pod-certs -type f -wholename '*/secrets/*.key' -exec stat -c "$POD_NAME %n permissions=%a" {} \; fi use_multiple_values: true tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | No remediation required; file permissions are managed by the operator. scored: true - id: 1.2 text: "API Server" checks: - id: 1.2.1 text: "Ensure that anonymous requests are authorized (Manual)" audit: | found=0 echo "# ClusterRoleBindings granting permissions to system:unauthenticated" crb_out="$(oc get clusterrolebindings -o json 2>/dev/null \ | jq -r '.items[] | select(.subjects[]? | select(.kind=="Group" and .name=="system:unauthenticated")) | .metadata.name + " -> " + .roleRef.kind + "/" + .roleRef.name' \ | sort -u)" if [ -n "$crb_out" ]; then echo "$crb_out" found=1 else echo "(none)" fi echo echo "# Namespaced RoleBindings granting permissions to system:unauthenticated" rb_out="$(oc get rolebindings -A -o json 2>/dev/null \ | jq -r '.items[] | select(.subjects[]? | select(.kind=="Group" and .name=="system:unauthenticated")) | (.metadata.namespace + "/" + .metadata.name) + " -> " + .roleRef.kind + "/" + .roleRef.name' \ | sort -u)" if [ -n "$rb_out" ]; then echo "$rb_out" found=1 else echo "(none)" fi # Provide a simple flag for the test harness if [ $found -eq 1 ]; then echo "unauthenticated_bindings_present" else echo "unauthenticated_bindings_missing" fi tests: test_items: - flag: "unauthenticated_bindings_present" set: true remediation: | None required. The default configuration should not be modified. scored: true - id: 1.2.2 text: "Ensure that the --basic-auth-file argument is not set (Manual)" audit: | oc -n openshift-kube-apiserver get cm config -o yaml | grep --color "basic-auth" oc -n openshift-apiserver get cm config -o yaml | grep --color "basic-auth" oc get clusteroperator authentication | awk '$3 != "AVAILABLE" { if ($3){print "available=true"}else{print "available=false"} }' tests: bin_op: and test_items: - flag: "basic-auth-file" set: false - flag: "available" compare: op: eq value: true remediation: | None required. --basic-auth-file cannot be configured on OpenShift. scored: true - id: 1.2.3 text: "Ensure that the --token-auth-file parameter is not set (Manual)" audit: | oc get configmap config -n openshift-kube-apiserver -ojson | jq -r '.data["config.yaml"]' | jq '.apiServerArguments' oc get configmap config -n openshift-apiserver -ojson | jq -r '.data["config.yaml"]' | jq '.apiServerArguments' oc get kubeapiservers.operator.openshift.io cluster -o json | jq '.spec.observedConfig.apiServerArguments' oc get clusteroperator authentication | awk '$3 != "AVAILABLE" { if ($3){print "available=true"}else{print "available=false"} }' tests: bin_op: and test_items: - flag: "token-auth-file" set: false - flag: "available" compare: op: eq value: true remediation: | None is required. scored: true - id: 1.2.4 text: "Use HTTPS for kubelet connections (Manual)" audit: | CFG=$(oc -n openshift-kube-apiserver get cm config -o jsonpath='{.data.config\.yaml}') # Extract kubelet client cert/key paths (support both layouts) CERT_FILE=$(printf '%s\n' "$CFG" \ | grep -Eo '/etc/kubernetes/static-pod-(resources/kube-apiserver-certs|certs)/secrets/kubelet-client/tls\.crt' \ | head -n1) KEY_FILE=$(printf '%s\n' "$CFG" \ | grep -Eo '/etc/kubernetes/static-pod-(resources/kube-apiserver-certs|certs)/secrets/kubelet-client/tls\.key' \ | head -n1) # 1) pass/fail on presence of both files if [ -n "$CERT_FILE" ] && [ -n "$KEY_FILE" ]; then echo "pass" else echo "fail" fi KUBELET_HTTPS=$(printf '%s\n' "$CFG" \ | grep -Eo '(^|[[:space:]])kubelet-https:[[:space:]]*(true|false)' \ | awk -F: '{print $2}' \ | tr -d '[:space:]' \ | head -n1) if [ "$KUBELET_HTTPS" = "false" ]; then echo "false" else echo "true" fi oc -n openshift-apiserver describe secret serving-cert | grep -E 'tls\.crt|tls\.key|Type:' tests: bin_op: and test_items: - flag: "pass" - flag: "true" - flag: "kubernetes.io/tls" - flag: "tls.crt" - flag: "tls.key" remediation: | OpenShift does not use the legacy --kubelet-https flag; TLS is enforced via kubelet client cert/key arguments and cluster CAs. Ensure: - apiServerArguments.kubelet-client-certificate[0] points to a real file - apiServerArguments.kubelet-client-key[0] points to a real file - The openshift-apiserver 'serving-cert' secret is type kubernetes.io/tls and contains tls.crt and tls.key scored: false - id: 1.2.5 text: "Ensure that the kubelet uses certificates to authenticate (Manual)" audit: | oc get configmap config -n openshift-kube-apiserver -ojson | jq -r '.data["config.yaml"]' | jq '.apiServerArguments["kubelet-client-certificate"]' oc get configmap config -n openshift-kube-apiserver -ojson | jq -r'.data["config.yaml"]' | jq '.apiServerArguments["kubelet-client-key"]' oc -n openshift-apiserver describe secret serving-cert tests: test_items: - flag: "/etc/kubernetes/static-pod-certs/secrets/kubelet-client/tls.crt" - flag: "/etc/kubernetes/static-pod-certs/secrets/kubelet-client/tls.key" - flag: "kubernetes.io/tls" remediation: | No remediation is required. OpenShift automatically manages kubelet authentication using X.509 certificates issued by the internal platform CA. Manual modification of these certificates is not supported and can disrupt platform components. scored: true - id: 1.2.6 text: "Verify that the kubelet certificate authority is set as appropriate (Manual)" audit: | oc get configmap config -n openshift-kube-apiserver -ojson \ | jq -r '.data["config.yaml"]' \ | jq '.apiServerArguments["kubelet-certificate-authority"]' tests: test_items: - flag: "/etc/kubernetes/static-pod-resources/configmaps/kubelet-serving-ca/ca-bundle.crt" remediation: | No remediation is required. OpenShift uses internal X.509 certificates and platform-managed CAs to verify kubelet server identities. This is not user-configurable and should not be modified. scored: true - id: 1.2.7 text: "Ensure that the --authorization-mode argument is not set to AlwaysAllow (Manual)" audit: | oc get configmap config -n openshift-kube-apiserver -o json \ | jq -r '.data["config.yaml"]' \ | jq '.apiServerArguments["authorization-mode"]' audit_config: | oc get configmap config -n openshift-kube-apiserver -ojson | jq -r '.data["config.yaml"]' | jq '.apiServerArguments' tests: bin_op: or test_items: - path: "{.authorization-mode}" compare: op: nothave value: "AlwaysAllow" - path: "{.authorization-mode}" flag: "authorization-mode" set: false remediation: | No remediation required. OpenShift does not support the 'AlwaysAllow' authorization mode. The API server is bootstrapped with secure authorization mechanisms including RBAC and Node by default. scored: true - id: 1.2.8 text: "Verify that RBAC is enabled (Manual)" audit: | oc get configmap config -n openshift-kube-apiserver -o json \ | jq -r '.data["config.yaml"]' \ | jq '.apiServerArguments["authorization-mode"]' audit_config: | oc get configmap config -n openshift-kube-apiserver -ojson | jq -r '.data["config.yaml"]' | jq '.apiServerArguments' tests: bin_op: or test_items: - path: "{.authorization-mode}" compare: op: has value: "RBAC" - path: "{.authorization-mode}" flag: "authorization-mode" set: false remediation: | No remediation is required. OpenShift is configured at bootstrap time to use Role-Based Access Control (RBAC) as the default authorization mode. RBAC is always enabled, and cannot be disabled through configuration. scored: true - id: 1.2.9 text: "Ensure that the APIPriorityAndFairness feature gate is enabled (Manual)" audit: | oc get kubeapiservers.operator.openshift.io cluster -o json | jq '.spec.observedConfig.apiServerArguments' oc -n openshift-kube-apiserver get configmap config -o json | jq -r '.data."config.yaml"' | jq '.apiServerArguments."enable-admission-plugins"' oc get kubeapiservers.operator.openshift.io cluster -o json | jq -r '.spec.unsupportedConfigOverrides' tests: bin_op: and test_items: - flag: "APIPriorityAndFairness=true" - flag: "EventRateLimit" set: false remediation: | No remediation is required scored: true - id: 1.2.10 text: "Ensure that the admission control plugin AlwaysAdmit is not set (Manual)" audit: | oc -n openshift-kube-apiserver get configmap config -o json | jq -r '.data."config.yaml"' | jq '.apiServerArguments."enable-admission-plugins"' tests: test_items: - flag: "AlwaysAdmit" set: false remediation: | No remediation is required. The AlwaysAdmit admission controller cannot be enabled in OpenShift. scored: true - id: 1.2.11 text: "Ensure that the admission control plugin AlwaysPullImages is set (Manual)" audit: | oc -n openshift-kube-apiserver get configmap config -o json | jq -r '.data."config.yaml"' | jq '.apiServerArguments."enable-admission-plugins"' tests: test_items: - flag: "AlwaysPullImages" set: false remediation: | None required. scored: true - id: 1.2.12 text: "Ensure that the admission control plugin ServiceAccount is set (Manual)" audit: | oc -n openshift-kube-apiserver get configmap config -o json | jq -r '.data."config.yaml"' | jq '.apiServerArguments."enable-admission-plugins"' tests: test_items: - flag: "ServiceAccount" set: true remediation: | None required. OpenShift is configured to use service accounts by default. scored: true - id: 1.2.13 text: "Ensure that the admission control plugin NamespaceLifecycle is set (Manual)" audit: | oc -n openshift-kube-apiserver get configmap config -o json | jq -r '.data."config.yaml"' | jq '.apiServerArguments."enable-admission-plugins"' output=$(oc -n openshift-kube-apiserver get configmap config -o json | jq -r '.data."config.yaml"' | jq '.apiServerArguments."enable-admission-plugins"') [ "$output" == "null" ] && echo "ocp 4.5 has NamespaceLifecycle compiled" || echo $output tests: test_items: - flag: "NamespaceLifecycle" remediation: | Ensure that the --disable-admission-plugins parameter does not include NamespaceLifecycle. scored: true - id: 1.2.14 text: "Ensure that the admission control plugin SecurityContextConstraint is set (Manual)" audit: | oc -n openshift-kube-apiserver get configmap config -o json | jq -r '.data."config.yaml"' | jq '.apiServerArguments."enable-admission-plugins"' output=$(oc -n openshift-kube-apiserver get configmap config -o json | jq -r '.data."config.yaml"' | jq '.apiServerArguments."enable-admission-plugins"') [ "$output" == "null" ] && echo "ocp 4.5 has SecurityContextConstraint compiled" || echo $output tests: test_items: - flag: "security.openshift.io/SecurityContextConstraint" remediation: | None required. Security Context Constraints are enabled by default in OpenShift and cannot be disabled. scored: true - id: 1.2.15 text: "Ensure that the admission control plugin NodeRestriction is set (Manual)" audit: | oc -n openshift-kube-apiserver get configmap config -o json | jq -r '.data."config.yaml"' | jq '.apiServerArguments."enable-admission-plugins"' output=$(oc -n openshift-kube-apiserver get configmap config -o json | jq -r '.data."config.yaml"' | jq '.apiServerArguments."enable-admission-plugins"') [ "$output" == "null" ] && echo "ocp 4.5 has NodeRestriction compiled" || echo $output tests: test_items: - flag: "NodeRestriction" remediation: | The NodeRestriction plugin cannot be disabled. scored: true - id: 1.2.16 text: "Ensure that the --insecure-bind-address argument is not set (manual)" audit: | # Get the insecure-bind-address value insecure_bind_address=$(oc get kubeapiservers.operator.openshift.io cluster -ojson \ | jq -r '.spec.observedConfig.apiServerArguments["insecure-bind-address"][]?') # Get port from openshift-kube-apiserver kube_api_port=$(oc -n openshift-kube-apiserver get endpoints -o jsonpath='{.items[*].subsets[*].ports[*].port}') # Get port from openshift-apiserver openshift_api_port=$(oc -n openshift-apiserver get endpoints -o jsonpath='{.items[*].subsets[*].ports[*].port}') # Evaluate logic [[ -z "$insecure_bind_address" ]] && \ [[ "$kube_api_port" == *"6443"* ]] && \ [[ "$openshift_api_port" == *"8443"* ]] && echo "pass" || echo "fail" tests: test_items: - flag: "pass" remediation: | No remediation is required. By default, OpenShift uses secure HTTPS ports (6443 and 8443) for all API communications. The API servers are not configured to expose insecure ports and are isolated within the pod network. scored: true - id: 1.2.17 text: "Ensure that the --insecure-port argument is set to 0 (Manual)" audit: | oc -n openshift-kube-apiserver get endpoints -o jsonpath='{.items[*].subsets[*].ports[*].port}' tests: test_items: - flag: "6443" remediation: | None required. The configuration is managed by the API server operator. scored: true - id: 1.2.18 text: "Ensure that the --secure-port argument is not set to 0 (Manual)" audit: | BIND_ADDR=$(oc get kubeapiservers.operator.openshift.io cluster -o json \ | jq -r '.spec.observedConfig.servingInfo.bindAddress') PORTS=$(oc get pods -n openshift-kube-apiserver -l app=openshift-kube-apiserver \ -o jsonpath='{.items[*].spec.containers[?(@.name=="kube-apiserver")].ports[*].containerPort}') if [ "$BIND_ADDR" = "0.0.0.0:6443" ] && echo "$PORTS" | grep -q '\b6443\b'; then echo "pass" else echo "fail" fi tests: test_items: - flag: "pass" remediation: | None required. OpenShift serves the API securely over port 6443 with TLS, authentication, and authorization. The insecure API port is not exposed or configurable by default. scored: true - id: 1.2.19 text: "Ensure that the healthz endpoint is protected by RBAC (Manual)" type: manual remediation: | None required as profiling data is protected by RBAC. scored: false - id: 1.2.20 text: "Ensure that the --audit-log-path argument is set (Manual)" audit: | # Get kube-apiserver audit log path kube_path=$(oc get configmap config -n openshift-kube-apiserver -ojson \ | jq -r '.data["config.yaml"]' \ | jq -r '.apiServerArguments["audit-log-path"][]?') # Get OpenShift apiserver audit log path os_path=$(oc get configmap config -n openshift-apiserver -ojson \ | jq -r '.data["config.yaml"]' \ | jq -r '.apiServerArguments["audit-log-path"][]?') # Check if log file exists in kube-apiserver pod kube_pod=$(oc get pods -n openshift-kube-apiserver -l app=openshift-kube-apiserver -o jsonpath='{.items[0].metadata.name}') oc rsh -n openshift-kube-apiserver -c kube-apiserver $kube_pod ls "$kube_path" >/dev/null 2>&1 kube_exists=$? # Check if log file exists in openshift-apiserver pod os_pod=$(oc get pods -n openshift-apiserver -l apiserver=true -o jsonpath='{.items[0].metadata.name}') oc rsh -n openshift-apiserver $os_pod ls "$os_path" >/dev/null 2>&1 os_exists=$? # Evaluate all conditions [[ "$kube_path" == "/var/log/kube-apiserver/audit.log" ]] && \ [[ "$os_path" == "/var/log/openshift-apiserver/audit.log" ]] && \ [[ $kube_exists -eq 0 ]] && \ [[ $os_exists -eq 0 ]] && echo "pass" || echo "fail" tests: test_items: - flag: "pass" remediation: | No remediation is required. OpenShift manages audit logging automatically via the apiserver configuration. By default, the audit log paths are: - /var/log/kube-apiserver/audit.log - /var/log/openshift-apiserver/audit.log scored: true - id: 1.2.21 text: "Ensure that the audit logs are forwarded off the cluster for retention (Manual)" type: "manual" remediation: | Follow the documentation for log forwarding. Forwarding logs to third party systems https://docs.openshift.com/container-platform/4.5/logging/cluster-logging-external.html scored: false - id: 1.2.22 text: "Ensure that the maximumRetainedFiles argument is set to 10 or as appropriate (Manual)" audit: | VALUE=$(oc get configmap config -n openshift-kube-apiserver -ojson \ | jq -r '.data["config.yaml"]' \ | jq -r '.apiServerArguments["audit-log-maxbackup"][0] // empty') if [ -n "$VALUE" ] && [ "$VALUE" -ge 10 ]; then echo "pass (current=$VALUE)" else echo "fail (current=$VALUE)" fi tests: test_items: - flag: "pass" remediation: | No remediation required. By default, OpenShift retains 10 audit log backup files. This provides sufficient log history for incident investigation and audit review. scored: true - id: 1.2.23 text: "Ensure that the maximumFileSizeMegabytes argument is set to 100 (Manual)" audit: | VALUE=$(oc get configmap config -n openshift-kube-apiserver -ojson \ | jq -r '.data["config.yaml"]' \ | jq -r '.apiServerArguments["audit-log-maxsize"][0] // empty') if [ -n "$VALUE" ] && [ "$VALUE" -ge 100 ]; then echo "pass (current=$VALUE)" else echo "fail (current=$VALUE)" fi tests: test_items: - flag: "pass" remediation: | Set the audit-log-maxsize parameter to 100 or as an appropriate number. maximumFileSizeMegabytes: 100 scored: true - id: 1.2.24 text: "Ensure that the --request-timeout argument is set (Manual)" audit: | VALUE=$(oc get configmap config -n openshift-kube-apiserver -ojson \ | jq -r '.data["config.yaml"]' \ | jq -r '.apiServerArguments["min-request-timeout"][0] // empty') if [ -n "$VALUE" ] && [ "$VALUE" -eq 3600 ]; then echo "pass (current=$VALUE)" else echo "fail (current=$VALUE)" fi tests: test_items: - flag: "pass" remediation: | TBD scored: true - id: 1.2.25 text: "Ensure that the --service-account-lookup argument is set to true (Manual)" audit: | oc get configmap config -n openshift-kube-apiserver -ojson | jq -r '.data["config.yaml"]' | jq '.apiServerArguments."service-account-lookup"[]' tests: test_items: - flag: "true" remediation: | TBD scored: true - id: 1.2.26 text: "Ensure that the --service-account-key-file argument is set as appropriate (Manual)" audit: | oc get configmap config -n openshift-kube-apiserver -ojson | jq -r '.data["config.yaml"]' | jq -r .serviceAccountPublicKeyFiles[] tests: bin_op: and test_items: - flag: "/etc/kubernetes/static-pod-resources/configmaps/sa-token-signing-certs" - flag: "/etc/kubernetes/static-pod-resources/configmaps/bound-sa-token-signing-certs" remediation: | The OpenShift API server does not use the service-account-key-file argument. The ServiceAccount token authenticator is configured with serviceAccountConfig.publicKeyFiles. OpenShift does not reuse the apiserver TLS key. This is not configurable. scored: true - id: 1.2.27 text: "Ensure that the --etcd-certfile and --etcd-keyfile arguments are set as appropriate (Manual)" audit: | oc get configmap config -n openshift-kube-apiserver -ojson \ | jq -r '.data["config.yaml"]' \ | jq -r '.apiServerArguments["etcd-certfile"][]?' oc get configmap config -n openshift-kube-apiserver -ojson \ | jq -r '.data["config.yaml"]' \ | jq -r '.apiServerArguments["etcd-keyfile"][]?' tests: bin_op: and test_items: - flag: "/etc/kubernetes/static-pod-resources/secrets/etcd-client/tls.crt" - flag: "/etc/kubernetes/static-pod-resources/secrets/etcd-client/tls.key" remediation: | No remediation is required. OpenShift automatically manages X.509 client certificates and TLS encryption for secure communication with etcd. These settings are handled by the platform and should not be manually modified. scored: true - id: 1.2.28 text: "Ensure that the --tls-cert-file and --tls-private-key-file arguments are set as appropriate (Manual)" audit: | oc get configmap config -n openshift-kube-apiserver -ojson \ | jq -r '.data["config.yaml"]' \ | jq -r '.apiServerArguments["tls-cert-file"][]?' oc get configmap config -n openshift-kube-apiserver -ojson \ | jq -r '.data["config.yaml"]' \ | jq -r '.apiServerArguments["tls-private-key-file"][]?' tests: bin_op: and test_items: - flag: "/etc/kubernetes/static-pod-certs/secrets/service-network-serving-certkey/tls.crt" - flag: "/etc/kubernetes/static-pod-certs/secrets/service-network-serving-certkey/tls.key" remediation: | No remediation is required. OpenShift automatically configures the API server with valid X.509 certificates and TLS keys. These are used to encrypt traffic between the API server and clients, including kubelets and users. Certificate rotation and lifecycle management are handled by the OpenShift platform. scored: true - id: 1.2.29 text: "Ensure that the --client-ca-file argument is set as appropriate (Manual)" audit: | oc get configmap config -n openshift-kube-apiserver -ojson | \ jq -r '.data["config.yaml"]' | \ jq -r .servingInfo.clientCA tests: test_items: - flag: "/etc/kubernetes/static-pod-certs/configmaps/client-ca/ca-bundle.crt" remediation: | OpenShift automatically manages TLS authentication for the API server communication with the node/kublet. This is not configurable. You may optionally set a custom default certificate to be used by the API server when serving content in order to enable clients to access the API server at a different host name or without the need to distribute the cluster-managed certificate authority (CA) certificates to the clients. User-provided certificates must be provided in a kubernetes.io/tls type Secret in the openshift-config namespace. Update the API server cluster configuration, the apiserver/cluster resource, to enable the use of the user-provided certificate. scored: true - id: 1.2.30 text: "Ensure that the --etcd-cafile argument is set as appropriate (Manual)" audit: | oc get configmap config -n openshift-kube-apiserver -ojson | \ jq -r '.data["config.yaml"]' | \ jq -r '.apiServerArguments["etcd-cafile"]' tests: test_items: - flag: "/etc/kubernetes/static-pod-resources/configmaps/etcd-serving-ca/ca-bundle.crt" remediation: | None required. OpenShift generates the etcd-cafile and sets the arguments appropriately in the API server. Communication with etcd is secured by the etcd serving CA. scored: true - id: 1.2.31 text: "Ensure that encryption providers are appropriately configured (Manual)" audit: | oc get openshiftapiserver -o=jsonpath='{range .items[0].status.conditions[?(@.type=="Encrypted")]}{.reason}{"\n"}{.message}{"\n"}' tests: test_items: - flag: "EncryptionCompleted" remediation: | Follow the Kubernetes documentation and configure a EncryptionConfig file. In this file, choose aescbc, kms or secretbox as the encryption provider. scored: true - id: 1.2.32 text: "Ensure that the API Server only makes use of Strong Cryptographic Ciphers (Manual)" type: manual audit: | oc get cm -n openshift-authentication v4-0-config-system-cliconfig -o jsonpath='{.data.v4\-0\-config\-system\-cliconfig}' | jq .servingInfo oc get kubeapiservers.operator.openshift.io cluster -o json |jq.spec.observedConfig.servingInfo oc get openshiftapiservers.operator.openshift.io cluster -o json |jq.spec.observedConfig.servingInfo oc describe --namespace=openshift-ingress-operator ingresscontroller/default remediation: | Verify that the tlsSecurityProfile is set to the value you chose. Note: The HAProxy Ingress controller image does not support TLS 1.3 and because the Modern profile requires TLS 1.3, it is not supported. The Ingress Operator converts the Modern profile to Intermediate. The Ingress Operator also converts the TLS 1.0 of an Old or Custom profile to 1.1, and TLS 1.3 of a Custom profile to 1.2. scored: false - id: 1.2.33 text: "Ensure unsupported configuration overrides are not used (Manual)" audit: | oc get kubeapiserver/cluster -o jsonpath='{.spec.unsupportedConfigOverrides}' tests: test_items: - flag: "null" remediation: | No remediation is required. OpenShift has deprecated and disabled unsupportedConfigOverrides. This field should remain null and must not be used in any supported configuration. scored: true - id: 1.3 text: "Controller Manager" checks: - id: 1.3.1 text: "Ensure that controller manager healthz endpoints are protected by RBAC (Manual)" type: manual audit: | # Verify configuration for ports, livenessProbe, readinessProbe, healthz oc -n openshift-kube-controller-manager get cm kube-controller-manager-pod -o json | jq -r '.data."pod.yaml"' | jq '.spec.containers' # Verify endpoints oc -n openshift-kube-controller-manager describe endpoints # Test to validate RBAC enabled on the controller endpoint; check with non-admin role oc project openshift-kube-controller-manage POD=$(oc get pods -n openshift-kube-controller-manager -l app=kube-controller-manager -o jsonpath='{.items[0].metadata.name}') PORT=$(oc get pods -n openshift-kube-controller-manager -l app=kube-controller-manager -o jsonpath='{.items[0].spec.containers[0].ports[0].hostPort}') # Following should return 403 Forbidden oc rsh -n openshift-kube-controller-manager ${POD} curl https://localhost:${PORT}/metrics -k # Create a service account to test RBAC oc create -n openshift-kube-controller-manager sa permission-test-sa # Should return 403 Forbidden SA_TOKEN=$(oc sa -n openshift-kube-controller-manager get-token permission-test-sa) oc rsh -n openshift-kube-controller-manager ${POD} curl https://localhost:${PORT}/metrics -H "Authorization: Bearer $SA_TOKEN" -k # Cleanup oc delete -n openshift-kube-controller-manager sa permission-test-sa # As cluster admin, should succeed CLUSTER_ADMIN_TOKEN=$(oc whoami -t) oc rsh -n openshift-kube-controller-manager ${POD} curl https://localhost:${PORT}/metrics -H "Authorization: Bearer $CLUSTER_ADMIN_TOKEN" -k remediation: | None required; profiling is protected by RBAC. scored: false - id: 1.3.2 text: "Ensure that the --use-service-account-credentials argument is set to true (Manual)" audit: | oc get configmaps config -n openshift-kube-controller-manager -ojson | \ jq -r '.data["config.yaml"]' | \ jq -r '.extendedArguments["use-service-account-credentials"][]' tests: test_items: - flag: "true" remediation: | The OpenShift Controller Manager operator manages and updates the OpenShift Controller Manager. The Kubernetes Controller Manager operator manages and updates the Kubernetes Controller Manager deployed on top of OpenShift. This operator is configured via KubeControllerManager custom resource. scored: true - id: 1.3.3 text: "Ensure that the --service-account-private-key-file argument is set as appropriate (Manual)" audit: | oc get configmaps config -n openshift-kube-controller-manager -ojson | \ jq -r '.data["config.yaml"]' | \ jq -r '.extendedArguments["service-account-private-key-file"][]' tests: test_items: - flag: "/etc/kubernetes/static-pod-resources/secrets/service-account-private-key/service-account.key" remediation: | None required. OpenShift manages the service account credentials for the scheduler automatically. scored: true - id: 1.3.4 text: "Ensure that the --root-ca-file argument is set as appropriate (Manual)" audit: | oc get configmaps config -n openshift-kube-controller-manager -ojson | \ jq -r '.data["config.yaml"]' | \ jq -r '.extendedArguments["root-ca-file"][]' tests: test_items: - flag: "/etc/kubernetes/static-pod-resources/configmaps/serviceaccount-ca/ca-bundle.crt" remediation: | None required. Certificates for OpenShift platform components are automatically created and rotated by the OpenShift Container Platform. scored: true - id: 1.3.5 text: "Ensure that the --bind-address argument is set to 127.0.0.1 (Manual)" audit: | oc get configmaps config -n openshift-kube-controller-manager -ojson \ | jq -r '.data["config.yaml"]' \ | jq -r '.extendedArguments["secure-port"][]?' export POD=$(oc get pods -n openshift-kube-controller-manager -l app=kube-controller-manager -o jsonpath='{.items[0].metadata.name}') oc rsh -n openshift-kube-controller-manager -c kube-controller-manager $POD curl -k -s -o /dev/null -w "%{http_code}" https://localhost:10257/metrics unset POD tests: bin_op: and test_items: - flag: "10257" - flag: "403" remediation: | No remediation is required. By default, OpenShift does not expose an insecure bind address. The kube-controller-manager uses secure-port 10257 and restricts unauthenticated access to the /metrics endpoint. scored: true - id: 1.4 text: "Scheduler" checks: - id: 1.4.1 text: "Ensure that the healthz endpoints for the scheduler are protected by RBAC (Manual)" type: manual audit: | # check configuration for ports, livenessProbe, readinessProbe, healthz oc -n openshift-kube-scheduler get cm kube-scheduler-pod -o json | jq -r '.data."pod.yaml"' | jq '.spec.containers' # Test to verify endpoints oc -n openshift-kube-scheduler describe endpoints # Test to validate RBAC enabled on the scheduler endpoint; check with non-admin role oc project openshift-kube-scheduler POD=$(oc get pods -l app=openshift-kube-scheduler -o jsonpath='{.items[0].metadata.name}') PORT=$(oc get pod $POD -o jsonpath='{.spec.containers[0].livenessProbe.httpGet.port}') # Should return 403 Forbidden oc rsh ${POD} curl http://localhost:${PORT}/metrics -k # Create a service account to test RBAC oc create sa permission-test-sa # Should return 403 Forbidden SA_TOKEN=$(oc sa get-token permission-test-sa) oc rsh ${POD} curl http://localhost:${PORT}/metrics -H "Authorization: Bearer $SA_TOKEN" -k # Cleanup oc delete sa permission-test-sa # As cluster admin, should succeed CLUSTER_ADMIN_TOKEN=$(oc whoami -t) oc rsh ${POD} curl http://localhost:${PORT}/metrics -H "Authorization: Bearer $CLUSTER_ADMIN_TOKEN" -k remediation: | A fix to this issue: https://bugzilla.redhat.com/show_bug.cgi?id=1889488 None required. Profiling is protected by RBAC and cannot be disabled. scored: false - id: 1.4.2 text: "Verify that the scheduler API service is protected by RBAC (Manual)" type: manual audit: | echo "Describing kube-scheduler endpoints..." oc -n openshift-kube-scheduler describe endpoints echo "Checking pod configuration for kube-scheduler to confirm no --bind-address or insecure arguments..." oc -n openshift-kube-scheduler get cm kube-scheduler-pod -o json \ | jq -r '.data["pod.yaml"]' \ | jq '.spec.containers[] | select(.name=="kube-scheduler") | .args' echo "Testing access to metrics endpoint as unauthenticated user..." oc project openshift-kube-scheduler export POD=$(oc get pods -l app=openshift-kube-scheduler -o jsonpath='{.items[0].metadata.name}') export POD_IP=$(oc get pods -l app=openshift-kube-scheduler -o jsonpath='{.items[0].status.podIP}') export PORT=$(oc get pod $POD -o jsonpath='{.spec.containers[0].livenessProbe.httpGet.port}') oc rsh $POD curl -k -s -o /dev/null -w "%{http_code}" https://$POD_IP:$PORT/metrics echo "Testing access with unprivileged service account..." oc create sa permission-test-sa export SA_TOKEN=$(oc create token permission-test-sa) oc rsh $POD curl -k -s -o /dev/null -w "%{http_code}" https://$POD_IP:$PORT/metrics -H "Authorization: Bearer $SA_TOKEN" echo "Testing access with cluster-admin..." export CLUSTER_ADMIN_TOKEN=$(oc whoami -t) oc rsh $POD curl -k -s -o /dev/null -w "%{http_code}" https://$POD_IP:$PORT/metrics -H "Authorization: Bearer $CLUSTER_ADMIN_TOKEN" # Cleanup unset CLUSTER_ADMIN_TOKEN POD PORT SA_TOKEN POD_IP oc delete sa permission-test-sa remediation: | By default, the --bind-address argument is not present, the readinessProbe and livenessProbe arguments are set to 10251 and the port argument is set to 0. Check the status of this issue: https://bugzilla.redhat.com/show_bug.cgi?id=1889488 scored: false ================================================ FILE: cfg/rh-1.4/node.yaml ================================================ --- controls: version: rh-1.4 id: 4 text: "Worker Nodes" type: "node" groups: - id: 4.1 text: "Worker Node Configuration Files" checks: - id: 4.1.1 text: "Ensure that the kubelet service file permissions are set to 644 or more restrictive (Automated)" audit: | NODE_NAME=$(oc get pod $HOSTNAME -o=jsonpath='{.spec.nodeName}') oc debug node/$NODE_NAME -- chroot /host stat -c "$NODE_NAME %n permissions=%a" /etc/systemd/system/kubelet.service 2> /dev/null tests: test_items: - flag: "permissions" compare: op: bitmask value: "644" remediation: | By default, the kubelet service file has permissions of 644. scored: true - id: 4.1.2 text: "Ensure that the kubelet service file ownership is set to root:root (Automated)" audit: | # Should return root:root for each node NODE_NAME=$(oc get pod $HOSTNAME -o=jsonpath='{.spec.nodeName}') oc debug node/$NODE_NAME -- chroot /host stat -c "$NODE_NAME %n %U:%G" /etc/systemd/system/kubelet.service 2> /dev/null tests: test_items: - flag: root:root remediation: | By default, the kubelet service file has ownership of root:root. scored: true - id: 4.1.3 text: "If proxy kube proxy configuration file exists ensure permissions are set to 644 or more restrictive (Manual)" audit: | # Get the node name where the pod is running NODE_NAME=$(oc get pod "$HOSTNAME" -o=jsonpath='{.spec.nodeName}') # Get the pod name in the openshift-sdn namespace POD_NAME=$(oc get pods -n openshift-sdn -l app=sdn --field-selector spec.nodeName="$NODE_NAME" -o jsonpath='{.items[0].metadata.name}' 2>/dev/null) if [ -z "$POD_NAME" ]; then echo "No matching pods found on the current node." else # Execute the stat command oc exec -n openshift-sdn "$POD_NAME" -- stat -Lc "$i %n permissions=%a" /config/kube-proxy-config.yaml 2>/dev/null fi tests: test_items: - flag: "permissions" set: true compare: op: bitmask value: "644" remediation: | None needed. scored: true - id: 4.1.4 text: "If proxy kubeconfig file exists ensure ownership is set to root:root (Manual)" audit: | # Get the node name where the pod is running NODE_NAME=$(oc get pod "$HOSTNAME" -o=jsonpath='{.spec.nodeName}') # Get the pod name in the openshift-sdn namespace POD_NAME=$(oc get pods -n openshift-sdn -l app=sdn --field-selector spec.nodeName="$NODE_NAME" -o jsonpath='{.items[0].metadata.name}' 2>/dev/null) if [ -z "$POD_NAME" ]; then echo "No matching pods found on the current node." else # Execute the stat command oc exec -n openshift-sdn "$POD_NAME" -- stat -Lc "$i %n %U:%G" /config/kube-proxy-config.yaml 2>/dev/null fi use_multiple_values: true tests: test_items: - flag: root:root remediation: | None required. The configuration is managed by OpenShift operators. scored: true - id: 4.1.5 text: "Ensure that the --kubeconfig kubelet.conf file permissions are set to 644 or more restrictive (Manual)" audit: | # Check permissions NODE_NAME=$(oc get pod $HOSTNAME -o=jsonpath='{.spec.nodeName}') oc debug node/$NODE_NAME -- chroot /host stat -c "$NODE_NAME %n permissions=%a" /etc/kubernetes/kubelet.conf 2> /dev/null use_multiple_values: true tests: test_items: - flag: "permissions" compare: op: bitmask value: "644" remediation: | None required. scored: true - id: 4.1.6 text: "Ensure that the --kubeconfig kubelet.conf file ownership is set to root:root (Manual)" audit: | NODE_NAME=$(oc get pod $HOSTNAME -o=jsonpath='{.spec.nodeName}') oc debug node/$NODE_NAME -- chroot /host stat -c "$NODE_NAME %n %U:%G" /etc/kubernetes/kubelet.conf 2> /dev/null use_multiple_values: true tests: test_items: - flag: root:root remediation: | None required. scored: true - id: 4.1.7 text: "Ensure that the certificate authorities file permissions are set to 644 or more restrictive" audit: | NODE_NAME=$(oc get pod $HOSTNAME -o=jsonpath='{.spec.nodeName}') oc get --raw /api/v1/nodes/$NODE_NAME/proxy/configz | jq '.kubeletconfig.authentication.x509.clientCAFile' oc debug node/$NODE_NAME -- chroot /host stat -c "$NODE_NAME permissions=%a" /etc/kubernetes/kubelet-ca.crt 2> /dev/null tests: test_items: - flag: "/etc/kubernetes/kubelet-ca.crt" - flag: "permissions" compare: op: bitmask value: "644" remediation: | No remediation required. OpenShift sets /etc/kubernetes/kubelet-ca.crt to 644 by default. If permissions are more permissive than 644, update with: chmod 644 /etc/kubernetes/kubelet-ca.crt scored: true - id: 4.1.8 text: "Ensure that the client certificate authorities file ownership is set to root:root (Automated)" audit: | NODE_NAME=$(oc get pod $HOSTNAME -o=jsonpath='{.spec.nodeName}') oc debug node/$NODE_NAME -- chroot /host stat -c "$NODE_NAME %n %U:%G" /etc/kubernetes/kubelet-ca.crt 2> /dev/null use_multiple_values: true tests: test_items: - flag: root:root remediation: | None required. scored: true - id: 4.1.9 text: "Ensure that the kubelet --config configuration file has permissions set to 600 or more restrictive (Automated)" audit: | NODE_NAME=$(oc get pod $HOSTNAME -o=jsonpath='{.spec.nodeName}') oc debug node/$NODE_NAME -- chroot /host stat -c "$NODE_NAME %n permissions=%a" /var/lib/kubelet/config.json 2> /dev/null use_multiple_values: true tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | None required. scored: true - id: 4.1.10 text: "Ensure that the kubelet configuration file ownership is set to root:root (Automated)" audit: | NODE_NAME=$(oc get pod $HOSTNAME -o=jsonpath='{.spec.nodeName}') oc debug node/$NODE_NAME -- chroot /host stat -c "$NODE_NAME %n %U:%G" /var/lib/kubelet/config.json 2> /dev/null use_multiple_values: true tests: test_items: - flag: root:root remediation: | None required. scored: true - id: 4.2 text: "Kubelet" checks: - id: 4.2.1 text: "Activate Garbage collection in OpenShift Container Platform 4, as appropriate (Manual)" audit: | echo "Retrieving and inspecting garbage collection configuration from node-local kubelet configz..." NODE_NAME=$(oc get pod $HOSTNAME -o=jsonpath='{.spec.nodeName}') oc get --raw /api/v1/nodes/$NODE_NAME/proxy/configz | jq '.kubeletconfig' tests: test_items: - flag: "evictionHard" - flag: "imageGCHighThresholdPercent" - flag: "imageGCLowThresholdPercent" - flag: "imageMinimumGCAge" remediation: | OpenShift manages node garbage collection through KubeletConfig custom resources per MachineConfigPool. To configure or adjust garbage collection thresholds, follow the documentation: https://docs.openshift.com/container-platform/latest/nodes/nodes/nodes-nodes-garbage-collection.html Example: Create or modify a KubeletConfig object to include: --- evictionHard: "memory.available": "200Mi" "nodefs.available": "10%" "imagefs.available": "15%" imageGCHighThresholdPercent: 85 imageGCLowThresholdPercent: 80 imageMinimumGCAge: "2m0s" Then apply the `KubeletConfig` to the appropriate `MachineConfigPool`. scored: true - id: 4.2.2 text: "Ensure that the --anonymous-auth argument is set to false (Automated)" audit: | echo "Checking if anonymous-auth is disabled in kubelet configuration on the current node..." NODE_NAME=$(oc get pod $HOSTNAME -o=jsonpath='{.spec.nodeName}') oc get --raw /api/v1/nodes/$NODE_NAME/proxy/configz | jq '.kubeletconfig.authentication.anonymous.enabled' tests: test_items: - flag: "false" remediation: | By default, OpenShift sets anonymous-auth to false in Kubelet configuration. If this value is found to be true, create or patch a KubeletConfig object with: --- kind: KubeletConfig apiVersion: machineconfiguration.openshift.io/v1 metadata: name: disable-anonymous-auth spec: kubeletConfig: authentication: anonymous: enabled: false Then apply this KubeletConfig to the appropriate MachineConfigPool. See OpenShift documentation on configuring node-level security settings. scored: true - id: 4.2.3 text: "Ensure that the --authorization-mode argument is not set to AlwaysAllow (Automated)" audit: | echo "Checking kubelet authorization mode on the current node..." NODE_NAME=$(oc get pod $HOSTNAME -o=jsonpath='{.spec.nodeName}') oc get --raw /api/v1/nodes/$NODE_NAME/proxy/configz | jq '.kubeletconfig.authorization.mode' tests: test_items: - flag: AlwaysAllow set: false remediation: | No remediation required. By default, OpenShift uses secure authorization modes such as 'Webhook' and does not allow AlwaysAllow. If AlwaysAllow is found, the node must be reconfigured using a KubeletConfig applied through the appropriate MachineConfigPool. scored: true - id: 4.2.4 text: "Ensure that the --client-ca-file argument is set as appropriate (Automated)" audit: | echo "Checking Kubelet 'clientCAFile' setting on current node..." NODE_NAME=$(oc get pod $HOSTNAME -o=jsonpath='{.spec.nodeName}') oc get --raw /api/v1/nodes/$NODE_NAME/proxy/configz \ | jq '.kubeletconfig.authentication.x509.clientCAFile' tests: test_items: - flag: "/etc/kubernetes/kubelet-ca.crt" remediation: | No remediation required. OpenShift sets the clientCAFile by default to /etc/kubernetes/kubelet-ca.crt. Manual modification is unsupported and unnecessary as OpenShift manages Kubelet certificate authentication via the Machine Config Operator. scored: true - id: 4.2.5 text: "Verify that the read only port is not used or is set to 0 (Automated)" audit: | echo "Checking 'kubelet-read-only-port' argument in openshift-kube-apiserver config..." oc -n openshift-kube-apiserver get configmap config -o json \ | jq -r '.data["config.yaml"]' \ | yq '.apiServerArguments."kubelet-read-only-port"[0]' tests: test_items: - flag: "0" remediation: | No remediation is required if the read-only port is set to 0. If this value is not set to 0 (or the argument is missing), create a KubeletConfig object and apply it to the appropriate MachineConfigPool to disable the read-only port. Example KubeletConfig: --- apiVersion: machineconfiguration.openshift.io/v1 kind: KubeletConfig metadata: name: disable-readonly-port spec: kubeletConfig: readOnlyPort: 0 scored: true - id: 4.2.6 text: "Ensure that the --streaming-connection-idle-timeout argument is not set to 0 (automated)" audit: | NODE_NAME=$(oc get pod $HOSTNAME -o=jsonpath='{.spec.nodeName}') oc get --raw /api/v1/nodes/$NODE_NAME/proxy/configz \ | jq '.kubeletconfig' tests: test_items: - path: ".streamingConnectionIdleTimeout" compare: op: noteq value: "0s" remediation: | By default, OpenShift sets streamingConnectionIdleTimeout to 4h0m0s. If it is manually set to "0s", this disables timeouts — which is insecure. To remediate, create a `KubeletConfig` CR with a safer timeout (e.g., 1h0m0s): --- apiVersion: machineconfiguration.openshift.io/v1 kind: KubeletConfig metadata: name: set-streaming-timeout spec: kubeletConfig: streamingConnectionIdleTimeout: "1h0m0s" scored: true - id: 4.2.7 text: "Ensure that the --make-iptables-util-chains argument is set to true (manual)" audit: | echo "Checking 'makeIPTablesUtilChains' setting in Kubelet config on current node..." NODE_NAME=$(oc get pod $HOSTNAME -o=jsonpath='{.spec.nodeName}') oc get --raw /api/v1/nodes/$NODE_NAME/proxy/configz \ | jq '.kubeletconfig' tests: test_items: - path: ".makeIPTablesUtilChains" compare: op: eq value: true remediation: | No remediation is required. By default, OpenShift sets makeIPTablesUtilChains to true. This allows Kubelet to manage iptables rules and keep them in sync with the dynamic pod network configuration. scored: true - id: 4.2.8 text: "Ensure that the kubeAPIQPS [--event-qps] argument is set to 0 or a level which ensures appropriate event capture (manual)" audit: | echo "Checking 'kubeAPIQPS' setting in Kubelet config on current node..." NODE_NAME=$(oc get pod $HOSTNAME -o=jsonpath='{.spec.nodeName}') oc get --raw /api/v1/nodes/$NODE_NAME/proxy/configz \ | jq '.kubeletconfig' tests: test_items: - path: ".kubeAPIQPS" compare: op: gte value: 1 remediation: | OpenShift sets kubeAPIQPS to a default of 50, which is appropriate in most environments. If kubeAPIQPS is set to 0, event rate limiting is disabled, which can overwhelm the kubelet with excessive events. To configure a proper limit, create or modify a `KubeletConfig` resource with an appropriate value: --- apiVersion: machineconfiguration.openshift.io/v1 kind: KubeletConfig metadata: name: set-kubeapiqps spec: kubeletConfig: kubeAPIQPS: 50 scored: true - id: 4.2.9 text: "Ensure that the --tls-cert-file and --tls-private-key-file arguments are set as appropriate (manual)" audit: | oc get configmap config -n openshift-kube-apiserver -ojson \ | jq -r '.data["config.yaml"]' \ | jq -r '.apiServerArguments["kubelet-client-certificate"][]?' oc get configmap config -n openshift-kube-apiserver -ojson \ | jq -r '.data["config.yaml"]' \ | jq -r '.apiServerArguments["kubelet-client-key"][]?' tests: bin_op: and test_items: - flag: "/etc/kubernetes/static-pod-certs/secrets/kubelet-client/tls.crt" - flag: "/etc/kubernetes/static-pod-certs/secrets/kubelet-client/tls.key" remediation: | No remediation is required. OpenShift manages secure TLS connections to kubelets by default using its internal certificate authority. These X.509 certificates are rotated and validated automatically by the platform. Manual modifications to the TLS paths or keys are not supported and can lead to cluster issues. scored: true - id: 4.2.10 text: "Ensure that the --rotate-certificates argument is not set to false (manual)" audit: | NODE_NAME=$(oc get pod $HOSTNAME -o=jsonpath='{.spec.nodeName}') oc get --raw /api/v1/nodes/$NODE_NAME/proxy/configz \ | jq '.kubeletconfig' tests: test_items: - path: ".rotateCertificates" compare: op: eq value: true remediation: | No remediation required. By default, OpenShift enables certificate rotation via rotateCertificates=true. If disabled, you must either enable rotation via KubeletConfig or implement external certificate renewal. Example remediation using KubeletConfig: --- apiVersion: machineconfiguration.openshift.io/v1 kind: KubeletConfig metadata: name: enable-cert-rotation spec: kubeletConfig: rotateCertificates: true scored: true - id: 4.2.11 text: "Verify that the RotateKubeletServerCertificate argument is set to true (manual)" audit: | echo "Checking that RotateKubeletServerCertificate is enabled in kubelet config on current node..." NODE_NAME=$(oc get pod $HOSTNAME -o=jsonpath='{.spec.nodeName}') echo "Verifying feature gate: RotateKubeletServerCertificate" oc get --raw /api/v1/nodes/$NODE_NAME/proxy/configz \ | jq '.kubeletconfig.featureGates.RotateKubeletServerCertificate' echo "Verifying that certificate rotation is enabled" oc get --raw /api/v1/nodes/$NODE_NAME/proxy/configz \ | jq '.kubeletconfig.rotateCertificates' tests: bin_op: and test_items: - flag: "RotateKubeletServerCertificate" compare: op: eq value: true - flag: "rotateCertificates" compare: op: eq value: true remediation: | No remediation is required. OpenShift enables RotateKubeletServerCertificate by default and manages certificate rotation automatically. If the feature gate or rotation setting is disabled, configure a `KubeletConfig` CR and apply it to the MachineConfigPool: --- apiVersion: machineconfiguration.openshift.io/v1 kind: KubeletConfig metadata: name: enable-server-cert-rotation spec: kubeletConfig: rotateCertificates: true featureGates: RotateKubeletServerCertificate: true scored: true - id: 4.2.13 text: "Ensure that the Kubelet only makes use of Strong Cryptographic Ciphers (Manual)" audit: | # needs verification # verify cipher suites oc describe --namespace=openshift-ingress-operator ingresscontroller/default oc get kubeapiservers.operator.openshift.io cluster -o json |jq .spec.observedConfig.servingInfo oc get openshiftapiservers.operator.openshift.io cluster -o json |jq .spec.observedConfig.servingInfo oc get cm -n openshift-authentication v4-0-config-system-cliconfig -o jsonpath='{.data.v4\-0\-config\-system\-cliconfig}' | jq .servingInfo #check value for tlsSecurityProfile; null is returned if default is used oc get kubeapiservers.operator.openshift.io cluster -o json |jq .spec.tlsSecurityProfile type: manual remediation: | Follow the directions above and in the OpenShift documentation to configure the tlsSecurityProfile. Configuring Ingress scored: false ================================================ FILE: cfg/rh-1.4/policies.yaml ================================================ --- controls: version: rh-1.4 id: 5 text: "Policies" type: "policies" groups: - id: 5.1 text: "RBAC and Service Accounts" checks: - id: 5.1.1 text: "Ensure that the cluster-admin role is only used where required (Manual)" type: "manual" audit: | #To get a list of users and service accounts with the cluster-admin role oc get clusterrolebindings -o=customcolumns=NAME:.metadata.name,ROLE:.roleRef.name,SUBJECT:.subjects[*].kind | grep cluster-admin #To verity that kbueadmin is removed, no results should be returned oc get secrets kubeadmin -n kube-system remediation: | Identify all clusterrolebindings to the cluster-admin role. Check if they are used and if they need this role or if they could use a role with fewer privileges. Where possible, first bind users to a lower privileged role and then remove the clusterrolebinding to the cluster-admin role : oc delete clusterrolebinding [name] scored: false - id: 5.1.2 text: "Minimize access to secrets (Manual)" type: "manual" remediation: | Where possible, remove get, list and watch access to secret objects in the cluster. scored: false - id: 5.1.3 text: "Minimize wildcard use in Roles and ClusterRoles (Manual)" type: "manual" remediation: | Where possible replace any use of wildcards in clusterroles and roles with specific objects or actions. scored: false - id: 5.1.4 text: "Minimize access to create pods (Manual)" type: "manual" remediation: | Where possible, remove create access to pod objects in the cluster. scored: false - id: 5.1.5 text: "Ensure that default service accounts are not actively used. (Manual)" type: "manual" remediation: | None required. scored: false - id: 5.1.6 text: "Ensure that Service Account Tokens are only mounted where necessary (Manual)" type: "manual" remediation: | Modify the definition of pods and service accounts which do not need to mount service account tokens to disable it. scored: false - id: 5.2 text: "Security Context Constraints (SCCs)" checks: - id: 5.2.1 text: "Minimize the admission of privileged containers (Manual)" audit: | oc get scc -o json \ | jq -r '[.items[] | select(.allowPrivilegedContainer==false) | .metadata.name] | length | if . > 0 then "pass" else "fail" end' tests: test_items: - flag: "pass" remediation: | If no SCCs exist that restrict privileged containers, create one by running: oc create -f - < 0 then "pass" else "fail" end' tests: test_items: - flag: "pass" remediation: | If SCCs with `allowHostPID: true` exist, ensure they are restricted to trusted service accounts only. To create a restrictive SCC that prevents host PID sharing: --- apiVersion: security.openshift.io/v1 kind: SecurityContextConstraints metadata: name: restricted-no-hostpid allowHostPID: false runAsUser: type: MustRunAsRange seLinuxContext: type: MustRunAs users: [] groups: - system:authenticated --- Apply the SCC and bind it only to users or groups that do **not** need hostPID access. scored: true - id: 5.2.3 text: "Minimize the admission of containers wishing to share the host IPC namespace (Manual)" audit: | oc get scc -o json \ | jq -r '[.items[] | select(.allowHostIPC==false) | .metadata.name] | length | if . > 0 then "pass" else "fail" end' tests: test_items: - flag: "pass" remediation: | If no SCCs restrict hostIPC usage, create one that explicitly sets allowHostIPC: false: --- apiVersion: security.openshift.io/v1 kind: SecurityContextConstraints metadata: name: restricted-no-hostipc allowHostIPC: false runAsUser: type: MustRunAsRange seLinuxContext: type: MustRunAs users: [] groups: - system:authenticated --- Then assign this SCC to general workloads and ensure any SCCs allowing hostIPC are tightly scoped via RBAC. scored: true - id: 5.2.4 text: "Minimize the admission of containers wishing to share the host network namespace (manual)" audit: | oc get scc -o json \ | jq -r '[.items[] | select(.allowHostNetwork==false) | .metadata.name] | length | if . > 0 then "pass" else "fail" end' tests: test_items: - flag: "pass" remediation: | If no SCCs restrict host networking, create one by running: --- apiVersion: security.openshift.io/v1 kind: SecurityContextConstraints metadata: name: restricted-no-hostnetwork allowHostNetwork: false runAsUser: type: MustRunAsRange seLinuxContext: type: MustRunAs users: [] groups: - system:authenticated --- Ensure only workloads that require `hostNetwork: true` (e.g., CNI, infra pods) are allowed to use SCCs where it is explicitly enabled. Restrict access to such SCCs using RBAC. scored: true - id: 5.2.5 text: "Minimize the admission of containers with allowPrivilegeEscalation (manual)" audit: | oc get scc -o json \ | jq -r '[.items[] | select(.allowPrivilegeEscalation==false) | .metadata.name] | length | if . > 0 then "pass" else "fail" end' tests: test_items: - flag: "pass" remediation: | If no SCCs exist that restrict the use of privilege escalation, create a custom SCC: --- apiVersion: security.openshift.io/v1 kind: SecurityContextConstraints metadata: name: restricted-no-priv-escalation allowPrivilegeEscalation: false runAsUser: type: MustRunAsRange seLinuxContext: type: MustRunAs users: [] groups: - system:authenticated --- Assign this SCC only to workloads and users that **do not require** the ability to escalate privileges. Use RBAC to restrict access to SCCs where `allowPrivilegeEscalation` is `true` to only trusted service accounts or admin roles. scored: true - id: 5.2.6 text: "Minimize the admission of root containers (manual)" audit: | sccs=$(oc get scc -o json | jq -r '.items[] | select(.runAsUser.type == "MustRunAsNonRoot") | .metadata.name') if [[ -n "$sccs" ]]; then echo "pass" else echo "fail" fi tests: test_items: - flag: "pass" remediation: | If no SCC is found with `runAsUser.type: MustRunAsNonRoot`, create one as follows: --- apiVersion: security.openshift.io/v1 kind: SecurityContextConstraints metadata: name: restricted-nonroot allowPrivilegeEscalation: false runAsUser: type: MustRunAsNonRoot seLinuxContext: type: MustRunAs users: [] groups: - system:authenticated --- Assign this SCC only to workloads that must not run as root. If an SCC allows `RunAsAny`, audit and restrict access using RBAC to prevent misuse. scored: true - id: 5.2.7 text: "Minimize the admission of containers with the NET_RAW capability (manual)" audit: | oc get scc -o json \ | jq -r '[.items[] | select((.requiredDropCapabilities // []) | index("ALL")) | .metadata.name] | length | if . > 0 then "pass" else "fail" end' tests: test_items: - flag: "pass" remediation: | If no SCCs drop ALL capabilities, create a custom SCC that explicitly drops NET_RAW: --- apiVersion: security.openshift.io/v1 kind: SecurityContextConstraints metadata: name: restricted-no-netraw requiredDropCapabilities: - NET_RAW allowPrivilegedContainer: false runAsUser: type: MustRunAsRange seLinuxContext: type: MustRunAs users: [] groups: - system:authenticated --- Apply this SCC to workloads that do not require NET_RAW. If NET_RAW is required (e.g., for low-level networking apps), isolate those workloads with a specific SCC and restrict access via RBAC. scored: true - id: 5.2.8 text: "Minimize the admission of containers with added capabilities (manual)" audit: | oc get scc -o json \ | jq -r '[.items[] | select(.allowedCapabilities == null) | .metadata.name] | length | if . > 0 then "pass" else "fail" end' oc get scc -o json \ | jq -r '[.items[] | select(.defaultAddCapabilities == null) | .metadata.name] | length | if . > 0 then "true" else "false" end' tests: test_items: - flag: "pass" - flag: "true" remediation: | If no SCCs restrict added capabilities, create a custom SCC as shown below: --- apiVersion: security.openshift.io/v1 kind: SecurityContextConstraints metadata: name: restricted-no-added-caps allowPrivilegedContainer: false allowedCapabilities: [] defaultAddCapabilities: [] runAsUser: type: MustRunAsRange seLinuxContext: type: MustRunAs users: [] groups: - system:authenticated --- Assign this SCC to workloads that do **not** require elevated capabilities. Create separate SCCs for workloads that require specific capabilities, and use RBAC to tightly restrict access to them. scored: true - id: 5.2.9 text: "Minimize the admission of containers with capabilities assigned (manual)" audit: | oc get scc -o json \ | jq -r '[.items[] | select((.requiredDropCapabilities // []) | index("ALL")) | .metadata.name] | length | if . > 0 then "true" else "false" end' tests: test_items: - flag: "true" remediation: | If no SCCs drop all capabilities, create one that sets 'requiredDropCapabilities: [ALL]': --- apiVersion: security.openshift.io/v1 kind: SecurityContextConstraints metadata: name: restricted-drop-all-capabilities requiredDropCapabilities: - ALL allowPrivilegedContainer: false runAsUser: type: MustRunAsRange seLinuxContext: type: MustRunAs users: [] groups: - system:authenticated --- Apply this SCC to general-purpose workloads that do not require elevated Linux capabilities. If certain workloads require capabilities, create a separate SCC with minimal permissions and scope it using RBAC. scored: true - id: 5.2.10 text: "Minimize access to privileged Security Context Constraints (Manual)" type: "manual" remediation: | Remove any users and groups who do not need access to an SCC, following the principle of least privilege. You can remove users and groups from an SCC using the oc edit scc $NAME command. Additionally, you can create your own SCCs that contain the container functionality you need for a particular use case and assign that SCC to users and groups if the default SCCs are not appropriate for your use case. scored: false - id: 5.3 text: "Network Policies and CNI" checks: - id: 5.3.1 text: "Ensure that the CNI in use supports Network Policies (Manual)" type: "manual" remediation: | None required. scored: false - id: 5.3.2 text: "Ensure that all Namespaces have Network Policies defined (Manual)" type: "manual" audit: | #Run the following command and review the NetworkPolicy objects created in the cluster. oc -n all get networkpolicy remediation: | Follow the documentation and create NetworkPolicy objects as you need them. scored: false - id: 5.4 text: "Secrets Management" checks: - id: 5.4.1 text: "Prefer using secrets as files over secrets as environment variables (Manual)" type: "manual" audit: | #Run the following command to find references to objects which use environment variables defined from secrets. oc get all -o jsonpath='{range .items[?(@..secretKeyRef)]} {.kind} {.metadata.name} {"\n"}{end}' -A remediation: | If possible, rewrite application code to read secrets from mounted secret files, rather than from environment variables. scored: false - id: 5.4.2 text: "Consider external secret storage (Manual)" type: "manual" remediation: | Refer to the secrets management options offered by your cloud provider or a third-party secrets management solution. scored: false - id: 5.5 text: "Extensible Admission Control" checks: - id: 5.5.1 text: "Configure Image Provenance using image controller configuration parameters (Manual)" type: "manual" remediation: | Follow the OpenShift documentation: [Image configuration resources](https://docs.openshift.com/container-platform/4.5/openshift_images/image-configuration.html scored: false - id: 5.7 text: "General Policies" checks: - id: 5.7.1 text: "Create administrative boundaries between resources using namespaces (Manual)" type: "manual" audit: | #Run the following command and review the namespaces created in the cluster. oc get namespaces #Ensure that these namespaces are the ones you need and are adequately administered as per your requirements. remediation: | Follow the documentation and create namespaces for objects in your deployment as you need them. scored: false - id: 5.7.2 text: "Ensure that the seccomp profile is set to docker/default in your pod definitions (Manual)" type: "manual" remediation: | To enable the default seccomp profile, use the reserved value /runtime/default that will make sure that the pod uses the default policy available on the host. scored: false - id: 5.7.3 text: "Apply Security Context to Your Pods and Containers (Manual)" type: "manual" remediation: | Follow the Kubernetes documentation and apply security contexts to your pods. For a suggested list of security contexts, you may refer to the CIS Security Benchmark for Docker Containers. scored: false - id: 5.7.4 text: "The default namespace should not be used (Manual)" type: "manual" audit: | #Run this command to list objects in default namespace oc project default oc get all #The only entries there should be system managed resources such as the kubernetes and openshift service remediation: | Ensure that namespaces are created to allow for appropriate segregation of Kubernetes resources and that all new resources are created in a specific namespace. scored: false ================================================ FILE: cfg/rh-1.8/config.yaml ================================================ --- ## Version-specific settings that override the values in cfg/config.yaml ================================================ FILE: cfg/rh-1.8/controlplane.yaml ================================================ --- controls: version: rh-1.8 id: 3 text: "Control Plane Configuration" type: "controlplane" groups: - id: 3.1 text: "Authentication and Authorization" checks: - id: 3.1.1 text: "Client certificate authentication should not be used for users (Manual)" audit: | # To verify user authentication is enabled oc describe authentication # To verify that an identity provider is configured oc get identity # To verify that a custom cluster-admin user exists oc get clusterrolebindings -o=custom-columns=NAME:.metadata.name,ROLE:.roleRef.name,SUBJECT:.subjects[*].kind | grep cluster-admin | grep User # To verity that kbueadmin is removed, no results should be returned oc get secrets kubeadmin -n kube-system type: manual remediation: | Configure an identity provider for the OpenShift cluster. Understanding identity provider configuration | Authentication | OpenShift Container Platform 4.5. Once an identity provider has been defined, you can use RBAC to define and apply permissions. After you define an identity provider and create a new cluster-admin user, remove the kubeadmin user to improve cluster security. scored: false - id: 3.2 text: "Logging" checks: - id: 3.2.1 text: "Ensure that a minimal audit policy is created (Manual)" audit: | #To view kube apiserver log files oc adm node-logs --role=master --path=kube-apiserver/ #To view openshift apiserver log files oc adm node-logs --role=master --path=openshift-apiserver/ #To verify kube apiserver audit config oc get configmap config -n openshift-kube-apiserver -ojson | jq -r '.data["config.yaml"]' | jq '.auditConfig[]?' #To verify openshift apiserver audit config oc get configmap config -n openshift-apiserver -ojson | jq -r '.data["config.yaml"]' | jq '.auditConfig[]?' type: manual remediation: | No remediation required. scored: false - id: 3.2.2 text: "Ensure that the audit policy covers key security concerns (Manual)" audit: | #To verify openshift apiserver audit config oc get configmap config -n openshift-kube-apiserver -ojson | jq -r '.data["config.yaml"]' | jq '.auditConfig.policyConfiguration.rules[]?' #To verify kube apiserver audit config oc get configmap config -n openshift-apiserver -ojson | jq -r '.data["config.yaml"]' | jq '.auditConfig.policyConfiguration.rules[]?' type: manual remediation: | In OpenShift 4.6 and higher, if appropriate for your needs, modify the audit policy. scored: false ================================================ FILE: cfg/rh-1.8/etcd.yaml ================================================ --- controls: version: rh-1.8 id: 2 text: "Etcd" type: "etcd" groups: - id: 2 text: "Etcd" checks: - id: 2.1 text: "Ensure that the --cert-file and --key-file arguments are set as appropriate (Manual)" audit: | # Get the node name where the pod is running NODE_NAME=$(oc get pod "$HOSTNAME" -o=jsonpath='{.spec.nodeName}') # Get the pod name in the openshift-etcd namespace POD_NAME=$(oc get pods -n openshift-etcd -l app=etcd --field-selector spec.nodeName="$NODE_NAME" -o jsonpath='{.items[0].metadata.name}' 2>/dev/null) if [ -z "$POD_NAME" ]; then echo "No matching file found on the current node." else # Execute the stat command oc exec -n openshift-etcd -c etcd "$POD_NAME" -- ps -o command= -C etcd | sed 's/.*\(--cert-file=[^ ]*\).*/\1/' oc exec -n openshift-etcd -c etcd "$POD_NAME" -- ps -o command= -C etcd | sed 's/.*\(--key-file=[^ ]*\).*/\1/' fi use_multiple_values: true tests: test_items: - flag: "file" compare: op: regex value: '\/etc\/kubernetes\/static-pod-certs\/secrets\/etcd-all-(serving|certs)\/etcd-serving-.*\.(?:crt|key)' remediation: | OpenShift does not use the etcd-certfile or etcd-keyfile flags. Certificates for etcd are managed by the etcd cluster operator. scored: true - id: 2.2 text: "Ensure that the --client-cert-auth argument is set to true (Manual)" audit: | # Get the node name where the pod is running NODE_NAME=$(oc get pod "$HOSTNAME" -o=jsonpath='{.spec.nodeName}') # Get the pod name in the openshift-etcd namespace POD_NAME=$(oc get pods -n openshift-etcd -l app=etcd --field-selector spec.nodeName="$NODE_NAME" -o jsonpath='{.items[0].metadata.name}' 2>/dev/null) if [ -z "$POD_NAME" ]; then echo "No matching file found on the current node." else # Execute the stat command oc exec -n openshift-etcd -c etcd "$POD_NAME" -- ps -o command= -C etcd | sed 's/.*\(--client-cert-auth=[^ ]*\).*/\1/' fi use_multiple_values: true tests: test_items: - flag: "--client-cert-auth" compare: op: eq value: true remediation: | This setting is managed by the cluster etcd operator. No remediation required." scored: true - id: 2.3 text: "Ensure that the --auto-tls argument is not set to true (Manual)" audit: | # Returns 0 if found, 1 if not found # Get the node name where the pod is running NODE_NAME=$(oc get pod "$HOSTNAME" -o=jsonpath='{.spec.nodeName}') # Get the pod name in the openshift-etcd namespace POD_NAME=$(oc get pods -n openshift-etcd -l app=etcd --field-selector spec.nodeName="$NODE_NAME" -o jsonpath='{.items[0].metadata.name}' 2>/dev/null) if [ -z "$POD_NAME" ]; then echo "No matching file found on the current node." else # Execute the stat command oc exec -n openshift-etcd -c etcd "$POD_NAME" -- ps -o command= -C etcd | grep -- --auto-tls=true 2>/dev/null ; echo exit_code=$? fi use_multiple_values: true tests: test_items: - flag: "exit_code" compare: op: eq value: "1" remediation: | This setting is managed by the cluster etcd operator. No remediation required. scored: true - id: 2.4 text: "Ensure that the --peer-cert-file and --peer-key-file arguments are set as appropriate (Manual)" audit: | # Get the node name where the pod is running NODE_NAME=$(oc get pod "$HOSTNAME" -o=jsonpath='{.spec.nodeName}') # Get the pod name in the openshift-etcd namespace POD_NAME=$(oc get pods -n openshift-etcd -l app=etcd --field-selector spec.nodeName="$NODE_NAME" -o jsonpath='{.items[0].metadata.name}' 2>/dev/null) if [ -z "$POD_NAME" ]; then echo "No matching file found on the current node." else # Execute the stat command oc exec -n openshift-etcd -c etcd "$POD_NAME" -- ps -o command= -C etcd | sed 's/.*\(--peer-cert-file=[^ ]*\).*/\1/' oc exec -n openshift-etcd -c etcd "$POD_NAME" -- ps -o command= -C etcd | sed 's/.*\(--peer-key-file=[^ ]*\).*/\1/' fi use_multiple_values: true tests: test_items: - flag: "file" compare: op: regex value: '\/etc\/kubernetes\/static-pod-certs\/secrets\/etcd-all-(peer|certs)\/etcd-peer-.*\.(?:crt|key)' remediation: | None. This configuration is managed by the etcd operator. scored: true - id: 2.5 text: "Ensure that the --peer-client-cert-auth argument is set to true (Manual)" audit: | # Get the node name where the pod is running NODE_NAME=$(oc get pod "$HOSTNAME" -o=jsonpath='{.spec.nodeName}') # Get the pod name in the openshift-etcd namespace POD_NAME=$(oc get pods -n openshift-etcd -l app=etcd --field-selector spec.nodeName="$NODE_NAME" -o jsonpath='{.items[0].metadata.name}' 2>/dev/null) if [ -z "$POD_NAME" ]; then echo "No matching file found on the current node." else # Execute the stat command oc exec -n openshift-etcd -c etcd "$POD_NAME" -- ps -o command= -C etcd | sed 's/.*\(--peer-client-cert-auth=[^ ]*\).*/\1/' fi use_multiple_values: true tests: test_items: - flag: "--peer-client-cert-auth" compare: op: eq value: true remediation: | This setting is managed by the cluster etcd operator. No remediation required. scored: true - id: 2.6 text: "Ensure that the --peer-auto-tls argument is not set to true (Manual)" audit: | # Returns 0 if found, 1 if not found # Get the node name where the pod is running NODE_NAME=$(oc get pod "$HOSTNAME" -o=jsonpath='{.spec.nodeName}') # Get the pod name in the openshift-etcd namespace POD_NAME=$(oc get pods -n openshift-etcd -l app=etcd --field-selector spec.nodeName="$NODE_NAME" -o jsonpath='{.items[0].metadata.name}' 2>/dev/null) if [ -z "$POD_NAME" ]; then echo "No matching file found on the current node." else # Execute the stat command oc exec -n openshift-etcd -c etcd "$POD_NAME" -- ps -o command= -C etcd | grep -- --peer-auto-tls=true 2>/dev/null ; echo exit_code=$? fi use_multiple_values: true tests: test_items: - flag: "exit_code" compare: op: eq value: "1" remediation: | This setting is managed by the cluster etcd operator. No remediation required. scored: true - id: 2.7 text: "Ensure that a unique Certificate Authority is used for etcd (Manual)" audit: | # Get the node name where the pod is running NODE_NAME=$(oc get pod "$HOSTNAME" -o=jsonpath='{.spec.nodeName}') # Get the pod name in the openshift-etcd namespace POD_NAME=$(oc get pods -n openshift-etcd -l app=etcd --field-selector spec.nodeName="$NODE_NAME" -o jsonpath='{.items[0].metadata.name}' 2>/dev/null) if [ -z "$POD_NAME" ]; then echo "No matching file found on the current node." else # Execute the stat command oc exec -n openshift-etcd -c etcd "$POD_NAME" -- ps -o command= -C etcd | sed 's/.*\(--trusted-ca-file=[^ ]*\).*/\1/' oc exec -n openshift-etcd -c etcd "$POD_NAME" -- ps -o command= -C etcd | sed 's/.*\(--peer-trusted-ca-file=[^ ]*\).*/\1/' fi use_multiple_values: true tests: test_items: - flag: "file" compare: op: regex value: '\/etc\/kubernetes\/static-pod-certs\/configmaps\/(?:etcd-(?:serving|peer-client)-ca\/ca-bundle\.crt|etcd-all-bundles\/server-ca-bundle\.crt)' remediation: | None required. Certificates for etcd are managed by the OpenShift cluster etcd operator. scored: true ================================================ FILE: cfg/rh-1.8/master.yaml ================================================ --- controls: version: rh-1.8 id: 1 text: "Control Plane Components" type: "master" groups: - id: 1.1 text: "Master Node Configuration Files" checks: - id: 1.1.1 text: "Ensure that the API server pod specification file permissions are set to 600 or more restrictive (Manual)" audit: | # Get the node name where the pod is running NODE_NAME=$(oc get pod "$HOSTNAME" -o=jsonpath='{.spec.nodeName}') # Get the pod name in the openshift-kube-apiserver namespace POD_NAME=$(oc get pods -n openshift-kube-apiserver -l app=openshift-kube-apiserver --field-selector spec.nodeName="$NODE_NAME" -o jsonpath='{.items[0].metadata.name}' 2>/dev/null) if [ -z "$POD_NAME" ]; then echo "No matching pods found on the current node." else # Execute the stat command oc exec -n openshift-kube-apiserver "$POD_NAME" -- stat -c "$POD_NAME %n permissions=%a" /etc/kubernetes/static-pod-resources/kube-apiserver-pod.yaml fi use_multiple_values: true tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | No remediation required; file permissions are managed by the operator. scored: true - id: 1.1.2 text: "Ensure that the API server pod specification file ownership is set to root:root (Manual)" audit: | # Get the node name where the pod is running NODE_NAME=$(oc get pod "$HOSTNAME" -o=jsonpath='{.spec.nodeName}') # Get the pod name in the openshift-kube-apiserver namespace POD_NAME=$(oc get pods -n openshift-kube-apiserver -l app=openshift-kube-apiserver --field-selector spec.nodeName="$NODE_NAME" -o jsonpath='{.items[0].metadata.name}' 2>/dev/null) if [ -z "$POD_NAME" ]; then echo "No matching pods found on the current node." else # Execute the stat command oc exec -n openshift-kube-apiserver "$POD_NAME" -- stat -c "$POD_NAME %n %U:%G" /etc/kubernetes/static-pod-resources/kube-apiserver-pod.yaml fi use_multiple_values: true tests: test_items: - flag: "root:root" remediation: | No remediation required; file permissions are managed by the operator. scored: true - id: 1.1.3 text: "Ensure that the controller manager pod specification file permissions are set to 600 or more restrictive (Manual)" audit: | # Get the node name where the pod is running NODE_NAME=$(oc get pod "$HOSTNAME" -o=jsonpath='{.spec.nodeName}') # Get the pod name in the openshift-kube-controller-manager namespace POD_NAME=$(oc get pods -n openshift-kube-controller-manager -l app=kube-controller-manager --field-selector spec.nodeName="$NODE_NAME" -o jsonpath='{.items[0].metadata.name}' 2>/dev/null) if [ -z "$POD_NAME" ]; then echo "No matching pods found on the current node." else # Execute the stat command oc exec -n openshift-kube-controller-manager "$POD_NAME" -- stat -c "$POD_NAME %n permissions=%a" /etc/kubernetes/static-pod-resources/kube-controller-manager-pod.yaml fi use_multiple_values: true tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | No remediation required; file permissions are managed by the operator. scored: true - id: 1.1.4 text: "Ensure that the controller manager pod specification file ownership is set to root:root (Manual)" audit: | # Get the node name where the pod is running NODE_NAME=$(oc get pod "$HOSTNAME" -o=jsonpath='{.spec.nodeName}') # Get the pod name in the openshift-kube-controller-manager namespace POD_NAME=$(oc get pods -n openshift-kube-controller-manager -l app=kube-controller-manager --field-selector spec.nodeName="$NODE_NAME" -o jsonpath='{.items[0].metadata.name}' 2>/dev/null) if [ -z "$POD_NAME" ]; then echo "No matching pods found on the current node." else # Execute the stat command oc exec -n openshift-kube-controller-manager "$POD_NAME" -- stat -c "$POD_NAME %n %U:%G" /etc/kubernetes/static-pod-resources/kube-controller-manager-pod.yaml fi use_multiple_values: true tests: test_items: - flag: "root:root" remediation: | No remediation required; file permissions are managed by the operator. scored: true - id: 1.1.5 text: "Ensure that the scheduler pod specification file permissions are set to 600 or more restrictive (Manual)" audit: | # Get the node name where the pod is running NODE_NAME=$(oc get pod "$HOSTNAME" -o=jsonpath='{.spec.nodeName}') # Get the pod name in the openshift-kube-scheduler namespace POD_NAME=$(oc get pods -n openshift-kube-scheduler -l app=openshift-kube-scheduler --field-selector spec.nodeName="$NODE_NAME" -o jsonpath='{.items[0].metadata.name}' 2>/dev/null) if [ -z "$POD_NAME" ]; then echo "No matching pods found on the current node." else # Execute the stat command oc exec -n openshift-kube-scheduler "$POD_NAME" -- stat -c "$POD_NAME %n permissions=%a" /etc/kubernetes/static-pod-resources/kube-scheduler-pod.yaml fi use_multiple_values: true tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | No remediation required; file permissions are managed by the operator. scored: true - id: 1.1.6 text: "Ensure that the scheduler pod specification file ownership is set to root:root (Manual))" audit: | # Get the node name where the pod is running NODE_NAME=$(oc get pod "$HOSTNAME" -o=jsonpath='{.spec.nodeName}') # Get the pod name in the openshift-kube-scheduler namespace POD_NAME=$(oc get pods -n openshift-kube-scheduler -l app=openshift-kube-scheduler --field-selector spec.nodeName="$NODE_NAME" -o jsonpath='{.items[0].metadata.name}' 2>/dev/null) if [ -z "$POD_NAME" ]; then echo "No matching pods found on the current node." else # Execute the stat command oc exec -n openshift-kube-scheduler "$POD_NAME" -- stat -c "$POD_NAME %n %U:%G" /etc/kubernetes/static-pod-resources/kube-scheduler-pod.yaml fi use_multiple_values: true tests: test_items: - flag: "root:root" remediation: | No remediation required; file permissions are managed by the operator. scored: true - id: 1.1.7 text: "Ensure that the etcd pod specification file permissions are set to 600 or more restrictive (Manual))" audit: | # Get the node name where the pod is running NODE_NAME=$(oc get pod "$HOSTNAME" -o=jsonpath='{.spec.nodeName}') # Get the pod name in the openshift-etcd namespace POD_NAME=$(oc get pods -n openshift-etcd -l app=etcd --field-selector spec.nodeName="$NODE_NAME" -o jsonpath='{.items[0].metadata.name}' 2>/dev/null) if [ -z "$POD_NAME" ]; then echo "No matching pods found on the current node." else # Execute the stat command oc rsh -n openshift-etcd "$POD_NAME" stat -c "$POD_NAME %n permissions=%a" /etc/kubernetes/manifests/etcd-pod.yaml fi use_multiple_values: true tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | No remediation required; file permissions are managed by the operator. scored: true - id: 1.1.8 text: "Ensure that the etcd pod specification file ownership is set to root:root (Manual)" audit: | # Get the node name where the pod is running NODE_NAME=$(oc get pod "$HOSTNAME" -o=jsonpath='{.spec.nodeName}') # Get the pod name in the openshift-etcd namespace POD_NAME=$(oc get pods -n openshift-etcd -l app=etcd --field-selector spec.nodeName="$NODE_NAME" -o jsonpath='{.items[0].metadata.name}' 2>/dev/null) if [ -z "$POD_NAME" ]; then echo "No matching pods found on the current node." else # Execute the stat command oc rsh -n openshift-etcd "$POD_NAME" stat -c "$POD_NAME %n %U:%G" /etc/kubernetes/manifests/etcd-pod.yaml fi use_multiple_values: true tests: test_items: - flag: "root:root" remediation: | No remediation required; file permissions are managed by the operator. scored: true - id: 1.1.9 text: "Ensure that the Container Network Interface file permissions are set to 600 or more restrictive (Manual)" audit: | # Get the node name where the pod is running NODE_NAME=$(oc get pod "$HOSTNAME" -o=jsonpath='{.spec.nodeName}') # For CNI multus # Get the pod name in the openshift-multus namespace POD_NAME=$(oc get pods -n openshift-multus -l app=multus --field-selector spec.nodeName="$NODE_NAME" -o jsonpath='{.items[0].metadata.name}' 2>/dev/null) if [ -z "$POD_NAME" ]; then echo "No matching pods found on the current node." else # Execute the stat command oc exec -n openshift-multus "$POD_NAME" -- /bin/bash -c "stat -c \"$i %n permissions=%a\" /host/etc/cni/net.d/*.conf"; 2>/dev/null oc exec -n openshift-multus "$POD_NAME" -- /bin/bash -c "stat -c \"$i %n permissions=%a\" /host/var/run/multus/cni/net.d/*.conf"; 2>/dev/null fi # For SDN pods POD_NAME=$(oc get pods -n openshift-sdn -l app=sdn --field-selector spec.nodeName="$NODE_NAME" -o jsonpath='{.items[0].metadata.name}' 2>/dev/null) if [ -z "$POD_NAME" ]; then echo "No matching pods found on the current node." else # Execute the stat command oc exec -n openshift-sdn "$POD_NAME" -- find /var/lib/cni/networks/openshift-sdn -type f -exec stat -c "$i %n permissions=%a" {} \; 2>/dev/null oc exec -n openshift-sdn "$POD_NAME" -- find /var/run/openshift-sdn -type f -exec stat -c "$i %n permissions=%a" {} \; 2>/dev/null fi # For OVS pods POD_NAME=$(oc get pods -n openshift-sdn -l app=ovs --field-selector spec.nodeName="$NODE_NAME" -o jsonpath='{.items[0].metadata.name}' 2>/dev/null) if [ -z "$POD_NAME" ]; then echo "No matching pods found on the current node." else # Execute the stat command oc exec -n openshift-sdn "$POD_NAME" -- find /var/run/openvswitch -type f -exec stat -c "$i %n permissions=%a" {} \; 2>/dev/null oc exec -n openshift-sdn "$POD_NAME" -- find /etc/openvswitch -type f -exec stat -c "$i %n permissions=%a" {} \; 2>/dev/null oc exec -n openshift-sdn "$POD_NAME" -- find /run/openvswitch -type f -exec stat -c "$i %n permissions=%a" {} \; 2>/dev/null fi use_multiple_values: true tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | No remediation required; file permissions are managed by the operator. scored: true - id: 1.1.10 text: "Ensure that the Container Network Interface file ownership is set to root:root (Manual)" audit: | # Get the node name where the pod is running NODE_NAME=$(oc get pod "$HOSTNAME" -o=jsonpath='{.spec.nodeName}') # For CNI multus # Get the pod name in the openshift-multus namespace POD_NAME=$(oc get pods -n openshift-multus -l app=multus --field-selector spec.nodeName="$NODE_NAME" -o jsonpath='{.items[0].metadata.name}' 2>/dev/null) if [ -z "$POD_NAME" ]; then echo "No matching pods found on the current node." else # Execute the stat command oc exec -n openshift-multus "$POD_NAME" -- /bin/bash -c "stat -c '$i %n %U:%G' /host/etc/cni/net.d/*.conf" 2>/dev/null oc exec -n openshift-multus $i -- /bin/bash -c "stat -c '$i %n %U:%G' /host/var/run/multus/cni/net.d/*.conf" 2>/dev/null fi # For SDN pods POD_NAME=$(oc get pods -n openshift-sdn -l app=sdn --field-selector spec.nodeName="$NODE_NAME" -o jsonpath='{.items[0].metadata.name}' 2>/dev/null) if [ -z "$POD_NAME" ]; then echo "No matching pods found on the current node." else # Execute the stat command oc exec -n openshift-sdn "$POD_NAME" -- find /var/lib/cni/networks/openshift-sdn -type f -exec stat -c "$i %n %U:%G" {} \; 2>/dev/null oc exec -n openshift-sdn "$POD_NAME" -- find /var/run/openshift-sdn -type f -exec stat -c "$i %n %U:%G" {} \; 2>/dev/null fi # For OVS pods in 4.5 POD_NAME=$(oc get pods -n openshift-sdn -l app=ovs --field-selector spec.nodeName="$NODE_NAME" -o jsonpath='{.items[0].metadata.name}' 2>/dev/null) if [ -z "$POD_NAME" ]; then echo "No matching pods found on the current node." else # Execute the stat command oc exec -n openshift-sdn "$POD_NAME" -- find /var/run/openvswitch -type f -exec stat -c "$i %n %U:%G" {} \; 2>/dev/null oc exec -n openshift-sdn "$POD_NAME" -- find /etc/openvswitch -type f -exec stat -c "$i %n %U:%G" {} \; 2>/dev/null oc exec -n openshift-sdn "$POD_NAME" -- find /run/openvswitch -type f -exec stat -c "$i %n %U:%G" {} \; 2>/dev/null fi use_multiple_values: true tests: test_items: - flag: "root:root" remediation: | No remediation required; file permissions are managed by the operator. scored: true - id: 1.1.11 text: "Ensure that the etcd data directory permissions are set to 700 or more restrictive (Manual)" audit: | # Get the node name where the pod is running NODE_NAME=$(oc get pod "$HOSTNAME" -o=jsonpath='{.spec.nodeName}') # Get the pod name in the openshift-etcd namespace POD_NAME=$(oc get pods -n openshift-etcd -l app=etcd --field-selector spec.nodeName="$NODE_NAME" -o jsonpath='{.items[0].metadata.name}' 2>/dev/null) if [ -z "$POD_NAME" ]; then echo "No matching pods found on the current node." else # Execute the stat command oc exec -n openshift-etcd "$POD_NAME" -- stat -c "$POD_NAME %n permissions=%a" /var/lib/etcd/member fi use_multiple_values: true tests: test_items: - flag: "permissions" compare: op: bitmask value: "700" remediation: | No remediation required; file permissions are managed by the operator. scored: true - id: 1.1.12 text: "Ensure that the etcd data directory ownership is set to etcd:etcd (Manual)" audit: | # Get the node name where the pod is running NODE_NAME=$(oc get pod "$HOSTNAME" -o=jsonpath='{.spec.nodeName}') # Get the pod name in the openshift-etcd namespace POD_NAME=$(oc get pods -n openshift-etcd -l app=etcd --field-selector spec.nodeName="$NODE_NAME" -o jsonpath='{.items[0].metadata.name}' 2>/dev/null) if [ -z "$POD_NAME" ]; then echo "No matching pods found on the current node." else # Execute the stat command oc exec -n openshift-etcd "$POD_NAME" -- stat -c "$POD_NAME %n %U:%G" /var/lib/etcd/member fi use_multiple_values: true tests: test_items: - flag: "root:root" remediation: | No remediation required; file permissions are managed by the operator. scored: true - id: 1.1.13 text: "Ensure that the kubeconfig file permissions are set to 600 or more restrictive (Manual)" audit: | NODE_NAME=$(oc get pod $HOSTNAME -o=jsonpath='{.spec.nodeName}') oc debug node/$NODE_NAME -- chroot /host stat -c "$NODE_NAME %n permissions=%a" /etc/kubernetes/kubeconfig 2> /dev/null use_multiple_values: true tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | No remediation required; file permissions are managed by the operator. scored: true - id: 1.1.14 text: "Ensure that the kubeconfig file ownership is set to root:root (Manual)" audit: | NODE_NAME=$(oc get pod $HOSTNAME -o=jsonpath='{.spec.nodeName}') oc debug node/$NODE_NAME -- chroot /host stat -c "$NODE_NAME %n %U:%G" /etc/kubernetes/kubeconfig 2> /dev/null use_multiple_values: true tests: test_items: - flag: "root:root" remediation: | No remediation required; file permissions are managed by the operator. scored: true - id: 1.1.15 text: "Ensure that the scheduler kubeconfig file permissions are set to 600 or more restrictive (Manual)" audit: | # Get the node name where the pod is running NODE_NAME=$(oc get pod "$HOSTNAME" -o=jsonpath='{.spec.nodeName}') # Get the pod name in the openshift-kube-scheduler namespace POD_NAME=$(oc get pods -n openshift-kube-scheduler -l app=openshift-kube-scheduler --field-selector spec.nodeName="$NODE_NAME" -o jsonpath='{.items[0].metadata.name}' 2>/dev/null) if [ -z "$POD_NAME" ]; then echo "No matching pods found on the current node." else # Execute the stat command oc exec -n openshift-kube-scheduler "$POD_NAME" -- stat -c "$POD_NAME %n permissions=%a" /etc/kubernetes/static-pod-resources/configmaps/scheduler-kubeconfig/kubeconfig fi use_multiple_values: true tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | No remediation required; file permissions are managed by the operator. scored: true - id: 1.1.16 text: "Ensure that the scheduler kubeconfig file ownership is set to root:root (Manual)" audit: | # Get the node name where the pod is running NODE_NAME=$(oc get pod "$HOSTNAME" -o=jsonpath='{.spec.nodeName}') # Get the pod name in the openshift-kube-scheduler namespace POD_NAME=$(oc get pods -n openshift-kube-scheduler -l app=openshift-kube-scheduler --field-selector spec.nodeName="$NODE_NAME" -o jsonpath='{.items[0].metadata.name}' 2>/dev/null) if [ -z "$POD_NAME" ]; then echo "No matching pods found on the current node." else # Execute the stat command oc exec -n openshift-kube-scheduler "$POD_NAME" -- stat -c "$POD_NAME %n %U:%G" /etc/kubernetes/static-pod-resources/configmaps/scheduler-kubeconfig/kubeconfig fi use_multiple_values: true tests: test_items: - flag: "root:root" remediation: | No remediation required; file permissions are managed by the operator. scored: true - id: 1.1.17 text: "Ensure that the controller-manager kubeconfig file permissions are set to 600 or more restrictive (Manual)" audit: | # Get the node name where the pod is running NODE_NAME=$(oc get pod "$HOSTNAME" -o=jsonpath='{.spec.nodeName}') # Get the pod name in the openshift-kube-controller-manager namespace POD_NAME=$(oc get pods -n openshift-kube-controller-manager -l app=kube-controller-manager --field-selector spec.nodeName="$NODE_NAME" -o jsonpath='{.items[0].metadata.name}' 2>/dev/null) if [ -z "$POD_NAME" ]; then echo "No matching pods found on the current node." else # Execute the stat command oc exec -n openshift-kube-controller-manager "$POD_NAME" -- stat -c "$POD_NAME %n permissions=%a" /etc/kubernetes/static-pod-resources/configmaps/controller-manager-kubeconfig/kubeconfig fi use_multiple_values: true tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | No remediation required; file permissions are managed by the operator. scored: true - id: 1.1.18 text: "Ensure that the controller-manager kubeconfig file ownership is set to root:root (Manual)" audit: | # Get the node name where the pod is running NODE_NAME=$(oc get pod "$HOSTNAME" -o=jsonpath='{.spec.nodeName}') # Get the pod name in the openshift-kube-controller-manager namespace POD_NAME=$(oc get pods -n openshift-kube-controller-manager -l app=kube-controller-manager --field-selector spec.nodeName="$NODE_NAME" -o jsonpath='{.items[0].metadata.name}' 2>/dev/null) if [ -z "$POD_NAME" ]; then echo "No matching pods found on the current node." else # Execute the stat command oc exec -n openshift-kube-controller-manager "$POD_NAME" -- stat -c "$POD_NAME %n %U:%G" /etc/kubernetes/static-pod-resources/configmaps/controller-manager-kubeconfig/kubeconfig fi use_multiple_values: true tests: test_items: - flag: "root:root" remediation: | No remediation required; file permissions are managed by the operator. scored: true - id: 1.1.19 text: "Ensure that the OpenShift PKI directory and file ownership is set to root:root (Manual)" audit: | # Should return root:root for all files and directories # Get the node name where the pod is running NODE_NAME=$(oc get pod "$HOSTNAME" -o=jsonpath='{.spec.nodeName}') # Get the pod name in the openshift-kube-controller-manager namespace POD_NAME=$(oc get pods -n openshift-kube-apiserver -l app=openshift-kube-apiserver --field-selector spec.nodeName="$NODE_NAME" -o jsonpath='{.items[0].metadata.name}' 2>/dev/null) if [ -z "$POD_NAME" ]; then echo "No matching pods found on the current node." else # echo $i static-pod-certs oc exec -n openshift-kube-apiserver "$POD_NAME" -c kube-apiserver -- find /etc/kubernetes/static-pod-certs -type d -wholename '*/secrets*' -exec stat -c "$i %n %U:%G" {} \; oc exec -n openshift-kube-apiserver "$POD_NAME" -c kube-apiserver -- find /etc/kubernetes/static-pod-certs -type f -wholename '*/secrets*' -exec stat -c "$i %n %U:%G" {} \; # echo $i static-pod-resources oc exec -n openshift-kube-apiserver "$POD_NAME" -c kube-apiserver -- find /etc/kubernetes/static-pod-resources -type d -wholename '*/secrets*' -exec stat -c "$i %n %U:%G" {} \; oc exec -n openshift-kube-apiserver "$POD_NAME" -c kube-apiserver -- find /etc/kubernetes/static-pod-resources -type f -wholename '*/secrets*' -exec stat -c "$i %n %U:%G" {} \; fi use_multiple_values: true tests: test_items: - flag: "root:root" remediation: | No remediation required; file permissions are managed by the operator. scored: true - id: 1.1.20 text: "Ensure that the OpenShift PKI certificate file permissions are set to 600 or more restrictive (Manual)" audit: | # Get the node name where the pod is running NODE_NAME=$(oc get pod "$HOSTNAME" -o=jsonpath='{.spec.nodeName}') # Get the pod name in the openshift-kube-apiserver namespace POD_NAME=$(oc get pods -n openshift-kube-apiserver -l app=openshift-kube-apiserver --field-selector spec.nodeName="$NODE_NAME" -o jsonpath='{.items[0].metadata.name}' 2>/dev/null) if [ -z "$POD_NAME" ]; then echo "No matching pods found on the current node." else # Execute the stat command oc exec -n openshift-kube-apiserver "$POD_NAME" -c kube-apiserver -- find /etc/kubernetes/static-pod-certs -type f -wholename '*/secrets/*.crt' -exec stat -c "$POD_NAME %n permissions=%a" {} \; fi use_multiple_values: true tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | No remediation required; file permissions are managed by the operator. scored: true - id: 1.1.21 text: "Ensure that the OpenShift PKI key file permissions are set to 600 (Manual)" audit: | # Get the node name where the pod is running NODE_NAME=$(oc get pod "$HOSTNAME" -o=jsonpath='{.spec.nodeName}') # Get the pod name in the openshift-kube-apiserver namespace POD_NAME=$(oc get pods -n openshift-kube-apiserver -l app=openshift-kube-apiserver --field-selector spec.nodeName="$NODE_NAME" -o jsonpath='{.items[0].metadata.name}' 2>/dev/null) if [ -z "$POD_NAME" ]; then echo "No matching pods found on the current node." else # Execute the stat command oc exec -n openshift-kube-apiserver "$POD_NAME" -c kube-apiserver -- find /etc/kubernetes/static-pod-certs -type f -wholename '*/secrets/*.key' -exec stat -c "$POD_NAME %n permissions=%a" {} \; fi use_multiple_values: true tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | No remediation required; file permissions are managed by the operator. scored: true - id: 1.2 text: "API Server" checks: - id: 1.2.1 text: "Ensure that anonymous requests are authorized (Manual)" audit: | found=0 echo "# ClusterRoleBindings granting permissions to system:unauthenticated" crb_out="$(oc get clusterrolebindings -o json 2>/dev/null \ | jq -r '.items[] | select(.subjects[]? | select(.kind=="Group" and .name=="system:unauthenticated")) | .metadata.name + " -> " + .roleRef.kind + "/" + .roleRef.name' \ | sort -u)" if [ -n "$crb_out" ]; then echo "$crb_out" found=1 else echo "(none)" fi echo echo "# Namespaced RoleBindings granting permissions to system:unauthenticated" rb_out="$(oc get rolebindings -A -o json 2>/dev/null \ | jq -r '.items[] | select(.subjects[]? | select(.kind=="Group" and .name=="system:unauthenticated")) | (.metadata.namespace + "/" + .metadata.name) + " -> " + .roleRef.kind + "/" + .roleRef.name' \ | sort -u)" if [ -n "$rb_out" ]; then echo "$rb_out" found=1 else echo "(none)" fi # Provide a simple flag for the test harness if [ $found -eq 1 ]; then echo "unauthenticated_bindings_present" else echo "unauthenticated_bindings_missing" fi tests: test_items: - flag: "unauthenticated_bindings_present" set: true remediation: | None required. The default configuration should not be modified. scored: true - id: 1.2.2 text: "Use HTTPS for kubelet connections (Manual)" audit: | CFG=$(oc -n openshift-kube-apiserver get cm config -o jsonpath='{.data.config\.yaml}') # Extract kubelet client cert/key paths (support both layouts) CERT_FILE=$(printf '%s\n' "$CFG" \ | grep -Eo '/etc/kubernetes/static-pod-(resources/kube-apiserver-certs|certs)/secrets/kubelet-client/tls\.crt' \ | head -n1) KEY_FILE=$(printf '%s\n' "$CFG" \ | grep -Eo '/etc/kubernetes/static-pod-(resources/kube-apiserver-certs|certs)/secrets/kubelet-client/tls\.key' \ | head -n1) # 1) pass/fail on presence of both files if [ -n "$CERT_FILE" ] && [ -n "$KEY_FILE" ]; then echo "pass" else echo "fail" fi KUBELET_HTTPS=$(printf '%s\n' "$CFG" \ | grep -Eo '(^|[[:space:]])kubelet-https:[[:space:]]*(true|false)' \ | awk -F: '{print $2}' \ | tr -d '[:space:]' \ | head -n1) if [ "$KUBELET_HTTPS" = "false" ]; then echo "false" else echo "true" fi oc -n openshift-apiserver describe secret serving-cert | grep -E 'tls\.crt|tls\.key|Type:' tests: bin_op: and test_items: - flag: "pass" - flag: "true" - flag: "kubernetes.io/tls" - flag: "tls.crt" - flag: "tls.key" remediation: | OpenShift does not use the legacy --kubelet-https flag; TLS is enforced via kubelet client cert/key arguments and cluster CAs. Ensure: - apiServerArguments.kubelet-client-certificate[0] points to a real file - apiServerArguments.kubelet-client-key[0] points to a real file - The openshift-apiserver 'serving-cert' secret is type kubernetes.io/tls and contains tls.crt and tls.key scored: false - id: 1.2.3 text: "Ensure that the kubelet uses certificates to authenticate (Manual)" audit: | oc get configmap config -n openshift-kube-apiserver -ojson | jq -r '.data["config.yaml"]' | jq '.apiServerArguments["kubelet-client-certificate"]' oc get configmap config -n openshift-kube-apiserver -ojson | jq -r'.data["config.yaml"]' | jq '.apiServerArguments["kubelet-client-key"]' oc -n openshift-apiserver describe secret serving-cert tests: test_items: - flag: "/etc/kubernetes/static-pod-certs/secrets/kubelet-client/tls.crt" - flag: "/etc/kubernetes/static-pod-certs/secrets/kubelet-client/tls.key" - flag: "kubernetes.io/tls" remediation: | No remediation is required. OpenShift automatically manages kubelet authentication using X.509 certificates issued by the internal platform CA. Manual modification of these certificates is not supported and can disrupt platform components. scored: true - id: 1.2.4 text: "Verify that the kubelet certificate authority is set as appropriate (Manual)" audit: | oc get configmap config -n openshift-kube-apiserver -ojson \ | jq -r '.data["config.yaml"]' \ | jq '.apiServerArguments["kubelet-certificate-authority"]' tests: test_items: - flag: "/etc/kubernetes/static-pod-resources/configmaps/kubelet-serving-ca/ca-bundle.crt" remediation: | No remediation is required. OpenShift uses internal X.509 certificates and platform-managed CAs to verify kubelet server identities. This is not user-configurable and should not be modified. scored: true - id: 1.2.5 text: "Ensure that the --authorization-mode argument is not set to AlwaysAllow (Manual)" audit: | oc get configmap config -n openshift-kube-apiserver -o json \ | jq -r '.data["config.yaml"]' \ | jq '.apiServerArguments["authorization-mode"]' audit_config: | oc get configmap config -n openshift-kube-apiserver -ojson | jq -r '.data["config.yaml"]' | jq '.apiServerArguments' tests: bin_op: or test_items: - path: "{.authorization-mode}" compare: op: nothave value: "AlwaysAllow" - path: "{.authorization-mode}" flag: "authorization-mode" set: false remediation: | No remediation required. OpenShift does not support the 'AlwaysAllow' authorization mode. The API server is bootstrapped with secure authorization mechanisms including RBAC and Node by default. scored: true - id: 1.2.6 text: "Verify that RBAC is enabled (Manual)" audit: | oc get configmap config -n openshift-kube-apiserver -o json \ | jq -r '.data["config.yaml"]' \ | jq '.apiServerArguments["authorization-mode"]' audit_config: | oc get configmap config -n openshift-kube-apiserver -ojson | jq -r '.data["config.yaml"]' | jq '.apiServerArguments' tests: bin_op: or test_items: - path: "{.authorization-mode}" compare: op: has value: "RBAC" - path: "{.authorization-mode}" flag: "authorization-mode" set: false remediation: | No remediation is required. OpenShift is configured at bootstrap time to use Role-Based Access Control (RBAC) as the default authorization mode. RBAC is always enabled, and cannot be disabled through configuration. scored: true - id: 1.2.7 text: "Ensure that the APIPriorityAndFairness feature gate is enabled (Manual)" audit: | oc get kubeapiservers.operator.openshift.io cluster -o json | jq '.spec.observedConfig.apiServerArguments' tests: test_items: - flag: "APIPriorityAndFairness=true" remediation: | No remediation is required scored: true - id: 1.2.8 text: "Ensure that the admission control plugin AlwaysAdmit is not set (Manual)" audit: | oc -n openshift-kube-apiserver get configmap config -o json | jq -r '.data."config.yaml"' | jq '.apiServerArguments."enable-admission-plugins"' tests: test_items: - flag: "AlwaysAdmit" set: false remediation: | No remediation is required. The AlwaysAdmit admission controller cannot be enabled in OpenShift. scored: true - id: 1.2.9 text: "Ensure that the admission control plugin AlwaysPullImages is set (Manual)" audit: | oc -n openshift-kube-apiserver get configmap config -o json | jq -r '.data."config.yaml"' | jq '.apiServerArguments."enable-admission-plugins"' tests: test_items: - flag: "AlwaysPullImages" set: false remediation: | None required. scored: true - id: 1.2.10 text: "Ensure that the admission control plugin ServiceAccount is set (Manual)" audit: | oc -n openshift-kube-apiserver get configmap config -o json | jq -r '.data."config.yaml"' | jq '.apiServerArguments."enable-admission-plugins"' tests: test_items: - flag: "ServiceAccount" set: true remediation: | None required. OpenShift is configured to use service accounts by default. scored: true - id: 1.2.11 text: "Ensure that the admission control plugin NamespaceLifecycle is set (Manual)" audit: | oc -n openshift-kube-apiserver get configmap config -o json | jq -r '.data."config.yaml"' | jq '.apiServerArguments."enable-admission-plugins"' output=$(oc -n openshift-kube-apiserver get configmap config -o json | jq -r '.data."config.yaml"' | jq '.apiServerArguments."enable-admission-plugins"') [ "$output" == "null" ] && echo "ocp 4.5 has NamespaceLifecycle compiled" || echo $output tests: test_items: - flag: "NamespaceLifecycle" remediation: | Ensure that the --disable-admission-plugins parameter does not include NamespaceLifecycle. scored: true - id: 1.2.12 text: "Ensure that the admission control plugin SecurityContextConstraint is set (Manual)" audit: | oc -n openshift-kube-apiserver get configmap config -o json | jq -r '.data."config.yaml"' | jq '.apiServerArguments."enable-admission-plugins"' output=$(oc -n openshift-kube-apiserver get configmap config -o json | jq -r '.data."config.yaml"' | jq '.apiServerArguments."enable-admission-plugins"') [ "$output" == "null" ] && echo "ocp 4.5 has SecurityContextConstraint compiled" || echo $output tests: test_items: - flag: "security.openshift.io/SecurityContextConstraint" remediation: | None required. Security Context Constraints are enabled by default in OpenShift and cannot be disabled. scored: true - id: 1.2.13 text: "Ensure that the admission control plugin NodeRestriction is set (Manual)" audit: | oc -n openshift-kube-apiserver get configmap config -o json | jq -r '.data."config.yaml"' | jq '.apiServerArguments."enable-admission-plugins"' output=$(oc -n openshift-kube-apiserver get configmap config -o json | jq -r '.data."config.yaml"' | jq '.apiServerArguments."enable-admission-plugins"') [ "$output" == "null" ] && echo "ocp 4.5 has NodeRestriction compiled" || echo $output tests: test_items: - flag: "NodeRestriction" remediation: | The NodeRestriction plugin cannot be disabled. scored: true - id: 1.2.14 text: "Ensure that the --insecure-bind-address argument is not set (manual)" audit: | # Get the insecure-bind-address value insecure_bind_address=$(oc get kubeapiservers.operator.openshift.io cluster -ojson \ | jq -r '.spec.observedConfig.apiServerArguments["insecure-bind-address"][]?') # Get port from openshift-kube-apiserver kube_api_port=$(oc -n openshift-kube-apiserver get endpoints -o jsonpath='{.items[*].subsets[*].ports[*].port}') # Get port from openshift-apiserver openshift_api_port=$(oc -n openshift-apiserver get endpoints -o jsonpath='{.items[*].subsets[*].ports[*].port}') # Evaluate logic [[ -z "$insecure_bind_address" ]] && \ [[ "$kube_api_port" == *"6443"* ]] && \ [[ "$openshift_api_port" == *"8443"* ]] && echo "pass" || echo "fail" tests: test_items: - flag: "pass" remediation: | No remediation is required. By default, OpenShift uses secure HTTPS ports (6443 and 8443) for all API communications. The API servers are not configured to expose insecure ports and are isolated within the pod network. scored: true - id: 1.2.15 text: "Ensure that the --insecure-port argument is set to 0 (Manual)" audit: | oc -n openshift-kube-apiserver get endpoints -o jsonpath='{.items[*].subsets[*].ports[*].port}' tests: test_items: - flag: "6443" remediation: | None required. The configuration is managed by the API server operator. scored: true - id: 1.2.16 text: "Ensure that the --secure-port argument is not set to 0 (Manual)" audit: | BIND_ADDR=$(oc get kubeapiservers.operator.openshift.io cluster -o json \ | jq -r '.spec.observedConfig.servingInfo.bindAddress') PORTS=$(oc get pods -n openshift-kube-apiserver -l app=openshift-kube-apiserver \ -o jsonpath='{.items[*].spec.containers[?(@.name=="kube-apiserver")].ports[*].containerPort}') if [ "$BIND_ADDR" = "0.0.0.0:6443" ] && echo "$PORTS" | grep -q '\b6443\b'; then echo "pass" else echo "fail" fi tests: test_items: - flag: "pass" remediation: | None required. OpenShift serves the API securely over port 6443 with TLS, authentication, and authorization. The insecure API port is not exposed or configurable by default. scored: true - id: 1.2.17 text: "Ensure that the healthz endpoint is protected by RBAC (Manual)" type: manual remediation: | None required as profiling data is protected by RBAC. scored: false - id: 1.2.18 text: "Ensure that the --audit-log-path argument is set (Manual)" audit: | # Get kube-apiserver audit log path kube_path=$(oc get configmap config -n openshift-kube-apiserver -ojson \ | jq -r '.data["config.yaml"]' \ | jq -r '.apiServerArguments["audit-log-path"][]?') # Get OpenShift apiserver audit log path os_path=$(oc get configmap config -n openshift-apiserver -ojson \ | jq -r '.data["config.yaml"]' \ | jq -r '.apiServerArguments["audit-log-path"][]?') # Check if log file exists in kube-apiserver pod kube_pod=$(oc get pods -n openshift-kube-apiserver -l app=openshift-kube-apiserver -o jsonpath='{.items[0].metadata.name}') oc rsh -n openshift-kube-apiserver -c kube-apiserver $kube_pod ls "$kube_path" >/dev/null 2>&1 kube_exists=$? # Check if log file exists in openshift-apiserver pod os_pod=$(oc get pods -n openshift-apiserver -l apiserver=true -o jsonpath='{.items[0].metadata.name}') oc rsh -n openshift-apiserver $os_pod ls "$os_path" >/dev/null 2>&1 os_exists=$? # Evaluate all conditions [[ "$kube_path" == "/var/log/kube-apiserver/audit.log" ]] && \ [[ "$os_path" == "/var/log/openshift-apiserver/audit.log" ]] && \ [[ $kube_exists -eq 0 ]] && \ [[ $os_exists -eq 0 ]] && echo "pass" || echo "fail" tests: test_items: - flag: "pass" remediation: | No remediation is required. OpenShift manages audit logging automatically via the apiserver configuration. By default, the audit log paths are: - /var/log/kube-apiserver/audit.log - /var/log/openshift-apiserver/audit.log scored: true - id: 1.2.19 text: "Ensure that the audit logs are forwarded off the cluster for retention (Manual)" type: "manual" remediation: | Follow the documentation for log forwarding. Forwarding logs to third party systems https://docs.openshift.com/container-platform/4.5/logging/cluster-logging-external.html scored: false - id: 1.2.20 text: "Ensure that the maximumRetainedFiles argument is set to 10 or as appropriate (Manual)" audit: | VALUE=$(oc get configmap config -n openshift-kube-apiserver -ojson \ | jq -r '.data["config.yaml"]' \ | jq -r '.apiServerArguments["audit-log-maxbackup"][0] // empty') if [ -n "$VALUE" ] && [ "$VALUE" -ge 10 ]; then echo "pass (current=$VALUE)" else echo "fail (current=$VALUE)" fi tests: test_items: - flag: "pass" remediation: | No remediation required. By default, OpenShift retains 10 audit log backup files. This provides sufficient log history for incident investigation and audit review. scored: true - id: 1.2.21 text: "Configure Kubernetes API Server Maximum Audit Log Size (Manual)" audit: | VALUE=$(oc get configmap config -n openshift-kube-apiserver -ojson \ | jq -r '.data["config.yaml"]' \ | jq -r '.apiServerArguments["audit-log-maxsize"][0] // empty') if [ -n "$VALUE" ] && [ "$VALUE" -ge 100 ]; then echo "pass (current=$VALUE)" else echo "fail (current=$VALUE)" fi tests: test_items: - flag: "pass" remediation: | Set the audit-log-maxsize parameter to 100 or as an appropriate number. maximumFileSizeMegabytes: 100 scored: true - id: 1.2.22 text: "Ensure that the --request-timeout argument is set (Manual)" audit: | VALUE=$(oc get configmap config -n openshift-kube-apiserver -ojson \ | jq -r '.data["config.yaml"]' \ | jq -r '.apiServerArguments["min-request-timeout"][0] // empty') if [ -n "$VALUE" ] && [ "$VALUE" -eq 3600 ]; then echo "pass (current=$VALUE)" else echo "fail (current=$VALUE)" fi tests: test_items: - flag: "pass" remediation: | TBD scored: true - id: 1.2.23 text: "Ensure that the --service-account-lookup argument is set to true (Manual)" audit: | oc get configmap config -n openshift-kube-apiserver -ojson | jq -r '.data["config.yaml"]' | jq '.apiServerArguments."service-account-lookup"[]' tests: test_items: - flag: "true" remediation: | TBD scored: true - id: 1.2.24 text: "Ensure that the --service-account-key-file argument is set as appropriate (Manual)" audit: | oc get configmap config -n openshift-kube-apiserver -ojson | jq -r '.data["config.yaml"]' | jq -r .serviceAccountPublicKeyFiles[] tests: bin_op: and test_items: - flag: "/etc/kubernetes/static-pod-resources/configmaps/sa-token-signing-certs" - flag: "/etc/kubernetes/static-pod-resources/configmaps/bound-sa-token-signing-certs" remediation: | The OpenShift API server does not use the service-account-key-file argument. The ServiceAccount token authenticator is configured with serviceAccountConfig.publicKeyFiles. OpenShift does not reuse the apiserver TLS key. This is not configurable. scored: true - id: 1.2.25 text: "Ensure that the --etcd-certfile and --etcd-keyfile arguments are set as appropriate (Manual)" audit: | oc get configmap config -n openshift-kube-apiserver -ojson \ | jq -r '.data["config.yaml"]' \ | jq -r '.apiServerArguments["etcd-certfile"][]?' oc get configmap config -n openshift-kube-apiserver -ojson \ | jq -r '.data["config.yaml"]' \ | jq -r '.apiServerArguments["etcd-keyfile"][]?' tests: bin_op: and test_items: - flag: "/etc/kubernetes/static-pod-resources/secrets/etcd-client/tls.crt" - flag: "/etc/kubernetes/static-pod-resources/secrets/etcd-client/tls.key" remediation: | No remediation is required. OpenShift automatically manages X.509 client certificates and TLS encryption for secure communication with etcd. These settings are handled by the platform and should not be manually modified. scored: true - id: 1.2.26 text: "Ensure that the --tls-cert-file and --tls-private-key-file arguments are set as appropriate (Manual)" audit: | oc get configmap config -n openshift-kube-apiserver -ojson \ | jq -r '.data["config.yaml"]' \ | jq -r '.apiServerArguments["tls-cert-file"][]?' oc get configmap config -n openshift-kube-apiserver -ojson \ | jq -r '.data["config.yaml"]' \ | jq -r '.apiServerArguments["tls-private-key-file"][]?' tests: bin_op: and test_items: - flag: "/etc/kubernetes/static-pod-certs/secrets/service-network-serving-certkey/tls.crt" - flag: "/etc/kubernetes/static-pod-certs/secrets/service-network-serving-certkey/tls.key" remediation: | No remediation is required. OpenShift automatically configures the API server with valid X.509 certificates and TLS keys. These are used to encrypt traffic between the API server and clients, including kubelets and users. Certificate rotation and lifecycle management are handled by the OpenShift platform. scored: true - id: 1.2.27 text: "Ensure that the --client-ca-file argument is set as appropriate (Manual)" audit: | oc get configmap config -n openshift-kube-apiserver -ojson | \ jq -r '.data["config.yaml"]' | \ jq -r .servingInfo.clientCA tests: test_items: - flag: "/etc/kubernetes/static-pod-certs/configmaps/client-ca/ca-bundle.crt" remediation: | OpenShift automatically manages TLS authentication for the API server communication with the node/kublet. This is not configurable. You may optionally set a custom default certificate to be used by the API server when serving content in order to enable clients to access the API server at a different host name or without the need to distribute the cluster-managed certificate authority (CA) certificates to the clients. User-provided certificates must be provided in a kubernetes.io/tls type Secret in the openshift-config namespace. Update the API server cluster configuration, the apiserver/cluster resource, to enable the use of the user-provided certificate. scored: true - id: 1.2.28 text: "Ensure that the --etcd-cafile argument is set as appropriate (Manual)" audit: | oc get configmap config -n openshift-kube-apiserver -ojson | \ jq -r '.data["config.yaml"]' | \ jq -r '.apiServerArguments["etcd-cafile"]' tests: test_items: - flag: "/etc/kubernetes/static-pod-resources/configmaps/etcd-serving-ca/ca-bundle.crt" remediation: | None required. OpenShift generates the etcd-cafile and sets the arguments appropriately in the API server. Communication with etcd is secured by the etcd serving CA. scored: true - id: 1.2.29 text: "Ensure that encryption providers are appropriately configured (Manual)" audit: | oc get openshiftapiserver -o=jsonpath='{range .items[0].status.conditions[?(@.type=="Encrypted")]}{.reason}{"\n"}{.message}{"\n"}' tests: test_items: - flag: "EncryptionCompleted" remediation: | Follow the Kubernetes documentation and configure a EncryptionConfig file. In this file, choose aescbc, kms or secretbox as the encryption provider. scored: true - id: 1.2.30 text: "Ensure that the API Server only makes use of Strong Cryptographic Ciphers (Manual)" type: manual audit: | oc get cm -n openshift-authentication v4-0-config-system-cliconfig -o jsonpath='{.data.v4\-0\-config\-system\-cliconfig}' | jq .servingInfo oc get kubeapiservers.operator.openshift.io cluster -o json |jq.spec.observedConfig.servingInfo oc get openshiftapiservers.operator.openshift.io cluster -o json |jq.spec.observedConfig.servingInfo oc describe --namespace=openshift-ingress-operator ingresscontroller/default remediation: | Verify that the tlsSecurityProfile is set to the value you chose. Note: The HAProxy Ingress controller image does not support TLS 1.3 and because the Modern profile requires TLS 1.3, it is not supported. The Ingress Operator converts the Modern profile to Intermediate. The Ingress Operator also converts the TLS 1.0 of an Old or Custom profile to 1.1, and TLS 1.3 of a Custom profile to 1.2. scored: false - id: 1.2.31 text: "Ensure unsupported configuration overrides are not used (Manual)" audit: | oc get kubeapiserver/cluster -o jsonpath='{.spec.unsupportedConfigOverrides}' tests: test_items: - flag: "null" remediation: | No remediation is required. OpenShift has deprecated and disabled unsupportedConfigOverrides. This field should remain null and must not be used in any supported configuration. scored: true - id: 1.3 text: "Controller Manager" checks: - id: 1.3.1 text: "Ensure that controller manager healthz endpoints are protected by RBAC (Manual)" type: manual audit: | # Verify configuration for ports, livenessProbe, readinessProbe, healthz oc -n openshift-kube-controller-manager get cm kube-controller-manager-pod -o json | jq -r '.data."pod.yaml"' | jq '.spec.containers' # Verify endpoints oc -n openshift-kube-controller-manager describe endpoints # Test to validate RBAC enabled on the controller endpoint; check with non-admin role oc project openshift-kube-controller-manage POD=$(oc get pods -n openshift-kube-controller-manager -l app=kube-controller-manager -o jsonpath='{.items[0].metadata.name}') PORT=$(oc get pods -n openshift-kube-controller-manager -l app=kube-controller-manager -o jsonpath='{.items[0].spec.containers[0].ports[0].hostPort}') # Following should return 403 Forbidden oc rsh -n openshift-kube-controller-manager ${POD} curl https://localhost:${PORT}/metrics -k # Create a service account to test RBAC oc create -n openshift-kube-controller-manager sa permission-test-sa # Should return 403 Forbidden SA_TOKEN=$(oc sa -n openshift-kube-controller-manager get-token permission-test-sa) oc rsh -n openshift-kube-controller-manager ${POD} curl https://localhost:${PORT}/metrics -H "Authorization: Bearer $SA_TOKEN" -k # Cleanup oc delete -n openshift-kube-controller-manager sa permission-test-sa # As cluster admin, should succeed CLUSTER_ADMIN_TOKEN=$(oc whoami -t) oc rsh -n openshift-kube-controller-manager ${POD} curl https://localhost:${PORT}/metrics -H "Authorization: Bearer $CLUSTER_ADMIN_TOKEN" -k remediation: | None required; profiling is protected by RBAC. scored: false - id: 1.3.2 text: "Ensure that the --use-service-account-credentials argument is set to true (Manual)" audit: | oc get configmaps config -n openshift-kube-controller-manager -ojson | \ jq -r '.data["config.yaml"]' | \ jq -r '.extendedArguments["use-service-account-credentials"][]' tests: test_items: - flag: "true" remediation: | The OpenShift Controller Manager operator manages and updates the OpenShift Controller Manager. The Kubernetes Controller Manager operator manages and updates the Kubernetes Controller Manager deployed on top of OpenShift. This operator is configured via KubeControllerManager custom resource. scored: true - id: 1.3.3 text: "Ensure that the --service-account-private-key-file argument is set as appropriate (Manual)" audit: | oc get configmaps config -n openshift-kube-controller-manager -ojson | \ jq -r '.data["config.yaml"]' | \ jq -r '.extendedArguments["service-account-private-key-file"][]' tests: test_items: - flag: "/etc/kubernetes/static-pod-resources/secrets/service-account-private-key/service-account.key" remediation: | None required. OpenShift manages the service account credentials for the scheduler automatically. scored: true - id: 1.3.4 text: "Ensure that the --root-ca-file argument is set as appropriate (Manual)" audit: | oc get configmaps config -n openshift-kube-controller-manager -ojson | \ jq -r '.data["config.yaml"]' | \ jq -r '.extendedArguments["root-ca-file"][]' tests: test_items: - flag: "/etc/kubernetes/static-pod-resources/configmaps/serviceaccount-ca/ca-bundle.crt" remediation: | None required. Certificates for OpenShift platform components are automatically created and rotated by the OpenShift Container Platform. scored: true - id: 1.4 text: "Scheduler" checks: - id: 1.4.1 text: "Ensure that the healthz endpoints for the scheduler are protected by RBAC (Manual)" type: manual audit: | # check configuration for ports, livenessProbe, readinessProbe, healthz oc -n openshift-kube-scheduler get cm kube-scheduler-pod -o json | jq -r '.data."pod.yaml"' | jq '.spec.containers' # Test to verify endpoints oc -n openshift-kube-scheduler describe endpoints # Test to validate RBAC enabled on the scheduler endpoint; check with non-admin role oc project openshift-kube-scheduler POD=$(oc get pods -l app=openshift-kube-scheduler -o jsonpath='{.items[0].metadata.name}') PORT=$(oc get pod $POD -o jsonpath='{.spec.containers[0].livenessProbe.httpGet.port}') # Should return 403 Forbidden oc rsh ${POD} curl http://localhost:${PORT}/metrics -k # Create a service account to test RBAC oc create sa permission-test-sa # Should return 403 Forbidden SA_TOKEN=$(oc sa get-token permission-test-sa) oc rsh ${POD} curl http://localhost:${PORT}/metrics -H "Authorization: Bearer $SA_TOKEN" -k # Cleanup oc delete sa permission-test-sa # As cluster admin, should succeed CLUSTER_ADMIN_TOKEN=$(oc whoami -t) oc rsh ${POD} curl http://localhost:${PORT}/metrics -H "Authorization: Bearer $CLUSTER_ADMIN_TOKEN" -k remediation: | A fix to this issue: https://bugzilla.redhat.com/show_bug.cgi?id=1889488 None required. Profiling is protected by RBAC and cannot be disabled. scored: false - id: 1.4.2 text: "Verify that the scheduler API service is protected by RBAC (Manual)" type: manual audit: | echo "Describing kube-scheduler endpoints..." oc -n openshift-kube-scheduler describe endpoints echo "Checking pod configuration for kube-scheduler to confirm no --bind-address or insecure arguments..." oc -n openshift-kube-scheduler get cm kube-scheduler-pod -o json \ | jq -r '.data["pod.yaml"]' \ | jq '.spec.containers[] | select(.name=="kube-scheduler") | .args' echo "Testing access to metrics endpoint as unauthenticated user..." oc project openshift-kube-scheduler export POD=$(oc get pods -l app=openshift-kube-scheduler -o jsonpath='{.items[0].metadata.name}') export POD_IP=$(oc get pods -l app=openshift-kube-scheduler -o jsonpath='{.items[0].status.podIP}') export PORT=$(oc get pod $POD -o jsonpath='{.spec.containers[0].livenessProbe.httpGet.port}') oc rsh $POD curl -k -s -o /dev/null -w "%{http_code}" https://$POD_IP:$PORT/metrics echo "Testing access with unprivileged service account..." oc create sa permission-test-sa export SA_TOKEN=$(oc create token permission-test-sa) oc rsh $POD curl -k -s -o /dev/null -w "%{http_code}" https://$POD_IP:$PORT/metrics -H "Authorization: Bearer $SA_TOKEN" echo "Testing access with cluster-admin..." export CLUSTER_ADMIN_TOKEN=$(oc whoami -t) oc rsh $POD curl -k -s -o /dev/null -w "%{http_code}" https://$POD_IP:$PORT/metrics -H "Authorization: Bearer $CLUSTER_ADMIN_TOKEN" # Cleanup unset CLUSTER_ADMIN_TOKEN POD PORT SA_TOKEN POD_IP oc delete sa permission-test-sa remediation: | By default, the --bind-address argument is not present, the readinessProbe and livenessProbe arguments are set to 10251 and the port argument is set to 0. Check the status of this issue: https://bugzilla.redhat.com/show_bug.cgi?id=1889488 scored: false ================================================ FILE: cfg/rh-1.8/node.yaml ================================================ --- controls: version: rh-1.8 id: 4 text: "Worker Nodes" type: "node" groups: - id: 4.1 text: "Worker Node Configuration Files" checks: - id: 4.1.1 text: "Ensure that the kubelet service file permissions are set to 644 or more restrictive (Automated)" audit: | NODE_NAME=$(oc get pod $HOSTNAME -o=jsonpath='{.spec.nodeName}') oc debug node/$NODE_NAME -- chroot /host stat -c "$NODE_NAME %n permissions=%a" /etc/systemd/system/kubelet.service 2> /dev/null tests: test_items: - flag: "permissions" compare: op: bitmask value: "644" remediation: | By default, the kubelet service file has permissions of 644. scored: true - id: 4.1.2 text: "Ensure that the kubelet service file ownership is set to root:root (Automated)" audit: | # Should return root:root for each node NODE_NAME=$(oc get pod $HOSTNAME -o=jsonpath='{.spec.nodeName}') oc debug node/$NODE_NAME -- chroot /host stat -c "$NODE_NAME %n %U:%G" /etc/systemd/system/kubelet.service 2> /dev/null tests: test_items: - flag: root:root remediation: | By default, the kubelet service file has ownership of root:root. scored: true - id: 4.1.3 text: "If proxy kube proxy configuration file exists ensure permissions are set to 644 or more restrictive (Manual)" audit: | # Get the node name where the pod is running NODE_NAME=$(oc get pod "$HOSTNAME" -o=jsonpath='{.spec.nodeName}') # Get the pod name in the openshift-sdn namespace POD_NAME=$(oc get pods -n openshift-sdn -l app=sdn --field-selector spec.nodeName="$NODE_NAME" -o jsonpath='{.items[0].metadata.name}' 2>/dev/null) if [ -z "$POD_NAME" ]; then echo "No matching pods found on the current node." else # Execute the stat command oc exec -n openshift-sdn "$POD_NAME" -- stat -Lc "$i %n permissions=%a" /config/kube-proxy-config.yaml 2>/dev/null fi tests: test_items: - flag: "permissions" set: true compare: op: bitmask value: "644" remediation: | None needed. scored: true - id: 4.1.4 text: "If proxy kubeconfig file exists ensure ownership is set to root:root (Manual)" audit: | # Get the node name where the pod is running NODE_NAME=$(oc get pod "$HOSTNAME" -o=jsonpath='{.spec.nodeName}') # Get the pod name in the openshift-sdn namespace POD_NAME=$(oc get pods -n openshift-sdn -l app=sdn --field-selector spec.nodeName="$NODE_NAME" -o jsonpath='{.items[0].metadata.name}' 2>/dev/null) if [ -z "$POD_NAME" ]; then echo "No matching pods found on the current node." else # Execute the stat command oc exec -n openshift-sdn "$POD_NAME" -- stat -Lc "$i %n %U:%G" /config/kube-proxy-config.yaml 2>/dev/null fi use_multiple_values: true tests: test_items: - flag: root:root remediation: | None required. The configuration is managed by OpenShift operators. scored: true - id: 4.1.5 text: "Ensure that the --kubeconfig kubelet.conf file permissions are set to 644 or more restrictive (Manual)" audit: | # Check permissions NODE_NAME=$(oc get pod $HOSTNAME -o=jsonpath='{.spec.nodeName}') oc debug node/$NODE_NAME -- chroot /host stat -c "$NODE_NAME %n permissions=%a" /etc/kubernetes/kubelet.conf 2> /dev/null use_multiple_values: true tests: test_items: - flag: "permissions" compare: op: bitmask value: "644" remediation: | None required. scored: true - id: 4.1.6 text: "Ensure that the --kubeconfig kubelet.conf file ownership is set to root:root (Manual)" audit: | NODE_NAME=$(oc get pod $HOSTNAME -o=jsonpath='{.spec.nodeName}') oc debug node/$NODE_NAME -- chroot /host stat -c "$NODE_NAME %n %U:%G" /etc/kubernetes/kubelet.conf 2> /dev/null use_multiple_values: true tests: test_items: - flag: root:root remediation: | None required. scored: true - id: 4.1.7 text: "Ensure that the certificate authorities file permissions are set to 644 or more restrictive" audit: | NODE_NAME=$(oc get pod $HOSTNAME -o=jsonpath='{.spec.nodeName}') oc get --raw /api/v1/nodes/$NODE_NAME/proxy/configz | jq '.kubeletconfig.authentication.x509.clientCAFile' oc debug node/$NODE_NAME -- chroot /host stat -c "$NODE_NAME permissions=%a" /etc/kubernetes/kubelet-ca.crt 2> /dev/null tests: test_items: - flag: "/etc/kubernetes/kubelet-ca.crt" - flag: "permissions" compare: op: bitmask value: "644" remediation: | No remediation required. OpenShift sets /etc/kubernetes/kubelet-ca.crt to 644 by default. If permissions are more permissive than 644, update with: chmod 644 /etc/kubernetes/kubelet-ca.crt scored: true - id: 4.1.8 text: "Ensure that the client certificate authorities file ownership is set to root:root (Automated)" audit: | NODE_NAME=$(oc get pod $HOSTNAME -o=jsonpath='{.spec.nodeName}') oc debug node/$NODE_NAME -- chroot /host stat -c "$NODE_NAME %n %U:%G" /etc/kubernetes/kubelet-ca.crt 2> /dev/null use_multiple_values: true tests: test_items: - flag: root:root remediation: | None required. scored: true - id: 4.1.9 text: "Ensure that the kubelet --config configuration file has permissions set to 600 or more restrictive (Automated)" audit: | NODE_NAME=$(oc get pod $HOSTNAME -o=jsonpath='{.spec.nodeName}') oc debug node/$NODE_NAME -- chroot /host stat -c "$NODE_NAME %n permissions=%a" /var/data/kubelet/config.json 2> /dev/null use_multiple_values: true tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | None required. scored: true - id: 4.1.10 text: "Ensure that the kubelet configuration file ownership is set to root:root (Automated)" audit: | NODE_NAME=$(oc get pod $HOSTNAME -o=jsonpath='{.spec.nodeName}') oc debug node/$NODE_NAME -- chroot /host stat -c "$NODE_NAME %n %U:%G" /var/data/kubelet/config.json 2> /dev/null use_multiple_values: true tests: test_items: - flag: root:root remediation: | None required. scored: true - id: 4.2 text: "Kubelet" checks: - id: 4.2.1 text: "Activate Garbage collection in OpenShift Container Platform 4, as appropriate (Manual)" audit: | echo "Retrieving and inspecting garbage collection configuration from node-local kubelet configz..." NODE_NAME=$(oc get pod $HOSTNAME -o=jsonpath='{.spec.nodeName}') oc get --raw /api/v1/nodes/$NODE_NAME/proxy/configz | jq '.kubeletconfig' tests: test_items: - flag: "evictionHard" - flag: "imageGCHighThresholdPercent" - flag: "imageGCLowThresholdPercent" - flag: "imageMinimumGCAge" remediation: | OpenShift manages node garbage collection through KubeletConfig custom resources per MachineConfigPool. To configure or adjust garbage collection thresholds, follow the documentation: https://docs.openshift.com/container-platform/latest/nodes/nodes/nodes-nodes-garbage-collection.html Example: Create or modify a KubeletConfig object to include: --- evictionHard: "memory.available": "200Mi" "nodefs.available": "10%" "imagefs.available": "15%" imageGCHighThresholdPercent: 85 imageGCLowThresholdPercent: 80 imageMinimumGCAge: "2m0s" Then apply the `KubeletConfig` to the appropriate `MachineConfigPool`. scored: true - id: 4.2.2 text: "Ensure that the --anonymous-auth argument is set to false (Automated)" audit: | echo "Checking if anonymous-auth is disabled in kubelet configuration on the current node..." NODE_NAME=$(oc get pod $HOSTNAME -o=jsonpath='{.spec.nodeName}') oc get --raw /api/v1/nodes/$NODE_NAME/proxy/configz | jq '.kubeletconfig.authentication.anonymous.enabled' tests: test_items: - flag: "false" remediation: | By default, OpenShift sets anonymous-auth to false in Kubelet configuration. If this value is found to be true, create or patch a KubeletConfig object with: --- kind: KubeletConfig apiVersion: machineconfiguration.openshift.io/v1 metadata: name: disable-anonymous-auth spec: kubeletConfig: authentication: anonymous: enabled: false Then apply this KubeletConfig to the appropriate MachineConfigPool. See OpenShift documentation on configuring node-level security settings. scored: true - id: 4.2.3 text: "Ensure that the --authorization-mode argument is not set to AlwaysAllow (Automated)" audit: | echo "Checking kubelet authorization mode on the current node..." NODE_NAME=$(oc get pod $HOSTNAME -o=jsonpath='{.spec.nodeName}') oc get --raw /api/v1/nodes/$NODE_NAME/proxy/configz | jq '.kubeletconfig.authorization.mode' tests: test_items: - flag: AlwaysAllow set: false remediation: | No remediation required. By default, OpenShift uses secure authorization modes such as 'Webhook' and does not allow AlwaysAllow. If AlwaysAllow is found, the node must be reconfigured using a KubeletConfig applied through the appropriate MachineConfigPool. scored: true - id: 4.2.4 text: "Ensure that the --client-ca-file argument is set as appropriate (Automated)" audit: | echo "Checking Kubelet 'clientCAFile' setting on current node..." NODE_NAME=$(oc get pod $HOSTNAME -o=jsonpath='{.spec.nodeName}') oc get --raw /api/v1/nodes/$NODE_NAME/proxy/configz \ | jq '.kubeletconfig.authentication.x509.clientCAFile' tests: test_items: - flag: "/etc/kubernetes/kubelet-ca.crt" remediation: | No remediation required. OpenShift sets the clientCAFile by default to /etc/kubernetes/kubelet-ca.crt. Manual modification is unsupported and unnecessary as OpenShift manages Kubelet certificate authentication via the Machine Config Operator. scored: true - id: 4.2.5 text: "Verify that the read only port is not used or is set to 0 (Automated)" audit: | echo "Checking 'kubelet-read-only-port' argument in openshift-kube-apiserver config..." oc -n openshift-kube-apiserver get configmap config -o json \ | jq -r '.data["config.yaml"]' \ | yq '.apiServerArguments."kubelet-read-only-port"[0]' tests: test_items: - flag: "0" remediation: | No remediation is required if the read-only port is set to 0. If this value is not set to 0 (or the argument is missing), create a KubeletConfig object and apply it to the appropriate MachineConfigPool to disable the read-only port. Example KubeletConfig: --- apiVersion: machineconfiguration.openshift.io/v1 kind: KubeletConfig metadata: name: disable-readonly-port spec: kubeletConfig: readOnlyPort: 0 scored: true - id: 4.2.6 text: "Ensure that the --streaming-connection-idle-timeout argument is not set to 0 (automated)" audit: | NODE_NAME=$(oc get pod $HOSTNAME -o=jsonpath='{.spec.nodeName}') oc get --raw /api/v1/nodes/$NODE_NAME/proxy/configz \ | jq '.kubeletconfig' tests: test_items: - path: ".streamingConnectionIdleTimeout" compare: op: noteq value: "0s" remediation: | By default, OpenShift sets streamingConnectionIdleTimeout to 4h0m0s. If it is manually set to "0s", this disables timeouts — which is insecure. To remediate, create a `KubeletConfig` CR with a safer timeout (e.g., 1h0m0s): --- apiVersion: machineconfiguration.openshift.io/v1 kind: KubeletConfig metadata: name: set-streaming-timeout spec: kubeletConfig: streamingConnectionIdleTimeout: "1h0m0s" scored: true - id: 4.2.7 text: "Ensure that the --make-iptables-util-chains argument is set to true (manual)" audit: | echo "Checking 'makeIPTablesUtilChains' setting in Kubelet config on current node..." NODE_NAME=$(oc get pod $HOSTNAME -o=jsonpath='{.spec.nodeName}') oc get --raw /api/v1/nodes/$NODE_NAME/proxy/configz \ | jq '.kubeletconfig' tests: test_items: - path: ".makeIPTablesUtilChains" compare: op: eq value: true remediation: | No remediation is required. By default, OpenShift sets makeIPTablesUtilChains to true. This allows Kubelet to manage iptables rules and keep them in sync with the dynamic pod network configuration. scored: true - id: 4.2.8 text: "Ensure that the kubeAPIQPS [--event-qps] argument is set to 0 or a level which ensures appropriate event capture (manual)" audit: | echo "Checking 'kubeAPIQPS' setting in Kubelet config on current node..." NODE_NAME=$(oc get pod $HOSTNAME -o=jsonpath='{.spec.nodeName}') oc get --raw /api/v1/nodes/$NODE_NAME/proxy/configz \ | jq '.kubeletconfig' tests: test_items: - path: ".kubeAPIQPS" compare: op: gte value: 1 remediation: | OpenShift sets kubeAPIQPS to a default of 50, which is appropriate in most environments. If kubeAPIQPS is set to 0, event rate limiting is disabled, which can overwhelm the kubelet with excessive events. To configure a proper limit, create or modify a `KubeletConfig` resource with an appropriate value: --- apiVersion: machineconfiguration.openshift.io/v1 kind: KubeletConfig metadata: name: set-kubeapiqps spec: kubeletConfig: kubeAPIQPS: 50 scored: true - id: 4.2.9 text: "Ensure that the --tls-cert-file and --tls-private-key-file arguments are set as appropriate (manual)" audit: | oc get configmap config -n openshift-kube-apiserver -ojson \ | jq -r '.data["config.yaml"]' \ | jq -r '.apiServerArguments["kubelet-client-certificate"][]?' oc get configmap config -n openshift-kube-apiserver -ojson \ | jq -r '.data["config.yaml"]' \ | jq -r '.apiServerArguments["kubelet-client-key"][]?' tests: bin_op: and test_items: - flag: "/etc/kubernetes/static-pod-certs/secrets/kubelet-client/tls.crt" - flag: "/etc/kubernetes/static-pod-certs/secrets/kubelet-client/tls.key" remediation: | No remediation is required. OpenShift manages secure TLS connections to kubelets by default using its internal certificate authority. These X.509 certificates are rotated and validated automatically by the platform. Manual modifications to the TLS paths or keys are not supported and can lead to cluster issues. scored: true - id: 4.2.10 text: "Ensure that the --rotate-certificates argument is not set to false (manual)" audit: | NODE_NAME=$(oc get pod $HOSTNAME -o=jsonpath='{.spec.nodeName}') oc get --raw /api/v1/nodes/$NODE_NAME/proxy/configz \ | jq '.kubeletconfig' tests: test_items: - path: ".rotateCertificates" compare: op: eq value: true remediation: | No remediation required. By default, OpenShift enables certificate rotation via rotateCertificates=true. If disabled, you must either enable rotation via KubeletConfig or implement external certificate renewal. Example remediation using KubeletConfig: --- apiVersion: machineconfiguration.openshift.io/v1 kind: KubeletConfig metadata: name: enable-cert-rotation spec: kubeletConfig: rotateCertificates: true scored: true - id: 4.2.11 text: "Verify that the RotateKubeletServerCertificate argument is set to true (manual)" audit: | echo "Checking that RotateKubeletServerCertificate is enabled in kubelet config on current node..." NODE_NAME=$(oc get pod $HOSTNAME -o=jsonpath='{.spec.nodeName}') echo "Verifying feature gate: RotateKubeletServerCertificate" oc get --raw /api/v1/nodes/$NODE_NAME/proxy/configz \ | jq '.kubeletconfig.featureGates.RotateKubeletServerCertificate' echo "Verifying that certificate rotation is enabled" oc get --raw /api/v1/nodes/$NODE_NAME/proxy/configz \ | jq '.kubeletconfig.rotateCertificates' tests: bin_op: and test_items: - flag: "RotateKubeletServerCertificate" compare: op: eq value: true - flag: "rotateCertificates" compare: op: eq value: true remediation: | No remediation is required. OpenShift enables RotateKubeletServerCertificate by default and manages certificate rotation automatically. If the feature gate or rotation setting is disabled, configure a `KubeletConfig` CR and apply it to the MachineConfigPool: --- apiVersion: machineconfiguration.openshift.io/v1 kind: KubeletConfig metadata: name: enable-server-cert-rotation spec: kubeletConfig: rotateCertificates: true featureGates: RotateKubeletServerCertificate: true scored: true - id: 4.2.13 text: "Ensure that the Kubelet only makes use of Strong Cryptographic Ciphers (Manual)" audit: | # needs verification # verify cipher suites oc describe --namespace=openshift-ingress-operator ingresscontroller/default oc get kubeapiservers.operator.openshift.io cluster -o json |jq .spec.observedConfig.servingInfo oc get openshiftapiservers.operator.openshift.io cluster -o json |jq .spec.observedConfig.servingInfo oc get cm -n openshift-authentication v4-0-config-system-cliconfig -o jsonpath='{.data.v4\-0\-config\-system\-cliconfig}' | jq .servingInfo #check value for tlsSecurityProfile; null is returned if default is used oc get kubeapiservers.operator.openshift.io cluster -o json |jq .spec.tlsSecurityProfile type: manual remediation: | Follow the directions above and in the OpenShift documentation to configure the tlsSecurityProfile. Configuring Ingress scored: false ================================================ FILE: cfg/rh-1.8/policies.yaml ================================================ --- controls: version: rh-1.8 id: 5 text: "Policies" type: "policies" groups: - id: 5.1 text: "RBAC and Service Accounts" checks: - id: 5.1.1 text: "Ensure that the cluster-admin role is only used where required (Manual)" type: "manual" audit: | #To get a list of users and service accounts with the cluster-admin role oc get clusterrolebindings -o=customcolumns=NAME:.metadata.name,ROLE:.roleRef.name,SUBJECT:.subjects[*].kind | grep cluster-admin #To verity that kbueadmin is removed, no results should be returned oc get secrets kubeadmin -n kube-system remediation: | Identify all clusterrolebindings to the cluster-admin role. Check if they are used and if they need this role or if they could use a role with fewer privileges. Where possible, first bind users to a lower privileged role and then remove the clusterrolebinding to the cluster-admin role : oc delete clusterrolebinding [name] scored: false - id: 5.1.2 text: "Minimize access to secrets (Manual)" type: "manual" remediation: | Where possible, remove get, list and watch access to secret objects in the cluster. scored: false - id: 5.1.3 text: "Minimize wildcard use in Roles and ClusterRoles (Manual)" type: "manual" remediation: | Where possible replace any use of wildcards in clusterroles and roles with specific objects or actions. scored: false - id: 5.1.4 text: "Minimize access to create pods (Manual)" type: "manual" remediation: | Where possible, remove create access to pod objects in the cluster. scored: false - id: 5.1.5 text: "Ensure that default service accounts are not actively used. (Manual)" type: "manual" remediation: | None required. scored: false - id: 5.1.6 text: "Ensure that Service Account Tokens are only mounted where necessary (Manual)" type: "manual" remediation: | Modify the definition of pods and service accounts which do not need to mount service account tokens to disable it. scored: false - id: 5.2 text: "Security Context Constraints (SCCs)" checks: - id: 5.2.1 text: "Minimize the admission of privileged containers (Manual)" audit: | oc get scc -o json \ | jq -r '[.items[] | select(.allowPrivilegedContainer==false) | .metadata.name] | length | if . > 0 then "pass" else "fail" end' tests: test_items: - flag: "pass" remediation: | If no SCCs exist that restrict privileged containers, create one by running: oc create -f - < 0 then "pass" else "fail" end' tests: test_items: - flag: "pass" remediation: | If SCCs with `allowHostPID: true` exist, ensure they are restricted to trusted service accounts only. To create a restrictive SCC that prevents host PID sharing: --- apiVersion: security.openshift.io/v1 kind: SecurityContextConstraints metadata: name: restricted-no-hostpid allowHostPID: false runAsUser: type: MustRunAsRange seLinuxContext: type: MustRunAs users: [] groups: - system:authenticated --- Apply the SCC and bind it only to users or groups that do **not** need hostPID access. scored: true - id: 5.2.3 text: "Minimize the admission of containers wishing to share the host IPC namespace (Manual)" audit: | oc get scc -o json \ | jq -r '[.items[] | select(.allowHostIPC==false) | .metadata.name] | length | if . > 0 then "pass" else "fail" end' tests: test_items: - flag: "pass" remediation: | If no SCCs restrict hostIPC usage, create one that explicitly sets allowHostIPC: false: --- apiVersion: security.openshift.io/v1 kind: SecurityContextConstraints metadata: name: restricted-no-hostipc allowHostIPC: false runAsUser: type: MustRunAsRange seLinuxContext: type: MustRunAs users: [] groups: - system:authenticated --- Then assign this SCC to general workloads and ensure any SCCs allowing hostIPC are tightly scoped via RBAC. scored: true - id: 5.2.4 text: "Minimize the admission of containers wishing to share the host network namespace (manual)" audit: | oc get scc -o json \ | jq -r '[.items[] | select(.allowHostNetwork==false) | .metadata.name] | length | if . > 0 then "pass" else "fail" end' tests: test_items: - flag: "pass" remediation: | If no SCCs restrict host networking, create one by running: --- apiVersion: security.openshift.io/v1 kind: SecurityContextConstraints metadata: name: restricted-no-hostnetwork allowHostNetwork: false runAsUser: type: MustRunAsRange seLinuxContext: type: MustRunAs users: [] groups: - system:authenticated --- Ensure only workloads that require `hostNetwork: true` (e.g., CNI, infra pods) are allowed to use SCCs where it is explicitly enabled. Restrict access to such SCCs using RBAC. scored: true - id: 5.2.5 text: "Minimize the admission of containers with allowPrivilegeEscalation (manual)" audit: | oc get scc -o json \ | jq -r '[.items[] | select(.allowPrivilegeEscalation==false) | .metadata.name] | length | if . > 0 then "pass" else "fail" end' tests: test_items: - flag: "pass" remediation: | If no SCCs exist that restrict the use of privilege escalation, create a custom SCC: --- apiVersion: security.openshift.io/v1 kind: SecurityContextConstraints metadata: name: restricted-no-priv-escalation allowPrivilegeEscalation: false runAsUser: type: MustRunAsRange seLinuxContext: type: MustRunAs users: [] groups: - system:authenticated --- Assign this SCC only to workloads and users that **do not require** the ability to escalate privileges. Use RBAC to restrict access to SCCs where `allowPrivilegeEscalation` is `true` to only trusted service accounts or admin roles. scored: true - id: 5.2.6 text: "Minimize the admission of root containers (manual)" audit: | sccs=$(oc get scc -o json | jq -r '.items[] | select(.runAsUser.type == "MustRunAsNonRoot") | .metadata.name') if [[ -n "$sccs" ]]; then echo "pass" else echo "fail" fi tests: test_items: - flag: "pass" remediation: | If no SCC is found with `runAsUser.type: MustRunAsNonRoot`, create one as follows: --- apiVersion: security.openshift.io/v1 kind: SecurityContextConstraints metadata: name: restricted-nonroot allowPrivilegeEscalation: false runAsUser: type: MustRunAsNonRoot seLinuxContext: type: MustRunAs users: [] groups: - system:authenticated --- Assign this SCC only to workloads that must not run as root. If an SCC allows `RunAsAny`, audit and restrict access using RBAC to prevent misuse. scored: true - id: 5.2.7 text: "Minimize the admission of containers with the NET_RAW capability (manual)" audit: | oc get scc -o json \ | jq -r '[.items[] | select((.requiredDropCapabilities // []) | index("ALL")) | .metadata.name] | length | if . > 0 then "pass" else "fail" end' tests: test_items: - flag: "pass" remediation: | If no SCCs drop ALL capabilities, create a custom SCC that explicitly drops NET_RAW: --- apiVersion: security.openshift.io/v1 kind: SecurityContextConstraints metadata: name: restricted-no-netraw requiredDropCapabilities: - NET_RAW allowPrivilegedContainer: false runAsUser: type: MustRunAsRange seLinuxContext: type: MustRunAs users: [] groups: - system:authenticated --- Apply this SCC to workloads that do not require NET_RAW. If NET_RAW is required (e.g., for low-level networking apps), isolate those workloads with a specific SCC and restrict access via RBAC. scored: true - id: 5.2.8 text: "Minimize the admission of containers with added capabilities (manual)" audit: | oc get scc -o json \ | jq -r '[.items[] | select(.allowedCapabilities == null) | .metadata.name] | length | if . > 0 then "pass" else "fail" end' oc get scc -o json \ | jq -r '[.items[] | select(.defaultAddCapabilities == null) | .metadata.name] | length | if . > 0 then "true" else "false" end' tests: test_items: - flag: "pass" - flag: "true" remediation: | If no SCCs restrict added capabilities, create a custom SCC as shown below: --- apiVersion: security.openshift.io/v1 kind: SecurityContextConstraints metadata: name: restricted-no-added-caps allowPrivilegedContainer: false allowedCapabilities: [] defaultAddCapabilities: [] runAsUser: type: MustRunAsRange seLinuxContext: type: MustRunAs users: [] groups: - system:authenticated --- Assign this SCC to workloads that do **not** require elevated capabilities. Create separate SCCs for workloads that require specific capabilities, and use RBAC to tightly restrict access to them. scored: true - id: 5.2.9 text: "Minimize the admission of containers with capabilities assigned (manual)" audit: | oc get scc -o json \ | jq -r '[.items[] | select((.requiredDropCapabilities // []) | index("ALL")) | .metadata.name] | length | if . > 0 then "true" else "false" end' tests: test_items: - flag: "true" remediation: | If no SCCs drop all capabilities, create one that sets 'requiredDropCapabilities: [ALL]': --- apiVersion: security.openshift.io/v1 kind: SecurityContextConstraints metadata: name: restricted-drop-all-capabilities requiredDropCapabilities: - ALL allowPrivilegedContainer: false runAsUser: type: MustRunAsRange seLinuxContext: type: MustRunAs users: [] groups: - system:authenticated --- Apply this SCC to general-purpose workloads that do not require elevated Linux capabilities. If certain workloads require capabilities, create a separate SCC with minimal permissions and scope it using RBAC. scored: true - id: 5.2.10 text: "Minimize access to privileged Security Context Constraints (Manual)" type: "manual" remediation: | Remove any users and groups who do not need access to an SCC, following the principle of least privilege. You can remove users and groups from an SCC using the oc edit scc $NAME command. Additionally, you can create your own SCCs that contain the container functionality you need for a particular use case and assign that SCC to users and groups if the default SCCs are not appropriate for your use case. scored: false - id: 5.3 text: "Network Policies and CNI" checks: - id: 5.3.1 text: "Ensure that the CNI in use supports Network Policies (Manual)" type: "manual" remediation: | None required. scored: false - id: 5.3.2 text: "Ensure that all Namespaces have Network Policies defined (Manual)" type: "manual" audit: | #Run the following command and review the NetworkPolicy objects created in the cluster. oc -n all get networkpolicy remediation: | Follow the documentation and create NetworkPolicy objects as you need them. scored: false - id: 5.4 text: "Secrets Management" checks: - id: 5.4.1 text: "Prefer using secrets as files over secrets as environment variables (Manual)" type: "manual" audit: | #Run the following command to find references to objects which use environment variables defined from secrets. oc get all -o jsonpath='{range .items[?(@..secretKeyRef)]} {.kind} {.metadata.name} {"\n"}{end}' -A remediation: | If possible, rewrite application code to read secrets from mounted secret files, rather than from environment variables. scored: false - id: 5.4.2 text: "Consider external secret storage (Manual)" type: "manual" remediation: | Refer to the secrets management options offered by your cloud provider or a third-party secrets management solution. scored: false - id: 5.5 text: "Extensible Admission Control" checks: - id: 5.5.1 text: "Configure Image Provenance using image controller configuration parameters (Manual)" type: "manual" remediation: | Follow the OpenShift documentation: [Image configuration resources](https://docs.openshift.com/container-platform/4.5/openshift_images/image-configuration.html scored: false - id: 5.7 text: "General Policies" checks: - id: 5.7.1 text: "Create administrative boundaries between resources using namespaces (Manual)" type: "manual" audit: | #Run the following command and review the namespaces created in the cluster. oc get namespaces #Ensure that these namespaces are the ones you need and are adequately administered as per your requirements. remediation: | Follow the documentation and create namespaces for objects in your deployment as you need them. scored: false - id: 5.7.2 text: "Ensure that the seccomp profile is set to docker/default in your pod definitions (Manual)" type: "manual" remediation: | To enable the default seccomp profile, use the reserved value /runtime/default that will make sure that the pod uses the default policy available on the host. scored: false - id: 5.7.3 text: "Apply Security Context to Your Pods and Containers (Manual)" type: "manual" remediation: | Follow the Kubernetes documentation and apply security contexts to your pods. For a suggested list of security contexts, you may refer to the CIS Security Benchmark for Docker Containers. scored: false - id: 5.7.4 text: "The default namespace should not be used (Manual)" type: "manual" audit: | #Run this command to list objects in default namespace oc project default oc get all #The only entries there should be system managed resources such as the kubernetes and openshift service remediation: | Ensure that namespaces are created to allow for appropriate segregation of Kubernetes resources and that all new resources are created in a specific namespace. scored: false ================================================ FILE: cfg/rke-cis-1.23/config.yaml ================================================ --- ## Version-specific settings that override the values in cfg/config.yaml ================================================ FILE: cfg/rke-cis-1.23/controlplane.yaml ================================================ --- controls: version: "rke-cis-1.23" id: 3 text: "Control Plane Configuration" type: "controlplane" groups: - id: 3.1 text: "Authentication and Authorization" checks: - id: 3.1.1 text: "Client certificate authentication should not be used for users (Manual)" type: "manual" remediation: | Alternative mechanisms provided by Kubernetes such as the use of OIDC should be implemented in place of client certificates. scored: false - id: 3.2 text: "Logging" checks: - id: 3.2.1 text: "Ensure that a minimal audit policy is created (Manual)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--audit-policy-file" set: true remediation: | Create an audit policy file for your cluster. scored: true - id: 3.2.2 text: "Ensure that the audit policy covers key security concerns (Manual)" type: "manual" remediation: | Review the audit policy provided for the cluster and ensure that it covers at least the following areas, - Access to Secrets managed by the cluster. Care should be taken to only log Metadata for requests to Secrets, ConfigMaps, and TokenReviews, in order to avoid risk of logging sensitive data. - Modification of Pod and Deployment objects. - Use of `pods/exec`, `pods/portforward`, `pods/proxy` and `services/proxy`. For most requests, minimally logging at the Metadata level is recommended (the most basic level of logging). scored: false ================================================ FILE: cfg/rke-cis-1.23/etcd.yaml ================================================ --- controls: version: "rke-cis-1.23" id: 2 text: "Etcd Node Configuration" type: "etcd" groups: - id: 2 text: "Etcd Node Configuration" checks: - id: 2.1 text: "Ensure that the --cert-file and --key-file arguments are set as appropriate (Automated)" audit: "/bin/ps -ef | /bin/grep $etcdbin | /bin/grep -v grep" tests: bin_op: and test_items: - flag: "--cert-file" env: "ETCD_CERT_FILE" set: true - flag: "--key-file" env: "ETCD_KEY_FILE" set: true remediation: | Follow the etcd service documentation and configure TLS encryption. Then, edit the etcd pod specification file /etc/kubernetes/manifests/etcd.yaml on the master node and set the below parameters. --cert-file= --key-file= scored: true - id: 2.2 text: "Ensure that the --client-cert-auth argument is set to true (Automated)" audit: "/bin/ps -ef | /bin/grep $etcdbin | /bin/grep -v grep" tests: bin_op: or test_items: - flag: "--client-cert-auth" set: true - flag: "--client-cert-auth" env: "ETCD_CLIENT_CERT_AUTH" compare: op: eq value: true set: true remediation: | Edit the etcd pod specification file $etcdconf on the master node and set the below parameter. --client-cert-auth="true" scored: true - id: 2.3 text: "Ensure that the --auto-tls argument is not set to true (Automated)" audit: "/bin/ps -ef | /bin/grep $etcdbin | /bin/grep -v grep" tests: bin_op: or test_items: - flag: "--auto-tls" env: "ETCD_AUTO_TLS" set: false - flag: "--auto-tls" env: "ETCD_AUTO_TLS" compare: op: eq value: false remediation: | Edit the etcd pod specification file $etcdconf on the master node and either remove the --auto-tls parameter or set it to false. --auto-tls=false scored: true - id: 2.4 text: "Ensure that the --peer-cert-file and --peer-key-file arguments are set as appropriate (Automated)" audit: "/bin/ps -ef | /bin/grep $etcdbin | /bin/grep -v grep" tests: bin_op: and test_items: - flag: "--peer-cert-file" env: "ETCD_PEER_CERT_FILE" set: true - flag: "--peer-key-file" env: "ETCD_PEER_KEY_FILE" set: true remediation: | Follow the etcd service documentation and configure peer TLS encryption as appropriate for your etcd cluster. Then, edit the etcd pod specification file $etcdconf on the master node and set the below parameters. --peer-client-file= --peer-key-file= scored: true - id: 2.5 text: "Ensure that the --peer-client-cert-auth argument is set to true (Automated)" audit: "/bin/ps -ef | /bin/grep $etcdbin | /bin/grep -v grep" tests: bin_op: or test_items: - flag: "--peer-client-cert-auth" set: true - flag: "--peer-client-cert-auth" env: "ETCD_PEER_CLIENT_CERT_AUTH" compare: op: eq value: true set: true remediation: | Edit the etcd pod specification file $etcdconf on the master node and set the below parameter. --peer-client-cert-auth=true scored: true - id: 2.6 text: "Ensure that the --peer-auto-tls argument is not set to true (Automated)" audit: "/bin/ps -ef | /bin/grep $etcdbin | /bin/grep -v grep" tests: bin_op: or test_items: - flag: "--peer-auto-tls" env: "ETCD_PEER_AUTO_TLS" set: false - flag: "--peer-auto-tls" env: "ETCD_PEER_AUTO_TLS" compare: op: eq value: false set: false remediation: | Edit the etcd pod specification file $etcdconf on the master node and either remove the --peer-auto-tls parameter or set it to false. --peer-auto-tls=false scored: true - id: 2.7 text: "Ensure that a unique Certificate Authority is used for etcd (Automated)" audit: "/bin/ps -ef | /bin/grep $etcdbin | /bin/grep -v grep" tests: test_items: - flag: "--trusted-ca-file" env: "ETCD_TRUSTED_CA_FILE" set: true remediation: | [Manual test] Follow the etcd documentation and create a dedicated certificate authority setup for the etcd service. Then, edit the etcd pod specification file $etcdconf on the master node and set the below parameter. --trusted-ca-file= scored: true ================================================ FILE: cfg/rke-cis-1.23/master.yaml ================================================ --- controls: version: "rke-cis-1.23" id: 1 text: "Control Plane Security Configuration" type: "master" groups: - id: 1.1 text: "Control Plane Node Configuration Files" checks: - id: 1.1.1 text: "Ensure that the API server pod specification file permissions are set to 644 or more restrictive (Automated)" type: "skip" audit: "/bin/sh -c 'if test -e $apiserverconf; then stat -c permissions=%a $apiserverconf; fi'" tests: test_items: - flag: "permissions" compare: op: bitmask value: "644" remediation: | Clusters provisioned by RKE do not require or maintain a configuration file for kube-apiserver. All configuration is passed in as arguments at container run time. scored: true - id: 1.1.2 text: "Ensure that the API server pod specification file ownership is set to root:root (Automated)" type: "skip" audit: "/bin/sh -c 'if test -e $apiserverconf; then stat -c %U:%G $apiserverconf; fi'" tests: test_items: - flag: "root:root" remediation: | Cluster provisioned by RKE doesn't require or maintain a configuration file for kube-apiserver. All configuration is passed in as arguments at container run time. scored: true - id: 1.1.3 text: "Ensure that the controller manager pod specification file permissions are set to 644 or more restrictive (Automated)" type: "skip" audit: "/bin/sh -c 'if test -e $controllermanagerconf; then stat -c permissions=%a $controllermanagerconf; fi'" tests: test_items: - flag: "permissions" compare: op: bitmask value: "644" remediation: | Cluster provisioned by RKE doesn't require or maintain a configuration file for controller-manager. All configuration is passed in as arguments at container run time. scored: true - id: 1.1.4 text: "Ensure that the controller manager pod specification file ownership is set to root:root (Automated)" type: "skip" audit: "/bin/sh -c 'if test -e $controllermanagerconf; then stat -c %U:%G $controllermanagerconf; fi'" tests: test_items: - flag: "root:root" remediation: | Cluster provisioned by RKE doesn't require or maintain a configuration file for controller-manager. All configuration is passed in as arguments at container run time. scored: true - id: 1.1.5 text: "Ensure that the scheduler pod specification file permissions are set to 644 or more restrictive (Automated)" type: "skip" audit: "/bin/sh -c 'if test -e $schedulerconf; then stat -c permissions=%a $schedulerconf; fi'" tests: test_items: - flag: "permissions" compare: op: bitmask value: "644" remediation: | Cluster provisioned by RKE doesn't require or maintain a configuration file for scheduler. All configuration is passed in as arguments at container run time. scored: true - id: 1.1.6 text: "Ensure that the scheduler pod specification file ownership is set to root:root (Automated)" type: "skip" audit: "/bin/sh -c 'if test -e $schedulerconf; then stat -c %U:%G $schedulerconf; fi'" tests: test_items: - flag: "root:root" remediation: | Cluster provisioned by RKE doesn't require or maintain a configuration file for scheduler. All configuration is passed in as arguments at container run time. scored: true - id: 1.1.7 text: "Ensure that the etcd pod specification file permissions are set to 644 or more restrictive (Automated)" type: "skip" audit: "/bin/sh -c 'if test -e $etcdconf; then find $etcdconf -name '*etcd*' | xargs stat -c permissions=%a; fi'" use_multiple_values: true tests: test_items: - flag: "permissions" compare: op: bitmask value: "644" remediation: | Cluster provisioned by RKE doesn't require or maintain a configuration file for etcd. All configuration is passed in as arguments at container run time. scored: true - id: 1.1.8 text: "Ensure that the etcd pod specification file ownership is set to root:root (Automated)" type: "skip" audit: "/bin/sh -c 'if test -e $etcdconf; then find $etcdconf -name '*etcd*' | xargs stat -c %U:%G; fi'" use_multiple_values: true tests: test_items: - flag: "root:root" remediation: | Cluster provisioned by RKE doesn't require or maintain a configuration file for etcd. All configuration is passed in as arguments at container run time. scored: true - id: 1.1.9 text: "Ensure that the Container Network Interface file permissions are set to 644 or more restrictive (Manual)" audit: | ps -ef | grep $kubeletbin | grep -- --cni-conf-dir | sed 's%.*cni-conf-dir[= ]\([^ ]*\).*%\1%' | xargs -I{} find {} -mindepth 1 | xargs --no-run-if-empty stat -c permissions=%a find /var/lib/cni/networks -type f 2> /dev/null | xargs --no-run-if-empty stat -c permissions=%a use_multiple_values: true tests: test_items: - flag: "permissions" compare: op: bitmask value: "644" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chmod 644 scored: false - id: 1.1.10 text: "Ensure that the Container Network Interface file ownership is set to root:root (Manual)" audit: | ps -ef | grep $kubeletbin | grep -- --cni-conf-dir | sed 's%.*cni-conf-dir[= ]\([^ ]*\).*%\1%' | xargs -I{} find {} -mindepth 1 | xargs --no-run-if-empty stat -c %U:%G find /var/lib/cni/networks -type f 2> /dev/null | xargs --no-run-if-empty stat -c %U:%G use_multiple_values: true tests: test_items: - flag: "root:root" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chown root:root scored: false - id: 1.1.11 text: "Ensure that the etcd data directory permissions are set to 700 or more restrictive (Automated)" audit: stat -c %a /var/lib/etcd tests: test_items: - flag: "700" compare: op: eq value: "700" set: true remediation: | On the etcd server node, get the etcd data directory, passed as an argument --data-dir, from the command 'ps -ef | grep etcd'. Run the below command (based on the etcd data directory found above). For example, chmod 700 /var/lib/etcd scored: true - id: 1.1.12 text: "Ensure that the etcd data directory ownership is set to etcd:etcd (Automated)" type: "skip" audit: "stat -c %U:%G /node/var/lib/etcd" tests: test_items: - flag: "etcd:etcd" set: true remediation: | On the etcd server node, get the etcd data directory, passed as an argument --data-dir, from the command 'ps -ef | grep etcd'. Run the below command (based on the etcd data directory found above). For example, chown etcd:etcd /var/lib/etcd scored: true - id: 1.1.13 text: "Ensure that the admin.conf file permissions are set to 600 or more restrictive (Automated)" type: "skip" audit: "/bin/sh -c 'if test -e /etc/kubernetes/admin.conf; then stat -c permissions=%a /etc/kubernetes/admin.conf; fi'" tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | A cluster provisioned by RKE does not store the kubernetes default kubeconfig credentials file on the nodes. scored: true - id: 1.1.14 text: "Ensure that the admin.conf file ownership is set to root:root (Automated)" type: "skip" audit: "/bin/sh -c 'if test -e /etc/kubernetes/admin.conf; then stat -c %U:%G /etc/kubernetes/admin.conf; fi'" tests: test_items: - flag: "root:root" remediation: | Cluster provisioned by RKE does not store the kubernetes default kubeconfig credentials file on the nodes. scored: true - id: 1.1.15 text: "Ensure that the scheduler.conf file permissions are set to 644 or more restrictive (Automated)" type: "skip" audit: "/bin/sh -c 'if test -e $schedulerkubeconfig; then stat -c permissions=%a $schedulerkubeconfig; fi'" tests: test_items: - flag: "permissions" compare: op: bitmask value: "644" remediation: | Cluster provisioned by RKE doesn't require or maintain a configuration file for scheduler. All configuration is passed in as arguments at container run time. scored: true - id: 1.1.16 text: "Ensure that the scheduler.conf file ownership is set to root:root (Automated)" type: "skip" audit: "/bin/sh -c 'if test -e $schedulerkubeconfig; then stat -c %U:%G $schedulerkubeconfig; fi'" tests: test_items: - flag: "root:root" remediation: | Cluster provisioned by RKE doesn't require or maintain a configuration file for scheduler. All configuration is passed in as arguments at container run time. scored: true - id: 1.1.17 text: "Ensure that the controller-manager.conf file permissions are set to 644 or more restrictive (Automated)" type: "skip" audit: "/bin/sh -c 'if test -e $controllermanagerkubeconfig; then stat -c permissions=%a $controllermanagerkubeconfig; fi'" tests: test_items: - flag: "permissions" compare: op: bitmask value: "644" remediation: | Cluster provisioned by RKE doesn't require or maintain a configuration file for controller-manager. All configuration is passed in as arguments at container run time. scored: true - id: 1.1.18 text: "Ensure that the controller-manager.conf file ownership is set to root:root (Automated)" type: "skip" audit: "/bin/sh -c 'if test -e $controllermanagerkubeconfig; then stat -c %U:%G $controllermanagerkubeconfig; fi'" tests: test_items: - flag: "root:root" remediation: | Cluster provisioned by RKE doesn't require or maintain a configuration file for controller-manager. All configuration is passed in as arguments at container run time. scored: true - id: 1.1.19 text: "Ensure that the Kubernetes PKI directory and file ownership is set to root:root (Automated)" audit: "check_files_owner_in_dir.sh /etc/kubernetes/ssl" tests: test_items: - flag: "true" compare: op: eq value: "true" set: true remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chown -R root:root /etc/kubernetes/pki/ scored: true - id: 1.1.20 text: "Ensure that the Kubernetes PKI certificate file permissions are set to 644 or more restrictive (Manual)" audit: "find /etc/kubernetes/pki/ -name '*.crt' | xargs stat -c permissions=%a" use_multiple_values: true tests: test_items: - flag: "permissions" compare: op: bitmask value: "644" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chmod -R 644 /etc/kubernetes/pki/*.crt scored: false - id: 1.1.21 text: "Ensure that the Kubernetes PKI key file permissions are set to 600 (Manual)" audit: "find /etc/kubernetes/pki/ -name '*.key' | xargs stat -c permissions=%a" use_multiple_values: true tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chmod -R 600 /etc/kubernetes/pki/*.key scored: false - id: 1.2 text: "API Server" checks: - id: 1.2.1 text: "Ensure that the --anonymous-auth argument is set to false (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--anonymous-auth" compare: op: eq value: false set: true remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the below parameter. --anonymous-auth=false scored: true - id: 1.2.2 text: "Ensure that the --token-auth-file parameter is not set (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--token-auth-file" set: false remediation: | Follow the documentation and configure alternate mechanisms for authentication. Then, edit the API server pod specification file $apiserverconf on the control plane node and remove the --token-auth-file= parameter. scored: true - id: 1.2.3 text: "Ensure that the --DenyServiceExternalIPs is not set (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: bin_op: or test_items: - flag: "--enable-admission-plugins" compare: op: nothave value: "DenyServiceExternalIPs" set: true - flag: "--enable-admission-plugins" set: false remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and remove the `DenyServiceExternalIPs` from enabled admission plugins. scored: true - id: 1.2.4 text: "Ensure that the --kubelet-https argument is set to true (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: bin_op: or test_items: - flag: "--kubelet-https" compare: op: eq value: true - flag: "--kubelet-https" set: false remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and remove the --kubelet-https parameter. scored: true - id: 1.2.5 text: "Ensure that the --kubelet-client-certificate and --kubelet-client-key arguments are set as appropriate (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: bin_op: and test_items: - flag: "--kubelet-client-certificate" set: true - flag: "--kubelet-client-key" set: true remediation: | Follow the Kubernetes documentation and set up the TLS connection between the apiserver and kubelets. Then, edit API server pod specification file $apiserverconf on the control plane node and set the kubelet client certificate and key parameters as below. --kubelet-client-certificate= --kubelet-client-key= scored: true - id: 1.2.6 text: "Ensure that the --kubelet-certificate-authority argument is set as appropriate (Automated)" type: "skip" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--kubelet-certificate-authority" set: true remediation: | Follow the Kubernetes documentation and setup the TLS connection between the apiserver and kubelets. Then, edit the API server pod specification file $apiserverconf on the control plane node and set the --kubelet-certificate-authority parameter to the path to the cert file for the certificate authority. --kubelet-certificate-authority= When generating serving certificates, functionality could break in conjunction with hostname overrides which are required for certain cloud providers. scored: true - id: 1.2.7 text: "Ensure that the --authorization-mode argument is not set to AlwaysAllow (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--authorization-mode" compare: op: nothave value: "AlwaysAllow" set: true remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the --authorization-mode parameter to values other than AlwaysAllow. One such example could be as below. --authorization-mode=RBAC scored: true - id: 1.2.8 text: "Ensure that the --authorization-mode argument includes Node (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--authorization-mode" compare: op: has value: "Node" set: true remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the --authorization-mode parameter to a value that includes Node. --authorization-mode=Node,RBAC scored: true - id: 1.2.9 text: "Ensure that the --authorization-mode argument includes RBAC (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--authorization-mode" compare: op: has value: "RBAC" set: true remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the --authorization-mode parameter to a value that includes RBAC, for example `--authorization-mode=Node,RBAC`. scored: true - id: 1.2.10 text: "Ensure that the admission control plugin EventRateLimit is set (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--enable-admission-plugins" compare: op: has value: "EventRateLimit" set: true remediation: | Follow the Kubernetes documentation and set the desired limits in a configuration file. Then, edit the API server pod specification file $apiserverconf and set the below parameters. --enable-admission-plugins=...,EventRateLimit,... --admission-control-config-file= scored: false - id: 1.2.11 text: "Ensure that the admission control plugin AlwaysAdmit is not set (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: bin_op: or test_items: - flag: "--enable-admission-plugins" compare: op: nothave value: AlwaysAdmit set: true - flag: "--enable-admission-plugins" set: false remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and either remove the --enable-admission-plugins parameter, or set it to a value that does not include AlwaysAdmit. scored: true - id: 1.2.12 text: "Ensure that the admission control plugin AlwaysPullImages is set (Manual)" type: "manual" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--enable-admission-plugins" compare: op: has value: "AlwaysPullImages" remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the --enable-admission-plugins parameter to include AlwaysPullImages. --enable-admission-plugins=...,AlwaysPullImages,... scored: false - id: 1.2.13 text: "Ensure that the admission control plugin SecurityContextDeny is set if PodSecurityPolicy is not used (Manual)" type: "manual" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: bin_op: or test_items: - flag: "--enable-admission-plugins" compare: op: has value: "SecurityContextDeny" - flag: "--enable-admission-plugins" compare: op: has value: "PodSecurityPolicy" remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the --enable-admission-plugins parameter to include SecurityContextDeny, unless PodSecurityPolicy is already in place. --enable-admission-plugins=...,SecurityContextDeny,... scored: false - id: 1.2.14 text: "Ensure that the admission control plugin ServiceAccount is set (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: bin_op: or test_items: - flag: "--disable-admission-plugins" compare: op: nothave value: "ServiceAccount" set: true - flag: "--disable-admission-plugins" set: false remediation: | Follow the documentation and create ServiceAccount objects as per your environment. Then, edit the API server pod specification file $apiserverconf on the control plane node and ensure that the --disable-admission-plugins parameter is set to a value that does not include ServiceAccount. scored: true - id: 1.2.15 text: "Ensure that the admission control plugin NamespaceLifecycle is set (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: bin_op: or test_items: - flag: "--disable-admission-plugins" compare: op: nothave value: "NamespaceLifecycle" set: true - flag: "--disable-admission-plugins" set: false remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the --disable-admission-plugins parameter to ensure it does not include NamespaceLifecycle. scored: true - id: 1.2.16 text: "Ensure that the admission control plugin NodeRestriction is set (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--enable-admission-plugins" compare: op: has value: "NodeRestriction" set: true remediation: | Follow the Kubernetes documentation and configure NodeRestriction plug-in on kubelets. Then, edit the API server pod specification file $apiserverconf on the control plane node and set the --enable-admission-plugins parameter to a value that includes NodeRestriction. --enable-admission-plugins=...,NodeRestriction,... scored: true - id: 1.2.17 text: "Ensure that the --secure-port argument is not set to 0 (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: bin_op: or test_items: - flag: "--secure-port" compare: op: gt value: 0 set: true - flag: "--secure-port" set: false remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and either remove the --secure-port parameter or set it to a different (non-zero) desired port. scored: true - id: 1.2.18 text: "Ensure that the --profiling argument is set to false (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--profiling" compare: op: eq value: false set: true remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the below parameter. --profiling=false scored: true - id: 1.2.19 text: "Ensure that the --audit-log-path argument is set (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--audit-log-path" set: true remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the --audit-log-path parameter to a suitable path and file where you would like audit logs to be written, for example, --audit-log-path=/var/log/apiserver/audit.log scored: true - id: 1.2.20 text: "Ensure that the --audit-log-maxage argument is set to 30 or as appropriate (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--audit-log-maxage" compare: op: gte value: 30 set: true remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the --audit-log-maxage parameter to 30 or as an appropriate number of days, for example, --audit-log-maxage=30 scored: true - id: 1.2.21 text: "Ensure that the --audit-log-maxbackup argument is set to 10 or as appropriate (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--audit-log-maxbackup" compare: op: gte value: 10 set: true remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the --audit-log-maxbackup parameter to 10 or to an appropriate value. For example, --audit-log-maxbackup=10 scored: true - id: 1.2.22 text: "Ensure that the --audit-log-maxsize argument is set to 100 or as appropriate (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--audit-log-maxsize" compare: op: gte value: 100 set: true remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the --audit-log-maxsize parameter to an appropriate size in MB. For example, to set it as 100 MB, --audit-log-maxsize=100 scored: true - id: 1.2.23 text: "Ensure that the --request-timeout argument is set as appropriate (Manual)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" type: manual remediation: | Edit the API server pod specification file $apiserverconf and set the below parameter as appropriate and if needed. For example, --request-timeout=300s scored: false - id: 1.2.24 text: "Ensure that the --service-account-lookup argument is set to true (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: bin_op: or test_items: - flag: "--service-account-lookup" set: false - flag: "--service-account-lookup" compare: op: eq value: true set: true remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the below parameter. --service-account-lookup=true Alternatively, you can delete the --service-account-lookup parameter from this file so that the default takes effect. scored: true - id: 1.2.25 text: "Ensure that the --service-account-key-file argument is set as appropriate (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--service-account-key-file" set: true remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the --service-account-key-file parameter to the public key file for service accounts. For example, --service-account-key-file= scored: true - id: 1.2.26 text: "Ensure that the --etcd-certfile and --etcd-keyfile arguments are set as appropriate (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: bin_op: and test_items: - flag: "--etcd-certfile" set: true - flag: "--etcd-keyfile" set: true remediation: | Follow the Kubernetes documentation and set up the TLS connection between the apiserver and etcd. Then, edit the API server pod specification file $apiserverconf on the control plane node and set the etcd certificate and key file parameters. --etcd-certfile= --etcd-keyfile= scored: true - id: 1.2.27 text: "Ensure that the --tls-cert-file and --tls-private-key-file arguments are set as appropriate (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: bin_op: and test_items: - flag: "--tls-cert-file" set: true - flag: "--tls-private-key-file" set: true remediation: | Follow the Kubernetes documentation and set up the TLS connection on the apiserver. Then, edit the API server pod specification file $apiserverconf on the control plane node and set the TLS certificate and private key file parameters. --tls-cert-file= --tls-private-key-file= scored: true - id: 1.2.28 text: "Ensure that the --client-ca-file argument is set as appropriate (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--client-ca-file" set: true remediation: | Follow the Kubernetes documentation and set up the TLS connection on the apiserver. Then, edit the API server pod specification file $apiserverconf on the control plane node and set the client certificate authority file. --client-ca-file= scored: true - id: 1.2.29 text: "Ensure that the --etcd-cafile argument is set as appropriate (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--etcd-cafile" set: true remediation: | Follow the Kubernetes documentation and set up the TLS connection between the apiserver and etcd. Then, edit the API server pod specification file $apiserverconf on the control plane node and set the etcd certificate authority file parameter. --etcd-cafile= scored: true - id: 1.2.30 text: "Ensure that the --encryption-provider-config argument is set as appropriate (Manual)" type: "skip" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--encryption-provider-config" set: true remediation: | Follow the Kubernetes documentation and configure a EncryptionConfig file. Then, edit the API server pod specification file $apiserverconf on the control plane node and set the --encryption-provider-config parameter to the path of that file. For example, --encryption-provider-config= scored: false - id: 1.2.31 text: "Ensure that encryption providers are appropriately configured (Manual)" type: "skip" audit: | ENCRYPTION_PROVIDER_CONFIG=$(ps -ef | grep $apiserverbin | grep -- --encryption-provider-config | sed 's%.*encryption-provider-config[= ]\([^ ]*\).*%\1%') if test -e $ENCRYPTION_PROVIDER_CONFIG; then grep -A1 'providers:' $ENCRYPTION_PROVIDER_CONFIG | tail -n1 | grep -o "[A-Za-z]*" | sed 's/^/provider=/'; fi tests: test_items: - flag: "provider" compare: op: valid_elements value: "aescbc,kms,secretbox" remediation: | Follow the Kubernetes documentation and configure a EncryptionConfig file. In this file, choose aescbc, kms or secretbox as the encryption provider. Enabling encryption changes how data can be recovered as data is encrypted. scored: false - id: 1.2.32 text: "Ensure that the API Server only makes use of Strong Cryptographic Ciphers (Manual)" type: "manual" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--tls-cipher-suites" compare: op: valid_elements value: "TLS_AES_128_GCM_SHA256,TLS_AES_256_GCM_SHA384,TLS_CHACHA20_POLY1305_SHA256,TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA,TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,TLS_RSA_WITH_3DES_EDE_CBC_SHA,TLS_RSA_WITH_AES_128_CBC_SHA,TLS_RSA_WITH_AES_128_GCM_SHA256,TLS_RSA_WITH_AES_256_CBC_SHA,TLS_RSA_WITH_AES_256_GCM_SHA384" remediation: | Edit the API server pod specification file /etc/kubernetes/manifests/kube-apiserver.yaml on the control plane node and set the below parameter. --tls-cipher-suites=TLS_AES_128_GCM_SHA256,TLS_AES_256_GCM_SHA384,TLS_CHACHA20_POLY1305_SHA256, TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA,TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305, TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,TLS_RSA_WITH_3DES_EDE_CBC_SHA,TLS_RSA_WITH_AES_128_CBC_SHA, TLS_RSA_WITH_AES_128_GCM_SHA256,TLS_RSA_WITH_AES_256_CBC_SHA,TLS_RSA_WITH_AES_256_GCM_SHA384 scored: false - id: 1.3 text: "Controller Manager" checks: - id: 1.3.1 text: "Ensure that the --terminated-pod-gc-threshold argument is set as appropriate (Automated)" audit: "/bin/ps -ef | grep $controllermanagerbin | grep -v grep" tests: test_items: - flag: "--terminated-pod-gc-threshold" set: true remediation: | Edit the Controller Manager pod specification file $controllermanagerconf on the control plane node and set the --terminated-pod-gc-threshold to an appropriate threshold, for example, --terminated-pod-gc-threshold=10 scored: true - id: 1.3.2 text: "Ensure that the --profiling argument is set to false (Automated)" audit: "/bin/ps -ef | grep $controllermanagerbin | grep -v grep" tests: test_items: - flag: "--profiling" compare: op: eq value: false set: true remediation: | Edit the Controller Manager pod specification file $controllermanagerconf on the control plane node and set the below parameter. --profiling=false scored: true - id: 1.3.3 text: "Ensure that the --use-service-account-credentials argument is set to true (Automated)" audit: "/bin/ps -ef | grep $controllermanagerbin | grep -v grep" tests: test_items: - flag: "--use-service-account-credentials" compare: op: noteq value: false set: true remediation: | Edit the Controller Manager pod specification file $controllermanagerconf on the control plane node to set the below parameter. --use-service-account-credentials=true scored: true - id: 1.3.4 text: "Ensure that the --service-account-private-key-file argument is set as appropriate (Automated)" audit: "/bin/ps -ef | grep $controllermanagerbin | grep -v grep" tests: test_items: - flag: "--service-account-private-key-file" set: true remediation: | Edit the Controller Manager pod specification file $controllermanagerconf on the control plane node and set the --service-account-private-key-file parameter to the private key file for service accounts. --service-account-private-key-file= scored: true - id: 1.3.5 text: "Ensure that the --root-ca-file argument is set as appropriate (Automated)" audit: "/bin/ps -ef | grep $controllermanagerbin | grep -v grep" tests: test_items: - flag: "--root-ca-file" set: true remediation: | Edit the Controller Manager pod specification file $controllermanagerconf on the control plane node and set the --root-ca-file parameter to the certificate bundle file`. --root-ca-file= scored: true - id: 1.3.6 text: "Ensure that the RotateKubeletServerCertificate argument is set to true (Automated)" type: "skip" audit: "/bin/ps -ef | grep $controllermanagerbin | grep -v grep" tests: bin_op: or test_items: - flag: "--feature-gates" compare: op: nothave value: "RotateKubeletServerCertificate=false" set: true - flag: "--feature-gates" set: false remediation: | Edit the Controller Manager pod specification file $controllermanagerconf on the control plane node and set the --feature-gates parameter to include RotateKubeletServerCertificate=true. --feature-gates=RotateKubeletServerCertificate=true Cluster provisioned by RKE handles certificate rotation directly through RKE. scored: true - id: 1.3.7 text: "Ensure that the --bind-address argument is set to 127.0.0.1 (Automated)" audit: "/bin/ps -ef | grep $controllermanagerbin | grep -v grep" tests: test_items: - flag: "--bind-address" compare: op: eq value: "127.0.0.1" remediation: | Edit the Controller Manager pod specification file $controllermanagerconf on the control plane node and ensure the correct value for the --bind-address parameter scored: true - id: 1.4 text: "Scheduler" checks: - id: 1.4.1 text: "Ensure that the --profiling argument is set to false (Automated)" audit: "/bin/ps -ef | grep $schedulerbin | grep -v grep" tests: test_items: - flag: "--profiling" compare: op: eq value: false set: true remediation: | Edit the Scheduler pod specification file $schedulerconf file on the control plane node and set the below parameter. --profiling=false scored: true - id: 1.4.2 text: "Ensure that the --bind-address argument is set to 127.0.0.1 (Automated)" audit: "/bin/ps -ef | grep $schedulerbin | grep -v grep" tests: test_items: - flag: "--bind-address" compare: op: eq value: "127.0.0.1" remediation: | Edit the Scheduler pod specification file $schedulerconf on the control plane node and ensure the correct value for the --bind-address parameter scored: true ================================================ FILE: cfg/rke-cis-1.23/node.yaml ================================================ --- controls: version: "rke-cis-1.23" id: 4 text: "Worker Node Security Configuration" type: "node" groups: - id: 4.1 text: "Worker Node Configuration Files" checks: - id: 4.1.1 text: "Ensure that the kubelet service file permissions are set to 644 or more restrictive (Automated)" type: "skip" audit: '/bin/sh -c ''if test -e $kubeletsvc; then stat -c permissions=%a $kubeletsvc; fi'' ' tests: test_items: - flag: "permissions" compare: op: bitmask value: "644" remediation: | Cluster provisioned by RKE doesn’t require or maintain a configuration file for the kubelet service. All configuration is passed in as arguments at container run time. scored: true - id: 4.1.2 text: "Ensure that the kubelet service file ownership is set to root:root (Automated)" type: "skip" audit: '/bin/sh -c ''if test -e $kubeletsvc; then stat -c %U:%G $kubeletsvc; fi'' ' tests: test_items: - flag: root:root remediation: | Cluster provisioned by RKE doesn’t require or maintain a configuration file for the kubelet service. All configuration is passed in as arguments at container run time. scored: true - id: 4.1.3 text: "If proxy kubeconfig file exists ensure permissions are set to 644 or more restrictive (Manual)" audit: '/bin/sh -c ''if test -e $proxykubeconfig; then stat -c permissions=%a $proxykubeconfig; fi'' ' tests: bin_op: or test_items: - flag: "permissions" set: true compare: op: bitmask value: "644" remediation: | Run the below command (based on the file location on your system) on the each worker node. For example, chmod 644 $proxykubeconfig scored: true - id: 4.1.4 text: "If proxy kubeconfig file exists ensure ownership is set to root:root (Manual)" audit: '/bin/sh -c ''if test -e $proxykubeconfig; then stat -c %U:%G $proxykubeconfig; fi'' ' tests: bin_op: or test_items: - flag: root:root remediation: | Run the below command (based on the file location on your system) on the each worker node. For example, chown root:root $proxykubeconfig scored: true - id: 4.1.5 text: "Ensure that the --kubeconfig kubelet.conf file permissions are set to 644 or more restrictive (Automated)" audit: '/bin/sh -c ''if test -e /node$kubeletkubeconfig; then stat -c permissions=%a /node$kubeletkubeconfig; fi'' ' tests: test_items: - flag: "permissions" compare: op: bitmask value: "644" remediation: | Run the below command (based on the file location on your system) on the each worker node. For example, chmod 644 $kubeletkubeconfig scored: true - id: 4.1.6 text: "Ensure that the --kubeconfig kubelet.conf file ownership is set to root:root (Automated)" audit: '/bin/sh -c ''if test -e /node$kubeletkubeconfig; then stat -c %U:%G /node$kubeletkubeconfig; fi'' ' tests: test_items: - flag: root:root remediation: | Run the below command (based on the file location on your system) on the each worker node. For example, chown root:root $kubeletkubeconfig scored: true - id: 4.1.7 text: "Ensure that the certificate authorities file permissions are set to 644 or more restrictive (Automated)" audit: "stat -c permissions=%a /etc/kubernetes/ssl/kube-ca.pem" tests: test_items: - flag: "permissions" compare: op: bitmask value: "644" remediation: | Run the following command to modify the file permissions of the --client-ca-file chmod 644 scored: true - id: 4.1.8 text: "Ensure that the client certificate authorities file ownership is set to root:root (Automated)" audit: "stat -c %U:%G /etc/kubernetes/ssl/kube-ca.pem" tests: test_items: - flag: root:root remediation: | Run the following command to modify the ownership of the --client-ca-file. chown root:root scored: true - id: 4.1.9 text: "Ensure that the kubelet --config configuration file has permissions set to 644 or more restrictive (Automated)" type: "skip" audit: '/bin/sh -c ''if test -e $kubeletconf; then stat -c permissions=%a $kubeletconf; fi'' ' tests: test_items: - flag: "permissions" compare: op: bitmask value: "644" remediation: | Clusters provisioned by RKE doesn’t require or maintain a configuration file for the kubelet. All configuration is passed in as arguments at container run time. Clusters provisioned by RKE doesn’t require or maintain a configuration file for the kubelet. All configuration is passed in as arguments at container run time. scored: true - id: 4.1.10 text: "Ensure that the kubelet --config configuration file ownership is set to root:root (Automated)" type: "skip" audit: '/bin/sh -c ''if test -e $kubeletconf; then stat -c %U:%G $kubeletconf; fi'' ' tests: test_items: - flag: root:root remediation: | Clusters provisioned by RKE doesn’t require or maintain a configuration file for the kubelet. All configuration is passed in as arguments at container run time. Clusters provisioned by RKE doesn’t require or maintain a configuration file for the kubelet. All configuration is passed in as arguments at container run time. scored: true - id: 4.2 text: "Kubelet" checks: - id: 4.2.1 text: "Ensure that the --anonymous-auth argument is set to false (Automated)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/sh -c 'if test -e $kubeletconf; then /bin/cat $kubeletconf; fi' " tests: test_items: - flag: "--anonymous-auth" path: '{.authentication.anonymous.enabled}' compare: op: eq value: false remediation: | If using a Kubelet config file, edit the file to set `authentication: anonymous: enabled` to `false`. If using executable arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_SYSTEM_PODS_ARGS variable. `--anonymous-auth=false` Based on your system, restart the kubelet service. For example, systemctl daemon-reload systemctl restart kubelet.service scored: true - id: 4.2.2 text: "Ensure that the --authorization-mode argument is not set to AlwaysAllow (Automated)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/sh -c 'if test -e $kubeletconf; then /bin/cat $kubeletconf; fi' " tests: test_items: - flag: --authorization-mode path: '{.authorization.mode}' compare: op: nothave value: AlwaysAllow remediation: | If using a Kubelet config file, edit the file to set `authorization.mode` to Webhook. If using executable arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_AUTHZ_ARGS variable. --authorization-mode=Webhook Based on your system, restart the kubelet service. For example, systemctl daemon-reload systemctl restart kubelet.service scored: true - id: 4.2.3 text: "Ensure that the --client-ca-file argument is set as appropriate (Automated)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/sh -c 'if test -e $kubeletconf; then /bin/cat $kubeletconf; fi' " tests: test_items: - flag: --client-ca-file path: '{.authentication.x509.clientCAFile}' remediation: | If using a Kubelet config file, edit the file to set `authentication.x509.clientCAFile` to the location of the client CA file. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_AUTHZ_ARGS variable. --client-ca-file= Based on your system, restart the kubelet service. For example, systemctl daemon-reload systemctl restart kubelet.service scored: true - id: 4.2.4 text: "Ensure that the --read-only-port argument is set to 0 (Automated)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/sh -c 'if test -e $kubeletconf; then /bin/cat $kubeletconf; fi' " tests: bin_op: or test_items: - flag: "--read-only-port" path: '{.readOnlyPort}' compare: op: eq value: 0 - flag: "--read-only-port" path: '{.readOnlyPort}' set: false remediation: | If using a Kubelet config file, edit the file to set `readOnlyPort` to 0. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_SYSTEM_PODS_ARGS variable. --read-only-port=0 Based on your system, restart the kubelet service. For example, systemctl daemon-reload systemctl restart kubelet.service scored: true - id: 4.2.5 text: "Ensure that the --streaming-connection-idle-timeout argument is not set to 0 (Automated)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/sh -c 'if test -e $kubeletconf; then /bin/cat $kubeletconf; fi' " tests: test_items: - flag: --streaming-connection-idle-timeout path: '{.streamingConnectionIdleTimeout}' compare: op: noteq value: 0 - flag: --streaming-connection-idle-timeout path: '{.streamingConnectionIdleTimeout}' set: false bin_op: or remediation: | If using a Kubelet config file, edit the file to set `streamingConnectionIdleTimeout` to a value other than 0. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_SYSTEM_PODS_ARGS variable. --streaming-connection-idle-timeout=5m Based on your system, restart the kubelet service. For example, systemctl daemon-reload systemctl restart kubelet.service scored: true - id: 4.2.6 text: "Ensure that the --protect-kernel-defaults argument is set to true (Automated)" type: "skip" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/sh -c 'if test -e $kubeletconf; then /bin/cat $kubeletconf; fi' " tests: test_items: - flag: --protect-kernel-defaults path: '{.protectKernelDefaults}' compare: op: eq value: true remediation: | If using a Kubelet config file, edit the file to set `protectKernelDefaults` to `true`. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_SYSTEM_PODS_ARGS variable. --protect-kernel-defaults=true Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service System level configurations are required prior to provisioning the cluster in order for this argument to be set to true. scored: true - id: 4.2.7 text: "Ensure that the --make-iptables-util-chains argument is set to true (Automated)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/sh -c 'if test -e $kubeletconf; then /bin/cat $kubeletconf; fi' " tests: test_items: - flag: --make-iptables-util-chains path: '{.makeIPTablesUtilChains}' compare: op: eq value: true - flag: --make-iptables-util-chains path: '{.makeIPTablesUtilChains}' set: false bin_op: or remediation: | If using a Kubelet config file, edit the file to set `makeIPTablesUtilChains` to `true`. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and remove the --make-iptables-util-chains argument from the KUBELET_SYSTEM_PODS_ARGS variable. Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: true - id: 4.2.8 text: "Ensure that the --hostname-override argument is not set (Manual)" # This is one of those properties that can only be set as a command line argument. # To check if the property is set as expected, we need to parse the kubelet command # instead reading the Kubelet Configuration file. type: "skip" audit: "/bin/ps -fC $kubeletbin " tests: test_items: - flag: --hostname-override set: false remediation: | Edit the kubelet service file $kubeletsvc on each worker node and remove the --hostname-override argument from the KUBELET_SYSTEM_PODS_ARGS variable. Based on your system, restart the kubelet service. For example, systemctl daemon-reload systemctl restart kubelet.service Clusters provisioned by RKE set the --hostname-override to avoid any hostname configuration errors scored: false - id: 4.2.9 text: "Ensure that the --event-qps argument is set to 0 or a level which ensures appropriate event capture (Automated)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/sh -c 'if test -e $kubeletconf; then /bin/cat $kubeletconf; fi' " tests: test_items: - flag: --event-qps path: '{.eventRecordQPS}' compare: op: eq value: 0 remediation: | If using a Kubelet config file, edit the file to set `eventRecordQPS` to an appropriate level. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_SYSTEM_PODS_ARGS variable. Based on your system, restart the kubelet service. For example, systemctl daemon-reload systemctl restart kubelet.service scored: true - id: 4.2.10 text: "Ensure that the --tls-cert-file and --tls-private-key-file arguments are set as appropriate (Manual)" type: "skip" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/sh -c 'if test -e $kubeletconf; then /bin/cat $kubeletconf; fi' " tests: test_items: - flag: --tls-cert-file path: '{.tlsCertFile}' - flag: --tls-private-key-file path: '{.tlsPrivateKeyFile}' remediation: | If using a Kubelet config file, edit the file to set `tlsCertFile` to the location of the certificate file to use to identify this Kubelet, and `tlsPrivateKeyFile` to the location of the corresponding private key file. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameters in KUBELET_CERTIFICATE_ARGS variable. --tls-cert-file= --tls-private-key-file= Based on your system, restart the kubelet service. For example, systemctl daemon-reload systemctl restart kubelet.service When generating serving certificates, functionality could break in conjunction with hostname overrides which are required for certain cloud providers. scored: false - id: 4.2.11 text: "Ensure that the --rotate-certificates argument is not set to false (Automated)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/sh -c 'if test -e $kubeletconf; then /bin/cat $kubeletconf; fi' " tests: test_items: - flag: --rotate-certificates path: '{.rotateCertificates}' compare: op: eq value: true - flag: --rotate-certificates path: '{.rotateCertificates}' set: false bin_op: or remediation: | If using a Kubelet config file, edit the file to add the line `rotateCertificates` to `true` or remove it altogether to use the default value. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and remove --rotate-certificates=false argument from the KUBELET_CERTIFICATE_ARGS variable. Based on your system, restart the kubelet service. For example, systemctl daemon-reload systemctl restart kubelet.service scored: true - id: 4.2.12 text: "Verify that the RotateKubeletServerCertificate argument is set to true (Manual)" type: "skip" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/sh -c 'if test -e $kubeletconf; then /bin/cat $kubeletconf; fi' " tests: bin_op: or test_items: - flag: RotateKubeletServerCertificate path: '{.featureGates.RotateKubeletServerCertificate}' compare: op: nothave value: false - flag: RotateKubeletServerCertificate path: '{.featureGates.RotateKubeletServerCertificate}' set: false remediation: | Edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_CERTIFICATE_ARGS variable. --feature-gates=RotateKubeletServerCertificate=true Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service Clusters provisioned by RKE handles certificate rotation directly through RKE. scored: false - id: 4.2.13 text: "Ensure that the Kubelet only makes use of Strong Cryptographic Ciphers (Automated)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/sh -c 'if test -e $kubeletconf; then /bin/cat $kubeletconf; fi' " tests: test_items: - flag: --tls-cipher-suites path: '{range .tlsCipherSuites[:]}{}{'',''}{end}' compare: op: valid_elements value: 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 remediation: | If using a Kubelet config file, edit the file to set `tlsCipherSuites` to 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 or to a subset of these values. If using executable arguments, edit the kubelet service file $kubeletsvc on each worker node and set the --tls-cipher-suites parameter as follows, or to a subset of these values. --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 Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: true ================================================ FILE: cfg/rke-cis-1.23/policies.yaml ================================================ --- controls: version: "rke-cis-1.23" id: 5 text: "Kubernetes Policies" type: "policies" groups: - id: 5.1 text: "RBAC and Service Accounts" checks: - id: 5.1.1 text: "Ensure that the cluster-admin role is only used where required (Manual)" type: "manual" remediation: | Identify all clusterrolebindings to the cluster-admin role. Check if they are used and if they need this role or if they could use a role with fewer privileges. Where possible, first bind users to a lower privileged role and then remove the clusterrolebinding to the cluster-admin role : kubectl delete clusterrolebinding [name] scored: false - id: 5.1.2 text: "Minimize access to secrets (Manual)" type: "manual" remediation: | Where possible, remove get, list and watch access to Secret objects in the cluster. scored: false - id: 5.1.3 text: "Minimize wildcard use in Roles and ClusterRoles (Manual)" type: "manual" remediation: | Where possible replace any use of wildcards in clusterroles and roles with specific objects or actions. scored: false - id: 5.1.4 text: "Minimize access to create pods (Manual)" type: "manual" remediation: | Where possible, remove create access to pod objects in the cluster. scored: false - id: 5.1.5 text: "Ensure that default service accounts are not actively used. (Manual)" type: "manual" remediation: | Create explicit service accounts wherever a Kubernetes workload requires specific access to the Kubernetes API server. Modify the configuration of each default service account to include this value automountServiceAccountToken: false scored: false - id: 5.1.6 text: "Ensure that Service Account Tokens are only mounted where necessary (Manual)" type: "manual" remediation: | Modify the definition of pods and service accounts which do not need to mount service account tokens to disable it. scored: false - id: 5.1.7 text: "Avoid use of system:masters group (Manual)" type: "manual" remediation: | Remove the system:masters group from all users in the cluster. scored: false - id: 5.1.8 text: "Limit use of the Bind, Impersonate and Escalate permissions in the Kubernetes cluster (Manual)" type: "manual" remediation: | Where possible, remove the impersonate, bind and escalate rights from subjects. scored: false - id: 5.2 text: "Pod Security Standards" checks: - id: 5.2.1 text: "Ensure that the cluster has at least one active policy control mechanism in place (Manual)" type: "manual" remediation: | Ensure that either Pod Security Admission or an external policy control system is in place for every namespace which contains user workloads. scored: false - id: 5.2.2 text: "Minimize the admission of privileged containers (Manual)" type: "manual" remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of privileged containers. scored: false - id: 5.2.3 text: "Minimize the admission of containers wishing to share the host process ID namespace (Automated)" type: "skip" remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of `hostPID` containers. scored: false - id: 5.2.4 text: "Minimize the admission of containers wishing to share the host IPC namespace (Automated)" type: "skip" remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of `hostIPC` containers. scored: false - id: 5.2.5 text: "Minimize the admission of containers wishing to share the host network namespace (Automated)" type: "skip" remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of `hostNetwork` containers. scored: false - id: 5.2.6 text: "Minimize the admission of containers with allowPrivilegeEscalation (Manual)" type: "manual" remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of containers with `.spec.allowPrivilegeEscalation` set to `true`. scored: false - id: 5.2.7 text: "Minimize the admission of root containers (Manual)" type: "manual" remediation: | Create a policy for each namespace in the cluster, ensuring that either `MustRunAsNonRoot` or `MustRunAs` with the range of UIDs not including 0, is set. scored: false - id: 5.2.8 text: "Minimize the admission of containers with the NET_RAW capability (Manual)" type: "manual" remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of containers with the `NET_RAW` capability. scored: false - id: 5.2.9 text: "Minimize the admission of containers with added capabilities (Manual)" type: "manual" remediation: | Ensure that `allowedCapabilities` is not present in policies for the cluster unless it is set to an empty array. scored: false - id: 5.2.10 text: "Minimize the admission of containers with capabilities assigned (Manual)" type: "manual" remediation: | Review the use of capabilites in applications running on your cluster. Where a namespace contains applications which do not require any Linux capabities to operate consider adding a PSP which forbids the admission of containers which do not drop all capabilities. scored: false - id: 5.2.11 text: "Minimize the admission of Windows HostProcess containers (Manual)" type: "manual" remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of containers that have `.securityContext.windowsOptions.hostProcess` set to `true`. scored: false - id: 5.2.12 text: "Minimize the admission of HostPath volumes (Manual)" type: "manual" remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of containers with `hostPath` volumes. scored: false - id: 5.2.13 text: "Minimize the admission of containers which use HostPorts (Manual)" type: "manual" remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of containers which use `hostPort` sections. scored: false - id: 5.3 text: "Network Policies and CNI" checks: - id: 5.3.1 text: "Ensure that the CNI in use supports NetworkPolicies (Manual)" type: "manual" remediation: | If the CNI plugin in use does not support network policies, consideration should be given to making use of a different plugin, or finding an alternate mechanism for restricting traffic in the Kubernetes cluster. scored: false - id: 5.3.2 text: "Ensure that all Namespaces have NetworkPolicies defined (Manual)" type: "skip" remediation: | Follow the documentation and create NetworkPolicy objects as you need them. scored: false - id: 5.4 text: "Secrets Management" checks: - id: 5.4.1 text: "Prefer using Secrets as files over Secrets as environment variables (Manual)" type: "manual" remediation: | If possible, rewrite application code to read Secrets from mounted secret files, rather than from environment variables. scored: false - id: 5.4.2 text: "Consider external secret storage (Manual)" type: "manual" remediation: | Refer to the Secrets management options offered by your cloud provider or a third-party secrets management solution. scored: false - id: 5.5 text: "Extensible Admission Control" checks: - id: 5.5.1 text: "Configure Image Provenance using ImagePolicyWebhook admission controller (Manual)" type: "manual" remediation: | Follow the Kubernetes documentation and setup image provenance. scored: false - id: 5.7 text: "General Policies" checks: - id: 5.7.1 text: "Create administrative boundaries between resources using namespaces (Manual)" type: "manual" remediation: | Follow the documentation and create namespaces for objects in your deployment as you need them. scored: false - id: 5.7.2 text: "Ensure that the seccomp profile is set to docker/default in your Pod definitions (Manual)" type: "manual" remediation: | Use `securityContext` to enable the docker/default seccomp profile in your pod definitions. An example is as below: securityContext: seccompProfile: type: RuntimeDefault scored: false - id: 5.7.3 text: "Apply SecurityContext to your Pods and Containers (Manual)" type: "manual" remediation: | Follow the Kubernetes documentation and apply SecurityContexts to your Pods. For a suggested list of SecurityContexts, you may refer to the CIS Security Benchmark for Docker Containers. scored: false - id: 5.7.4 text: "The default namespace should not be used (Manual)" type: "skip" remediation: | Ensure that namespaces are created to allow for appropriate segregation of Kubernetes resources and that all new resources are created in a specific namespace. scored: false ================================================ FILE: cfg/rke-cis-1.24/config.yaml ================================================ --- ## Version-specific settings that override the values in cfg/config.yaml ================================================ FILE: cfg/rke-cis-1.24/controlplane.yaml ================================================ --- controls: version: "rke-cis-1.24" id: 3 text: "Control Plane Configuration" type: "controlplane" groups: - id: 3.1 text: "Authentication and Authorization" checks: - id: 3.1.1 text: "Client certificate authentication should not be used for users (Manual)" type: "manual" remediation: | Alternative mechanisms provided by Kubernetes such as the use of OIDC should be implemented in place of client certificates. scored: false - id: 3.2 text: "Logging" checks: - id: 3.2.1 text: "Ensure that a minimal audit policy is created (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--audit-policy-file" set: true remediation: | Create an audit policy file for your cluster. scored: true - id: 3.2.2 text: "Ensure that the audit policy covers key security concerns (Manual)" type: "manual" remediation: | Review the audit policy provided for the cluster and ensure that it covers at least the following areas, - Access to Secrets managed by the cluster. Care should be taken to only log Metadata for requests to Secrets, ConfigMaps, and TokenReviews, in order to avoid risk of logging sensitive data. - Modification of Pod and Deployment objects. - Use of `pods/exec`, `pods/portforward`, `pods/proxy` and `services/proxy`. For most requests, minimally logging at the Metadata level is recommended (the most basic level of logging). scored: false ================================================ FILE: cfg/rke-cis-1.24/etcd.yaml ================================================ --- controls: version: "rke-cis-1.24" id: 2 text: "Etcd Node Configuration" type: "etcd" groups: - id: 2 text: "Etcd Node Configuration" checks: - id: 2.1 text: "Ensure that the --cert-file and --key-file arguments are set as appropriate (Automated)" audit: "/bin/ps -ef | /bin/grep $etcdbin | /bin/grep -v grep" tests: bin_op: and test_items: - flag: "--cert-file" env: "ETCD_CERT_FILE" set: true - flag: "--key-file" env: "ETCD_KEY_FILE" set: true remediation: | Follow the etcd service documentation and configure TLS encryption. Then, edit the etcd pod specification file /etc/kubernetes/manifests/etcd.yaml on the master node and set the below parameters. --cert-file= --key-file= scored: true - id: 2.2 text: "Ensure that the --client-cert-auth argument is set to true (Automated)" audit: "/bin/ps -ef | /bin/grep $etcdbin | /bin/grep -v grep" tests: bin_op: or test_items: - flag: "--client-cert-auth" set: true - flag: "--client-cert-auth" env: "ETCD_CLIENT_CERT_AUTH" compare: op: eq value: true set: true remediation: | Edit the etcd pod specification file $etcdconf on the master node and set the below parameter. --client-cert-auth="true" scored: true - id: 2.3 text: "Ensure that the --auto-tls argument is not set to true (Automated)" audit: "/bin/ps -ef | /bin/grep $etcdbin | /bin/grep -v grep" tests: bin_op: or test_items: - flag: "--auto-tls" env: "ETCD_AUTO_TLS" set: false - flag: "--auto-tls" env: "ETCD_AUTO_TLS" compare: op: eq value: false remediation: | Edit the etcd pod specification file $etcdconf on the master node and either remove the --auto-tls parameter or set it to false. --auto-tls=false scored: true - id: 2.4 text: "Ensure that the --peer-cert-file and --peer-key-file arguments are set as appropriate (Automated)" audit: "/bin/ps -ef | /bin/grep $etcdbin | /bin/grep -v grep" tests: bin_op: and test_items: - flag: "--peer-cert-file" env: "ETCD_PEER_CERT_FILE" set: true - flag: "--peer-key-file" env: "ETCD_PEER_KEY_FILE" set: true remediation: | Follow the etcd service documentation and configure peer TLS encryption as appropriate for your etcd cluster. Then, edit the etcd pod specification file $etcdconf on the master node and set the below parameters. --peer-client-file= --peer-key-file= scored: true - id: 2.5 text: "Ensure that the --peer-client-cert-auth argument is set to true (Automated)" audit: "/bin/ps -ef | /bin/grep $etcdbin | /bin/grep -v grep" tests: bin_op: or test_items: - flag: "--peer-client-cert-auth" set: true - flag: "--peer-client-cert-auth" env: "ETCD_PEER_CLIENT_CERT_AUTH" compare: op: eq value: true set: true remediation: | Edit the etcd pod specification file $etcdconf on the master node and set the below parameter. --peer-client-cert-auth=true scored: true - id: 2.6 text: "Ensure that the --peer-auto-tls argument is not set to true (Automated)" audit: "/bin/ps -ef | /bin/grep $etcdbin | /bin/grep -v grep" tests: bin_op: or test_items: - flag: "--peer-auto-tls" env: "ETCD_PEER_AUTO_TLS" set: false - flag: "--peer-auto-tls" env: "ETCD_PEER_AUTO_TLS" compare: op: eq value: false set: false remediation: | Edit the etcd pod specification file $etcdconf on the master node and either remove the --peer-auto-tls parameter or set it to false. --peer-auto-tls=false scored: true - id: 2.7 text: "Ensure that a unique Certificate Authority is used for etcd (Automated)" audit: "/bin/ps -ef | /bin/grep $etcdbin | /bin/grep -v grep" tests: test_items: - flag: "--trusted-ca-file" env: "ETCD_TRUSTED_CA_FILE" set: true remediation: | [Manual test] Follow the etcd documentation and create a dedicated certificate authority setup for the etcd service. Then, edit the etcd pod specification file $etcdconf on the master node and set the below parameter. --trusted-ca-file= scored: true ================================================ FILE: cfg/rke-cis-1.24/master.yaml ================================================ --- controls: version: "rke-cis-1.24" id: 1 text: "Control Plane Security Configuration" type: "master" groups: - id: 1.1 text: "Control Plane Node Configuration Files" checks: - id: 1.1.1 text: "Ensure that the API server pod specification file permissions are set to 644 or more restrictive (Automated)" audit: "/bin/sh -c 'if test -e $apiserverconf; then stat -c permissions=%a $apiserverconf;else echo \"File not found\"; fi'" tests: bin_op: or test_items: - flag: "permissions" compare: op: bitmask value: "644" - flag: "File not found" remediation: | Cluster provisioned by RKE doesn't require or maintain a configuration file for kube-apiserver. All configuration is passed in as arguments at container run time. scored: true - id: 1.1.2 text: "Ensure that the API server pod specification file ownership is set to root:root (Automated)" type: "skip" audit: "/bin/sh -c 'if test -e $apiserverconf; then stat -c %U:%G $apiserverconf; fi'" tests: test_items: - flag: "root:root" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chown root:root $apiserverconf scored: true - id: 1.1.3 text: "Ensure that the controller manager pod specification file permissions are set to 600 or more restrictive (Automated)" type: "skip" audit: "/bin/sh -c 'if test -e $controllermanagerconf; then stat -c permissions=%a $controllermanagerconf; fi'" tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chmod 600 $controllermanagerconf scored: true - id: 1.1.4 text: "Ensure that the controller manager pod specification file ownership is set to root:root (Automated)" type: "skip" audit: "/bin/sh -c 'if test -e $controllermanagerconf; then stat -c %U:%G $controllermanagerconf; fi'" tests: test_items: - flag: "root:root" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chown root:root $controllermanagerconf scored: true - id: 1.1.5 text: "Ensure that the scheduler pod specification file permissions are set to 600 or more restrictive (Automated)" type: "skip" audit: "/bin/sh -c 'if test -e $schedulerconf; then stat -c permissions=%a $schedulerconf; fi'" tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chmod 600 $schedulerconf scored: true - id: 1.1.6 text: "Ensure that the scheduler pod specification file ownership is set to root:root (Automated)" type: "skip" audit: "/bin/sh -c 'if test -e $schedulerconf; then stat -c %U:%G $schedulerconf; fi'" tests: test_items: - flag: "root:root" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chown root:root $schedulerconf scored: true - id: 1.1.7 text: "Ensure that the etcd pod specification file permissions are set to 600 or more restrictive (Automated)" type: "skip" audit: "/bin/sh -c 'if test -e $etcdconf; then find $etcdconf -name '*etcd*' | xargs stat -c permissions=%a; fi'" use_multiple_values: true tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chmod 600 $etcdconf scored: true - id: 1.1.8 text: "Ensure that the etcd pod specification file ownership is set to root:root (Automated)" type: "skip" audit: "/bin/sh -c 'if test -e $etcdconf; then find $etcdconf -name '*etcd*' | xargs stat -c %U:%G; fi'" use_multiple_values: true tests: test_items: - flag: "root:root" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chown root:root $etcdconf scored: true - id: 1.1.9 text: "Ensure that the Container Network Interface file permissions are set to 600 or more restrictive (Manual)" audit: | ps -ef | grep $kubeletbin | grep -- --cni-conf-dir | sed 's%.*cni-conf-dir[= ]\([^ ]*\).*%\1%' | xargs -I{} find {} -mindepth 1 | xargs --no-run-if-empty stat -c permissions=%a find /var/lib/cni/networks -type f 2> /dev/null | xargs --no-run-if-empty stat -c permissions=%a use_multiple_values: true tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chmod 600 scored: false - id: 1.1.10 text: "Ensure that the Container Network Interface file ownership is set to root:root (Automated)" audit: | ps -ef | grep $kubeletbin | grep -- --cni-conf-dir | sed 's%.*cni-conf-dir[= ]\([^ ]*\).*%\1%' | xargs -I{} find {} -mindepth 1 | xargs --no-run-if-empty stat -c %U:%G find /var/lib/cni/networks -type f 2> /dev/null | xargs --no-run-if-empty stat -c %U:%G use_multiple_values: true tests: test_items: - flag: "root:root" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chown root:root scored: true - id: 1.1.11 text: "Ensure that the etcd data directory permissions are set to 700 or more restrictive (Automated)" audit: stat -c %a /var/lib/etcd tests: test_items: - flag: "700" compare: op: eq value: "700" set: true remediation: | On the etcd server node, get the etcd data directory, passed as an argument --data-dir, from the command 'ps -ef | grep etcd'. Run the below command (based on the etcd data directory found above). For example, chmod 700 /var/lib/etcd scored: true - id: 1.1.12 text: "Ensure that the etcd data directory ownership is set to etcd:etcd (Automated)" type: "skip" audit: "stat -c %U:%G /node/var/lib/etcd" tests: test_items: - flag: "etcd:etcd" set: true remediation: | On the etcd server node, get the etcd data directory, passed as an argument --data-dir, from the command 'ps -ef | grep etcd'. Run the below command (based on the etcd data directory found above). For example, chown etcd:etcd /var/lib/etcd scored: true - id: 1.1.13 text: "Ensure that the admin.conf file permissions are set to 600 or more restrictive (Automated)" type: "skip" audit: "/bin/sh -c 'if test -e /etc/kubernetes/admin.conf; then stat -c permissions=%a /etc/kubernetes/admin.conf; fi'" tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chmod 600 /etc/kubernetes/admin.conf scored: true - id: 1.1.14 text: "Ensure that the admin.conf file ownership is set to root:root (Automated)" type: "skip" audit: "/bin/sh -c 'if test -e /etc/kubernetes/admin.conf; then stat -c %U:%G /etc/kubernetes/admin.conf; fi'" tests: test_items: - flag: "root:root" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chown root:root /etc/kubernetes/admin.conf scored: true - id: 1.1.15 text: "Ensure that the scheduler.conf file permissions are set to 600 or more restrictive (Automated)" type: "skip" audit: "/bin/sh -c 'if test -e $schedulerkubeconfig; then stat -c permissions=%a $schedulerkubeconfig; fi'" tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chmod 600 $schedulerkubeconfig scored: true - id: 1.1.16 text: "Ensure that the scheduler.conf file ownership is set to root:root (Automated)" type: "skip" audit: "/bin/sh -c 'if test -e $schedulerkubeconfig; then stat -c %U:%G $schedulerkubeconfig; fi'" tests: test_items: - flag: "root:root" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chown root:root $schedulerkubeconfig scored: true - id: 1.1.17 text: "Ensure that the controller-manager.conf file permissions are set to 600 or more restrictive (Automated)" type: "skip" audit: "/bin/sh -c 'if test -e $controllermanagerkubeconfig; then stat -c permissions=%a $controllermanagerkubeconfig; fi'" tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chmod 600 $controllermanagerkubeconfig scored: true - id: 1.1.18 text: "Ensure that the controller-manager.conf file ownership is set to root:root (Automated)" type: "skip" audit: "/bin/sh -c 'if test -e $controllermanagerkubeconfig; then stat -c %U:%G $controllermanagerkubeconfig; fi'" tests: test_items: - flag: "root:root" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chown root:root $controllermanagerkubeconfig scored: true - id: 1.1.19 text: "Ensure that the Kubernetes PKI directory and file ownership is set to root:root (Automated)" audit: "check_files_owner_in_dir.sh /etc/kubernetes/ssl" tests: test_items: - flag: "true" compare: op: eq value: "true" set: true remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chown -R root:root /etc/kubernetes/pki/ scored: true - id: 1.1.20 text: "Ensure that the Kubernetes PKI certificate file permissions are set to 600 or more restrictive (Automated)" audit: | if test -n "$(find /etc/kubernetes/ssl/ -name '*.pem' ! -name '*key.pem')"; then find /etc/kubernetes/ssl/ -name '*.pem' ! -name '*key.pem' | xargs stat -c permissions=%a;else echo "File not found"; fi tests: bin_op: or test_items: - flag: "File not found" - flag: "permissions" compare: op: bitmask value: "600" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, find /etc/kubernetes/ssl/ -name '*.pem' ! -name '*key.pem' -exec chmod -R 600 {} + scored: true - id: 1.1.21 text: "Ensure that the Kubernetes PKI key file permissions are set to 600 (Automated)" audit: | if test -n "$(find /etc/kubernetes/ssl/ -name '*.pem')"; then find /etc/kubernetes/ssl/ -name '*.pem' | xargs stat -c permissions=%a;else echo \"File not found\"; fi tests: bin_op: or test_items: - flag: "permissions" compare: op: bitmask value: "600" - flag: "File not found" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, find /etc/kubernetes/ssl/ -name '*key.pem' -exec chmod -R 600 {} + scored: true - id: 1.2 text: "API Server" checks: - id: 1.2.1 text: "Ensure that the --anonymous-auth argument is set to false (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--anonymous-auth" compare: op: eq value: false set: true remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the below parameter. --anonymous-auth=false scored: true - id: 1.2.2 text: "Ensure that the --token-auth-file parameter is not set (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--token-auth-file" set: false remediation: | Follow the documentation and configure alternate mechanisms for authentication. Then, edit the API server pod specification file $apiserverconf on the control plane node and remove the --token-auth-file= parameter. scored: true - id: 1.2.3 text: "Ensure that the --DenyServiceExternalIPs is not set (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: bin_op: or test_items: - flag: "--enable-admission-plugins" compare: op: nothave value: "DenyServiceExternalIPs" set: true - flag: "--enable-admission-plugins" set: false remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and remove the `DenyServiceExternalIPs` from enabled admission plugins. scored: true - id: 1.2.4 text: "Ensure that the --kubelet-https argument is set to true (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--kubelet-https" compare: op: eq value: true remediation: | Edit the API server pod specification file /etc/kubernetes/manifests/kube-apiserver.yaml on the control plane node and remove the --kubelet-https parameter. scored: true - id: 1.2.5 text: "Ensure that the --kubelet-client-certificate and --kubelet-client-key arguments are set as appropriate (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: bin_op: and test_items: - flag: "--kubelet-client-certificate" set: true - flag: "--kubelet-client-key" set: true remediation: | Follow the Kubernetes documentation and set up the TLS connection between the apiserver and kubelets. Then, edit API server pod specification file $apiserverconf on the control plane node and set the kubelet client certificate and key parameters as below. --kubelet-client-certificate= --kubelet-client-key= scored: true - id: 1.2.6 text: "Ensure that the --kubelet-certificate-authority argument is set as appropriate (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--kubelet-certificate-authority" set: true remediation: | Follow the Kubernetes documentation and setup the TLS connection between the apiserver and kubelets. Then, edit the API server pod specification file $apiserverconf on the control plane node and set the --kubelet-certificate-authority parameter to the path to the cert file for the certificate authority. --kubelet-certificate-authority= When generating serving certificates, functionality could break in conjunction with hostname overrides which are required for certain cloud providers. scored: true - id: 1.2.7 text: "Ensure that the --authorization-mode argument is not set to AlwaysAllow (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--authorization-mode" compare: op: nothave value: "AlwaysAllow" set: true remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the --authorization-mode parameter to values other than AlwaysAllow. One such example could be as below. --authorization-mode=RBAC scored: true - id: 1.2.8 text: "Ensure that the --authorization-mode argument includes Node (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--authorization-mode" compare: op: has value: "Node" set: true remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the --authorization-mode parameter to a value that includes Node. --authorization-mode=Node,RBAC scored: true - id: 1.2.9 text: "Ensure that the --authorization-mode argument includes RBAC (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--authorization-mode" compare: op: has value: "RBAC" set: true remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the --authorization-mode parameter to a value that includes RBAC, for example `--authorization-mode=Node,RBAC`. scored: true - id: 1.2.10 text: "Ensure that the admission control plugin EventRateLimit is set (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--enable-admission-plugins" compare: op: has value: "EventRateLimit" set: true remediation: | Follow the Kubernetes documentation and set the desired limits in a configuration file. Then, edit the API server pod specification file $apiserverconf and set the below parameters. --enable-admission-plugins=...,EventRateLimit,... --admission-control-config-file= scored: true - id: 1.2.11 text: "Ensure that the admission control plugin AlwaysAdmit is not set (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: bin_op: or test_items: - flag: "--enable-admission-plugins" compare: op: nothave value: AlwaysAdmit set: true - flag: "--enable-admission-plugins" set: false remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and either remove the --enable-admission-plugins parameter, or set it to a value that does not include AlwaysAdmit. scored: true - id: 1.2.12 text: "Ensure that the admission control plugin AlwaysPullImages is set (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--enable-admission-plugins" compare: op: has value: "AlwaysPullImages" remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the --enable-admission-plugins parameter to include AlwaysPullImages. --enable-admission-plugins=...,AlwaysPullImages,... scored: true - id: 1.2.13 text: "Ensure that the admission control plugin SecurityContextDeny is set if PodSecurityPolicy is not used (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: bin_op: or test_items: - flag: "--enable-admission-plugins" compare: op: has value: "SecurityContextDeny" - flag: "--enable-admission-plugins" compare: op: has value: "PodSecurityPolicy" remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the --enable-admission-plugins parameter to include SecurityContextDeny, unless PodSecurityPolicy is already in place. --enable-admission-plugins=...,SecurityContextDeny,... scored: true - id: 1.2.14 text: "Ensure that the admission control plugin ServiceAccount is set (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: bin_op: or test_items: - flag: "--disable-admission-plugins" compare: op: nothave value: "ServiceAccount" set: true - flag: "--disable-admission-plugins" set: false remediation: | Follow the documentation and create ServiceAccount objects as per your environment. Then, edit the API server pod specification file $apiserverconf on the control plane node and ensure that the --disable-admission-plugins parameter is set to a value that does not include ServiceAccount. scored: true - id: 1.2.15 text: "Ensure that the admission control plugin NamespaceLifecycle is set (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: bin_op: or test_items: - flag: "--disable-admission-plugins" compare: op: nothave value: "NamespaceLifecycle" set: true - flag: "--disable-admission-plugins" set: false remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the --disable-admission-plugins parameter to ensure it does not include NamespaceLifecycle. scored: true - id: 1.2.16 text: "Ensure that the admission control plugin NodeRestriction is set (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--enable-admission-plugins" compare: op: has value: "NodeRestriction" set: true remediation: | Follow the Kubernetes documentation and configure NodeRestriction plug-in on kubelets. Then, edit the API server pod specification file $apiserverconf on the control plane node and set the --enable-admission-plugins parameter to a value that includes NodeRestriction. --enable-admission-plugins=...,NodeRestriction,... scored: true - id: 1.2.17 text: "Ensure that the --secure-port argument is not set to 0 (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: bin_op: or test_items: - flag: "--secure-port" compare: op: gt value: 0 set: true - flag: "--secure-port" set: false remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and either remove the --secure-port parameter or set it to a different (non-zero) desired port. scored: true - id: 1.2.18 text: "Ensure that the --profiling argument is set to false (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--profiling" compare: op: eq value: false set: true remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the below parameter. --profiling=false scored: true - id: 1.2.19 text: "Ensure that the --audit-log-path argument is set (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--audit-log-path" set: true remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the --audit-log-path parameter to a suitable path and file where you would like audit logs to be written, for example, --audit-log-path=/var/log/apiserver/audit.log scored: true - id: 1.2.20 text: "Ensure that the --audit-log-maxage argument is set to 30 or as appropriate (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--audit-log-maxage" compare: op: gte value: 30 set: true remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the --audit-log-maxage parameter to 30 or as an appropriate number of days, for example, --audit-log-maxage=30 scored: true - id: 1.2.21 text: "Ensure that the --audit-log-maxbackup argument is set to 10 or as appropriate (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--audit-log-maxbackup" compare: op: gte value: 10 set: true remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the --audit-log-maxbackup parameter to 10 or to an appropriate value. For example, --audit-log-maxbackup=10 scored: true - id: 1.2.22 text: "Ensure that the --audit-log-maxsize argument is set to 100 or as appropriate (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--audit-log-maxsize" compare: op: gte value: 100 set: true remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the --audit-log-maxsize parameter to an appropriate size in MB. For example, to set it as 100 MB, --audit-log-maxsize=100 scored: true - id: 1.2.23 text: "Ensure that the --request-timeout argument is set as appropriate (Manual)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" type: manual remediation: | Edit the API server pod specification file $apiserverconf and set the below parameter as appropriate and if needed. For example, --request-timeout=300s scored: false - id: 1.2.24 text: "Ensure that the --service-account-lookup argument is set to true (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: bin_op: or test_items: - flag: "--service-account-lookup" set: false - flag: "--service-account-lookup" compare: op: eq value: true set: true remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the below parameter. --service-account-lookup=true Alternatively, you can delete the --service-account-lookup parameter from this file so that the default takes effect. scored: true - id: 1.2.25 text: "Ensure that the --service-account-key-file argument is set as appropriate (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--service-account-key-file" set: true remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the --service-account-key-file parameter to the public key file for service accounts. For example, --service-account-key-file= scored: true - id: 1.2.26 text: "Ensure that the --etcd-certfile and --etcd-keyfile arguments are set as appropriate (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: bin_op: and test_items: - flag: "--etcd-certfile" set: true - flag: "--etcd-keyfile" set: true remediation: | Follow the Kubernetes documentation and set up the TLS connection between the apiserver and etcd. Then, edit the API server pod specification file $apiserverconf on the control plane node and set the etcd certificate and key file parameters. --etcd-certfile= --etcd-keyfile= scored: true - id: 1.2.27 text: "Ensure that the --tls-cert-file and --tls-private-key-file arguments are set as appropriate (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: bin_op: and test_items: - flag: "--tls-cert-file" set: true - flag: "--tls-private-key-file" set: true remediation: | Follow the Kubernetes documentation and set up the TLS connection on the apiserver. Then, edit the API server pod specification file $apiserverconf on the control plane node and set the TLS certificate and private key file parameters. --tls-cert-file= --tls-private-key-file= scored: true - id: 1.2.28 text: "Ensure that the --client-ca-file argument is set as appropriate (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--client-ca-file" set: true remediation: | Follow the Kubernetes documentation and set up the TLS connection on the apiserver. Then, edit the API server pod specification file $apiserverconf on the control plane node and set the client certificate authority file. --client-ca-file= scored: true - id: 1.2.29 text: "Ensure that the --etcd-cafile argument is set as appropriate (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--etcd-cafile" set: true remediation: | Follow the Kubernetes documentation and set up the TLS connection between the apiserver and etcd. Then, edit the API server pod specification file $apiserverconf on the control plane node and set the etcd certificate authority file parameter. --etcd-cafile= scored: true - id: 1.2.30 text: "Ensure that the --encryption-provider-config argument is set as appropriate (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--encryption-provider-config" set: true remediation: | Follow the Kubernetes documentation and configure a EncryptionConfig file. Then, edit the API server pod specification file $apiserverconf on the control plane node and set the --encryption-provider-config parameter to the path of that file. For example, --encryption-provider-config= scored: true - id: 1.2.31 text: "Ensure that encryption providers are appropriately configured (Automated)" audit: | ENCRYPTION_PROVIDER_CONFIG=$(ps -ef | grep $apiserverbin | grep -- --encryption-provider-config | sed 's%.*encryption-provider-config[= ]\([^ ]*\).*%\1%') if test -e $ENCRYPTION_PROVIDER_CONFIG; then grep -A1 'providers:' $ENCRYPTION_PROVIDER_CONFIG | tail -n1 | grep -o "[A-Za-z]*" | sed 's/^/provider=/'; fi tests: test_items: - flag: "provider" compare: op: valid_elements value: "aescbc,kms,secretbox" remediation: | Follow the Kubernetes documentation and configure a EncryptionConfig file. In this file, choose aescbc, kms or secretbox as the encryption provider. Enabling encryption changes how data can be recovered as data is encrypted. scored: true - id: 1.2.32 text: "Ensure that the API Server only makes use of Strong Cryptographic Ciphers (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--tls-cipher-suites" compare: op: valid_elements value: "TLS_AES_128_GCM_SHA256,TLS_AES_256_GCM_SHA384,TLS_CHACHA20_POLY1305_SHA256,TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA,TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,TLS_RSA_WITH_3DES_EDE_CBC_SHA,TLS_RSA_WITH_AES_128_CBC_SHA,TLS_RSA_WITH_AES_128_GCM_SHA256,TLS_RSA_WITH_AES_256_CBC_SHA,TLS_RSA_WITH_AES_256_GCM_SHA384" remediation: | Edit the API server pod specification file /etc/kubernetes/manifests/kube-apiserver.yaml on the control plane node and set the below parameter. --tls-cipher-suites=TLS_AES_128_GCM_SHA256,TLS_AES_256_GCM_SHA384,TLS_CHACHA20_POLY1305_SHA256, TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA,TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305, TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,TLS_RSA_WITH_3DES_EDE_CBC_SHA,TLS_RSA_WITH_AES_128_CBC_SHA, TLS_RSA_WITH_AES_128_GCM_SHA256,TLS_RSA_WITH_AES_256_CBC_SHA,TLS_RSA_WITH_AES_256_GCM_SHA384 scored: true - id: 1.3 text: "Controller Manager" checks: - id: 1.3.1 text: "Ensure that the --terminated-pod-gc-threshold argument is set as appropriate (Automated)" audit: "/bin/ps -ef | grep $controllermanagerbin | grep -v grep" tests: test_items: - flag: "--terminated-pod-gc-threshold" set: true remediation: | Edit the Controller Manager pod specification file $controllermanagerconf on the control plane node and set the --terminated-pod-gc-threshold to an appropriate threshold, for example, --terminated-pod-gc-threshold=10 scored: true - id: 1.3.2 text: "Ensure that the --profiling argument is set to false (Automated)" audit: "/bin/ps -ef | grep $controllermanagerbin | grep -v grep" tests: test_items: - flag: "--profiling" compare: op: eq value: false set: true remediation: | Edit the Controller Manager pod specification file $controllermanagerconf on the control plane node and set the below parameter. --profiling=false scored: true - id: 1.3.3 text: "Ensure that the --use-service-account-credentials argument is set to true (Automated)" audit: "/bin/ps -ef | grep $controllermanagerbin | grep -v grep" tests: test_items: - flag: "--use-service-account-credentials" compare: op: noteq value: false set: true remediation: | Edit the Controller Manager pod specification file $controllermanagerconf on the control plane node to set the below parameter. --use-service-account-credentials=true scored: true - id: 1.3.4 text: "Ensure that the --service-account-private-key-file argument is set as appropriate (Automated)" audit: "/bin/ps -ef | grep $controllermanagerbin | grep -v grep" tests: test_items: - flag: "--service-account-private-key-file" set: true remediation: | Edit the Controller Manager pod specification file $controllermanagerconf on the control plane node and set the --service-account-private-key-file parameter to the private key file for service accounts. --service-account-private-key-file= scored: true - id: 1.3.5 text: "Ensure that the --root-ca-file argument is set as appropriate (Automated)" audit: "/bin/ps -ef | grep $controllermanagerbin | grep -v grep" tests: test_items: - flag: "--root-ca-file" set: true remediation: | Edit the Controller Manager pod specification file $controllermanagerconf on the control plane node and set the --root-ca-file parameter to the certificate bundle file`. --root-ca-file= scored: true - id: 1.3.6 text: "Ensure that the RotateKubeletServerCertificate argument is set to true (Automated)" audit: "/bin/ps -ef | grep $controllermanagerbin | grep -v grep" tests: bin_op: or test_items: - flag: "--feature-gates" compare: op: nothave value: "RotateKubeletServerCertificate=false" set: true - flag: "--feature-gates" set: false remediation: | Edit the Controller Manager pod specification file $controllermanagerconf on the control plane node and set the --feature-gates parameter to include RotateKubeletServerCertificate=true. --feature-gates=RotateKubeletServerCertificate=true Cluster provisioned by RKE handles certificate rotation directly through RKE. scored: true - id: 1.3.7 text: "Ensure that the --bind-address argument is set to 127.0.0.1 (Automated)" audit: "/bin/ps -ef | grep $controllermanagerbin | grep -v grep" tests: test_items: - flag: "--bind-address" compare: op: eq value: "127.0.0.1" set: true remediation: | Edit the Controller Manager pod specification file $controllermanagerconf on the control plane node and ensure the correct value for the --bind-address parameter scored: true - id: 1.4 text: "Scheduler" checks: - id: 1.4.1 text: "Ensure that the --profiling argument is set to false (Automated)" audit: "/bin/ps -ef | grep $schedulerbin | grep -v grep" tests: test_items: - flag: "--profiling" compare: op: eq value: false set: true remediation: | Edit the Scheduler pod specification file $schedulerconf file on the control plane node and set the below parameter. --profiling=false scored: true - id: 1.4.2 text: "Ensure that the --bind-address argument is set to 127.0.0.1 (Automated)" audit: "/bin/ps -ef | grep $schedulerbin | grep -v grep" tests: test_items: - flag: "--bind-address" compare: op: eq value: "127.0.0.1" set: true remediation: | Edit the Scheduler pod specification file $schedulerconf on the control plane node and ensure the correct value for the --bind-address parameter scored: true ================================================ FILE: cfg/rke-cis-1.24/node.yaml ================================================ --- controls: version: "rke-cis-1.24" id: 4 text: "Worker Node Security Configuration" type: "node" groups: - id: 4.1 text: "Worker Node Configuration Files" checks: - id: 4.1.1 text: "Ensure that the kubelet service file permissions are set to 600 or more restrictive (Automated)" audit: '/bin/sh -c ''if test -e $kubeletsvc; then stat -c permissions=%a $kubeletsvc; fi'' ' tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Run the below command (based on the file location on your system) on the each worker node. For example, chmod 600 $kubeletsvc scored: true - id: 4.1.2 text: "Ensure that the kubelet service file ownership is set to root:root (Automated)" type: "skip" audit: '/bin/sh -c ''if test -e $kubeletsvc; then stat -c %U:%G $kubeletsvc; fi'' ' tests: test_items: - flag: root:root remediation: | Run the below command (based on the file location on your system) on the each worker node. For example, chown root:root $kubeletsvc scored: true - id: 4.1.3 text: "If proxy kubeconfig file exists ensure permissions are set to 600 or more restrictive (Automated)" audit: '/bin/sh -c ''if test -e $proxykubeconfig; then stat -c permissions=%a $proxykubeconfig; fi'' ' tests: bin_op: or test_items: - flag: "permissions" set: true compare: op: bitmask value: "600" remediation: | Run the below command (based on the file location on your system) on the each worker node. For example, chmod 600 $proxykubeconfig scored: true - id: 4.1.4 text: "If proxy kubeconfig file exists ensure ownership is set to root:root (Automated)" audit: '/bin/sh -c ''if test -e $proxykubeconfig; then stat -c %U:%G $proxykubeconfig; fi'' ' tests: test_items: - flag: root:root remediation: | Run the below command (based on the file location on your system) on the each worker node. For example, chown root:root $proxykubeconfig scored: true - id: 4.1.5 text: "Ensure that the --kubeconfig kubelet.conf file permissions are set to 600 or more restrictive (Automated)" audit: '/bin/sh -c ''if test -e $kubeletkubeconfig; then stat -c permissions=%a $kubeletkubeconfig; fi'' ' tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Run the below command (based on the file location on your system) on the each worker node. For example, chmod 600 $kubeletkubeconfig scored: true - id: 4.1.6 text: "Ensure that the --kubeconfig kubelet.conf file ownership is set to root:root (Automated)" audit: '/bin/sh -c ''if test -e $kubeletkubeconfig; then stat -c %U:%G $kubeletkubeconfig; fi'' ' tests: test_items: - flag: root:root remediation: | Run the below command (based on the file location on your system) on the each worker node. For example, chown root:root $kubeletkubeconfig scored: true - id: 4.1.7 text: "Ensure that the certificate authorities file permissions are set to 600 or more restrictive (Automated)" audit: '/bin/sh -c "if test -e /etc/kubernetes/ssl/kube-ca.pem; then stat -c permissions=%a /etc/kubernetes/ssl/kube-ca.pem; else echo \"File not found\"; fi"' tests: bin_op: or test_items: - flag: "permissions" compare: op: bitmask value: "600" - flag: "File not found" remediation: | Run the following command to modify the file permissions of the --client-ca-file chmod 600 scored: true - id: 4.1.8 text: "Ensure that the client certificate authorities file ownership is set to root:root (Automated)" audit: '/bin/sh -c "if test -e /etc/kubernetes/ssl/kube-ca.pem; then stat -c %U:%G /etc/kubernetes/ssl/kube-ca.pem; else echo \"File not found\"; fi"' tests: bin_op: or test_items: - flag: root:root - flag: "File not found" remediation: | Run the following command to modify the ownership of the --client-ca-file. chown root:root scored: true - id: 4.1.9 text: "If the kubelet config.yaml configuration file is being used validate permissions set to 600 or more restrictive (Automated)" audit: '/bin/sh -c ''if test -e $kubeletconf; then stat -c permissions=%a $kubeletconf; fi'' ' tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Cluster provisioned by RKE doesn't require or maintain a configuration file for the kubelet. All configuration is passed in as arguments at container run time. scored: true - id: 4.1.10 text: "If the kubelet config.yaml configuration file is being used validate file ownership is set to root:root (Automated)" audit: '/bin/sh -c ''if test -e $kubeletconf; then stat -c %U:%G $kubeletconf; fi'' ' tests: test_items: - flag: root:root remediation: | Cluster provisioned by RKE doesn’t require or maintain a configuration file for the kubelet. All configuration is passed in as arguments at container run time. scored: true - id: 4.2 text: "Kubelet" checks: - id: 4.2.1 text: "Ensure that the --anonymous-auth argument is set to false (Automated)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/sh -c 'if test -e $kubeletconf; then /bin/cat $kubeletconf; fi' " tests: test_items: - flag: "--anonymous-auth" path: '{.authentication.anonymous.enabled}' compare: op: eq value: false remediation: | If using a Kubelet config file, edit the file to set `authentication: anonymous: enabled` to `false`. If using executable arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_SYSTEM_PODS_ARGS variable. `--anonymous-auth=false` Based on your system, restart the kubelet service. For example, systemctl daemon-reload systemctl restart kubelet.service scored: true - id: 4.2.2 text: "Ensure that the --authorization-mode argument is not set to AlwaysAllow (Automated)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/sh -c 'if test -e $kubeletconf; then /bin/cat $kubeletconf; fi' " tests: test_items: - flag: --authorization-mode path: '{.authorization.mode}' compare: op: nothave value: AlwaysAllow remediation: | If using a Kubelet config file, edit the file to set `authorization.mode` to Webhook. If using executable arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_AUTHZ_ARGS variable. --authorization-mode=Webhook Based on your system, restart the kubelet service. For example, systemctl daemon-reload systemctl restart kubelet.service scored: true - id: 4.2.3 text: "Ensure that the --client-ca-file argument is set as appropriate (Automated)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/sh -c 'if test -e $kubeletconf; then /bin/cat $kubeletconf; fi' " tests: test_items: - flag: --client-ca-file path: '{.authentication.x509.clientCAFile}' remediation: | If using a Kubelet config file, edit the file to set `authentication.x509.clientCAFile` to the location of the client CA file. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_AUTHZ_ARGS variable. --client-ca-file= Based on your system, restart the kubelet service. For example, systemctl daemon-reload systemctl restart kubelet.service scored: true - id: 4.2.4 text: "Verify that the --read-only-port argument is set to 0 (Automated)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/sh -c 'if test -e $kubeletconf; then /bin/cat $kubeletconf; fi' " tests: bin_op: or test_items: - flag: "--read-only-port" path: '{.readOnlyPort}' compare: op: eq value: 0 - flag: "--read-only-port" path: '{.readOnlyPort}' set: false remediation: | If using a Kubelet config file, edit the file to set `readOnlyPort` to 0. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_SYSTEM_PODS_ARGS variable. --read-only-port=0 Based on your system, restart the kubelet service. For example, systemctl daemon-reload systemctl restart kubelet.service scored: true - id: 4.2.5 text: "Ensure that the --streaming-connection-idle-timeout argument is not set to 0 (Automated)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/sh -c 'if test -e $kubeletconf; then /bin/cat $kubeletconf; fi' " tests: test_items: - flag: --streaming-connection-idle-timeout path: '{.streamingConnectionIdleTimeout}' compare: op: noteq value: 0 - flag: --streaming-connection-idle-timeout path: '{.streamingConnectionIdleTimeout}' set: false bin_op: or remediation: | If using a Kubelet config file, edit the file to set `streamingConnectionIdleTimeout` to a value other than 0. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_SYSTEM_PODS_ARGS variable. --streaming-connection-idle-timeout=5m Based on your system, restart the kubelet service. For example, systemctl daemon-reload systemctl restart kubelet.service scored: true - id: 4.2.6 text: "Ensure that the --protect-kernel-defaults argument is set to true (Automated)" type: "skip" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/sh -c 'if test -e $kubeletconf; then /bin/cat $kubeletconf; fi' " tests: test_items: - flag: --protect-kernel-defaults path: '{.protectKernelDefaults}' compare: op: eq value: true remediation: | If using a Kubelet config file, edit the file to set `protectKernelDefaults` to `true`. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_SYSTEM_PODS_ARGS variable. --protect-kernel-defaults=true Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service System level configurations are required prior to provisioning the cluster in order for this argument to be set to true. scored: true - id: 4.2.7 text: "Ensure that the --make-iptables-util-chains argument is set to true (Automated)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/sh -c 'if test -e $kubeletconf; then /bin/cat $kubeletconf; fi' " tests: test_items: - flag: --make-iptables-util-chains path: '{.makeIPTablesUtilChains}' compare: op: eq value: true - flag: --make-iptables-util-chains path: '{.makeIPTablesUtilChains}' set: false bin_op: or remediation: | If using a Kubelet config file, edit the file to set `makeIPTablesUtilChains` to `true`. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and remove the --make-iptables-util-chains argument from the KUBELET_SYSTEM_PODS_ARGS variable. Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: true - id: 4.2.8 text: "Ensure that the --hostname-override argument is not set (Manual)" # This is one of those properties that can only be set as a command line argument. # To check if the property is set as expected, we need to parse the kubelet command # instead reading the Kubelet Configuration file. type: "skip" audit: "/bin/ps -fC $kubeletbin " tests: test_items: - flag: --hostname-override set: false remediation: | Edit the kubelet service file $kubeletsvc on each worker node and remove the --hostname-override argument from the KUBELET_SYSTEM_PODS_ARGS variable. Based on your system, restart the kubelet service. For example, systemctl daemon-reload systemctl restart kubelet.service Clusters provisioned by RKE set the --hostname-override to avoid any hostname configuration errors scored: false - id: 4.2.9 text: "Ensure that the eventRecordQPS argument is set to a level which ensures appropriate event capture (Automated)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/sh -c 'if test -e $kubeletconf; then /bin/cat $kubeletconf; fi' " tests: test_items: - flag: --event-qps path: '{.eventRecordQPS}' compare: op: eq value: 0 remediation: | If using a Kubelet config file, edit the file to set `eventRecordQPS` to an appropriate level. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_SYSTEM_PODS_ARGS variable. Based on your system, restart the kubelet service. For example, systemctl daemon-reload systemctl restart kubelet.service scored: true - id: 4.2.10 text: "Ensure that the --tls-cert-file and --tls-private-key-file arguments are set as appropriate (Automated)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/sh -c 'if test -e $kubeletconf; then /bin/cat $kubeletconf; fi' " tests: test_items: - flag: --tls-cert-file path: '{.tlsCertFile}' - flag: --tls-private-key-file path: '{.tlsPrivateKeyFile}' remediation: | If using a Kubelet config file, edit the file to set `tlsCertFile` to the location of the certificate file to use to identify this Kubelet, and `tlsPrivateKeyFile` to the location of the corresponding private key file. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameters in KUBELET_CERTIFICATE_ARGS variable. --tls-cert-file= --tls-private-key-file= Based on your system, restart the kubelet service. For example, systemctl daemon-reload systemctl restart kubelet.service When generating serving certificates, functionality could break in conjunction with hostname overrides which are required for certain cloud providers. scored: true - id: 4.2.11 text: "Ensure that the --rotate-certificates argument is not set to false (Automated)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/sh -c 'if test -e $kubeletconf; then /bin/cat $kubeletconf; fi' " tests: test_items: - flag: --rotate-certificates path: '{.rotateCertificates}' compare: op: eq value: true - flag: --rotate-certificates path: '{.rotateCertificates}' set: false bin_op: or remediation: | If using a Kubelet config file, edit the file to add the line `rotateCertificates` to `true` or remove it altogether to use the default value. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and remove --rotate-certificates=false argument from the KUBELET_CERTIFICATE_ARGS variable. Based on your system, restart the kubelet service. For example, systemctl daemon-reload systemctl restart kubelet.service scored: true - id: 4.2.12 text: "Verify that the RotateKubeletServerCertificate argument is set to true (Manual)" type: "skip" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/sh -c 'if test -e $kubeletconf; then /bin/cat $kubeletconf; fi' " tests: bin_op: or test_items: - flag: RotateKubeletServerCertificate path: '{.featureGates.RotateKubeletServerCertificate}' compare: op: nothave value: false - flag: RotateKubeletServerCertificate path: '{.featureGates.RotateKubeletServerCertificate}' set: false remediation: | Edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_CERTIFICATE_ARGS variable. --feature-gates=RotateKubeletServerCertificate=true Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service Clusters provisioned by RKE handles certificate rotation directly through RKE. scored: false - id: 4.2.13 text: "Ensure that the Kubelet only makes use of Strong Cryptographic Ciphers (Automated)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/sh -c 'if test -e $kubeletconf; then /bin/cat $kubeletconf; fi' " tests: test_items: - flag: --tls-cipher-suites path: '{range .tlsCipherSuites[:]}{}{'',''}{end}' compare: op: valid_elements value: 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 remediation: | If using a Kubelet config file, edit the file to set `tlsCipherSuites` to 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 or to a subset of these values. If using executable arguments, edit the kubelet service file $kubeletsvc on each worker node and set the --tls-cipher-suites parameter as follows, or to a subset of these values. --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 Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: true ================================================ FILE: cfg/rke-cis-1.24/policies.yaml ================================================ --- controls: version: "rke-cis-1.24" id: 5 text: "Kubernetes Policies" type: "policies" groups: - id: 5.1 text: "RBAC and Service Accounts" checks: - id: 5.1.1 text: "Ensure that the cluster-admin role is only used where required (Manual)" type: "manual" remediation: | Identify all clusterrolebindings to the cluster-admin role. Check if they are used and if they need this role or if they could use a role with fewer privileges. Where possible, first bind users to a lower privileged role and then remove the clusterrolebinding to the cluster-admin role : kubectl delete clusterrolebinding [name] scored: false - id: 5.1.2 text: "Minimize access to secrets (Manual)" type: "manual" remediation: | Where possible, remove get, list and watch access to Secret objects in the cluster. scored: false - id: 5.1.3 text: "Minimize wildcard use in Roles and ClusterRoles (Manual)" type: "manual" remediation: | Where possible replace any use of wildcards in clusterroles and roles with specific objects or actions. scored: false - id: 5.1.4 text: "Minimize access to create pods (Manual)" type: "manual" remediation: | Where possible, remove create access to pod objects in the cluster. scored: false - id: 5.1.5 text: "Ensure that default service accounts are not actively used. (Manual)" type: "manual" remediation: | Create explicit service accounts wherever a Kubernetes workload requires specific access to the Kubernetes API server. Modify the configuration of each default service account to include this value automountServiceAccountToken: false scored: false - id: 5.1.6 text: "Ensure that Service Account Tokens are only mounted where necessary (Manual)" type: "manual" remediation: | Modify the definition of pods and service accounts which do not need to mount service account tokens to disable it. scored: false - id: 5.1.7 text: "Avoid use of system:masters group (Manual)" type: "manual" remediation: | Remove the system:masters group from all users in the cluster. scored: false - id: 5.1.8 text: "Limit use of the Bind, Impersonate and Escalate permissions in the Kubernetes cluster (Manual)" type: "manual" remediation: | Where possible, remove the impersonate, bind and escalate rights from subjects. scored: false - id: 5.2 text: "Pod Security Standards" checks: - id: 5.2.1 text: "Ensure that the cluster has at least one active policy control mechanism in place (Manual)" type: "manual" remediation: | Ensure that either Pod Security Admission or an external policy control system is in place for every namespace which contains user workloads. scored: false - id: 5.2.2 text: "Minimize the admission of privileged containers (Manual)" type: "manual" remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of privileged containers. scored: false - id: 5.2.3 text: "Minimize the admission of containers wishing to share the host process ID namespace (Automated)" audit: | kubectl get psp -o json | jq .items[] | jq -r 'select((.spec.hostPID == null) or (.spec.hostPID == false))' | jq .metadata.name | wc -l | xargs -I {} echo '--count={}' tests: bin_op: or test_items: - flag: "kubectl: not found" - flag: "jq: not found" - flag: count compare: op: gt value: 0 remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of `hostPID` containers. scored: true - id: 5.2.4 text: "Minimize the admission of containers wishing to share the host IPC namespace (Automated)" audit: | kubectl get psp -o json | jq .items[] | jq -r 'select((.spec.hostIPC == null) or (.spec.hostIPC == false))' | jq .metadata.name | wc -l | xargs -I {} echo '--count={}' tests: bin_op: or test_items: - flag: "kubectl: not found" - flag: "jq: not found" - flag: count compare: op: gt value: 0 remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of `hostIPC` containers. scored: true - id: 5.2.5 text: "Minimize the admission of containers wishing to share the host network namespace (Automated)" audit: | kubectl get psp -o json | jq .items[] | jq -r 'select((.spec.hostNetwork == null) or (.spec.hostNetwork == false))' | jq .metadata.name | wc -l | xargs -I {} echo '--count={}' tests: bin_op: or test_items: - flag: "kubectl: not found" - flag: "jq: not found" - flag: count compare: op: gt value: 0 remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of `hostNetwork` containers. scored: true - id: 5.2.6 text: "Minimize the admission of containers with allowPrivilegeEscalation (Automated)" audit: | kubectl get psp -o json | jq .items[] | jq -r 'select((.spec.allowPrivilegeEscalation == null) or (.spec.allowPrivilegeEscalation == false))' | jq .metadata.name | wc -l | xargs -I {} echo '--count={}' tests: bin_op: or test_items: - flag: "kubectl: not found" - flag: "jq: not found" - flag: count compare: op: gt value: 0 remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of containers with `.spec.allowPrivilegeEscalation` set to `true`. scored: true - id: 5.2.7 text: "Minimize the admission of root containers (Manual)" type: "manual" remediation: | Create a policy for each namespace in the cluster, ensuring that either `MustRunAsNonRoot` or `MustRunAs` with the range of UIDs not including 0, is set. scored: false - id: 5.2.8 text: "Minimize the admission of containers with the NET_RAW capability (Manual)" type: "manual" remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of containers with the `NET_RAW` capability. scored: false - id: 5.2.9 text: "Minimize the admission of containers with added capabilities (Manual)" type: "manual" remediation: | Ensure that `allowedCapabilities` is not present in policies for the cluster unless it is set to an empty array. scored: false - id: 5.2.10 text: "Minimize the admission of containers with capabilities assigned (Manual)" type: "manual" remediation: | Review the use of capabilites in applications running on your cluster. Where a namespace contains applications which do not require any Linux capabities to operate consider adding a PSP which forbids the admission of containers which do not drop all capabilities. scored: false - id: 5.2.11 text: "Minimize the admission of Windows HostProcess containers (Manual)" type: "manual" remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of containers that have `.securityContext.windowsOptions.hostProcess` set to `true`. scored: false - id: 5.2.12 text: "Minimize the admission of HostPath volumes (Manual)" type: "manual" remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of containers with `hostPath` volumes. scored: false - id: 5.2.13 text: "Minimize the admission of containers which use HostPorts (Manual)" type: "manual" remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of containers which use `hostPort` sections. scored: false - id: 5.3 text: "Network Policies and CNI" checks: - id: 5.3.1 text: "Ensure that the CNI in use supports NetworkPolicies (Manual)" type: "manual" remediation: | If the CNI plugin in use does not support network policies, consideration should be given to making use of a different plugin, or finding an alternate mechanism for restricting traffic in the Kubernetes cluster. scored: false - id: 5.3.2 text: "Ensure that all Namespaces have NetworkPolicies defined (Manual)" type: "skip" remediation: | Follow the documentation and create NetworkPolicy objects as you need them. scored: false - id: 5.4 text: "Secrets Management" checks: - id: 5.4.1 text: "Prefer using Secrets as files over Secrets as environment variables (Manual)" type: "manual" remediation: | If possible, rewrite application code to read Secrets from mounted secret files, rather than from environment variables. scored: false - id: 5.4.2 text: "Consider external secret storage (Manual)" type: "manual" remediation: | Refer to the Secrets management options offered by your cloud provider or a third-party secrets management solution. scored: false - id: 5.5 text: "Extensible Admission Control" checks: - id: 5.5.1 text: "Configure Image Provenance using ImagePolicyWebhook admission controller (Manual)" type: "manual" remediation: | Follow the Kubernetes documentation and setup image provenance. scored: false - id: 5.7 text: "General Policies" checks: - id: 5.7.1 text: "Create administrative boundaries between resources using namespaces (Manual)" type: "manual" remediation: | Follow the documentation and create namespaces for objects in your deployment as you need them. scored: false - id: 5.7.2 text: "Ensure that the seccomp profile is set to docker/default in your Pod definitions (Manual)" type: "manual" remediation: | Use `securityContext` to enable the docker/default seccomp profile in your pod definitions. An example is as below: securityContext: seccompProfile: type: RuntimeDefault scored: false - id: 5.7.3 text: "Apply SecurityContext to your Pods and Containers (Manual)" type: "manual" remediation: | Follow the Kubernetes documentation and apply SecurityContexts to your Pods. For a suggested list of SecurityContexts, you may refer to the CIS Security Benchmark for Docker Containers. scored: false - id: 5.7.4 text: "The default namespace should not be used (Automated)" audit: | #!/bin/bash set -eE handle_error() { echo "false" } trap 'handle_error' ERR count=$(kubectl get all -n default -o json | jq .items[] | jq -r 'select((.metadata.name!="kubernetes"))' | jq .metadata.name | wc -l) if [[ ${count} -gt 0 ]]; then echo "false" exit fi echo "true" tests: bin_op: or test_items: - flag: "kubectl: not found" - flag: "jq: not found" - flag: "true" remediation: | Ensure that namespaces are created to allow for appropriate segregation of Kubernetes resources and that all new resources are created in a specific namespace. scored: true ================================================ FILE: cfg/rke-cis-1.7/config.yaml ================================================ --- ## Version-specific settings that override the values in cfg/config.yaml ================================================ FILE: cfg/rke-cis-1.7/controlplane.yaml ================================================ --- controls: version: "rke-cis-1.7" id: 3 text: "Control Plane Configuration" type: "controlplane" groups: - id: 3.1 text: "Authentication and Authorization" checks: - id: 3.1.1 text: "Client certificate authentication should not be used for users (Manual)" type: "manual" remediation: | Alternative mechanisms provided by Kubernetes such as the use of OIDC should be implemented in place of client certificates. scored: false - id: 3.1.2 text: "Service account token authentication should not be used for users (Manual)" type: "manual" remediation: | Alternative mechanisms provided by Kubernetes such as the use of OIDC should be implemented in place of service account tokens. scored: false - id: 3.1.3 text: "Bootstrap token authentication should not be used for users (Manual)" type: "manual" remediation: | Alternative mechanisms provided by Kubernetes such as the use of OIDC should be implemented in place of bootstrap tokens. scored: false - id: 3.2 text: "Logging" checks: - id: 3.2.1 text: "Ensure that a minimal audit policy is created (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--audit-policy-file" set: true remediation: | Create an audit policy file for your cluster. scored: true - id: 3.2.2 text: "Ensure that the audit policy covers key security concerns (Manual)" type: "manual" remediation: | Review the audit policy provided for the cluster and ensure that it covers at least the following areas, - Access to Secrets managed by the cluster. Care should be taken to only log Metadata for requests to Secrets, ConfigMaps, and TokenReviews, in order to avoid risk of logging sensitive data. - Modification of Pod and Deployment objects. - Use of `pods/exec`, `pods/portforward`, `pods/proxy` and `services/proxy`. For most requests, minimally logging at the Metadata level is recommended (the most basic level of logging). scored: false ================================================ FILE: cfg/rke-cis-1.7/etcd.yaml ================================================ --- controls: version: "rke-cis-1.7" id: 2 text: "Etcd Node Configuration" type: "etcd" groups: - id: 2 text: "Etcd Node Configuration" checks: - id: 2.1 text: "Ensure that the --cert-file and --key-file arguments are set as appropriate (Automated)" audit: "/bin/ps -ef | /bin/grep $etcdbin | /bin/grep -v grep" tests: bin_op: and test_items: - flag: "--cert-file" env: "ETCD_CERT_FILE" - flag: "--key-file" env: "ETCD_KEY_FILE" remediation: | Follow the etcd service documentation and configure TLS encryption. Then, edit the etcd pod specification file /etc/kubernetes/manifests/etcd.yaml on the master node and set the below parameters. --cert-file= --key-file= scored: true - id: 2.2 text: "Ensure that the --client-cert-auth argument is set to true (Automated)" audit: "/bin/ps -ef | /bin/grep $etcdbin | /bin/grep -v grep" tests: test_items: - flag: "--client-cert-auth" env: "ETCD_CLIENT_CERT_AUTH" compare: op: eq value: true remediation: | Edit the etcd pod specification file $etcdconf on the master node and set the below parameter. --client-cert-auth="true" scored: true - id: 2.3 text: "Ensure that the --auto-tls argument is not set to true (Automated)" audit: "/bin/ps -ef | /bin/grep $etcdbin | /bin/grep -v grep" tests: bin_op: or test_items: - flag: "--auto-tls" env: "ETCD_AUTO_TLS" set: false - flag: "--auto-tls" env: "ETCD_AUTO_TLS" compare: op: eq value: false remediation: | Edit the etcd pod specification file $etcdconf on the master node and either remove the --auto-tls parameter or set it to false. --auto-tls=false scored: true - id: 2.4 text: "Ensure that the --peer-cert-file and --peer-key-file arguments are set as appropriate (Automated)" audit: "/bin/ps -ef | /bin/grep $etcdbin | /bin/grep -v grep" tests: bin_op: and test_items: - flag: "--peer-cert-file" env: "ETCD_PEER_CERT_FILE" - flag: "--peer-key-file" env: "ETCD_PEER_KEY_FILE" remediation: | Follow the etcd service documentation and configure peer TLS encryption as appropriate for your etcd cluster. Then, edit the etcd pod specification file $etcdconf on the master node and set the below parameters. --peer-client-file= --peer-key-file= scored: true - id: 2.5 text: "Ensure that the --peer-client-cert-auth argument is set to true (Automated)" audit: "/bin/ps -ef | /bin/grep $etcdbin | /bin/grep -v grep" tests: test_items: - flag: "--peer-client-cert-auth" env: "ETCD_PEER_CLIENT_CERT_AUTH" compare: op: eq value: true remediation: | Edit the etcd pod specification file $etcdconf on the master node and set the below parameter. --peer-client-cert-auth=true scored: true - id: 2.6 text: "Ensure that the --peer-auto-tls argument is not set to true (Automated)" audit: "/bin/ps -ef | /bin/grep $etcdbin | /bin/grep -v grep" tests: bin_op: or test_items: - flag: "--peer-auto-tls" env: "ETCD_PEER_AUTO_TLS" set: false - flag: "--peer-auto-tls" env: "ETCD_PEER_AUTO_TLS" compare: op: eq value: false remediation: | Edit the etcd pod specification file $etcdconf on the master node and either remove the --peer-auto-tls parameter or set it to false. --peer-auto-tls=false scored: true - id: 2.7 text: "Ensure that a unique Certificate Authority is used for etcd (Automated)" audit: "/bin/ps -ef | /bin/grep $etcdbin | /bin/grep -v grep" tests: test_items: - flag: "--trusted-ca-file" env: "ETCD_TRUSTED_CA_FILE" set: true remediation: | [Manual test] Follow the etcd documentation and create a dedicated certificate authority setup for the etcd service. Then, edit the etcd pod specification file $etcdconf on the master node and set the below parameter. --trusted-ca-file= scored: true ================================================ FILE: cfg/rke-cis-1.7/master.yaml ================================================ --- controls: version: "rke-cis-1.7" id: 1 text: "Control Plane Security Configuration" type: "master" groups: - id: 1.1 text: "Control Plane Node Configuration Files" checks: - id: 1.1.1 text: "Ensure that the API server pod specification file permissions are set to 600 or more restrictive (Automated)" type: "skip" audit: "/bin/sh -c 'if test -e $apiserverconf; then stat -c permissions=%a $apiserverconf; fi'" tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chmod 600 $apiserverconf Not Applicable - Cluster provisioned by RKE doesn't require or maintain a configuration file for kube-apiserver. All configuration is passed in as arguments at container run time. scored: true - id: 1.1.2 text: "Ensure that the API server pod specification file ownership is set to root:root (Automated)" type: "skip" audit: "/bin/sh -c 'if test -e $apiserverconf; then stat -c %U:%G $apiserverconf; fi'" tests: test_items: - flag: "root:root" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chown root:root $apiserverconf Not Applicable - Cluster provisioned by RKE doesn't require or maintain a configuration file for kube-apiserver. All configuration is passed in as arguments at container run time. scored: true - id: 1.1.3 text: "Ensure that the controller manager pod specification file permissions are set to 600 or more restrictive (Automated)" type: "skip" audit: "/bin/sh -c 'if test -e $controllermanagerconf; then stat -c permissions=%a $controllermanagerconf; fi'" tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chmod 600 $controllermanagerconf Not Applicable - Cluster provisioned by RKE doesn't require or maintain a configuration file for kube-apiserver. All configuration is passed in as arguments at container run time. scored: true - id: 1.1.4 text: "Ensure that the controller manager pod specification file ownership is set to root:root (Automated)" type: "skip" audit: "/bin/sh -c 'if test -e $controllermanagerconf; then stat -c %U:%G $controllermanagerconf; fi'" tests: test_items: - flag: "root:root" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chown root:root $controllermanagerconf Not Applicable - Cluster provisioned by RKE doesn't require or maintain a configuration file for kube-apiserver. All configuration is passed in as arguments at container run time. scored: true - id: 1.1.5 text: "Ensure that the scheduler pod specification file permissions are set to 600 or more restrictive (Automated)" type: "skip" audit: "/bin/sh -c 'if test -e $schedulerconf; then stat -c permissions=%a $schedulerconf; fi'" tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chmod 600 $schedulerconf Not Applicable - Cluster provisioned by RKE doesn't require or maintain a configuration file for kube-apiserver. All configuration is passed in as arguments at container run time. scored: true - id: 1.1.6 text: "Ensure that the scheduler pod specification file ownership is set to root:root (Automated)" type: "skip" audit: "/bin/sh -c 'if test -e $schedulerconf; then stat -c %U:%G $schedulerconf; fi'" tests: test_items: - flag: "root:root" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chown root:root $schedulerconf Not Applicable - Cluster provisioned by RKE doesn't require or maintain a configuration file for kube-apiserver. All configuration is passed in as arguments at container run time. scored: true - id: 1.1.7 text: "Ensure that the etcd pod specification file permissions are set to 600 or more restrictive (Automated)" type: "skip" audit: "/bin/sh -c 'if test -e $etcdconf; then find $etcdconf -name '*etcd*' | xargs stat -c permissions=%a; fi'" use_multiple_values: true tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chmod 600 $etcdconf Not Applicable - Cluster provisioned by RKE doesn't require or maintain a configuration file for kube-apiserver. All configuration is passed in as arguments at container run time. scored: true - id: 1.1.8 text: "Ensure that the etcd pod specification file ownership is set to root:root (Automated)" type: "skip" audit: "/bin/sh -c 'if test -e $etcdconf; then find $etcdconf -name '*etcd*' | xargs stat -c %U:%G; fi'" use_multiple_values: true tests: test_items: - flag: "root:root" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chown root:root $etcdconf Not Applicable - Cluster provisioned by RKE doesn't require or maintain a configuration file for kube-apiserver. All configuration is passed in as arguments at container run time. scored: true - id: 1.1.9 text: "Ensure that the Container Network Interface file permissions are set to 600 or more restrictive (Manual)" audit: | ps -ef | grep $kubeletbin | grep -- --cni-conf-dir | sed 's%.*cni-conf-dir[= ]\([^ ]*\).*%\1%' | xargs -I{} find {} -mindepth 1 | xargs --no-run-if-empty stat -c permissions=%a find /var/lib/cni/networks -type f 2> /dev/null | xargs --no-run-if-empty stat -c permissions=%a use_multiple_values: true tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chmod 600 scored: false - id: 1.1.10 text: "Ensure that the Container Network Interface file ownership is set to root:root (Manual)" audit: | ps -ef | grep $kubeletbin | grep -- --cni-conf-dir | sed 's%.*cni-conf-dir[= ]\([^ ]*\).*%\1%' | xargs -I{} find {} -mindepth 1 | xargs --no-run-if-empty stat -c %U:%G find /var/lib/cni/networks -type f 2> /dev/null | xargs --no-run-if-empty stat -c %U:%G use_multiple_values: true tests: test_items: - flag: "root:root" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chown root:root scored: false - id: 1.1.11 text: "Ensure that the etcd data directory permissions are set to 700 or more restrictive (Automated)" audit: '/bin/sh -c "if test -e /var/lib/etcd; then stat -c %a /var/lib/etcd; else echo \"Directory not found\"; fi"' tests: bin_op: or test_items: - flag: "700" - flag: "Directory not found" remediation: | On the etcd server node, get the etcd data directory, passed as an argument --data-dir, from the command 'ps -ef | grep etcd'. Run the below command (based on the etcd data directory found above). For example, chmod 700 /var/lib/etcd scored: true - id: 1.1.12 text: "Ensure that the etcd data directory ownership is set to etcd:etcd (Automated)" type: "skip" audit: "stat -c %U:%G /node/var/lib/etcd" tests: test_items: - flag: "etcd:etcd" set: true remediation: | On the etcd server node, get the etcd data directory, passed as an argument --data-dir, from the command 'ps -ef | grep etcd'. Run the below command (based on the etcd data directory found above). For example, chown etcd:etcd /var/lib/etcd Permissive - A system service account is required for etcd data directory ownership. Refer to Rancher's hardening guide for more details on how to configure this ownership. scored: true - id: 1.1.13 text: "Ensure that the admin.conf file permissions are set to 600 or more restrictive (Automated)" type: "skip" audit: "/bin/sh -c 'if test -e /etc/kubernetes/admin.conf; then stat -c permissions=%a /etc/kubernetes/admin.conf; fi'" tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chmod 600 /etc/kubernetes/admin.conf Not Applicable - Cluster provisioned by RKE does not store the kubernetes default kubeconfig credentials file on the nodes. scored: true - id: 1.1.14 text: "Ensure that the admin.conf file ownership is set to root:root (Automated)" type: "skip" audit: "/bin/sh -c 'if test -e /etc/kubernetes/admin.conf; then stat -c %U:%G /etc/kubernetes/admin.conf; fi'" tests: test_items: - flag: "root:root" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chown root:root /etc/kubernetes/admin.conf Not Applicable - Cluster provisioned by RKE does not store the kubernetes default kubeconfig credentials file on the nodes. scored: true - id: 1.1.15 text: "Ensure that the scheduler.conf file permissions are set to 600 or more restrictive (Automated)" type: "skip" audit: "/bin/sh -c 'if test -e $schedulerkubeconfig; then stat -c permissions=%a $schedulerkubeconfig; fi'" tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chmod 600 $schedulerkubeconfig Not Applicable - Cluster provisioned by RKE doesn't require or maintain a configuration file for scheduler. All configuration is passed in as arguments at container run time. scored: true - id: 1.1.16 text: "Ensure that the scheduler.conf file ownership is set to root:root (Automated)" type: "skip" audit: "/bin/sh -c 'if test -e $schedulerkubeconfig; then stat -c %U:%G $schedulerkubeconfig; fi'" tests: test_items: - flag: "root:root" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chown root:root $schedulerkubeconfig Not Applicable - Cluster provisioned by RKE doesn't require or maintain a configuration file for scheduler. All configuration is passed in as arguments at container run time. scored: true - id: 1.1.17 text: "Ensure that the controller-manager.conf file permissions are set to 600 or more restrictive (Automated)" type: "skip" audit: "/bin/sh -c 'if test -e $controllermanagerkubeconfig; then stat -c permissions=%a $controllermanagerkubeconfig; fi'" tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chmod 600 $controllermanagerkubeconfig Not Applicable - Cluster provisioned by RKE doesn't require or maintain a configuration file for controller-manager. All configuration is passed in as arguments at container run time. scored: true - id: 1.1.18 text: "Ensure that the controller-manager.conf file ownership is set to root:root (Automated)" type: "skip" audit: "/bin/sh -c 'if test -e $controllermanagerkubeconfig; then stat -c %U:%G $controllermanagerkubeconfig; fi'" tests: test_items: - flag: "root:root" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chown root:root $controllermanagerkubeconfig Not Applicable - Cluster provisioned by RKE doesn't require or maintain a configuration file for controller-manager. All configuration is passed in as arguments at container run time. scored: true - id: 1.1.19 text: "Ensure that the Kubernetes PKI directory and file ownership is set to root:root (Automated)" audit: "check_files_owner_in_dir.sh /etc/kubernetes/ssl" tests: test_items: - flag: "true" compare: op: eq value: "true" set: true remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chown -R root:root /etc/kubernetes/ssl/ scored: true - id: 1.1.20 text: "Ensure that the Kubernetes PKI certificate file permissions are set to 600 or more restrictive (Manual)" audit: "find /etc/kubernetes/ssl/ -name '*.pem' ! -name '*key.pem' | xargs stat -c permissions=%a" use_multiple_values: true tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, find /etc/kubernetes/ssl/ -name '*.pem' ! -name '*key.pem' -exec chmod -R 600 {} + scored: false - id: 1.1.21 text: "Ensure that the Kubernetes PKI key file permissions are set to 600 (Manual)" audit: "find /etc/kubernetes/ssl/ -name '*key.pem' | xargs stat -c permissions=%a" use_multiple_values: true tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, find /etc/kubernetes/ssl/ -name '*key.pem' -exec chmod -R 600 {} + scored: false - id: 1.2 text: "API Server" checks: - id: 1.2.1 text: "Ensure that the --anonymous-auth argument is set to false (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--anonymous-auth" compare: op: eq value: false set: true remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the below parameter. --anonymous-auth=false scored: true - id: 1.2.2 text: "Ensure that the --token-auth-file parameter is not set (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--token-auth-file" set: false remediation: | Follow the documentation and configure alternate mechanisms for authentication. Then, edit the API server pod specification file $apiserverconf on the control plane node and remove the --token-auth-file= parameter. scored: true - id: 1.2.3 text: "Ensure that the --DenyServiceExternalIPs is not set (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: bin_op: or test_items: - flag: "--enable-admission-plugins" compare: op: nothave value: "DenyServiceExternalIPs" set: true - flag: "--enable-admission-plugins" set: false remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and remove the `DenyServiceExternalIPs` from enabled admission plugins. scored: true - id: 1.2.4 text: "Ensure that the --kubelet-client-certificate and --kubelet-client-key arguments are set as appropriate (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: bin_op: and test_items: - flag: "--kubelet-client-certificate" - flag: "--kubelet-client-key" remediation: | Follow the Kubernetes documentation and set up the TLS connection between the apiserver and kubelets. Then, edit API server pod specification file $apiserverconf on the control plane node and set the kubelet client certificate and key parameters as below. --kubelet-client-certificate= --kubelet-client-key= scored: true - id: 1.2.5 text: "Ensure that the --kubelet-certificate-authority argument is set as appropriate (Automated)" type: "skip" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--kubelet-certificate-authority" set: true remediation: | Follow the Kubernetes documentation and setup the TLS connection between the apiserver and kubelets. Then, edit the API server pod specification file $apiserverconf on the control plane node and set the --kubelet-certificate-authority parameter to the path to the cert file for the certificate authority. --kubelet-certificate-authority= Permissive - When generating serving certificates, functionality could break in conjunction with hostname overrides which are required for certain cloud providers. scored: true - id: 1.2.6 text: "Ensure that the --authorization-mode argument is not set to AlwaysAllow (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--authorization-mode" compare: op: nothave value: "AlwaysAllow" remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the --authorization-mode parameter to values other than AlwaysAllow. One such example could be as below. --authorization-mode=RBAC scored: true - id: 1.2.7 text: "Ensure that the --authorization-mode argument includes Node (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--authorization-mode" compare: op: has value: "Node" remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the --authorization-mode parameter to a value that includes Node. --authorization-mode=Node,RBAC scored: true - id: 1.2.8 text: "Ensure that the --authorization-mode argument includes RBAC (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--authorization-mode" compare: op: has value: "RBAC" remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the --authorization-mode parameter to a value that includes RBAC, for example `--authorization-mode=Node,RBAC`. scored: true - id: 1.2.9 text: "Ensure that the admission control plugin EventRateLimit is set (Manual)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--enable-admission-plugins" compare: op: has value: "EventRateLimit" remediation: | Follow the Kubernetes documentation and set the desired limits in a configuration file. Then, edit the API server pod specification file $apiserverconf and set the below parameters. --enable-admission-plugins=...,EventRateLimit,... --admission-control-config-file= scored: false - id: 1.2.10 text: "Ensure that the admission control plugin AlwaysAdmit is not set (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: bin_op: or test_items: - flag: "--enable-admission-plugins" compare: op: nothave value: AlwaysAdmit - flag: "--enable-admission-plugins" set: false remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and either remove the --enable-admission-plugins parameter, or set it to a value that does not include AlwaysAdmit. scored: true - id: 1.2.11 text: "Ensure that the admission control plugin AlwaysPullImages is set (Manual)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--enable-admission-plugins" compare: op: has value: "AlwaysPullImages" remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the --enable-admission-plugins parameter to include AlwaysPullImages. --enable-admission-plugins=...,AlwaysPullImages,... scored: false - id: 1.2.12 text: "Ensure that the admission control plugin SecurityContextDeny is set if PodSecurityPolicy is not used (Manual)" type: "skip" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: bin_op: or test_items: - flag: "--enable-admission-plugins" compare: op: has value: "SecurityContextDeny" - flag: "--enable-admission-plugins" compare: op: has value: "PodSecurityPolicy" remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the --enable-admission-plugins parameter to include SecurityContextDeny, unless PodSecurityPolicy is already in place. --enable-admission-plugins=...,SecurityContextDeny,... Permissive - Enabling Pod Security Policy can cause applications to unexpectedly fail. scored: false - id: 1.2.13 text: "Ensure that the admission control plugin ServiceAccount is set (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: bin_op: or test_items: - flag: "--disable-admission-plugins" compare: op: nothave value: "ServiceAccount" - flag: "--disable-admission-plugins" set: false remediation: | Follow the documentation and create ServiceAccount objects as per your environment. Then, edit the API server pod specification file $apiserverconf on the control plane node and ensure that the --disable-admission-plugins parameter is set to a value that does not include ServiceAccount. scored: true - id: 1.2.14 text: "Ensure that the admission control plugin NamespaceLifecycle is set (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: bin_op: or test_items: - flag: "--disable-admission-plugins" compare: op: nothave value: "NamespaceLifecycle" - flag: "--disable-admission-plugins" set: false remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the --disable-admission-plugins parameter to ensure it does not include NamespaceLifecycle. scored: true - id: 1.2.15 text: "Ensure that the admission control plugin NodeRestriction is set (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--enable-admission-plugins" compare: op: has value: "NodeRestriction" remediation: | Follow the Kubernetes documentation and configure NodeRestriction plug-in on kubelets. Then, edit the API server pod specification file $apiserverconf on the control plane node and set the --enable-admission-plugins parameter to a value that includes NodeRestriction. --enable-admission-plugins=...,NodeRestriction,... scored: true - id: 1.2.16 text: "Ensure that the --secure-port argument is not set to 0 - NoteThis recommendation is obsolete and will be deleted per the consensus process (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: bin_op: or test_items: - flag: "--secure-port" compare: op: gt value: 0 - flag: "--secure-port" set: false remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and either remove the --secure-port parameter or set it to a different (non-zero) desired port. scored: true - id: 1.2.17 text: "Ensure that the --profiling argument is set to false (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--profiling" compare: op: eq value: false remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the below parameter. --profiling=false scored: true - id: 1.2.18 text: "Ensure that the --audit-log-path argument is set (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--audit-log-path" remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the --audit-log-path parameter to a suitable path and file where you would like audit logs to be written, for example, --audit-log-path=/var/log/apiserver/audit.log scored: true - id: 1.2.19 text: "Ensure that the --audit-log-maxage argument is set to 30 or as appropriate (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--audit-log-maxage" compare: op: gte value: 30 remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the --audit-log-maxage parameter to 30 or as an appropriate number of days, for example, --audit-log-maxage=30 scored: true - id: 1.2.20 text: "Ensure that the --audit-log-maxbackup argument is set to 10 or as appropriate (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--audit-log-maxbackup" compare: op: gte value: 10 remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the --audit-log-maxbackup parameter to 10 or to an appropriate value. For example, --audit-log-maxbackup=10 scored: true - id: 1.2.21 text: "Ensure that the --audit-log-maxsize argument is set to 100 or as appropriate (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--audit-log-maxsize" compare: op: gte value: 100 remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the --audit-log-maxsize parameter to an appropriate size in MB. For example, to set it as 100 MB, --audit-log-maxsize=100 scored: true - id: 1.2.22 text: "Ensure that the --request-timeout argument is set as appropriate (Manual)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" type: manual remediation: | Edit the API server pod specification file $apiserverconf and set the below parameter as appropriate and if needed. For example, --request-timeout=300s scored: false - id: 1.2.23 text: "Ensure that the --service-account-lookup argument is set to true (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: bin_op: or test_items: - flag: "--service-account-lookup" set: false - flag: "--service-account-lookup" compare: op: eq value: true remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the below parameter. --service-account-lookup=true Alternatively, you can delete the --service-account-lookup parameter from this file so that the default takes effect. scored: true - id: 1.2.24 text: "Ensure that the --service-account-key-file argument is set as appropriate (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--service-account-key-file" remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the --service-account-key-file parameter to the public key file for service accounts. For example, --service-account-key-file= scored: true - id: 1.2.25 text: "Ensure that the --etcd-certfile and --etcd-keyfile arguments are set as appropriate (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: bin_op: and test_items: - flag: "--etcd-certfile" - flag: "--etcd-keyfile" remediation: | Follow the Kubernetes documentation and set up the TLS connection between the apiserver and etcd. Then, edit the API server pod specification file $apiserverconf on the control plane node and set the etcd certificate and key file parameters. --etcd-certfile= --etcd-keyfile= scored: true - id: 1.2.26 text: "Ensure that the --tls-cert-file and --tls-private-key-file arguments are set as appropriate (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: bin_op: and test_items: - flag: "--tls-cert-file" - flag: "--tls-private-key-file" remediation: | Follow the Kubernetes documentation and set up the TLS connection on the apiserver. Then, edit the API server pod specification file $apiserverconf on the control plane node and set the TLS certificate and private key file parameters. --tls-cert-file= --tls-private-key-file= scored: true - id: 1.2.27 text: "Ensure that the --client-ca-file argument is set as appropriate (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--client-ca-file" remediation: | Follow the Kubernetes documentation and set up the TLS connection on the apiserver. Then, edit the API server pod specification file $apiserverconf on the control plane node and set the client certificate authority file. --client-ca-file= scored: true - id: 1.2.28 text: "Ensure that the --etcd-cafile argument is set as appropriate (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--etcd-cafile" remediation: | Follow the Kubernetes documentation and set up the TLS connection between the apiserver and etcd. Then, edit the API server pod specification file $apiserverconf on the control plane node and set the etcd certificate authority file parameter. --etcd-cafile= scored: true - id: 1.2.29 text: "Ensure that the --encryption-provider-config argument is set as appropriate (Manual)" type: "skip" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--encryption-provider-config" remediation: | Follow the Kubernetes documentation and configure a EncryptionConfig file. Then, edit the API server pod specification file $apiserverconf on the control plane node and set the --encryption-provider-config parameter to the path of that file. For example, --encryption-provider-config= Permissive - Enabling encryption changes how data can be recovered as data is encrypted. scored: false - id: 1.2.30 text: "Ensure that encryption providers are appropriately configured (Manual)" type: "skip" audit: | ENCRYPTION_PROVIDER_CONFIG=$(ps -ef | grep $apiserverbin | grep -- --encryption-provider-config | sed 's%.*encryption-provider-config[= ]\([^ ]*\).*%\1%') if test -e $ENCRYPTION_PROVIDER_CONFIG; then grep -A1 'providers:' $ENCRYPTION_PROVIDER_CONFIG | tail -n1 | grep -o "[A-Za-z]*" | sed 's/^/provider=/'; fi tests: test_items: - flag: "provider" compare: op: valid_elements value: "aescbc,kms,secretbox" remediation: | Follow the Kubernetes documentation and configure a EncryptionConfig file. In this file, choose aescbc, kms or secretbox as the encryption provider. Permissive - Enabling encryption changes how data can be recovered as data is encrypted. scored: false - id: 1.2.31 text: "Ensure that the API Server only makes use of Strong Cryptographic Ciphers (Manual)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--tls-cipher-suites" compare: op: valid_elements value: "TLS_AES_128_GCM_SHA256,TLS_AES_256_GCM_SHA384,TLS_CHACHA20_POLY1305_SHA256,TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA,TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,TLS_RSA_WITH_3DES_EDE_CBC_SHA,TLS_RSA_WITH_AES_128_CBC_SHA,TLS_RSA_WITH_AES_128_GCM_SHA256,TLS_RSA_WITH_AES_256_CBC_SHA,TLS_RSA_WITH_AES_256_GCM_SHA384" remediation: | Edit the API server pod specification file /etc/kubernetes/manifests/kube-apiserver.yaml on the control plane node and set the below parameter. --tls-cipher-suites=TLS_AES_128_GCM_SHA256,TLS_AES_256_GCM_SHA384,TLS_CHACHA20_POLY1305_SHA256, TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA,TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305, TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,TLS_RSA_WITH_3DES_EDE_CBC_SHA,TLS_RSA_WITH_AES_128_CBC_SHA, TLS_RSA_WITH_AES_128_GCM_SHA256,TLS_RSA_WITH_AES_256_CBC_SHA,TLS_RSA_WITH_AES_256_GCM_SHA384 scored: false - id: 1.3 text: "Controller Manager" checks: - id: 1.3.1 text: "Ensure that the --terminated-pod-gc-threshold argument is set as appropriate (Automated)" audit: "/bin/ps -ef | grep $controllermanagerbin | grep -v grep" tests: test_items: - flag: "--terminated-pod-gc-threshold" remediation: | Edit the Controller Manager pod specification file $controllermanagerconf on the control plane node and set the --terminated-pod-gc-threshold to an appropriate threshold, for example, --terminated-pod-gc-threshold=10 scored: true - id: 1.3.2 text: "Ensure that the --profiling argument is set to false (Automated)" audit: "/bin/ps -ef | grep $controllermanagerbin | grep -v grep" tests: test_items: - flag: "--profiling" compare: op: eq value: false remediation: | Edit the Controller Manager pod specification file $controllermanagerconf on the control plane node and set the below parameter. --profiling=false scored: true - id: 1.3.3 text: "Ensure that the --use-service-account-credentials argument is set to true (Automated)" audit: "/bin/ps -ef | grep $controllermanagerbin | grep -v grep" tests: test_items: - flag: "--use-service-account-credentials" compare: op: noteq value: false remediation: | Edit the Controller Manager pod specification file $controllermanagerconf on the control plane node to set the below parameter. --use-service-account-credentials=true scored: true - id: 1.3.4 text: "Ensure that the --service-account-private-key-file argument is set as appropriate (Automated)" audit: "/bin/ps -ef | grep $controllermanagerbin | grep -v grep" tests: test_items: - flag: "--service-account-private-key-file" remediation: | Edit the Controller Manager pod specification file $controllermanagerconf on the control plane node and set the --service-account-private-key-file parameter to the private key file for service accounts. --service-account-private-key-file= scored: true - id: 1.3.5 text: "Ensure that the --root-ca-file argument is set as appropriate (Automated)" audit: "/bin/ps -ef | grep $controllermanagerbin | grep -v grep" tests: test_items: - flag: "--root-ca-file" remediation: | Edit the Controller Manager pod specification file $controllermanagerconf on the control plane node and set the --root-ca-file parameter to the certificate bundle file`. --root-ca-file= scored: true - id: 1.3.6 text: "Ensure that the RotateKubeletServerCertificate argument is set to true (Automated)" type: "skip" audit: "/bin/ps -ef | grep $controllermanagerbin | grep -v grep" tests: bin_op: or test_items: - flag: "--feature-gates" compare: op: nothave value: "RotateKubeletServerCertificate=false" set: true - flag: "--feature-gates" set: false remediation: | Edit the Controller Manager pod specification file $controllermanagerconf on the control plane node and set the --feature-gates parameter to include RotateKubeletServerCertificate=true. --feature-gates=RotateKubeletServerCertificate=true Cluster provisioned by RKE handles certificate rotation directly through RKE. scored: true - id: 1.3.7 text: "Ensure that the --bind-address argument is set to 127.0.0.1 (Automated)" audit: '/bin/sh -c "if test -e $controllermanagerbin; then /bin/ps -ef | grep $controllermanagerbin | grep -v grep; else echo \"Controller manager binary not found\"; fi"' tests: bin_op: or test_items: - flag: "--bind-address" compare: op: eq value: "127.0.0.1" set: true - flag: "Controller manager binary not found" remediation: | Edit the Controller Manager pod specification file $controllermanagerconf on the control plane node and ensure the correct value for the --bind-address parameter scored: true - id: 1.4 text: "Scheduler" checks: - id: 1.4.1 text: "Ensure that the --profiling argument is set to false (Automated)" audit: "/bin/ps -ef | grep $schedulerbin | grep -v grep" tests: test_items: - flag: "--profiling" compare: op: eq value: false remediation: | Edit the Scheduler pod specification file $schedulerconf file on the control plane node and set the below parameter. --profiling=false scored: true - id: 1.4.2 text: "Ensure that the --bind-address argument is set to 127.0.0.1 (Automated)" audit: '/bin/sh -c "if test -e $schedulerbin; then /bin/ps -ef | grep $schedulerbin | grep -v grep; else echo \"Scheduler binary not found\"; fi"' tests: bin_op: or test_items: - flag: "--bind-address" compare: op: eq value: "127.0.0.1" set: true - flag: "Scheduler binary not found" remediation: | Edit the Scheduler pod specification file $schedulerconf on the control plane node and ensure the correct value for the --bind-address parameter scored: true ================================================ FILE: cfg/rke-cis-1.7/node.yaml ================================================ --- controls: version: "rke-cis-1.7" id: 4 text: "Worker Node Security Configuration" type: "node" groups: - id: 4.1 text: "Worker Node Configuration Files" checks: - id: 4.1.1 text: "Ensure that the kubelet service file permissions are set to 600 or more restrictive (Automated)" type: "skip" audit: '/bin/sh -c ''if test -e $kubeletsvc; then stat -c permissions=%a $kubeletsvc; fi'' ' tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Run the below command (based on the file location on your system) on the each worker node. For example, chmod 600 $kubeletsvc Not Applicable - Clusters provisioned by RKE doesn’t require or maintain a configuration file for the kubelet service. All configuration is passed in as arguments at container run time. scored: true - id: 4.1.2 text: "Ensure that the kubelet service file ownership is set to root:root (Automated)" type: "skip" audit: '/bin/sh -c ''if test -e $kubeletsvc; then stat -c %U:%G $kubeletsvc; fi'' ' tests: test_items: - flag: root:root remediation: | Run the below command (based on the file location on your system) on the each worker node. For example, chown root:root $kubeletsvc Not Applicable - Clusters provisioned by RKE doesn’t require or maintain a configuration file for the kubelet service. All configuration is passed in as arguments at container run time. scored: true - id: 4.1.3 text: "If proxy kubeconfig file exists ensure permissions are set to 600 or more restrictive (Automated)" audit: '/bin/sh -c ''if test -e $proxykubeconfig; then stat -c permissions=%a $proxykubeconfig; fi'' ' tests: bin_op: or test_items: - flag: "permissions" set: true compare: op: bitmask value: "600" remediation: | Run the below command (based on the file location on your system) on the each worker node. For example, chmod 600 $proxykubeconfig scored: true - id: 4.1.4 text: "If proxy kubeconfig file exists ensure ownership is set to root:root (Automated)" audit: '/bin/sh -c ''if test -e $proxykubeconfig; then stat -c %U:%G $proxykubeconfig; fi'' ' tests: bin_op: or test_items: - flag: root:root remediation: | Run the below command (based on the file location on your system) on the each worker node. For example, chown root:root $proxykubeconfig scored: true - id: 4.1.5 text: "Ensure that the --kubeconfig kubelet.conf file permissions are set to 600 or more restrictive (Automated)" audit: '/bin/sh -c ''if test -e $kubeletkubeconfig; then stat -c permissions=%a $kubeletkubeconfig; fi'' ' tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Run the below command (based on the file location on your system) on the each worker node. For example, chmod 600 $kubeletkubeconfig scored: true - id: 4.1.6 text: "Ensure that the --kubeconfig kubelet.conf file ownership is set to root:root (Automated)" audit: '/bin/sh -c ''if test -e $kubeletkubeconfig; then stat -c %U:%G $kubeletkubeconfig; fi'' ' tests: test_items: - flag: root:root remediation: | Run the below command (based on the file location on your system) on the each worker node. For example, chown root:root $kubeletkubeconfig scored: true - id: 4.1.7 text: "Ensure that the certificate authorities file permissions are set to 600 or more restrictive (Automated)" audit: "stat -c permissions=%a /etc/kubernetes/ssl/kube-ca.pem" tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Run the following command to modify the file permissions of the --client-ca-file chmod 600 scored: true - id: 4.1.8 text: "Ensure that the client certificate authorities file ownership is set to root:root (Automated)" audit: "stat -c %U:%G /etc/kubernetes/ssl/kube-ca.pem" tests: test_items: - flag: root:root remediation: | Run the following command to modify the ownership of the --client-ca-file. chown root:root scored: true - id: 4.1.9 text: "If the kubelet config.yaml configuration file is being used validate permissions set to 600 or more restrictive (Automated)" type: "skip" audit: '/bin/sh -c ''if test -e $kubeletconf; then stat -c permissions=%a $kubeletconf; fi'' ' tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Run the following command (using the config file location identified in the Audit step) chmod 600 $kubeletconf Not Applicable - Clusters provisioned by RKE do not require or maintain a configuration file for the kubelet. All configuration is passed in as arguments at container run time. scored: true - id: 4.1.10 text: "If the kubelet config.yaml configuration file is being used validate file ownership is set to root:root (Manual)" type: "skip" audit: '/bin/sh -c ''if test -e $kubeletconf; then stat -c %U:%G $kubeletconf; fi'' ' tests: test_items: - flag: root:root remediation: | Run the following command (using the config file location identified in the Audit step) chown root:root $kubeletconf Not Applicable - Clusters provisioned by RKE doesn’t require or maintain a configuration file for the kubelet. All configuration is passed in as arguments at container run time. scored: false - id: 4.2 text: "Kubelet" checks: - id: 4.2.1 text: "Ensure that the --anonymous-auth argument is set to false (Automated)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/sh -c 'if test -e $kubeletconf; then /bin/cat $kubeletconf; fi' " tests: test_items: - flag: "--anonymous-auth" path: '{.authentication.anonymous.enabled}' compare: op: eq value: false remediation: | If using a Kubelet config file, edit the file to set `authentication: anonymous: enabled` to `false`. If using executable arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_SYSTEM_PODS_ARGS variable. `--anonymous-auth=false` Based on your system, restart the kubelet service. For example, systemctl daemon-reload systemctl restart kubelet.service scored: true - id: 4.2.2 text: "Ensure that the --authorization-mode argument is not set to AlwaysAllow (Automated)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/sh -c 'if test -e $kubeletconf; then /bin/cat $kubeletconf; fi' " tests: test_items: - flag: --authorization-mode path: '{.authorization.mode}' compare: op: nothave value: AlwaysAllow remediation: | If using a Kubelet config file, edit the file to set `authorization.mode` to Webhook. If using executable arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_AUTHZ_ARGS variable. --authorization-mode=Webhook Based on your system, restart the kubelet service. For example, systemctl daemon-reload systemctl restart kubelet.service scored: true - id: 4.2.3 text: "Ensure that the --client-ca-file argument is set as appropriate (Automated)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/sh -c 'if test -e $kubeletconf; then /bin/cat $kubeletconf; fi' " tests: test_items: - flag: --client-ca-file path: '{.authentication.x509.clientCAFile}' remediation: | If using a Kubelet config file, edit the file to set `authentication.x509.clientCAFile` to the location of the client CA file. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_AUTHZ_ARGS variable. --client-ca-file= Based on your system, restart the kubelet service. For example, systemctl daemon-reload systemctl restart kubelet.service scored: true - id: 4.2.4 text: "Verify that the --read-only-port argument is set to 0 (Automated)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/sh -c 'if test -e $kubeletconf; then /bin/cat $kubeletconf; fi' " tests: bin_op: or test_items: - flag: "--read-only-port" path: '{.readOnlyPort}' compare: op: eq value: 0 - flag: "--read-only-port" path: '{.readOnlyPort}' set: false remediation: | If using a Kubelet config file, edit the file to set `readOnlyPort` to 0. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_SYSTEM_PODS_ARGS variable. --read-only-port=0 Based on your system, restart the kubelet service. For example, systemctl daemon-reload systemctl restart kubelet.service scored: true - id: 4.2.5 text: "Ensure that the --streaming-connection-idle-timeout argument is not set to 0 (Manual)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/sh -c 'if test -e $kubeletconf; then /bin/cat $kubeletconf; fi' " tests: test_items: - flag: --streaming-connection-idle-timeout path: '{.streamingConnectionIdleTimeout}' compare: op: noteq value: 0 - flag: --streaming-connection-idle-timeout path: '{.streamingConnectionIdleTimeout}' set: false bin_op: or remediation: | If using a Kubelet config file, edit the file to set `streamingConnectionIdleTimeout` to a value other than 0. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_SYSTEM_PODS_ARGS variable. --streaming-connection-idle-timeout=5m Based on your system, restart the kubelet service. For example, systemctl daemon-reload systemctl restart kubelet.service scored: false - id: 4.2.6 text: "Ensure that the --make-iptables-util-chains argument is set to true (Automated)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/sh -c 'if test -e $kubeletconf; then /bin/cat $kubeletconf; fi' " tests: test_items: - flag: --make-iptables-util-chains path: '{.makeIPTablesUtilChains}' compare: op: eq value: true - flag: --make-iptables-util-chains path: '{.makeIPTablesUtilChains}' set: false bin_op: or remediation: | If using a Kubelet config file, edit the file to set `makeIPTablesUtilChains` to `true`. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and remove the --make-iptables-util-chains argument from the KUBELET_SYSTEM_PODS_ARGS variable. Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: true - id: 4.2.7 text: "Ensure that the --hostname-override argument is not set (Manual)" # This is one of those properties that can only be set as a command line argument. # To check if the property is set as expected, we need to parse the kubelet command # instead reading the Kubelet Configuration file. type: "skip" audit: "/bin/ps -fC $kubeletbin " tests: test_items: - flag: --hostname-override set: false remediation: | Edit the kubelet service file $kubeletsvc on each worker node and remove the --hostname-override argument from the KUBELET_SYSTEM_PODS_ARGS variable. Based on your system, restart the kubelet service. For example, systemctl daemon-reload systemctl restart kubelet.service Not Applicable - Clusters provisioned by RKE set the --hostname-override to avoid any hostname configuration errors scored: false - id: 4.2.8 text: "Ensure that the eventRecordQPS argument is set to a level which ensures appropriate event capture (Manual)" type: "skip" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/sh -c 'if test -e $kubeletconf; then /bin/cat $kubeletconf; fi' " tests: test_items: - flag: --event-qps path: '{.eventRecordQPS}' compare: op: gte value: 0 - flag: --event-qps path: '{.eventRecordQPS}' set: false bin_op: or remediation: | If using a Kubelet config file, edit the file to set `eventRecordQPS` to an appropriate level. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_SYSTEM_PODS_ARGS variable. Based on your system, restart the kubelet service. For example, systemctl daemon-reload systemctl restart kubelet.service scored: false - id: 4.2.9 text: "Ensure that the --tls-cert-file and --tls-private-key-file arguments are set as appropriate (Manual)" type: "skip" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/sh -c 'if test -e $kubeletconf; then /bin/cat $kubeletconf; fi' " tests: test_items: - flag: --tls-cert-file path: '{.tlsCertFile}' - flag: --tls-private-key-file path: '{.tlsPrivateKeyFile}' remediation: | If using a Kubelet config file, edit the file to set `tlsCertFile` to the location of the certificate file to use to identify this Kubelet, and `tlsPrivateKeyFile` to the location of the corresponding private key file. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameters in KUBELET_CERTIFICATE_ARGS variable. --tls-cert-file= --tls-private-key-file= Based on your system, restart the kubelet service. For example, systemctl daemon-reload systemctl restart kubelet.service Permissive - When generating serving certificates, functionality could break in conjunction with hostname overrides which are required for certain cloud providers. scored: false - id: 4.2.10 text: "Ensure that the --rotate-certificates argument is not set to false (Automated)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/sh -c 'if test -e $kubeletconf; then /bin/cat $kubeletconf; fi' " tests: test_items: - flag: --rotate-certificates path: '{.rotateCertificates}' compare: op: eq value: true - flag: --rotate-certificates path: '{.rotateCertificates}' set: false bin_op: or remediation: | If using a Kubelet config file, edit the file to add the line `rotateCertificates` to `true` or remove it altogether to use the default value. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and remove --rotate-certificates=false argument from the KUBELET_CERTIFICATE_ARGS variable. Based on your system, restart the kubelet service. For example, systemctl daemon-reload systemctl restart kubelet.service scored: true - id: 4.2.11 text: "Verify that the RotateKubeletServerCertificate argument is set to true (Manual)" type: "skip" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/sh -c 'if test -e $kubeletconf; then /bin/cat $kubeletconf; fi' " tests: bin_op: or test_items: - flag: RotateKubeletServerCertificate path: '{.featureGates.RotateKubeletServerCertificate}' compare: op: nothave value: false - flag: RotateKubeletServerCertificate path: '{.featureGates.RotateKubeletServerCertificate}' set: false remediation: | Edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_CERTIFICATE_ARGS variable. --feature-gates=RotateKubeletServerCertificate=true Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service Not Applicable - Clusters provisioned by RKE handles certificate rotation directly through RKE. scored: false - id: 4.2.12 text: "Ensure that the Kubelet only makes use of Strong Cryptographic Ciphers (Automated)" type: "skip" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/sh -c 'if test -e $kubeletconf; then /bin/cat $kubeletconf; fi' " tests: test_items: - flag: --tls-cipher-suites path: '{range .tlsCipherSuites[:]}{}{'',''}{end}' compare: op: valid_elements value: 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 remediation: | If using a Kubelet config file, edit the file to set `tlsCipherSuites` to 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 or to a subset of these values. If using executable arguments, edit the kubelet service file $kubeletsvc on each worker node and set the --tls-cipher-suites parameter as follows, or to a subset of these values. --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 Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: true - id: 4.2.13 text: "Ensure that a limit is set on pod PIDs (Manual)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/sh -c 'if test -e $kubeletconf; then /bin/cat $kubeletconf; fi' " tests: test_items: - flag: --pod-max-pids path: '{.podPidsLimit}' remediation: | Decide on an appropriate level for this parameter and set it, either via the --pod-max-pids command line parameter or the PodPidsLimit configuration file setting. scored: false ================================================ FILE: cfg/rke-cis-1.7/policies.yaml ================================================ --- controls: version: "rke-cis-1.7" id: 5 text: "Kubernetes Policies" type: "policies" groups: - id: 5.1 text: "RBAC and Service Accounts" checks: - id: 5.1.1 text: "Ensure that the cluster-admin role is only used where required (Manual)" type: "manual" remediation: | Identify all clusterrolebindings to the cluster-admin role. Check if they are used and if they need this role or if they could use a role with fewer privileges. Where possible, first bind users to a lower privileged role and then remove the clusterrolebinding to the cluster-admin role : kubectl delete clusterrolebinding [name] scored: false - id: 5.1.2 text: "Minimize access to secrets (Manual)" type: "manual" remediation: | Where possible, remove get, list and watch access to Secret objects in the cluster. scored: false - id: 5.1.3 text: "Minimize wildcard use in Roles and ClusterRoles (Manual)" type: "manual" remediation: | Where possible replace any use of wildcards in clusterroles and roles with specific objects or actions. scored: false - id: 5.1.4 text: "Minimize access to create pods (Manual)" type: "manual" remediation: | Where possible, remove create access to pod objects in the cluster. scored: false - id: 5.1.5 text: "Ensure that default service accounts are not actively used. (Manual)" type: "manual" remediation: | Create explicit service accounts wherever a Kubernetes workload requires specific access to the Kubernetes API server. Modify the configuration of each default service account to include this value automountServiceAccountToken: false scored: false - id: 5.1.6 text: "Ensure that Service Account Tokens are only mounted where necessary (Manual)" type: "manual" remediation: | Modify the definition of pods and service accounts which do not need to mount service account tokens to disable it. scored: false - id: 5.1.7 text: "Avoid use of system:masters group (Manual)" type: "manual" remediation: | Remove the system:masters group from all users in the cluster. scored: false - id: 5.1.8 text: "Limit use of the Bind, Impersonate and Escalate permissions in the Kubernetes cluster (Manual)" type: "manual" remediation: | Where possible, remove the impersonate, bind and escalate rights from subjects. scored: false - id: 5.1.9 text: "Minimize access to create persistent volumes (Manual)" type: "manual" remediation: | Where possible, remove create access to PersistentVolume objects in the cluster. scored: false - id: 5.1.10 text: "Minimize access to the proxy sub-resource of nodes (Manual)" type: "manual" remediation: | Where possible, remove access to the proxy sub-resource of node objects. scored: false - id: 5.1.11 text: "Minimize access to the approval sub-resource of certificatesigningrequests objects (Manual)" type: "manual" remediation: | Where possible, remove access to the approval sub-resource of certificatesigningrequest objects. scored: false - id: 5.1.12 text: "Minimize access to webhook configuration objects (Manual)" type: "manual" remediation: | Where possible, remove access to the validatingwebhookconfigurations or mutatingwebhookconfigurations objects scored: false - id: 5.1.13 text: "Minimize access to the service account token creation (Manual)" type: "manual" remediation: | Where possible, remove access to the token sub-resource of serviceaccount objects. scored: false - id: 5.2 text: "Pod Security Standards" checks: - id: 5.2.1 text: "Ensure that the cluster has at least one active policy control mechanism in place (Manual)" type: "manual" remediation: | Ensure that either Pod Security Admission or an external policy control system is in place for every namespace which contains user workloads. scored: false - id: 5.2.2 text: "Minimize the admission of privileged containers (Manual)" type: "manual" remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of privileged containers. scored: false - id: 5.2.3 text: "Minimize the admission of containers wishing to share the host process ID namespace (Automated)" type: "skip" remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of `hostPID` containers. Permissive - Enabling Pod Security Policy can cause applications to unexpectedly fail. scored: false - id: 5.2.4 text: "Minimize the admission of containers wishing to share the host IPC namespace (Automated)" type: "skip" remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of `hostIPC` containers. Permissive - Enabling Pod Security Policy can cause applications to unexpectedly fail. scored: false - id: 5.2.5 text: "Minimize the admission of containers wishing to share the host network namespace (Automated)" type: "skip" remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of `hostNetwork` containers. Permissive - Enabling Pod Security Policy can cause applications to unexpectedly fail. scored: false - id: 5.2.6 text: "Minimize the admission of containers with allowPrivilegeEscalation (Manual)" type: "manual" remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of containers with `.spec.allowPrivilegeEscalation` set to `true`. scored: false - id: 5.2.7 text: "Minimize the admission of root containers (Manual)" type: "manual" remediation: | Create a policy for each namespace in the cluster, ensuring that either `MustRunAsNonRoot` or `MustRunAs` with the range of UIDs not including 0, is set. scored: false - id: 5.2.8 text: "Minimize the admission of containers with the NET_RAW capability (Manual)" type: "manual" remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of containers with the `NET_RAW` capability. scored: false - id: 5.2.9 text: "Minimize the admission of containers with added capabilities (Manual)" type: "manual" remediation: | Ensure that `allowedCapabilities` is not present in policies for the cluster unless it is set to an empty array. scored: false - id: 5.2.10 text: "Minimize the admission of containers with capabilities assigned (Manual)" type: "manual" remediation: | Review the use of capabilites in applications running on your cluster. Where a namespace contains applications which do not require any Linux capabities to operate consider adding a PSP which forbids the admission of containers which do not drop all capabilities. scored: false - id: 5.2.11 text: "Minimize the admission of Windows HostProcess containers (Manual)" type: "manual" remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of containers that have `.securityContext.windowsOptions.hostProcess` set to `true`. scored: false - id: 5.2.12 text: "Minimize the admission of HostPath volumes (Manual)" type: "manual" remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of containers with `hostPath` volumes. scored: false - id: 5.2.13 text: "Minimize the admission of containers which use HostPorts (Manual)" type: "manual" remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of containers which use `hostPort` sections. scored: false - id: 5.3 text: "Network Policies and CNI" checks: - id: 5.3.1 text: "Ensure that the CNI in use supports NetworkPolicies (Manual)" type: "manual" remediation: | If the CNI plugin in use does not support network policies, consideration should be given to making use of a different plugin, or finding an alternate mechanism for restricting traffic in the Kubernetes cluster. scored: false - id: 5.3.2 text: "Ensure that all Namespaces have NetworkPolicies defined (Manual)" type: "skip" remediation: | Follow the documentation and create NetworkPolicy objects as you need them. Permissive - Enabling Network Policies can prevent certain applications from communicating with each other. scored: false - id: 5.4 text: "Secrets Management" checks: - id: 5.4.1 text: "Prefer using Secrets as files over Secrets as environment variables (Manual)" type: "manual" remediation: | If possible, rewrite application code to read Secrets from mounted secret files, rather than from environment variables. scored: false - id: 5.4.2 text: "Consider external secret storage (Manual)" type: "manual" remediation: | Refer to the Secrets management options offered by your cloud provider or a third-party secrets management solution. scored: false - id: 5.5 text: "Extensible Admission Control" checks: - id: 5.5.1 text: "Configure Image Provenance using ImagePolicyWebhook admission controller (Manual)" type: "manual" remediation: | Follow the Kubernetes documentation and setup image provenance. scored: false - id: 5.7 text: "General Policies" checks: - id: 5.7.1 text: "Create administrative boundaries between resources using namespaces (Manual)" type: "manual" remediation: | Follow the documentation and create namespaces for objects in your deployment as you need them. scored: false - id: 5.7.2 text: "Ensure that the seccomp profile is set to docker/default in your Pod definitions (Manual)" type: "manual" remediation: | Use `securityContext` to enable the docker/default seccomp profile in your pod definitions. An example is as below: securityContext: seccompProfile: type: RuntimeDefault scored: false - id: 5.7.3 text: "Apply SecurityContext to your Pods and Containers (Manual)" type: "manual" remediation: | Follow the Kubernetes documentation and apply SecurityContexts to your Pods. For a suggested list of SecurityContexts, you may refer to the CIS Security Benchmark for Docker Containers. scored: false - id: 5.7.4 text: "The default namespace should not be used (Manual)" type: "skip" remediation: | Ensure that namespaces are created to allow for appropriate segregation of Kubernetes resources and that all new resources are created in a specific namespace. Permissive - Kubernetes provides a default namespace. scored: false ================================================ FILE: cfg/rke2-cis-1.23/config.yaml ================================================ --- ## Version-specific settings that override the values in cfg/config.yaml ================================================ FILE: cfg/rke2-cis-1.23/controlplane.yaml ================================================ --- controls: version: "rke2-cis-1.23" id: 3 text: "Control Plane Configuration" type: "controlplane" groups: - id: 3.1 text: "Authentication and Authorization" checks: - id: 3.1.1 text: "Client certificate authentication should not be used for users (Manual)" type: "manual" remediation: | Alternative mechanisms provided by Kubernetes such as the use of OIDC should be implemented in place of client certificates. scored: false - id: 3.2 text: "Logging" checks: - id: 3.2.1 text: "Ensure that a minimal audit policy is created (Automated)" audit: "/bin/ps -ef | grep kube-apiserver | grep -v grep | grep -o audit-policy-file" type: "skip" tests: test_items: - flag: "--audit-policy-file" compare: op: eq value: "--audit-policy-file" set: true remediation: | Create an audit policy file for your cluster. scored: true - id: 3.2.2 text: "Ensure that the audit policy covers key security concerns (Manual)" type: "manual" remediation: | Review the audit policy provided for the cluster and ensure that it covers at least the following areas, - Access to Secrets managed by the cluster. Care should be taken to only log Metadata for requests to Secrets, ConfigMaps, and TokenReviews, in order to avoid risk of logging sensitive data. - Modification of Pod and Deployment objects. - Use of `pods/exec`, `pods/portforward`, `pods/proxy` and `services/proxy`. For most requests, minimally logging at the Metadata level is recommended (the most basic level of logging). scored: false ================================================ FILE: cfg/rke2-cis-1.23/etcd.yaml ================================================ --- controls: version: "rke2-cis-1.23" id: 2 text: "Etcd Node Configuration" type: "etcd" groups: - id: 2 text: "Etcd Node Configuration" checks: - id: 2.1 text: "Ensure that the --cert-file and --key-file arguments are set as appropriate (Automated)" audit: "/bin/ps -ef | /bin/grep $etcdbin | /bin/grep -v grep" tests: bin_op: and test_items: - flag: "--cert-file" env: "ETCD_CERT_FILE" - flag: "--key-file" env: "ETCD_KEY_FILE" remediation: | Follow the etcd service documentation and configure TLS encryption. Then, edit the etcd pod specification file /etc/kubernetes/manifests/etcd.yaml on the master node and set the below parameters. --cert-file= --key-file= scored: true type: "skip" - id: 2.2 text: "Ensure that the --client-cert-auth argument is set to true (Automated)" audit: "/bin/ps -ef | /bin/grep $etcdbin | /bin/grep -v grep" tests: test_items: - flag: "--client-cert-auth" env: "ETCD_CLIENT_CERT_AUTH" compare: op: eq value: true type: "skip" remediation: | Edit the etcd pod specification file $etcdconf on the master node and set the below parameter. --client-cert-auth="true" scored: true - id: 2.3 text: "Ensure that the --auto-tls argument is not set to true (Automated)" audit: "/bin/ps -ef | /bin/grep $etcdbin | /bin/grep -v grep" tests: bin_op: or test_items: - flag: "--auto-tls" env: "ETCD_AUTO_TLS" set: false - flag: "--auto-tls" env: "ETCD_AUTO_TLS" compare: op: eq value: false remediation: | Edit the etcd pod specification file $etcdconf on the master node and either remove the --auto-tls parameter or set it to false. --auto-tls=false scored: true - id: 2.4 text: "Ensure that the --peer-cert-file and --peer-key-file arguments are set as appropriate (Automated)" audit: "/bin/ps -ef | /bin/grep $etcdbin | /bin/grep -v grep" tests: bin_op: and test_items: - flag: "--peer-cert-file" env: "ETCD_PEER_CERT_FILE" set: true - flag: "--peer-key-file" env: "ETCD_PEER_KEY_FILE" set: true remediation: | Follow the etcd service documentation and configure peer TLS encryption as appropriate for your etcd cluster. Then, edit the etcd pod specification file $etcdconf on the master node and set the below parameters. --peer-client-file= --peer-key-file= scored: true type: "skip" - id: 2.5 text: "Ensure that the --peer-client-cert-auth argument is set to true (Automated)" audit: "/bin/ps -ef | /bin/grep $etcdbin | /bin/grep -v grep" tests: test_items: - flag: "--peer-client-cert-auth" env: "ETCD_PEER_CLIENT_CERT_AUTH" compare: op: eq value: true set: true remediation: | Edit the etcd pod specification file $etcdconf on the master node and set the below parameter. --peer-client-cert-auth=true scored: true type: "skip" - id: 2.6 text: "Ensure that the --peer-auto-tls argument is not set to true (Automated)" audit: "/bin/ps -ef | /bin/grep $etcdbin | /bin/grep -v grep" tests: bin_op: or test_items: - flag: "--peer-auto-tls" env: "ETCD_PEER_AUTO_TLS" set: false - flag: "--peer-auto-tls" env: "ETCD_PEER_AUTO_TLS" compare: op: eq value: false set: true remediation: | Edit the etcd pod specification file $etcdconf on the master node and either remove the --peer-auto-tls parameter or set it to false. --peer-auto-tls=false scored: true - id: 2.7 text: "Ensure that a unique Certificate Authority is used for etcd (Automated)" audit: "/bin/ps -ef | /bin/grep $etcdbin | /bin/grep -v grep" audit_config: "cat /var/lib/rancher/rke2/server/db/etcd/config" tests: bin_op: or test_items: - flag: "--trusted-ca-file" env: "ETCD_TRUSTED_CA_FILE" - path: "{.peer-transport-security.trusted-ca-file}" compare: op: eq value: "/var/lib/rancher/rke2/server/tls/etcd/peer-ca.crt" set: true remediation: | [Manual test] Follow the etcd documentation and create a dedicated certificate authority setup for the etcd service. Then, edit the etcd pod specification file $etcdconf on the master node and set the below parameter. --trusted-ca-file= scored: false ================================================ FILE: cfg/rke2-cis-1.23/master.yaml ================================================ --- controls: version: "rke2-cis-1.23" id: 1 text: "Control Plane Security Configuration" type: "master" groups: - id: 1.1 text: "Control Plane Node Configuration Files" checks: - id: 1.1.1 text: "Ensure that the API server pod specification file permissions are set to 644 or more restrictive (Automated)" audit: "stat -c permissions=%a /var/lib/rancher/rke2/agent/pod-manifests/kube-apiserver.yaml" tests: test_items: - flag: "permissions" compare: op: eq value: "644" set: true remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chmod 644 $apiserverconf scored: true - id: 1.1.2 text: "Ensure that the API server pod specification file ownership is set to root:root (Automated)" audit: "/bin/sh -c 'if test -e $apiserverconf; then stat -c %U:%G $apiserverconf; fi'" tests: test_items: - flag: "root:root" compare: op: eq value: "root:root" set: true remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chown root:root $apiserverconf scored: true - id: 1.1.3 text: "Ensure that the controller manager pod specification file permissions are set to 644 or more restrictive (Automated)" audit: "/bin/sh -c 'if test -e $controllermanagerconf; then stat -c permissions=%a $controllermanagerconf; fi'" tests: test_items: - flag: "644" compare: op: eq value: "644" set: true remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chmod 644 $controllermanagerconf scored: true - id: 1.1.4 text: "Ensure that the controller manager pod specification file ownership is set to root:root (Automated)" audit: "/bin/sh -c 'if test -e $controllermanagerconf; then stat -c %U:%G $controllermanagerconf; fi'" tests: test_items: - flag: "root:root" compare: op: eq value: "root:root" set: true remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chown root:root $controllermanagerconf scored: true - id: 1.1.5 text: "Ensure that the scheduler pod specification file permissions are set to 644 or more restrictive (Automated)" audit: "/bin/sh -c 'if test -e $schedulerconf; then stat -c permissions=%a $schedulerconf; fi'" tests: test_items: - flag: "644" compare: op: eq value: "644" set: true remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chmod 644 $schedulerconf scored: true - id: 1.1.6 text: "Ensure that the scheduler pod specification file ownership is set to root:root (Automated)" audit: "/bin/sh -c 'if test -e $schedulerconf; then stat -c %U:%G $schedulerconf; fi'" tests: test_items: - flag: "root:root" set: true remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chown root:root $schedulerconf scored: true - id: 1.1.7 text: "Ensure that the etcd pod specification file permissions are set to 644 or more restrictive (Automated)" audit: | /bin/sh -c ' if [ -e "$etcdconf" ]; then stat -c "permissions=%a %n" "$etcdconf" else echo "Directory not found" fi ' tests: bin_op: or test_items: - flag: "permissions" compare: op: bitmask value: "644" - flag: "Directory not found" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chmod 644 $etcdconf scored: true - id: 1.1.8 text: "Ensure that the etcd pod specification file ownership is set to root:root (Automated)" audit: | /bin/sh -c ' if [ -e $etcdconf ]; then stat -c "ownership=%U:%G %n" $etcdconf else echo "Directory not found" fi ' tests: bin_op: or test_items: - flag: "ownership" compare: op: eq value: "root:root" set: true - flag: "Directory not found" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chown root:root $etcdconf scored: true - id: 1.1.9 text: "Ensure that the Container Network Interface file permissions are set to 644 or more restrictive (Manual)" audit: | ps -fC ${kubeletbin:-kubelet} | grep -- --cni-conf-dir || echo "/etc/cni/net.d" | sed 's%.*cni-conf-dir[= ]\([^ ]*\).*%\1%' | xargs -I{} find {} -mindepth 1 | xargs --no-run-if-empty stat -c permissions=%a find /var/lib/cni/networks -type f 2> /dev/null | xargs --no-run-if-empty stat -c permissions=%a use_multiple_values: true tests: test_items: - flag: "permissions" compare: op: bitmask value: "644" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chmod 644 scored: false - id: 1.1.10 text: "Ensure that the Container Network Interface file ownership is set to root:root (Manual)" audit: | ps -fC ${kubeletbin:-kubelet} | grep -- --cni-conf-dir || echo "/etc/cni/net.d" | sed 's%.*cni-conf-dir[= ]\([^ ]*\).*%\1%' | xargs -I{} find {} -mindepth 1 | xargs --no-run-if-empty stat -c %U:%G find /var/lib/cni/networks -type f 2> /dev/null | xargs --no-run-if-empty stat -c %U:%G use_multiple_values: true tests: test_items: - flag: "root:root" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chown root:root scored: false - id: 1.1.11 text: "Ensure that the etcd data directory permissions are set to 700 or more restrictive (Automated)" audit: | /bin/sh -c 'if [ -d "/var/lib/rancher/rke2/server/db/etcd" ]; then stat -c "permissions=%a" "/var/lib/rancher/rke2/server/db/etcd" else echo "Directory not found" fi' tests: bin_op: or test_items: - flag: "permissions" compare: op: bitmask value: "700" set: true - flag: "Directory not found" remediation: | On the etcd server node, get the etcd data directory, passed as an argument --data-dir, from the command 'ps -ef | grep etcd'. Run the below command (based on the etcd data directory found above). For example, chmod 700 /var/lib/etcd scored: true - id: 1.1.12 text: "Ensure that the etcd data directory ownership is set to etcd:etcd (Automated)" type: "skip" audit: "stat -c %U:%G /var/lib/rancher/rke2/server/db/etcd" tests: test_items: - flag: "etcd:etcd" remediation: | On the etcd server node, get the etcd data directory, passed as an argument --data-dir, from the command 'ps -ef | grep etcd'. Run the below command (based on the etcd data directory found above). For example, chown etcd:etcd /var/lib/etcd scored: true - id: 1.1.13 text: "Ensure that the admin.conf file permissions are set to 644 or more restrictive (Automated)" audit: "stat -c permissions=%a /var/lib/rancher/rke2/server/cred/admin.kubeconfig" tests: test_items: - flag: "permissions" compare: op: bitmask value: "644" set: true remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chmod 600 /etc/kubernetes/admin.conf scored: true - id: 1.1.14 text: "Ensure that the admin.conf file ownership is set to root:root (Automated)" audit: "stat -c %U:%G /var/lib/rancher/rke2/server/cred/admin.kubeconfig" tests: test_items: - flag: "root:root" compare: op: eq value: "root:root" set: true remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chown root:root /etc/kubernetes/admin.conf scored: true - id: 1.1.15 text: "Ensure that the scheduler.conf file permissions are set to 644 or more restrictive (Automated)" audit: "stat -c %a $schedulerkubeconfig" tests: test_items: - flag: "644" compare: op: eq value: "644" set: true remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chmod 644 $schedulerkubeconfig scored: true - id: 1.1.16 text: "Ensure that the scheduler.conf file ownership is set to root:root (Automated)" audit: "stat -c %U:%G $schedulerkubeconfig" tests: test_items: - flag: "root:root" compare: op: eq value: "root:root" set: true remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chown root:root $schedulerkubeconfig scored: true - id: 1.1.17 text: "Ensure that the controller-manager.conf file permissions are set to 644 or more restrictive (Automated)" audit: "stat -c %a $controllermanagerkubeconfig" tests: test_items: - flag: "644" compare: op: eq value: "644" set: true remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chmod 644 $controllermanagerkubeconfig scored: true - id: 1.1.18 text: "Ensure that the controller-manager.conf file ownership is set to root:root (Automated)" audit: "stat -c %U:%G $controllermanagerkubeconfig" tests: test_items: - flag: "root:root" compare: op: eq value: "root:root" set: true remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chown root:root $controllermanagerkubeconfig scored: true - id: 1.1.19 text: "Ensure that the Kubernetes PKI directory and file ownership is set to root:root (Automated)" audit: "stat -c %U:%G /var/lib/rancher/rke2/server/tls" use_multiple_values: true tests: test_items: - flag: "root:root" compare: op: eq value: "root:root" set: true remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chown -R root:root /etc/kubernetes/pki/ scored: true - id: 1.1.20 text: "Ensure that the Kubernetes PKI certificate file permissions are set to 644 or more restrictive (Manual)" audit: "stat -c permissions=%a /var/lib/rancher/rke2/server/tls/*.crt" use_multiple_values: true tests: test_items: - flag: "permissions" compare: op: bitmask value: "644" set: true remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chmod -R 644 /var/lib/rancher/rke2/server/tls/*.crt scored: false - id: 1.1.21 text: "Ensure that the Kubernetes PKI key file permissions are set to 600 (Manual)" audit: "stat -c permissions=%a /var/lib/rancher/rke2/server/tls/*.key" use_multiple_values: true tests: test_items: - flag: "permissions" compare: op: eq value: "600" set: true remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chmod -R 600 /var/lib/rancher/rke2/server/tls/*.key scored: false - id: 1.2 text: "API Server" checks: - id: 1.2.1 text: "Ensure that the --anonymous-auth argument is set to false (Manual)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" type: manual tests: test_items: - flag: "--anonymous-auth" compare: op: eq value: false remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the below parameter. --anonymous-auth=false scored: false - id: 1.2.2 text: "Ensure that the --token-auth-file parameter is not set (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--token-auth-file" set: false remediation: | Follow the documentation and configure alternate mechanisms for authentication. Then, edit the API server pod specification file $apiserverconf on the control plane node and remove the --token-auth-file= parameter. scored: true - id: 1.2.3 text: "Ensure that the --DenyServiceExternalIPs is not set (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: bin_op: or test_items: - flag: "--enable-admission-plugins" compare: op: nothave value: "DenyServiceExternalIPs" set: true - flag: "--enable-admission-plugins" set: false remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and remove the `DenyServiceExternalIPs` from enabled admission plugins. scored: true - id: 1.2.4 text: "Ensure that the --kubelet-https argument is set to true (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: bin_op: or test_items: - flag: "--kubelet-https" compare: op: eq value: true - flag: "--kubelet-https" set: false remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and remove the --kubelet-https parameter. scored: true - id: 1.2.5 text: "Ensure that the --kubelet-client-certificate and --kubelet-client-key arguments are set as appropriate (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: bin_op: and test_items: - flag: "--kubelet-client-certificate" set: true - flag: "--kubelet-client-key" set: true remediation: | Follow the Kubernetes documentation and set up the TLS connection between the apiserver and kubelets. Then, edit API server pod specification file $apiserverconf on the control plane node and set the kubelet client certificate and key parameters as below. --kubelet-client-certificate= --kubelet-client-key= scored: true - id: 1.2.6 text: "Ensure that the --kubelet-certificate-authority argument is set as appropriate (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--kubelet-certificate-authority" set: true remediation: | Follow the Kubernetes documentation and setup the TLS connection between the apiserver and kubelets. Then, edit the API server pod specification file $apiserverconf on the control plane node and set the --kubelet-certificate-authority parameter to the path to the cert file for the certificate authority. --kubelet-certificate-authority= scored: true - id: 1.2.7 text: "Ensure that the --authorization-mode argument is not set to AlwaysAllow (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--authorization-mode" compare: op: nothave value: "AlwaysAllow" set: true remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the --authorization-mode parameter to values other than AlwaysAllow. One such example could be as below. --authorization-mode=RBAC scored: true - id: 1.2.8 text: "Ensure that the --authorization-mode argument includes Node (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--authorization-mode" compare: op: has value: "Node" set: true remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the --authorization-mode parameter to a value that includes Node. --authorization-mode=Node,RBAC scored: true - id: 1.2.9 text: "Ensure that the --authorization-mode argument includes RBAC (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--authorization-mode" compare: op: has value: "RBAC" set: true remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the --authorization-mode parameter to a value that includes RBAC, for example `--authorization-mode=Node,RBAC`. scored: true - id: 1.2.10 text: "Ensure that the admission control plugin EventRateLimit is set (Manual)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--enable-admission-plugins" compare: op: has value: "EventRateLimit" set: true remediation: | Follow the Kubernetes documentation and set the desired limits in a configuration file. Then, edit the API server pod specification file $apiserverconf and set the below parameters. --enable-admission-plugins=...,EventRateLimit,... --admission-control-config-file= scored: false - id: 1.2.11 text: "Ensure that the admission control plugin AlwaysAdmit is not set (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: bin_op: or test_items: - flag: "--enable-admission-plugins" compare: op: nothave value: AlwaysAdmit set: true - flag: "--enable-admission-plugins" set: false remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and either remove the --enable-admission-plugins parameter, or set it to a value that does not include AlwaysAdmit. scored: true - id: 1.2.12 text: "Ensure that the admission control plugin AlwaysPullImages is set (Manual)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--enable-admission-plugins" compare: op: has value: "AlwaysPullImages" set: true remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the --enable-admission-plugins parameter to include AlwaysPullImages. --enable-admission-plugins=...,AlwaysPullImages,... scored: false - id: 1.2.13 text: "Ensure that the admission control plugin SecurityContextDeny is set if PodSecurityPolicy is not used (Manual)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: bin_op: or test_items: - flag: "--enable-admission-plugins" compare: op: has value: "SecurityContextDeny" set: true - flag: "--enable-admission-plugins" compare: op: has value: "PodSecurityPolicy" set: true remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the --enable-admission-plugins parameter to include SecurityContextDeny, unless PodSecurityPolicy is already in place. --enable-admission-plugins=...,SecurityContextDeny,... scored: false - id: 1.2.14 text: "Ensure that the admission control plugin ServiceAccount is set (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: bin_op: or test_items: - flag: "--disable-admission-plugins" compare: op: nothave value: "ServiceAccount" - flag: "--disable-admission-plugins" set: false remediation: | Follow the documentation and create ServiceAccount objects as per your environment. Then, edit the API server pod specification file $apiserverconf on the control plane node and ensure that the --disable-admission-plugins parameter is set to a value that does not include ServiceAccount. scored: true - id: 1.2.15 text: "Ensure that the admission control plugin NamespaceLifecycle is set (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: bin_op: or test_items: - flag: "--disable-admission-plugins" compare: op: nothave value: "NamespaceLifecycle" - flag: "--disable-admission-plugins" set: false remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the --disable-admission-plugins parameter to ensure it does not include NamespaceLifecycle. scored: true - id: 1.2.16 text: "Ensure that the admission control plugin NodeRestriction is set (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--enable-admission-plugins" compare: op: has value: "NodeRestriction" set: true remediation: | Follow the Kubernetes documentation and configure NodeRestriction plug-in on kubelets. Then, edit the API server pod specification file $apiserverconf on the control plane node and set the --enable-admission-plugins parameter to a value that includes NodeRestriction. --enable-admission-plugins=...,NodeRestriction,... scored: true - id: 1.2.17 text: "Ensure that the --secure-port argument is not set to 0 (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: bin_op: or test_items: - flag: "--secure-port" compare: op: gt value: 0 set: true - flag: "--secure-port" set: false remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and either remove the --secure-port parameter or set it to a different (non-zero) desired port. scored: true - id: 1.2.18 text: "Ensure that the --profiling argument is set to false (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--profiling" compare: op: eq value: false set: true remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the below parameter. --profiling=false scored: true - id: 1.2.19 text: "Ensure that the --audit-log-path argument is set (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" type: "skip" tests: test_items: - flag: "--audit-log-path" set: true remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the --audit-log-path parameter to a suitable path and file where you would like audit logs to be written, for example, --audit-log-path=/var/log/apiserver/audit.log scored: true - id: 1.2.20 text: "Ensure that the --audit-log-maxage argument is set to 30 or as appropriate (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" type: "skip" tests: test_items: - flag: "--audit-log-maxage" compare: op: gte value: 30 set: true remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the --audit-log-maxage parameter to 30 or as an appropriate number of days, for example, --audit-log-maxage=30 scored: true - id: 1.2.21 text: "Ensure that the --audit-log-maxbackup argument is set to 10 or as appropriate (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" type: "skip" tests: test_items: - flag: "--audit-log-maxbackup" compare: op: gte value: 10 set: true remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the --audit-log-maxbackup parameter to 10 or to an appropriate value. For example, --audit-log-maxbackup=10 scored: true - id: 1.2.22 text: "Ensure that the --audit-log-maxsize argument is set to 100 or as appropriate (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" type: "skip" tests: test_items: - flag: "--audit-log-maxsize" compare: op: gte value: 100 set: true remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the --audit-log-maxsize parameter to an appropriate size in MB. For example, to set it as 100 MB, --audit-log-maxsize=100 scored: true - id: 1.2.23 text: "Ensure that the --request-timeout argument is set as appropriate (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: bin_op: or test_items: - flag: "--request-timeout" set: false - flag: "--request-timeout" remediation: | Edit the API server pod specification file $apiserverconf and set the below parameter as appropriate and if needed. For example, --request-timeout=300s scored: true - id: 1.2.24 text: "Ensure that the --service-account-lookup argument is set to true (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: bin_op: or test_items: - flag: "--service-account-lookup" set: false - flag: "--service-account-lookup" compare: op: eq value: true remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the below parameter. --service-account-lookup=true Alternatively, you can delete the --service-account-lookup parameter from this file so that the default takes effect. scored: true - id: 1.2.25 text: "Ensure that the --service-account-key-file argument is set as appropriate (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--service-account-key-file" set: true remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the --service-account-key-file parameter to the public key file for service accounts. For example, --service-account-key-file= scored: true - id: 1.2.26 text: "Ensure that the --etcd-certfile and --etcd-keyfile arguments are set as appropriate (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: bin_op: and test_items: - flag: "--etcd-certfile" set: true - flag: "--etcd-keyfile" set: true remediation: | Follow the Kubernetes documentation and set up the TLS connection between the apiserver and etcd. Then, edit the API server pod specification file $apiserverconf on the control plane node and set the etcd certificate and key file parameters. --etcd-certfile= --etcd-keyfile= scored: true - id: 1.2.27 text: "Ensure that the --tls-cert-file and --tls-private-key-file arguments are set as appropriate (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: bin_op: and test_items: - flag: "--tls-cert-file" set: true - flag: "--tls-private-key-file" set: true remediation: | Follow the Kubernetes documentation and set up the TLS connection on the apiserver. Then, edit the API server pod specification file $apiserverconf on the control plane node and set the TLS certificate and private key file parameters. --tls-cert-file= --tls-private-key-file= scored: true - id: 1.2.28 text: "Ensure that the --client-ca-file argument is set as appropriate (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--client-ca-file" set: true remediation: | Follow the Kubernetes documentation and set up the TLS connection on the apiserver. Then, edit the API server pod specification file $apiserverconf on the control plane node and set the client certificate authority file. --client-ca-file= scored: true - id: 1.2.29 text: "Ensure that the --etcd-cafile argument is set as appropriate (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--etcd-cafile" set: true remediation: | Follow the Kubernetes documentation and set up the TLS connection between the apiserver and etcd. Then, edit the API server pod specification file $apiserverconf on the control plane node and set the etcd certificate authority file parameter. --etcd-cafile= scored: true - id: 1.2.30 text: "Ensure that the --encryption-provider-config argument is set as appropriate (Manual)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--encryption-provider-config" remediation: | Follow the Kubernetes documentation and configure a EncryptionConfig file. Then, edit the API server pod specification file $apiserverconf on the control plane node and set the --encryption-provider-config parameter to the path of that file. For example, --encryption-provider-config= scored: false - id: 1.2.31 text: "Ensure that encryption providers are appropriately configured (Manual)" type: "skip" audit: | ENCRYPTION_PROVIDER_CONFIG=$(ps -ef | grep $apiserverbin | grep -- --encryption-provider-config | sed 's%.*encryption-provider-config[= ]\([^ ]*\).*%\1%') if test -e $ENCRYPTION_PROVIDER_CONFIG; then grep -A1 'providers:' $ENCRYPTION_PROVIDER_CONFIG | tail -n1 | grep -o "[A-Za-z]*" | sed 's/^/provider=/'; fi tests: test_items: - flag: "provider" compare: op: valid_elements value: "aescbc,kms,secretbox" remediation: | Follow the Kubernetes documentation and configure a EncryptionConfig file. In this file, choose aescbc, kms or secretbox as the encryption provider. scored: false - id: 1.2.32 text: "Ensure that the API Server only makes use of Strong Cryptographic Ciphers (Manual)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--tls-cipher-suites" compare: op: valid_elements value: "TLS_AES_128_GCM_SHA256,TLS_AES_256_GCM_SHA384,TLS_CHACHA20_POLY1305_SHA256,TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA,TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,TLS_RSA_WITH_3DES_EDE_CBC_SHA,TLS_RSA_WITH_AES_128_CBC_SHA,TLS_RSA_WITH_AES_128_GCM_SHA256,TLS_RSA_WITH_AES_256_CBC_SHA,TLS_RSA_WITH_AES_256_GCM_SHA384" remediation: | Edit the API server pod specification file /etc/kubernetes/manifests/kube-apiserver.yaml on the control plane node and set the below parameter. --tls-cipher-suites=TLS_AES_128_GCM_SHA256,TLS_AES_256_GCM_SHA384,TLS_CHACHA20_POLY1305_SHA256, TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA,TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305, TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,TLS_RSA_WITH_3DES_EDE_CBC_SHA,TLS_RSA_WITH_AES_128_CBC_SHA, TLS_RSA_WITH_AES_128_GCM_SHA256,TLS_RSA_WITH_AES_256_CBC_SHA,TLS_RSA_WITH_AES_256_GCM_SHA384 scored: false type: skip - id: 1.3 text: "Controller Manager" checks: - id: 1.3.1 text: "Ensure that the --terminated-pod-gc-threshold argument is set as appropriate (Manual)" audit: "/bin/ps -ef | grep $controllermanagerbin | grep -v grep" tests: test_items: - flag: "--terminated-pod-gc-threshold" remediation: | Edit the Controller Manager pod specification file $controllermanagerconf on the control plane node and set the --terminated-pod-gc-threshold to an appropriate threshold, for example, --terminated-pod-gc-threshold=10 scored: false - id: 1.3.2 text: "Ensure that the --profiling argument is set to false (Automated)" audit: "/bin/ps -ef | grep $controllermanagerbin | grep -v grep" tests: test_items: - flag: "--profiling" compare: op: eq value: false set: true remediation: | Edit the Controller Manager pod specification file $controllermanagerconf on the control plane node and set the below parameter. --profiling=false scored: true - id: 1.3.3 text: "Ensure that the --use-service-account-credentials argument is set to true (Automated)" audit: "/bin/ps -ef | grep $controllermanagerbin | grep -v grep" tests: test_items: - flag: "--use-service-account-credentials" compare: op: noteq value: false set: true remediation: | Edit the Controller Manager pod specification file $controllermanagerconf on the control plane node to set the below parameter. --use-service-account-credentials=true scored: true - id: 1.3.4 text: "Ensure that the --service-account-private-key-file argument is set as appropriate (Automated)" audit: "/bin/ps -ef | grep $controllermanagerbin | grep -v grep" tests: test_items: - flag: "--service-account-private-key-file" set: true remediation: | Edit the Controller Manager pod specification file $controllermanagerconf on the control plane node and set the --service-account-private-key-file parameter to the private key file for service accounts. --service-account-private-key-file= scored: true - id: 1.3.5 text: "Ensure that the --root-ca-file argument is set as appropriate (Automated)" audit: "/bin/ps -ef | grep $controllermanagerbin | grep -v grep" tests: test_items: - flag: "--root-ca-file" set: true remediation: | Edit the Controller Manager pod specification file $controllermanagerconf on the control plane node and set the --root-ca-file parameter to the certificate bundle file`. --root-ca-file= scored: true - id: 1.3.6 text: "Ensure that the RotateKubeletServerCertificate argument is set to true (Automated)" audit: "/bin/ps -ef | grep $controllermanagerbin | grep -v grep" tests: bin_op: or test_items: - flag: "--feature-gates" compare: op: nothave value: "RotateKubeletServerCertificate=false" set: true - flag: "--feature-gates" set: false remediation: | Edit the Controller Manager pod specification file $controllermanagerconf on the control plane node and set the --feature-gates parameter to include RotateKubeletServerCertificate=true. --feature-gates=RotateKubeletServerCertificate=true scored: true type: skip - id: 1.3.7 text: "Ensure that the --bind-address argument is set to 127.0.0.1 (Automated)" audit: "/bin/ps -ef | grep $controllermanagerbin | grep -v grep" tests: bin_op: or test_items: - flag: "--bind-address" compare: op: eq value: "127.0.0.1" set: true - flag: "--bind-address" set: false remediation: | Edit the Controller Manager pod specification file $controllermanagerconf on the control plane node and ensure the correct value for the --bind-address parameter scored: true - id: 1.4 text: "Scheduler" checks: - id: 1.4.1 text: "Ensure that the --profiling argument is set to false (Automated)" audit: "/bin/ps -ef | grep $schedulerbin | grep -v grep" tests: test_items: - flag: "--profiling" compare: op: eq value: false set: true remediation: | Edit the Scheduler pod specification file $schedulerconf file on the control plane node and set the below parameter. --profiling=false scored: true - id: 1.4.2 text: "Ensure that the --bind-address argument is set to 127.0.0.1 (Automated)" audit: "/bin/ps -ef | grep $schedulerbin | grep -v grep" tests: bin_op: or test_items: - flag: "--bind-address" compare: op: eq value: "127.0.0.1" set: true - flag: "--bind-address" set: false remediation: | Edit the Scheduler pod specification file $schedulerconf on the control plane node and ensure the correct value for the --bind-address parameter scored: true ================================================ FILE: cfg/rke2-cis-1.23/node.yaml ================================================ --- controls: version: "rke2-cis-1.23" id: 4 text: "Worker Node Security Configuration" type: "node" groups: - id: 4.1 text: "Worker Node Configuration Files" checks: - id: 4.1.1 text: "Ensure that the kubelet service file permissions are set to 644 or more restrictive (Automated)" audit: '/bin/sh -c ''if test -e $kubeletsvc; then stat -c permissions=%a $kubeletsvc; fi'' ' type: "skip" tests: test_items: - flag: "permissions" compare: op: bitmask value: "644" remediation: | Run the below command (based on the file location on your system) on the each worker node. For example, chmod 644 $kubeletsvc scored: true - id: 4.1.2 text: "Ensure that the kubelet service file ownership is set to root:root (Automated)" audit: '/bin/sh -c ''if test -e $kubeletsvc; then stat -c %U:%G $kubeletsvc; fi'' ' type: "skip" tests: test_items: - flag: "root:root" set: true remediation: | Run the below command (based on the file location on your system) on the each worker node. For example, chown root:root $kubeletsvc scored: true - id: 4.1.3 text: "If proxy kubeconfig file exists ensure permissions are set to 644 or more restrictive (Manual)" audit: '/bin/sh -c ''if test -e $proxykubeconfig; then stat -c permissions=%a $proxykubeconfig; fi'' ' tests: bin_op: or test_items: - flag: "permissions" set: true compare: op: bitmask value: "644" remediation: | Run the below command (based on the file location on your system) on the each worker node. For example, chmod 644 $proxykubeconfig scored: false - id: 4.1.4 text: "If proxy kubeconfig file exists ensure ownership is set to root:root (Manual)" audit: '/bin/sh -c ''if test -e $proxykubeconfig; then stat -c %U:%G $proxykubeconfig; fi'' ' tests: bin_op: or test_items: - flag: root:root remediation: | Run the below command (based on the file location on your system) on the each worker node. For example, chown root:root $proxykubeconfig scored: false - id: 4.1.5 text: "Ensure that the --kubeconfig kubelet.conf file permissions are set to 644 or more restrictive (Automated)" audit: '/bin/sh -c ''if test -e $kubeletkubeconfig; then stat -c permissions=%a $kubeletkubeconfig; fi'' ' tests: test_items: - flag: "644" compare: op: eq value: "644" set: true remediation: | Run the below command (based on the file location on your system) on the each worker node. For example, chmod 644 $kubeletkubeconfig scored: true - id: 4.1.6 text: "Ensure that the --kubeconfig kubelet.conf file ownership is set to root:root (Automated)" audit: '/bin/sh -c ''if test -e $kubeletkubeconfig; then stat -c %U:%G $kubeletkubeconfig; fi'' ' tests: test_items: - flag: root:root set: true compare: op: eq value: root:root remediation: | Run the below command (based on the file location on your system) on the each worker node. For example, chown root:root $kubeletkubeconfig scored: true - id: 4.1.7 text: "Ensure that the certificate authorities file permissions are set to 644 or more restrictive (Manual)" audit: "check_cafile_permissions.sh" tests: test_items: - flag: "permissions" compare: op: bitmask value: "644" set: true remediation: | Run the following command to modify the file permissions of the --client-ca-file chmod 644 scored: false - id: 4.1.8 text: "Ensure that the client certificate authorities file ownership is set to root:root (Manual)" audit: "check_cafile_ownership.sh" tests: test_items: - flag: root:root remediation: | Run the following command to modify the ownership of the --client-ca-file. chown root:root scored: false - id: 4.1.9 text: "Ensure that the kubelet --config configuration file has permissions set to 644 or more restrictive (Automated)" audit: | /bin/sh -c 'if test -e "$kubeletconf"; then stat -c "permissions=%a" "$kubeletconf" else echo "File not found" fi' tests: bin_op: or test_items: - flag: "permissions" compare: op: bitmask value: "644" set: true - flag: "File not found" remediation: | Run the following command (using the config file location identified in the Audit step) chmod 644 $kubeletconf scored: true - id: 4.1.10 text: "Ensure that the kubelet --config configuration file ownership is set to root:root (Automated)" audit: | /bin/sh -c 'if test -e "$kubeletconf"; then stat -c "%U:%G" "$kubeletconf" else echo "File not found" fi' tests: bin_op: or test_items: - flag: root:root set: true - flag: File not found remediation: | Run the following command (using the config file location identified in the Audit step) chown root:root $kubeletconf scored: true - id: 4.2 text: "Kubelet" checks: - id: 4.2.1 text: "Ensure that the --anonymous-auth argument is set to false (Automated)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: "--anonymous-auth" path: '{.authentication.anonymous.enabled}' compare: op: eq value: false remediation: | If using a Kubelet config file, edit the file to set `authentication: anonymous: enabled` to `false`. If using executable arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_SYSTEM_PODS_ARGS variable. `--anonymous-auth=false` Based on your system, restart the kubelet service. For example, systemctl daemon-reload systemctl restart kubelet.service scored: true - id: 4.2.2 text: "Ensure that the --authorization-mode argument is not set to AlwaysAllow (Automated)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --authorization-mode path: '{.authorization.mode}' compare: op: nothave value: AlwaysAllow remediation: | If using a Kubelet config file, edit the file to set `authorization.mode` to Webhook. If using executable arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_AUTHZ_ARGS variable. --authorization-mode=Webhook Based on your system, restart the kubelet service. For example, systemctl daemon-reload systemctl restart kubelet.service scored: true - id: 4.2.3 text: "Ensure that the --client-ca-file argument is set as appropriate (Automated)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --client-ca-file path: '{.authentication.x509.clientCAFile}' remediation: | If using a Kubelet config file, edit the file to set `authentication.x509.clientCAFile` to the location of the client CA file. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_AUTHZ_ARGS variable. --client-ca-file= Based on your system, restart the kubelet service. For example, systemctl daemon-reload systemctl restart kubelet.service scored: true - id: 4.2.4 text: "Ensure that the --read-only-port argument is set to 0 (Manual)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: bin_op: or test_items: - flag: "--read-only-port" path: '{.readOnlyPort}' compare: op: eq value: 0 - flag: "--read-only-port" path: '{.readOnlyPort}' set: false remediation: | If using a Kubelet config file, edit the file to set `readOnlyPort` to 0. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_SYSTEM_PODS_ARGS variable. --read-only-port=0 Based on your system, restart the kubelet service. For example, systemctl daemon-reload systemctl restart kubelet.service scored: false - id: 4.2.5 text: "Ensure that the --streaming-connection-idle-timeout argument is not set to 0 (Manual)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --streaming-connection-idle-timeout path: '{.streamingConnectionIdleTimeout}' compare: op: noteq value: 0 - flag: --streaming-connection-idle-timeout path: '{.streamingConnectionIdleTimeout}' set: false bin_op: or remediation: | If using a Kubelet config file, edit the file to set `streamingConnectionIdleTimeout` to a value other than 0. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_SYSTEM_PODS_ARGS variable. --streaming-connection-idle-timeout=5m Based on your system, restart the kubelet service. For example, systemctl daemon-reload systemctl restart kubelet.service scored: false - id: 4.2.6 text: "Ensure that the --protect-kernel-defaults argument is set to true (Automated)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: bin_op: or test_items: - flag: --protect-kernel-defaults path: '{.protectKernelDefaults}' compare: op: eq value: true set: true - flag: --protect-kernel-defaults path: '{.protectKernelDefaults}' set: false remediation: | If using a Kubelet config file, edit the file to set `protectKernelDefaults` to `true`. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_SYSTEM_PODS_ARGS variable. --protect-kernel-defaults=true Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: true - id: 4.2.7 text: "Ensure that the --make-iptables-util-chains argument is set to true (Automated)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --make-iptables-util-chains path: '{.makeIPTablesUtilChains}' compare: op: eq value: true - flag: --make-iptables-util-chains path: '{.makeIPTablesUtilChains}' set: false bin_op: or remediation: | If using a Kubelet config file, edit the file to set `makeIPTablesUtilChains` to `true`. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and remove the --make-iptables-util-chains argument from the KUBELET_SYSTEM_PODS_ARGS variable. Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: true - id: 4.2.8 text: "Ensure that the --hostname-override argument is not set (Manual)" # This is one of those properties that can only be set as a command line argument. # To check if the property is set as expected, we need to parse the kubelet command # instead reading the Kubelet Configuration file. audit: "/bin/ps -fC $kubeletbin " tests: test_items: - flag: --hostname-override set: false remediation: | Edit the kubelet service file $kubeletsvc on each worker node and remove the --hostname-override argument from the KUBELET_SYSTEM_PODS_ARGS variable. Based on your system, restart the kubelet service. For example, systemctl daemon-reload systemctl restart kubelet.service scored: false type: skip - id: 4.2.9 text: "Ensure that the --event-qps argument is set to 0 or a level which ensures appropriate event capture (Manual)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --event-qps path: '{.eventRecordQPS}' compare: op: eq value: 0 remediation: | If using a Kubelet config file, edit the file to set `eventRecordQPS` to an appropriate level. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_SYSTEM_PODS_ARGS variable. Based on your system, restart the kubelet service. For example, systemctl daemon-reload systemctl restart kubelet.service scored: false - id: 4.2.10 text: "Ensure that the --tls-cert-file and --tls-private-key-file arguments are set as appropriate (Manual)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --tls-cert-file path: '{.tlsCertFile}' - flag: --tls-private-key-file path: '{.tlsPrivateKeyFile}' remediation: | If using a Kubelet config file, edit the file to set `tlsCertFile` to the location of the certificate file to use to identify this Kubelet, and `tlsPrivateKeyFile` to the location of the corresponding private key file. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameters in KUBELET_CERTIFICATE_ARGS variable. --tls-cert-file= --tls-private-key-file= Based on your system, restart the kubelet service. For example, systemctl daemon-reload systemctl restart kubelet.service scored: false - id: 4.2.11 text: "Ensure that the --rotate-certificates argument is not set to false (Automated)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --rotate-certificates path: '{.rotateCertificates}' compare: op: eq value: true - flag: --rotate-certificates path: '{.rotateCertificates}' set: false bin_op: or remediation: | If using a Kubelet config file, edit the file to add the line `rotateCertificates` to `true` or remove it altogether to use the default value. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and remove --rotate-certificates=false argument from the KUBELET_CERTIFICATE_ARGS variable. Based on your system, restart the kubelet service. For example, systemctl daemon-reload systemctl restart kubelet.service scored: true - id: 4.2.12 text: "Verify that the RotateKubeletServerCertificate argument is set to true (Manual)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: bin_op: or test_items: - flag: RotateKubeletServerCertificate path: '{.featureGates.RotateKubeletServerCertificate}' compare: op: nothave value: false - flag: RotateKubeletServerCertificate path: '{.featureGates.RotateKubeletServerCertificate}' set: false remediation: | Edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_CERTIFICATE_ARGS variable. --feature-gates=RotateKubeletServerCertificate=true Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: false - id: 4.2.13 text: "Ensure that the Kubelet only makes use of Strong Cryptographic Ciphers (Manual)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --tls-cipher-suites path: '{range .tlsCipherSuites[:]}{}{'',''}{end}' compare: op: valid_elements value: 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 remediation: | If using a Kubelet config file, edit the file to set `tlsCipherSuites` to 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 or to a subset of these values. If using executable arguments, edit the kubelet service file $kubeletsvc on each worker node and set the --tls-cipher-suites parameter as follows, or to a subset of these values. --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 Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: false ================================================ FILE: cfg/rke2-cis-1.23/policies.yaml ================================================ --- controls: version: "rke2-cis-1.23" id: 5 text: "Kubernetes Policies" type: "policies" groups: - id: 5.1 text: "RBAC and Service Accounts" checks: - id: 5.1.1 text: "Ensure that the cluster-admin role is only used where required (Manual)" type: "manual" remediation: | Identify all clusterrolebindings to the cluster-admin role. Check if they are used and if they need this role or if they could use a role with fewer privileges. Where possible, first bind users to a lower privileged role and then remove the clusterrolebinding to the cluster-admin role : kubectl delete clusterrolebinding [name] scored: false - id: 5.1.2 text: "Minimize access to secrets (Manual)" type: "manual" remediation: | Where possible, remove get, list and watch access to Secret objects in the cluster. scored: false - id: 5.1.3 text: "Minimize wildcard use in Roles and ClusterRoles (Manual)" type: "manual" remediation: | Where possible replace any use of wildcards in clusterroles and roles with specific objects or actions. scored: false - id: 5.1.4 text: "Minimize access to create pods (Manual)" type: "manual" remediation: | Where possible, remove create access to pod objects in the cluster. scored: false - id: 5.1.5 text: "Ensure that default service accounts are not actively used. (Manual)" type: "manual" remediation: | Create explicit service accounts wherever a Kubernetes workload requires specific access to the Kubernetes API server. Modify the configuration of each default service account to include this value automountServiceAccountToken: false scored: false - id: 5.1.6 text: "Ensure that Service Account Tokens are only mounted where necessary (Manual)" type: "manual" remediation: | Modify the definition of pods and service accounts which do not need to mount service account tokens to disable it. scored: false - id: 5.1.7 text: "Avoid use of system:masters group (Manual)" type: "manual" remediation: | Remove the system:masters group from all users in the cluster. scored: false - id: 5.1.8 text: "Limit use of the Bind, Impersonate and Escalate permissions in the Kubernetes cluster (Manual)" type: "manual" remediation: | Where possible, remove the impersonate, bind and escalate rights from subjects. scored: false - id: 5.2 text: "Pod Security Standards" checks: - id: 5.2.1 text: "Ensure that the cluster has at least one active policy control mechanism in place (Manual)" type: "manual" remediation: | Ensure that either Pod Security Admission or an external policy control system is in place for every namespace which contains user workloads. scored: false - id: 5.2.2 text: "Minimize the admission of privileged containers (Manual)" type: "manual" remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of privileged containers. scored: false - id: 5.2.3 text: "Minimize the admission of containers wishing to share the host process ID namespace (Automated)" type: "manual" remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of `hostPID` containers. scored: false - id: 5.2.4 text: "Minimize the admission of containers wishing to share the host IPC namespace (Automated)" type: "manual" remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of `hostIPC` containers. scored: false - id: 5.2.5 text: "Minimize the admission of containers wishing to share the host network namespace (Automated)" type: "manual" remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of `hostNetwork` containers. scored: false - id: 5.2.6 text: "Minimize the admission of containers with allowPrivilegeEscalation (Automated)" type: "manual" remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of containers with `.spec.allowPrivilegeEscalation` set to `true`. scored: false - id: 5.2.7 text: "Minimize the admission of root containers (Automated)" type: "manual" remediation: | Create a policy for each namespace in the cluster, ensuring that either `MustRunAsNonRoot` or `MustRunAs` with the range of UIDs not including 0, is set. scored: false - id: 5.2.8 text: "Minimize the admission of containers with the NET_RAW capability (Automated)" type: "manual" remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of containers with the `NET_RAW` capability. scored: false - id: 5.2.9 text: "Minimize the admission of containers with added capabilities (Automated)" type: "manual" remediation: | Ensure that `allowedCapabilities` is not present in policies for the cluster unless it is set to an empty array. scored: false - id: 5.2.10 text: "Minimize the admission of containers with capabilities assigned (Manual)" type: "manual" remediation: | Review the use of capabilites in applications running on your cluster. Where a namespace contains applications which do not require any Linux capabities to operate consider adding a PSP which forbids the admission of containers which do not drop all capabilities. scored: false - id: 5.2.11 text: "Minimize the admission of Windows HostProcess containers (Manual)" type: "manual" remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of containers that have `.securityContext.windowsOptions.hostProcess` set to `true`. scored: false - id: 5.2.12 text: "Minimize the admission of HostPath volumes (Manual)" type: "manual" remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of containers with `hostPath` volumes. scored: false - id: 5.2.13 text: "Minimize the admission of containers which use HostPorts (Manual)" type: "manual" remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of containers which use `hostPort` sections. scored: false - id: 5.3 text: "Network Policies and CNI" checks: - id: 5.3.1 text: "Ensure that the CNI in use supports NetworkPolicies (Manual)" type: "manual" remediation: | If the CNI plugin in use does not support network policies, consideration should be given to making use of a different plugin, or finding an alternate mechanism for restricting traffic in the Kubernetes cluster. scored: false - id: 5.3.2 text: "Ensure that all Namespaces have NetworkPolicies defined (Manual)" type: "manual" remediation: | Follow the documentation and create NetworkPolicy objects as you need them. scored: false - id: 5.4 text: "Secrets Management" checks: - id: 5.4.1 text: "Prefer using Secrets as files over Secrets as environment variables (Manual)" type: "manual" remediation: | If possible, rewrite application code to read Secrets from mounted secret files, rather than from environment variables. scored: false - id: 5.4.2 text: "Consider external secret storage (Manual)" type: "manual" remediation: | Refer to the Secrets management options offered by your cloud provider or a third-party secrets management solution. scored: false - id: 5.5 text: "Extensible Admission Control" checks: - id: 5.5.1 text: "Configure Image Provenance using ImagePolicyWebhook admission controller (Manual)" type: "manual" remediation: | Follow the Kubernetes documentation and setup image provenance. scored: false - id: 5.7 text: "General Policies" checks: - id: 5.7.1 text: "Create administrative boundaries between resources using namespaces (Manual)" type: "manual" remediation: | Follow the documentation and create namespaces for objects in your deployment as you need them. scored: false - id: 5.7.2 text: "Ensure that the seccomp profile is set to docker/default in your Pod definitions (Manual)" type: "manual" remediation: | Use `securityContext` to enable the docker/default seccomp profile in your pod definitions. An example is as below: securityContext: seccompProfile: type: RuntimeDefault scored: false - id: 5.7.3 text: "Apply SecurityContext to your Pods and Containers (Manual)" type: "manual" remediation: | Follow the Kubernetes documentation and apply SecurityContexts to your Pods. For a suggested list of SecurityContexts, you may refer to the CIS Security Benchmark for Docker Containers. scored: false - id: 5.7.4 text: "The default namespace should not be used (Manual)" type: "manual" remediation: | Ensure that namespaces are created to allow for appropriate segregation of Kubernetes resources and that all new resources are created in a specific namespace. scored: false ================================================ FILE: cfg/rke2-cis-1.24/config.yaml ================================================ --- ## Version-specific settings that override the values in cfg/config.yaml ================================================ FILE: cfg/rke2-cis-1.24/controlplane.yaml ================================================ --- controls: version: "rke2-cis-1.24" id: 3 text: "Control Plane Configuration" type: "controlplane" groups: - id: 3.1 text: "Authentication and Authorization" checks: - id: 3.1.1 text: "Client certificate authentication should not be used for users (Manual)" type: "manual" remediation: | Alternative mechanisms provided by Kubernetes such as the use of OIDC should be implemented in place of client certificates. scored: false - id: 3.2 text: "Logging" checks: - id: 3.2.1 text: "Ensure that a minimal audit policy is created (Automated)" audit: "/bin/ps -ef | grep kube-apiserver | grep -v grep | grep -o audit-policy-file" type: "skip" tests: test_items: - flag: "--audit-policy-file" compare: op: eq value: "--audit-policy-file" set: true remediation: | Create an audit policy file for your cluster. scored: true - id: 3.2.2 text: "Ensure that the audit policy covers key security concerns (Manual)" type: "manual" remediation: | Review the audit policy provided for the cluster and ensure that it covers at least the following areas, - Access to Secrets managed by the cluster. Care should be taken to only log Metadata for requests to Secrets, ConfigMaps, and TokenReviews, in order to avoid risk of logging sensitive data. - Modification of Pod and Deployment objects. - Use of `pods/exec`, `pods/portforward`, `pods/proxy` and `services/proxy`. For most requests, minimally logging at the Metadata level is recommended (the most basic level of logging). scored: false ================================================ FILE: cfg/rke2-cis-1.24/etcd.yaml ================================================ --- controls: version: "rke2-cis-1.24" id: 2 text: "Etcd Node Configuration" type: "etcd" groups: - id: 2 text: "Etcd Node Configuration" checks: - id: 2.1 text: "Ensure that the --cert-file and --key-file arguments are set as appropriate (Automated)" audit: "/bin/ps -ef | /bin/grep $etcdbin | /bin/grep -v grep" tests: bin_op: and test_items: - flag: "--cert-file" env: "ETCD_CERT_FILE" - flag: "--key-file" env: "ETCD_KEY_FILE" remediation: | Follow the etcd service documentation and configure TLS encryption. Then, edit the etcd pod specification file /etc/kubernetes/manifests/etcd.yaml on the master node and set the below parameters. --cert-file= --key-file= scored: true type: "skip" - id: 2.2 text: "Ensure that the --client-cert-auth argument is set to true (Automated)" audit: "/bin/ps -ef | /bin/grep $etcdbin | /bin/grep -v grep" tests: test_items: - flag: "--client-cert-auth" env: "ETCD_CLIENT_CERT_AUTH" compare: op: eq value: true type: "skip" remediation: | Edit the etcd pod specification file $etcdconf on the master node and set the below parameter. --client-cert-auth="true" scored: true - id: 2.3 text: "Ensure that the --auto-tls argument is not set to true (Automated)" audit: "/bin/ps -ef | /bin/grep $etcdbin | /bin/grep -v grep" tests: bin_op: or test_items: - flag: "--auto-tls" env: "ETCD_AUTO_TLS" set: false - flag: "--auto-tls" env: "ETCD_AUTO_TLS" compare: op: eq value: false remediation: | Edit the etcd pod specification file $etcdconf on the master node and either remove the --auto-tls parameter or set it to false. --auto-tls=false scored: true - id: 2.4 text: "Ensure that the --peer-cert-file and --peer-key-file arguments are set as appropriate (Automated)" audit: "/bin/ps -ef | /bin/grep $etcdbin | /bin/grep -v grep" tests: bin_op: and test_items: - flag: "--peer-cert-file" env: "ETCD_PEER_CERT_FILE" set: true - flag: "--peer-key-file" env: "ETCD_PEER_KEY_FILE" set: true remediation: | Follow the etcd service documentation and configure peer TLS encryption as appropriate for your etcd cluster. Then, edit the etcd pod specification file $etcdconf on the master node and set the below parameters. --peer-client-file= --peer-key-file= scored: true type: "skip" - id: 2.5 text: "Ensure that the --peer-client-cert-auth argument is set to true (Automated)" audit: "/bin/ps -ef | /bin/grep $etcdbin | /bin/grep -v grep" tests: test_items: - flag: "--peer-client-cert-auth" env: "ETCD_PEER_CLIENT_CERT_AUTH" compare: op: eq value: true set: true remediation: | Edit the etcd pod specification file $etcdconf on the master node and set the below parameter. --peer-client-cert-auth=true scored: true type: "skip" - id: 2.6 text: "Ensure that the --peer-auto-tls argument is not set to true (Automated)" audit: "/bin/ps -ef | /bin/grep $etcdbin | /bin/grep -v grep" tests: bin_op: or test_items: - flag: "--peer-auto-tls" env: "ETCD_PEER_AUTO_TLS" set: false - flag: "--peer-auto-tls" env: "ETCD_PEER_AUTO_TLS" compare: op: eq value: false set: true remediation: | Edit the etcd pod specification file $etcdconf on the master node and either remove the --peer-auto-tls parameter or set it to false. --peer-auto-tls=false scored: true - id: 2.7 text: "Ensure that a unique Certificate Authority is used for etcd (Automated)" audit: "/bin/ps -ef | /bin/grep $etcdbin | /bin/grep -v grep" audit_config: "cat /var/lib/rancher/rke2/server/db/etcd/config" tests: bin_op: or test_items: - flag: "--trusted-ca-file" env: "ETCD_TRUSTED_CA_FILE" - path: "{.peer-transport-security.trusted-ca-file}" compare: op: eq value: "/var/lib/rancher/rke2/server/tls/etcd/peer-ca.crt" set: true remediation: | [Manual test] Follow the etcd documentation and create a dedicated certificate authority setup for the etcd service. Then, edit the etcd pod specification file $etcdconf on the master node and set the below parameter. --trusted-ca-file= scored: false ================================================ FILE: cfg/rke2-cis-1.24/master.yaml ================================================ --- controls: version: "rke2-cis-1.24" id: 1 text: "Control Plane Security Configuration" type: "master" groups: - id: 1.1 text: "Control Plane Node Configuration Files" checks: - id: 1.1.1 text: "Ensure that the API server pod specification file permissions are set to 644 or more restrictive (Automated)" audit: "stat -c permissions=%a /var/lib/rancher/rke2/agent/pod-manifests/kube-apiserver.yaml" tests: test_items: - flag: "permissions" compare: op: eq value: "644" set: true remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chmod 644 $apiserverconf scored: true - id: 1.1.2 text: "Ensure that the API server pod specification file ownership is set to root:root (Automated)" audit: "/bin/sh -c 'if test -e $apiserverconf; then stat -c %U:%G $apiserverconf; fi'" tests: test_items: - flag: "root:root" compare: op: eq value: "root:root" set: true remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chown root:root $apiserverconf scored: true - id: 1.1.3 text: "Ensure that the controller manager pod specification file permissions are set to 600 or more restrictive (Automated)" audit: "/bin/sh -c 'if test -e $controllermanagerconf; then stat -c permissions=%a $controllermanagerconf; fi'" tests: test_items: - flag: "644" compare: op: eq value: "600" set: true remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chmod 600 $controllermanagerconf scored: true - id: 1.1.4 text: "Ensure that the controller manager pod specification file ownership is set to root:root (Automated)" audit: "/bin/sh -c 'if test -e $controllermanagerconf; then stat -c %U:%G $controllermanagerconf; fi'" tests: test_items: - flag: "root:root" compare: op: eq value: "root:root" set: true remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chown root:root $controllermanagerconf scored: true - id: 1.1.5 text: "Ensure that the scheduler pod specification file permissions are set to 600 or more restrictive (Automated)" audit: "/bin/sh -c 'if test -e $schedulerconf; then stat -c permissions=%a $schedulerconf; fi'" tests: test_items: - flag: "600" compare: op: eq value: "600" set: true remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chmod 600 $schedulerconf scored: true - id: 1.1.6 text: "Ensure that the scheduler pod specification file ownership is set to root:root (Automated)" audit: "/bin/sh -c 'if test -e $schedulerconf; then stat -c %U:%G $schedulerconf; fi'" tests: test_items: - flag: "root:root" set: true remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chown root:root $schedulerconf scored: true - id: 1.1.7 text: "Ensure that the etcd pod specification file permissions are set to 644 or more restrictive (Automated)" audit: | /bin/sh -c ' if [ -e "$etcdconf" ]; then stat -c "permissions=%a %n" "$etcdconf" else echo "Directory not found" fi ' tests: bin_op: or test_items: - flag: "permissions" compare: op: bitmask value: "644" - flag: "Directory not found" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chmod 644 $etcdconf scored: true - id: 1.1.8 text: "Ensure that the etcd pod specification file ownership is set to root:root (Automated)" audit: | /bin/sh -c ' if [ -e $etcdconf ]; then stat -c "ownership=%U:%G %n" $etcdconf else echo "Directory not found" fi ' tests: bin_op: or test_items: - flag: "ownership" compare: op: eq value: "root:root" set: true - flag: "Directory not found" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chown root:root $etcdconf scored: true - id: 1.1.9 text: "Ensure that the Container Network Interface file permissions are set to 600 or more restrictive (Manual)" audit: | ps -fC ${kubeletbin:-kubelet} | grep -- --cni-conf-dir || echo "/etc/cni/net.d" | sed 's%.*cni-conf-dir[= ]\([^ ]*\).*%\1%' | xargs -I{} find {} -mindepth 1 | xargs --no-run-if-empty stat -c permissions=%a find /var/lib/cni/networks -type f 2> /dev/null | xargs --no-run-if-empty stat -c permissions=%a use_multiple_values: true tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chmod 600 scored: false - id: 1.1.10 text: "Ensure that the Container Network Interface file ownership is set to root:root (Manual)" audit: | '/bin/sh -c "if [[ -e /etc/cni/net.d ]]; then ps -fC "${kubeletbin:-kubelet}" | grep -- --cni-conf-dir || echo "/etc/cni/net.d" | sed 's%.*cni-conf-dir[= ]\([^ ]*\).*%\1%' | xargs -I{} find {} -mindepth 1 | xargs --no-run-if-empty stat -c %U:%G find /var/lib/cni/networks -type f 2> /dev/null | xargs --no-run-if-empty stat -c %U:%G else echo "File not found" fi' use_multiple_values: true tests: bin_op: or test_items: - flag: "root:root" - flag: "File not found" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chown root:root scored: false - id: 1.1.11 text: "Ensure that the etcd data directory permissions are set to 700 or more restrictive (Automated)" audit: | /bin/sh -c 'if [ -d "/var/lib/rancher/rke2/server/db/etcd" ]; then stat -c "permissions=%a" "/var/lib/rancher/rke2/server/db/etcd" else echo "Directory not found" fi' tests: bin_op: or test_items: - flag: "permissions" compare: op: bitmask value: "700" set: true - flag: "Directory not found" remediation: | On the etcd server node, get the etcd data directory, passed as an argument --data-dir, from the command 'ps -ef | grep etcd'. Run the below command (based on the etcd data directory found above). For example, chmod 700 /var/lib/etcd scored: true - id: 1.1.12 text: "Ensure that the etcd data directory ownership is set to etcd:etcd (Automated)" type: "skip" audit: "stat -c %U:%G /var/lib/rancher/rke2/server/db/etcd" tests: test_items: - flag: "etcd:etcd" remediation: | On the etcd server node, get the etcd data directory, passed as an argument --data-dir, from the command 'ps -ef | grep etcd'. Run the below command (based on the etcd data directory found above). For example, chown etcd:etcd /var/lib/etcd scored: true - id: 1.1.13 text: "Ensure that the admin.conf file permissions are set to 600 or more restrictive (Automated)" audit: "stat -c permissions=%a /var/lib/rancher/rke2/server/cred/admin.kubeconfig" tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" set: true remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chmod 600 /etc/kubernetes/admin.conf scored: true - id: 1.1.14 text: "Ensure that the admin.conf file ownership is set to root:root (Automated)" audit: "stat -c %U:%G /var/lib/rancher/rke2/server/cred/admin.kubeconfig" tests: test_items: - flag: "root:root" compare: op: eq value: "root:root" set: true remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chown root:root /etc/kubernetes/admin.conf scored: true - id: 1.1.15 text: "Ensure that the scheduler.conf file permissions are set to 600 or more restrictive (Automated)" audit: "stat -c %a $schedulerkubeconfig" tests: test_items: - flag: "600" compare: op: eq value: "600" set: true remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chmod 600 $schedulerkubeconfig scored: true - id: 1.1.16 text: "Ensure that the scheduler.conf file ownership is set to root:root (Automated)" audit: "stat -c %U:%G $schedulerkubeconfig" tests: test_items: - flag: "root:root" compare: op: eq value: "root:root" set: true remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chown root:root $schedulerkubeconfig scored: true - id: 1.1.17 text: "Ensure that the controller-manager.conf file permissions are set to 600 or more restrictive (Automated)" audit: "stat -c %a $controllermanagerkubeconfig" tests: test_items: - flag: "600" compare: op: eq value: "600" set: true remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chmod 600 $controllermanagerkubeconfig scored: true - id: 1.1.18 text: "Ensure that the controller-manager.conf file ownership is set to root:root (Automated)" audit: "stat -c %U:%G $controllermanagerkubeconfig" tests: test_items: - flag: "root:root" compare: op: eq value: "root:root" set: true remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chown root:root $controllermanagerkubeconfig scored: true - id: 1.1.19 text: "Ensure that the Kubernetes PKI directory and file ownership is set to root:root (Automated)" audit: "stat -c %U:%G /var/lib/rancher/rke2/server/tls" use_multiple_values: true tests: test_items: - flag: "root:root" compare: op: eq value: "root:root" set: true remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chown -R root:root /etc/kubernetes/pki/ scored: true - id: 1.1.20 text: "Ensure that the Kubernetes PKI certificate file permissions are set to 600 or more restrictive (Manual)" audit: "stat -c permissions=%a /var/lib/rancher/rke2/server/tls/*.crt" use_multiple_values: true tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" set: true remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chmod -R 600 /var/lib/rancher/rke2/server/tls/*.crt scored: false - id: 1.1.21 text: "Ensure that the Kubernetes PKI key file permissions are set to 600 (Manual)" audit: | '/bin/sh -c if test -e "/var/lib/rancher/rke2/server/tls/*.key"; then stat -c "%a" "/var/lib/rancher/rke2/server/tls/*.key" else echo "File not found" fi' use_multiple_values: true tests: bin_op: or test_items: - flag: "permissions" - flag: "File not found" compare: op: eq value: "600" set: true remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chmod -R 600 /var/lib/rancher/rke2/server/tls/*.key scored: false - id: 1.2 text: "API Server" checks: - id: 1.2.1 text: "Ensure that the --anonymous-auth argument is set to false (Manual)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" type: manual tests: test_items: - flag: "--anonymous-auth" compare: op: eq value: false remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the below parameter. --anonymous-auth=false scored: false - id: 1.2.2 text: "Ensure that the --token-auth-file parameter is not set (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--token-auth-file" set: false remediation: | Follow the documentation and configure alternate mechanisms for authentication. Then, edit the API server pod specification file $apiserverconf on the control plane node and remove the --token-auth-file= parameter. scored: true - id: 1.2.3 text: "Ensure that the --DenyServiceExternalIPs is not set (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: bin_op: or test_items: - flag: "--enable-admission-plugins" compare: op: nothave value: "DenyServiceExternalIPs" set: true - flag: "--enable-admission-plugins" set: false remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and remove the `DenyServiceExternalIPs` from enabled admission plugins. scored: true - id: 1.2.4 text: "Ensure that the --kubelet-https argument is set to true (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: bin_op: or test_items: - flag: "--kubelet-https" compare: op: eq value: true - flag: "--kubelet-https" set: false remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and remove the --kubelet-https parameter. scored: true - id: 1.2.5 text: "Ensure that the --kubelet-client-certificate and --kubelet-client-key arguments are set as appropriate (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: bin_op: and test_items: - flag: "--kubelet-client-certificate" set: true - flag: "--kubelet-client-key" set: true remediation: | Follow the Kubernetes documentation and set up the TLS connection between the apiserver and kubelets. Then, edit API server pod specification file $apiserverconf on the control plane node and set the kubelet client certificate and key parameters as below. --kubelet-client-certificate= --kubelet-client-key= scored: true - id: 1.2.6 text: "Ensure that the --kubelet-certificate-authority argument is set as appropriate (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--kubelet-certificate-authority" set: true remediation: | Follow the Kubernetes documentation and setup the TLS connection between the apiserver and kubelets. Then, edit the API server pod specification file $apiserverconf on the control plane node and set the --kubelet-certificate-authority parameter to the path to the cert file for the certificate authority. --kubelet-certificate-authority= scored: true - id: 1.2.7 text: "Ensure that the --authorization-mode argument is not set to AlwaysAllow (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--authorization-mode" compare: op: nothave value: "AlwaysAllow" set: true remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the --authorization-mode parameter to values other than AlwaysAllow. One such example could be as below. --authorization-mode=RBAC scored: true - id: 1.2.8 text: "Ensure that the --authorization-mode argument includes Node (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--authorization-mode" compare: op: has value: "Node" set: true remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the --authorization-mode parameter to a value that includes Node. --authorization-mode=Node,RBAC scored: true - id: 1.2.9 text: "Ensure that the --authorization-mode argument includes RBAC (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--authorization-mode" compare: op: has value: "RBAC" set: true remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the --authorization-mode parameter to a value that includes RBAC, for example `--authorization-mode=Node,RBAC`. scored: true - id: 1.2.10 text: "Ensure that the admission control plugin EventRateLimit is set (Manual)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--enable-admission-plugins" compare: op: has value: "EventRateLimit" set: true remediation: | Follow the Kubernetes documentation and set the desired limits in a configuration file. Then, edit the API server pod specification file $apiserverconf and set the below parameters. --enable-admission-plugins=...,EventRateLimit,... --admission-control-config-file= scored: false - id: 1.2.11 text: "Ensure that the admission control plugin AlwaysAdmit is not set (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: bin_op: or test_items: - flag: "--enable-admission-plugins" compare: op: nothave value: AlwaysAdmit set: true - flag: "--enable-admission-plugins" set: false remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and either remove the --enable-admission-plugins parameter, or set it to a value that does not include AlwaysAdmit. scored: true - id: 1.2.12 text: "Ensure that the admission control plugin AlwaysPullImages is set (Manual)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--enable-admission-plugins" compare: op: has value: "AlwaysPullImages" set: true remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the --enable-admission-plugins parameter to include AlwaysPullImages. --enable-admission-plugins=...,AlwaysPullImages,... scored: false - id: 1.2.13 text: "Ensure that the admission control plugin SecurityContextDeny is set if PodSecurityPolicy is not used (Manual)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: bin_op: or test_items: - flag: "--enable-admission-plugins" compare: op: has value: "SecurityContextDeny" set: true - flag: "--enable-admission-plugins" compare: op: has value: "PodSecurityPolicy" set: true remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the --enable-admission-plugins parameter to include SecurityContextDeny, unless PodSecurityPolicy is already in place. --enable-admission-plugins=...,SecurityContextDeny,... scored: false - id: 1.2.14 text: "Ensure that the admission control plugin ServiceAccount is set (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: bin_op: or test_items: - flag: "--disable-admission-plugins" compare: op: nothave value: "ServiceAccount" - flag: "--disable-admission-plugins" set: false remediation: | Follow the documentation and create ServiceAccount objects as per your environment. Then, edit the API server pod specification file $apiserverconf on the control plane node and ensure that the --disable-admission-plugins parameter is set to a value that does not include ServiceAccount. scored: true - id: 1.2.15 text: "Ensure that the admission control plugin NamespaceLifecycle is set (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: bin_op: or test_items: - flag: "--disable-admission-plugins" compare: op: nothave value: "NamespaceLifecycle" - flag: "--disable-admission-plugins" set: false remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the --disable-admission-plugins parameter to ensure it does not include NamespaceLifecycle. scored: true - id: 1.2.16 text: "Ensure that the admission control plugin NodeRestriction is set (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--enable-admission-plugins" compare: op: has value: "NodeRestriction" set: true remediation: | Follow the Kubernetes documentation and configure NodeRestriction plug-in on kubelets. Then, edit the API server pod specification file $apiserverconf on the control plane node and set the --enable-admission-plugins parameter to a value that includes NodeRestriction. --enable-admission-plugins=...,NodeRestriction,... scored: true - id: 1.2.17 text: "Ensure that the --secure-port argument is not set to 0 (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: bin_op: or test_items: - flag: "--secure-port" compare: op: gt value: 0 set: true - flag: "--secure-port" set: false remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and either remove the --secure-port parameter or set it to a different (non-zero) desired port. scored: true - id: 1.2.18 text: "Ensure that the --profiling argument is set to false (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--profiling" compare: op: eq value: false set: true remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the below parameter. --profiling=false scored: true - id: 1.2.19 text: "Ensure that the --audit-log-path argument is set (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" type: "skip" tests: test_items: - flag: "--audit-log-path" set: true remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the --audit-log-path parameter to a suitable path and file where you would like audit logs to be written, for example, --audit-log-path=/var/log/apiserver/audit.log scored: true - id: 1.2.20 text: "Ensure that the --audit-log-maxage argument is set to 30 or as appropriate (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" type: "skip" tests: test_items: - flag: "--audit-log-maxage" compare: op: gte value: 30 set: true remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the --audit-log-maxage parameter to 30 or as an appropriate number of days, for example, --audit-log-maxage=30 scored: true - id: 1.2.21 text: "Ensure that the --audit-log-maxbackup argument is set to 10 or as appropriate (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" type: "skip" tests: test_items: - flag: "--audit-log-maxbackup" compare: op: gte value: 10 set: true remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the --audit-log-maxbackup parameter to 10 or to an appropriate value. For example, --audit-log-maxbackup=10 scored: true - id: 1.2.22 text: "Ensure that the --audit-log-maxsize argument is set to 100 or as appropriate (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" type: "skip" tests: test_items: - flag: "--audit-log-maxsize" compare: op: gte value: 100 set: true remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the --audit-log-maxsize parameter to an appropriate size in MB. For example, to set it as 100 MB, --audit-log-maxsize=100 scored: true - id: 1.2.23 text: "Ensure that the --request-timeout argument is set as appropriate (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: bin_op: or test_items: - flag: "--request-timeout" set: false - flag: "--request-timeout" remediation: | Edit the API server pod specification file $apiserverconf and set the below parameter as appropriate and if needed. For example, --request-timeout=300s scored: true - id: 1.2.24 text: "Ensure that the --service-account-lookup argument is set to true (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: bin_op: or test_items: - flag: "--service-account-lookup" set: false - flag: "--service-account-lookup" compare: op: eq value: true remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the below parameter. --service-account-lookup=true Alternatively, you can delete the --service-account-lookup parameter from this file so that the default takes effect. scored: true - id: 1.2.25 text: "Ensure that the --service-account-key-file argument is set as appropriate (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--service-account-key-file" set: true remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the --service-account-key-file parameter to the public key file for service accounts. For example, --service-account-key-file= scored: true - id: 1.2.26 text: "Ensure that the --etcd-certfile and --etcd-keyfile arguments are set as appropriate (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: bin_op: and test_items: - flag: "--etcd-certfile" set: true - flag: "--etcd-keyfile" set: true remediation: | Follow the Kubernetes documentation and set up the TLS connection between the apiserver and etcd. Then, edit the API server pod specification file $apiserverconf on the control plane node and set the etcd certificate and key file parameters. --etcd-certfile= --etcd-keyfile= scored: true - id: 1.2.27 text: "Ensure that the --tls-cert-file and --tls-private-key-file arguments are set as appropriate (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: bin_op: and test_items: - flag: "--tls-cert-file" set: true - flag: "--tls-private-key-file" set: true remediation: | Follow the Kubernetes documentation and set up the TLS connection on the apiserver. Then, edit the API server pod specification file $apiserverconf on the control plane node and set the TLS certificate and private key file parameters. --tls-cert-file= --tls-private-key-file= scored: true - id: 1.2.28 text: "Ensure that the --client-ca-file argument is set as appropriate (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--client-ca-file" set: true remediation: | Follow the Kubernetes documentation and set up the TLS connection on the apiserver. Then, edit the API server pod specification file $apiserverconf on the control plane node and set the client certificate authority file. --client-ca-file= scored: true - id: 1.2.29 text: "Ensure that the --etcd-cafile argument is set as appropriate (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--etcd-cafile" set: true remediation: | Follow the Kubernetes documentation and set up the TLS connection between the apiserver and etcd. Then, edit the API server pod specification file $apiserverconf on the control plane node and set the etcd certificate authority file parameter. --etcd-cafile= scored: true - id: 1.2.30 text: "Ensure that the --encryption-provider-config argument is set as appropriate (Manual)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--encryption-provider-config" remediation: | Follow the Kubernetes documentation and configure a EncryptionConfig file. Then, edit the API server pod specification file $apiserverconf on the control plane node and set the --encryption-provider-config parameter to the path of that file. For example, --encryption-provider-config= scored: false - id: 1.2.31 text: "Ensure that encryption providers are appropriately configured (Manual)" type: "skip" audit: | ENCRYPTION_PROVIDER_CONFIG=$(ps -ef | grep $apiserverbin | grep -- --encryption-provider-config | sed 's%.*encryption-provider-config[= ]\([^ ]*\).*%\1%') if test -e $ENCRYPTION_PROVIDER_CONFIG; then grep -A1 'providers:' $ENCRYPTION_PROVIDER_CONFIG | tail -n1 | grep -o "[A-Za-z]*" | sed 's/^/provider=/'; fi tests: test_items: - flag: "provider" compare: op: valid_elements value: "aescbc,kms,secretbox" remediation: | Follow the Kubernetes documentation and configure a EncryptionConfig file. In this file, choose aescbc, kms or secretbox as the encryption provider. scored: false - id: 1.2.32 text: "Ensure that the API Server only makes use of Strong Cryptographic Ciphers (Manual)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--tls-cipher-suites" compare: op: valid_elements value: "TLS_AES_128_GCM_SHA256,TLS_AES_256_GCM_SHA384,TLS_CHACHA20_POLY1305_SHA256,TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA,TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,TLS_RSA_WITH_3DES_EDE_CBC_SHA,TLS_RSA_WITH_AES_128_CBC_SHA,TLS_RSA_WITH_AES_128_GCM_SHA256,TLS_RSA_WITH_AES_256_CBC_SHA,TLS_RSA_WITH_AES_256_GCM_SHA384" remediation: | Edit the API server pod specification file /etc/kubernetes/manifests/kube-apiserver.yaml on the control plane node and set the below parameter. --tls-cipher-suites=TLS_AES_128_GCM_SHA256,TLS_AES_256_GCM_SHA384,TLS_CHACHA20_POLY1305_SHA256, TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA,TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305, TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,TLS_RSA_WITH_3DES_EDE_CBC_SHA,TLS_RSA_WITH_AES_128_CBC_SHA, TLS_RSA_WITH_AES_128_GCM_SHA256,TLS_RSA_WITH_AES_256_CBC_SHA,TLS_RSA_WITH_AES_256_GCM_SHA384 scored: false type: skip - id: 1.3 text: "Controller Manager" checks: - id: 1.3.1 text: "Ensure that the --terminated-pod-gc-threshold argument is set as appropriate (Manual)" audit: "/bin/ps -ef | grep $controllermanagerbin | grep -v grep" tests: test_items: - flag: "--terminated-pod-gc-threshold" remediation: | Edit the Controller Manager pod specification file $controllermanagerconf on the control plane node and set the --terminated-pod-gc-threshold to an appropriate threshold, for example, --terminated-pod-gc-threshold=10 scored: false - id: 1.3.2 text: "Ensure that the --profiling argument is set to false (Automated)" audit: "/bin/ps -ef | grep $controllermanagerbin | grep -v grep" tests: test_items: - flag: "--profiling" compare: op: eq value: false set: true remediation: | Edit the Controller Manager pod specification file $controllermanagerconf on the control plane node and set the below parameter. --profiling=false scored: true - id: 1.3.3 text: "Ensure that the --use-service-account-credentials argument is set to true (Automated)" audit: "/bin/ps -ef | grep $controllermanagerbin | grep -v grep" tests: test_items: - flag: "--use-service-account-credentials" compare: op: noteq value: false set: true remediation: | Edit the Controller Manager pod specification file $controllermanagerconf on the control plane node to set the below parameter. --use-service-account-credentials=true scored: true - id: 1.3.4 text: "Ensure that the --service-account-private-key-file argument is set as appropriate (Automated)" audit: "/bin/ps -ef | grep $controllermanagerbin | grep -v grep" tests: test_items: - flag: "--service-account-private-key-file" set: true remediation: | Edit the Controller Manager pod specification file $controllermanagerconf on the control plane node and set the --service-account-private-key-file parameter to the private key file for service accounts. --service-account-private-key-file= scored: true - id: 1.3.5 text: "Ensure that the --root-ca-file argument is set as appropriate (Automated)" audit: "/bin/ps -ef | grep $controllermanagerbin | grep -v grep" tests: test_items: - flag: "--root-ca-file" set: true remediation: | Edit the Controller Manager pod specification file $controllermanagerconf on the control plane node and set the --root-ca-file parameter to the certificate bundle file`. --root-ca-file= scored: true - id: 1.3.6 text: "Ensure that the RotateKubeletServerCertificate argument is set to true (Automated)" audit: "/bin/ps -ef | grep $controllermanagerbin | grep -v grep" tests: bin_op: or test_items: - flag: "--feature-gates" compare: op: nothave value: "RotateKubeletServerCertificate=false" set: true - flag: "--feature-gates" set: false remediation: | Edit the Controller Manager pod specification file $controllermanagerconf on the control plane node and set the --feature-gates parameter to include RotateKubeletServerCertificate=true. --feature-gates=RotateKubeletServerCertificate=true scored: false type: skip - id: 1.3.7 text: "Ensure that the --bind-address argument is set to 127.0.0.1 (Automated)" audit: "/bin/ps -ef | grep $controllermanagerbin | grep -v grep" tests: bin_op: or test_items: - flag: "--bind-address" compare: op: eq value: "127.0.0.1" set: true - flag: "--bind-address" set: false remediation: | Edit the Controller Manager pod specification file $controllermanagerconf on the control plane node and ensure the correct value for the --bind-address parameter scored: true - id: 1.4 text: "Scheduler" checks: - id: 1.4.1 text: "Ensure that the --profiling argument is set to false (Automated)" audit: "/bin/ps -ef | grep $schedulerbin | grep -v grep" tests: test_items: - flag: "--profiling" compare: op: eq value: false set: true remediation: | Edit the Scheduler pod specification file $schedulerconf file on the control plane node and set the below parameter. --profiling=false scored: true - id: 1.4.2 text: "Ensure that the --bind-address argument is set to 127.0.0.1 (Automated)" audit: "/bin/ps -ef | grep $schedulerbin | grep -v grep" tests: bin_op: or test_items: - flag: "--bind-address" compare: op: eq value: "127.0.0.1" set: true - flag: "--bind-address" set: false remediation: | Edit the Scheduler pod specification file $schedulerconf on the control plane node and ensure the correct value for the --bind-address parameter scored: true ================================================ FILE: cfg/rke2-cis-1.24/node.yaml ================================================ --- controls: version: "rke2-cis-1.24" id: 4 text: "Worker Node Security Configuration" type: "node" groups: - id: 4.1 text: "Worker Node Configuration Files" checks: - id: 4.1.1 text: "Ensure that the kubelet service file permissions are set to 600 or more restrictive (Automated)" audit: '/bin/sh -c ''if test -e $kubeletsvc; then stat -c permissions=%a $kubeletsvc; fi'' ' type: "skip" tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Run the below command (based on the file location on your system) on the each worker node. For example, chmod 600 $kubeletsvc scored: true - id: 4.1.2 text: "Ensure that the kubelet service file ownership is set to root:root (Automated)" audit: '/bin/sh -c ''if test -e $kubeletsvc; then stat -c %U:%G $kubeletsvc; fi'' ' type: "skip" tests: test_items: - flag: "root:root" set: true remediation: | Run the below command (based on the file location on your system) on the each worker node. For example, chown root:root $kubeletsvc scored: true - id: 4.1.3 text: "If proxy kubeconfig file exists ensure permissions are set to 600 or more restrictive (Manual)" audit: '/bin/sh -c ''if test -e $proxykubeconfig; then stat -c permissions=%a $proxykubeconfig; fi'' ' tests: bin_op: or test_items: - flag: "permissions" set: true compare: op: bitmask value: "600" remediation: | Run the below command (based on the file location on your system) on the each worker node. For example, chmod 600 $proxykubeconfig scored: false - id: 4.1.4 text: "If proxy kubeconfig file exists ensure ownership is set to root:root (Manual)" audit: '/bin/sh -c ''if test -e $proxykubeconfig; then stat -c %U:%G $proxykubeconfig; fi'' ' tests: bin_op: or test_items: - flag: root:root remediation: | Run the below command (based on the file location on your system) on the each worker node. For example, chown root:root $proxykubeconfig scored: false - id: 4.1.5 text: "Ensure that the --kubeconfig kubelet.conf file permissions are set to 600 or more restrictive (Automated)" audit: '/bin/sh -c ''if test -e $kubeletkubeconfig; then stat -c permissions=%a $kubeletkubeconfig; fi'' ' tests: test_items: - flag: "600" compare: op: eq value: "600" set: true remediation: | Run the below command (based on the file location on your system) on the each worker node. For example, chmod 600 $kubeletkubeconfig scored: true - id: 4.1.6 text: "Ensure that the --kubeconfig kubelet.conf file ownership is set to root:root (Automated)" audit: '/bin/sh -c ''if test -e $kubeletkubeconfig; then stat -c %U:%G $kubeletkubeconfig; fi'' ' tests: test_items: - flag: root:root set: true compare: op: eq value: root:root remediation: | Run the below command (based on the file location on your system) on the each worker node. For example, chown root:root $kubeletkubeconfig scored: true - id: 4.1.7 text: "Ensure that the certificate authorities file permissions are set to 600 or more restrictive (Manual)" audit: "check_cafile_permissions.sh" tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" set: true remediation: | Run the following command to modify the file permissions of the --client-ca-file chmod 600 scored: false - id: 4.1.8 text: "Ensure that the client certificate authorities file ownership is set to root:root (Manual)" audit: "check_cafile_ownership.sh" tests: test_items: - flag: root:root remediation: | Run the following command to modify the ownership of the --client-ca-file. chown root:root scored: false - id: 4.1.9 text: "If the kubelet config.yaml configuration file is being used validate permissions set to 600 or more restrictive (Automated)" audit: | /bin/sh -c 'if test -e "$kubeletconf"; then stat -c "permissions=%a" "$kubeletconf" else echo "File not found" fi' tests: bin_op: or test_items: - flag: "permissions" compare: op: bitmask value: "600" set: true - flag: "File not found" remediation: | Run the following command (using the config file location identified in the Audit step) chmod 600 $kubeletconf scored: true - id: 4.1.10 text: "If the kubelet config.yaml configuration file is being used validate file ownership is set to root:root (Automated)" audit: | /bin/sh -c 'if test -e "$kubeletconf"; then stat -c "%U:%G" "$kubeletconf" else echo "File not found" fi' tests: bin_op: or test_items: - flag: root:root set: true - flag: "File not found" remediation: | Run the following command (using the config file location identified in the Audit step) chown root:root $kubeletconf scored: true - id: 4.2 text: "Kubelet" checks: - id: 4.2.1 text: "Ensure that the --anonymous-auth argument is set to false (Automated)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: "--anonymous-auth" path: '{.authentication.anonymous.enabled}' compare: op: eq value: false remediation: | If using a Kubelet config file, edit the file to set `authentication: anonymous: enabled` to `false`. If using executable arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_SYSTEM_PODS_ARGS variable. `--anonymous-auth=false` Based on your system, restart the kubelet service. For example, systemctl daemon-reload systemctl restart kubelet.service scored: true - id: 4.2.2 text: "Ensure that the --authorization-mode argument is not set to AlwaysAllow (Automated)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --authorization-mode path: '{.authorization.mode}' compare: op: nothave value: AlwaysAllow remediation: | If using a Kubelet config file, edit the file to set `authorization.mode` to Webhook. If using executable arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_AUTHZ_ARGS variable. --authorization-mode=Webhook Based on your system, restart the kubelet service. For example, systemctl daemon-reload systemctl restart kubelet.service scored: true - id: 4.2.3 text: "Ensure that the --client-ca-file argument is set as appropriate (Automated)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --client-ca-file path: '{.authentication.x509.clientCAFile}' remediation: | If using a Kubelet config file, edit the file to set `authentication.x509.clientCAFile` to the location of the client CA file. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_AUTHZ_ARGS variable. --client-ca-file= Based on your system, restart the kubelet service. For example, systemctl daemon-reload systemctl restart kubelet.service scored: true - id: 4.2.4 text: "Verify that the --read-only-port argument is set to 0 (Manual)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: bin_op: or test_items: - flag: "--read-only-port" path: '{.readOnlyPort}' compare: op: eq value: 0 - flag: "--read-only-port" path: '{.readOnlyPort}' set: false remediation: | If using a Kubelet config file, edit the file to set `readOnlyPort` to 0. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_SYSTEM_PODS_ARGS variable. --read-only-port=0 Based on your system, restart the kubelet service. For example, systemctl daemon-reload systemctl restart kubelet.service scored: false - id: 4.2.5 text: "Ensure that the --streaming-connection-idle-timeout argument is not set to 0 (Manual)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --streaming-connection-idle-timeout path: '{.streamingConnectionIdleTimeout}' compare: op: noteq value: 0 - flag: --streaming-connection-idle-timeout path: '{.streamingConnectionIdleTimeout}' set: false bin_op: or remediation: | If using a Kubelet config file, edit the file to set `streamingConnectionIdleTimeout` to a value other than 0. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_SYSTEM_PODS_ARGS variable. --streaming-connection-idle-timeout=5m Based on your system, restart the kubelet service. For example, systemctl daemon-reload systemctl restart kubelet.service scored: false - id: 4.2.6 text: "Ensure that the --protect-kernel-defaults argument is set to true (Automated)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: bin_op: or test_items: - flag: --protect-kernel-defaults path: '{.protectKernelDefaults}' compare: op: eq value: true set: true - flag: --protect-kernel-defaults path: '{.protectKernelDefaults}' set: false remediation: | If using a Kubelet config file, edit the file to set `protectKernelDefaults` to `true`. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_SYSTEM_PODS_ARGS variable. --protect-kernel-defaults=true Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: true - id: 4.2.7 text: "Ensure that the --make-iptables-util-chains argument is set to true (Automated)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --make-iptables-util-chains path: '{.makeIPTablesUtilChains}' compare: op: eq value: true - flag: --make-iptables-util-chains path: '{.makeIPTablesUtilChains}' set: false bin_op: or remediation: | If using a Kubelet config file, edit the file to set `makeIPTablesUtilChains` to `true`. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and remove the --make-iptables-util-chains argument from the KUBELET_SYSTEM_PODS_ARGS variable. Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: true - id: 4.2.8 text: "Ensure that the --hostname-override argument is not set (Manual)" # This is one of those properties that can only be set as a command line argument. # To check if the property is set as expected, we need to parse the kubelet command # instead reading the Kubelet Configuration file. audit: "/bin/ps -fC $kubeletbin " tests: test_items: - flag: --hostname-override set: false remediation: | Edit the kubelet service file $kubeletsvc on each worker node and remove the --hostname-override argument from the KUBELET_SYSTEM_PODS_ARGS variable. Based on your system, restart the kubelet service. For example, systemctl daemon-reload systemctl restart kubelet.service scored: false type: skip - id: 4.2.9 text: "Ensure that the eventRecordQPS argument is set to a level which ensures appropriate event capture (Manual)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --event-qps path: '{.eventRecordQPS}' compare: op: eq value: 0 remediation: | If using a Kubelet config file, edit the file to set `eventRecordQPS` to an appropriate level. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_SYSTEM_PODS_ARGS variable. Based on your system, restart the kubelet service. For example, systemctl daemon-reload systemctl restart kubelet.service scored: false - id: 4.2.10 text: "Ensure that the --tls-cert-file and --tls-private-key-file arguments are set as appropriate (Manual)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --tls-cert-file path: '{.tlsCertFile}' - flag: --tls-private-key-file path: '{.tlsPrivateKeyFile}' remediation: | If using a Kubelet config file, edit the file to set `tlsCertFile` to the location of the certificate file to use to identify this Kubelet, and `tlsPrivateKeyFile` to the location of the corresponding private key file. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameters in KUBELET_CERTIFICATE_ARGS variable. --tls-cert-file= --tls-private-key-file= Based on your system, restart the kubelet service. For example, systemctl daemon-reload systemctl restart kubelet.service scored: false - id: 4.2.11 text: "Ensure that the --rotate-certificates argument is not set to false (Automated)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --rotate-certificates path: '{.rotateCertificates}' compare: op: eq value: true - flag: --rotate-certificates path: '{.rotateCertificates}' set: false bin_op: or remediation: | If using a Kubelet config file, edit the file to add the line `rotateCertificates` to `true` or remove it altogether to use the default value. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and remove --rotate-certificates=false argument from the KUBELET_CERTIFICATE_ARGS variable. Based on your system, restart the kubelet service. For example, systemctl daemon-reload systemctl restart kubelet.service scored: true - id: 4.2.12 text: "Verify that the RotateKubeletServerCertificate argument is set to true (Manual)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: bin_op: or test_items: - flag: RotateKubeletServerCertificate path: '{.featureGates.RotateKubeletServerCertificate}' compare: op: nothave value: false - flag: RotateKubeletServerCertificate path: '{.featureGates.RotateKubeletServerCertificate}' set: false remediation: | Edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_CERTIFICATE_ARGS variable. --feature-gates=RotateKubeletServerCertificate=true Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: false type: skip - id: 4.2.13 text: "Ensure that the Kubelet only makes use of Strong Cryptographic Ciphers (Manual)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --tls-cipher-suites path: '{range .tlsCipherSuites[:]}{}{'',''}{end}' compare: op: valid_elements value: 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 remediation: | If using a Kubelet config file, edit the file to set `tlsCipherSuites` to 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 or to a subset of these values. If using executable arguments, edit the kubelet service file $kubeletsvc on each worker node and set the --tls-cipher-suites parameter as follows, or to a subset of these values. --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 Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: false ================================================ FILE: cfg/rke2-cis-1.24/policies.yaml ================================================ --- controls: version: "rke2-cis-1.24" id: 5 text: "Kubernetes Policies" type: "policies" groups: - id: 5.1 text: "RBAC and Service Accounts" checks: - id: 5.1.1 text: "Ensure that the cluster-admin role is only used where required (Manual)" type: "manual" remediation: | Identify all clusterrolebindings to the cluster-admin role. Check if they are used and if they need this role or if they could use a role with fewer privileges. Where possible, first bind users to a lower privileged role and then remove the clusterrolebinding to the cluster-admin role : kubectl delete clusterrolebinding [name] scored: false - id: 5.1.2 text: "Minimize access to secrets (Manual)" type: "manual" remediation: | Where possible, remove get, list and watch access to Secret objects in the cluster. scored: false - id: 5.1.3 text: "Minimize wildcard use in Roles and ClusterRoles (Manual)" type: "manual" remediation: | Where possible replace any use of wildcards in clusterroles and roles with specific objects or actions. scored: false - id: 5.1.4 text: "Minimize access to create pods (Manual)" type: "manual" remediation: | Where possible, remove create access to pod objects in the cluster. scored: false - id: 5.1.5 text: "Ensure that default service accounts are not actively used. (Manual)" type: "manual" remediation: | Create explicit service accounts wherever a Kubernetes workload requires specific access to the Kubernetes API server. Modify the configuration of each default service account to include this value automountServiceAccountToken: false scored: false - id: 5.1.6 text: "Ensure that Service Account Tokens are only mounted where necessary (Manual)" type: "manual" remediation: | Modify the definition of pods and service accounts which do not need to mount service account tokens to disable it. scored: false - id: 5.1.7 text: "Avoid use of system:masters group (Manual)" type: "manual" remediation: | Remove the system:masters group from all users in the cluster. scored: false - id: 5.1.8 text: "Limit use of the Bind, Impersonate and Escalate permissions in the Kubernetes cluster (Manual)" type: "manual" remediation: | Where possible, remove the impersonate, bind and escalate rights from subjects. scored: false - id: 5.2 text: "Pod Security Standards" checks: - id: 5.2.1 text: "Ensure that the cluster has at least one active policy control mechanism in place (Manual)" type: "manual" remediation: | Ensure that either Pod Security Admission or an external policy control system is in place for every namespace which contains user workloads. scored: false - id: 5.2.2 text: "Minimize the admission of privileged containers (Manual)" type: "manual" remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of privileged containers. scored: false - id: 5.2.3 text: "Minimize the admission of containers wishing to share the host process ID namespace (Manual)" type: "manual" remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of `hostPID` containers. scored: false - id: 5.2.4 text: "Minimize the admission of containers wishing to share the host IPC namespace (Manual)" type: "manual" remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of `hostIPC` containers. scored: false - id: 5.2.5 text: "Minimize the admission of containers wishing to share the host network namespace (Manual)" type: "manual" remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of `hostNetwork` containers. scored: false - id: 5.2.6 text: "Minimize the admission of containers with allowPrivilegeEscalation (Manual)" type: "manual" remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of containers with `.spec.allowPrivilegeEscalation` set to `true`. scored: false - id: 5.2.7 text: "Minimize the admission of root containers (Manual)" type: "manual" remediation: | Create a policy for each namespace in the cluster, ensuring that either `MustRunAsNonRoot` or `MustRunAs` with the range of UIDs not including 0, is set. scored: false - id: 5.2.8 text: "Minimize the admission of containers with the NET_RAW capability (Manual)" type: "manual" remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of containers with the `NET_RAW` capability. scored: false - id: 5.2.9 text: "Minimize the admission of containers with added capabilities (Automated)" type: "manual" remediation: | Ensure that `allowedCapabilities` is not present in policies for the cluster unless it is set to an empty array. scored: false - id: 5.2.10 text: "Minimize the admission of containers with capabilities assigned (Manual)" type: "manual" remediation: | Review the use of capabilites in applications running on your cluster. Where a namespace contains applications which do not require any Linux capabities to operate consider adding a PSP which forbids the admission of containers which do not drop all capabilities. scored: false - id: 5.2.11 text: "Minimize the admission of Windows HostProcess containers (Manual)" type: "manual" remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of containers that have `.securityContext.windowsOptions.hostProcess` set to `true`. scored: false - id: 5.2.12 text: "Minimize the admission of HostPath volumes (Manual)" type: "manual" remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of containers with `hostPath` volumes. scored: false - id: 5.2.13 text: "Minimize the admission of containers which use HostPorts (Manual)" type: "manual" remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of containers which use `hostPort` sections. scored: false - id: 5.3 text: "Network Policies and CNI" checks: - id: 5.3.1 text: "Ensure that the CNI in use supports NetworkPolicies (Manual)" type: "manual" remediation: | If the CNI plugin in use does not support network policies, consideration should be given to making use of a different plugin, or finding an alternate mechanism for restricting traffic in the Kubernetes cluster. scored: false - id: 5.3.2 text: "Ensure that all Namespaces have NetworkPolicies defined (Manual)" type: "manual" remediation: | Follow the documentation and create NetworkPolicy objects as you need them. scored: false - id: 5.4 text: "Secrets Management" checks: - id: 5.4.1 text: "Prefer using Secrets as files over Secrets as environment variables (Manual)" type: "manual" remediation: | If possible, rewrite application code to read Secrets from mounted secret files, rather than from environment variables. scored: false - id: 5.4.2 text: "Consider external secret storage (Manual)" type: "manual" remediation: | Refer to the Secrets management options offered by your cloud provider or a third-party secrets management solution. scored: false - id: 5.5 text: "Extensible Admission Control" checks: - id: 5.5.1 text: "Configure Image Provenance using ImagePolicyWebhook admission controller (Manual)" type: "manual" remediation: | Follow the Kubernetes documentation and setup image provenance. scored: false - id: 5.7 text: "General Policies" checks: - id: 5.7.1 text: "Create administrative boundaries between resources using namespaces (Manual)" type: "manual" remediation: | Follow the documentation and create namespaces for objects in your deployment as you need them. scored: false - id: 5.7.2 text: "Ensure that the seccomp profile is set to docker/default in your Pod definitions (Manual)" type: "manual" remediation: | Use `securityContext` to enable the docker/default seccomp profile in your pod definitions. An example is as below: securityContext: seccompProfile: type: RuntimeDefault scored: false - id: 5.7.3 text: "Apply SecurityContext to your Pods and Containers (Manual)" type: "manual" remediation: | Follow the Kubernetes documentation and apply SecurityContexts to your Pods. For a suggested list of SecurityContexts, you may refer to the CIS Security Benchmark for Docker Containers. scored: false - id: 5.7.4 text: "The default namespace should not be used (Manual)" type: "manual" remediation: | Ensure that namespaces are created to allow for appropriate segregation of Kubernetes resources and that all new resources are created in a specific namespace. scored: false ================================================ FILE: cfg/rke2-cis-1.7/config.yaml ================================================ --- ## Version-specific settings that override the values in cfg/config.yaml ================================================ FILE: cfg/rke2-cis-1.7/controlplane.yaml ================================================ --- controls: version: "rke2-cis-1.7" id: 3 text: "Control Plane Configuration" type: "controlplane" groups: - id: 3.1 text: "Authentication and Authorization" checks: - id: 3.1.1 text: "Client certificate authentication should not be used for users (Manual)" type: "manual" remediation: | Alternative mechanisms provided by Kubernetes such as the use of OIDC should be implemented in place of client certificates. scored: false - id: 3.1.2 text: "Service account token authentication should not be used for users (Manual)" type: "manual" remediation: | Alternative mechanisms provided by Kubernetes such as the use of OIDC should be implemented in place of service account tokens. scored: false - id: 3.1.3 text: "Bootstrap token authentication should not be used for users (Manual)" type: "manual" remediation: | Alternative mechanisms provided by Kubernetes such as the use of OIDC should be implemented in place of bootstrap tokens. scored: false - id: 3.2 text: "Logging" checks: - id: 3.2.1 text: "Ensure that a minimal audit policy is created (Automated)" type: "skip" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--audit-policy-file" set: true remediation: | Create an audit policy file for your cluster. Permissive. scored: true - id: 3.2.2 text: "Ensure that the audit policy covers key security concerns (Manual)" type: "manual" remediation: | Review the audit policy provided for the cluster and ensure that it covers at least the following areas, - Access to Secrets managed by the cluster. Care should be taken to only log Metadata for requests to Secrets, ConfigMaps, and TokenReviews, in order to avoid risk of logging sensitive data. - Modification of Pod and Deployment objects. - Use of `pods/exec`, `pods/portforward`, `pods/proxy` and `services/proxy`. For most requests, minimally logging at the Metadata level is recommended (the most basic level of logging). scored: false ================================================ FILE: cfg/rke2-cis-1.7/etcd.yaml ================================================ --- controls: version: "rke2-cis-1.7" id: 2 text: "Etcd Node Configuration" type: "etcd" groups: - id: 2 text: "Etcd Node Configuration" checks: - id: 2.1 text: "Ensure that the --cert-file and --key-file arguments are set as appropriate (Automated)" type: "skip" audit: "/bin/ps -ef | /bin/grep $etcdbin | /bin/grep -v grep" tests: bin_op: and test_items: - flag: "--cert-file" env: "ETCD_CERT_FILE" - flag: "--key-file" env: "ETCD_KEY_FILE" remediation: | Follow the etcd service documentation and configure TLS encryption. Then, edit the etcd pod specification file /etc/kubernetes/manifests/etcd.yaml on the master node and set the below parameters. --cert-file= --key-file= Not Applicable. scored: true - id: 2.2 text: "Ensure that the --client-cert-auth argument is set to true (Automated)" type: "skip" audit: "/bin/ps -ef | /bin/grep $etcdbin | /bin/grep -v grep" tests: test_items: - flag: "--client-cert-auth" env: "ETCD_CLIENT_CERT_AUTH" compare: op: eq value: true remediation: | Edit the etcd pod specification file $etcdconf on the master node and set the below parameter. --client-cert-auth="true" Not Applicable. scored: true - id: 2.3 text: "Ensure that the --auto-tls argument is not set to true (Automated)" audit: "/bin/ps -ef | /bin/grep $etcdbin | /bin/grep -v grep" tests: bin_op: or test_items: - flag: "--auto-tls" env: "ETCD_AUTO_TLS" set: false - flag: "--auto-tls" env: "ETCD_AUTO_TLS" compare: op: eq value: false remediation: | Edit the etcd pod specification file $etcdconf on the master node and either remove the --auto-tls parameter or set it to false. --auto-tls=false scored: true - id: 2.4 text: "Ensure that the --peer-cert-file and --peer-key-file arguments are set as appropriate (Automated)" type: "skip" audit: "/bin/ps -ef | /bin/grep $etcdbin | /bin/grep -v grep" tests: bin_op: and test_items: - flag: "--peer-cert-file" env: "ETCD_PEER_CERT_FILE" set: true - flag: "--peer-key-file" env: "ETCD_PEER_KEY_FILE" set: true remediation: | Follow the etcd service documentation and configure peer TLS encryption as appropriate for your etcd cluster. Then, edit the etcd pod specification file $etcdconf on the master node and set the below parameters. --peer-client-file= --peer-key-file= Not Applicable. scored: true - id: 2.5 text: "Ensure that the --peer-client-cert-auth argument is set to true (Automated)" type: "skip" audit: "/bin/ps -ef | /bin/grep $etcdbin | /bin/grep -v grep" tests: test_items: - flag: "--peer-client-cert-auth" env: "ETCD_PEER_CLIENT_CERT_AUTH" compare: op: eq value: true set: true remediation: | Edit the etcd pod specification file $etcdconf on the master node and set the below parameter. --peer-client-cert-auth=true Not Applicable. scored: true - id: 2.6 text: "Ensure that the --peer-auto-tls argument is not set to true (Automated)" audit: "/bin/ps -ef | /bin/grep $etcdbin | /bin/grep -v grep" tests: bin_op: or test_items: - flag: "--peer-auto-tls" env: "ETCD_PEER_AUTO_TLS" set: false - flag: "--peer-auto-tls" env: "ETCD_PEER_AUTO_TLS" compare: op: eq value: false set: true remediation: | Edit the etcd pod specification file $etcdconf on the master node and either remove the --peer-auto-tls parameter or set it to false. --peer-auto-tls=false scored: true - id: 2.7 text: "Ensure that a unique Certificate Authority is used for etcd (Automated)" audit: "/bin/ps -ef | /bin/grep $etcdbin | /bin/grep -v grep" audit_config: "cat /var/lib/rancher/rke2/server/db/etcd/config" tests: bin_op: or test_items: - flag: "--trusted-ca-file" env: "ETCD_TRUSTED_CA_FILE" - path: "{.peer-transport-security.trusted-ca-file}" compare: op: eq value: "/var/lib/rancher/rke2/server/tls/etcd/peer-ca.crt" set: true remediation: | [Manual test] Follow the etcd documentation and create a dedicated certificate authority setup for the etcd service. Then, edit the etcd pod specification file $etcdconf on the master node and set the below parameter. --trusted-ca-file= scored: true ================================================ FILE: cfg/rke2-cis-1.7/master.yaml ================================================ --- controls: version: "rke2-cis-1.7" id: 1 text: "Control Plane Security Configuration" type: "master" groups: - id: 1.1 text: "Control Plane Node Configuration Files" checks: - id: 1.1.1 text: "Ensure that the API server pod specification file permissions are set to 600 or more restrictive (Automated)" audit: "stat -c permissions=%a /var/lib/rancher/rke2/agent/pod-manifests/kube-apiserver.yaml" tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" set: true remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chmod 600 $apiserverconf scored: true - id: 1.1.2 text: "Ensure that the API server pod specification file ownership is set to root:root (Automated)" audit: "/bin/sh -c 'if test -e $apiserverconf; then stat -c %U:%G $apiserverconf; fi'" tests: test_items: - flag: "root:root" compare: op: eq value: "root:root" set: true remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chown root:root $apiserverconf scored: true - id: 1.1.3 text: "Ensure that the controller manager pod specification file permissions are set to 600 or more restrictive (Automated)" audit: "/bin/sh -c 'if test -e $controllermanagerconf; then stat -c permissions=%a $controllermanagerconf; fi'" tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" set: true remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chmod 600 $controllermanagerconf scored: true - id: 1.1.4 text: "Ensure that the controller manager pod specification file ownership is set to root:root (Automated)" audit: "/bin/sh -c 'if test -e $controllermanagerconf; then stat -c %U:%G $controllermanagerconf; fi'" tests: test_items: - flag: "root:root" compare: op: eq value: "root:root" set: true remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chown root:root $controllermanagerconf scored: true - id: 1.1.5 text: "Ensure that the scheduler pod specification file permissions are set to 600 or more restrictive (Automated)" audit: "/bin/sh -c 'if test -e $schedulerconf; then stat -c permissions=%a $schedulerconf; fi'" tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" set: true remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chmod 600 $schedulerconf scored: true - id: 1.1.6 text: "Ensure that the scheduler pod specification file ownership is set to root:root (Automated)" audit: "/bin/sh -c 'if test -e $schedulerconf; then stat -c %U:%G $schedulerconf; fi'" tests: test_items: - flag: "root:root" set: true remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chown root:root $schedulerconf scored: true - id: 1.1.7 text: "Ensure that the etcd pod specification file permissions are set to 600 or more restrictive (Automated)" audit: | /bin/sh -c ' if [ -e "$etcdconf" ]; then stat -c "permissions=%a %n" "$etcdconf" else echo "Directory not found" fi ' tests: bin_op: or test_items: - flag: "permissions" compare: op: bitmask value: "600" set: true - flag: "Directory not found" set: true remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chmod 600 $etcdconf scored: true - id: 1.1.8 text: "Ensure that the etcd pod specification file ownership is set to root:root (Automated)" audit: | /bin/sh -c ' if [ -e $etcdconf ]; then stat -c "ownership=%U:%G %n" $etcdconf else echo "Directory not found" fi ' tests: bin_op: or test_items: - flag: "ownership" compare: op: eq value: "root:root" set: true - flag: "Directory not found" set: true remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chown root:root $etcdconf scored: true - id: 1.1.9 text: "Ensure that the Container Network Interface file permissions are set to 600 or more restrictive (Manual)" audit: | ps -fC ${kubeletbin:-kubelet} | grep -- --cni-conf-dir || echo "/etc/cni/net.d" | sed 's%.*cni-conf-dir[= ]\([^ ]*\).*%\1%' | xargs -I{} find {} -mindepth 1 | xargs --no-run-if-empty stat -c permissions=%a find /var/lib/cni/networks -type f 2> /dev/null | xargs --no-run-if-empty stat -c permissions=%a use_multiple_values: true tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chmod 600 scored: false - id: 1.1.10 text: "Ensure that the Container Network Interface file ownership is set to root:root (Manual)" audit: | ps -fC ${kubeletbin:-kubelet} | grep -- --cni-conf-dir || echo "/etc/cni/net.d" | sed 's%.*cni-conf-dir[= ]\([^ ]*\).*%\1%' | xargs -I{} find {} -mindepth 1 | xargs --no-run-if-empty stat -c %U:%G find /var/lib/cni/networks -type f 2> /dev/null | xargs --no-run-if-empty stat -c %U:%G use_multiple_values: true tests: test_items: - flag: "root:root" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chown root:root scored: false - id: 1.1.11 text: "Ensure that the etcd data directory permissions are set to 700 or more restrictive (Automated)" audit: | /bin/sh -c 'if [ -d "/var/lib/rancher/rke2/server/db/etcd" ]; then stat -c "permissions=%a" "/var/lib/rancher/rke2/server/db/etcd" else echo "Directory not found" fi' tests: bin_op: or test_items: - flag: "permissions" compare: op: bitmask value: "700" set: true - flag: "Directory not found" set: true remediation: | On the etcd server node, get the etcd data directory, passed as an argument --data-dir, from the command 'ps -ef | grep etcd'. Run the below command (based on the etcd data directory found above). For example, chmod 700 /var/lib/etcd scored: true - id: 1.1.12 text: "Ensure that the etcd data directory ownership is set to etcd:etcd (Automated)" type: "skip" audit: "stat -c %U:%G /var/lib/rancher/rke2/server/db/etcd" tests: test_items: - flag: "etcd:etcd" remediation: | On the etcd server node, get the etcd data directory, passed as an argument --data-dir, from the command 'ps -ef | grep etcd'. Run the below command (based on the etcd data directory found above). For example, chown etcd:etcd /var/lib/etcd scored: true - id: 1.1.13 text: "Ensure that the admin.conf file permissions are set to 600 or more restrictive (Automated)" audit: "stat -c permissions=%a /var/lib/rancher/rke2/server/cred/admin.kubeconfig" tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" set: true remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chmod 600 /etc/kubernetes/admin.conf scored: true - id: 1.1.14 text: "Ensure that the admin.conf file ownership is set to root:root (Automated)" audit: "stat -c %U:%G /var/lib/rancher/rke2/server/cred/admin.kubeconfig" tests: test_items: - flag: "root:root" compare: op: eq value: "root:root" set: true remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chown root:root /etc/kubernetes/admin.conf scored: true - id: 1.1.15 text: "Ensure that the scheduler.conf file permissions are set to 600 or more restrictive (Automated)" audit: "/bin/sh -c 'if test -e $schedulerkubeconfig; then stat -c permissions=%a $schedulerkubeconfig; fi'" tests: test_items: - flag: "600" compare: op: eq value: "600" set: true remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chmod 600 $schedulerkubeconfig scored: true - id: 1.1.16 text: "Ensure that the scheduler.conf file ownership is set to root:root (Automated)" audit: "stat -c %U:%G $schedulerkubeconfig" tests: test_items: - flag: "root:root" compare: op: eq value: "root:root" set: true remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chown root:root $schedulerkubeconfig scored: true - id: 1.1.17 text: "Ensure that the controller-manager.conf file permissions are set to 600 or more restrictive (Automated)" audit: "/bin/sh -c 'if test -e $controllermanagerkubeconfig; then stat -c permissions=%a $controllermanagerkubeconfig; fi'" tests: test_items: - flag: "600" compare: op: eq value: "600" set: true remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chmod 600 $controllermanagerkubeconfig scored: true - id: 1.1.18 text: "Ensure that the controller-manager.conf file ownership is set to root:root (Automated)" audit: "stat -c %U:%G $controllermanagerkubeconfig" tests: test_items: - flag: "root:root" compare: op: eq value: "root:root" set: true remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chown root:root $controllermanagerkubeconfig scored: true - id: 1.1.19 text: "Ensure that the Kubernetes PKI directory and file ownership is set to root:root (Automated)" audit: "stat -c %U:%G /var/lib/rancher/rke2/server/tls" use_multiple_values: true tests: test_items: - flag: "root:root" compare: op: eq value: "root:root" set: true remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chown -R root:root /etc/kubernetes/pki/ scored: true - id: 1.1.20 text: "Ensure that the Kubernetes PKI certificate file permissions are set to 600 or more restrictive (Manual)" audit: "stat -c permissions=%a /var/lib/rancher/rke2/server/tls/*.crt" use_multiple_values: true tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" set: true remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chmod -R 600 /var/lib/rancher/rke2/server/tls/*.crt scored: false - id: 1.1.21 text: "Ensure that the Kubernetes PKI key file permissions are set to 600 (Manual)" audit: "stat -c permissions=%a /var/lib/rancher/rke2/server/tls/*.key" use_multiple_values: true tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" set: true remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chmod -R 600 /var/lib/rancher/rke2/server/tls/*.key scored: false - id: 1.2 text: "API Server" checks: - id: 1.2.1 text: "Ensure that the --anonymous-auth argument is set to false (Manual)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" type: manual tests: test_items: - flag: "--anonymous-auth" compare: op: eq value: false remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the below parameter. --anonymous-auth=false scored: false - id: 1.2.2 text: "Ensure that the --token-auth-file parameter is not set (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--token-auth-file" set: false remediation: | Follow the documentation and configure alternate mechanisms for authentication. Then, edit the API server pod specification file $apiserverconf on the control plane node and remove the --token-auth-file= parameter. scored: true - id: 1.2.3 text: "Ensure that the --DenyServiceExternalIPs is not set (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: bin_op: or test_items: - flag: "--enable-admission-plugins" compare: op: nothave value: "DenyServiceExternalIPs" set: true - flag: "--enable-admission-plugins" set: false remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and remove the `DenyServiceExternalIPs` from enabled admission plugins. scored: true - id: 1.2.4 text: "Ensure that the --kubelet-client-certificate and --kubelet-client-key arguments are set as appropriate (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: bin_op: and test_items: - flag: "--kubelet-client-certificate" set: true - flag: "--kubelet-client-key" set: true remediation: | Follow the Kubernetes documentation and set up the TLS connection between the apiserver and kubelets. Then, edit API server pod specification file $apiserverconf on the control plane node and set the kubelet client certificate and key parameters as below. --kubelet-client-certificate= --kubelet-client-key= scored: true - id: 1.2.5 text: "Ensure that the --kubelet-certificate-authority argument is set as appropriate (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--kubelet-certificate-authority" set: true remediation: | Follow the Kubernetes documentation and setup the TLS connection between the apiserver and kubelets. Then, edit the API server pod specification file $apiserverconf on the control plane node and set the --kubelet-certificate-authority parameter to the path to the cert file for the certificate authority. --kubelet-certificate-authority= scored: true - id: 1.2.6 text: "Ensure that the --authorization-mode argument is not set to AlwaysAllow (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--authorization-mode" compare: op: nothave value: "AlwaysAllow" set: true remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the --authorization-mode parameter to values other than AlwaysAllow. One such example could be as below. --authorization-mode=RBAC scored: true - id: 1.2.7 text: "Ensure that the --authorization-mode argument includes Node (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--authorization-mode" compare: op: has value: "Node" set: true remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the --authorization-mode parameter to a value that includes Node. --authorization-mode=Node,RBAC scored: true - id: 1.2.8 text: "Ensure that the --authorization-mode argument includes RBAC (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--authorization-mode" compare: op: has value: "RBAC" set: true remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the --authorization-mode parameter to a value that includes RBAC, for example `--authorization-mode=Node,RBAC`. scored: true - id: 1.2.9 text: "Ensure that the admission control plugin EventRateLimit is set (Manual)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--enable-admission-plugins" compare: op: has value: "EventRateLimit" set: true remediation: | Follow the Kubernetes documentation and set the desired limits in a configuration file. Then, edit the API server pod specification file $apiserverconf and set the below parameters. --enable-admission-plugins=...,EventRateLimit,... --admission-control-config-file= scored: false - id: 1.2.10 text: "Ensure that the admission control plugin AlwaysAdmit is not set (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: bin_op: or test_items: - flag: "--enable-admission-plugins" compare: op: nothave value: AlwaysAdmit set: true - flag: "--enable-admission-plugins" set: false remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and either remove the --enable-admission-plugins parameter, or set it to a value that does not include AlwaysAdmit. scored: true - id: 1.2.11 text: "Ensure that the admission control plugin AlwaysPullImages is set (Manual)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--enable-admission-plugins" compare: op: has value: "AlwaysPullImages" set: true remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the --enable-admission-plugins parameter to include AlwaysPullImages. --enable-admission-plugins=...,AlwaysPullImages,... scored: false - id: 1.2.12 text: "Ensure that the admission control plugin SecurityContextDeny is set if PodSecurityPolicy is not used (Manual)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: bin_op: or test_items: - flag: "--enable-admission-plugins" compare: op: has value: "SecurityContextDeny" set: true - flag: "--enable-admission-plugins" compare: op: has value: "PodSecurityPolicy" set: true remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the --enable-admission-plugins parameter to include SecurityContextDeny, unless PodSecurityPolicy is already in place. --enable-admission-plugins=...,SecurityContextDeny,... scored: false - id: 1.2.13 text: "Ensure that the admission control plugin ServiceAccount is set (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: bin_op: or test_items: - flag: "--disable-admission-plugins" compare: op: nothave value: "ServiceAccount" - flag: "--disable-admission-plugins" set: false remediation: | Follow the documentation and create ServiceAccount objects as per your environment. Then, edit the API server pod specification file $apiserverconf on the control plane node and ensure that the --disable-admission-plugins parameter is set to a value that does not include ServiceAccount. scored: true - id: 1.2.14 text: "Ensure that the admission control plugin NamespaceLifecycle is set (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: bin_op: or test_items: - flag: "--disable-admission-plugins" compare: op: nothave value: "NamespaceLifecycle" - flag: "--disable-admission-plugins" set: false remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the --disable-admission-plugins parameter to ensure it does not include NamespaceLifecycle. scored: true - id: 1.2.15 text: "Ensure that the admission control plugin NodeRestriction is set (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--enable-admission-plugins" compare: op: has value: "NodeRestriction" set: true remediation: | Follow the Kubernetes documentation and configure NodeRestriction plug-in on kubelets. Then, edit the API server pod specification file $apiserverconf on the control plane node and set the --enable-admission-plugins parameter to a value that includes NodeRestriction. --enable-admission-plugins=...,NodeRestriction,... scored: true - id: 1.2.16 text: "Ensure that the --secure-port argument is not set to 0 - NoteThis recommendation is obsolete and will be deleted per the consensus process (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: bin_op: or test_items: - flag: "--secure-port" compare: op: gt value: 0 set: true - flag: "--secure-port" set: false remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and either remove the --secure-port parameter or set it to a different (non-zero) desired port. scored: true - id: 1.2.17 text: "Ensure that the --profiling argument is set to false (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--profiling" compare: op: eq value: false set: true remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the below parameter. --profiling=false scored: true - id: 1.2.18 text: "Ensure that the --audit-log-path argument is set (Automated)" type: "skip" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--audit-log-path" set: true remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the --audit-log-path parameter to a suitable path and file where you would like audit logs to be written, for example, --audit-log-path=/var/log/apiserver/audit.log Permissive. scored: true - id: 1.2.19 text: "Ensure that the --audit-log-maxage argument is set to 30 or as appropriate (Automated)" type: "skip" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--audit-log-maxage" compare: op: gte value: 30 set: true remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the --audit-log-maxage parameter to 30 or as an appropriate number of days, for example, --audit-log-maxage=30 Permissive. scored: true - id: 1.2.20 text: "Ensure that the --audit-log-maxbackup argument is set to 10 or as appropriate (Automated)" type: "skip" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--audit-log-maxbackup" compare: op: gte value: 10 set: true remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the --audit-log-maxbackup parameter to 10 or to an appropriate value. For example, --audit-log-maxbackup=10 Permissive. scored: true - id: 1.2.21 text: "Ensure that the --audit-log-maxsize argument is set to 100 or as appropriate (Automated)" type: "skip" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--audit-log-maxsize" compare: op: gte value: 100 set: true remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the --audit-log-maxsize parameter to an appropriate size in MB. For example, to set it as 100 MB, --audit-log-maxsize=100 scored: true - id: 1.2.22 text: "Ensure that the --request-timeout argument is set as appropriate (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: bin_op: or test_items: - flag: "--request-timeout" set: false - flag: "--request-timeout" remediation: | Edit the API server pod specification file $apiserverconf and set the below parameter as appropriate and if needed. For example, --request-timeout=300s scored: true - id: 1.2.23 text: "Ensure that the --service-account-lookup argument is set to true (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: bin_op: or test_items: - flag: "--service-account-lookup" set: false - flag: "--service-account-lookup" compare: op: eq value: true remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the below parameter. --service-account-lookup=true Alternatively, you can delete the --service-account-lookup parameter from this file so that the default takes effect. scored: true - id: 1.2.24 text: "Ensure that the --service-account-key-file argument is set as appropriate (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--service-account-key-file" set: true remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the --service-account-key-file parameter to the public key file for service accounts. For example, --service-account-key-file= scored: true - id: 1.2.25 text: "Ensure that the --etcd-certfile and --etcd-keyfile arguments are set as appropriate (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: bin_op: and test_items: - flag: "--etcd-certfile" set: true - flag: "--etcd-keyfile" set: true remediation: | Follow the Kubernetes documentation and set up the TLS connection between the apiserver and etcd. Then, edit the API server pod specification file $apiserverconf on the control plane node and set the etcd certificate and key file parameters. --etcd-certfile= --etcd-keyfile= scored: true - id: 1.2.26 text: "Ensure that the --tls-cert-file and --tls-private-key-file arguments are set as appropriate (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: bin_op: and test_items: - flag: "--tls-cert-file" set: true - flag: "--tls-private-key-file" set: true remediation: | Follow the Kubernetes documentation and set up the TLS connection on the apiserver. Then, edit the API server pod specification file $apiserverconf on the control plane node and set the TLS certificate and private key file parameters. --tls-cert-file= --tls-private-key-file= scored: true - id: 1.2.27 text: "Ensure that the --client-ca-file argument is set as appropriate (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--client-ca-file" set: true remediation: | Follow the Kubernetes documentation and set up the TLS connection on the apiserver. Then, edit the API server pod specification file $apiserverconf on the control plane node and set the client certificate authority file. --client-ca-file= scored: true - id: 1.2.28 text: "Ensure that the --etcd-cafile argument is set as appropriate (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--etcd-cafile" set: true remediation: | Follow the Kubernetes documentation and set up the TLS connection between the apiserver and etcd. Then, edit the API server pod specification file $apiserverconf on the control plane node and set the etcd certificate authority file parameter. --etcd-cafile= scored: true - id: 1.2.29 text: "Ensure that the --encryption-provider-config argument is set as appropriate (Manual)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--encryption-provider-config" remediation: | Follow the Kubernetes documentation and configure a EncryptionConfig file. Then, edit the API server pod specification file $apiserverconf on the control plane node and set the --encryption-provider-config parameter to the path of that file. For example, --encryption-provider-config= scored: false - id: 1.2.30 text: "Ensure that encryption providers are appropriately configured (Manual)" type: "skip" audit: | ENCRYPTION_PROVIDER_CONFIG=$(ps -ef | grep $apiserverbin | grep -- --encryption-provider-config | sed 's%.*encryption-provider-config[= ]\([^ ]*\).*%\1%') if test -e $ENCRYPTION_PROVIDER_CONFIG; then grep -A1 'providers:' $ENCRYPTION_PROVIDER_CONFIG | tail -n1 | grep -o "[A-Za-z]*" | sed 's/^/provider=/'; fi tests: test_items: - flag: "provider" compare: op: valid_elements value: "aescbc,kms,secretbox" remediation: | Follow the Kubernetes documentation and configure a EncryptionConfig file. In this file, choose aescbc, kms or secretbox as the encryption provider. Permissive. scored: false - id: 1.2.31 text: "Ensure that the API Server only makes use of Strong Cryptographic Ciphers (Manual)" type: skip audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--tls-cipher-suites" compare: op: valid_elements value: "TLS_AES_128_GCM_SHA256,TLS_AES_256_GCM_SHA384,TLS_CHACHA20_POLY1305_SHA256,TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA,TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,TLS_RSA_WITH_3DES_EDE_CBC_SHA,TLS_RSA_WITH_AES_128_CBC_SHA,TLS_RSA_WITH_AES_128_GCM_SHA256,TLS_RSA_WITH_AES_256_CBC_SHA,TLS_RSA_WITH_AES_256_GCM_SHA384" remediation: | Edit the API server pod specification file /etc/kubernetes/manifests/kube-apiserver.yaml on the control plane node and set the below parameter. --tls-cipher-suites=TLS_AES_128_GCM_SHA256,TLS_AES_256_GCM_SHA384,TLS_CHACHA20_POLY1305_SHA256, TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA,TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305, TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,TLS_RSA_WITH_3DES_EDE_CBC_SHA,TLS_RSA_WITH_AES_128_CBC_SHA, TLS_RSA_WITH_AES_128_GCM_SHA256,TLS_RSA_WITH_AES_256_CBC_SHA,TLS_RSA_WITH_AES_256_GCM_SHA384 Not Applicable. scored: false - id: 1.3 text: "Controller Manager" checks: - id: 1.3.1 text: "Ensure that the --terminated-pod-gc-threshold argument is set as appropriate (Manual)" audit: "/bin/ps -ef | grep $controllermanagerbin | grep -v grep" tests: test_items: - flag: "--terminated-pod-gc-threshold" remediation: | Edit the Controller Manager pod specification file $controllermanagerconf on the control plane node and set the --terminated-pod-gc-threshold to an appropriate threshold, for example, --terminated-pod-gc-threshold=10 scored: false - id: 1.3.2 text: "Ensure that the --profiling argument is set to false (Automated)" audit: "/bin/ps -ef | grep $controllermanagerbin | grep -v grep" tests: test_items: - flag: "--profiling" compare: op: eq value: false set: true remediation: | Edit the Controller Manager pod specification file $controllermanagerconf on the control plane node and set the below parameter. --profiling=false scored: true - id: 1.3.3 text: "Ensure that the --use-service-account-credentials argument is set to true (Automated)" audit: "/bin/ps -ef | grep $controllermanagerbin | grep -v grep" tests: test_items: - flag: "--use-service-account-credentials" compare: op: noteq value: false set: true remediation: | Edit the Controller Manager pod specification file $controllermanagerconf on the control plane node to set the below parameter. --use-service-account-credentials=true scored: true - id: 1.3.4 text: "Ensure that the --service-account-private-key-file argument is set as appropriate (Automated)" audit: "/bin/ps -ef | grep $controllermanagerbin | grep -v grep" tests: test_items: - flag: "--service-account-private-key-file" set: true remediation: | Edit the Controller Manager pod specification file $controllermanagerconf on the control plane node and set the --service-account-private-key-file parameter to the private key file for service accounts. --service-account-private-key-file= scored: true - id: 1.3.5 text: "Ensure that the --root-ca-file argument is set as appropriate (Automated)" audit: "/bin/ps -ef | grep $controllermanagerbin | grep -v grep" tests: test_items: - flag: "--root-ca-file" set: true remediation: | Edit the Controller Manager pod specification file $controllermanagerconf on the control plane node and set the --root-ca-file parameter to the certificate bundle file`. --root-ca-file= scored: true - id: 1.3.6 text: "Ensure that the RotateKubeletServerCertificate argument is set to true (Automated)" audit: "/bin/ps -ef | grep $controllermanagerbin | grep -v grep" tests: bin_op: or test_items: - flag: "--feature-gates" compare: op: nothave value: "RotateKubeletServerCertificate=false" set: true - flag: "--feature-gates" set: false remediation: | Edit the Controller Manager pod specification file $controllermanagerconf on the control plane node and set the --feature-gates parameter to include RotateKubeletServerCertificate=true. --feature-gates=RotateKubeletServerCertificate=true scored: true type: skip - id: 1.3.7 text: "Ensure that the --bind-address argument is set to 127.0.0.1 (Automated)" audit: "/bin/ps -ef | grep $controllermanagerbin | grep -v grep" tests: bin_op: or test_items: - flag: "--bind-address" compare: op: eq value: "127.0.0.1" set: true - flag: "--bind-address" set: false remediation: | Edit the Controller Manager pod specification file $controllermanagerconf on the control plane node and ensure the correct value for the --bind-address parameter scored: true - id: 1.4 text: "Scheduler" checks: - id: 1.4.1 text: "Ensure that the --profiling argument is set to false (Automated)" audit: "/bin/ps -ef | grep $schedulerbin | grep -v grep" tests: test_items: - flag: "--profiling" compare: op: eq value: false set: true remediation: | Edit the Scheduler pod specification file $schedulerconf file on the control plane node and set the below parameter. --profiling=false scored: true - id: 1.4.2 text: "Ensure that the --bind-address argument is set to 127.0.0.1 (Automated)" audit: "/bin/ps -ef | grep $schedulerbin | grep -v grep" tests: bin_op: or test_items: - flag: "--bind-address" compare: op: eq value: "127.0.0.1" set: true - flag: "--bind-address" set: false remediation: | Edit the Scheduler pod specification file $schedulerconf on the control plane node and ensure the correct value for the --bind-address parameter scored: true ================================================ FILE: cfg/rke2-cis-1.7/node.yaml ================================================ --- controls: version: "rke2-cis-1.7" id: 4 text: "Worker Node Security Configuration" type: "node" groups: - id: 4.1 text: "Worker Node Configuration Files" checks: - id: 4.1.1 text: "Ensure that the kubelet service file permissions are set to 600 or more restrictive (Automated)" audit: '/bin/sh -c ''if test -e $kubeletsvc; then stat -c permissions=%a $kubeletsvc; fi'' ' type: "skip" tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Run the below command (based on the file location on your system) on the each worker node. For example, chmod 600 $kubeletsvc Not Applicable. scored: true - id: 4.1.2 text: "Ensure that the kubelet service file ownership is set to root:root (Automated)" audit: '/bin/sh -c ''if test -e $kubeletsvc; then stat -c %U:%G $kubeletsvc; fi'' ' type: "skip" tests: test_items: - flag: "root:root" set: true remediation: | Run the below command (based on the file location on your system) on the each worker node. For example, chown root:root $kubeletsvc Not applicable. scored: true - id: 4.1.3 text: "If proxy kubeconfig file exists ensure permissions are set to 600 or more restrictive (Manual)" audit: '/bin/sh -c ''if test -e $proxykubeconfig; then stat -c permissions=%a $proxykubeconfig; fi'' ' tests: bin_op: or test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Run the below command (based on the file location on your system) on the each worker node. For example, chmod 600 $proxykubeconfig scored: false - id: 4.1.4 text: "If proxy kubeconfig file exists ensure ownership is set to root:root (Manual)" audit: '/bin/sh -c ''if test -e $proxykubeconfig; then stat -c %U:%G $proxykubeconfig; fi'' ' tests: bin_op: or test_items: - flag: root:root remediation: | Run the below command (based on the file location on your system) on the each worker node. For example, chown root:root $proxykubeconfig scored: false - id: 4.1.5 text: "Ensure that the --kubeconfig kubelet.conf file permissions are set to 600 or more restrictive (Automated)" audit: '/bin/sh -c ''if test -e $kubeletkubeconfig; then stat -c permissions=%a $kubeletkubeconfig; fi'' ' tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" set: true remediation: | Run the below command (based on the file location on your system) on the each worker node. For example, chmod 600 $kubeletkubeconfig scored: true - id: 4.1.6 text: "Ensure that the --kubeconfig kubelet.conf file ownership is set to root:root (Automated)" audit: '/bin/sh -c ''if test -e $kubeletkubeconfig; then stat -c %U:%G $kubeletkubeconfig; fi'' ' tests: test_items: - flag: root:root set: true compare: op: eq value: root:root remediation: | Run the below command (based on the file location on your system) on the each worker node. For example, chown root:root $kubeletkubeconfig scored: true - id: 4.1.7 text: "Ensure that the certificate authorities file permissions are set to 600 or more restrictive (Manual)" audit: "check_cafile_permissions.sh" tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" set: true remediation: | Run the following command to modify the file permissions of the --client-ca-file chmod 600 scored: false - id: 4.1.8 text: "Ensure that the client certificate authorities file ownership is set to root:root (Manual)" audit: "check_cafile_ownership.sh" tests: test_items: - flag: root:root remediation: | Run the following command to modify the ownership of the --client-ca-file. chown root:root scored: false - id: 4.1.9 text: "If the kubelet config.yaml configuration file is being used validate permissions set to 600 or more restrictive (Automated)" audit: | /bin/sh -c 'if test -e "$kubeletconf"; then stat -c "permissions=%a" "$kubeletconf" else echo "File not found" fi' tests: bin_op: or test_items: - flag: "permissions" compare: op: bitmask value: "600" set: true - flag: "File not found" remediation: | Run the following command (using the config file location identified in the Audit step) chmod 600 $kubeletconf scored: true - id: 4.1.10 text: "If the kubelet config.yaml configuration file is being used validate file ownership is set to root:root (Automated)" audit: | /bin/sh -c 'if test -e "$kubeletconf"; then stat -c "%U:%G" "$kubeletconf" else echo "File not found" fi' tests: bin_op: or test_items: - flag: root:root set: true - flag: "File not found" remediation: | Run the following command (using the config file location identified in the Audit step) chown root:root $kubeletconf scored: true - id: 4.2 text: "Kubelet" checks: - id: 4.2.1 text: "Ensure that the --anonymous-auth argument is set to false (Automated)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: "--anonymous-auth" path: '{.authentication.anonymous.enabled}' compare: op: eq value: false remediation: | If using a Kubelet config file, edit the file to set `authentication: anonymous: enabled` to `false`. If using executable arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_SYSTEM_PODS_ARGS variable. `--anonymous-auth=false` Based on your system, restart the kubelet service. For example, systemctl daemon-reload systemctl restart kubelet.service scored: true - id: 4.2.2 text: "Ensure that the --authorization-mode argument is not set to AlwaysAllow (Automated)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --authorization-mode path: '{.authorization.mode}' compare: op: nothave value: AlwaysAllow remediation: | If using a Kubelet config file, edit the file to set `authorization.mode` to Webhook. If using executable arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_AUTHZ_ARGS variable. --authorization-mode=Webhook Based on your system, restart the kubelet service. For example, systemctl daemon-reload systemctl restart kubelet.service scored: true - id: 4.2.3 text: "Ensure that the --client-ca-file argument is set as appropriate (Automated)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --client-ca-file path: '{.authentication.x509.clientCAFile}' remediation: | If using a Kubelet config file, edit the file to set `authentication.x509.clientCAFile` to the location of the client CA file. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_AUTHZ_ARGS variable. --client-ca-file= Based on your system, restart the kubelet service. For example, systemctl daemon-reload systemctl restart kubelet.service scored: true - id: 4.2.4 text: "Verify that the --read-only-port argument is set to 0 (Manual)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: bin_op: or test_items: - flag: "--read-only-port" path: '{.readOnlyPort}' compare: op: eq value: 0 - flag: "--read-only-port" path: '{.readOnlyPort}' set: false remediation: | If using a Kubelet config file, edit the file to set `readOnlyPort` to 0. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_SYSTEM_PODS_ARGS variable. --read-only-port=0 Based on your system, restart the kubelet service. For example, systemctl daemon-reload systemctl restart kubelet.service scored: false - id: 4.2.5 text: "Ensure that the --streaming-connection-idle-timeout argument is not set to 0 (Manual)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --streaming-connection-idle-timeout path: '{.streamingConnectionIdleTimeout}' compare: op: noteq value: 0 - flag: --streaming-connection-idle-timeout path: '{.streamingConnectionIdleTimeout}' set: false bin_op: or remediation: | If using a Kubelet config file, edit the file to set `streamingConnectionIdleTimeout` to a value other than 0. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_SYSTEM_PODS_ARGS variable. --streaming-connection-idle-timeout=5m Based on your system, restart the kubelet service. For example, systemctl daemon-reload systemctl restart kubelet.service scored: false - id: 4.2.6 text: "Ensure that the --make-iptables-util-chains argument is set to true (Automated)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --make-iptables-util-chains path: '{.makeIPTablesUtilChains}' compare: op: eq value: true - flag: --make-iptables-util-chains path: '{.makeIPTablesUtilChains}' set: false bin_op: or remediation: | If using a Kubelet config file, edit the file to set `makeIPTablesUtilChains` to `true`. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and remove the --make-iptables-util-chains argument from the KUBELET_SYSTEM_PODS_ARGS variable. Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: true - id: 4.2.7 text: "Ensure that the --hostname-override argument is not set (Manual)" # This is one of those properties that can only be set as a command line argument. # To check if the property is set as expected, we need to parse the kubelet command # instead reading the Kubelet Configuration file. audit: "/bin/ps -fC $kubeletbin " type: skip tests: test_items: - flag: --hostname-override set: false remediation: | Edit the kubelet service file $kubeletsvc on each worker node and remove the --hostname-override argument from the KUBELET_SYSTEM_PODS_ARGS variable. Based on your system, restart the kubelet service. For example, systemctl daemon-reload systemctl restart kubelet.service Not Applicable. scored: false - id: 4.2.8 text: "Ensure that the eventRecordQPS argument is set to a level which ensures appropriate event capture (Manual)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --event-qps path: '{.eventRecordQPS}' compare: op: gte value: 0 - flag: --event-qps path: '{.eventRecordQPS}' set: false bin_op: or remediation: | If using a Kubelet config file, edit the file to set `eventRecordQPS` to an appropriate level. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_SYSTEM_PODS_ARGS variable. Based on your system, restart the kubelet service. For example, systemctl daemon-reload systemctl restart kubelet.service scored: false - id: 4.2.9 text: "Ensure that the --tls-cert-file and --tls-private-key-file arguments are set as appropriate (Manual)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --tls-cert-file path: '{.tlsCertFile}' - flag: --tls-private-key-file path: '{.tlsPrivateKeyFile}' remediation: | If using a Kubelet config file, edit the file to set `tlsCertFile` to the location of the certificate file to use to identify this Kubelet, and `tlsPrivateKeyFile` to the location of the corresponding private key file. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameters in KUBELET_CERTIFICATE_ARGS variable. --tls-cert-file= --tls-private-key-file= Based on your system, restart the kubelet service. For example, systemctl daemon-reload systemctl restart kubelet.service scored: false - id: 4.2.10 text: "Ensure that the --rotate-certificates argument is not set to false (Automated)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --rotate-certificates path: '{.rotateCertificates}' compare: op: eq value: true - flag: --rotate-certificates path: '{.rotateCertificates}' set: false bin_op: or remediation: | If using a Kubelet config file, edit the file to add the line `rotateCertificates` to `true` or remove it altogether to use the default value. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and remove --rotate-certificates=false argument from the KUBELET_CERTIFICATE_ARGS variable. Based on your system, restart the kubelet service. For example, systemctl daemon-reload systemctl restart kubelet.service scored: true - id: 4.2.11 text: "Verify that the RotateKubeletServerCertificate argument is set to true (Manual)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: bin_op: or test_items: - flag: RotateKubeletServerCertificate path: '{.featureGates.RotateKubeletServerCertificate}' compare: op: nothave value: false - flag: RotateKubeletServerCertificate path: '{.featureGates.RotateKubeletServerCertificate}' set: false remediation: | Edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_CERTIFICATE_ARGS variable. --feature-gates=RotateKubeletServerCertificate=true Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: false - id: 4.2.12 text: "Ensure that the Kubelet only makes use of Strong Cryptographic Ciphers (Manual)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --tls-cipher-suites path: '{range .tlsCipherSuites[:]}{}{'',''}{end}' compare: op: valid_elements value: 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 remediation: | If using a Kubelet config file, edit the file to set `tlsCipherSuites` to 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 or to a subset of these values. If using executable arguments, edit the kubelet service file $kubeletsvc on each worker node and set the --tls-cipher-suites parameter as follows, or to a subset of these values. --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 Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: false - id: 4.2.13 text: "Ensure that a limit is set on pod PIDs (Manual)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/cat $kubeletconf" tests: test_items: - flag: --pod-max-pids path: '{.podPidsLimit}' remediation: | Decide on an appropriate level for this parameter and set it, either via the --pod-max-pids command line parameter or the PodPidsLimit configuration file setting. scored: false ================================================ FILE: cfg/rke2-cis-1.7/policies.yaml ================================================ --- controls: version: "rke2-cis-1.7" id: 5 text: "Kubernetes Policies" type: "policies" groups: - id: 5.1 text: "RBAC and Service Accounts" checks: - id: 5.1.1 text: "Ensure that the cluster-admin role is only used where required (Manual)" type: "manual" remediation: | Identify all clusterrolebindings to the cluster-admin role. Check if they are used and if they need this role or if they could use a role with fewer privileges. Where possible, first bind users to a lower privileged role and then remove the clusterrolebinding to the cluster-admin role : kubectl delete clusterrolebinding [name] scored: false - id: 5.1.2 text: "Minimize access to secrets (Manual)" type: "manual" remediation: | Where possible, remove get, list and watch access to Secret objects in the cluster. scored: false - id: 5.1.3 text: "Minimize wildcard use in Roles and ClusterRoles (Manual)" type: "manual" remediation: | Where possible replace any use of wildcards in clusterroles and roles with specific objects or actions. scored: false - id: 5.1.4 text: "Minimize access to create pods (Manual)" type: "manual" remediation: | Where possible, remove create access to pod objects in the cluster. scored: false - id: 5.1.5 text: "Ensure that default service accounts are not actively used. (Manual)" type: "manual" remediation: | Create explicit service accounts wherever a Kubernetes workload requires specific access to the Kubernetes API server. Modify the configuration of each default service account to include this value automountServiceAccountToken: false scored: false - id: 5.1.6 text: "Ensure that Service Account Tokens are only mounted where necessary (Manual)" type: "manual" remediation: | Modify the definition of pods and service accounts which do not need to mount service account tokens to disable it. scored: false - id: 5.1.7 text: "Avoid use of system:masters group (Manual)" type: "manual" remediation: | Remove the system:masters group from all users in the cluster. scored: false - id: 5.1.8 text: "Limit use of the Bind, Impersonate and Escalate permissions in the Kubernetes cluster (Manual)" type: "manual" remediation: | Where possible, remove the impersonate, bind and escalate rights from subjects. scored: false - id: 5.1.9 text: "Minimize access to create persistent volumes (Manual)" type: "manual" remediation: | Where possible, remove create access to PersistentVolume objects in the cluster. scored: false - id: 5.1.10 text: "Minimize access to the proxy sub-resource of nodes (Manual)" type: "manual" remediation: | Where possible, remove access to the proxy sub-resource of node objects. scored: false - id: 5.1.11 text: "Minimize access to the approval sub-resource of certificatesigningrequests objects (Manual)" type: "manual" remediation: | Where possible, remove access to the approval sub-resource of certificatesigningrequest objects. scored: false - id: 5.1.12 text: "Minimize access to webhook configuration objects (Manual)" type: "manual" remediation: | Where possible, remove access to the validatingwebhookconfigurations or mutatingwebhookconfigurations objects scored: false - id: 5.1.13 text: "Minimize access to the service account token creation (Manual)" type: "manual" remediation: | Where possible, remove access to the token sub-resource of serviceaccount objects. scored: false - id: 5.2 text: "Pod Security Standards" checks: - id: 5.2.1 text: "Ensure that the cluster has at least one active policy control mechanism in place (Manual)" type: "manual" remediation: | Ensure that either Pod Security Admission or an external policy control system is in place for every namespace which contains user workloads. scored: false - id: 5.2.2 text: "Minimize the admission of privileged containers (Manual)" type: "manual" remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of privileged containers. scored: false - id: 5.2.3 text: "Minimize the admission of containers wishing to share the host process ID namespace (Manual)" type: "manual" remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of `hostPID` containers. scored: false - id: 5.2.4 text: "Minimize the admission of containers wishing to share the host IPC namespace (Manual)" type: "manual" remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of `hostIPC` containers. scored: false - id: 5.2.5 text: "Minimize the admission of containers wishing to share the host network namespace (Manual)" type: "manual" remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of `hostNetwork` containers. scored: false - id: 5.2.6 text: "Minimize the admission of containers with allowPrivilegeEscalation (Manual)" type: "manual" remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of containers with `.spec.allowPrivilegeEscalation` set to `true`. scored: false - id: 5.2.7 text: "Minimize the admission of root containers (Manual)" type: "manual" remediation: | Create a policy for each namespace in the cluster, ensuring that either `MustRunAsNonRoot` or `MustRunAs` with the range of UIDs not including 0, is set. scored: false - id: 5.2.8 text: "Minimize the admission of containers with the NET_RAW capability (Manual)" type: "manual" remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of containers with the `NET_RAW` capability. scored: false - id: 5.2.9 text: "Minimize the admission of containers with added capabilities (Automated)" type: "manual" remediation: | Ensure that `allowedCapabilities` is not present in policies for the cluster unless it is set to an empty array. scored: false - id: 5.2.10 text: "Minimize the admission of containers with capabilities assigned (Manual)" type: "manual" remediation: | Review the use of capabilites in applications running on your cluster. Where a namespace contains applications which do not require any Linux capabities to operate consider adding a PSP which forbids the admission of containers which do not drop all capabilities. scored: false - id: 5.2.11 text: "Minimize the admission of Windows HostProcess containers (Manual)" type: "manual" remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of containers that have `.securityContext.windowsOptions.hostProcess` set to `true`. scored: false - id: 5.2.12 text: "Minimize the admission of HostPath volumes (Manual)" type: "manual" remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of containers with `hostPath` volumes. scored: false - id: 5.2.13 text: "Minimize the admission of containers which use HostPorts (Manual)" type: "manual" remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of containers which use `hostPort` sections. scored: false - id: 5.3 text: "Network Policies and CNI" checks: - id: 5.3.1 text: "Ensure that the CNI in use supports NetworkPolicies (Manual)" type: "manual" remediation: | If the CNI plugin in use does not support network policies, consideration should be given to making use of a different plugin, or finding an alternate mechanism for restricting traffic in the Kubernetes cluster. scored: false - id: 5.3.2 text: "Ensure that all Namespaces have NetworkPolicies defined (Manual)" type: "manual" remediation: | Follow the documentation and create NetworkPolicy objects as you need them. scored: false - id: 5.4 text: "Secrets Management" checks: - id: 5.4.1 text: "Prefer using Secrets as files over Secrets as environment variables (Manual)" type: "manual" remediation: | If possible, rewrite application code to read Secrets from mounted secret files, rather than from environment variables. scored: false - id: 5.4.2 text: "Consider external secret storage (Manual)" type: "manual" remediation: | Refer to the Secrets management options offered by your cloud provider or a third-party secrets management solution. scored: false - id: 5.5 text: "Extensible Admission Control" checks: - id: 5.5.1 text: "Configure Image Provenance using ImagePolicyWebhook admission controller (Manual)" type: "manual" remediation: | Follow the Kubernetes documentation and setup image provenance. scored: false - id: 5.7 text: "General Policies" checks: - id: 5.7.1 text: "Create administrative boundaries between resources using namespaces (Manual)" type: "manual" remediation: | Follow the documentation and create namespaces for objects in your deployment as you need them. scored: false - id: 5.7.2 text: "Ensure that the seccomp profile is set to docker/default in your Pod definitions (Manual)" type: "manual" remediation: | Use `securityContext` to enable the docker/default seccomp profile in your pod definitions. An example is as below: securityContext: seccompProfile: type: RuntimeDefault scored: false - id: 5.7.3 text: "Apply SecurityContext to your Pods and Containers (Manual)" type: "manual" remediation: | Follow the Kubernetes documentation and apply SecurityContexts to your Pods. For a suggested list of SecurityContexts, you may refer to the CIS Security Benchmark for Docker Containers. scored: false - id: 5.7.4 text: "The default namespace should not be used (Manual)" type: "manual" remediation: | Ensure that namespaces are created to allow for appropriate segregation of Kubernetes resources and that all new resources are created in a specific namespace. scored: false ================================================ FILE: cfg/rke2-cis-1.8/config.yaml ================================================ --- ## Version-specific settings that override the values in cfg/config.yaml ================================================ FILE: cfg/rke2-cis-1.8/controlplane.yaml ================================================ --- controls: version: "rke2-cis-1.8" id: 3 text: "Control Plane Configuration" type: "controlplane" groups: - id: 3.1 text: "Authentication and Authorization" checks: - id: 3.1.1 text: "Client certificate authentication should not be used for users (Manual)" type: "manual" remediation: | Alternative mechanisms provided by Kubernetes such as the use of OIDC should be implemented in place of client certificates. scored: false - id: 3.1.2 text: "Service account token authentication should not be used for users (Manual)" type: "manual" remediation: | Alternative mechanisms provided by Kubernetes such as the use of OIDC should be implemented in place of service account tokens. scored: false - id: 3.1.3 text: "Bootstrap token authentication should not be used for users (Manual)" type: "manual" remediation: | Alternative mechanisms provided by Kubernetes such as the use of OIDC should be implemented in place of bootstrap tokens. scored: false - id: 3.2 text: "Logging" checks: - id: 3.2.1 text: "Ensure that a minimal audit policy is created (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--audit-policy-file" set: true remediation: | Create an audit policy file for your cluster. scored: true - id: 3.2.2 text: "Ensure that the audit policy covers key security concerns (Manual)" type: "manual" remediation: | Review the audit policy provided for the cluster and ensure that it covers at least the following areas, - Access to Secrets managed by the cluster. Care should be taken to only log Metadata for requests to Secrets, ConfigMaps, and TokenReviews, in order to avoid risk of logging sensitive data. - Modification of Pod and Deployment objects. - Use of `pods/exec`, `pods/portforward`, `pods/proxy` and `services/proxy`. For most requests, minimally logging at the Metadata level is recommended (the most basic level of logging). scored: false ================================================ FILE: cfg/rke2-cis-1.8/etcd.yaml ================================================ --- controls: version: "rke2-cis-1.8" id: 2 text: "Etcd Node Configuration" type: "etcd" groups: - id: 2 text: "Etcd Node Configuration" checks: - id: 2.1 text: "Ensure that the --cert-file and --key-file arguments are set as appropriate (Automated)" audit: "cat $etcdconf" tests: bin_op: and test_items: - flag: "cert-file" path: "{.client-transport-security.cert-file}" compare: op: eq value: "/var/lib/rancher/rke2/server/tls/etcd/server-client.crt" - flag: "key-file" path: "{.client-transport-security.key-file}" compare: op: eq value: "/var/lib/rancher/rke2/server/tls/etcd/server-client.key" remediation: | Follow the etcd service documentation and configure TLS encryption. Then, edit the etcd pod specification file /etc/kubernetes/manifests/etcd.yaml on the master node and set the below parameters. --cert-file= --key-file= scored: true - id: 2.2 text: "Ensure that the --client-cert-auth argument is set to true (Automated)" audit: "cat $etcdconf" tests: test_items: - flag: "client-cert-auth" path: "{.client-transport-security.client-cert-auth}" compare: op: eq value: true remediation: | Edit the etcd pod specification file $etcdconf on the master node and set the below parameter. --client-cert-auth="true" scored: true - id: 2.3 text: "Ensure that the --auto-tls argument is not set to true (Automated)" audit: "cat $etcdconf" tests: bin_op: or test_items: - flag: "auto-tls" path: "{.client-transport-security.auto-tls}" compare: op: noteq value: true - flag: "auto-tls" path: "{.client-transport-security.auto-tls}" set: false remediation: | Edit the etcd pod specification file $etcdconf on the master node and either remove the --auto-tls parameter or set it to false. --auto-tls=false scored: true - id: 2.4 text: "Ensure that the --peer-cert-file and --peer-key-file arguments are set as appropriate (Automated)" audit: "cat $etcdconf" audit_config: "cat $etcdconf" tests: test_items: - path: "{.peer-transport-security.cert-file}" compare: op: eq value: "/var/lib/rancher/rke2/server/tls/etcd/peer-server-client.crt" - path: "{.peer-transport-security.key-file}" compare: op: eq value: "/var/lib/rancher/rke2/server/tls/etcd/peer-server-client.key" remediation: | Follow the etcd service documentation and configure peer TLS encryption as appropriate for your etcd cluster. Then, edit the etcd pod specification file $etcdconf on the master node and set the below parameters. --peer-client-file= --peer-key-file= scored: true - id: 2.5 text: "Ensure that the --peer-client-cert-auth argument is set to true (Automated)" audit: "cat $etcdconf" tests: test_items: - flag: "client-cert-auth" path: "{.peer-transport-security.client-cert-auth}" compare: op: eq value: true remediation: | Edit the etcd pod specification file $etcdconf on the master node and set the below parameter. --peer-client-cert-auth=true scored: true - id: 2.6 text: "Ensure that the --peer-auto-tls argument is not set to true (Automated)" audit: "cat $etcdconf" tests: bin_op: or test_items: - flag: "peer-auto-tls" path: "{.peer-transport-security.auto-tls}" compare: op: noteq value: true - flag: "peer-auto-tls" path: "{.peer-transport-security.auto-tls}" set: false remediation: | Edit the etcd pod specification file $etcdconf on the master node and either remove the --peer-auto-tls parameter or set it to false. --peer-auto-tls=false scored: true - id: 2.7 text: "Ensure that a unique Certificate Authority is used for etcd (Automated)" audit: "cat $etcdconf" audit_config: "cat $etcdconf" tests: test_items: - path: "{.peer-transport-security.trusted-ca-file}" compare: op: eq value: "/var/lib/rancher/rke2/server/tls/etcd/peer-ca.crt" set: true remediation: | [Manual test] Follow the etcd documentation and create a dedicated certificate authority setup for the etcd service. Then, edit the etcd pod specification file $etcdconf on the master node and set the below parameter. --trusted-ca-file= scored: true ================================================ FILE: cfg/rke2-cis-1.8/master.yaml ================================================ --- controls: version: "rke2-cis-1.8" id: 1 text: "Control Plane Security Configuration" type: "master" groups: - id: 1.1 text: "Control Plane Node Configuration Files" checks: - id: 1.1.1 text: "Ensure that the API server pod specification file permissions are set to 600 or more restrictive (Automated)" audit: "/bin/sh -c 'if test -e $apiserverconf; then stat -c permissions=%a $apiserverconf; fi'" tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chmod 600 $apiserverconf Not Applicable - Cluster provisioned by RKE doesn't require or maintain a configuration file for kube-apiserver. All configuration is passed in as arguments at container run time. scored: true - id: 1.1.2 text: "Ensure that the API server pod specification file ownership is set to root:root (Automated)" audit: "/bin/sh -c 'if test -e $apiserverconf; then stat -c %U:%G $apiserverconf; fi'" tests: test_items: - flag: "root:root" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chown root:root $apiserverconf Not Applicable - Cluster provisioned by RKE doesn't require or maintain a configuration file for kube-apiserver. All configuration is passed in as arguments at container run time. scored: true - id: 1.1.3 text: "Ensure that the controller manager pod specification file permissions are set to 600 or more restrictive (Automated)" audit: "/bin/sh -c 'if test -e $controllermanagerconf; then stat -c permissions=%a $controllermanagerconf; fi'" tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chmod 600 $controllermanagerconf Not Applicable - Cluster provisioned by RKE doesn't require or maintain a configuration file for kube-apiserver. All configuration is passed in as arguments at container run time. scored: true - id: 1.1.4 text: "Ensure that the controller manager pod specification file ownership is set to root:root (Automated)" audit: "/bin/sh -c 'if test -e $controllermanagerconf; then stat -c %U:%G $controllermanagerconf; fi'" tests: test_items: - flag: "root:root" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chown root:root $controllermanagerconf Not Applicable - Cluster provisioned by RKE doesn't require or maintain a configuration file for kube-apiserver. All configuration is passed in as arguments at container run time. scored: true - id: 1.1.5 text: "Ensure that the scheduler pod specification file permissions are set to 600 or more restrictive (Automated)" audit: "/bin/sh -c 'if test -e $schedulerconf; then stat -c permissions=%a $schedulerconf; fi'" tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chmod 600 $schedulerconf Not Applicable - Cluster provisioned by RKE doesn't require or maintain a configuration file for kube-apiserver. All configuration is passed in as arguments at container run time. scored: true - id: 1.1.6 text: "Ensure that the scheduler pod specification file ownership is set to root:root (Automated)" audit: "/bin/sh -c 'if test -e $schedulerconf; then stat -c %U:%G $schedulerconf; fi'" tests: test_items: - flag: "root:root" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chown root:root $schedulerconf Not Applicable - Cluster provisioned by RKE doesn't require or maintain a configuration file for kube-apiserver. All configuration is passed in as arguments at container run time. scored: true - id: 1.1.7 text: "Ensure that the etcd pod specification file permissions are set to 600 or more restrictive (Automated)" audit: | /bin/sh -c ' if [ -e "$etcdconf" ]; then stat -c "permissions=%a %n" "$etcdconf" else echo "Directory not found" fi ' tests: bin_op: or test_items: - flag: "permissions" compare: op: bitmask value: "600" - flag: "Directory not found" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chmod 600 $etcdconf Not Applicable - Cluster provisioned by RKE doesn't require or maintain a configuration file for kube-apiserver. All configuration is passed in as arguments at container run time. scored: true - id: 1.1.8 text: "Ensure that the etcd pod specification file ownership is set to root:root (Automated)" audit: | /bin/sh -c ' if [ -e "$etcdconf" ]; then stat -c "ownership=%U:%G %n" "$etcdconf" else echo "Directory not found" fi ' tests: bin_op: or test_items: - flag: "ownership" compare: op: eq value: "root:root" set: true - flag: "Directory not found" set: true remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chown root:root $etcdconf Not Applicable - Cluster provisioned by RKE doesn't require or maintain a configuration file for kube-apiserver. All configuration is passed in as arguments at container run time. scored: true - id: 1.1.9 text: "Ensure that the Container Network Interface file permissions are set to 600 or more restrictive (Manual)" audit: | ps -ef | grep $kubeletbin | grep -- --cni-conf-dir | sed 's%.*cni-conf-dir[= ]\([^ ]*\).*%\1%' | xargs -I{} find {} -mindepth 1 | xargs --no-run-if-empty stat -c permissions=%a find /var/lib/cni/networks -type f 2> /dev/null | xargs --no-run-if-empty stat -c permissions=%a use_multiple_values: true tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chmod 600 scored: false - id: 1.1.10 text: "Ensure that the Container Network Interface file ownership is set to root:root (Manual)" audit: | ps -ef | grep $kubeletbin | grep -- --cni-conf-dir | sed 's%.*cni-conf-dir[= ]\([^ ]*\).*%\1%' | xargs -I{} find {} -mindepth 1 | xargs --no-run-if-empty stat -c %U:%G find /var/lib/cni/networks -type f 2> /dev/null | xargs --no-run-if-empty stat -c %U:%G use_multiple_values: true tests: test_items: - flag: "root:root" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chown root:root scored: false - id: 1.1.11 text: "Ensure that the etcd data directory permissions are set to 700 or more restrictive (Automated)" audit: | /bin/sh -c 'if [ -e "/var/lib/rancher/rke2/server/db/etcd" ]; then stat -c "permissions=%a %n" "/var/lib/rancher/rke2/server/db/etcd" else echo "Directory not found: /var/lib/rancher/rke2/server/db/etcd" fi' tests: bin_op: or test_items: - flag: "permissions" compare: op: bitmask value: "700" set: true - flag: "Directory not found: /var/lib/rancher/rke2/server/db/etcd" remediation: | On the etcd server node, get the etcd data directory, passed as an argument --data-dir, from the command 'ps -ef | grep etcd'. Run the below command (based on the etcd data directory found above). For example, chmod 700 /var/lib/etcd scored: true - id: 1.1.12 text: "Ensure that the etcd data directory ownership is set to etcd:etcd (Automated)" audit: | /bin/sh -c ' if [ -e "$etcdconf" ]; then stat -c "ownership=%U:%G %n" "$etcdconf" else echo "Directory not found" fi ' type: "skip" tests: bin_op: or test_items: - flag: "ownership" compare: op: eq value: "root:root" set: true - flag: "Directory not found" set: true remediation: | On the etcd server node, get the etcd data directory, passed as an argument --data-dir, from the command 'ps -ef | grep etcd'. Run the below command (based on the etcd data directory found above). For example, chown etcd:etcd /var/lib/etcd Permissive - A system service account is required for etcd data directory ownership. Refer to Rancher's hardening guide for more details on how to configure this ownership. scored: true - id: 1.1.13 text: "Ensure that the admin.conf file permissions are set to 600 or more restrictive (Automated)" audit: "stat -c permissions=%a /var/lib/rancher/rke2/server/cred/admin.kubeconfig" tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chmod 600 /etc/kubernetes/admin.conf Not Applicable - Cluster provisioned by RKE does not store the kubernetes default kubeconfig credentials file on the nodes. scored: true - id: 1.1.14 text: "Ensure that the admin.conf file ownership is set to root:root (Automated)" audit: "stat -c %U:%G /var/lib/rancher/rke2/server/cred/admin.kubeconfig" tests: test_items: - flag: "root:root" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chown root:root /etc/kubernetes/admin.conf Not Applicable - Cluster provisioned by RKE does not store the kubernetes default kubeconfig credentials file on the nodes. scored: true - id: 1.1.15 text: "Ensure that the scheduler.conf file permissions are set to 600 or more restrictive (Automated)" audit: "/bin/sh -c 'if test -e $schedulerkubeconfig; then stat -c permissions=%a $schedulerkubeconfig; fi'" tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chmod 600 $schedulerkubeconfig Not Applicable - Cluster provisioned by RKE doesn't require or maintain a configuration file for scheduler. All configuration is passed in as arguments at container run time. scored: true - id: 1.1.16 text: "Ensure that the scheduler.conf file ownership is set to root:root (Automated)" audit: "/bin/sh -c 'if test -e $schedulerkubeconfig; then stat -c %U:%G $schedulerkubeconfig; fi'" tests: test_items: - flag: "root:root" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chown root:root $schedulerkubeconfig Not Applicable - Cluster provisioned by RKE doesn't require or maintain a configuration file for scheduler. All configuration is passed in as arguments at container run time. scored: true - id: 1.1.17 text: "Ensure that the controller-manager.conf file permissions are set to 600 or more restrictive (Automated)" audit: "/bin/sh -c 'if test -e $controllermanagerkubeconfig; then stat -c permissions=%a $controllermanagerkubeconfig; fi'" tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chmod 600 $controllermanagerkubeconfig Not Applicable - Cluster provisioned by RKE doesn't require or maintain a configuration file for controller-manager. All configuration is passed in as arguments at container run time. scored: true - id: 1.1.18 text: "Ensure that the controller-manager.conf file ownership is set to root:root (Automated)" audit: "/bin/sh -c 'if test -e $controllermanagerkubeconfig; then stat -c %U:%G $controllermanagerkubeconfig; fi'" tests: test_items: - flag: "root:root" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chown root:root $controllermanagerkubeconfig Not Applicable - Cluster provisioned by RKE doesn't require or maintain a configuration file for controller-manager. All configuration is passed in as arguments at container run time. scored: true - id: 1.1.19 text: "Ensure that the Kubernetes PKI directory and file ownership is set to root:root (Automated)" audit: | if test -d /var/lib/rancher/rke2/server/tls; then find /var/lib/rancher/rke2/server/tls -exec stat -c "%U:%G" {} \; else echo "NA" fi tests: test_items: - flag: "root:root" set: true remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, chown root:root $controllermanagerkubeconfig Not Applicable - Cluster provisioned by RKE doesn't require or maintain a configuration file for controller-manager. All configuration is passed in as arguments at container run time. scored: true - id: 1.1.20 text: "Ensure that the Kubernetes PKI certificate file permissions are set to 600 or more restrictive (Manual)" audit: | if [ -d /node/etc/kubernetes/ssl ]; then find /node/etc/kubernetes/ssl -type f -name '*.pem' ! -name '*key.pem' \ -exec stat -c 'permissions=%a %n' {} + else echo "Directory not found: /node/etc/kubernetes/ssl" fi use_multiple_values: true tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, find /node/etc/kubernetes/ssl/ -name '*.pem' ! -name '*key.pem' -exec chmod -R 600 {} + scored: false - id: 1.1.21 text: "Ensure that the Kubernetes PKI key file permissions are set to 600 (Manual)" audit: | if [ -d /node/etc/kubernetes/ssl ]; then find /node/etc/kubernetes/ssl -type f -name '*key.pem' -exec stat -c 'permissions=%a %n' {} + else echo "Directory not found: /node/etc/kubernetes/ssl" fi use_multiple_values: true tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Run the below command (based on the file location on your system) on the control plane node. For example, find /node/etc/kubernetes/ssl/ -name '*key.pem' -exec chmod -R 600 {} + scored: false - id: 1.2 text: "API Server" checks: - id: 1.2.1 text: "Ensure that the --anonymous-auth argument is set to false (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--anonymous-auth" compare: op: eq value: false set: true remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the below parameter. --anonymous-auth=false scored: true - id: 1.2.2 text: "Ensure that the --token-auth-file parameter is not set (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--token-auth-file" set: false remediation: | Follow the documentation and configure alternate mechanisms for authentication. Then, edit the API server pod specification file $apiserverconf on the control plane node and remove the --token-auth-file= parameter. scored: true - id: 1.2.3 text: "Ensure that the --DenyServiceExternalIPs is set (Manual)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--enable-admission-plugins" compare: op: has value: "DenyServiceExternalIPs" remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and add the `DenyServiceExternalIPs` plugin to the enabled admission plugins, as such --enable-admission-plugin=DenyServiceExternalIPs. scored: false - id: 1.2.4 text: "Ensure that the --kubelet-client-certificate and --kubelet-client-key arguments are set as appropriate (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: bin_op: and test_items: - flag: "--kubelet-client-certificate" - flag: "--kubelet-client-key" remediation: | Follow the Kubernetes documentation and set up the TLS connection between the apiserver and kubelets. Then, edit API server pod specification file $apiserverconf on the control plane node and set the kubelet client certificate and key parameters as below. --kubelet-client-certificate= --kubelet-client-key= scored: true - id: 1.2.5 text: "Ensure that the --kubelet-certificate-authority argument is set as appropriate (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--kubelet-certificate-authority" set: true remediation: | Follow the Kubernetes documentation and setup the TLS connection between the apiserver and kubelets. Then, edit the API server pod specification file $apiserverconf on the control plane node and set the --kubelet-certificate-authority parameter to the path to the cert file for the certificate authority. --kubelet-certificate-authority= Permissive - When generating serving certificates, functionality could break in conjunction with hostname overrides which are required for certain cloud providers. scored: true - id: 1.2.6 text: "Ensure that the --authorization-mode argument is not set to AlwaysAllow (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--authorization-mode" compare: op: nothave value: "AlwaysAllow" remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the --authorization-mode parameter to values other than AlwaysAllow. One such example could be as below. --authorization-mode=RBAC scored: true - id: 1.2.7 text: "Ensure that the --authorization-mode argument includes Node (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--authorization-mode" compare: op: has value: "Node" remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the --authorization-mode parameter to a value that includes Node. --authorization-mode=Node,RBAC scored: true - id: 1.2.8 text: "Ensure that the --authorization-mode argument includes RBAC (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--authorization-mode" compare: op: has value: "RBAC" remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the --authorization-mode parameter to a value that includes RBAC, for example `--authorization-mode=Node,RBAC`. scored: true - id: 1.2.9 text: "Ensure that the admission control plugin EventRateLimit is set (Manual)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--enable-admission-plugins" compare: op: has value: "EventRateLimit" remediation: | Follow the Kubernetes documentation and set the desired limits in a configuration file. Then, edit the API server pod specification file $apiserverconf and set the below parameters. --enable-admission-plugins=...,EventRateLimit,... --admission-control-config-file= scored: false - id: 1.2.10 text: "Ensure that the admission control plugin AlwaysAdmit is not set (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: bin_op: or test_items: - flag: "--enable-admission-plugins" compare: op: nothave value: AlwaysAdmit - flag: "--enable-admission-plugins" set: false remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and either remove the --enable-admission-plugins parameter, or set it to a value that does not include AlwaysAdmit. scored: true - id: 1.2.11 text: "Ensure that the admission control plugin AlwaysPullImages is set (Manual)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--enable-admission-plugins" compare: op: has value: "AlwaysPullImages" remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the --enable-admission-plugins parameter to include AlwaysPullImages. --enable-admission-plugins=...,AlwaysPullImages,... scored: false - id: 1.2.12 text: "Ensure that the admission control plugin SecurityContextDeny is set if PodSecurityPolicy is not used (Manual)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: bin_op: or test_items: - flag: "--enable-admission-plugins" compare: op: has value: "SecurityContextDeny" - flag: "--enable-admission-plugins" compare: op: has value: "PodSecurityPolicy" remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the --enable-admission-plugins parameter to include SecurityContextDeny, unless PodSecurityPolicy is already in place. --enable-admission-plugins=...,SecurityContextDeny,... Permissive - Enabling Pod Security Policy can cause applications to unexpectedly fail. scored: false - id: 1.2.13 text: "Ensure that the admission control plugin ServiceAccount is set (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: bin_op: or test_items: - flag: "--disable-admission-plugins" compare: op: nothave value: "ServiceAccount" - flag: "--disable-admission-plugins" set: false remediation: | Follow the documentation and create ServiceAccount objects as per your environment. Then, edit the API server pod specification file $apiserverconf on the control plane node and ensure that the --disable-admission-plugins parameter is set to a value that does not include ServiceAccount. scored: true - id: 1.2.14 text: "Ensure that the admission control plugin NamespaceLifecycle is set (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: bin_op: or test_items: - flag: "--disable-admission-plugins" compare: op: nothave value: "NamespaceLifecycle" - flag: "--disable-admission-plugins" set: false remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the --disable-admission-plugins parameter to ensure it does not include NamespaceLifecycle. scored: true - id: 1.2.15 text: "Ensure that the admission control plugin NodeRestriction is set (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--enable-admission-plugins" compare: op: has value: "NodeRestriction" remediation: | Follow the Kubernetes documentation and configure NodeRestriction plug-in on kubelets. Then, edit the API server pod specification file $apiserverconf on the control plane node and set the --enable-admission-plugins parameter to a value that includes NodeRestriction. --enable-admission-plugins=...,NodeRestriction,... scored: true - id: 1.2.16 text: "Ensure that the --profiling argument is set to false (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--profiling" compare: op: eq value: false remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the below parameter. --profiling=false scored: true - id: 1.2.17 text: "Ensure that the --audit-log-path argument is set (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--audit-log-path" remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the --audit-log-path parameter to a suitable path and file where you would like audit logs to be written, for example, --audit-log-path=/var/log/apiserver/audit.log scored: true - id: 1.2.18 text: "Ensure that the --audit-log-maxage argument is set to 30 or as appropriate (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--audit-log-maxage" compare: op: gte value: 30 remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the --audit-log-maxage parameter to 30 or as an appropriate number of days, for example, --audit-log-maxage=30 scored: true - id: 1.2.19 text: "Ensure that the --audit-log-maxbackup argument is set to 10 or as appropriate (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--audit-log-maxbackup" compare: op: gte value: 10 remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the --audit-log-maxbackup parameter to 10 or to an appropriate value. For example, --audit-log-maxbackup=10 scored: true - id: 1.2.20 text: "Ensure that the --audit-log-maxsize argument is set to 100 or as appropriate (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--audit-log-maxsize" compare: op: gte value: 100 remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the --audit-log-maxsize parameter to an appropriate size in MB. For example, to set it as 100 MB, --audit-log-maxsize=100 scored: true - id: 1.2.21 text: "Ensure that the --request-timeout argument is set as appropriate (Manual)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" type: manual remediation: | Edit the API server pod specification file $apiserverconf and set the below parameter as appropriate and if needed. For example, --request-timeout=300s scored: false - id: 1.2.22 text: "Ensure that the --service-account-lookup argument is set to true (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: bin_op: or test_items: - flag: "--service-account-lookup" set: false - flag: "--service-account-lookup" compare: op: eq value: true remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the below parameter. --service-account-lookup=true Alternatively, you can delete the --service-account-lookup parameter from this file so that the default takes effect. scored: true - id: 1.2.23 text: "Ensure that the --service-account-key-file argument is set as appropriate (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--service-account-key-file" remediation: | Edit the API server pod specification file $apiserverconf on the control plane node and set the --service-account-key-file parameter to the public key file for service accounts. For example, --service-account-key-file= scored: true - id: 1.2.24 text: "Ensure that the --etcd-certfile and --etcd-keyfile arguments are set as appropriate (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: bin_op: and test_items: - flag: "--etcd-certfile" - flag: "--etcd-keyfile" remediation: | Follow the Kubernetes documentation and set up the TLS connection between the apiserver and etcd. Then, edit the API server pod specification file $apiserverconf on the control plane node and set the etcd certificate and key file parameters. --etcd-certfile= --etcd-keyfile= scored: true - id: 1.2.25 text: "Ensure that the --tls-cert-file and --tls-private-key-file arguments are set as appropriate (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: bin_op: and test_items: - flag: "--tls-cert-file" - flag: "--tls-private-key-file" remediation: | Follow the Kubernetes documentation and set up the TLS connection on the apiserver. Then, edit the API server pod specification file $apiserverconf on the control plane node and set the TLS certificate and private key file parameters. --tls-cert-file= --tls-private-key-file= scored: true - id: 1.2.26 text: "Ensure that the --client-ca-file argument is set as appropriate (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--client-ca-file" remediation: | Follow the Kubernetes documentation and set up the TLS connection on the apiserver. Then, edit the API server pod specification file $apiserverconf on the control plane node and set the client certificate authority file. --client-ca-file= scored: true - id: 1.2.27 text: "Ensure that the --etcd-cafile argument is set as appropriate (Automated)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--etcd-cafile" remediation: | Follow the Kubernetes documentation and set up the TLS connection between the apiserver and etcd. Then, edit the API server pod specification file $apiserverconf on the control plane node and set the etcd certificate authority file parameter. --etcd-cafile= scored: true - id: 1.2.28 text: "Ensure that the --encryption-provider-config argument is set as appropriate (Manual)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--encryption-provider-config" remediation: | Follow the Kubernetes documentation and configure a EncryptionConfig file. Then, edit the API server pod specification file $apiserverconf on the control plane node and set the --encryption-provider-config parameter to the path of that file. For example, --encryption-provider-config= Permissive - Enabling encryption changes how data can be recovered as data is encrypted. scored: false - id: 1.2.29 text: "Ensure that encryption providers are appropriately configured (Manual)" audit: | ENCRYPTION_PROVIDER_CONFIG=$(ps -ef | grep $apiserverbin | grep -- --encryption-provider-config | sed 's%.*encryption-provider-config[= ]\([^ ]*\).*%\1%') if test -e $ENCRYPTION_PROVIDER_CONFIG; then grep -A1 'providers:' $ENCRYPTION_PROVIDER_CONFIG | tail -n1 | grep -o "[A-Za-z]*" | sed 's/^/provider=/'; fi tests: test_items: - flag: "provider" compare: op: valid_elements value: "aescbc,kms,secretbox" remediation: | Follow the Kubernetes documentation and configure a EncryptionConfig file. In this file, choose aescbc, kms or secretbox as the encryption provider. Permissive - Enabling encryption changes how data can be recovered as data is encrypted. scored: false - id: 1.2.30 text: "Ensure that the API Server only makes use of Strong Cryptographic Ciphers (Manual)" audit: "/bin/ps -ef | grep $apiserverbin | grep -v grep" tests: test_items: - flag: "--tls-cipher-suites" compare: op: valid_elements value: "TLS_AES_128_GCM_SHA256,TLS_AES_256_GCM_SHA384,TLS_CHACHA20_POLY1305_SHA256,TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA,TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,TLS_RSA_WITH_3DES_EDE_CBC_SHA,TLS_RSA_WITH_AES_128_CBC_SHA,TLS_RSA_WITH_AES_128_GCM_SHA256,TLS_RSA_WITH_AES_256_CBC_SHA,TLS_RSA_WITH_AES_256_GCM_SHA384" remediation: | Edit the API server pod specification file /etc/kubernetes/manifests/kube-apiserver.yaml on the control plane node and set the below parameter. --tls-cipher-suites=TLS_AES_128_GCM_SHA256,TLS_AES_256_GCM_SHA384,TLS_CHACHA20_POLY1305_SHA256, TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA,TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305, TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,TLS_RSA_WITH_3DES_EDE_CBC_SHA,TLS_RSA_WITH_AES_128_CBC_SHA, TLS_RSA_WITH_AES_128_GCM_SHA256,TLS_RSA_WITH_AES_256_CBC_SHA,TLS_RSA_WITH_AES_256_GCM_SHA384 scored: false - id: 1.3 text: "Controller Manager" checks: - id: 1.3.1 text: "Ensure that the --terminated-pod-gc-threshold argument is set as appropriate (Automated)" audit: "/bin/ps -ef | grep $controllermanagerbin | grep -v grep" tests: test_items: - flag: "--terminated-pod-gc-threshold" remediation: | Edit the Controller Manager pod specification file $controllermanagerconf on the control plane node and set the --terminated-pod-gc-threshold to an appropriate threshold, for example, --terminated-pod-gc-threshold=10 scored: true - id: 1.3.2 text: "Ensure that the --profiling argument is set to false (Automated)" audit: "/bin/ps -ef | grep $controllermanagerbin | grep -v grep" tests: test_items: - flag: "--profiling" compare: op: eq value: false remediation: | Edit the Controller Manager pod specification file $controllermanagerconf on the control plane node and set the below parameter. --profiling=false scored: true - id: 1.3.3 text: "Ensure that the --use-service-account-credentials argument is set to true (Automated)" audit: "/bin/ps -ef | grep $controllermanagerbin | grep -v grep" tests: test_items: - flag: "--use-service-account-credentials" compare: op: noteq value: false remediation: | Edit the Controller Manager pod specification file $controllermanagerconf on the control plane node to set the below parameter. --use-service-account-credentials=true scored: true - id: 1.3.4 text: "Ensure that the --service-account-private-key-file argument is set as appropriate (Automated)" audit: "/bin/ps -ef | grep $controllermanagerbin | grep -v grep" tests: test_items: - flag: "--service-account-private-key-file" remediation: | Edit the Controller Manager pod specification file $controllermanagerconf on the control plane node and set the --service-account-private-key-file parameter to the private key file for service accounts. --service-account-private-key-file= scored: true - id: 1.3.5 text: "Ensure that the --root-ca-file argument is set as appropriate (Automated)" audit: "/bin/ps -ef | grep $controllermanagerbin | grep -v grep" tests: test_items: - flag: "--root-ca-file" remediation: | Edit the Controller Manager pod specification file $controllermanagerconf on the control plane node and set the --root-ca-file parameter to the certificate bundle file`. --root-ca-file= scored: true - id: 1.3.6 text: "Ensure that the RotateKubeletServerCertificate argument is set to true (Automated)" audit: "/bin/ps -ef | grep $controllermanagerbin | grep -v grep" tests: bin_op: or test_items: - flag: "--feature-gates" compare: op: nothave value: "RotateKubeletServerCertificate=false" set: true - flag: "--feature-gates" set: false remediation: | Edit the Controller Manager pod specification file $controllermanagerconf on the control plane node and set the --feature-gates parameter to include RotateKubeletServerCertificate=true. --feature-gates=RotateKubeletServerCertificate=true Cluster provisioned by RKE handles certificate rotation directly through RKE. scored: true - id: 1.3.7 text: "Ensure that the --bind-address argument is set to 127.0.0.1 (Automated)" audit: "/bin/ps -ef | grep $controllermanagerbin | grep -v grep" tests: bin_op: or test_items: - flag: "--bind-address" compare: op: eq value: "127.0.0.1" - flag: "--bind-address" set: false remediation: | Edit the Controller Manager pod specification file $controllermanagerconf on the control plane node and ensure the correct value for the --bind-address parameter scored: true - id: 1.4 text: "Scheduler" checks: - id: 1.4.1 text: "Ensure that the --profiling argument is set to false (Automated)" audit: "/bin/ps -ef | grep $schedulerbin | grep -v grep" tests: test_items: - flag: "--profiling" compare: op: eq value: false remediation: | Edit the Scheduler pod specification file $schedulerconf file on the control plane node and set the below parameter. --profiling=false scored: true - id: 1.4.2 text: "Ensure that the --bind-address argument is set to 127.0.0.1 (Automated)" audit: "/bin/ps -ef | grep $schedulerbin | grep -v grep" tests: bin_op: or test_items: - flag: "--bind-address" compare: op: eq value: "127.0.0.1" - flag: "--bind-address" set: false remediation: | Edit the Scheduler pod specification file $schedulerconf on the control plane node and ensure the correct value for the --bind-address parameter scored: true ================================================ FILE: cfg/rke2-cis-1.8/node.yaml ================================================ --- controls: version: "rke2-cis-1.8" id: 4 text: "Worker Node Security Configuration" type: "node" groups: - id: 4.1 text: "Worker Node Configuration Files" checks: - id: 4.1.1 text: "Ensure that the kubelet service file permissions are set to 600 or more restrictive (Automated)" audit: '/bin/sh -c "if test -e $kubeletsvc; then stat -c permissions=%a $kubeletsvc; else echo \"File not found\"; fi"' tests: bin_op: or test_items: - flag: "permissions" compare: op: bitmask value: "600" - flag: "File not found" remediation: | Run the below command (based on the file location on your system) on the each worker node. For example, chmod 600 $kubeletsvc Not Applicable - Clusters provisioned by RKE doesn't require or maintain a configuration file for the kubelet service. All configuration is passed in as arguments at container run time. scored: true - id: 4.1.2 text: "Ensure that the kubelet service file ownership is set to root:root (Automated)" audit: '/bin/sh -c "if test -e $kubeletsvc; then stat -c ownership=%U:%G $kubeletsvc; else echo \"File not found\"; fi"' tests: bin_op: or test_items: - flag: "ownership" compare: op: eq value: "root:root" - flag: "File not found" remediation: | Run the below command (based on the file location on your system) on the each worker node. For example, chown root:root $kubeletsvc Not Applicable - Clusters provisioned by RKE does not require or maintain a configuration file for the kubelet service. All configuration is passed in as arguments at container run time. scored: true - id: 4.1.3 text: "If proxy kubeconfig file exists ensure permissions are set to 600 or more restrictive (Automated)" audit: '/bin/sh -c ''if test -e $proxykubeconfig; then stat -c permissions=%a $proxykubeconfig; fi'' ' tests: bin_op: or test_items: - flag: "permissions" set: true compare: op: bitmask value: "600" remediation: | Run the below command (based on the file location on your system) on the each worker node. For example, chmod 600 $proxykubeconfig scored: true - id: 4.1.4 text: "If proxy kubeconfig file exists ensure ownership is set to root:root (Automated)" audit: '/bin/sh -c ''if test -e $proxykubeconfig; then stat -c %U:%G $proxykubeconfig; fi'' ' tests: bin_op: or test_items: - flag: root:root remediation: | Run the below command (based on the file location on your system) on the each worker node. For example, chown root:root $proxykubeconfig scored: true - id: 4.1.5 text: "Ensure that the --kubeconfig kubelet.conf file permissions are set to 600 or more restrictive (Automated)" audit: '/bin/sh -c ''if test -e $kubeletkubeconfig; then stat -c permissions=%a $kubeletkubeconfig; fi'' ' tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Run the below command (based on the file location on your system) on the each worker node. For example, chmod 600 $kubeletkubeconfig scored: true - id: 4.1.6 text: "Ensure that the --kubeconfig kubelet.conf file ownership is set to root:root (Automated)" audit: '/bin/sh -c ''if test -e $kubeletkubeconfig; then stat -c %U:%G $kubeletkubeconfig; fi'' ' tests: test_items: - flag: root:root remediation: | Run the below command (based on the file location on your system) on the each worker node. For example, chown root:root $kubeletkubeconfig scored: true - id: 4.1.7 text: "Ensure that the certificate authorities file permissions are set to 600 or more restrictive (Automated)" audit: | if [ -f /var/lib/rancher/rke2/agent/client-ca.crt ]; then stat -c permissions=%a /var/lib/rancher/rke2/agent/client-ca.crt else echo "NA" fi tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" remediation: | Run the following command to modify the file permissions of the --client-ca-file chmod 600 scored: true - id: 4.1.8 text: "Ensure that the client certificate authorities file ownership is set to root:root (Automated)" audit: | if [ -f /var/lib/rancher/rke2/agent/client-ca.crt ]; then stat -c %U:%G /var/lib/rancher/rke2/agent/client-ca.crt else echo "NA" fi tests: test_items: - flag: root:root compare: op: eq value: root:root remediation: | Run the following command to modify the ownership of the --client-ca-file. chown root:root scored: true - id: 4.1.9 text: "If the kubelet config.yaml configuration file is being used validate permissions set to 600 or more restrictive (Automated)" type: skip audit: | /bin/sh -c 'if test -e "$kubeletconf"; then stat -c "permissions=%a" "$kubeletconf" else echo "File not found" fi' tests: bin_op: or test_items: - flag: "permissions" compare: op: bitmask value: "600" - flag: "File not found" remediation: | Run the following command (using the config file location identified in the Audit step) chmod 600 $kubeletconf Not Applicable - Clusters provisioned by RKE do not require or maintain a configuration file for the kubelet. All configuration is passed in as arguments at container run time. scored: false - id: 4.1.10 text: "If the kubelet config.yaml configuration file is being used validate file ownership is set to root:root (Manual)" type: skip audit: | /bin/sh -c 'if test -e "$kubeletconf"; then stat -c "%U:%G" "$kubeletconf" else echo "File not found" fi' tests: bin_op: or test_items: - flag: root:root - flag: "File not found" remediation: | Run the following command (using the config file location identified in the Audit step) chown root:root $kubeletconf Not Applicable - Clusters provisioned by RKE does not require or maintain a configuration file for the kubelet. All configuration is passed in as arguments at container run time. scored: false - id: 4.2 text: "Kubelet" checks: - id: 4.2.1 text: "Ensure that the --anonymous-auth argument is set to false (Automated)" audit: "/bin/ps -fC $kubeletbin" audit_config: /bin/sh -c 'if test -d $kubeletconf; then cat $kubeletconf/*; elif test -e $kubeletconf; then cat $kubeletconf; fi' tests: test_items: - flag: "anonymous-auth" path: '{.authentication.anonymous.enabled}' compare: op: eq value: false remediation: | If using a Kubelet config file, edit the file to set `authentication: anonymous: enabled` to `false`. If using executable arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_SYSTEM_PODS_ARGS variable. `--anonymous-auth=false` Based on your system, restart the kubelet service. For example, systemctl daemon-reload systemctl restart kubelet.service scored: true - id: 4.2.2 text: "Ensure that the --authorization-mode argument is not set to AlwaysAllow (Automated)" audit: "/bin/ps -fC $kubeletbin" audit_config: /bin/sh -c 'if test -d $kubeletconf; then cat $kubeletconf/*; elif test -e $kubeletconf; then cat $kubeletconf; fi' tests: test_items: - flag: authorization-mode path: '{.authorization.mode}' compare: op: nothave value: AlwaysAllow remediation: | If using a Kubelet config file, edit the file to set `authorization.mode` to Webhook. If using executable arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_AUTHZ_ARGS variable. --authorization-mode=Webhook Based on your system, restart the kubelet service. For example, systemctl daemon-reload systemctl restart kubelet.service scored: true - id: 4.2.3 text: "Ensure that the --client-ca-file argument is set as appropriate (Automated)" audit: "/bin/ps -fC $kubeletbin" audit_config: /bin/sh -c 'if test -d $kubeletconf; then cat $kubeletconf/*; elif test -e $kubeletconf; then cat $kubeletconf; fi' tests: test_items: - flag: clientCAFile path: "{.authentication.x509.clientCAFile}" compare: op: eq value: "/var/lib/rancher/rke2/agent/client-ca.crt" remediation: | If using a Kubelet config file, edit the file to set `authentication.x509.clientCAFile` to the location of the client CA file. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_AUTHZ_ARGS variable. --client-ca-file= Based on your system, restart the kubelet service. For example, systemctl daemon-reload systemctl restart kubelet.service scored: true - id: 4.2.4 text: "Verify that the --read-only-port argument is set to 0 (Automated)" audit: "/bin/ps -fC $kubeletbin" audit_config: /bin/sh -c 'if test -d $kubeletconf; then cat $kubeletconf/*; elif test -e $kubeletconf; then cat $kubeletconf; fi' tests: bin_op: or test_items: - flag: "--read-only-port" path: '{.readOnlyPort}' compare: op: eq value: 0 - flag: "--read-only-port" path: '{.readOnlyPort}' set: false remediation: | If using a Kubelet config file, edit the file to set `readOnlyPort` to 0. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_SYSTEM_PODS_ARGS variable. --read-only-port=0 Based on your system, restart the kubelet service. For example, systemctl daemon-reload systemctl restart kubelet.service scored: true - id: 4.2.5 text: "Ensure that the --streaming-connection-idle-timeout argument is not set to 0 (Automated)" audit: "/bin/ps -fC $kubeletbin" audit_config: /bin/sh -c 'if test -d $kubeletconf; then cat $kubeletconf/*; elif test -e $kubeletconf; then cat $kubeletconf; fi' tests: test_items: - flag: --streaming-connection-idle-timeout path: '{.streamingConnectionIdleTimeout}' compare: op: noteq value: 0 - flag: --streaming-connection-idle-timeout path: '{.streamingConnectionIdleTimeout}' set: false bin_op: or remediation: | If using a Kubelet config file, edit the file to set `streamingConnectionIdleTimeout` to a value other than 0. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_SYSTEM_PODS_ARGS variable. --streaming-connection-idle-timeout=5m Based on your system, restart the kubelet service. For example, systemctl daemon-reload systemctl restart kubelet.service scored: false - id: 4.2.6 text: "Ensure that the --make-iptables-util-chains argument is set to true (Automated)" audit: "/bin/ps -fC $kubeletbin" audit_config: /bin/sh -c 'if test -d $kubeletconf; then cat $kubeletconf/*; elif test -e $kubeletconf; then cat $kubeletconf; fi' tests: test_items: - flag: --make-iptables-util-chains path: '{.makeIPTablesUtilChains}' compare: op: eq value: true - flag: --make-iptables-util-chains path: '{.makeIPTablesUtilChains}' set: false bin_op: or remediation: | If using a Kubelet config file, edit the file to set `makeIPTablesUtilChains` to `true`. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and remove the --make-iptables-util-chains argument from the KUBELET_SYSTEM_PODS_ARGS variable. Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: true - id: 4.2.7 text: "Ensure that the --hostname-override argument is not set (Manual)" audit: "/bin/ps -fC $kubeletbin " audit_config: /bin/sh -c 'if test -d $kubeletconf; then cat $kubeletconf/*; elif test -e $kubeletconf; then cat $kubeletconf; fi' tests: test_items: - flag: --hostname-override set: false remediation: | Edit the kubelet service file $kubeletsvc on each worker node and remove the --hostname-override argument from the KUBELET_SYSTEM_PODS_ARGS variable. Based on your system, restart the kubelet service. For example, systemctl daemon-reload systemctl restart kubelet.service Not Applicable - Clusters provisioned by RKE set the --hostname-override to avoid any hostname configuration errors scored: false - id: 4.2.8 text: "Ensure that the eventRecordQPS argument is set to a level which ensures appropriate event capture (Manual)" audit: "/bin/ps -fC $kubeletbin" audit_config: /bin/sh -c 'if test -d $kubeletconf; then cat $kubeletconf/*; elif test -e $kubeletconf; then cat $kubeletconf; fi' tests: test_items: - flag: --event-qps path: '{.eventRecordQPS}' compare: op: gte value: 0 - flag: --event-qps path: '{.eventRecordQPS}' set: false bin_op: or remediation: | If using a Kubelet config file, edit the file to set `eventRecordQPS` to an appropriate level. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_SYSTEM_PODS_ARGS variable. Based on your system, restart the kubelet service. For example, systemctl daemon-reload systemctl restart kubelet.service scored: false - id: 4.2.9 text: "Ensure that the --tls-cert-file and --tls-private-key-file arguments are set as appropriate (Manual)" audit: "/bin/ps -fC $kubeletbin" audit_config: /bin/sh -c 'if test -d $kubeletconf; then cat $kubeletconf/*; elif test -e $kubeletconf; then cat $kubeletconf; fi' tests: test_items: - flag: --tls-cert-file path: '{.tlsCertFile}' - flag: --tls-private-key-file path: '{.tlsPrivateKeyFile}' remediation: | If using a Kubelet config file, edit the file to set `tlsCertFile` to the location of the certificate file to use to identify this Kubelet, and `tlsPrivateKeyFile` to the location of the corresponding private key file. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameters in KUBELET_CERTIFICATE_ARGS variable. --tls-cert-file= --tls-private-key-file= Based on your system, restart the kubelet service. For example, systemctl daemon-reload systemctl restart kubelet.service Permissive - When generating serving certificates, functionality could break in conjunction with hostname overrides which are required for certain cloud providers. scored: false - id: 4.2.10 text: "Ensure that the --rotate-certificates argument is not set to false (Automated)" audit: "/bin/ps -fC $kubeletbin" audit_config: "/bin/sh -c 'if test -d $kubeletconf; then cat $kubeletconf/*; elif test -f $kubeletconf; then cat $kubeletconf; fi'" tests: bin_op: or test_items: - flag: "--rotate-certificates=false" set: false - path: "{.rotateCertificates}" compare: op: eq value: true - path: "{.rotateCertificates}" set: false remediation: | If using a Kubelet config file, edit the file to add the line `rotateCertificates` to `true` or remove it altogether to use the default value. If using command line arguments, edit the kubelet service file $kubeletsvc on each worker node and remove --rotate-certificates=false argument from the KUBELET_CERTIFICATE_ARGS variable. Based on your system, restart the kubelet service. For example, systemctl daemon-reload systemctl restart kubelet.service scored: true - id: 4.2.11 text: "Verify that the RotateKubeletServerCertificate argument is set to true (Manual)" audit: "/bin/ps -fC $kubeletbin" audit_config: /bin/sh -c 'if test -d $kubeletconf; then cat $kubeletconf/*; elif test -e $kubeletconf; then cat $kubeletconf; fi' tests: bin_op: or test_items: - flag: RotateKubeletServerCertificate path: '{.featureGates.RotateKubeletServerCertificate}' compare: op: nothave value: false - flag: RotateKubeletServerCertificate path: '{.featureGates.RotateKubeletServerCertificate}' set: false remediation: | Edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_CERTIFICATE_ARGS variable. --feature-gates=RotateKubeletServerCertificate=true Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service Not Applicable - Clusters provisioned by RKE handles certificate rotation directly through RKE. scored: false - id: 4.2.12 text: "Ensure that the Kubelet only makes use of Strong Cryptographic Ciphers (Automated)" audit: "/bin/ps -fC $kubeletbin" audit_config: /bin/sh -c 'if test -d $kubeletconf; then cat $kubeletconf/*; elif test -e $kubeletconf; then cat $kubeletconf; fi' tests: test_items: - flag: --tls-cipher-suites path: '{range .tlsCipherSuites[:]}{}{'',''}{end}' compare: op: valid_elements value: 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 remediation: | If using a Kubelet config file, edit the file to set `TLSCipherSuites` to 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 or to a subset of these values. If using executable arguments, edit the kubelet service file $kubeletsvc on each worker node and set the --tls-cipher-suites parameter as follows, or to a subset of these values. --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 Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: true - id: 4.2.13 text: "Ensure that a limit is set on pod PIDs (Manual)" audit: "/bin/ps -fC $kubeletbin" audit_config: /bin/sh -c 'if test -d $kubeletconf; then cat $kubeletconf/*; elif test -e $kubeletconf; then cat $kubeletconf; fi' tests: test_items: - flag: --pod-max-pids path: '{.podPidsLimit}' remediation: | Decide on an appropriate level for this parameter and set it, either via the --pod-max-pids command line parameter or the PodPidsLimit configuration file setting. scored: false ================================================ FILE: cfg/rke2-cis-1.8/policies.yaml ================================================ --- controls: version: "rke2-cis-1.8" id: 5 text: "Kubernetes Policies" type: "policies" groups: - id: 5.1 text: "RBAC and Service Accounts" checks: - id: 5.1.1 text: "Ensure that the cluster-admin role is only used where required (Manual)" type: "manual" remediation: | Identify all clusterrolebindings to the cluster-admin role. Check if they are used and if they need this role or if they could use a role with fewer privileges. Where possible, first bind users to a lower privileged role and then remove the clusterrolebinding to the cluster-admin role : kubectl delete clusterrolebinding [name] scored: false - id: 5.1.2 text: "Minimize access to secrets (Manual)" type: "manual" remediation: | Where possible, remove get, list and watch access to Secret objects in the cluster. scored: false - id: 5.1.3 text: "Minimize wildcard use in Roles and ClusterRoles (Manual)" type: "manual" remediation: | Where possible replace any use of wildcards in clusterroles and roles with specific objects or actions. scored: false - id: 5.1.4 text: "Minimize access to create pods (Manual)" type: "manual" remediation: | Where possible, remove create access to pod objects in the cluster. scored: false - id: 5.1.5 text: "Ensure that default service accounts are not actively used. (Manual)" type: "manual" audit: check_for_default_sa.sh remediation: | Create explicit service accounts wherever a Kubernetes workload requires specific access to the Kubernetes API server. Modify the configuration of each default service account to include this value automountServiceAccountToken: false Permissive - Kubernetes provides default service accounts to be used. scored: false - id: 5.1.6 text: "Ensure that Service Account Tokens are only mounted where necessary (Manual)" type: "manual" remediation: | Modify the definition of pods and service accounts which do not need to mount service account tokens to disable it. scored: false - id: 5.1.7 text: "Avoid use of system:masters group (Manual)" type: "manual" remediation: | Remove the system:masters group from all users in the cluster. scored: false - id: 5.1.8 text: "Limit use of the Bind, Impersonate and Escalate permissions in the Kubernetes cluster (Manual)" type: "manual" remediation: | Where possible, remove the impersonate, bind and escalate rights from subjects. scored: false - id: 5.1.9 text: "Minimize access to create persistent volumes (Manual)" type: "manual" remediation: | Where possible, remove create access to PersistentVolume objects in the cluster. scored: false - id: 5.1.10 text: "Minimize access to the proxy sub-resource of nodes (Manual)" type: "manual" remediation: | Where possible, remove access to the proxy sub-resource of node objects. scored: false - id: 5.1.11 text: "Minimize access to the approval sub-resource of certificatesigningrequests objects (Manual)" type: "manual" remediation: | Where possible, remove access to the approval sub-resource of certificatesigningrequest objects. scored: false - id: 5.1.12 text: "Minimize access to webhook configuration objects (Manual)" type: "manual" remediation: | Where possible, remove access to the validatingwebhookconfigurations or mutatingwebhookconfigurations objects scored: false - id: 5.1.13 text: "Minimize access to the service account token creation (Manual)" type: "manual" remediation: | Where possible, remove access to the token sub-resource of serviceaccount objects. scored: false - id: 5.2 text: "Pod Security Standards" checks: - id: 5.2.1 text: "Ensure that the cluster has at least one active policy control mechanism in place (Manual)" type: "manual" remediation: | Ensure that either Pod Security Admission or an external policy control system is in place for every namespace which contains user workloads. scored: false - id: 5.2.2 text: "Minimize the admission of privileged containers (Manual)" type: "manual" remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of privileged containers. scored: false - id: 5.2.3 text: "Minimize the admission of containers wishing to share the host process ID namespace (Manual)" type: "manual" remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of `hostPID` containers. Permissive - Enabling Pod Security Policy can cause applications to unexpectedly fail. scored: true - id: 5.2.4 text: "Minimize the admission of containers wishing to share the host IPC namespace (Manual)" type: "manual" remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of `hostIPC` containers. Permissive - Enabling Pod Security Policy can cause applications to unexpectedly fail. scored: true - id: 5.2.5 text: "Minimize the admission of containers wishing to share the host network namespace (Manual)" type: "manual" remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of `hostNetwork` containers. Permissive - Enabling Pod Security Policy can cause applications to unexpectedly fail. scored: true - id: 5.2.6 text: "Minimize the admission of containers with allowPrivilegeEscalation (Manual)" type: "manual" remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of containers with `.spec.allowPrivilegeEscalation` set to `true`. scored: false - id: 5.2.7 text: "Minimize the admission of root containers (Manual)" type: "manual" remediation: | Create a policy for each namespace in the cluster, ensuring that either `MustRunAsNonRoot` or `MustRunAs` with the range of UIDs not including 0, is set. scored: false - id: 5.2.8 text: "Minimize the admission of containers with the NET_RAW capability (Manual)" type: "manual" remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of containers with the `NET_RAW` capability. scored: false - id: 5.2.9 text: "Minimize the admission of containers with added capabilities (Manual)" type: "manual" remediation: | Ensure that `allowedCapabilities` is not present in policies for the cluster unless it is set to an empty array. scored: false - id: 5.2.10 text: "Minimize the admission of containers with capabilities assigned (Manual)" type: "manual" remediation: | Review the use of capabilites in applications running on your cluster. Where a namespace contains applicaions which do not require any Linux capabities to operate consider adding a PSP which forbids the admission of containers which do not drop all capabilities. scored: false - id: 5.2.11 text: "Minimize the admission of Windows HostProcess containers (Manual)" type: "manual" remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of containers that have `.securityContext.windowsOptions.hostProcess` set to `true`. scored: false - id: 5.2.12 text: "Minimize the admission of HostPath volumes (Manual)" type: "manual" remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of containers with `hostPath` volumes. scored: false - id: 5.2.13 text: "Minimize the admission of containers which use HostPorts (Manual)" type: "manual" remediation: | Add policies to each namespace in the cluster which has user workloads to restrict the admission of containers which use `hostPort` sections. scored: false - id: 5.3 text: "Network Policies and CNI" checks: - id: 5.3.1 text: "Ensure that the CNI in use supports NetworkPolicies (Manual)" type: "manual" remediation: | If the CNI plugin in use does not support network policies, consideration should be given to making use of a different plugin, or finding an alternate mechanism for restricting traffic in the Kubernetes cluster. scored: false - id: 5.3.2 text: "Ensure that all Namespaces have NetworkPolicies defined (Manual)" type: "manual" remediation: | Follow the documentation and create NetworkPolicy objects as you need them. Permissive - Enabling Network Policies can prevent certain applications from communicating with each other. scored: false - id: 5.4 text: "Secrets Management" checks: - id: 5.4.1 text: "Prefer using Secrets as files over Secrets as environment variables (Manual)" type: "manual" remediation: | If possible, rewrite application code to read Secrets from mounted secret files, rather than from environment variables. scored: false - id: 5.4.2 text: "Consider external secret storage (Manual)" type: "manual" remediation: | Refer to the Secrets management options offered by your cloud provider or a third-party secrets management solution. scored: false - id: 5.5 text: "Extensible Admission Control" checks: - id: 5.5.1 text: "Configure Image Provenance using ImagePolicyWebhook admission controller (Manual)" type: "manual" remediation: | Follow the Kubernetes documentation and setup image provenance. scored: false - id: 5.7 text: "General Policies" checks: - id: 5.7.1 text: "Create administrative boundaries between resources using namespaces (Manual)" type: "manual" remediation: | Follow the documentation and create namespaces for objects in your deployment as you need them. scored: false - id: 5.7.2 text: "Ensure that the seccomp profile is set to docker/default in your Pod definitions (Manual)" type: "manual" remediation: | Use `securityContext` to enable the docker/default seccomp profile in your pod definitions. An example is as below: securityContext: seccompProfile: type: RuntimeDefault scored: false - id: 5.7.3 text: "Apply SecurityContext to your Pods and Containers (Manual)" type: "manual" remediation: | Follow the Kubernetes documentation and apply SecurityContexts to your Pods. For a suggested list of SecurityContexts, you may refer to the CIS Security Benchmark for Docker Containers. scored: false - id: 5.7.4 text: "The default namespace should not be used (Manual)" type: "manual" remediation: | Ensure that namespaces are created to allow for appropriate segregation of Kubernetes resources and that all new resources are created in a specific namespace. Permissive - Kubernetes provides a default namespace. scored: false ================================================ FILE: cfg/tkgi-1.2.53/config.yaml ================================================ --- ## Version-specific settings that override the values in cfg/config.yaml ================================================ FILE: cfg/tkgi-1.2.53/controlplane.yaml ================================================ --- controls: version: "tkgi-1.2.53" id: 3 text: "Control Plane Configuration" type: "controlplane" groups: - id: 3.1 text: "Authentication and Authorization" checks: - id: 3.1.1 text: "Client certificate authentication should not be used for users" audit: ps -ef | grep kube-apiserver | grep -- "--oidc-issuer-url=" type: "manual" remediation: | Alternative mechanisms provided by Kubernetes such as the use of OIDC should be implemented in place of client certificates. Exception This setting is site-specific. It can be set in the "Configure created clusters to use UAA as the OIDC provider." section of the "UAA" scored: false - id: 3.2 text: "Logging" checks: - id: 3.2.1 text: "Ensure that a minimal audit policy is created" audit: ps -ef | grep kube-apiserver | grep -v tini | grep -- "--audit-policy-file=" tests: test_items: - flag: "--audit-policy-file" remediation: | Create an audit policy file for your cluster. scored: true - id: 3.2.2 text: "Ensure that the audit policy covers key security concerns" audit: | diff /var/vcap/jobs/kube-apiserver/config/audit_policy.yml \ <(echo "--- apiVersion: audit.k8s.io/v1beta1 kind: Policy rules: - level: None resources: - group: '' resources: - endpoints - services - services/status users: - system:kube-proxy verbs: - watch - level: None resources: - group: '' resources: - nodes - nodes/status users: - kubelet verbs: - get - level: None resources: - group: '' resources: - nodes - nodes/status userGroups: - system:nodes verbs: - get - level: None namespaces: - kube-system resources: - group: '' resources: - endpoints users: - system:kube-controller-manager - system:kube-scheduler - system:serviceaccount:kube- system:endpoint-controller verbs: - get - update - level: None resources: - group: '' resources: - namespaces - namespaces/status - namespaces/finalize users: - system:apiserver verbs: - get - level: None resources: - group: metrics.k8s.io users: - system:kube-controller-manager verbs: - get - list - level: None nonResourceURLs: - \"/healthz*\" - \"/version\" - \"/swagger*\" - level: None resources: - group: '' resources: - events - level: Request omitStages: - RequestReceived resources: - group: '' resources: - nodes/status - pods/status userGroups: - system:nodes verbs: - update - patch - level: Request omitStages: - RequestReceived users: - system:serviceaccount:kube-system:namespace-controller verbs: - deletecollection - level: Metadata omitStages: - RequestReceived resources: - group: '' resources: - secrets - configmaps - group: authentication.k8s.io resources: - tokenreviews - level: Request omitStages: - RequestReceived resources: - group: '' - group: admissionregistration.k8s.io - group: apiextensions.k8s.io - group: apiregistration.k8s.io - group: apps - group: authentication.k8s.io - group: authorization.k8s.io - group: autoscaling - group: batch - group: certificates.k8s.io - group: extensions - group: metrics.k8s.io - group: networking.k8s.io - group: policy - group: rbac.authorization.k8s.io - group: settings.k8s.io - group: storage.k8s.io verbs: - get - list - watch - level: RequestResponse omitStages: - RequestReceived resources: - group: '' - group: admissionregistration.k8s.io - group: apiextensions.k8s.io - group: apiregistration.k8s.io - group: apps - group: authentication.k8s.io - group: authorization.k8s.io - group: autoscaling - group: batch - group: certificates.k8s.io - group: extensions - group: metrics.k8s.io - group: networking.k8s.io - group: policy - group: rbac.authorization.k8s.io - group: settings.k8s.io - group: storage.k8s.io - level: Metadata omitStages: - RequestReceived ") type: "manual" remediation: | Consider modification of the audit policy in use on the cluster to include these items, at a minimum. scored: false ================================================ FILE: cfg/tkgi-1.2.53/etcd.yaml ================================================ --- controls: version: "tkgi-1.2.53" id: 2 text: "Etcd Node Configuration" type: "etcd" groups: - id: 2 text: "Etcd Node Configuration Files" checks: - id: 2.1 text: "Ensure that the --cert-file and --key-file arguments are set as appropriate" audit: ps -ef | grep etcd | grep -- "--cert-file=/var/vcap/jobs/etcd/config/etcd.crt" | grep -- "--key-file=/var/vcap/jobs/etcd/config/etcd.key" type: manual tests: bin_op: and test_items: - flag: "--cert-file" - flag: "--key-file" remediation: | Follow the etcd service documentation and configure TLS encryption. Then, edit the etcd pod specification file /etc/kubernetes/manifests/etcd.yaml on the master node and set the below parameters. --cert-file= --key-file= scored: false - id: 2.2 text: "Ensure that the --client-cert-auth argument is set to true" audit: ps -ef | grep etcd | grep -- "--client\-cert\-auth" tests: test_items: - flag: "--client-cert-auth" compare: op: eq value: true remediation: | Edit the etcd pod specification file etcd config on the master node and set the below parameter. --client-cert-auth="true" scored: true - id: 2.3 text: "Ensure that the --auto-tls argument is not set to true" audit: ps -ef | grep etcd | grep -v -- "--auto-tls" tests: test_items: - flag: "--auto-tls" compare: op: eq value: true set: false remediation: | Edit the etcd pod specification file etcd config on the master node and either remove the --auto-tls parameter or set it to false. --auto-tls=false scored: true - id: 2.4 text: "Ensure that the --peer-cert-file and --peer-key-file arguments are set as appropriate" audit: ps -ef | grep etcd | grep -- "--peer-cert-file=/var/vcap/jobs/etcd/config/peer.crt" | grep -- "--peer-key-file=/var/vcap/jobs/etcd/config/peer.key" type: manual tests: bin_op: and test_items: - flag: "--peer-cert-file" - flag: "--peer-key-file" remediation: | Follow the etcd service documentation and configure peer TLS encryption as appropriate for your etcd cluster. Then, edit the etcd pod specification file etcd config on the master node and set the below parameters. --peer-client-file= --peer-key-file= scored: false - id: 2.5 text: "Ensure that the --peer-client-cert-auth argument is set to true" audit: ps -ef | grep etcd | grep -- "--peer\-client\-cert\-auth" tests: test_items: - flag: "--peer-client-cert-auth" compare: op: eq value: true remediation: | Edit the etcd pod specification file etcd config on the master node and set the below parameter. --peer-client-cert-auth=true scored: true - id: 2.6 text: "Ensure that the --peer-auto-tls argument is not set to true" audit: ps -ef | grep etcd | grep -v -- "--peer-auto-tls" tests: test_items: - flag: "--peer-auto-tls" compare: op: eq value: true set: false remediation: | Edit the etcd pod specification file etcd config on the master node and either remove the --peer-auto-tls parameter or set it to false. --peer-auto-tls=false scored: true - id: 2.7 text: "Ensure that a unique Certificate Authority is used for etcd" audit: diff /var/vcap/jobs/kube-apiserver/config/kubernetes-ca.pem /var/vcap/jobs/etcd/config/etcd-ca.crt | grep -c"^>" | grep -v "^0$" type: manual tests: test_items: - flag: "--trusted-ca-file" remediation: | Follow the etcd documentation and create a dedicated certificate authority setup for the etcd service. Then, edit the etcd pod specification file etcd config on the master node and set the below parameter. --trusted-ca-file= scored: false ================================================ FILE: cfg/tkgi-1.2.53/master.yaml ================================================ --- controls: version: "tkgi-1.2.53" id: 1 text: "Master Node Security Configuration" type: "master" groups: - id: 1.1 text: "Master Node Configuration Files" checks: - id: 1.1.1 text: "Ensure that the API server pod specification file permissions are set to 644 or more restrictive" audit: stat -c permissions=%a /var/vcap/jobs/kube-apiserver/config/bpm.yml tests: test_items: - flag: "permissions" compare: op: bitmask value: "644" remediation: | Run the below command (based on the file location on your system) on the master node. For example, chmod 644 /var/vcap/jobs/kube-apiserver/config/bpm.yml scored: true - id: 1.1.2 text: "Ensure that the API server pod specification file ownership is set to root:root" audit: stat -c %U:%G /var/vcap/jobs/kube-apiserver/config/bpm.yml tests: test_items: - flag: "root:root" remediation: | Run the below command (based on the file location on your system) on the master node. For example, chown root:root /var/vcap/jobs/kube-apiserver/config/bpm.yml scored: true - id: 1.1.3 text: "Ensure that the controller manager pod specification file permissions are set to 644 or more restrictive" audit: stat -c permissions=%a /var/vcap/jobs/kube-controller-manager/config/bpm.yml tests: test_items: - flag: "permissions" compare: op: bitmask value: "644" remediation: | Run the below command (based on the file location on your system) on the master node. For example, chmod 644 /var/vcap/jobs/kube-apiserver/config/bpm.yml scored: true - id: 1.1.4 text: "Ensure that the controller manager pod specification file ownership is set to root:root" audit: stat -c %U:%G /var/vcap/jobs/kube-controller-manager/config/bpm.yml tests: test_items: - flag: "root:root" remediation: | Run the below command (based on the file location on your system) on the master node. For example, chown root:root /etc/kubernetes/manifests/kube-controller-manager.yaml scored: true - id: 1.1.5 text: "Ensure that the scheduler pod specification file permissions are set to 644 or more restrictive" audit: stat -c permissions=%a /var/vcap/jobs/kube-scheduler/config/bpm.yml tests: test_items: - flag: "permissions" compare: op: bitmask value: "644" remediation: | Run the below command (based on the file location on your system) on the master node. For example, chown 644 /var/vcap/jobs/kube-scheduler/config/bpm.yml scored: true - id: 1.1.6 text: "Ensure that the scheduler pod specification file ownership is set to root:root" audit: stat -c %U:%G /var/vcap/jobs/kube-scheduler/config/bpm.yml tests: test_items: - flag: "root:root" remediation: | Run the below command (based on the file location on your system) on the master node. For example, chown root:root /var/vcap/jobs/kube-scheduler/config/bpm.yml scored: true - id: 1.1.7 text: "Ensure that the etcd pod specification file permissions are set to 644 or more restrictive" audit: stat -c permissions=%a /var/vcap/jobs/etcd/config/bpm.yml tests: test_items: - flag: "permissions" compare: op: bitmask value: "644" remediation: | Run the below command (based on the file location on your system) on the master node. For example, chmod 644 stat -c permissions=%a /var/vcap/jobs/etcd/config/bpm.yml scored: true - id: 1.1.8 text: "Ensure that the etcd pod specification file ownership is set to root:root" audit: stat -c %U:%G /var/vcap/jobs/etcd/config/bpm.yml tests: test_items: - flag: "root:root" remediation: | Run the below command (based on the file location on your system) on the master node. For example, chown root:root /var/vcap/jobs/etcd/config/bpm.yml scored: true - id: 1.1.9 text: "Ensure that the Container Network Interface file permissions are set to 644 or more restrictive" audit: find ((CNI_DIR))/config/ -type f -not -perm 640 | awk 'END{print NR}' | grep "^0$" type: manual tests: test_items: - flag: "permissions" compare: op: bitmask value: "644" remediation: | Run the below command (based on the file location on your system) on the master node. For example, chmod 644 scored: false - id: 1.1.10 text: "Ensure that the Container Network Interface file ownership is set to root:root" audit: find ((CNI_DIR))/config/ -type f -not -user root -or -not -group root | awk 'END{print NR}' | grep "^0$" type: manual tests: test_items: - flag: "root:root" remediation: | Run the below command (based on the file location on your system) on the master node. For example, chown root:root scored: false - id: 1.1.11 text: "Ensure that the etcd data directory permissions are set to 700 or more restrictive" audit: stat -c permissions=%a /var/vcap/store/etcd/ tests: test_items: - flag: "permissions" compare: op: bitmask value: "700" remediation: | Run the below command (based on the etcd data directory found above). For example, chmod 700 /var/vcap/store/etcd/ scored: true - id: 1.1.12 text: "Ensure that the etcd data directory ownership is set to etcd:etcd" audit: stat -c %U:%G /var/vcap/store/etcd/ type: manual tests: test_items: - flag: "etcd:etcd" remediation: | Run the below command (based on the etcd data directory found above). For example, chown etcd:etcd /var/vcap/store/etcd/ Exception: All bosh processes run as vcap user The etcd data directory ownership is vcap:vcap scored: false - id: 1.1.13 text: "Ensure that the admin.conf file permissions are set to 644 or more restrictive" audit: stat -c permissions=%a /etc/kubernetes/admin.conf type: manual tests: test_items: - flag: "permissions" compare: op: bitmask value: "644" remediation: | Run the below command (based on the file location on your system) on the master node. For example, chmod 644 /etc/kubernetes/admin.conf Exception kubeadm is not used to provision/bootstrap the cluster. kubeadm and associated config files do not exist on master Reference: https://kubernetes.io/docs/reference/setup-tools/kubeadm/implementation-details/#generate- kubeconfig-files-for-control-plane-components scored: false - id: 1.1.14 text: "Ensure that the admin.conf file ownership is set to root:root" audit: stat -c %U:%G /etc/kubernetes/admin.conf type: manual tests: test_items: - flag: "root:root" remediation: | Run the below command (based on the file location on your system) on the master node. For example, chown root:root /etc/kubernetes/admin.conf Exception kubeadm is not used to provision/bootstrap the cluster. kubeadm and associated config files do not exist on master Reference: https://kubernetes.io/docs/reference/setup-tools/kubeadm/implementation-details/#generate- kubeconfig-files-for-control-plane-components scored: false - id: 1.1.15 text: "Ensure that the scheduler configuration file permissions are set to 644" audit: stat -c permissions=%a /etc/kubernetes/scheduler.conf type: manual tests: test_items: - flag: "permissions" compare: op: bitmask value: "644" remediation: | Run the below command (based on the file location on your system) on the master node. For example, chmod 644 /etc/kubernetes/scheduler.conf Exception kubeadm is not used to provision/bootstrap the cluster. kubeadm and associated config files do not exist on master Reference: https://kubernetes.io/docs/reference/setup-tools/kubeadm/implementation-details/#generate- kubeconfig-files-for-control-plane-components scored: false - id: 1.1.16 text: "Ensure that the scheduler configuration file ownership is set to root:root" audit: stat -c %U:%G /etc/kubernetes/scheduler.conf type: manual tests: test_items: - flag: "root:root" remediation: | Run the below command (based on the file location on your system) on the master node. For example, chown root:root /etc/kubernetes/scheduler.conf Exception kubeadm is not used to provision/bootstrap the cluster. kubeadm and associated config files do not exist on master Reference: https://kubernetes.io/docs/reference/setup-tools/kubeadm/implementation-details/#generate- kubeconfig-files-for-control-plane-components scored: false - id: 1.1.17 text: "Ensure that the controller manager configuration file permissions are set to 644" audit: stat -c permissions=%a /etc/kubernetes/controller-manager.conf type: manual tests: test_items: - flag: "permissions" compare: op: bitmask value: "644" remediation: | Run the below command (based on the file location on your system) on the master node. For example, chmod 644 /etc/kubernetes/controller-manager.conf Exception kubeadm is not used to provision/bootstrap the cluster. kubeadm and associated config files do not exist on master Reference: https://kubernetes.io/docs/reference/setup-tools/kubeadm/implementation-details/#generate- kubeconfig-files-for-control-plane-components scored: false - id: 1.1.18 text: "Ensure that the controller manager configuration file ownership is set to root:root" audit: stat -c %U:%G /etc/kubernetes/controller-manager.conf type: manual tests: test_items: - flag: "root:root" remediation: | Run the below command (based on the file location on your system) on the master node. For example, chown root:root /etc/kubernetes/controller-manager.conf Exception kubeadm is not used to provision/bootstrap the cluster. kubeadm and associated config files do not exist on master Reference: https://kubernetes.io/docs/reference/setup-tools/kubeadm/implementation-details/#generate- kubeconfig-files-for-control-plane-components scored: false - id: 1.1.19 text: "Ensure that the Kubernetes PKI directory and file ownership is set to root:root" audit: | find -L /var/vcap/jobs/kube-apiserver/config /var/vcap/jobs/kube-controller-manager/config /var/vcap/jobs/kube- scheduler/config ((CNI_DIR))/config /var/vcap/jobs/etcd/config | sort -u | xargs ls -ld | awk '{ print $3 " " $4}' | grep -c -v "root root" | grep "^0$" type: manual tests: test_items: - flag: "root:root" remediation: | Run the below command (based on the file location on your system) on the master node. For example, chown -R root:root /etc/kubernetes/pki/ Exception Files are group owned by vcap scored: false - id: 1.1.20 text: "Ensure that the Kubernetes PKI certificate file permissions are set to 644 or more restrictive" audit: | find -L /var/vcap/jobs/kube-apiserver/config \( -name '*.crt' -or -name '*.pem' \) -and -not -perm 640 | grep -v "packages/golang" | grep -v "packages/ncp_rootfs" | awk 'END{print NR}' | grep "^0$" type: manual tests: test_items: - flag: "permissions" compare: op: bitmask value: "644" remediation: | Run the below command (based on the file location on your system) on the master node. For example, chmod -R 644 /etc/kubernetes/pki/*.crt Exception Ignoring packages/golang as the package includes test certs used by golang. Ignoring packages/ncp_rootfs on TKG1 with NSX-T container plugin uses the package is used as the overlay filesystem `mount | grep "packages/ncp_rootfs"` scored: false - id: 1.1.21 text: "Ensure that the Kubernetes PKI key file permissions are set to 600" audit: | find -L /var/vcap/jobs/kube-apiserver/config -name '*.key' -and -not -perm 600 | awk 'END{print NR}' | grep "^0$" type: manual tests: test_items: - flag: "permissions" compare: op: eq value: "600" remediation: | Run the below command (based on the file location on your system) on the master node. For example, chmod -R 600 /etc/kubernetes/pki/*.key Exception Permission on etcd .key files is set to 640, to allow read access to vcap group scored: false - id: 1.2 text: "API Server" checks: - id: 1.2.1 text: "Ensure that the --anonymous-auth argument is set to false" audit: ps -ef | grep kube-apiserver | grep -- "--anonymous-auth=false" type: manual tests: test_items: - flag: "--anonymous-auth=false" remediation: | Edit the API server pod specification file kube-apiserver on the master node and set the below parameter. --anonymous-auth=false Exception The flag is set to true to enable API discoveribility. "Starting in 1.6, the ABAC and RBAC authorizers require explicit authorization of the system:anonymous user or the system:unauthenticated group, so legacy policy rules that grant access to the * user or * group do not include anonymous users." -authorization-mode is set to RBAC scored: false - id: 1.2.2 text: "Ensure that the --basic-auth-file argument is not set" audit: ps -ef | grep kube-apiserver | grep -v -- "--basic-auth-file" tests: test_items: - flag: "--basic-auth-file" set: false remediation: | Follow the documentation and configure alternate mechanisms for authentication. Then, edit the API server pod specification file kube-apiserver on the master node and remove the --basic-auth-file= parameter. scored: true - id: 1.2.3 text: "Ensure that the --token-auth-file parameter is not set" audit: ps -ef | grep "/var/vcap/packages/kubernetes/bin/kube-apiserve[r]" | grep -v tini | grep -v -- "--token-auth-file=" type: manual tests: test_items: - flag: "--token-auth-file" set: false remediation: | Follow the documentation and configure alternate mechanisms for authentication. Then, edit the API server pod specification file /var/vcap/packages/kubernetes/bin/kube-apiserve[r] on the master node and remove the --token-auth-file= parameter. Exception Since k8s processes' lifecyle are managed by BOSH, token based authentication is required when processes restart. The file has 0640 permission and root:vcap ownership scored: false - id: 1.2.4 text: "Ensure that the --kubelet-https argument is set to true" audit: ps -ef | grep kube-apiserver | grep -v -- "--kubelet-https=true" tests: test_items: - flag: "--kubelet-https=true" remediation: | Edit the API server pod specification file kube-apiserver on the master node and remove the --kubelet-https parameter. scored: true - id: 1.2.5 text: "Ensure that the --kubelet-client-certificate and --kubelet-client-key arguments are set as appropriate" audit: | ps -ef | grep kube-apiserver | grep -- "--kubelet-client-certificate=/var/vcap/jobs/kube-apiserver/config/kubelet- client-cert.pem" | grep -- "--kubelet-client-key=/var/vcap/jobs/kube-apiserver/config/kubelet-client-key.pem" type: manual tests: bin_op: and test_items: - flag: "--kubelet-client-certificate" - flag: "--kubelet-client-key" remediation: | Follow the Kubernetes documentation and set up the TLS connection between the apiserver and kubelets. Then, edit API server pod specification file kube-apiserver on the master node and set the kubelet client certificate and key parameters as below. --kubelet-client-certificate= --kubelet-client-key= scored: false - id: 1.2.6 text: "Ensure that the --kubelet-certificate-authority argument is set as appropriate" audit: ps -ef | grep kube-apiserver | grep -- "--kubelet-certificate-authority=" type: manual tests: test_items: - flag: "--kubelet-certificate-authority" remediation: | Follow the Kubernetes documentation and setup the TLS connection between the apiserver and kubelets. Then, edit the API server pod specification file kube-apiserver on the master node and set the --kubelet-certificate-authority parameter to the path to the cert file for the certificate authority. --kubelet-certificate-authority= Exception JIRA ticket #PKS-696 created to investigate a fix. PR opened to address the issue https://github.com/cloudfoundry- incubator/kubo-release/pull/179 scored: false - id: 1.2.7 text: "Ensure API server authorization modes does not include AlwaysAllow" audit: | ps -ef | grep kube-apiserver | grep -- "--authorization-mode" && ps -ef | grep kube-apiserver | grep -v -- "-- authorization-mode=\(\w\+\|,\)*AlwaysAllow\(\w\+\|,\)*" tests: test_items: - flag: "--authorization-mode" compare: op: nothave value: "AlwaysAllow" remediation: | Edit the API server pod specification file kube-apiserver on the master node and set the --authorization-mode parameter to values other than AlwaysAllow. One such example could be as below. --authorization-mode=RBAC scored: true - id: 1.2.8 text: "Ensure that the --authorization-mode argument includes Node" audit: | ps -ef | grep kube-apiserver | grep -v tini | grep -- "--authorization-mode=\(\w\+\|,\)*Node\(\w\+\|,\)* --" type: manual tests: test_items: - flag: "--authorization-mode" compare: op: has value: "Node" remediation: | Edit the API server pod specification file kube-apiserver on the master node and set the --authorization-mode parameter to a value that includes Node. --authorization-mode=Node,RBAC Exception This flag can be added using Kubernetes Profiles. Please follow instructions here https://docs.pivotal.io/tkgi/1- 8/k8s-profiles.html scored: false - id: 1.2.9 text: "Ensure that the --authorization-mode argument includes RBAC" audit: ps -ef | grep kube-apiserver | grep -v tini | grep -- "--authorization-mode=\(\w\+\|,\)*RBAC\(\w\+\|,\)* --" tests: test_items: - flag: "--authorization-mode" compare: op: has value: "RBAC" remediation: | Edit the API server pod specification file kube-apiserver on the master node and set the --authorization-mode parameter to a value that includes RBAC, for example: --authorization-mode=Node,RBAC scored: true - id: 1.2.10 text: "Ensure that the admission control plugin EventRateLimit is set" audit: | ps -ef | grep kube-apiserver | grep -v tini | grep -- "--enable-admission-plugins=\(\w\+\|,\)*EventRateLimit\ (\w\+\|,\)*" type: manual tests: test_items: - flag: "--enable-admission-plugins" compare: op: has value: "EventRateLimit" remediation: | Follow the Kubernetes documentation and set the desired limits in a configuration file. Then, edit the API server pod specification file kube-apiserver and set the below parameters. --enable-admission-plugins=...,EventRateLimit,... --admission-control-config-file= Exception "Note: This is an Alpha feature in the Kubernetes v1.13" Control provides rate limiting and is site-specific scored: false - id: 1.2.11 text: "Ensure that the admission control plugin AlwaysAdmit is not set" audit: | ps -ef | grep kube-apiserver | grep -v -- "--enable-admission-plugins=\(\w\+\|,\)*AlwaysAdmit\(\w\+\|,\)*" tests: test_items: - flag: "--enable-admission-plugins" compare: op: nothave value: AlwaysAdmit remediation: | Edit the API server pod specification file kube-apiserver on the master node and either remove the --enable-admission-plugins parameter, or set it to a value that does not include AlwaysAdmit. scored: true - id: 1.2.12 text: "Ensure that the admission control plugin AlwaysPullImages is set" audit: | ps -ef | grep kube-apiserver | grep -v tini | grep -- "--enable-admission-plugins=\(\w\+\|,\)*AlwaysPullImages\ (\w\+\|,\)* --" type: manual tests: test_items: - flag: "--enable-admission-plugins" compare: op: has value: "AlwaysPullImages" remediation: | Edit the API server pod specification file kube-apiserver on the master node and set the --enable-admission-plugins parameter to include AlwaysPullImages. --enable-admission-plugins=...,AlwaysPullImages,... Exception "Credentials would be required to pull the private images every time. Also, in trusted environments, this might increases load on network, registry, and decreases speed. This setting could impact offline or isolated clusters, which have images pre-loaded and do not have access to a registry to pull in-use images. This setting is not appropriate for clusters which use this configuration." TKGi is packages with pre-loaded images. scored: false - id: 1.2.13 text: "Ensure that the admission control plugin SecurityContextDeny is set" audit: | ps -ef | grep kube-apiserver | grep -v tini | grep -- "--enable-admission-plugins=\(\w\+\|,\)*SecurityContextDeny\ (\w\+\|,\)* --" type: manual tests: test_items: - flag: "--enable-admission-plugins" compare: op: has value: "SecurityContextDeny" remediation: | Edit the API server pod specification file kube-apiserver on the master node and set the --enable-admission-plugins parameter to include SecurityContextDeny, unless PodSecurityPolicy is already in place. --enable-admission-plugins=...,SecurityContextDeny,... Exception This setting is site-specific. It can be set in the "Admission Plugins" section of the appropriate "Plan" scored: false - id: 1.2.14 text: "Ensure that the admission control plugin ServiceAccount is set" audit: | ps -ef | grep kube-apiserver | grep -v tini | grep -v -- "--disable-admission-plugins=\(\w\+\|,\)*ServiceAccount\ (\w\+\|,\)* --" tests: test_items: - flag: "--disable-admission-plugins" compare: op: nothave value: "ServiceAccount" remediation: | Follow the documentation and create ServiceAccount objects as per your environment. Then, edit the API server pod specification file kube-apiserver on the master node and ensure that the --disable-admission-plugins parameter is set to a value that does not include ServiceAccount. scored: true - id: 1.2.15 text: "Ensure that the admission control plugin NamespaceLifecycle is set" audit: | ps -ef | grep kube-apiserver | grep -v tini | grep -v -- "--disable-admission-plugins=\ (\w\+\|,\)*NamespaceLifecycle\(\w\+\|,\)* --" tests: test_items: - flag: "--disable-admission-plugins" compare: op: nothave value: "NamespaceLifecycle" remediation: | Edit the API server pod specification file kube-apiserver on the master node and set the --disable-admission-plugins parameter to ensure it does not include NamespaceLifecycle. scored: true - id: 1.2.16 text: "Ensure that the admission control plugin PodSecurityPolicy is set" audit: | ps -ef | grep kube-apiserver | grep -v tini | grep -- "--enable-admission-plugins=\(\w\+\|,\)*PodSecurityPolicy\ (\w\+\|,\)* --" type: manual tests: test_items: - flag: "--enable-admission-plugins" compare: op: has value: "PodSecurityPolicy" remediation: | Follow the documentation and create Pod Security Policy objects as per your environment. Then, edit the API server pod specification file kube-apiserver on the master node and set the --enable-admission-plugins parameter to a value that includes PodSecurityPolicy: --enable-admission-plugins=...,PodSecurityPolicy,... Then restart the API Server. Exception This setting is site-specific. It can be set in the "Admission Plugins" section of the appropriate "Plan" scored: false - id: 1.2.17 text: "Ensure that the admission control plugin NodeRestriction is set" audit: | ps -ef | grep kube-apiserver | grep -v tini | grep -- "--enable-admission-plugins=\(\w\+\|,\)*NodeRestriction\ (\w\+\|,\)* --" type: manual tests: test_items: - flag: "--enable-admission-plugins" compare: op: has value: "NodeRestriction" remediation: | Follow the Kubernetes documentation and configure NodeRestriction plug-in on kubelets. Then, edit the API server pod specification file kube-apiserver on the master node and set the --enable-admission-plugins parameter to a value that includes NodeRestriction. --enable-admission-plugins=...,NodeRestriction,... Exception PR opened to address the issue https://github.com/cloudfoundry-incubator/kubo-release/pull/179" scored: true - id: 1.2.18 text: "Ensure that the --insecure-bind-address argument is not set" audit: | ps -ef | grep kube-apiserver | grep -v tini | grep -v -- "--insecure-bind-address" tests: test_items: - flag: "--insecure-bind-address" set: false remediation: | Edit the API server pod specification file kube-apiserver on the master node and remove the --insecure-bind-address parameter. scored: true - id: 1.2.19 text: "Ensure that the --insecure-port argument is set to 0" audit: | ps -ef | grep kube-apiserver | grep -v tini | grep -- "--insecure-port=0" type: manual tests: test_items: - flag: "--insecure-port=0" remediation: | Edit the API server pod specification file kube-apiserver on the master node and set the below parameter. --insecure-port=0 Exception Related to 1.2.1 The insecure port is 8080, and is binding only to localhost on the master node, in use by other components on the master that are bypassing authn/z. The components connecting to the APIServer are: kube-controller-manager kube-proxy kube-scheduler Pods are not scheduled on the master node. scored: false - id: 1.2.20 text: "Ensure that the --secure-port argument is not set to 0" audit: | ps -ef | grep kube-apiserver | grep -v tini | grep -v -- "--secure-port=0" tests: test_items: - flag: "--secure-port" compare: op: noteq value: 0 remediation: | Edit the API server pod specification file kube-apiserver on the master node and either remove the --secure-port parameter or set it to a different (non-zero) desired port. scored: true - id: 1.2.21 text: "Ensure that the --profiling argument is set to false" audit: ps -ef | grep kube-apiserver | grep -v tini | grep -- "--profiling=false" tests: test_items: - flag: "--profiling=false" remediation: | Edit the API server pod specification file kube-apiserver on the master node and set the below parameter. --profiling=false scored: true - id: 1.2.22 text: "Ensure that the --audit-log-path argument is set as appropriate" audit: | ps -ef | grep kube-apiserver | grep -v tini | grep -- "--audit-log-path=\/var\/vcap\/sys\/log\/kube-apiserver\/audit.log" type: manual tests: test_items: - flag: "--audit-log-path" remediation: | Edit the API server pod specification file kube-apiserver on the master node and set the --audit-log-path parameter to a suitable path and file where you would like audit logs to be written, for example: --audit-log-path=/var/log/apiserver/audit.log scored: false - id: 1.2.23 text: "Ensure that the --audit-log-maxage argument is set to 30 or as appropriate" audit: ps -ef | grep kube-apiserver | grep -v tini | grep -- "--audit-log-maxage=30" type: manual tests: test_items: - flag: "--audit-log-maxage=30" remediation: | Edit the API server pod specification file kube-apiserver on the master node and set the --audit-log-maxage parameter to 30 or as an appropriate number of days: --audit-log-maxage=30 Exception This setting can be set to expected value using Kubernetes Profiles. Please follow instructions here https://docs.pivotal.io/tkgi/1-8/k8s-profiles.html scored: false - id: 1.2.24 text: "Ensure that the --audit-log-maxbackup argument is set to 10 or as appropriate" audit: ps -ef | grep kube-apiserver | grep -v tini | grep -- "--audit-log-maxbackup=10" type: manual tests: test_items: - flag: "--audit-log-maxbackup=10" remediation: | Edit the API server pod specification file kube-apiserver on the master node and set the --audit-log-maxbackup parameter to 10 or to an appropriate value. --audit-log-maxbackup=10 Exception This setting can be set to expected value using Kubernetes Profiles. Please follow instructions here https://docs.pivotal.io/tkgi/1-8/k8s-profiles.html scored: false - id: 1.2.25 text: "Ensure that the --audit-log-maxsize argument is set to 100 or as appropriate" audit: ps -ef | grep kube-apiserver | grep -v tini | grep -- "--audit-log-maxsize=100" type: manual tests: test_items: - flag: "--audit-log-maxsize=100" remediation: | Edit the API server pod specification file kube-apiserver on the master node and set the --audit-log-maxsize parameter to an appropriate size in MB. For example, to set it as 100 MB: --audit-log-maxsize=100 Exception This setting can be set to expected value using Kubernetes Profiles. Please follow instructions here https://docs.pivotal.io/tkgi/1-8/k8s-profiles.html scored: false - id: 1.2.26 text: "Ensure that the --request-timeout argument is set as appropriate" audit: ps -ef | grep kube-apiserver | grep -v tini | grep -v -- "--request-timeout=" type: manual tests: test_items: - flag: "--request-timeout" remediation: | Edit the API server pod specification file kube-apiserver and set the below parameter as appropriate and if needed. For example, --request-timeout=300s scored: false - id: 1.2.27 text: "Ensure that the --service-account-lookup argument is set to true" audit: ps -ef | grep kube-apiserver | grep -v tini | grep -v -- "--service-account-lookup" tests: test_items: - flag: "--service-account-lookup=true" remediation: | Edit the API server pod specification file kube-apiserver on the master node and set the below parameter. --service-account-lookup=true Alternatively, you can delete the --service-account-lookup parameter from this file so that the default takes effect. scored: true - id: 1.2.28 text: "Ensure that the --service-account-key-file argument is set as appropriate" audit: | ps -ef | grep kube-apiserver | grep -v tini | grep -- "--service-account-key-file=/var/vcap/jobs/kube- apiserver/config/service-account-public-key.pem" type: manual tests: test_items: - flag: "--service-account-key-file" remediation: | Edit the API server pod specification file kube-apiserver on the master node and set the --service-account-key-file parameter to the public key file for service accounts: --service-account-key-file= scored: false - id: 1.2.29 text: "Ensure that the --etcd-certfile and --etcd-keyfile arguments are set as appropriate" audit: | ps -ef | grep kube-apiserver | grep -v tini | grep -- "--etcd-certfile=/var/vcap/jobs/kube-apiserver/config/etcd- client.crt" | grep -- "--etcd-keyfile=/var/vcap/jobs/kube-apiserver/config/etcd-client.key" type: manual tests: bin_op: and test_items: - flag: "--etcd-certfile" - flag: "--etcd-keyfile" remediation: | Follow the Kubernetes documentation and set up the TLS connection between the apiserver and etcd. Then, edit the API server pod specification file kube-apiserver on the master node and set the etcd certificate and key file parameters. --etcd-certfile= --etcd-keyfile= scored: false - id: 1.2.30 text: "Ensure that the --tls-cert-file and --tls-private-key-file arguments are set as appropriate" audit: | ps -ef | grep kube-apiserver | grep -v tini | grep -- "--tls-cert-file=/var/vcap/jobs/kube-apiserver/config/kubernetes.pem" | grep -- "--tls-private-key-file=/var/vcap/jobs/kube- apiserver/config/kubernetes-key.pem" type: manual tests: bin_op: and test_items: - flag: "--tls-cert-file" - flag: "--tls-private-key-file" remediation: | Follow the Kubernetes documentation and set up the TLS connection on the apiserver. Then, edit the API server pod specification file kube-apiserver on the master node and set the TLS certificate and private key file parameters. --tls-cert-file= --tls-private-key-file= scored: false - id: 1.2.31 text: "Ensure that the --client-ca-file argument is set as appropriate" audit: | ps -ef | grep kube-apiserver | grep -v tini | grep -- "--client-ca-file=/var/vcap/jobs/kube-apiserver/config/kubernetes-ca.pem" type: manual tests: test_items: - flag: "--client-ca-file" remediation: | Follow the Kubernetes documentation and set up the TLS connection on the apiserver. Then, edit the API server pod specification file kube-apiserver on the master node and set the client certificate authority file. --client-ca-file= scored: false - id: 1.2.32 text: "Ensure that the --etcd-cafile argument is set as appropriate" audit: | ps -ef | grep kube-apiserver | grep -v tini | grep -- "--etcd-cafile=/var/vcap/jobs/kube-apiserver/config/etcd-ca.crt" type: manual tests: test_items: - flag: "--etcd-cafile" remediation: | Follow the Kubernetes documentation and set up the TLS connection between the apiserver and etcd. Then, edit the API server pod specification file kube-apiserver on the master node and set the etcd certificate authority file parameter. --etcd-cafile= scored: false - id: 1.2.33 text: "Ensure that the --encryption-provider-config argument is set as appropriate" audit: | ps -ef | grep kube-apiserver | grep -v tini | grep -- "--encryption-provider-config=" type: manual tests: test_items: - flag: "--encryption-provider-config" remediation: | Follow the Kubernetes documentation and configure a EncryptionConfig file. Then, edit the API server pod specification file kube-apiserver on the master node and set the --encryption-provider-config parameter to the path of that file: --encryption-provider-config= Exception Encrypting Secrets in an etcd database can be enabled using Kubernetes Profiles. Please follow instructions here https://docs.pivotal.io/tkgi/1-8/k8s-profiles-encrypt-etcd.html scored: false - id: 1.2.34 text: "Ensure that the encryption provider is set to aescbc" audit: | ENC_CONF=`ps -ef | grep kube-apiserver | grep -v tini | sed $'s/ /\\\\\\n/g' | grep -- '--encryption-provider- config=' | cut -d'=' -f2` grep -- "- \(aescbc\|kms\|secretbox\):" $ENC_CONF type: manual remediation: | Follow the Kubernetes documentation and configure a EncryptionConfig file. In this file, choose aescbc, kms or secretbox as the encryption provider. Exception Encrypting Secrets in an etcd database can be enabled using Kubernetes Profiles. Please follow instructions here https://docs.pivotal.io/tkgi/1-8/k8s-profiles-encrypt-etcd.html scored: false - id: 1.2.35 text: "Ensure that the API Server only makes use of Strong Cryptographic Ciphers" audit: ps -ef | grep kube-apiserver | grep -v tini | grep -- "--tls-cipher-suites=" type: manual tests: test_items: - flag: "--tls-cipher-suites" compare: op: valid_elements value: "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" remediation: | Edit the API server pod specification file /etc/kubernetes/manifests/kube-apiserver.yaml on the master node and set the below parameter. --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 scored: false - id: 1.3 text: "Controller Manager" checks: - id: 1.3.1 text: "Ensure that the --terminated-pod-gc-threshold argument is set as appropriate" audit: ps -ef | grep kube-controller-manager | grep -- "--terminated-pod-gc-threshold=100" type: manual tests: test_items: - flag: "--terminated-pod-gc-threshold" remediation: | Edit the Controller Manager pod specification file controller manager conf on the master node and set the --terminated-pod-gc-threshold to an appropriate threshold, for example: --terminated-pod-gc-threshold=10 scored: false - id: 1.3.2 text: "Ensure controller manager profiling is disabled" audit: ps -ef | grep kube-controller-manager | grep -- "--profiling=false" tests: test_items: - flag: "--profiling=false" remediation: | Edit the Controller Manager pod specification file controller manager conf on the master node and set the below parameter. --profiling=false scored: true - id: 1.3.3 text: "Ensure that the --use-service-account-credentials argument is set to true" audit: ps -ef | grep kube-controller-manager | grep -- "--use\-service\-account\-credentials=true" tests: test_items: - flag: "--use-service-account-credentials=true" remediation: | Edit the Controller Manager pod specification file controller manager conf on the master node to set the below parameter. --use-service-account-credentials=true scored: true - id: 1.3.4 text: "Ensure that the --service-account-private-key-file argument is set as appropriate" audit: | ps -ef | grep kube-controller-manager | grep -- "--service\-account\-private\-key\-file=\/var\/vcap\/jobs\/kube\- controller\-manager\/config\/service\-account\-private\-key.pem" type: manual tests: test_items: - flag: "--service-account-private-key-file" remediation: | Edit the Controller Manager pod specification file controller manager conf on the master node and set the --service-account-private-key-file parameter to the private key file for service accounts. --service-account-private-key-file= scored: false - id: 1.3.5 text: "Ensure that the --root-ca-file argument is set as appropriate" audit: | ps -ef | grep kube-controller-manager | grep -- "--root\-ca\-file=\/var\/vcap\/jobs\/kube\-controller\-manager\/config\/ca.pem" type: manual tests: test_items: - flag: "--root-ca-file" remediation: | Edit the Controller Manager pod specification file controller manager conf on the master node and set the --root-ca-file parameter to the certificate bundle file`. --root-ca-file= scored: false - id: 1.3.6 text: "Ensure that the RotateKubeletServerCertificate argument is set to true" audit: | ps -ef | grep kube-controller-manager | grep -- "--feature-gates=\ (\w\+\|,\)*RotateKubeletServerCertificate=true\(\w\+\|,\)*" type: manual tests: test_items: - flag: "--feature-gates=RotateKubeletServerCertificate=true" remediation: | Edit the Controller Manager pod specification file controller manager conf on the master node and set the --feature-gates parameter to include RotateKubeletServerCertificate=true. --feature-gates=RotateKubeletServerCertificate=true Exception Certificate rotation is handled by Credhub scored: false - id: 1.3.7 text: "Ensure that the --bind-address argument is set to 127.0.0.1" audit: | ps -ef | grep "/var/vcap/packages/kubernetes/bin/kube-controller-manage[r]" | grep -v tini | grep -- "--bind-address=127.0.0.1" type: manual tests: test_items: - flag: "--bind-address=127.0.0.1" remediation: | Edit the Controller Manager pod specification file controller manager conf on the master node and ensure the correct value for the --bind-address parameter Exception This setting can be set to expected value using Kubernetes Profiles. Please follow instructions here https://docs.pivotal.io/tkgi/1-8/k8s-profiles.html scored: false - id: 1.4 text: "Scheduler" checks: - id: 1.4.1 text: "Ensure that the --profiling argument is set to false" audit: ps -ef | grep kube-scheduler | grep -v tini | grep -- "--profiling=false" tests: test_items: - flag: "--profiling=false" remediation: | Edit the Scheduler pod specification file scheduler config file on the master node and set the below parameter. --profiling=false scored: true - id: 1.4.2 text: "Ensure that the --bind-address argument is set to 127.0.0.1" audit: ps -ef | grep "/var/vcap/packages/kubernetes/bin/kube-schedule[r]" | grep -v tini | grep -- "--bind-address=127.0.0.1" type: manual tests: test_items: - flag: "--bind-address" compare: op: eq value: "127.0.0.1" remediation: | Edit the Scheduler pod specification file scheduler config on the master node and ensure the correct value for the --bind-address parameter Exception This setting can be set to expected value using Kubernetes Profiles. Please follow instructions here https://docs.pivotal.io/tkgi/1-8/k8s-profiles.html scored: false ================================================ FILE: cfg/tkgi-1.2.53/node.yaml ================================================ --- controls: version: "tkgi-1.2.53" id: 4 text: "Worker Node Security Configuration" type: "node" groups: - id: 4.1 text: "Worker Node Configuration Files" checks: - id: 4.1.1 text: "Ensure that the kubelet service file permissions are set to 644 or more restrictive" audit: stat -c permissions=%a /var/vcap/jobs/kubelet/monit tests: test_items: - flag: "permissions" compare: op: bitmask value: "644" remediation: | Run the below command (based on the file location on your system) on the each worker node. For example, chmod 644 /var/vcap/jobs/kubelet/monit scored: true - id: 4.1.2 text: "Ensure that the kubelet service file ownership is set to root:root" audit: stat -c %U:%G /var/vcap/jobs/kubelet/monit tests: test_items: - flag: root:root remediation: | Run the below command (based on the file location on your system) on the each worker node. For example, chown root:root /var/vcap/jobs/kubelet/monit Exception File is group owned by vcap scored: true - id: 4.1.3 text: "Ensure that the proxy kubeconfig file permissions are set to 644 or more restrictive" audit: stat -c permissions=%a /var/vcap/jobs/kube-proxy/config/kubeconfig tests: test_items: - flag: "permissions" compare: op: bitmask value: "644" remediation: | Run the below command (based on the file location on your system) on the each worker node. For example, chmod 644 /var/vcap/jobs/kube-proxy/config/kubeconfig scored: true - id: 4.1.4 text: "Ensure that the proxy kubeconfig file ownership is set to root:root" audit: stat -c %U:%G /var/vcap/jobs/kube-proxy/config/kubeconfig type: manual tests: test_items: - flag: root:root remediation: | Run the below command (based on the file location on your system) on the each worker node. For example, chown root:root /var/vcap/jobs/kube-proxy/config/kubeconfig Exception File is group owned by vcap scored: false - id: 4.1.5 text: "Ensure that the kubelet.conf file permissions are set to 644 or more restrictive" audit: stat -c permissions=%a /var/vcap/jobs/kube-proxy/config/kubeconfig type: manual tests: test_items: - flag: "permissions" compare: op: bitmask value: "644" remediation: | Run the below command (based on the file location on your system) on the each worker node. For example, chmod 644 /var/vcap/jobs/kube-proxy/config/kubeconfig Exception kubeadm is not used to provision/bootstrap the cluster. kubeadm and associated config files do not exist on worker scored: false - id: 4.1.6 text: "Ensure that the kubelet.conf file ownership is set to root:root" audit: stat -c %U:%G /etc/kubernetes/kubelet.conf type: manual tests: test_items: - flag: root:root remediation: | Run the below command (based on the file location on your system) on the each worker node. For example, chown root:root /etc/kubernetes/kubelet.conf Exception file ownership is vcap:vcap scored: false - id: 4.1.7 text: "Ensure that the certificate authorities file permissions are set to 644 or more restrictive" audit: stat -c permissions=%a /var/vcap/jobs/kubelet/config/kubelet-client-ca.pem tests: test_items: - flag: "permissions" compare: op: bitmask value: "644" remediation: | Run the following command to modify the file permissions of the --client-ca-file chmod 644 scored: true - id: 4.1.8 text: "Ensure that the client certificate authorities file ownership is set to root:root" audit: stat -c %U:%G /var/vcap/jobs/kubelet/config/kubelet-client-ca.pem type: manual tests: test_items: - flag: root:root compare: op: eq value: root:root remediation: | Run the following command to modify the ownership of the --client-ca-file. chown root:root Exception File is group owned by vcap scored: false - id: 4.1.9 text: "Ensure that the kubelet --config configuration file has permissions set to 644 or more restrictive" audit: stat -c permissions=%a /var/vcap/jobs/kubelet/config/kubeletconfig.yml tests: test_items: - flag: "permissions" compare: op: bitmask value: "644" remediation: | Run the following command (using the config file location identified in the Audit step) chmod 644 /var/vcap/jobs/kubelet/config/kubeletconfig.yml scored: true - id: 4.1.10 text: "Ensure that the kubelet --config configuration file ownership is set to root:root" audit: stat -c %U:%G /var/vcap/jobs/kubelet/config/kubeletconfig.yml type: manual tests: test_items: - flag: root:root remediation: | Run the following command (using the config file location identified in the Audit step) chown root:root /var/vcap/jobs/kubelet/config/kubeletconfig.yml Exception File is group owned by vcap scored: false - id: 4.2 text: "Kubelet" checks: - id: 4.2.1 text: "Ensure that the anonymous-auth argument is set to false" audit: grep "^authentication:\n\s{2}anonymous:\n\s{4}enabled:\sfalse$" /var/vcap/jobs/kubelet/config/kubeletconfig.yml tests: test_items: - flag: "enabled: false" remediation: | If using a Kubelet config file, edit the file to set authentication: anonymous: enabled to false. If using executable arguments, edit the kubelet service file on each worker node and set the below parameter in KUBELET_SYSTEM_PODS_ARGS variable. --anonymous-auth=false Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: true - id: 4.2.2 text: "Ensure that the --authorization-mode argument is not set to AlwaysAllow" audit: | grep "^authorization:\n\s{2}mode: AlwaysAllow$" /var/vcap/jobs/kubelet/config/kubeletconfig.yml tests: test_items: - flag: "AlwaysAllow" set: false remediation: | If using a Kubelet config file, edit the file to set authorization: mode to Webhook. If using executable arguments, edit the kubelet service file on each worker node and set the below parameter in KUBELET_AUTHZ_ARGS variable. --authorization-mode=Webhook Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: true - id: 4.2.3 text: "Ensure that the --client-ca-file argument is set as appropriate" audit: | grep ^authentication:\n\s{2}anonymous:\n\s{4}enabled:\sfalse\n(\s{2}webhook:\n\s{4}cacheTTL:\s\d+s\n\s{4}enabled:.*\n)? \s{2}x509:\n\s{4}clientCAFile:\s"\/var\/vcap\/jobs\/kubelet\/config\/kubelet-client-ca\.pem" /var/vcap/jobs/kubelet/config/kubeletconfig.yml tests: test_items: - flag: "clientCAFile" remediation: | If using a Kubelet config file, edit the file to set authentication: x509: clientCAFile to the location of the client CA file. If using command line arguments, edit the kubelet service file on each worker node and set the below parameter in KUBELET_AUTHZ_ARGS variable. --client-ca-file= Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: true - id: 4.2.4 text: "Ensure that the --read-only-port argument is set to 0" audit: | grep "readOnlyPort: 0" /var/vcap/jobs/kubelet/config/kubeletconfig.yml tests: test_items: - flag: "readOnlyPort: 0" remediation: | If using a Kubelet config file, edit the file to set readOnlyPort to 0. If using command line arguments, edit the kubelet service file on each worker node and set the below parameter in KUBELET_SYSTEM_PODS_ARGS variable. --read-only-port=0 Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: true - id: 4.2.5 text: "Ensure that the --streaming-connection-idle-timeout argument is not set to 0" audit: | grep -- "streamingConnectionIdleTimeout: 0" /var/vcap/jobs/kubelet/config/kubeletconfig.yml tests: test_items: - flag: "streamingConnectionIdleTimeout: 0" set: false remediation: | If using a Kubelet config file, edit the file to set streamingConnectionIdleTimeout to a value other than 0. If using command line arguments, edit the kubelet service file on each worker node and set the below parameter in KUBELET_SYSTEM_PODS_ARGS variable. --streaming-connection-idle-timeout=5m Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: true - id: 4.2.6 text: "Ensure that the --protect-kernel-defaults argument is set to true" audit: | grep -- "protectKernelDefaults: true" /var/vcap/jobs/kubelet/config/kubeletconfig.yml tests: test_items: - flag: "protectKernelDefaults: true" remediation: | If using a Kubelet config file, edit the file to set protectKernelDefaults: true. If using command line arguments, edit the kubelet service file on each worker node and set the below parameter in KUBELET_SYSTEM_PODS_ARGS variable. --protect-kernel-defaults=true Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: true - id: 4.2.7 text: "Ensure that the --make-iptables-util-chains argument is set to true" audit: | grep -- "makeIPTablesUtilChains: true" /var/vcap/jobs/kubelet/config/kubeletconfig.yml tests: test_items: - flag: "makeIPTablesUtilChains: true" remediation: | If using a Kubelet config file, edit the file to set makeIPTablesUtilChains: true. If using command line arguments, edit the kubelet service file on each worker node and remove the --make-iptables-util-chains argument from the KUBELET_SYSTEM_PODS_ARGS variable. Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: true - id: 4.2.8 text: "Ensure that the --hostname-override argument is not set" audit: | ps -ef | grep [k]ubelet | grep -- --[c]onfig=/var/vcap/jobs/kubelet/config/kubeletconfig.yml | grep -v -- --hostname-override type: manual remediation: | Edit the kubelet service file on each worker node and remove the --hostname-override argument from the KUBELET_SYSTEM_PODS_ARGS variable. Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service Exception On GCE, the hostname needs to be set to the instance name so the gce cloud provider can manage the instance. In other cases its set to the IP address of the VM. scored: false - id: 4.2.9 text: "Ensure that the --event-qps argument is set to 0 or a level which ensures appropriate event capture" audit: grep -- "--event-qps" /var/vcap/jobs/kubelet/config/kubeletconfig.yml type: manual tests: test_items: - flag: "--event-qps" compare: op: eq value: 0 remediation: | If using a Kubelet config file, edit the file to set eventRecordQPS: to an appropriate level. If using command line arguments, edit the kubelet service file on each worker node and set the below parameter in KUBELET_SYSTEM_PODS_ARGS variable. Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: false - id: 4.2.10 text: "Ensure that the --tls-cert-file and --tls-private-key-file arguments are set as appropriate" audit: | grep ^tlsCertFile:\s\"\/var\/vcap\/jobs\/kubelet\/config\/kubelet\.pem\"\ntlsPrivateKeyFile:\s\"\/var\/vcap\/jobs\/kubelet\/config\/kubelet-key\.pem\"$ /var/vcap/jobs/kubelet/config/kubeletconfig.yml tests: bin_op: and test_items: - flag: "tlsCertFile" - flag: "tlsPrivateKeyFile" remediation: | If using a Kubelet config file, edit the file to set tlsCertFile to the location of the certificate file to use to identify this Kubelet, and tlsPrivateKeyFile to the location of the corresponding private key file. If using command line arguments, edit the kubelet service file on each worker node and set the below parameters in KUBELET_CERTIFICATE_ARGS variable. --tls-cert-file= --tls-private-key-file= Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: true - id: 4.2.11 text: "Ensure that the --rotate-certificates argument is not set to false" audit: ps -ef | grep kubele[t] | grep -- "--rotate-certificates=false" type: manual tests: test_items: - flag: "--rotate-certificates=false" set: false remediation: | If using a Kubelet config file, edit the file to add the line rotateCertificates: true or remove it altogether to use the default value. If using command line arguments, edit the kubelet service file on each worker node and remove --rotate-certificates=false argument from the KUBELET_CERTIFICATE_ARGS variable. Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service Exception Certificate rotation is handled by Credhub scored: false - id: 4.2.12 text: "Verify that the RotateKubeletServerCertificate argument is set to true" audit: ps -ef | grep kubele[t] | grep -- "--feature-gates=\(\w\+\|,\)*RotateKubeletServerCertificate=true\(\w\+\|,\)*" type: manual tests: test_items: - flag: "RotateKubeletServerCertificate=true" remediation: | Edit the kubelet service file on each worker node and set the below parameter in KUBELET_CERTIFICATE_ARGS variable. --feature-gates=RotateKubeletServerCertificate=true Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service Exception Certificate rotation is handled by Credhub scored: false - id: 4.2.13 text: "Ensure that the Kubelet only makes use of Strong Cryptographic Ciphers" audit: ps -ef | grep kubele[t] | grep -- "--tls-cipher- suites=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384" type: manual tests: test_items: - flag: --tls-cipher-suites compare: op: regex value: (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) remediation: | If using a Kubelet config file, edit the file to set tlsCipherSuites: to 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 or to a subset of these values. If using executable arguments, edit the kubelet service file on each worker node and set the --tls-cipher-suites parameter as follows, or to a subset of these values. --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 Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service scored: false ================================================ FILE: cfg/tkgi-1.2.53/policies.yaml ================================================ --- controls: version: "tkgi-1.2.53" id: 5 text: "Kubernetes Policies" type: "policies" groups: - id: 5.1 text: "RBAC and Service Accounts" checks: - id: 5.1.1 text: "Ensure that the cluster-admin role is only used where required" type: "manual" remediation: | Identify all clusterrolebindings to the cluster-admin role. Check if they are used and if they need this role or if they could use a role with fewer privileges. Where possible, first bind users to a lower privileged role and then remove the clusterrolebinding to the cluster-admin role : kubectl delete clusterrolebinding [name] Exception This is site-specific setting. scored: false - id: 5.1.2 text: "Minimize access to secrets" type: "manual" remediation: | Where possible, remove get, list and watch access to secret objects in the cluster. Exception This is site-specific setting. scored: false - id: 5.1.3 text: "Minimize wildcard use in Roles and ClusterRoles" type: "manual" remediation: | Where possible replace any use of wildcards in clusterroles and roles with specific objects or actions. Exception This is site-specific setting. scored: false - id: 5.1.4 text: "Minimize access to create pods" type: "manual" remediation: | Where possible, remove create access to pod objects in the cluster. Exception This is site-specific setting. scored: false - id: 5.1.5 text: "Ensure that default service accounts are not actively used." type: "manual" remediation: | Create explicit service accounts wherever a Kubernetes workload requires specific access to the Kubernetes API server. Modify the configuration of each default service account to include this value automountServiceAccountToken: false Exception This is site-specific setting. scored: false - id: 5.1.6 text: "Ensure that Service Account Tokens are only mounted where necessary" type: "manual" remediation: | Modify the definition of pods and service accounts which do not need to mount service account tokens to disable it. Exception This is site-specific setting. scored: false - id: 5.2 text: "Pod Security Policies" checks: - id: 5.2.1 text: "Minimize the admission of privileged containers" type: "manual" remediation: | Create a PSP as described in the Kubernetes documentation, ensuring that the .spec.privileged field is omitted or set to false. Exception This is site-specific setting. scored: false - id: 5.2.2 text: "Minimize the admission of containers wishing to share the host process ID namespace" type: "manual" remediation: | Create a PSP as described in the Kubernetes documentation, ensuring that the .spec.hostPID field is omitted or set to false. Exception This is site-specific setting. scored: false - id: 5.2.3 text: "Minimize the admission of containers wishing to share the host IPC namespace" type: "manual" remediation: | Create a PSP as described in the Kubernetes documentation, ensuring that the .spec.hostIPC field is omitted or set to false. Exception This is site-specific setting. scored: false - id: 5.2.4 text: "Minimize the admission of containers wishing to share the host network namespace" type: "manual" remediation: | Create a PSP as described in the Kubernetes documentation, ensuring that the .spec.hostNetwork field is omitted or set to false. Exception This is site-specific setting. scored: false - id: 5.2.5 text: "Minimize the admission of containers with allowPrivilegeEscalation" type: "manual" remediation: | Create a PSP as described in the Kubernetes documentation, ensuring that the .spec.allowPrivilegeEscalation field is omitted or set to false. Exception This is site-specific setting. scored: false - id: 5.2.6 text: "Minimize the admission of root containers" type: "manual" remediation: | Create a PSP as described in the Kubernetes documentation, ensuring that the .spec.runAsUser.rule is set to either MustRunAsNonRoot or MustRunAs with the range of UIDs not including 0. Exception This is site-specific setting. scored: false - id: 5.2.7 text: "Minimize the admission of containers with the NET_RAW capability" type: "manual" remediation: | Create a PSP as described in the Kubernetes documentation, ensuring that the .spec.requiredDropCapabilities is set to include either NET_RAW or ALL. Exception This is site-specific setting. scored: false - id: 5.2.8 text: "Minimize the admission of containers with added capabilities" type: "manual" remediation: | Ensure that allowedCapabilities is not present in PSPs for the cluster unless it is set to an empty array. Exception This is site-specific setting. scored: false - id: 5.2.9 text: "Minimize the admission of containers with capabilities assigned" type: "manual" remediation: | Review the use of capabilites in applications running on your cluster. Where a namespace contains applications which do not require any Linux capabities to operate consider adding a PSP which forbids the admission of containers which do not drop all capabilities. Exception This is site-specific setting. scored: false - id: 5.3 text: "Network Policies and CNI" checks: - id: 5.3.1 text: "Ensure that the CNI in use supports Network Policies" type: "manual" remediation: | If the CNI plugin in use does not support network policies, consideration should be given to making use of a different plugin, or finding an alternate mechanism for restricting traffic in the Kubernetes cluster. Exception This is site-specific setting. scored: false - id: 5.3.2 text: "Ensure that all Namespaces have Network Policies defined" type: "manual" remediation: | Follow the documentation and create NetworkPolicy objects as you need them. Exception This is site-specific setting. scored: false - id: 5.4 text: "Secrets Management" checks: - id: 5.4.1 text: "Prefer using secrets as files over secrets as environment variables" type: "manual" remediation: | if possible, rewrite application code to read secrets from mounted secret files, rather than from environment variables. Exception This is site-specific setting. scored: false - id: 5.4.2 text: "Consider external secret storage" type: "manual" remediation: | Refer to the secrets management options offered by your cloud provider or a third-party secrets management solution. Exception This is site-specific setting. scored: false - id: 5.5 text: "Extensible Admission Control" checks: - id: 5.5.1 text: "Configure Image Provenance using ImagePolicyWebhook admission controller" type: "manual" remediation: | Follow the Kubernetes documentation and setup image provenance. Exception This is site-specific setting. scored: false - id: 5.7 text: "General Policies" checks: - id: 5.7.1 text: "Create administrative boundaries between resources using namespaces" type: "manual" remediation: | Follow the documentation and create namespaces for objects in your deployment as you need them. Exception This is site-specific setting. scored: false - id: 5.7.2 text: "Ensure that the seccomp profile is set to docker/default in your pod definitions" type: "manual" remediation: | Seccomp is an alpha feature currently. By default, all alpha features are disabled. So, you would need to enable alpha features in the apiserver by passing "--feature- gates=AllAlpha=true" argument. Edit the /etc/kubernetes/apiserver file on the master node and set the KUBE_API_ARGS parameter to "--feature-gates=AllAlpha=true" KUBE_API_ARGS="--feature-gates=AllAlpha=true" Based on your system, restart the kube-apiserver service. For example: systemctl restart kube-apiserver.service Use annotations to enable the docker/default seccomp profile in your pod definitions. An example is as below: apiVersion: v1 kind: Pod metadata: name: trustworthy-pod annotations: seccomp.security.alpha.kubernetes.io/pod: docker/default spec: containers: - name: trustworthy-container image: sotrustworthy:latest Exception This is site-specific setting. scored: false - id: 5.7.3 text: "Apply Security Context to Your Pods and Containers " type: "manual" remediation: | Follow the Kubernetes documentation and apply security contexts to your pods. For a suggested list of security contexts, you may refer to the CIS Security Benchmark for Docker Containers. Exception This is site-specific setting. scored: false - id: 5.7.4 text: "The default namespace should not be used" type: "manual" remediation: | Ensure that namespaces are created to allow for appropriate segregation of Kubernetes resources and that all new resources are created in a specific namespace. Exception This is site-specific setting. scored: false ================================================ FILE: check/check.go ================================================ // Copyright © 2017 Aqua Security Software Ltd. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package check import ( "bytes" "fmt" "os/exec" "strings" "github.com/golang/glog" ) // NodeType indicates the type of node (master, node). type NodeType string // State is the state of a control check. type State string const ( // PASS check passed. PASS State = "PASS" // FAIL check failed. FAIL State = "FAIL" // WARN could not carry out check. WARN State = "WARN" // INFO informational message INFO State = "INFO" // SKIP for when a check should be skipped. SKIP = "skip" // MASTER a master node MASTER NodeType = "master" // NODE a node NODE NodeType = "node" // FEDERATED a federated deployment. FEDERATED NodeType = "federated" // ETCD an etcd node ETCD NodeType = "etcd" // CONTROLPLANE a control plane node CONTROLPLANE NodeType = "controlplane" // POLICIES a node to run policies from POLICIES NodeType = "policies" // MANAGEDSERVICES a node to run managedservices from MANAGEDSERVICES = "managedservices" // MANUAL Check Type MANUAL string = "manual" ) // Check contains information about a recommendation in the // CIS Kubernetes document. type Check struct { ID string `yaml:"id" json:"test_number"` Text string `json:"test_desc"` Audit string `json:"audit"` AuditEnv string `yaml:"audit_env"` AuditConfig string `yaml:"audit_config"` Type string `json:"type"` Tests *tests `json:"-"` Set bool `json:"-"` Remediation string `json:"remediation"` TestInfo []string `json:"test_info"` State `json:"status"` ActualValue string `json:"actual_value"` Scored bool `json:"scored"` IsMultiple bool `yaml:"use_multiple_values"` ExpectedResult string `json:"expected_result"` Reason string `json:"reason,omitempty"` AuditOutput string `json:"-"` AuditEnvOutput string `json:"-"` AuditConfigOutput string `json:"-"` DisableEnvTesting bool `json:"-"` } // Runner wraps the basic Run method. type Runner interface { // Run runs a given check and returns the execution state. Run(c *Check) State } // NewRunner constructs a default Runner. func NewRunner() Runner { return &defaultRunner{} } type defaultRunner struct{} func (r *defaultRunner) Run(c *Check) State { return c.run() } // Run executes the audit commands specified in a check and outputs // the results. func (c *Check) run() State { glog.V(3).Infof("----- Running check %v -----", c.ID) // Since this is an Scored check // without tests return a 'WARN' to alert // the user that this check needs attention if c.Scored && strings.TrimSpace(c.Type) == "" && c.Tests == nil { c.Reason = "There are no tests" c.State = WARN glog.V(3).Info(c.Reason) return c.State } // If check type is skip, force result to INFO if c.Type == SKIP { c.Reason = "Test marked as skip" c.State = INFO glog.V(3).Info(c.Reason) return c.State } // If check type is manual force result to WARN if c.Type == MANUAL { c.Reason = "Test marked as a manual test" c.State = WARN glog.V(3).Info(c.Reason) return c.State } // If there aren't any tests defined this is a FAIL or WARN if c.Tests == nil || len(c.Tests.TestItems) == 0 { c.Reason = "No tests defined" if c.Scored { c.State = FAIL } else { c.State = WARN } glog.V(3).Info(c.Reason) return c.State } // Command line parameters override the setting in the config file, so if we get a good result from the Audit command that's all we need to run var finalOutput *testOutput var lastCommand string lastCommand, err := c.runAuditCommands() if err == nil { finalOutput, err = c.execute() } if finalOutput != nil { if finalOutput.testResult { c.State = PASS } else { if c.Scored { c.State = FAIL } else { c.State = WARN } } c.ActualValue = finalOutput.actualResult c.ExpectedResult = finalOutput.ExpectedResult } if err != nil { c.Reason = err.Error() if c.Scored { c.State = FAIL } else { c.State = WARN } glog.V(3).Info(c.Reason) } if finalOutput != nil { glog.V(3).Infof("Command: %q TestResult: %t State: %q \n", lastCommand, finalOutput.testResult, c.State) } else { glog.V(3).Infof("Command: %q TestResult: <> \n", lastCommand) } if c.Reason != "" { glog.V(2).Info(c.Reason) } return c.State } func (c *Check) runAuditCommands() (lastCommand string, err error) { // Always run auditEnvOutput if needed if c.AuditEnv != "" { c.AuditEnvOutput, err = runAudit(c.AuditEnv) if err != nil { return c.AuditEnv, err } } // Run the audit command and auditConfig commands, if present c.AuditOutput, err = runAudit(c.Audit) if err != nil { return c.Audit, err } c.AuditConfigOutput, err = runAudit(c.AuditConfig) // when file not found then error comes as exit status 127 // in some env same error comes as exit status 1 if err != nil && (strings.Contains(err.Error(), "exit status 127") || strings.Contains(err.Error(), "No such file or directory")) && (c.AuditEnvOutput != "" || c.AuditOutput != "") { // suppress file not found error when there is Audit OR auditEnv output present glog.V(3).Info(err) err = nil c.AuditConfigOutput = "" } return c.AuditConfig, err } func (c *Check) execute() (finalOutput *testOutput, err error) { finalOutput = &testOutput{} ts := c.Tests res := make([]testOutput, len(ts.TestItems)) expectedResultArr := make([]string, len(res)) glog.V(3).Infof("Running %d test_items", len(ts.TestItems)) for i, t := range ts.TestItems { t.isMultipleOutput = c.IsMultiple // Try with the auditOutput first, and if that's not found, try the auditConfigOutput t.auditUsed = AuditCommand result := *(t.execute(c.AuditOutput)) // Check for AuditConfigOutput only if AuditConfig is set and auditConfigOutput is not empty if !result.flagFound && c.AuditConfig != "" && c.AuditConfigOutput != "" { // t.isConfigSetting = true t.auditUsed = AuditConfig result = *(t.execute(c.AuditConfigOutput)) if !result.flagFound && t.Env != "" { t.auditUsed = AuditEnv result = *(t.execute(c.AuditEnvOutput)) } } if !result.flagFound && t.Env != "" { t.auditUsed = AuditEnv result = *(t.execute(c.AuditEnvOutput)) } glog.V(2).Infof("Used %s", t.auditUsed) res[i] = result expectedResultArr[i] = res[i].ExpectedResult } var result bool // If no binary operation is specified, default to AND switch ts.BinOp { default: glog.V(2).Info(fmt.Sprintf("unknown binary operator for tests %s\n", ts.BinOp)) finalOutput.actualResult = fmt.Sprintf("unknown binary operator for tests %s\n", ts.BinOp) return finalOutput, fmt.Errorf("unknown binary operator for tests %s", ts.BinOp) case and, "": result = true for i := range res { result = result && res[i].testResult } // Generate an AND expected result finalOutput.ExpectedResult = strings.Join(expectedResultArr, " AND ") case or: result = false for i := range res { result = result || res[i].testResult } // Generate an OR expected result finalOutput.ExpectedResult = strings.Join(expectedResultArr, " OR ") } finalOutput.testResult = result finalOutput.actualResult = res[0].actualResult glog.V(3).Infof("Returning from execute on tests: finalOutput %#v", finalOutput) return finalOutput, nil } func runAudit(audit string) (output string, err error) { var out bytes.Buffer audit = strings.TrimSpace(audit) if len(audit) == 0 { return output, err } cmd := exec.Command("/bin/sh") cmd.Stdin = strings.NewReader(audit) cmd.Stdout = &out cmd.Stderr = &out err = cmd.Run() output = out.String() if err != nil { err = fmt.Errorf("failed to run: %q, output: %q, error: %s", audit, output, err) } else { glog.V(3).Infof("Command: %q", audit) glog.V(3).Infof("Output:\n %q", output) } return output, err } ================================================ FILE: check/check_test.go ================================================ // Copyright © 2017-2020 Aqua Security Software Ltd. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package check import ( "strings" "testing" ) func TestCheck_Run(t *testing.T) { type TestCase struct { name string check Check Expected State } testCases := []TestCase{ {name: "Manual check should WARN", check: Check{Type: MANUAL}, Expected: WARN}, {name: "Skip check should INFO", check: Check{Type: "skip"}, Expected: INFO}, {name: "Unscored check (with no type) should WARN on failure", check: Check{Scored: false}, Expected: WARN}, { name: "Unscored check that pass should PASS", check: Check{ Scored: false, Audit: "echo hello", Tests: &tests{TestItems: []*testItem{{ Flag: "hello", Set: true, }}}, }, Expected: PASS, }, {name: "Check with no tests should WARN", check: Check{Scored: true}, Expected: WARN}, {name: "Scored check with empty tests should FAIL", check: Check{Scored: true, Tests: &tests{}}, Expected: FAIL}, { name: "Scored check that doesn't pass should FAIL", check: Check{ Scored: true, Audit: "echo hello", Tests: &tests{TestItems: []*testItem{{ Flag: "hello", Set: false, }}}, }, Expected: FAIL, }, { name: "Scored checks that pass should PASS", check: Check{ Scored: true, Audit: "echo hello", Tests: &tests{TestItems: []*testItem{{ Flag: "hello", Set: true, }}}, }, Expected: PASS, }, { name: "Scored checks that pass should PASS when config file is not present", check: Check{ Scored: true, Audit: "echo hello", AuditConfig: "/test/config.yaml", Tests: &tests{TestItems: []*testItem{{ Flag: "hello", Set: true, }}}, }, Expected: PASS, }, { name: "Scored checks that pass should FAIL when config file is not present", check: Check{ Scored: true, AuditConfig: "/test/config.yaml", Tests: &tests{TestItems: []*testItem{{ Flag: "hello", Set: true, }}}, }, Expected: FAIL, }, } for _, testCase := range testCases { t.Run(testCase.name, func(t *testing.T) { testCase.check.run() if testCase.check.State != testCase.Expected { t.Errorf("expected %s, actual %s", testCase.Expected, testCase.check.State) } }) } } func TestCheckAuditEnv(t *testing.T) { passingCases := []*Check{ controls.Groups[2].Checks[0], controls.Groups[2].Checks[2], controls.Groups[2].Checks[3], controls.Groups[2].Checks[4], } failingCases := []*Check{ controls.Groups[2].Checks[1], controls.Groups[2].Checks[5], controls.Groups[2].Checks[6], } for _, c := range passingCases { t.Run(c.Text, func(t *testing.T) { c.run() if c.State != "PASS" { t.Errorf("Should PASS, got: %v", c.State) } }) } for _, c := range failingCases { t.Run(c.Text, func(t *testing.T) { c.run() if c.State != "FAIL" { t.Errorf("Should FAIL, got: %v", c.State) } }) } } func TestCheckAuditConfig(t *testing.T) { passingCases := []*Check{ controls.Groups[1].Checks[0], controls.Groups[1].Checks[3], controls.Groups[1].Checks[5], controls.Groups[1].Checks[7], controls.Groups[1].Checks[9], controls.Groups[1].Checks[15], } failingCases := []*Check{ controls.Groups[1].Checks[1], controls.Groups[1].Checks[2], controls.Groups[1].Checks[4], controls.Groups[1].Checks[6], controls.Groups[1].Checks[8], controls.Groups[1].Checks[10], controls.Groups[1].Checks[11], controls.Groups[1].Checks[12], controls.Groups[1].Checks[13], controls.Groups[1].Checks[14], controls.Groups[1].Checks[16], } for _, c := range passingCases { t.Run(c.Text, func(t *testing.T) { c.run() if c.State != "PASS" { t.Errorf("Should PASS, got: %v", c.State) } }) } for _, c := range failingCases { t.Run(c.Text, func(t *testing.T) { c.run() if c.State != "FAIL" { t.Errorf("Should FAIL, got: %v", c.State) } }) } } func Test_runAudit(t *testing.T) { type args struct { audit string output string } tests := []struct { name string args args errMsg string output string }{ { name: "run success", args: args{ audit: "echo 'hello world'", }, errMsg: "", output: "hello world\n", }, { name: "run multiple lines script", args: args{ audit: ` hello() { echo "hello world" } hello `, }, errMsg: "", output: "hello world\n", }, { name: "run failed", args: args{ audit: "unknown_command", }, errMsg: "failed to run: \"unknown_command\", output: \"/bin/sh: ", output: "not found\n", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { var errMsg string output, err := runAudit(tt.args.audit) if err != nil { errMsg = err.Error() } if errMsg != "" && !strings.Contains(errMsg, tt.errMsg) { t.Errorf("name %s errMsg = %q, want %q", tt.name, errMsg, tt.errMsg) } if errMsg == "" && output != tt.output { t.Errorf("name %s output = %q, want %q", tt.name, output, tt.output) } if errMsg != "" && !strings.Contains(output, tt.output) { t.Errorf("name %s output = %q, want %q", tt.name, output, tt.output) } }) } } ================================================ FILE: check/controls.go ================================================ // Copyright © 2017 Aqua Security Software Ltd. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package check import ( "bytes" "encoding/json" "encoding/xml" "fmt" "time" "github.com/aws/aws-sdk-go-v2/aws" "github.com/aws/aws-sdk-go-v2/service/securityhub/types" "github.com/golang/glog" "github.com/onsi/ginkgo/reporters" "github.com/spf13/viper" "gopkg.in/yaml.v2" ) const ( // UNKNOWN is when the AWS account can't be found UNKNOWN = "Unknown" // ARN for the AWS Security Hub service ARN = "arn:aws:securityhub:%s::product/aqua-security/kube-bench" // SCHEMA for the AWS Security Hub service SCHEMA = "2018-10-08" // TYPE is type of Security Hub finding TYPE = "Software and Configuration Checks/Industry and Regulatory Standards/CIS Kubernetes Benchmark" ) type OverallControls struct { Controls []*Controls Totals Summary } // Controls holds all controls to check for master nodes. type Controls struct { ID string `yaml:"id" json:"id"` Version string `json:"version"` DetectedVersion string `json:"detected_version,omitempty"` Text string `json:"text"` Type NodeType `json:"node_type"` Groups []*Group `json:"tests"` Summary } // Group is a collection of similar checks. type Group struct { ID string `yaml:"id" json:"section"` Type string `yaml:"type" json:"type"` Pass int `json:"pass"` Fail int `json:"fail"` Warn int `json:"warn"` Info int `json:"info"` Text string `json:"desc"` Checks []*Check `json:"results"` } // Summary is a summary of the results of control checks run. type Summary struct { Pass int `json:"total_pass"` Fail int `json:"total_fail"` Warn int `json:"total_warn"` Info int `json:"total_info"` } // Predicate a predicate on the given Group and Check arguments. type Predicate func(group *Group, check *Check) bool // NewControls instantiates a new master Controls object. func NewControls(t NodeType, in []byte, detectedVersion string) (*Controls, error) { c := new(Controls) err := yaml.Unmarshal(in, c) if err != nil { return nil, fmt.Errorf("failed to unmarshal YAML: %s", err) } if t != c.Type { return nil, fmt.Errorf("non-%s controls file specified", t) } c.DetectedVersion = detectedVersion return c, nil } // RunChecks runs the checks with the given Runner. Only checks for which the filter Predicate returns `true` will run. func (controls *Controls) RunChecks(runner Runner, filter Predicate, skipIDMap map[string]bool) Summary { var g []*Group m := make(map[string]*Group) controls.Summary.Pass, controls.Summary.Fail, controls.Summary.Warn, controls.Info = 0, 0, 0, 0 for _, group := range controls.Groups { for _, check := range group.Checks { if !filter(group, check) { continue } _, groupSkippedViaCmd := skipIDMap[group.ID] _, checkSkippedViaCmd := skipIDMap[check.ID] if group.Type == SKIP || groupSkippedViaCmd || checkSkippedViaCmd { check.Type = SKIP } state := runner.Run(check) check.TestInfo = append(check.TestInfo, check.Remediation) // Check if we have already added this checks group. if v, ok := m[group.ID]; !ok { // Create a group with same info w := &Group{ ID: group.ID, Text: group.Text, Checks: []*Check{}, } // Add this check to the new group w.Checks = append(w.Checks, check) summarizeGroup(w, state) // Add to groups we have visited. m[w.ID] = w g = append(g, w) } else { v.Checks = append(v.Checks, check) summarizeGroup(v, state) } summarize(controls, state) } } controls.Groups = g return controls.Summary } // JSON encodes the results of last run to JSON. func (controls *Controls) JSON() ([]byte, error) { return json.Marshal(controls) } // JUnit encodes the results of last run to JUnit. func (controls *Controls) JUnit() ([]byte, error) { suite := reporters.JUnitTestSuite{ Name: controls.Text, TestCases: []reporters.JUnitTestCase{}, Tests: controls.Summary.Pass + controls.Summary.Fail + controls.Summary.Info + controls.Summary.Warn, Failures: controls.Summary.Fail, } for _, g := range controls.Groups { for _, check := range g.Checks { jsonCheck := "" jsonBytes, err := json.Marshal(check) if err != nil { jsonCheck = fmt.Sprintf("Failed to marshal test into JSON: %v. Test as text: %#v", err, check) } else { jsonCheck = string(jsonBytes) } tc := reporters.JUnitTestCase{ Name: fmt.Sprintf("%v %v", check.ID, check.Text), ClassName: g.Text, // Store the entire json serialization as system out so we don't lose data in cases where deeper debugging is necessary. SystemOut: jsonCheck, } switch check.State { case FAIL: tc.FailureMessage = &reporters.JUnitFailureMessage{Message: check.Remediation} case WARN, INFO: // WARN and INFO are two different versions of skipped tests. Either way it would be a false positive/negative to report // it any other way. tc.Skipped = &reporters.JUnitSkipped{} case PASS: default: glog.Warningf("Unrecognized state %s", check.State) } suite.TestCases = append(suite.TestCases, tc) } } var b bytes.Buffer encoder := xml.NewEncoder(&b) encoder.Indent("", " ") err := encoder.Encode(suite) if err != nil { return nil, fmt.Errorf("Failed to generate JUnit report: %s", err.Error()) } return b.Bytes(), nil } // ASFF encodes the results of last run to AWS Security Finding Format(ASFF). func (controls *Controls) ASFF() ([]types.AwsSecurityFinding, error) { fs := []types.AwsSecurityFinding{} account, err := getConfig("AWS_ACCOUNT") if err != nil { return nil, err } cluster, err := getConfig("CLUSTER_ARN") if err != nil { return nil, err } region, err := getConfig("AWS_REGION") if err != nil { return nil, err } nodeName, _ := getConfig("NODE_NAME") arn := fmt.Sprintf(ARN, region) ti := time.Now() tf := ti.Format(time.RFC3339) for _, g := range controls.Groups { for _, check := range g.Checks { if check.State == FAIL || check.State == WARN { // ASFF ProductFields['Actual result'] can't be longer than 1024 characters actualValue := check.ActualValue remediation := check.Remediation reason := check.Reason if len(check.ActualValue) > 1024 { actualValue = check.ActualValue[0:1023] } // Fix issue https://github.com/aquasecurity/kube-bench/issues/903 if len(check.Remediation) > 512 { remediation = check.Remediation[0:511] } if len(check.Reason) > 1024 { reason = check.Reason[0:1023] } id := aws.String(fmt.Sprintf("%s%sEKSnodeID+%s+%s", arn, account, check.ID, cluster)) if nodeName != "" { id = aws.String(fmt.Sprintf("%s%sEKSnodeID+%s+%s+%s", arn, account, check.ID, cluster, nodeName)) } f := types.AwsSecurityFinding{ AwsAccountId: aws.String(account), Confidence: aws.Int32(100), GeneratorId: aws.String(fmt.Sprintf("%s/cis-kubernetes-benchmark/%s/%s", arn, controls.Version, check.ID)), Id: id, CreatedAt: aws.String(tf), Description: aws.String(check.Text), ProductArn: aws.String(arn), SchemaVersion: aws.String(SCHEMA), Title: aws.String(fmt.Sprintf("%s %s", check.ID, check.Text)), UpdatedAt: aws.String(tf), Types: []string{*aws.String(TYPE)}, Severity: &types.Severity{ Label: types.SeverityLabelHigh, }, Remediation: &types.Remediation{ Recommendation: &types.Recommendation{ Text: aws.String(remediation), }, }, ProductFields: map[string]string{ "Reason": reason, "Actual result": actualValue, "Expected result": check.ExpectedResult, "Section": fmt.Sprintf("%s %s", controls.ID, controls.Text), "Subsection": fmt.Sprintf("%s %s", g.ID, g.Text), }, Resources: []types.Resource{ { Id: aws.String(cluster), Type: aws.String(TYPE), }, }, } fs = append(fs, f) } } } return fs, nil } func getConfig(name string) (string, error) { r := viper.GetString(name) if len(r) == 0 { return "", fmt.Errorf("%s not set", name) } return r, nil } func summarize(controls *Controls, state State) { switch state { case PASS: controls.Summary.Pass++ case FAIL: controls.Summary.Fail++ case WARN: controls.Summary.Warn++ case INFO: controls.Summary.Info++ default: glog.Warningf("Unrecognized state %s", state) } } func summarizeGroup(group *Group, state State) { switch state { case PASS: group.Pass++ case FAIL: group.Fail++ case WARN: group.Warn++ case INFO: group.Info++ default: glog.Warningf("Unrecognized state %s", state) } } ================================================ FILE: check/controls_test.go ================================================ // Copyright © 2017-2019 Aqua Security Software Ltd. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package check import ( "bytes" "encoding/json" "encoding/xml" "fmt" "os" "path/filepath" "reflect" "testing" "github.com/aws/aws-sdk-go-v2/aws" "github.com/aws/aws-sdk-go-v2/service/securityhub/types" "github.com/onsi/ginkgo/reporters" "github.com/spf13/viper" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" "gopkg.in/yaml.v2" ) const cfgDir = "../cfg/" type mockRunner struct { mock.Mock } func (m *mockRunner) Run(c *Check) State { args := m.Called(c) return args.Get(0).(State) } // validate that the files we're shipping are valid YAML func TestYamlFiles(t *testing.T) { err := filepath.Walk(cfgDir, func(path string, info os.FileInfo, err error) error { if err != nil { t.Fatalf("failure accessing path %q: %v\n", path, err) } if !info.IsDir() { t.Logf("reading file: %s", path) in, err := os.ReadFile(path) if err != nil { t.Fatalf("error opening file %s: %v", path, err) } c := new(Controls) err = yaml.Unmarshal(in, c) if err == nil { t.Logf("YAML file successfully unmarshalled: %s", path) } else { t.Fatalf("failed to load YAML from %s: %v", path, err) } } return nil }) if err != nil { t.Fatalf("failure walking cfg dir: %v\n", err) } } func TestNewControls(t *testing.T) { t.Run("Should return error when node type is not specified", func(t *testing.T) { // given in := []byte(` --- controls: type: # not specified groups: `) // when _, err := NewControls(MASTER, in, "") // then assert.EqualError(t, err, "non-master controls file specified") }) t.Run("Should return error when input YAML is invalid", func(t *testing.T) { // given in := []byte("BOOM") // when _, err := NewControls(MASTER, in, "") // then assert.EqualError(t, err, "failed to unmarshal YAML: yaml: unmarshal errors:\n line 1: cannot unmarshal !!str `BOOM` into check.Controls") }) } func TestControls_RunChecks_SkippedCmd(t *testing.T) { t.Run("Should skip checks and groups specified by skipMap", func(t *testing.T) { // given normalRunner := &defaultRunner{} // and in := []byte(` --- type: "master" groups: - id: G1 checks: - id: G1/C1 - id: G1/C2 - id: G1/C3 - id: G2 checks: - id: G2/C1 - id: G2/C2 `) controls, err := NewControls(MASTER, in, "") assert.NoError(t, err) var allChecks Predicate = func(group *Group, c *Check) bool { return true } skipMap := make(map[string]bool, 0) skipMap["G1"] = true skipMap["G2/C1"] = true skipMap["G2/C2"] = true controls.RunChecks(normalRunner, allChecks, skipMap) G1 := controls.Groups[0] assertEqualGroupSummary(t, 0, 0, 3, 0, G1) G2 := controls.Groups[1] assertEqualGroupSummary(t, 0, 0, 2, 0, G2) }) } func TestControls_RunChecks_Skipped(t *testing.T) { t.Run("Should skip checks where the parent group is marked as skip", func(t *testing.T) { // given normalRunner := &defaultRunner{} // and in := []byte(` --- type: "master" groups: - id: G1 type: skip checks: - id: G1/C1 `) controls, err := NewControls(MASTER, in, "") assert.NoError(t, err) var allChecks Predicate = func(group *Group, c *Check) bool { return true } emptySkipList := make(map[string]bool, 0) controls.RunChecks(normalRunner, allChecks, emptySkipList) G1 := controls.Groups[0] assertEqualGroupSummary(t, 0, 0, 1, 0, G1) }) } func TestControls_RunChecks(t *testing.T) { t.Run("Should run checks matching the filter and update summaries", func(t *testing.T) { // given runner := new(mockRunner) // and in := []byte(` --- type: "master" groups: - id: G1 checks: - id: G1/C1 - id: G2 checks: - id: G2/C1 text: "Verify that the SomeSampleFlag argument is set to true" audit: "grep -B1 SomeSampleFlag=true /this/is/a/file/path" tests: test_items: - flag: "SomeSampleFlag=true" compare: op: has value: "true" set: true remediation: | Edit the config file /this/is/a/file/path and set SomeSampleFlag to true. scored: true `) // and controls, err := NewControls(MASTER, in, "") assert.NoError(t, err) // and runner.On("Run", controls.Groups[0].Checks[0]).Return(PASS) runner.On("Run", controls.Groups[1].Checks[0]).Return(FAIL) // and var runAll Predicate = func(group *Group, c *Check) bool { return true } var emptySkipList = make(map[string]bool, 0) // when controls.RunChecks(runner, runAll, emptySkipList) // then assert.Equal(t, 2, len(controls.Groups)) // and G1 := controls.Groups[0] assert.Equal(t, "G1", G1.ID) assert.Equal(t, "G1/C1", G1.Checks[0].ID) assertEqualGroupSummary(t, 1, 0, 0, 0, G1) // and G2 := controls.Groups[1] assert.Equal(t, "G2", G2.ID) assert.Equal(t, "G2/C1", G2.Checks[0].ID) assert.Equal(t, "has", G2.Checks[0].Tests.TestItems[0].Compare.Op) assert.Equal(t, "true", G2.Checks[0].Tests.TestItems[0].Compare.Value) assert.Equal(t, true, G2.Checks[0].Tests.TestItems[0].Set) assert.Equal(t, "SomeSampleFlag=true", G2.Checks[0].Tests.TestItems[0].Flag) assert.Equal(t, "Edit the config file /this/is/a/file/path and set SomeSampleFlag to true.\n", G2.Checks[0].Remediation) assert.Equal(t, true, G2.Checks[0].Scored) assertEqualGroupSummary(t, 0, 1, 0, 0, G2) // and assert.Equal(t, 1, controls.Summary.Pass) assert.Equal(t, 1, controls.Summary.Fail) assert.Equal(t, 0, controls.Summary.Info) assert.Equal(t, 0, controls.Summary.Warn) // and runner.AssertExpectations(t) }) } func TestControls_JUnitIncludesJSON(t *testing.T) { testCases := []struct { desc string input *Controls expect []byte }{ { desc: "Serializes to junit", input: &Controls{ Groups: []*Group{ { ID: "g1", Checks: []*Check{ {ID: "check1id", Text: "check1text", State: PASS}, }, }, }, }, expect: []byte(` {"test_number":"check1id","test_desc":"check1text","audit":"","AuditEnv":"","AuditConfig":"","type":"","remediation":"","test_info":null,"status":"PASS","actual_value":"","scored":false,"IsMultiple":false,"expected_result":""} `), }, { desc: "Summary values come from summary not checks", input: &Controls{ Summary: Summary{ Fail: 99, Pass: 100, Warn: 101, Info: 102, }, Groups: []*Group{ { ID: "g1", Checks: []*Check{ {ID: "check1id", Text: "check1text", State: PASS}, }, }, }, }, expect: []byte(` {"test_number":"check1id","test_desc":"check1text","audit":"","AuditEnv":"","AuditConfig":"","type":"","remediation":"","test_info":null,"status":"PASS","actual_value":"","scored":false,"IsMultiple":false,"expected_result":""} `), }, { desc: "Warn and Info are considered skips and failed tests properly reported", input: &Controls{ Groups: []*Group{ { ID: "g1", Checks: []*Check{ {ID: "check1id", Text: "check1text", State: PASS}, {ID: "check2id", Text: "check2text", State: INFO}, {ID: "check3id", Text: "check3text", State: WARN}, {ID: "check4id", Text: "check4text", State: FAIL}, }, }, }, }, expect: []byte(` {"test_number":"check1id","test_desc":"check1text","audit":"","AuditEnv":"","AuditConfig":"","type":"","remediation":"","test_info":null,"status":"PASS","actual_value":"","scored":false,"IsMultiple":false,"expected_result":""} {"test_number":"check2id","test_desc":"check2text","audit":"","AuditEnv":"","AuditConfig":"","type":"","remediation":"","test_info":null,"status":"INFO","actual_value":"","scored":false,"IsMultiple":false,"expected_result":""} {"test_number":"check3id","test_desc":"check3text","audit":"","AuditEnv":"","AuditConfig":"","type":"","remediation":"","test_info":null,"status":"WARN","actual_value":"","scored":false,"IsMultiple":false,"expected_result":""} {"test_number":"check4id","test_desc":"check4text","audit":"","AuditEnv":"","AuditConfig":"","type":"","remediation":"","test_info":null,"status":"FAIL","actual_value":"","scored":false,"IsMultiple":false,"expected_result":""} `), }, } for _, tc := range testCases { t.Run(tc.desc, func(t *testing.T) { junitBytes, err := tc.input.JUnit() if err != nil { t.Fatalf("Failed to serialize to JUnit: %v", err) } var out reporters.JUnitTestSuite if err := xml.Unmarshal(junitBytes, &out); err != nil { t.Fatalf("Unable to deserialize from resulting JUnit: %v", err) } // Check that each check was serialized as json and stored as systemOut. for iGroup, group := range tc.input.Groups { for iCheck, check := range group.Checks { jsonBytes, err := json.Marshal(check) if err != nil { t.Fatalf("Failed to serialize to JUnit: %v", err) } if out.TestCases[iGroup*iCheck+iCheck].SystemOut != string(jsonBytes) { t.Errorf("Expected\n\t%v\n\tbut got\n\t%v", out.TestCases[iGroup*iCheck+iCheck].SystemOut, string(jsonBytes), ) } } } if !bytes.Equal(junitBytes, tc.expect) { t.Errorf("Expected\n\t%v\n\tbut got\n\t%v", string(tc.expect), string(junitBytes), ) } }) } } func assertEqualGroupSummary(t *testing.T, pass, fail, info, warn int, actual *Group) { t.Helper() assert.Equal(t, pass, actual.Pass) assert.Equal(t, fail, actual.Fail) assert.Equal(t, info, actual.Info) assert.Equal(t, warn, actual.Warn) } func TestControls_ASFF(t *testing.T) { type fields struct { ID string Version string Text string Groups []*Group Summary Summary } tests := []struct { name string fields fields want []types.AwsSecurityFinding wantErr bool }{ { name: "Test simple conversion", fields: fields{ ID: "test1", Version: "1", Text: "test runnner", Summary: Summary{ Fail: 99, Pass: 100, Warn: 101, Info: 102, }, Groups: []*Group{ { ID: "g1", Text: "Group text", Checks: []*Check{ {ID: "check1id", Text: "check1text", State: FAIL, Remediation: "fix me", Reason: "failed", ExpectedResult: "failed", ActualValue: "failed", }, }, }, }}, want: []types.AwsSecurityFinding{ { AwsAccountId: aws.String("foo account"), Confidence: aws.Int32(100), GeneratorId: aws.String(fmt.Sprintf("%s/cis-kubernetes-benchmark/%s/%s", fmt.Sprintf(ARN, "somewhere"), "1", "check1id")), Description: aws.String("check1text"), ProductArn: aws.String(fmt.Sprintf(ARN, "somewhere")), SchemaVersion: aws.String(SCHEMA), Title: aws.String(fmt.Sprintf("%s %s", "check1id", "check1text")), Types: []string{*aws.String(TYPE)}, Severity: &types.Severity{ Label: types.SeverityLabelHigh, }, Remediation: &types.Remediation{ Recommendation: &types.Recommendation{ Text: aws.String("fix me"), }, }, ProductFields: map[string]string{ "Reason": "failed", "Actual result": "failed", "Expected result": "failed", "Section": fmt.Sprintf("%s %s", "test1", "test runnner"), "Subsection": fmt.Sprintf("%s %s", "g1", "Group text"), }, Resources: []types.Resource{ { Id: aws.String("foo Cluster"), Type: aws.String(TYPE), }, }, }, }, wantErr: false, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { viper.Set("AWS_ACCOUNT", "foo account") viper.Set("CLUSTER_ARN", "foo Cluster") viper.Set("AWS_REGION", "somewhere") controls := &Controls{ ID: tt.fields.ID, Version: tt.fields.Version, Text: tt.fields.Text, Groups: tt.fields.Groups, Summary: tt.fields.Summary, } got, _ := controls.ASFF() tt.want[0].CreatedAt = got[0].CreatedAt tt.want[0].UpdatedAt = got[0].UpdatedAt tt.want[0].Id = got[0].Id if !reflect.DeepEqual(got, tt.want) { t.Errorf("Controls.ASFF() = %v, want %v", got, tt.want) } }) } } ================================================ FILE: check/data ================================================ --- controls: id: 1 text: "Test Checks" type: "master" groups: - id: 1.1 text: "First Group" checks: - id: 0 text: "flag is set" audit: "echo \"Non empty command\"" tests: test_items: - flag: "--allow-privileged" set: true - id: 1 text: "flag is not set" audit: "echo \"Non empty command\"" tests: test_items: - flag: "--basic-auth" set: false - id: 2 text: "flag value is set to some value" audit: "echo \"Non empty command\"" tests: test_items: - flag: "--insecure-port" compare: op: eq value: 0 set: true - id: 3 text: "flag value is greater than or equal some number" audit: "echo \"Non empty command\"" tests: test_items: - flag: "--audit-log-maxage" compare: op: gte value: 30 set: true - id: 4 text: "flag value is less than some number" audit: "echo \"Non empty command\"" tests: test_items: - flag: "--max-backlog" compare: op: lt value: 30 set: true - id: 5 text: "flag value does not have some value" audit: "echo \"Non empty command\"" tests: test_items: - flag: "--admission-control" compare: op: nothave value: AlwaysAdmit set: true - id: 6 text: "test AND binary operation" audit: "echo \"Non empty command\"" tests: bin_op: and test_items: - flag: "--kubelet-client-certificate" set: true - flag: "--kubelet-clientkey" set: true - id: 7 text: "test OR binary operation" audit: "echo \"Non empty command\"" tests: bin_op: or test_items: - flag: "--secure-port" compare: op: eq value: 0 set: true - flag: "--secure-port" set: false - id: 8 text: "test flag with arbitrary text" audit: "echo \"Non empty command\"" tests: test_items: - flag: "permissions" compare: op: eq value: "SomeValue" set: true - id: 9 text: "test permissions" audit: "/bin/sh -c 'if test -e $config; then stat -c permissions=%a $config; fi'" tests: test_items: - flag: "permissions" compare: op: bitmask value: "644" set: true - id: 10 text: "flag value includes some value in a comma-separated list, value is last in list" audit: "echo \"Non empty command\"" tests: test_items: - flag: "--admission-control" compare: op: has value: RBAC set: true - id: 11 text: "flag value includes some value in a comma-separated list, value is first in list" audit: "echo \"Non empty command\"" tests: test_items: - flag: "--admission-control" compare: op: has value: WebHook set: true - id: 12 text: "flag value includes some value in a comma-separated list, value middle of list" audit: "echo \"Non empty command\"" tests: test_items: - flag: "--admission-control" compare: op: has value: Something set: true - id: 13 text: "flag value includes some value in a comma-separated list, value only one in list" audit: "echo \"Non empty command\"" tests: test_items: - flag: "--admission-control" compare: op: has value: Something set: true - id: 14 text: "check that flag some-arg is set to some-val with ':' separator" audit: "echo \"Non empty command\"" tests: test_items: - flag: "some-arg" compare: op: eq value: some-val set: true - id: 15 text: "jsonpath correct value on field" audit: "echo \"Non empty command\"" audit_config: "echo \"Non empty command\"" tests: bin_op: or test_items: - path: "{.readOnlyPort}" compare: op: eq value: 15000 set: true - path: "{.readOnlyPort}" compare: op: gte value: 15000 set: true - path: "{.readOnlyPort}" compare: op: lte value: 15000 set: true - id: 16 text: "jsonpath correct case-sensitive value on string field" audit: "echo \"Non empty command\"" audit_config: "echo \"Non empty command\"" tests: test_items: - path: "{.stringValue}" compare: op: noteq value: "None" set: true - path: "{.stringValue}" compare: op: noteq value: "webhook,Something,RBAC" set: true - path: "{.stringValue}" compare: op: eq value: "WebHook,Something,RBAC" set: true - id: 17 text: "jsonpath correct value on boolean field" audit: "echo \"Non empty command\"" audit_config: "echo \"Non empty command\"" tests: test_items: - path: "{.trueValue}" compare: op: noteq value: somethingElse set: true - path: "{.trueValue}" compare: op: noteq value: false set: true - path: "{.trueValue}" compare: op: eq value: true set: true - id: 18 text: "jsonpath field absent" audit: "echo \"Non empty command\"" audit_config: "echo \"Non empty command\"" tests: test_items: - path: "{.notARealField}" set: false - id: 19 text: "jsonpath correct value on nested field" audit: "echo \"Non empty command\"" audit_config: "echo \"Non empty command\"" tests: test_items: - path: "{.authentication.anonymous.enabled}" compare: op: eq value: "false" set: true - id: 20 text: "yamlpath correct value on field" audit: "echo \"Non empty command\"" audit_config: "echo \"Non empty command\"" tests: test_items: - path: "{.readOnlyPort}" compare: op: gt value: 14999 set: true - id: 21 text: "yamlpath field absent" audit: "echo \"Non empty command\"" audit_config: "echo \"Non empty command\"" tests: test_items: - path: "{.fieldThatIsUnset}" set: false - id: 22 text: "yamlpath correct value on nested field" audit: "echo \"Non empty command\"" audit_config: "echo \"Non empty command\"" tests: test_items: - path: "{.authentication.anonymous.enabled}" compare: op: eq value: "false" set: true - id: 23 text: "path on invalid json" audit: "echo \"Non empty command\"" audit_config: "echo \"Non empty command\"" tests: test_items: - path: "{.authentication.anonymous.enabled}" compare: op: eq value: "false" set: true - id: 24 text: "path with broken expression" audit: "echo \"Non empty command\"" audit_config: "echo \"Non empty command\"" tests: test_items: - path: "{.missingClosingBrace}" set: true - id: 25 text: "yamlpath on invalid yaml" audit: "echo \"Non empty command\"" tests: test_items: - path: "{.authentication.anonymous.enabled}" compare: op: eq value: "false" set: true - id: 26 text: "check regex op matches" audit: "echo \"Non empty command\"" audit_config: "echo \"Non empty command\"" tests: test_items: - path: "{.currentMasterVersion}" compare: op: regex value: '^1\.12.*$' set: true - id: 27 text: "check boolean flag with no value" audit: "echo \"Non empty command\"" tests: test_items: - flag: "--peer-client-cert-auth" compare: op: eq value: true set: true - id: 28 text: "check boolean flag with false value" audit: "echo \"Non empty command\"" tests: test_items: - flag: "--peer-client-cert-auth" compare: op: eq value: false set: true - id: 29 text: "flag is set (via env)" tests: test_items: - flag: "--allow-privileged" env: "ALLOW_PRIVILEGED" set: true - id: 30 text: "flag is not set (via env)" tests: test_items: - flag: "--basic-auth" env: "BASIC_AUTH" set: false - id: 31 text: "flag value is set to some value (via env)" tests: test_items: - flag: "--insecure-port" env: "INSECURE_PORT" compare: op: eq value: 0 set: true - id: 32 text: "flag value is greater than or equal some number (via env)" tests: test_items: - flag: "--audit-log-maxage" env: "AUDIT_LOG_MAXAGE" compare: op: gte value: 30 set: true - id: 33 text: "flag value is less than some number (via env)" tests: test_items: - env: "MAX_BACKLOG" compare: op: lt value: 30 set: true - id: 2.1 text: "audit and audit_config commands" checks: - id: 0 text: "audit finds flag and passes, audit_config doesn't exist -> pass" audit: "echo flag=correct" tests: test_items: - flag: "flag" compare: op: eq value: "correct" set: true scored: true - id: 1 text: "audit finds flag and fails, audit_config doesn't exist -> fail" audit: "echo flag=wrong" tests: test_items: - flag: "flag" compare: op: eq value: "correct" set: true scored: true - id: 2 text: "audit doesn't find flag, audit_config doesn't exist -> fail" audit: "echo somethingElse=correct" tests: test_items: - flag: "flag" compare: op: eq value: "correct" set: true scored: true - id: 3 text: "audit doesn't find flag, audit_config has correct setting -> pass" audit: "echo somethingElse=correct" audit_config: "echo 'flag: correct'" tests: test_items: - flag: "flag" path: "{.flag}" compare: op: eq value: "correct" set: true scored: true - id: 4 text: "audit doesn't find flag, audit_config has wrong setting -> fail" audit: "echo somethingElse=correct" audit_config: "echo 'flag: wrong'" tests: test_items: - flag: "flag" path: "{.flag}" compare: op: eq value: "correct" set: true scored: true - id: 5 text: "audit finds correct flag, audit_config has wrong setting -> pass" audit: "echo flag=correct" audit_config: "echo 'flag: wrong'" tests: test_items: - flag: "flag" path: "{.flag}" compare: op: eq value: "correct" set: true scored: true - id: 6 text: "neither audit nor audit_config has correct setting -> fail" audit: "echo flag=wrong" audit_config: "echo 'flag: wrong'" tests: test_items: - flag: "flag" path: "{.flag}" compare: op: eq value: "correct" set: true scored: true - id: 7 text: "audit isn't present, superfluous flag field,audit_config is correct -> pass" audit_config: "echo 'flag: correct'" tests: test_items: - flag: "flag" path: "{.flag}" compare: op: eq value: "correct" set: true scored: true - id: 8 text: "audit isn't present, superfluous flag field,audit_config is wrong -> fail" audit_config: "echo 'flag: wrong'" tests: test_items: - flag: "flag" path: "{.flag}" compare: op: eq value: "correct" set: true scored: true - id: 9 text: "test use_multiple_values is correct -> pass" audit: "printf 'permissions=600\npermissions=600\npermissions=600'" use_multiple_values: true tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" set: true scored: true - id: 10 text: "test use_multiple_values is wrong -> fail" audit: "printf 'permissions=600\npermissions=600\npermissions=644'" use_multiple_values: true tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" set: true scored: true - id: 11 text: "test use_multiple_values include empty value -> fail" audit: "printf 'permissions=600\n\npermissions=600'" use_multiple_values: true tests: test_items: - flag: "permissions" compare: op: bitmask value: "600" set: true scored: true - id: 12 text: "audit is present and wrong, audit_config is right -> fail (command line parameters override config file)" audit: "echo flag=wrong" audit_config: "echo 'flag: correct'" tests: test_items: - flag: "flag" path: "{.flag}" compare: op: eq value: "correct" set: true scored: true - id: 13 text: "parameter and config file don't have same default - parameter has failing value" audit: "echo '--read-only-port=1'" audit_config: "echo 'readOnlyPort: 0'" tests: bin_op: and test_items: - flag: "--read-only-port" path: "{.readOnlyPort}" set: true compare: op: eq value: 0 - flag: "--read-only-port" path: '{.readOnlyPort}' set: false scored: true - id: 14 text: "parameter and config file don't have same default - config file has failing value" audit: "echo ''" audit_config: "echo 'readOnlyPort: 1'" tests: bin_op: or test_items: - flag: "--read-only-port" path: '{.readOnlyPort}' set: true compare: op: eq value: 0 - flag: "--read-only-port" path: '{.readOnlyPort}' set: false scored: true - id: 15 text: "parameter and config file don't have same default - passing" audit: "echo ''" audit_config: "echo ''" tests: bin_op: or test_items: - flag: "--read-only-port" path: '{.readOnlyPort}' set: true compare: op: eq value: 0 - flag: "--read-only-port" path: '{.readOnlyPort}' set: false scored: true - id: 16 text: "parameter and config file don't have same default - parameter has bad value and config is not present - failing" audit: "echo '--read-only-port=1'" audit_config: "echo ''" tests: bin_op: or test_items: - flag: "--read-only-port" path: '{.readOnlyPort}' set: true compare: op: eq value: 0 - flag: "--read-only-port" path: '{.readOnlyPort}' set: false scored: true - id: 3.1 text: "audit_env commands" checks: - id: 0 text: "audit fails to find flag, audit_env finds flag -> pass" audit: "echo in=incorrect" audit_env: "echo flag=correct" tests: test_items: - flag: "flag" env: "flag" compare: op: eq value: "correct" set: true scored: true - id: 1 text: "audit fails to find flag, audit_env finds flag and fails -> fail" audit: "echo in=wrong" audit_env: "echo flag=wrong" tests: test_items: - flag: "flag" env: "flag" compare: op: eq value: "correct" set: true scored: true - id: 2 text: "audit finds correct flag, audit_env is incorrect -> pass" audit: "echo flag=correct" audit_env: "echo flag=incorrect" tests: test_items: - flag: "flag" env: "flag" compare: op: eq value: "correct" set: true scored: true - id: 3 text: "audit doesn't flag flag, audit_config finds it and passes, audit_env is not present -> pass" audit: "echo in=correct" audit_config: "echo 'flag: correct'" tests: test_items: - flag: "flag" path: "{.flag}" compare: op: eq value: "correct" set: true scored: true - id: 4 text: "audit doesn't flag flag, audit_config doesn't find flag, audit_env finds and passes -> pass" audit: "echo in=correct" audit_config: "echo 'in: correct'" audit_env: "echo flag=correct" tests: test_items: - flag: "flag" path: "{.flag}" env: "flag" compare: op: eq value: "correct" set: true scored: true - id: 5 text: "audit doesn't find flag, audit_config doesn't find flag, audit_env finds and fails -> fails" audit: "echo in=correct" audit_config: "echo 'in: correct'" audit_env: "echo flag=incorrect" tests: test_items: - flag: "flag" path: "{.flag}" env: "flag" compare: op: eq value: "correct" set: true scored: true - id: 6 text: "audit finds flag and fails, audit_config finds flag and fails, audit_env finds and passes -> fails" audit: "echo flag=incorrect" audit_config: "echo 'flag: incorrect'" audit_env: "echo flag=correct" tests: test_items: - flag: "flag" path: "{.flag}" env: "flag" compare: op: eq value: "correct" set: true scored: true ================================================ FILE: check/test.go ================================================ // Copyright © 2017 Aqua Security Software Ltd. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package check import ( "bytes" "encoding/json" "fmt" "os" "regexp" "strconv" "strings" "github.com/golang/glog" "gopkg.in/yaml.v2" "k8s.io/client-go/util/jsonpath" ) // test: // flag: OPTION // set: (true|false) // compare: // op: (eq|gt|gte|lt|lte|has) // value: val type binOp string const ( and binOp = "and" or = "or" defaultArraySeparator = "," ) type tests struct { TestItems []*testItem `yaml:"test_items"` BinOp binOp `yaml:"bin_op"` } type AuditUsed string const ( AuditCommand AuditUsed = "auditCommand" AuditConfig AuditUsed = "auditConfig" AuditEnv AuditUsed = "auditEnv" ) type testItem struct { Flag string Env string Path string Output string Value string Set bool Compare compare isMultipleOutput bool auditUsed AuditUsed } type ( envTestItem testItem pathTestItem testItem flagTestItem testItem ) type compare struct { Op string Value string } type testOutput struct { testResult bool flagFound bool actualResult string ExpectedResult string } func failTestItem(s string) *testOutput { return &testOutput{testResult: false, actualResult: s} } func (t testItem) value() string { if t.auditUsed == AuditConfig { return t.Path } if t.auditUsed == AuditEnv { return t.Env } return t.Flag } func (t testItem) findValue(s string) (match bool, value string, err error) { if t.auditUsed == AuditEnv { et := envTestItem(t) return et.findValue(s) } if t.auditUsed == AuditConfig { pt := pathTestItem(t) return pt.findValue(s) } ft := flagTestItem(t) return ft.findValue(s) } func (t flagTestItem) findValue(s string) (match bool, value string, err error) { if s == "" || t.Flag == "" { return } match = strings.Contains(s, t.Flag) if match { // Expects flags in the form; // --flag=somevalue // flag: somevalue // --flag // somevalue // DOESN'T COVER - use pathTestItem implementation of findValue() for this // flag: // - wehbook pttn := `(` + t.Flag + `)(=|: *)*([^\s]*) *` flagRe := regexp.MustCompile(pttn) vals := flagRe.FindStringSubmatch(s) if len(vals) > 0 { if vals[3] != "" { value = vals[3] } else { // --bool-flag if strings.HasPrefix(t.Flag, "--") { value = "true" } else { value = vals[1] } } } else { err = fmt.Errorf("invalid flag in testItem definition: %s", s) } } glog.V(3).Infof("In flagTestItem.findValue %s", value) return match, value, err } func (t pathTestItem) findValue(s string) (match bool, value string, err error) { var jsonInterface interface{} err = unmarshal(s, &jsonInterface) if err != nil { return false, "", fmt.Errorf("failed to load YAML or JSON from input \"%s\": %v", s, err) } value, err = executeJSONPath(t.Path, &jsonInterface) if err != nil { return false, "", fmt.Errorf("unable to parse path expression \"%s\": %v", t.Path, err) } glog.V(3).Infof("In pathTestItem.findValue %s", value) match = value != "" return match, value, err } func (t envTestItem) findValue(s string) (match bool, value string, err error) { if s != "" && t.Env != "" { r, _ := regexp.Compile(fmt.Sprintf("%s=.*(?:$|\\n)", t.Env)) out := r.FindString(s) out = strings.Replace(out, "\n", "", 1) out = strings.Replace(out, fmt.Sprintf("%s=", t.Env), "", 1) if len(out) > 0 { match = true value = out } else { match = false value = "" } } glog.V(3).Infof("In envTestItem.findValue %s", value) return match, value, nil } func (t testItem) execute(s string) *testOutput { result := &testOutput{} s = strings.TrimRight(s, " \n") // If the test has output that should be evaluated for each row var output []string if t.isMultipleOutput { output = strings.Split(s, "\n") } else { output = []string{s} } for _, op := range output { result = t.evaluate(op) // If the test failed for the current row, no need to keep testing for this output if !result.testResult { break } } result.actualResult = s return result } func (t testItem) evaluate(s string) *testOutput { result := &testOutput{} match, value, err := t.findValue(s) if err != nil { fmt.Fprint(os.Stderr, err.Error()) return failTestItem(err.Error()) } if t.Set { if match && t.Compare.Op != "" { result.ExpectedResult, result.testResult = compareOp(t.Compare.Op, value, t.Compare.Value, t.value()) } else { result.ExpectedResult = fmt.Sprintf("'%s' is present", t.value()) result.testResult = match } } else { result.ExpectedResult = fmt.Sprintf("'%s' is not present", t.value()) result.testResult = !match } result.flagFound = match isExist := "exists" if !result.flagFound { isExist = "does not exist" } switch t.auditUsed { case AuditCommand: glog.V(3).Infof("Flag '%s' %s", t.Flag, isExist) case AuditConfig: glog.V(3).Infof("Path '%s' %s", t.Path, isExist) case AuditEnv: glog.V(3).Infof("Env '%s' %s", t.Env, isExist) default: glog.V(3).Infof("Error with identify audit used %s", t.auditUsed) } return result } func compareOp(tCompareOp string, flagVal string, tCompareValue string, flagName string) (string, bool) { expectedResultPattern := "" testResult := false switch tCompareOp { case "eq": expectedResultPattern = "'%s' is equal to '%s'" value := strings.ToLower(flagVal) // Do case insensitive comparaison for booleans ... if value == "false" || value == "true" { testResult = value == tCompareValue } else { testResult = flagVal == tCompareValue } case "noteq": expectedResultPattern = "'%s' is not equal to '%s'" value := strings.ToLower(flagVal) // Do case insensitive comparaison for booleans ... if value == "false" || value == "true" { testResult = !(value == tCompareValue) } else { testResult = !(flagVal == tCompareValue) } case "gt", "gte", "lt", "lte": a, b, err := toNumeric(flagVal, tCompareValue) if err != nil { expectedResultPattern = "Invalid Number(s) used for comparison: '%s' '%s'" glog.V(1).Infof(fmt.Sprintf("Not numeric value - flag: %q - compareValue: %q %v\n", flagVal, tCompareValue, err)) return fmt.Sprintf(expectedResultPattern, flagVal, tCompareValue), false } switch tCompareOp { case "gt": expectedResultPattern = "'%s' is greater than %s" testResult = a > b case "gte": expectedResultPattern = "'%s' is greater or equal to %s" testResult = a >= b case "lt": expectedResultPattern = "'%s' is lower than %s" testResult = a < b case "lte": expectedResultPattern = "'%s' is lower or equal to %s" testResult = a <= b } case "has": expectedResultPattern = "'%s' has '%s'" testResult = strings.Contains(flagVal, tCompareValue) case "nothave": expectedResultPattern = "'%s' does not have '%s'" testResult = !strings.Contains(flagVal, tCompareValue) case "regex": expectedResultPattern = "'%s' matched by regex expression '%s'" opRe := regexp.MustCompile(tCompareValue) testResult = opRe.MatchString(flagVal) case "valid_elements": expectedResultPattern = "'%s' contains valid elements from '%s'" s := splitAndRemoveLastSeparator(flagVal, defaultArraySeparator) target := splitAndRemoveLastSeparator(tCompareValue, defaultArraySeparator) testResult = allElementsValid(s, target) case "bitmask": expectedResultPattern = "%s has permissions " + flagVal + ", expected %s or more restrictive" requested, err := strconv.ParseInt(flagVal, 8, 64) if err != nil { glog.V(1).Infof(fmt.Sprintf("Not numeric value - flag: %q - compareValue: %q %v\n", flagVal, tCompareValue, err)) return fmt.Sprintf("Not numeric value - flag: %s", flagVal), false } max, err := strconv.ParseInt(tCompareValue, 8, 64) if err != nil { glog.V(1).Infof(fmt.Sprintf("Not numeric value - flag: %q - compareValue: %q %v\n", flagVal, tCompareValue, err)) return fmt.Sprintf("Not numeric value - flag: %s", tCompareValue), false } testResult = (max & requested) == requested } if expectedResultPattern == "" { return expectedResultPattern, testResult } return fmt.Sprintf(expectedResultPattern, flagName, tCompareValue), testResult } func unmarshal(s string, jsonInterface *interface{}) error { data := []byte(s) err := json.Unmarshal(data, jsonInterface) if err != nil { err := yaml.Unmarshal(data, jsonInterface) if err != nil { return err } } return nil } func executeJSONPath(path string, jsonInterface interface{}) (string, error) { j := jsonpath.New("jsonpath") j.AllowMissingKeys(true) err := j.Parse(path) if err != nil { return "", err } buf := new(bytes.Buffer) err = j.Execute(buf, jsonInterface) if err != nil { return "", err } jsonpathResult := buf.String() return jsonpathResult, nil } func allElementsValid(s, t []string) bool { sourceEmpty := len(s) == 0 targetEmpty := len(t) == 0 if sourceEmpty && targetEmpty { return true } // XOR comparison - // if either value is empty and the other is not empty, // not all elements are valid if (sourceEmpty || targetEmpty) && !(sourceEmpty && targetEmpty) { return false } for _, sv := range s { found := false for _, tv := range t { if sv == tv { found = true break } } if !found { return false } } return true } func splitAndRemoveLastSeparator(s, sep string) []string { cleanS := strings.TrimRight(strings.TrimSpace(s), sep) if len(cleanS) == 0 { return []string{} } ts := strings.Split(cleanS, sep) for i := range ts { ts[i] = strings.TrimSpace(ts[i]) } return ts } func toNumeric(a, b string) (c, d int, err error) { c, err = strconv.Atoi(strings.TrimSpace(a)) if err != nil { return -1, -1, fmt.Errorf("toNumeric - error converting %s: %s", a, err) } d, err = strconv.Atoi(strings.TrimSpace(b)) if err != nil { return -1, -1, fmt.Errorf("toNumeric - error converting %s: %s", b, err) } return c, d, nil } func (t *testItem) UnmarshalYAML(unmarshal func(interface{}) error) error { type buildTest testItem // Make Set parameter to be true by default. newTestItem := buildTest{Set: true} err := unmarshal(&newTestItem) if err != nil { return err } *t = testItem(newTestItem) return nil } ================================================ FILE: check/test_test.go ================================================ // Copyright © 2017-2020 Aqua Security Software Ltd. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package check import ( "fmt" "os" "strings" "testing" ) var ( in []byte controls *Controls ) func init() { var err error in, err = os.ReadFile("data") if err != nil { panic("Failed reading test data: " + err.Error()) } // substitute variables in data file user := os.Getenv("USER") s := strings.Replace(string(in), "$user", user, -1) controls, err = NewControls(MASTER, []byte(s), "") // controls, err = NewControls(MASTER, in) if err != nil { panic("Failed creating test controls: " + err.Error()) } } func TestTestExecute(t *testing.T) { cases := []struct { check *Check str string strConfig string expectedTestResult string strEnv string }{ { check: controls.Groups[0].Checks[0], str: "2:45 ../kubernetes/kube-apiserver --allow-privileged=false --option1=20,30,40", strConfig: "", expectedTestResult: "'--allow-privileged' is present", }, { check: controls.Groups[0].Checks[1], str: "2:45 ../kubernetes/kube-apiserver --allow-privileged=false", strConfig: "", expectedTestResult: "'--basic-auth' is not present", }, { check: controls.Groups[0].Checks[2], str: "niinai 13617 2635 99 19:26 pts/20 00:03:08 ./kube-apiserver --insecure-port=0 --anonymous-auth", strConfig: "", expectedTestResult: "'--insecure-port' is equal to '0'", }, { check: controls.Groups[0].Checks[3], str: "2:45 ../kubernetes/kube-apiserver --secure-port=0 --audit-log-maxage=40 --option", strConfig: "", expectedTestResult: "'--audit-log-maxage' is greater or equal to 30", }, { check: controls.Groups[0].Checks[4], str: "2:45 ../kubernetes/kube-apiserver --max-backlog=20 --secure-port=0 --audit-log-maxage=40 --option", strConfig: "", expectedTestResult: "'--max-backlog' is lower than 30", }, { check: controls.Groups[0].Checks[5], str: "2:45 ../kubernetes/kube-apiserver --option --admission-control=WebHook,RBAC ---audit-log-maxage=40", strConfig: "", expectedTestResult: "'--admission-control' does not have 'AlwaysAdmit'", }, { check: controls.Groups[0].Checks[6], str: "2:45 .. --kubelet-clientkey=foo --kubelet-client-certificate=bar --admission-control=Webhook,RBAC", strConfig: "", expectedTestResult: "'--kubelet-client-certificate' is present AND '--kubelet-clientkey' is present", }, { check: controls.Groups[0].Checks[7], str: "2:45 .. --secure-port=0 --kubelet-client-certificate=bar --admission-control=Webhook,RBAC", strConfig: "", expectedTestResult: "'--secure-port' is equal to '0' OR '--secure-port' is not present", }, { check: controls.Groups[0].Checks[8], str: "permissions=SomeValue", strConfig: "", expectedTestResult: "'permissions' is equal to 'SomeValue'", }, { check: controls.Groups[0].Checks[9], str: "permissions=640", strConfig: "", expectedTestResult: "permissions has permissions 640, expected 644 or more restrictive", }, { check: controls.Groups[0].Checks[9], str: "permissions=600", strConfig: "", expectedTestResult: "permissions has permissions 600, expected 644 or more restrictive", }, { check: controls.Groups[0].Checks[10], str: "2:45 ../kubernetes/kube-apiserver --option --admission-control=WebHook,RBAC ---audit-log-maxage=40", strConfig: "", expectedTestResult: "'--admission-control' has 'RBAC'", }, { check: controls.Groups[0].Checks[11], str: "2:45 ../kubernetes/kube-apiserver --option --admission-control=WebHook,RBAC ---audit-log-maxage=40", strConfig: "", expectedTestResult: "'--admission-control' has 'WebHook'", }, { check: controls.Groups[0].Checks[12], str: "2:45 ../kubernetes/kube-apiserver --option --admission-control=WebHook,Something,RBAC ---audit-log-maxage=40", strConfig: "", expectedTestResult: "'--admission-control' has 'Something'", }, { check: controls.Groups[0].Checks[13], str: "2:45 ../kubernetes/kube-apiserver --option --admission-control=Something ---audit-log-maxage=40", strConfig: "", expectedTestResult: "'--admission-control' has 'Something'", }, { // check for ':' as argument-value separator, with space between arg and val check: controls.Groups[0].Checks[14], str: "2:45 kube-apiserver some-arg: some-val --admission-control=Something ---audit-log-maxage=40", strConfig: "", expectedTestResult: "'some-arg' is equal to 'some-val'", }, { // check for ':' as argument-value separator, with no space between arg and val check: controls.Groups[0].Checks[14], str: "2:45 kube-apiserver some-arg:some-val --admission-control=Something ---audit-log-maxage=40", strConfig: "", expectedTestResult: "'some-arg' is equal to 'some-val'", }, { check: controls.Groups[0].Checks[15], str: "", strConfig: "{\"readOnlyPort\": 15000}", expectedTestResult: "'{.readOnlyPort}' is equal to '15000' OR '{.readOnlyPort}' is greater or equal to 15000 OR '{.readOnlyPort}' is lower or equal to 15000", }, { check: controls.Groups[0].Checks[16], str: "", strConfig: "{\"stringValue\": \"WebHook,Something,RBAC\"}", expectedTestResult: "'{.stringValue}' is not equal to 'None' AND '{.stringValue}' is not equal to 'webhook,Something,RBAC' AND '{.stringValue}' is equal to 'WebHook,Something,RBAC'", }, { check: controls.Groups[0].Checks[17], str: "", strConfig: "{\"trueValue\": true}", expectedTestResult: "'{.trueValue}' is not equal to 'somethingElse' AND '{.trueValue}' is not equal to 'false' AND '{.trueValue}' is equal to 'true'", }, { check: controls.Groups[0].Checks[18], str: "", strConfig: "{\"readOnlyPort\": 15000}", expectedTestResult: "'{.notARealField}' is not present", }, { check: controls.Groups[0].Checks[19], str: "", strConfig: "{\"authentication\": { \"anonymous\": {\"enabled\": false}}}", expectedTestResult: "'{.authentication.anonymous.enabled}' is equal to 'false'", }, { check: controls.Groups[0].Checks[20], str: "", strConfig: "readOnlyPort: 15000", expectedTestResult: "'{.readOnlyPort}' is greater than 14999", }, { check: controls.Groups[0].Checks[21], str: "", strConfig: "readOnlyPort: 15000", expectedTestResult: "'{.fieldThatIsUnset}' is not present", }, { check: controls.Groups[0].Checks[22], str: "", strConfig: "authentication:\n anonymous:\n enabled: false", expectedTestResult: "'{.authentication.anonymous.enabled}' is equal to 'false'", }, { check: controls.Groups[0].Checks[26], str: "", strConfig: "currentMasterVersion: 1.12.7", expectedTestResult: "'{.currentMasterVersion}' matched by regex expression '^1\\.12.*$'", }, { check: controls.Groups[0].Checks[27], str: "--peer-client-cert-auth", strConfig: "", expectedTestResult: "'--peer-client-cert-auth' is equal to 'true'", }, { check: controls.Groups[0].Checks[27], str: "--abc=true --peer-client-cert-auth --efg=false", strConfig: "", expectedTestResult: "'--peer-client-cert-auth' is equal to 'true'", }, { check: controls.Groups[0].Checks[27], str: "--abc --peer-client-cert-auth --efg", strConfig: "", expectedTestResult: "'--peer-client-cert-auth' is equal to 'true'", }, { check: controls.Groups[0].Checks[27], str: "--peer-client-cert-auth=true", strConfig: "", expectedTestResult: "'--peer-client-cert-auth' is equal to 'true'", }, { check: controls.Groups[0].Checks[27], str: "--abc --peer-client-cert-auth=true --efg", strConfig: "", expectedTestResult: "'--peer-client-cert-auth' is equal to 'true'", }, { check: controls.Groups[0].Checks[28], str: "--abc --peer-client-cert-auth=false --efg", strConfig: "", expectedTestResult: "'--peer-client-cert-auth' is equal to 'false'", }, { check: controls.Groups[0].Checks[29], str: "2:45 ../kubernetes/kube-apiserver --option1=20,30,40", strConfig: "", expectedTestResult: "'ALLOW_PRIVILEGED' is present", strEnv: "SOME_OTHER_ENV=true\nALLOW_PRIVILEGED=false", }, { check: controls.Groups[0].Checks[30], str: "2:45 ../kubernetes/kube-apiserver --option1=20,30,40", strConfig: "", expectedTestResult: "'BASIC_AUTH' is not present", strEnv: "", }, { check: controls.Groups[0].Checks[31], str: "2:45 ../kubernetes/kube-apiserver --option1=20,30,40", strConfig: "", expectedTestResult: "'INSECURE_PORT' is equal to '0'", strEnv: "INSECURE_PORT=0", }, { check: controls.Groups[0].Checks[32], str: "2:45 ../kubernetes/kube-apiserver --option1=20,30,40", strConfig: "", expectedTestResult: "'AUDIT_LOG_MAXAGE' is greater or equal to 30", strEnv: "AUDIT_LOG_MAXAGE=40", }, { check: controls.Groups[0].Checks[33], str: "2:45 ../kubernetes/kube-apiserver --option1=20,30,40", strConfig: "", expectedTestResult: "'MAX_BACKLOG' is lower than 30", strEnv: "MAX_BACKLOG=20", }, } for _, c := range cases { t.Run(c.check.Text, func(t *testing.T) { c.check.AuditOutput = c.str c.check.AuditConfigOutput = c.strConfig c.check.AuditEnvOutput = c.strEnv res, err := c.check.execute() if err != nil { t.Error(err.Error()) } if !res.testResult { t.Errorf("Test ID %v - expected:%v, got:%v", c.check.ID, true, res) } if res.ExpectedResult != c.expectedTestResult { t.Errorf("Test ID %v - \nexpected:%v, \ngot: %v", c.check.ID, c.expectedTestResult, res.ExpectedResult) } }) } } func TestTestExecuteExceptions(t *testing.T) { cases := []struct { *Check str string }{ { controls.Groups[0].Checks[23], "this is not valid json {} at all", }, { controls.Groups[0].Checks[24], "{\"key\": \"value\"}", }, { controls.Groups[0].Checks[25], "broken } yaml\nenabled: true", }, { controls.Groups[0].Checks[26], "currentMasterVersion: 1.11", }, { controls.Groups[0].Checks[26], "currentMasterVersion: ", }, } for _, c := range cases { t.Run(c.Text, func(t *testing.T) { c.Check.AuditConfigOutput = c.str res, err := c.Check.execute() if err != nil { t.Error(err.Error()) } if res.testResult { t.Errorf("expected:%v, got:%v", false, res) } }) } } func TestTestUnmarshal(t *testing.T) { type kubeletConfig struct { Kind string ApiVersion string Address string } cases := []struct { content string jsonInterface interface{} expectedToFail bool }{ { `{ "kind": "KubeletConfiguration", "apiVersion": "kubelet.config.k8s.io/v1beta1", "address": "0.0.0.0" } `, kubeletConfig{}, false, }, { ` kind: KubeletConfiguration address: 0.0.0.0 apiVersion: kubelet.config.k8s.io/v1beta1 authentication: anonymous: enabled: false webhook: cacheTTL: 2m0s enabled: true x509: clientCAFile: /etc/kubernetes/pki/ca.crt tlsCipherSuites: - TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 - TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 `, kubeletConfig{}, false, }, { ` kind: ddress: 0.0.0.0 apiVersion: kubelet.config.k8s.io/v1beta `, kubeletConfig{}, true, }, } for id, c := range cases { t.Run(fmt.Sprintf("%d", id), func(t *testing.T) { err := unmarshal(c.content, &c.jsonInterface) if err != nil { if !c.expectedToFail { t.Errorf("should pass, got error:%v", err) } } else { if c.expectedToFail { t.Errorf("should fail, but passed") } } }) } } func TestExecuteJSONPath(t *testing.T) { type kubeletConfig struct { Kind string ApiVersion string Address string } cases := []struct { name string jsonPath string jsonInterface kubeletConfig expectedResult string expectedToFail bool }{ { "JSONPath parse works, results don't match", "{.resourcesproviders.aescbc}", kubeletConfig{ Kind: "KubeletConfiguration", ApiVersion: "kubelet.config.k8s.io/v1beta1", Address: "127.0.0.0", }, "blah", true, }, { "JSONPath parse works, results match", "{.Kind}", kubeletConfig{ Kind: "KubeletConfiguration", ApiVersion: "kubelet.config.k8s.io/v1beta1", Address: "127.0.0.0", }, "KubeletConfiguration", false, }, { "JSONPath parse fails", "{.ApiVersion", kubeletConfig{ Kind: "KubeletConfiguration", ApiVersion: "kubelet.config.k8s.io/v1beta1", Address: "127.0.0.0", }, "", true, }, } for _, c := range cases { t.Run(c.name, func(t *testing.T) { result, err := executeJSONPath(c.jsonPath, c.jsonInterface) if err != nil && !c.expectedToFail { t.Fatalf("jsonPath:%q, expectedResult:%q got:%v", c.jsonPath, c.expectedResult, err) } if c.expectedResult != result && !c.expectedToFail { t.Errorf("jsonPath:%q, expectedResult:%q got:%q", c.jsonPath, c.expectedResult, result) } }) } } func TestAllElementsValid(t *testing.T) { cases := []struct { source []string target []string valid bool }{ { source: []string{}, target: []string{}, valid: true, }, { source: []string{"blah"}, target: []string{}, valid: false, }, { source: []string{}, target: []string{ "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", }, valid: false, }, { source: []string{"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256", "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256"}, target: []string{ "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", }, valid: true, }, { source: []string{"blah"}, target: []string{ "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", }, valid: false, }, { source: []string{"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256", "blah"}, target: []string{ "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", }, valid: false, }, } for id, c := range cases { t.Run(fmt.Sprintf("%d", id), func(t *testing.T) { if !allElementsValid(c.source, c.target) && c.valid { t.Errorf("Not All Elements in %q are found in %q", c.source, c.target) } }) } } func TestSplitAndRemoveLastSeparator(t *testing.T) { cases := []struct { source string valid bool elementCnt int }{ { source: "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", valid: true, elementCnt: 8, }, { source: "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,", valid: true, elementCnt: 2, }, { source: "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,", valid: true, elementCnt: 2, }, { source: "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, ", valid: true, elementCnt: 2, }, { source: " TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,", valid: true, elementCnt: 2, }, } for id, c := range cases { t.Run(fmt.Sprintf("%d", id), func(t *testing.T) { as := splitAndRemoveLastSeparator(c.source, defaultArraySeparator) if len(as) == 0 && c.valid { t.Errorf("Split did not work with %q", c.source) } if c.elementCnt != len(as) { t.Errorf("Split did not work with %q expected: %d got: %d", c.source, c.elementCnt, len(as)) } }) } } func TestCompareOp(t *testing.T) { cases := []struct { label string op string flagVal string // Test output. compareValue string // Flag value to compare with. expectedResultPattern string flagName string // Compared flag name. testResult bool }{ // Test Op not matching {label: "empty - op", op: "", flagVal: "", compareValue: "", expectedResultPattern: "", testResult: false, flagName: ""}, {label: "op=blah", op: "blah", flagVal: "foo", compareValue: "bar", expectedResultPattern: "", testResult: false, flagName: ""}, // Test Op "eq" {label: "op=eq, both empty", op: "eq", flagVal: "", compareValue: "", expectedResultPattern: "'' is equal to ''", testResult: true, flagName: ""}, { label: "op=eq, true==true", op: "eq", flagVal: "true", compareValue: "true", expectedResultPattern: "'parameterTrue' is equal to 'true'", testResult: true, flagName: "parameterTrue", }, { label: "op=eq, false==false", op: "eq", flagVal: "false", compareValue: "false", expectedResultPattern: "'parameterFalse' is equal to 'false'", testResult: true, flagName: "parameterFalse", }, { label: "op=eq, false==true", op: "eq", flagVal: "false", compareValue: "true", expectedResultPattern: "'parameterFalse' is equal to 'true'", testResult: false, flagName: "parameterFalse", }, { label: "op=eq, strings match", op: "eq", flagVal: "KubeletConfiguration", compareValue: "KubeletConfiguration", expectedResultPattern: "'--FlagNameKubeletConf' is equal to 'KubeletConfiguration'", testResult: true, flagName: "--FlagNameKubeletConf", }, { label: "op=eq, flagVal=empty", op: "eq", flagVal: "", compareValue: "KubeletConfiguration", expectedResultPattern: "'--FlagNameKubeletConf' is equal to 'KubeletConfiguration'", testResult: false, flagName: "--FlagNameKubeletConf", }, { label: "op=eq, compareValue=empty", op: "eq", flagVal: "KubeletConfiguration", compareValue: "", expectedResultPattern: "'--FlagNameKubeletConf' is equal to ''", testResult: false, flagName: "--FlagNameKubeletConf", }, // Test Op "noteq" { label: "op=noteq, both empty", op: "noteq", flagVal: "", compareValue: "", expectedResultPattern: "'parameter' is not equal to ''", testResult: false, flagName: "parameter", }, { label: "op=noteq, true!=true", op: "noteq", flagVal: "true", compareValue: "true", expectedResultPattern: "'parameterTrue' is not equal to 'true'", testResult: false, flagName: "parameterTrue", }, { label: "op=noteq, false!=false", op: "noteq", flagVal: "false", compareValue: "false", expectedResultPattern: "'parameterFalse' is not equal to 'false'", testResult: false, flagName: "parameterFalse", }, { label: "op=noteq, false!=true", op: "noteq", flagVal: "false", compareValue: "true", expectedResultPattern: "'parameterFalse' is not equal to 'true'", testResult: true, flagName: "parameterFalse", }, { label: "op=noteq, strings match", op: "noteq", flagVal: "KubeletConfiguration", compareValue: "KubeletConfiguration", expectedResultPattern: "'--FlagNameKubeletConf' is not equal to 'KubeletConfiguration'", testResult: false, flagName: "--FlagNameKubeletConf", }, { label: "op=noteq, flagVal=empty", op: "noteq", flagVal: "", compareValue: "KubeletConfiguration", expectedResultPattern: "'--FlagNameKubeletConf' is not equal to 'KubeletConfiguration'", testResult: true, flagName: "--FlagNameKubeletConf", }, { label: "op=noteq, compareValue=empty", op: "noteq", flagVal: "KubeletConfiguration", compareValue: "", expectedResultPattern: "'--FlagNameKubeletConf' is not equal to ''", testResult: true, flagName: "--FlagNameKubeletConf", }, // Test Op "gt" { label: "op=gt, both empty", op: "gt", flagVal: "", compareValue: "", expectedResultPattern: "Invalid Number(s) used for comparison: '' ''", testResult: false, flagName: "flagName", }, { label: "op=gt, 0 > 0", op: "gt", flagVal: "0", compareValue: "0", expectedResultPattern: "'flagName' is greater than 0", testResult: false, flagName: "flagName", }, { label: "op=gt, 4 > 5", op: "gt", flagVal: "4", compareValue: "5", expectedResultPattern: "'flagName' is greater than 5", testResult: false, flagName: "flagName", }, { label: "op=gt, 5 > 4", op: "gt", flagVal: "5", compareValue: "4", expectedResultPattern: "'flagName' is greater than 4", testResult: true, flagName: "flagName", }, { label: "op=gt, 5 > 5", op: "gt", flagVal: "5", compareValue: "5", expectedResultPattern: "'flagName' is greater than 5", testResult: false, flagName: "flagName", }, { label: "op=gt, Pikachu > 5", op: "gt", flagVal: "Pikachu", compareValue: "5", expectedResultPattern: "Invalid Number(s) used for comparison: 'Pikachu' '5'", testResult: false, flagName: "flagName", }, { label: "op=gt, 5 > Bulbasaur", op: "gt", flagVal: "5", compareValue: "Bulbasaur", expectedResultPattern: "Invalid Number(s) used for comparison: '5' 'Bulbasaur'", testResult: false, flagName: "flagName", }, // Test Op "lt" { label: "op=lt, both empty", op: "lt", flagVal: "", compareValue: "", expectedResultPattern: "Invalid Number(s) used for comparison: '' ''", testResult: false, flagName: "flagName", }, { label: "op=lt, 0 < 0", op: "lt", flagVal: "0", compareValue: "0", expectedResultPattern: "'flagName' is lower than 0", testResult: false, flagName: "flagName", }, { label: "op=lt, 4 < 5", op: "lt", flagVal: "4", compareValue: "5", expectedResultPattern: "'flagName' is lower than 5", testResult: true, flagName: "flagName", }, { label: "op=lt, 5 < 4", op: "lt", flagVal: "5", compareValue: "4", expectedResultPattern: "'flagName' is lower than 4", testResult: false, flagName: "flagName", }, { label: "op=lt, 5 < 5", op: "lt", flagVal: "5", compareValue: "5", expectedResultPattern: "'flagName' is lower than 5", testResult: false, flagName: "flagName", }, { label: "op=lt, Charmander < 5", op: "lt", flagVal: "Charmander", compareValue: "5", expectedResultPattern: "Invalid Number(s) used for comparison: 'Charmander' '5'", testResult: false, flagName: "flagName", }, { label: "op=lt, 5 < Charmeleon", op: "lt", flagVal: "5", compareValue: "Charmeleon", expectedResultPattern: "Invalid Number(s) used for comparison: '5' 'Charmeleon'", testResult: false, flagName: "flagName", }, // Test Op "gte" { label: "op=gte, both empty", op: "gte", flagVal: "", compareValue: "", expectedResultPattern: "Invalid Number(s) used for comparison: '' ''", testResult: false, flagName: "flagName", }, { label: "op=gte, 0 >= 0", op: "gte", flagVal: "0", compareValue: "0", expectedResultPattern: "'flagName' is greater or equal to 0", testResult: true, flagName: "flagName", }, { label: "op=gte, 4 >= 5", op: "gte", flagVal: "4", compareValue: "5", expectedResultPattern: "'flagName' is greater or equal to 5", testResult: false, flagName: "flagName", }, { label: "op=gte, 5 >= 4", op: "gte", flagVal: "5", compareValue: "4", expectedResultPattern: "'flagName' is greater or equal to 4", testResult: true, flagName: "flagName", }, { label: "op=gte, 5 >= 5", op: "gte", flagVal: "5", compareValue: "5", expectedResultPattern: "'flagName' is greater or equal to 5", testResult: true, flagName: "flagName", }, { label: "op=gte, Ekans >= 5", op: "gte", flagVal: "Ekans", compareValue: "5", expectedResultPattern: "Invalid Number(s) used for comparison: 'Ekans' '5'", testResult: false, flagName: "flagName", }, { label: "op=gte, 4 >= Zubat", op: "gte", flagVal: "4", compareValue: "Zubat", expectedResultPattern: "Invalid Number(s) used for comparison: '4' 'Zubat'", testResult: false, flagName: "flagName", }, // Test Op "lte" { label: "op=lte, both empty", op: "lte", flagVal: "", compareValue: "", expectedResultPattern: "Invalid Number(s) used for comparison: '' ''", testResult: false, flagName: "flagName", }, { label: "op=lte, 0 <= 0", op: "lte", flagVal: "0", compareValue: "0", expectedResultPattern: "'flagName' is lower or equal to 0", testResult: true, flagName: "flagName", }, { label: "op=lte, 4 <= 5", op: "lte", flagVal: "4", compareValue: "5", expectedResultPattern: "'flagName' is lower or equal to 5", testResult: true, flagName: "flagName", }, { label: "op=lte, 5 <= 4", op: "lte", flagVal: "5", compareValue: "4", expectedResultPattern: "'flagName' is lower or equal to 4", testResult: false, flagName: "flagName", }, { label: "op=lte, 5 <= 5", op: "lte", flagVal: "5", compareValue: "5", expectedResultPattern: "'flagName' is lower or equal to 5", testResult: true, flagName: "flagName", }, { label: "op=lte, Venomoth <= 4", op: "lte", flagVal: "Venomoth", compareValue: "4", expectedResultPattern: "Invalid Number(s) used for comparison: 'Venomoth' '4'", testResult: false, flagName: "flagName", }, { label: "op=lte, 5 <= Meowth", op: "lte", flagVal: "5", compareValue: "Meowth", expectedResultPattern: "Invalid Number(s) used for comparison: '5' 'Meowth'", testResult: false, flagName: "flagName", }, // Test Op "has" { label: "op=has, both empty", op: "has", flagVal: "", compareValue: "", expectedResultPattern: "'flagName' has ''", testResult: true, flagName: "flagName", }, { label: "op=has, flagVal=empty", op: "has", flagVal: "", compareValue: "blah", expectedResultPattern: "'flagName' has 'blah'", testResult: false, flagName: "flagName", }, { label: "op=has, compareValue=empty", op: "has", flagVal: "blah", compareValue: "", expectedResultPattern: "'flagName-blah' has ''", testResult: true, flagName: "flagName-blah", }, { label: "op=has, 'blah' has 'la'", op: "has", flagVal: "blah", compareValue: "la", expectedResultPattern: "'flagName-blah' has 'la'", testResult: true, flagName: "flagName-blah", }, { label: "op=has, 'blah' has 'LA'", op: "has", flagVal: "blah", compareValue: "LA", expectedResultPattern: "'flagName-blah' has 'LA'", testResult: false, flagName: "flagName-blah", }, { label: "op=has, 'blah' has 'lo'", op: "has", flagVal: "blah", compareValue: "lo", expectedResultPattern: "'flagName-blah' has 'lo'", testResult: false, flagName: "flagName-blah", }, // Test Op "nothave" { label: "op=nothave, both empty", op: "nothave", flagVal: "", compareValue: "", expectedResultPattern: "'flagName' does not have ''", testResult: false, flagName: "flagName", }, { label: "op=nothave, flagVal=empty", op: "nothave", flagVal: "", compareValue: "blah", expectedResultPattern: "'flagName' does not have 'blah'", testResult: true, flagName: "flagName", }, { label: "op=nothave, compareValue=empty", op: "nothave", flagVal: "blah", compareValue: "", expectedResultPattern: "'flagName-blah' does not have ''", testResult: false, flagName: "flagName-blah", }, { label: "op=nothave, 'blah' not have 'la'", op: "nothave", flagVal: "blah", compareValue: "la", expectedResultPattern: "'flagName-blah' does not have 'la'", testResult: false, flagName: "flagName-blah", }, { label: "op=nothave, 'blah' not have 'LA'", op: "nothave", flagVal: "blah", compareValue: "LA", expectedResultPattern: "'flagName-blah' does not have 'LA'", testResult: true, flagName: "flagName-blah", }, { label: "op=nothave, 'blah' not have 'lo'", op: "nothave", flagVal: "blah", compareValue: "lo", expectedResultPattern: "'flagName-blah' does not have 'lo'", testResult: true, flagName: "flagName-blah", }, // Test Op "regex" { label: "op=regex, both empty", op: "regex", flagVal: "", compareValue: "", expectedResultPattern: "'flagName' matched by regex expression ''", testResult: true, flagName: "flagName", }, { label: "op=regex, flagVal=empty", op: "regex", flagVal: "", compareValue: "blah", expectedResultPattern: "'flagName' matched by regex expression 'blah'", testResult: false, flagName: "flagName", }, // Test Op "valid_elements" { label: "op=valid_elements, valid_elements both empty", op: "valid_elements", flagVal: "", compareValue: "", expectedResultPattern: "'flagWithMultipleElements' contains valid elements from ''", testResult: true, flagName: "flagWithMultipleElements", }, { label: "op=valid_elements, valid_elements flagVal empty", op: "valid_elements", flagVal: "", compareValue: "a,b", expectedResultPattern: "'flagWithMultipleElements' contains valid elements from 'a,b'", testResult: false, flagName: "flagWithMultipleElements", }, { label: "op=valid_elements, valid_elements compareValue empty", op: "valid_elements", flagVal: "a,b", compareValue: "", expectedResultPattern: "'flagWithMultipleElements' contains valid elements from ''", testResult: false, flagName: "flagWithMultipleElements", }, { label: "op=valid_elements, valid_elements two list equals", op: "valid_elements", flagVal: "a,b,c", compareValue: "a,b,c", expectedResultPattern: "'flagWithMultipleElements' contains valid elements from 'a,b,c'", testResult: true, flagName: "flagWithMultipleElements", }, { label: "op=valid_elements, valid_elements partial flagVal valid", op: "valid_elements", flagVal: "a,c", compareValue: "a,b,c", expectedResultPattern: "'flagWithMultipleElements' contains valid elements from 'a,b,c'", testResult: true, flagName: "flagWithMultipleElements", }, { label: "op=valid_elements, valid_elements partial compareValue valid", op: "valid_elements", flagVal: "a,b,c", compareValue: "a,c", expectedResultPattern: "'flagWithMultipleElements' contains valid elements from 'a,c'", testResult: false, flagName: "flagWithMultipleElements", }, // Test Op "bitmask" { label: "op=bitmask, 644 AND 640", op: "bitmask", flagVal: "640", compareValue: "644", expectedResultPattern: "etc/fileExamplePermission640 has permissions 640, expected 644 or more restrictive", testResult: true, flagName: "etc/fileExamplePermission640", }, { label: "op=bitmask, 644 AND 777", op: "bitmask", flagVal: "777", compareValue: "644", expectedResultPattern: "etc/fileExamplePermission777 has permissions 777, expected 644 or more restrictive", testResult: false, flagName: "etc/fileExamplePermission777", }, { label: "op=bitmask, 644 AND 444", op: "bitmask", flagVal: "444", compareValue: "644", expectedResultPattern: "etc/fileExamplePermission444 has permissions 444, expected 644 or more restrictive", testResult: true, flagName: "etc/fileExamplePermission444", }, { label: "op=bitmask, 644 AND 211", op: "bitmask", flagVal: "211", compareValue: "644", expectedResultPattern: "etc/fileExamplePermission211 has permissions 211, expected 644 or more restrictive", testResult: false, flagName: "etc/fileExamplePermission211", }, { label: "op=bitmask, Harry AND 211", op: "bitmask", flagVal: "Harry", compareValue: "644", expectedResultPattern: "Not numeric value - flag: Harry", testResult: false, flagName: "etc/fileExample", }, { label: "op=bitmask, 644 AND Potter", op: "bitmask", flagVal: "211", compareValue: "Potter", expectedResultPattern: "Not numeric value - flag: Potter", testResult: false, flagName: "etc/fileExample", }, } for _, c := range cases { t.Run(c.label, func(t *testing.T) { expectedResultPattern, testResult := compareOp(c.op, c.flagVal, c.compareValue, c.flagName) if expectedResultPattern != c.expectedResultPattern { t.Errorf("'expectedResultPattern' did not match - op: %q expected:%q got:%q", c.op, c.expectedResultPattern, expectedResultPattern) } if testResult != c.testResult { t.Errorf("'testResult' did not match - lop: %q expected:%t got:%t", c.op, c.testResult, testResult) } }) } } func TestToNumeric(t *testing.T) { cases := []struct { firstValue string secondValue string expectedToFail bool }{ { firstValue: "a", secondValue: "b", expectedToFail: true, }, { firstValue: "5", secondValue: "b", expectedToFail: true, }, { firstValue: "5", secondValue: "6", expectedToFail: false, }, } for id, c := range cases { t.Run(fmt.Sprintf("%d", id), func(t *testing.T) { f, s, err := toNumeric(c.firstValue, c.secondValue) if c.expectedToFail && err == nil { t.Errorf("Expected error while converting %s and %s", c.firstValue, c.secondValue) } if !c.expectedToFail && (f != 5 || s != 6) { t.Errorf("Expected to return %d,%d - got %d,%d", 5, 6, f, s) } }) } } func TestExecuteJSONPathOnEncryptionConfig(t *testing.T) { type Resources struct { Resources []string `json:"resources"` Providers []map[string]interface{} `json:"providers"` } type EncryptionConfig struct { Kind string `json:"kind"` ApiVersion string `json:"apiVersion"` Resources []Resources `json:"resources"` } type Key struct { Secret string `json:"secret"` Name string `json:"name"` } type Aescbc struct { Keys []Key `json:"keys"` } type SecretBox struct { Keys []Key `json:"keys"` } type Aesgcm struct { Keys []Key `json:"keys"` } // identity disable encryption when set as the first parameter type Identity struct{} cases := []struct { name string jsonPath string jsonInterface EncryptionConfig expectedResult string expectedToFail bool }{ { "JSONPath parse works, results match", "{.resources[*].providers[*].aescbc.keys[*].secret}", EncryptionConfig{ Kind: "EncryptionConfig", ApiVersion: "v1", Resources: []Resources{{Resources: []string{"secrets"}, Providers: []map[string]interface{}{ {"aescbc": Aescbc{Keys: []Key{{Secret: "secret1", Name: "name1"}}}}, }}}, }, "secret1", false, }, { "JSONPath parse works, results match", "{.resources[*].providers[*].aescbc.keys[*].name}", EncryptionConfig{ Kind: "EncryptionConfig", ApiVersion: "v1", Resources: []Resources{{Resources: []string{"secrets"}, Providers: []map[string]interface{}{ {"aescbc": Aescbc{Keys: []Key{{Secret: "secret1", Name: "name1"}}}}, }}}, }, "name1", false, }, { "JSONPath parse works, results don't match", "{.resources[*].providers[*].aescbc.keys[*].secret}", EncryptionConfig{ Kind: "EncryptionConfig", ApiVersion: "v1", Resources: []Resources{{Resources: []string{"secrets"}, Providers: []map[string]interface{}{ {"aesgcm": Aesgcm{Keys: []Key{{Secret: "secret1", Name: "name1"}}}}, }}}, }, "secret1", true, }, { "JSONPath parse works, results match", "{.resources[*].providers[*].aesgcm.keys[*].secret}", EncryptionConfig{ Kind: "EncryptionConfig", ApiVersion: "v1", Resources: []Resources{{Resources: []string{"secrets"}, Providers: []map[string]interface{}{ {"aesgcm": Aesgcm{Keys: []Key{{Secret: "secret1", Name: "name1"}}}}, }}}, }, "secret1", false, }, { "JSONPath parse works, results match", "{.resources[*].providers[*].secretbox.keys[*].secret}", EncryptionConfig{ Kind: "EncryptionConfig", ApiVersion: "v1", Resources: []Resources{{Resources: []string{"secrets"}, Providers: []map[string]interface{}{ {"secretbox": SecretBox{Keys: []Key{{Secret: "secret1", Name: "name1"}}}}, }}}, }, "secret1", false, }, { "JSONPath parse works, results match", "{.resources[*].providers[*].aescbc.keys[*].secret}", EncryptionConfig{ Kind: "EncryptionConfig", ApiVersion: "v1", Resources: []Resources{{Resources: []string{"secrets"}, Providers: []map[string]interface{}{ {"aescbc": Aescbc{Keys: []Key{{Secret: "secret1", Name: "name1"}, {Secret: "secret2", Name: "name2"}}}}, }}}, }, "secret1 secret2", false, }, } for _, c := range cases { t.Run(c.name, func(t *testing.T) { result, err := executeJSONPath(c.jsonPath, c.jsonInterface) if err != nil && !c.expectedToFail { t.Fatalf("jsonPath:%q, expectedResult:%q got:%v", c.jsonPath, c.expectedResult, err) } if c.expectedResult != result && !c.expectedToFail { t.Errorf("jsonPath:%q, expectedResult:%q got:%q", c.jsonPath, c.expectedResult, result) } }) } } ================================================ FILE: cmd/common.go ================================================ // Copyright © 2017 Aqua Security Software Ltd. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package cmd import ( "bufio" "encoding/json" "fmt" "os" "path/filepath" "sort" "strconv" "strings" "github.com/aquasecurity/kube-bench/check" "github.com/golang/glog" "github.com/spf13/viper" ) // NewRunFilter constructs a Predicate based on FilterOpts which determines whether tested Checks should be run or not. func NewRunFilter(opts FilterOpts) (check.Predicate, error) { if opts.CheckList != "" && opts.GroupList != "" { return nil, fmt.Errorf("group option and check option can't be used together") } var groupIDs map[string]bool if opts.GroupList != "" { groupIDs = cleanIDs(opts.GroupList) } var checkIDs map[string]bool if opts.CheckList != "" { checkIDs = cleanIDs(opts.CheckList) } return func(g *check.Group, c *check.Check) bool { test := true if len(groupIDs) > 0 { _, ok := groupIDs[g.ID] test = test && ok } if len(checkIDs) > 0 { _, ok := checkIDs[c.ID] test = test && ok } test = test && (opts.Scored && c.Scored || opts.Unscored && !c.Scored) return test }, nil } func runChecks(nodetype check.NodeType, testYamlFile, detectedVersion string) { // Verify config file was loaded into Viper during Cobra sub-command initialization. if configFileError != nil { colorPrint(check.FAIL, fmt.Sprintf("Failed to read config file: %v\n", configFileError)) os.Exit(1) } in, err := os.ReadFile(testYamlFile) if err != nil { exitWithError(fmt.Errorf("error opening %s test file: %v", testYamlFile, err)) } glog.V(1).Info(fmt.Sprintf("Using test file: %s\n", testYamlFile)) // Get the viper config for this section of tests typeConf := viper.Sub(string(nodetype)) if typeConf == nil { colorPrint(check.FAIL, fmt.Sprintf("No config settings for %s\n", string(nodetype))) os.Exit(1) } // Get the set of executables we need for this section of the tests binmap, err := getBinaries(typeConf, nodetype) // Checks that the executables we need for the section are running. if err != nil { glog.V(1).Info(fmt.Sprintf("failed to get a set of executables needed for tests: %v", err)) } confmap := getFiles(typeConf, "config") svcmap := getFiles(typeConf, "service") kubeconfmap := getFiles(typeConf, "kubeconfig") cafilemap := getFiles(typeConf, "ca") datadirmap := getFiles(typeConf, "datadir") // Variable substitutions. Replace all occurrences of variables in controls files. s := string(in) s, binSubs := makeSubstitutions(s, "bin", binmap) s, _ = makeSubstitutions(s, "conf", confmap) s, _ = makeSubstitutions(s, "svc", svcmap) s, _ = makeSubstitutions(s, "kubeconfig", kubeconfmap) s, _ = makeSubstitutions(s, "cafile", cafilemap) s, _ = makeSubstitutions(s, "datadir", datadirmap) controls, err := check.NewControls(nodetype, []byte(s), detectedVersion) if err != nil { exitWithError(fmt.Errorf("error setting up %s controls: %v", nodetype, err)) } runner := check.NewRunner() filter, err := NewRunFilter(filterOpts) if err != nil { exitWithError(fmt.Errorf("error setting up run filter: %v", err)) } generateDefaultEnvAudit(controls, binSubs) controls.RunChecks(runner, filter, parseSkipIds(skipIds)) controlsCollection = append(controlsCollection, controls) } func generateDefaultEnvAudit(controls *check.Controls, binSubs []string) { for _, group := range controls.Groups { for _, checkItem := range group.Checks { if checkItem.Tests != nil && !checkItem.DisableEnvTesting { for _, test := range checkItem.Tests.TestItems { if test.Env != "" && checkItem.AuditEnv == "" { binPath := "" if len(binSubs) == 1 { binPath = binSubs[0] } else { glog.V(1).Infof("AuditEnv not explicit for check (%s), where bin path cannot be determined", checkItem.ID) } if test.Env != "" && checkItem.AuditEnv == "" { checkItem.AuditEnv = fmt.Sprintf("cat \"/proc/$(/bin/ps -C %s -o pid= | tr -d ' ')/environ\" | tr '\\0' '\\n'", binPath) } } } } } } } func parseSkipIds(skipIds string) map[string]bool { skipIdMap := make(map[string]bool, 0) if skipIds != "" { for _, id := range strings.Split(skipIds, ",") { skipIdMap[strings.Trim(id, " ")] = true } } return skipIdMap } // colorPrint outputs the state in a specific colour, along with a message string func colorPrint(state check.State, s string) { colors[state].Printf("[%s] ", state) fmt.Printf("%s", s) } // prettyPrint outputs the results to stdout in human-readable format func prettyPrint(r *check.Controls, summary check.Summary) { // Print check results. if !noResults { colorPrint(check.INFO, fmt.Sprintf("%s %s\n", r.ID, r.Text)) for _, g := range r.Groups { colorPrint(check.INFO, fmt.Sprintf("%s %s\n", g.ID, g.Text)) for _, c := range g.Checks { colorPrint(c.State, fmt.Sprintf("%s %s\n", c.ID, c.Text)) if includeTestOutput && c.State == check.FAIL && len(c.ActualValue) > 0 { printRawOutput(c.ActualValue) } } } fmt.Println() } // Print remediations. if !noRemediations { if summary.Fail > 0 || summary.Warn > 0 { colors[check.WARN].Printf("== Remediations %s ==\n", r.Type) for _, g := range r.Groups { for _, c := range g.Checks { if c.State == check.FAIL { fmt.Printf("%s %s\n", c.ID, c.Remediation) } if c.State == check.WARN { // Print the error if test failed due to problem with the audit command if c.Reason != "" && c.Type != "manual" { fmt.Printf("%s audit test did not run: %s\n", c.ID, c.Reason) } else { fmt.Printf("%s %s\n", c.ID, c.Remediation) } } } } fmt.Println() } } // Print summary setting output color to highest severity. if !noSummary { printSummary(summary, string(r.Type)) } } func printSummary(summary check.Summary, sectionName string) { var res check.State if summary.Fail > 0 { res = check.FAIL } else if summary.Warn > 0 { res = check.WARN } else { res = check.PASS } colors[res].Printf("== Summary %s ==\n", sectionName) fmt.Printf("%d checks PASS\n%d checks FAIL\n%d checks WARN\n%d checks INFO\n\n", summary.Pass, summary.Fail, summary.Warn, summary.Info, ) } // loadConfig finds the correct config dir based on the kubernetes version, // merges any specific config.yaml file found with the main config // and returns the benchmark file to use. func loadConfig(nodetype check.NodeType, benchmarkVersion string) string { var file string var err error switch nodetype { case check.MASTER: file = masterFile case check.NODE: file = nodeFile case check.CONTROLPLANE: file = controlplaneFile case check.ETCD: file = etcdFile case check.POLICIES: file = policiesFile case check.MANAGEDSERVICES: file = managedservicesFile } path, err := getConfigFilePath(benchmarkVersion, file) if err != nil { exitWithError(fmt.Errorf("can't find %s controls file in %s: %v", nodetype, cfgDir, err)) } // Merge version-specific config if any. mergeConfig(path) return filepath.Join(path, file) } func mergeConfig(path string) error { viper.SetConfigFile(path + "/config.yaml") err := viper.MergeInConfig() if err != nil { if os.IsNotExist(err) { glog.V(2).Info(fmt.Sprintf("No version-specific config.yaml file in %s", path)) } else { return fmt.Errorf("couldn't read config file %s: %v", path+"/config.yaml", err) } } glog.V(1).Info(fmt.Sprintf("Using config file: %s\n", viper.ConfigFileUsed())) return nil } func mapToBenchmarkVersion(kubeToBenchmarkMap map[string]string, kv string) (string, error) { kvOriginal := kv cisVersion, found := kubeToBenchmarkMap[kv] glog.V(2).Info(fmt.Sprintf("mapToBenchmarkVersion for k8sVersion: %q cisVersion: %q found: %t\n", kv, cisVersion, found)) for !found && (kv != defaultKubeVersion && !isEmpty(kv)) { kv = decrementVersion(kv) cisVersion, found = kubeToBenchmarkMap[kv] glog.V(2).Info(fmt.Sprintf("mapToBenchmarkVersion for k8sVersion: %q cisVersion: %q found: %t\n", kv, cisVersion, found)) } if !found { glog.V(1).Info(fmt.Sprintf("mapToBenchmarkVersion unable to find a match for: %q", kvOriginal)) glog.V(3).Info(fmt.Sprintf("mapToBenchmarkVersion kubeToBenchmarkMap: %#v", kubeToBenchmarkMap)) return "", fmt.Errorf("unable to find a matching Benchmark Version match for kubernetes version: %s", kvOriginal) } return cisVersion, nil } func loadVersionMapping(v *viper.Viper) (map[string]string, error) { kubeToBenchmarkMap := v.GetStringMapString("version_mapping") if kubeToBenchmarkMap == nil || (len(kubeToBenchmarkMap) == 0) { return nil, fmt.Errorf("config file is missing 'version_mapping' section") } return kubeToBenchmarkMap, nil } func loadTargetMapping(v *viper.Viper) (map[string][]string, error) { benchmarkVersionToTargetsMap := v.GetStringMapStringSlice("target_mapping") if len(benchmarkVersionToTargetsMap) == 0 { return nil, fmt.Errorf("config file is missing 'target_mapping' section") } return benchmarkVersionToTargetsMap, nil } func getBenchmarkVersion(kubeVersion, benchmarkVersion string, platform Platform, v *viper.Viper) (bv string, err error) { detecetedKubeVersion = "none" if !isEmpty(kubeVersion) && !isEmpty(benchmarkVersion) { return "", fmt.Errorf("It is an error to specify both --version and --benchmark flags") } if isEmpty(benchmarkVersion) && isEmpty(kubeVersion) && !isEmpty(platform.Name) { benchmarkVersion = getPlatformBenchmarkVersion(platform) if !isEmpty(benchmarkVersion) { detecetedKubeVersion = benchmarkVersion } } if isEmpty(benchmarkVersion) { if isEmpty(kubeVersion) { kv, err := getKubeVersion() if err != nil { return "", fmt.Errorf("Version check failed: %s\nAlternatively, you can specify the version with --version", err) } kubeVersion = kv.BaseVersion() detecetedKubeVersion = kubeVersion } kubeToBenchmarkMap, err := loadVersionMapping(v) if err != nil { return "", err } benchmarkVersion, err = mapToBenchmarkVersion(kubeToBenchmarkMap, kubeVersion) if err != nil { return "", err } glog.V(2).Info(fmt.Sprintf("Mapped Kubernetes version: %s to Benchmark version: %s", kubeVersion, benchmarkVersion)) } glog.V(1).Info(fmt.Sprintf("Kubernetes version: %q to Benchmark version: %q", kubeVersion, benchmarkVersion)) return benchmarkVersion, nil } // isMaster verify if master components are running on the node. func isMaster() bool { return isThisNodeRunning(check.MASTER) } // isEtcd verify if etcd components are running on the node. func isEtcd() bool { return isThisNodeRunning(check.ETCD) } func isThisNodeRunning(nodeType check.NodeType) bool { glog.V(3).Infof("Checking if the current node is running %s components", nodeType) nodeTypeConf := viper.Sub(string(nodeType)) if nodeTypeConf == nil { glog.V(2).Infof("No config for %s components found", nodeType) return false } components, err := getBinariesFunc(nodeTypeConf, nodeType) if err != nil { glog.V(2).Infof("Failed to find %s binaries: %v", nodeType, err) return false } if len(components) == 0 { glog.V(2).Infof("No %s binaries specified", nodeType) return false } glog.V(2).Infof("Node is running %s components", nodeType) return true } func exitCodeSelection(controlsCollection []*check.Controls) int { for _, control := range controlsCollection { if control.Fail > 0 { return exitCode } } return 0 } func writeOutput(controlsCollection []*check.Controls) { sort.Slice(controlsCollection, func(i, j int) bool { iid, _ := strconv.Atoi(controlsCollection[i].ID) jid, _ := strconv.Atoi(controlsCollection[j].ID) return iid < jid }) if junitFmt { writeJunitOutput(controlsCollection) return } if jsonFmt { writeJSONOutput(controlsCollection) return } if pgSQL { writePgsqlOutput(controlsCollection) return } if aSFF { writeASFFOutput(controlsCollection) return } writeStdoutOutput(controlsCollection) } func writeJSONOutput(controlsCollection []*check.Controls) { var out []byte var err error if !noTotals { var totals check.OverallControls totals.Controls = controlsCollection totals.Totals = getSummaryTotals(controlsCollection) out, err = json.Marshal(totals) } else { out, err = json.Marshal(controlsCollection) } if err != nil { exitWithError(fmt.Errorf("failed to output in JSON format: %v", err)) } printOutput(string(out), outputFile) } func writeJunitOutput(controlsCollection []*check.Controls) { // QuickFix for issue https://github.com/aquasecurity/kube-bench/issues/883 // Should consider to deprecate of switch to using Junit template prefix := "\n" suffix := "\n" var outputAllControls []byte for _, controls := range controlsCollection { tempOut, err := controls.JUnit() outputAllControls = append(outputAllControls[:], tempOut[:]...) if err != nil { exitWithError(fmt.Errorf("failed to output in JUnit format: %v", err)) } } printOutput(prefix+string(outputAllControls)+suffix, outputFile) } func writePgsqlOutput(controlsCollection []*check.Controls) { for _, controls := range controlsCollection { out, err := controls.JSON() if err != nil { exitWithError(fmt.Errorf("failed to output in Postgresql format: %v", err)) } savePgsql(string(out)) } } func writeASFFOutput(controlsCollection []*check.Controls) { for _, controls := range controlsCollection { out, err := controls.ASFF() if err != nil { exitWithError(fmt.Errorf("failed to format findings as ASFF: %v", err)) } if err := writeFinding(out); err != nil { exitWithError(fmt.Errorf("failed to output to ASFF: %v", err)) } } } func writeStdoutOutput(controlsCollection []*check.Controls) { for _, controls := range controlsCollection { summary := controls.Summary prettyPrint(controls, summary) } if !noTotals { printSummary(getSummaryTotals(controlsCollection), "total") } } func getSummaryTotals(controlsCollection []*check.Controls) check.Summary { var totalSummary check.Summary for _, controls := range controlsCollection { summary := controls.Summary totalSummary.Fail = totalSummary.Fail + summary.Fail totalSummary.Warn = totalSummary.Warn + summary.Warn totalSummary.Pass = totalSummary.Pass + summary.Pass totalSummary.Info = totalSummary.Info + summary.Info } return totalSummary } func printRawOutput(output string) { for _, row := range strings.Split(output, "\n") { fmt.Println(fmt.Sprintf("\t %s", row)) } } func writeOutputToFile(output string, outputFile string) error { file, err := os.Create(outputFile) if err != nil { return err } defer file.Close() w := bufio.NewWriter(file) fmt.Fprintln(w, output) return w.Flush() } func printOutput(output string, outputFile string) { if outputFile == "" { fmt.Println(output) } else { err := writeOutputToFile(output, outputFile) if err != nil { exitWithError(fmt.Errorf("Failed to write to output file %s: %v", outputFile, err)) } } } // validTargets helps determine if the targets // are legitimate for the benchmarkVersion. func validTargets(benchmarkVersion string, targets []string, v *viper.Viper) (bool, error) { benchmarkVersionToTargetsMap, err := loadTargetMapping(v) if err != nil { return false, err } providedTargets, found := benchmarkVersionToTargetsMap[benchmarkVersion] if !found { return false, fmt.Errorf("No targets configured for %s", benchmarkVersion) } for _, pt := range targets { f := false for _, t := range providedTargets { if pt == strings.ToLower(t) { f = true break } } if !f { return false, nil } } return true, nil } ================================================ FILE: cmd/common_test.go ================================================ // Copyright © 2017-2019 Aqua Security Software Ltd. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package cmd import ( "encoding/json" "errors" "fmt" "io" "os" "path" "path/filepath" "testing" "time" "github.com/aquasecurity/kube-bench/check" "github.com/spf13/viper" "github.com/stretchr/testify/assert" ) type JsonOutputFormat struct { Controls []*check.Controls `json:"Controls"` TotalSummary map[string]int `json:"Totals"` } type JsonOutputFormatNoTotals struct { Controls []*check.Controls `json:"Controls"` } func TestParseSkipIds(t *testing.T) { skipMap := parseSkipIds("4.12,4.13,5") _, fourTwelveExists := skipMap["4.12"] _, fourThirteenExists := skipMap["4.13"] _, fiveExists := skipMap["5"] _, other := skipMap["G1"] assert.True(t, fourThirteenExists) assert.True(t, fourTwelveExists) assert.True(t, fiveExists) assert.False(t, other) } func TestNewRunFilter(t *testing.T) { type TestCase struct { Name string FilterOpts FilterOpts Group *check.Group Check *check.Check Expected bool } testCases := []TestCase{ { Name: "Should return true when scored flag is enabled and check is scored", FilterOpts: FilterOpts{Scored: true, Unscored: false}, Group: &check.Group{}, Check: &check.Check{Scored: true}, Expected: true, }, { Name: "Should return false when scored flag is enabled and check is not scored", FilterOpts: FilterOpts{Scored: true, Unscored: false}, Group: &check.Group{}, Check: &check.Check{Scored: false}, Expected: false, }, { Name: "Should return true when unscored flag is enabled and check is not scored", FilterOpts: FilterOpts{Scored: false, Unscored: true}, Group: &check.Group{}, Check: &check.Check{Scored: false}, Expected: true, }, { Name: "Should return false when unscored flag is enabled and check is scored", FilterOpts: FilterOpts{Scored: false, Unscored: true}, Group: &check.Group{}, Check: &check.Check{Scored: true}, Expected: false, }, { Name: "Should return true when group flag contains group's ID", FilterOpts: FilterOpts{Scored: true, Unscored: true, GroupList: "G1,G2,G3"}, Group: &check.Group{ID: "G2"}, Check: &check.Check{}, Expected: true, }, { Name: "Should return false when group flag doesn't contain group's ID", FilterOpts: FilterOpts{GroupList: "G1,G3"}, Group: &check.Group{ID: "G2"}, Check: &check.Check{}, Expected: false, }, { Name: "Should return true when check flag contains check's ID", FilterOpts: FilterOpts{Scored: true, Unscored: true, CheckList: "C1,C2,C3"}, Group: &check.Group{}, Check: &check.Check{ID: "C2"}, Expected: true, }, { Name: "Should return false when check flag doesn't contain check's ID", FilterOpts: FilterOpts{CheckList: "C1,C3"}, Group: &check.Group{}, Check: &check.Check{ID: "C2"}, Expected: false, }, } for _, testCase := range testCases { t.Run(testCase.Name, func(t *testing.T) { filter, _ := NewRunFilter(testCase.FilterOpts) assert.Equal(t, testCase.Expected, filter(testCase.Group, testCase.Check)) }) } t.Run("Should return error when both group and check flags are used", func(t *testing.T) { // given opts := FilterOpts{GroupList: "G1", CheckList: "C1"} // when _, err := NewRunFilter(opts) // then assert.EqualError(t, err, "group option and check option can't be used together") }) } func TestIsMaster(t *testing.T) { testCases := []struct { name string cfgFile string getBinariesFunc func(*viper.Viper, check.NodeType) (map[string]string, error) isMaster bool }{ { name: "valid config, is master and all components are running", cfgFile: "../cfg/config.yaml", getBinariesFunc: func(viper *viper.Viper, nt check.NodeType) (strings map[string]string, i error) { return map[string]string{"apiserver": "kube-apiserver"}, nil }, isMaster: true, }, { name: "valid config, is master and but not all components are running", cfgFile: "../cfg/config.yaml", getBinariesFunc: func(viper *viper.Viper, nt check.NodeType) (strings map[string]string, i error) { return map[string]string{}, nil }, isMaster: false, }, { name: "valid config, is master, not all components are running and fails to find all binaries", cfgFile: "../cfg/config.yaml", getBinariesFunc: func(viper *viper.Viper, nt check.NodeType) (strings map[string]string, i error) { return map[string]string{}, errors.New("failed to find binaries") }, isMaster: false, }, { name: "valid config, does not include master", cfgFile: "../hack/node_only.yaml", isMaster: false, }, } cfgDirOld := cfgDir cfgDir = "../cfg" defer func() { cfgDir = cfgDirOld }() execCode := `#!/bin/sh echo "Server Version: v1.13.10" ` restore, err := fakeExecutableInPath("kubectl", execCode) if err != nil { t.Fatal("Failed when calling fakeExecutableInPath ", err) } defer restore() for _, tc := range testCases { func() { cfgFile = tc.cfgFile initConfig() oldGetBinariesFunc := getBinariesFunc getBinariesFunc = tc.getBinariesFunc defer func() { getBinariesFunc = oldGetBinariesFunc cfgFile = "" }() assert.Equal(t, tc.isMaster, isMaster(), tc.name) }() } } func TestMapToCISVersion(t *testing.T) { viperWithData, err := loadConfigForTest() if err != nil { t.Fatalf("Unable to load config file %v", err) } kubeToBenchmarkMap, err := loadVersionMapping(viperWithData) if err != nil { t.Fatalf("Unable to load config file %v", err) } cases := []struct { kubeVersion string succeed bool exp string expErr string }{ {kubeVersion: "1.9", succeed: false, exp: "", expErr: "unable to find a matching Benchmark Version match for kubernetes version: 1.9"}, {kubeVersion: "1.11", succeed: false, exp: "", expErr: "unable to find a matching Benchmark Version match for kubernetes version: 1.11"}, {kubeVersion: "1.12", succeed: false, exp: "", expErr: "unable to find a matching Benchmark Version match for kubernetes version: 1.12"}, {kubeVersion: "1.13", succeed: false, exp: "", expErr: "unable to find a matching Benchmark Version match for kubernetes version: 1.13"}, {kubeVersion: "1.14", succeed: false, exp: "", expErr: "unable to find a matching Benchmark Version match for kubernetes version: 1.14"}, {kubeVersion: "1.15", succeed: true, exp: "cis-1.5"}, {kubeVersion: "1.16", succeed: true, exp: "cis-1.6"}, {kubeVersion: "1.17", succeed: true, exp: "cis-1.6"}, {kubeVersion: "1.18", succeed: true, exp: "cis-1.6"}, {kubeVersion: "1.19", succeed: true, exp: "cis-1.20"}, {kubeVersion: "1.20", succeed: true, exp: "cis-1.20"}, {kubeVersion: "1.21", succeed: true, exp: "cis-1.20"}, {kubeVersion: "1.22", succeed: true, exp: "cis-1.23"}, {kubeVersion: "1.23", succeed: true, exp: "cis-1.23"}, {kubeVersion: "1.24", succeed: true, exp: "cis-1.24"}, {kubeVersion: "1.25", succeed: true, exp: "cis-1.7"}, {kubeVersion: "1.26", succeed: true, exp: "cis-1.8"}, {kubeVersion: "1.27", succeed: true, exp: "cis-1.9"}, {kubeVersion: "1.28", succeed: true, exp: "cis-1.10"}, {kubeVersion: "1.29", succeed: true, exp: "cis-1.11"}, {kubeVersion: "1.30", succeed: true, exp: "cis-1.11"}, {kubeVersion: "1.31", succeed: true, exp: "cis-1.11"}, {kubeVersion: "1.32", succeed: true, exp: "cis-1.12"}, {kubeVersion: "1.33", succeed: true, exp: "cis-1.12"}, {kubeVersion: "1.34", succeed: true, exp: "cis-1.12"}, {kubeVersion: "gke-1.2.0", succeed: true, exp: "gke-1.2.0"}, {kubeVersion: "ocp-3.10", succeed: true, exp: "rh-0.7"}, {kubeVersion: "ocp-3.11", succeed: true, exp: "rh-0.7"}, {kubeVersion: "unknown", succeed: false, exp: "", expErr: "unable to find a matching Benchmark Version match for kubernetes version: unknown"}, } for _, c := range cases { rv, err := mapToBenchmarkVersion(kubeToBenchmarkMap, c.kubeVersion) if c.succeed { if err != nil { t.Errorf("[%q]-Unexpected error: %v", c.kubeVersion, err) } if len(rv) == 0 { t.Errorf("[%q]-missing return value", c.kubeVersion) } if c.exp != rv { t.Errorf("[%q]- expected %q but Got %q", c.kubeVersion, c.exp, rv) } } else { if c.exp != rv { t.Errorf("[%q]-mapToBenchmarkVersion kubeversion: %q Got %q expected %s", c.kubeVersion, c.kubeVersion, rv, c.exp) } if c.expErr != err.Error() { t.Errorf("[%q]-mapToBenchmarkVersion expected Error: %q instead Got %q", c.kubeVersion, c.expErr, err.Error()) } } } } func TestLoadVersionMapping(t *testing.T) { setDefault := func(v *viper.Viper, key string, value interface{}) *viper.Viper { v.SetDefault(key, value) return v } viperWithData, err := loadConfigForTest() if err != nil { t.Fatalf("Unable to load config file %v", err) } cases := []struct { n string v *viper.Viper succeed bool }{ {n: "empty", v: viper.New(), succeed: false}, { n: "novals", v: setDefault(viper.New(), "version_mapping", "novals"), succeed: false, }, { n: "good", v: viperWithData, succeed: true, }, } for _, c := range cases { rv, err := loadVersionMapping(c.v) if c.succeed { if err != nil { t.Errorf("[%q]-Unexpected error: %v", c.n, err) } if len(rv) == 0 { t.Errorf("[%q]-missing mapping value", c.n) } } else { if err == nil { t.Errorf("[%q]-Expected error but got none", c.n) } } } } func TestGetBenchmarkVersion(t *testing.T) { viperWithData, err := loadConfigForTest() if err != nil { t.Fatalf("Unable to load config file %v", err) } type getBenchmarkVersionFnToTest func(kubeVersion, benchmarkVersion string, platform Platform, v *viper.Viper) (string, error) withFakeKubectl := func(kubeVersion, benchmarkVersion string, platform Platform, v *viper.Viper, fn getBenchmarkVersionFnToTest) (string, error) { execCode := `#!/bin/sh echo '{"serverVersion": {"major": "1", "minor": "18", "gitVersion": "v1.18.10"}}' ` restore, err := fakeExecutableInPath("kubectl", execCode) if err != nil { t.Fatal("Failed when calling fakeExecutableInPath ", err) } defer restore() return fn(kubeVersion, benchmarkVersion, platform, v) } withNoPath := func(kubeVersion, benchmarkVersion string, platform Platform, v *viper.Viper, fn getBenchmarkVersionFnToTest) (string, error) { restore, err := prunePath() if err != nil { t.Fatal("Failed when calling prunePath ", err) } defer restore() return fn(kubeVersion, benchmarkVersion, platform, v) } type getBenchmarkVersionFn func(string, string, Platform, *viper.Viper, getBenchmarkVersionFnToTest) (string, error) cases := []struct { n string kubeVersion string benchmarkVersion string platform Platform v *viper.Viper callFn getBenchmarkVersionFn exp string succeed bool }{ {n: "both versions", kubeVersion: "1.11", benchmarkVersion: "cis-1.3", platform: Platform{}, exp: "cis-1.3", callFn: withNoPath, v: viper.New(), succeed: false}, {n: "no version-missing-kubectl", kubeVersion: "", benchmarkVersion: "", platform: Platform{}, v: viperWithData, exp: "cis-1.6", callFn: withNoPath, succeed: true}, {n: "no version-fakeKubectl", kubeVersion: "", benchmarkVersion: "", platform: Platform{}, v: viperWithData, exp: "cis-1.6", callFn: withFakeKubectl, succeed: true}, {n: "kubeVersion", kubeVersion: "1.15", benchmarkVersion: "", platform: Platform{}, v: viperWithData, exp: "cis-1.5", callFn: withNoPath, succeed: true}, {n: "ocpVersion310", kubeVersion: "ocp-3.10", benchmarkVersion: "", platform: Platform{}, v: viperWithData, exp: "rh-0.7", callFn: withNoPath, succeed: true}, {n: "ocpVersion311", kubeVersion: "ocp-3.11", benchmarkVersion: "", platform: Platform{}, v: viperWithData, exp: "rh-0.7", callFn: withNoPath, succeed: true}, {n: "gke12", kubeVersion: "gke-1.2.0", benchmarkVersion: "", platform: Platform{}, v: viperWithData, exp: "gke-1.2.0", callFn: withNoPath, succeed: true}, } for _, c := range cases { rv, err := c.callFn(c.kubeVersion, c.benchmarkVersion, c.platform, c.v, getBenchmarkVersion) if c.succeed { if err != nil { t.Errorf("[%q]-Unexpected error: %v", c.n, err) } if len(rv) == 0 { t.Errorf("[%q]-missing return value", c.n) } if c.exp != rv { t.Errorf("[%q]- expected %q but Got %q", c.n, c.exp, rv) } } else { if err == nil { t.Errorf("[%q]-Expected error but got none", c.n) } } } } func TestValidTargets(t *testing.T) { viperWithData, err := loadConfigForTest() if err != nil { t.Fatalf("Unable to load config file %v", err) } cases := []struct { name string benchmark string targets []string expected bool }{ { name: "cis-1.5 no dummy", benchmark: "cis-1.5", targets: []string{"master", "node", "controlplane", "etcd", "dummy"}, expected: false, }, { name: "cis-1.5 valid", benchmark: "cis-1.5", targets: []string{"master", "node", "controlplane", "etcd", "policies"}, expected: true, }, { name: "cis-1.6 no Pikachu", benchmark: "cis-1.6", targets: []string{"master", "node", "controlplane", "etcd", "Pikachu"}, expected: false, }, { name: "cis-1.6 valid", benchmark: "cis-1.6", targets: []string{"master", "node", "controlplane", "etcd", "policies"}, expected: true, }, { name: "gke-1.2.0 valid", benchmark: "gke-1.2.0", targets: []string{"master", "node", "controlplane", "policies", "managedservices"}, expected: true, }, { name: "aks-1.0 valid", benchmark: "aks-1.0", targets: []string{"node", "policies", "controlplane", "managedservices"}, expected: true, }, { name: "aks-1.7 valid", benchmark: "aks-1.7", targets: []string{"node", "policies", "controlplane", "managedservices"}, expected: true, }, { name: "eks-1.0.1 valid", benchmark: "eks-1.0.1", targets: []string{"node", "policies", "controlplane", "managedservices"}, expected: true, }, { name: "eks-1.1.0 valid", benchmark: "eks-1.1.0", targets: []string{"node", "policies", "controlplane", "managedservices"}, expected: true, }, { name: "eks-1.2.0 valid", benchmark: "eks-1.2.0", targets: []string{"node", "policies", "controlplane", "managedservices"}, expected: true, }, { name: "eks-1.5.0 valid", benchmark: "eks-1.5.0", targets: []string{"node", "policies", "controlplane", "managedservices"}, expected: true, }, { name: "eks-1.7.0 valid", benchmark: "eks-1.7.0", targets: []string{"node", "policies", "controlplane", "managedservices"}, expected: true, }, } for _, c := range cases { t.Run(c.name, func(t *testing.T) { ret, err := validTargets(c.benchmark, c.targets, viperWithData) if err != nil { t.Fatalf("Expected nil error, got: %v", err) } if ret != c.expected { t.Fatalf("Expected %t, got %t", c.expected, ret) } }) } } func TestIsEtcd(t *testing.T) { testCases := []struct { name string cfgFile string getBinariesFunc func(*viper.Viper, check.NodeType) (map[string]string, error) isEtcd bool }{ { name: "valid config, is etcd and all components are running", cfgFile: "../cfg/config.yaml", getBinariesFunc: func(viper *viper.Viper, nt check.NodeType) (strings map[string]string, i error) { return map[string]string{"etcd": "etcd"}, nil }, isEtcd: true, }, { name: "valid config, is etcd and but not all components are running", cfgFile: "../cfg/config.yaml", getBinariesFunc: func(viper *viper.Viper, nt check.NodeType) (strings map[string]string, i error) { return map[string]string{}, nil }, isEtcd: false, }, { name: "valid config, is etcd, not all components are running and fails to find all binaries", cfgFile: "../cfg/config.yaml", getBinariesFunc: func(viper *viper.Viper, nt check.NodeType) (strings map[string]string, i error) { return map[string]string{}, errors.New("failed to find binaries") }, isEtcd: false, }, { name: "valid config, does not include etcd", cfgFile: "../hack/node_only.yaml", isEtcd: false, }, } cfgDirOld := cfgDir cfgDir = "../cfg" defer func() { cfgDir = cfgDirOld }() execCode := `#!/bin/sh echo "Server Version: v1.15.03" ` restore, err := fakeExecutableInPath("kubectl", execCode) if err != nil { t.Fatal("Failed when calling fakeExecutableInPath ", err) } defer restore() for _, tc := range testCases { func() { cfgFile = tc.cfgFile initConfig() oldGetBinariesFunc := getBinariesFunc getBinariesFunc = tc.getBinariesFunc defer func() { getBinariesFunc = oldGetBinariesFunc cfgFile = "" }() assert.Equal(t, tc.isEtcd, isEtcd(), tc.name) }() } } func TestWriteResultToJsonFile(t *testing.T) { defer func() { controlsCollection = []*check.Controls{} jsonFmt = false outputFile = "" }() var err error jsonFmt = true outputFile = path.Join(os.TempDir(), fmt.Sprintf("%d", time.Now().UnixNano())) controlsCollection, err = parseControlsJsonFile("./testdata/controlsCollection.json") if err != nil { t.Error(err) } writeOutput(controlsCollection) var expect JsonOutputFormat var result JsonOutputFormat result, err = parseResultJsonFile(outputFile) if err != nil { t.Error(err) } expect, err = parseResultJsonFile("./testdata/result.json") if err != nil { t.Error(err) } assert.Equal(t, expect, result) } func TestWriteResultNoTotalsToJsonFile(t *testing.T) { defer func() { controlsCollection = []*check.Controls{} jsonFmt = false outputFile = "" }() var err error jsonFmt = true outputFile = path.Join(os.TempDir(), fmt.Sprintf("%d", time.Now().UnixNano())) noTotals = true controlsCollection, err = parseControlsJsonFile("./testdata/controlsCollection.json") if err != nil { t.Error(err) } writeOutput(controlsCollection) var expect []*check.Controls var result []*check.Controls result, err = parseResultNoTotalsJsonFile(outputFile) if err != nil { t.Error(err) } expect, err = parseResultNoTotalsJsonFile("./testdata/result_no_totals.json") if err != nil { t.Error(err) } assert.Equal(t, expect, result) } func TestExitCodeSelection(t *testing.T) { exitCode = 10 controlsCollectionAllPassed, errPassed := parseControlsJsonFile("./testdata/passedControlsCollection.json") if errPassed != nil { t.Error(errPassed) } controlsCollectionWithFailures, errFailure := parseControlsJsonFile("./testdata/controlsCollection.json") if errFailure != nil { t.Error(errFailure) } exitCodePassed := exitCodeSelection(controlsCollectionAllPassed) assert.Equal(t, 0, exitCodePassed) exitCodeFailure := exitCodeSelection(controlsCollectionWithFailures) assert.Equal(t, 10, exitCodeFailure) } func TestGenerationDefaultEnvAudit(t *testing.T) { input := []byte(` --- type: "master" groups: - id: G1 checks: - id: G1/C1 - id: G2 checks: - id: G2/C1 text: "Verify that the SomeSampleFlag argument is set to true" audit: "grep -B1 SomeSampleFlag=true /this/is/a/file/path" tests: test_items: - flag: "SomeSampleFlag=true" env: "SOME_SAMPLE_FLAG" compare: op: has value: "true" set: true remediation: | Edit the config file /this/is/a/file/path and set SomeSampleFlag to true. scored: true `) controls, err := check.NewControls(check.MASTER, input, "") assert.NoError(t, err) binSubs := []string{"TestBinPath"} generateDefaultEnvAudit(controls, binSubs) expectedAuditEnv := fmt.Sprintf("cat \"/proc/$(/bin/ps -C %s -o pid= | tr -d ' ')/environ\" | tr '\\0' '\\n'", binSubs[0]) assert.Equal(t, expectedAuditEnv, controls.Groups[1].Checks[0].AuditEnv) } func TestGetSummaryTotals(t *testing.T) { controlsCollection, err := parseControlsJsonFile("./testdata/controlsCollection.json") if err != nil { t.Error(err) } resultTotals := getSummaryTotals(controlsCollection) assert.Equal(t, 12, resultTotals.Fail) assert.Equal(t, 14, resultTotals.Warn) assert.Equal(t, 0, resultTotals.Info) assert.Equal(t, 49, resultTotals.Pass) } func TestPrintSummary(t *testing.T) { controlsCollection, err := parseControlsJsonFile("./testdata/controlsCollection.json") if err != nil { t.Error(err) } resultTotals := getSummaryTotals(controlsCollection) rescueStdout := os.Stdout r, w, _ := os.Pipe() os.Stdout = w printSummary(resultTotals, "totals") w.Close() out, _ := io.ReadAll(r) os.Stdout = rescueStdout assert.Contains(t, string(out), "49 checks PASS\n12 checks FAIL\n14 checks WARN\n0 checks INFO\n\n") } func TestPrettyPrintNoSummary(t *testing.T) { controlsCollection, err := parseControlsJsonFile("./testdata/controlsCollection.json") if err != nil { t.Error(err) } resultTotals := getSummaryTotals(controlsCollection) rescueStdout := os.Stdout r, w, _ := os.Pipe() os.Stdout = w noSummary = true prettyPrint(controlsCollection[0], resultTotals) w.Close() out, _ := io.ReadAll(r) os.Stdout = rescueStdout assert.NotContains(t, string(out), "49 checks PASS") } func TestPrettyPrintSummary(t *testing.T) { controlsCollection, err := parseControlsJsonFile("./testdata/controlsCollection.json") if err != nil { t.Error(err) } resultTotals := getSummaryTotals(controlsCollection) rescueStdout := os.Stdout r, w, _ := os.Pipe() os.Stdout = w noSummary = false prettyPrint(controlsCollection[0], resultTotals) w.Close() out, _ := io.ReadAll(r) os.Stdout = rescueStdout assert.Contains(t, string(out), "49 checks PASS") } func TestWriteStdoutOutputNoTotal(t *testing.T) { controlsCollection, err := parseControlsJsonFile("./testdata/controlsCollection.json") if err != nil { t.Error(err) } rescueStdout := os.Stdout r, w, _ := os.Pipe() os.Stdout = w noTotals = true writeStdoutOutput(controlsCollection) w.Close() out, _ := io.ReadAll(r) os.Stdout = rescueStdout assert.NotContains(t, string(out), "49 checks PASS") } func TestWriteStdoutOutputTotal(t *testing.T) { controlsCollection, err := parseControlsJsonFile("./testdata/controlsCollection.json") if err != nil { t.Error(err) } rescueStdout := os.Stdout r, w, _ := os.Pipe() os.Stdout = w noTotals = false writeStdoutOutput(controlsCollection) w.Close() out, _ := io.ReadAll(r) os.Stdout = rescueStdout assert.Contains(t, string(out), "49 checks PASS") } func parseControlsJsonFile(filepath string) ([]*check.Controls, error) { var result []*check.Controls d, err := os.ReadFile(filepath) if err != nil { return nil, err } err = json.Unmarshal(d, &result) if err != nil { return nil, err } return result, nil } func parseResultJsonFile(filepath string) (JsonOutputFormat, error) { var result JsonOutputFormat d, err := os.ReadFile(filepath) if err != nil { return result, err } err = json.Unmarshal(d, &result) if err != nil { return result, err } return result, nil } func parseResultNoTotalsJsonFile(filepath string) ([]*check.Controls, error) { var result []*check.Controls d, err := os.ReadFile(filepath) if err != nil { return nil, err } err = json.Unmarshal(d, &result) if err != nil { return nil, err } return result, nil } func loadConfigForTest() (*viper.Viper, error) { viperWithData := viper.New() viperWithData.SetConfigFile("../cfg/config.yaml") if err := viperWithData.ReadInConfig(); err != nil { return nil, err } return viperWithData, nil } type restoreFn func() func fakeExecutableInPath(execFile, execCode string) (restoreFn, error) { pathenv := os.Getenv("PATH") tmp, err := os.MkdirTemp("", "TestfakeExecutableInPath") if err != nil { return nil, err } wd, err := os.Getwd() if err != nil { return nil, err } if len(execCode) > 0 { os.WriteFile(filepath.Join(tmp, execFile), []byte(execCode), 0700) } else { f, err := os.OpenFile(execFile, os.O_CREATE|os.O_EXCL, 0700) if err != nil { return nil, err } err = f.Close() if err != nil { return nil, err } } err = os.Setenv("PATH", fmt.Sprintf("%s:%s", tmp, pathenv)) if err != nil { return nil, err } restorePath := func() { os.RemoveAll(tmp) os.Chdir(wd) os.Setenv("PATH", pathenv) } return restorePath, nil } func prunePath() (restoreFn, error) { pathenv := os.Getenv("PATH") err := os.Setenv("PATH", "") if err != nil { return nil, err } restorePath := func() { os.Setenv("PATH", pathenv) } return restorePath, nil } ================================================ FILE: cmd/database.go ================================================ package cmd import ( "fmt" "os" "time" "github.com/golang/glog" "github.com/spf13/viper" "gorm.io/driver/postgres" "gorm.io/gorm" ) type PsqlConnInfo struct { Host string User string DbName string SslMode string Password string } func getPsqlConnInfo() (PsqlConnInfo, error) { var host string if value := viper.GetString("PGSQL_HOST"); value != "" { host = value } else { return PsqlConnInfo{}, fmt.Errorf("%s_PGSQL_HOST env var is required", envVarsPrefix) } var user string if value := viper.GetString("PGSQL_USER"); value != "" { user = value } else { return PsqlConnInfo{}, fmt.Errorf("%s_PGSQL_USER env var is required", envVarsPrefix) } var dbName string if value := viper.GetString("PGSQL_DBNAME"); value != "" { dbName = value } else { return PsqlConnInfo{}, fmt.Errorf("%s_PGSQL_DBNAME env var is required", envVarsPrefix) } var sslMode string if value := viper.GetString("PGSQL_SSLMODE"); value != "" { sslMode = value } else { return PsqlConnInfo{}, fmt.Errorf("%s_PGSQL_SSLMODE env var is required", envVarsPrefix) } var password string if value := viper.GetString("PGSQL_PASSWORD"); value != "" { password = value } else { return PsqlConnInfo{}, fmt.Errorf("%s_PGSQL_PASSWORD env var is required", envVarsPrefix) } return PsqlConnInfo{ Host: host, User: user, DbName: dbName, SslMode: sslMode, Password: password, }, nil } func (c *PsqlConnInfo) toString() string { return fmt.Sprintf("host=%s user=%s dbname=%s sslmode=%s password=%s", c.Host, c.User, c.DbName, c.SslMode, c.Password, ) } func savePgsql(jsonInfo string) { var hostname string if value := viper.GetString("K8S_HOST"); value != "" { // Adhere to the ScanHost column definition below if len(value) > 63 { exitWithError(fmt.Errorf("%s_K8S_HOST value's length must be less than 63 chars", envVarsPrefix)) } hostname = value } else { host, err := os.Hostname() if err != nil { exitWithError(fmt.Errorf("received error looking up hostname: %s", err)) } hostname = host } PsqlConnInfo, err := getPsqlConnInfo() if err != nil { exitWithError(err) } db, err := gorm.Open(postgres.Open(PsqlConnInfo.toString()), &gorm.Config{}) if err != nil { exitWithError(fmt.Errorf("received error connecting to database: %s", err)) } timestamp := time.Now() type ScanResult struct { gorm.Model ScanHost string `gorm:"type:varchar(63) not null"` // https://www.ietf.org/rfc/rfc1035.txt ScanTime time.Time `gorm:"not null"` ScanInfo string `gorm:"type:jsonb not null"` } db.Debug().AutoMigrate(&ScanResult{}) db.Save(&ScanResult{ScanHost: hostname, ScanTime: timestamp, ScanInfo: jsonInfo}) glog.V(2).Info(fmt.Sprintf("successfully stored result to: %s", PsqlConnInfo.Host)) } ================================================ FILE: cmd/kubernetes_version.go ================================================ package cmd import ( "crypto/tls" "encoding/json" "encoding/pem" "fmt" "io" "net/http" "os" "strings" "time" "github.com/golang/glog" ) type KubeVersion struct { Major string Minor string baseVersion string GitVersion string } func (k *KubeVersion) BaseVersion() string { if k.baseVersion != "" { return k.baseVersion } // Some provides return the minor version like "15+" minor := strings.Replace(k.Minor, "+", "", -1) ver := fmt.Sprintf("%s.%s", k.Major, minor) k.baseVersion = ver return ver } func getKubeVersionFromRESTAPI() (*KubeVersion, error) { glog.V(2).Info("Try to get version from Rest API") k8sVersionURL := getKubernetesURL() serviceaccount := "/var/run/secrets/kubernetes.io/serviceaccount" cacertfile := fmt.Sprintf("%s/ca.crt", serviceaccount) tokenfile := fmt.Sprintf("%s/token", serviceaccount) tlsCert, err := loadCertificate(cacertfile) if err != nil { glog.V(2).Infof("Failed loading certificate Error: %s", err) return nil, err } tb, err := os.ReadFile(tokenfile) if err != nil { glog.V(2).Infof("Failed reading token file Error: %s", err) return nil, err } token := strings.TrimSpace(string(tb)) data, err := getWebDataWithRetry(k8sVersionURL, token, tlsCert) if err != nil { glog.V(2).Infof("Failed to get data Error: %s", err) return nil, err } k8sVersion, err := extractVersion(data) if err != nil { return nil, err } return k8sVersion, nil } // The idea of this function is so if Kubernetes DNS is not completely seetup and the // Container where kube-bench is running needs time for DNS configure. // Basically try 10 times, waiting 1 second until either it is successful or it fails. func getWebDataWithRetry(k8sVersionURL, token string, cacert *tls.Certificate) (data []byte, err error) { tries := 0 // We retry a few times in case the DNS service has not had time to come up for tries < 10 { data, err = getWebData(k8sVersionURL, token, cacert) if err == nil { return } tries++ time.Sleep(1 * time.Second) } return } type VersionResponse struct { Major string Minor string GitVersion string GitCommit string GitTreeState string BuildDate string GoVersion string Compiler string Platform string } func extractVersion(data []byte) (*KubeVersion, error) { vrObj := &VersionResponse{} glog.V(2).Info(fmt.Sprintf("vd: %s\n", string(data))) err := json.Unmarshal(data, vrObj) if err != nil { return nil, err } glog.V(2).Info(fmt.Sprintf("vrObj: %#v\n", vrObj)) return &KubeVersion{ Major: vrObj.Major, Minor: vrObj.Minor, GitVersion: vrObj.GitVersion, }, nil } func getWebData(srvURL, token string, cacert *tls.Certificate) ([]byte, error) { glog.V(2).Info(fmt.Sprintf("getWebData srvURL: %s\n", srvURL)) tlsConf := &tls.Config{ Certificates: []tls.Certificate{*cacert}, InsecureSkipVerify: true, } tr := &http.Transport{ TLSClientConfig: tlsConf, } client := &http.Client{Transport: tr} req, err := http.NewRequest(http.MethodGet, srvURL, nil) if err != nil { return nil, err } authToken := fmt.Sprintf("Bearer %s", token) req.Header.Set("Authorization", authToken) resp, err := client.Do(req) if err != nil { glog.V(2).Info(fmt.Sprintf("HTTP ERROR: %v\n", err)) return nil, err } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { glog.V(2).Info(fmt.Sprintf("URL:[%s], StatusCode:[%d] \n Headers: %#v\n", srvURL, resp.StatusCode, resp.Header)) err = fmt.Errorf("URL:[%s], StatusCode:[%d]", srvURL, resp.StatusCode) return nil, err } return io.ReadAll(resp.Body) } func loadCertificate(certFile string) (*tls.Certificate, error) { cacert, err := os.ReadFile(certFile) if err != nil { return nil, err } var tlsCert tls.Certificate block, _ := pem.Decode(cacert) if block == nil { return nil, fmt.Errorf("unable to Decode certificate") } glog.V(2).Info("Loading CA certificate") tlsCert.Certificate = append(tlsCert.Certificate, block.Bytes) return &tlsCert, nil } func getKubernetesURL() string { k8sVersionURL := "https://kubernetes.default.svc/version" // The following provides flexibility to use // K8S provided variables is situations where // hostNetwork: true if !isEmpty(os.Getenv("KUBE_BENCH_K8S_ENV")) { k8sHost := os.Getenv("KUBERNETES_SERVICE_HOST") k8sPort := os.Getenv("KUBERNETES_SERVICE_PORT_HTTPS") if !isEmpty(k8sHost) && !isEmpty(k8sPort) { return fmt.Sprintf("https://%s:%s/version", k8sHost, k8sPort) } glog.V(2).Info("KUBE_BENCH_K8S_ENV is set, but environment variables KUBERNETES_SERVICE_HOST or KUBERNETES_SERVICE_PORT_HTTPS are not set") } return k8sVersionURL } ================================================ FILE: cmd/kubernetes_version_test.go ================================================ package cmd import ( "crypto/tls" "fmt" "net/http" "net/http/httptest" "os" "strconv" "testing" ) func TestLoadCertificate(t *testing.T) { tmp, err := os.MkdirTemp("", "TestFakeLoadCertificate") if err != nil { t.Fatalf("unable to create temp directory: %v", err) } defer os.RemoveAll(tmp) goodCertFile, _ := os.CreateTemp(tmp, "good-cert-*") _, _ = goodCertFile.Write([]byte(`-----BEGIN CERTIFICATE----- MIICyDCCAbCgAwIBAgIBADANBgkqhkiG9w0BAQsFADAVMRMwEQYDVQQDEwprdWJl cm5ldGVzMB4XDTE5MTEwODAxNDAwMFoXDTI5MTEwNTAxNDAwMFowFTETMBEGA1UE AxMKa3ViZXJuZXRlczCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMn6 wjvhMc9e0MDwpQNhp8SPxmv1DsYJ4Btp1GeScIgKKDwppuoOmVizLiMNdV5+70yI MgNfm/gwFRNDOtN3R7msfZDD5Dd1vI6qRTP21DFOGVdysFdwqJTs0nGcmfvZEOtw 9cjcsXrBi2Mg54v+X/pq2w51xajCGBt2+bpxJJ3WBiWqKYv0RQdNL0WZGm+V9BuP pHRWPBeLxuCzt5K3Gx+1QDy8o6Y4sSRPssWC4RhD9Hs5/9eeGRyZslLs+AuqdDLQ aziiSjHVtgCfRXE9nYVxaDIwTFuh+Q1IvtB36NRLyX47oya+BbX3PoCtSjA36RBb tcJfulr3oNHnb2ZlfcUCAwEAAaMjMCEwDgYDVR0PAQH/BAQDAgKkMA8GA1UdEwEB /wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAAeQDkbM6DilLkIVQDyxauETgJDV 2AaVzYaAgDApQGAoYV6WIY7Exk4TlmLeKQjWt2s/GtthQWuzUDKTcEvWcG6gNdXk gzuCRRDMGu25NtG3m67w4e2RzW8Z/lzvbfyJZGoV2c6dN+yP9/Pw2MXlrnMWugd1 jLv3UYZRHMpuNS8BJU74BuVzVPHd55RAl+bV8yemdZJ7pPzMvGbZ7zRXWODTDlge CQb9lY+jYErisH8Sq7uABFPvi7RaTh8SS7V7OxqHZvmttNTdZs4TIkk45JK7Y+Xq FAjB57z2NcIgJuVpQnGRYtr/JcH2Qdsq8bLtXaojUIWOOqoTDRLYozdMOOQ= -----END CERTIFICATE-----`)) badCertFile, _ := os.CreateTemp(tmp, "bad-cert-*") cases := []struct { file string fail bool }{ { file: "missing cert file", fail: true, }, { file: badCertFile.Name(), fail: true, }, { file: goodCertFile.Name(), fail: false, }, } for id, c := range cases { t.Run(strconv.Itoa(id), func(t *testing.T) { tlsCert, err := loadCertificate(c.file) if !c.fail { if err != nil { t.Errorf("unexpected error: %v", err) } if tlsCert == nil { t.Errorf("missing returned TLS Certificate") } } else { if err == nil { t.Errorf("Expected error") } } }) } } func TestGetWebData(t *testing.T) { okfn := func(w http.ResponseWriter, r *http.Request) { _, _ = fmt.Fprintln(w, `{ "major": "1", "minor": "15"}`) } errfn := func(w http.ResponseWriter, r *http.Request) { http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) } token := "dummyToken" var tlsCert tls.Certificate cases := []struct { fn http.HandlerFunc fail bool }{ { fn: okfn, fail: false, }, { fn: errfn, fail: true, }, } for id, c := range cases { t.Run(strconv.Itoa(id), func(t *testing.T) { ts := httptest.NewServer(c.fn) defer ts.Close() data, err := getWebData(ts.URL, token, &tlsCert) if !c.fail { if err != nil { t.Errorf("unexpected error: %v", err) } if len(data) == 0 { t.Errorf("missing data") } } else { if err == nil { t.Errorf("Expected error") } } }) } } func TestGetWebDataWithRetry(t *testing.T) { okfn := func(w http.ResponseWriter, r *http.Request) { _, _ = fmt.Fprintln(w, `{ "major": "1", "minor": "15"}`) } errfn := func(w http.ResponseWriter, r *http.Request) { http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) } token := "dummyToken" var tlsCert tls.Certificate cases := []struct { fn http.HandlerFunc fail bool }{ { fn: okfn, fail: false, }, { fn: errfn, fail: true, }, } for id, c := range cases { t.Run(strconv.Itoa(id), func(t *testing.T) { ts := httptest.NewServer(c.fn) defer ts.Close() data, err := getWebDataWithRetry(ts.URL, token, &tlsCert) if !c.fail { if err != nil { t.Errorf("unexpected error: %v", err) } if len(data) == 0 { t.Errorf("missing data") } } else { if err == nil { t.Errorf("Expected error") } } }) } } func TestExtractVersion(t *testing.T) { okJSON := []byte(`{ "major": "1", "minor": "15", "gitVersion": "v1.15.3", "gitCommit": "2d3c76f9091b6bec110a5e63777c332469e0cba2", "gitTreeState": "clean", "buildDate": "2019-08-20T18:57:36Z", "goVersion": "go1.12.9", "compiler": "gc", "platform": "linux/amd64" }`) invalidJSON := []byte(`{ "major": "1", "minor": "15", "gitVersion": "v1.15.3", "gitCommit": "2d3c76f9091b6bec110a5e63777c332469e0cba2", "gitTreeState": "clean",`) cases := []struct { data []byte fail bool expectedVer string }{ { data: okJSON, fail: false, expectedVer: "1.15", }, { data: invalidJSON, fail: true, }, } for id, c := range cases { t.Run(strconv.Itoa(id), func(t *testing.T) { ver, err := extractVersion(c.data) if !c.fail { if err != nil { t.Errorf("unexpected error: %v", err) } if c.expectedVer != ver.BaseVersion() { t.Errorf("Expected %q but Got %q", c.expectedVer, ver) } } else { if err == nil { t.Errorf("Expected error") } } }) } } func TestGetKubernetesURL(t *testing.T) { resetEnvs := func() { os.Unsetenv("KUBE_BENCH_K8S_ENV") os.Unsetenv("KUBERNETES_SERVICE_HOST") os.Unsetenv("KUBERNETES_SERVICE_PORT_HTTPS") } setEnvs := func() { os.Setenv("KUBE_BENCH_K8S_ENV", "1") os.Setenv("KUBERNETES_SERVICE_HOST", "testHostServer") os.Setenv("KUBERNETES_SERVICE_PORT_HTTPS", "443") } cases := []struct { useDefault bool expected string }{ { useDefault: true, expected: "https://kubernetes.default.svc/version", }, { useDefault: false, expected: "https://testHostServer:443/version", }, } for id, c := range cases { t.Run(strconv.Itoa(id), func(t *testing.T) { resetEnvs() defer resetEnvs() if !c.useDefault { setEnvs() } k8sURL := getKubernetesURL() if k8sURL != c.expected { t.Errorf("Expected %q but Got %q", k8sURL, c.expected) } }) } } ================================================ FILE: cmd/root.go ================================================ // Copyright © 2017 Aqua Security Software Ltd. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package cmd import ( goflag "flag" "fmt" "os" "github.com/aquasecurity/kube-bench/check" "github.com/golang/glog" "github.com/spf13/cobra" "github.com/spf13/viper" ) type FilterOpts struct { CheckList string GroupList string Scored bool Unscored bool } var ( envVarsPrefix = "KUBE_BENCH" defaultKubeVersion = "1.18" kubeVersion string detecetedKubeVersion string benchmarkVersion string cfgFile string cfgDir = "./cfg/" jsonFmt bool junitFmt bool pgSQL bool aSFF bool masterFile = "master.yaml" nodeFile = "node.yaml" etcdFile = "etcd.yaml" controlplaneFile = "controlplane.yaml" policiesFile = "policies.yaml" managedservicesFile = "managedservices.yaml" exitCode int noResults bool noSummary bool noRemediations bool skipIds string noTotals bool filterOpts FilterOpts includeTestOutput bool outputFile string configFileError error controlsCollection []*check.Controls ) // RootCmd represents the base command when called without any subcommands var RootCmd = &cobra.Command{ Use: os.Args[0], Short: "Run CIS Benchmarks checks against a Kubernetes deployment", Long: `This tool runs the CIS Kubernetes Benchmark (https://www.cisecurity.org/benchmark/kubernetes/)`, Run: func(cmd *cobra.Command, args []string) { bv, err := getBenchmarkVersion(kubeVersion, benchmarkVersion, getPlatformInfo(), viper.GetViper()) if err != nil { exitWithError(fmt.Errorf("unable to determine benchmark version: %v", err)) } glog.V(1).Infof("Running checks for benchmark %v", bv) if isMaster() { glog.V(1).Info("== Running master checks ==") runChecks(check.MASTER, loadConfig(check.MASTER, bv), detecetedKubeVersion) // Control Plane is only valid for CIS 1.5 and later, // this a gatekeeper for previous versions valid, err := validTargets(bv, []string{string(check.CONTROLPLANE)}, viper.GetViper()) if err != nil { exitWithError(fmt.Errorf("error validating targets: %v", err)) } if valid { glog.V(1).Info("== Running control plane checks ==") runChecks(check.CONTROLPLANE, loadConfig(check.CONTROLPLANE, bv), detecetedKubeVersion) } } else { glog.V(1).Info("== Skipping master checks ==") } // Etcd is only valid for CIS 1.5 and later, // this a gatekeeper for previous versions. valid, err := validTargets(bv, []string{string(check.ETCD)}, viper.GetViper()) if err != nil { exitWithError(fmt.Errorf("error validating targets: %v", err)) } if valid && isEtcd() { glog.V(1).Info("== Running etcd checks ==") runChecks(check.ETCD, loadConfig(check.ETCD, bv), detecetedKubeVersion) } else { glog.V(1).Info("== Skipping etcd checks ==") } glog.V(1).Info("== Running node checks ==") runChecks(check.NODE, loadConfig(check.NODE, bv), detecetedKubeVersion) // Policies is only valid for CIS 1.5 and later, // this a gatekeeper for previous versions. valid, err = validTargets(bv, []string{string(check.POLICIES)}, viper.GetViper()) if err != nil { exitWithError(fmt.Errorf("error validating targets: %v", err)) } if valid { glog.V(1).Info("== Running policies checks ==") runChecks(check.POLICIES, loadConfig(check.POLICIES, bv), detecetedKubeVersion) } else { glog.V(1).Info("== Skipping policies checks ==") } // Managedservices is only valid for GKE 1.0 and later, // this a gatekeeper for previous versions. valid, err = validTargets(bv, []string{string(check.MANAGEDSERVICES)}, viper.GetViper()) if err != nil { exitWithError(fmt.Errorf("error validating targets: %v", err)) } if valid { glog.V(1).Info("== Running managed services checks ==") runChecks(check.MANAGEDSERVICES, loadConfig(check.MANAGEDSERVICES, bv), detecetedKubeVersion) } else { glog.V(1).Info("== Skipping managed services checks ==") } writeOutput(controlsCollection) os.Exit(exitCodeSelection(controlsCollection)) }, } // Execute adds all child commands to the root command sets flags appropriately. // This is called by main.main(). It only needs to happen once to the rootCmd. func Execute() { goflag.CommandLine.Parse([]string{}) if err := RootCmd.Execute(); err != nil { fmt.Println(err) // flush before exit non-zero glog.Flush() os.Exit(-1) } // flush before exit glog.Flush() } func init() { cobra.OnInitialize(initConfig) // Output control RootCmd.PersistentFlags().IntVar(&exitCode, "exit-code", 0, "Specify the exit code for when checks fail") RootCmd.PersistentFlags().BoolVar(&noResults, "noresults", false, "Disable printing of results section") RootCmd.PersistentFlags().BoolVar(&noSummary, "nosummary", false, "Disable printing of summary section") RootCmd.PersistentFlags().BoolVar(&noRemediations, "noremediations", false, "Disable printing of remediations section") RootCmd.PersistentFlags().BoolVar(&noTotals, "nototals", false, "Disable printing of totals for failed, passed, ... checks across all sections") RootCmd.PersistentFlags().BoolVar(&jsonFmt, "json", false, "Prints the results as JSON") RootCmd.PersistentFlags().BoolVar(&junitFmt, "junit", false, "Prints the results as JUnit") RootCmd.PersistentFlags().BoolVar(&pgSQL, "pgsql", false, "Save the results to PostgreSQL") RootCmd.PersistentFlags().BoolVar(&aSFF, "asff", false, "Send the results to AWS Security Hub") RootCmd.PersistentFlags().BoolVar(&filterOpts.Scored, "scored", true, "Run the scored CIS checks") RootCmd.PersistentFlags().BoolVar(&filterOpts.Unscored, "unscored", true, "Run the unscored CIS checks") RootCmd.PersistentFlags().StringVar(&skipIds, "skip", "", "List of comma separated values of checks to be skipped") RootCmd.PersistentFlags().BoolVar(&includeTestOutput, "include-test-output", false, "Prints the actual result when test fails") RootCmd.PersistentFlags().StringVar(&outputFile, "outputfile", "", "Writes the results to output file when run with --json or --junit") RootCmd.PersistentFlags().StringVarP( &filterOpts.CheckList, "check", "c", "", `A comma-delimited list of checks to run as specified in CIS document. Example --check="1.1.1,1.1.2"`, ) RootCmd.PersistentFlags().StringVarP( &filterOpts.GroupList, "group", "g", "", `Run all the checks under this comma-delimited list of groups. Example --group="1.1"`, ) RootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is ./cfg/config.yaml)") RootCmd.PersistentFlags().StringVarP(&cfgDir, "config-dir", "D", cfgDir, "config directory") RootCmd.PersistentFlags().StringVar(&kubeVersion, "version", "", "Manually specify Kubernetes version, automatically detected if unset") RootCmd.PersistentFlags().StringVar(&benchmarkVersion, "benchmark", "", "Manually specify CIS benchmark version. It would be an error to specify both --version and --benchmark flags") if err := goflag.Set("logtostderr", "true"); err != nil { fmt.Printf("unable to set logtostderr: %+v\n", err) os.Exit(-1) } goflag.CommandLine.VisitAll(func(goflag *goflag.Flag) { RootCmd.PersistentFlags().AddGoFlag(goflag) }) } // initConfig reads in config file and ENV variables if set. func initConfig() { if cfgFile != "" { // enable ability to specify config file via flag viper.SetConfigFile(cfgFile) } else { viper.SetConfigName("config") // name of config file (without extension) viper.AddConfigPath(cfgDir) // adding ./cfg as first search path } // Read flag values from environment variables. // Precedence: Command line flags take precedence over environment variables. viper.SetEnvPrefix(envVarsPrefix) viper.AutomaticEnv() if kubeVersion == "" { if env := viper.Get("version"); env != nil { kubeVersion = env.(string) } } // If a config file is found, read it in. if err := viper.ReadInConfig(); err != nil { if _, ok := err.(viper.ConfigFileNotFoundError); ok { // Config file not found; ignore error for now to prevent commands // which don't need the config file exiting. configFileError = err } else { // Config file was found but another error was produced colorPrint(check.FAIL, fmt.Sprintf("Failed to read config file: %v\n", err)) os.Exit(1) } } } ================================================ FILE: cmd/run.go ================================================ package cmd import ( "fmt" "os" "path/filepath" "strings" "github.com/aquasecurity/kube-bench/check" "github.com/golang/glog" "github.com/spf13/cobra" "github.com/spf13/viper" ) func init() { RootCmd.AddCommand(runCmd) runCmd.Flags().StringSliceP("targets", "s", []string{}, `Specify targets of the benchmark to run. These names need to match the filenames in the cfg/ directory. For example, to run the tests specified in master.yaml and etcd.yaml, specify --targets=master,etcd If no targets are specified, run tests from all files in the cfg/ directory. `) } // runCmd represents the run command var runCmd = &cobra.Command{ Use: "run", Short: "Run tests", Long: `Run tests. If no arguments are specified, runs tests from all files`, Run: func(cmd *cobra.Command, args []string) { targets, err := cmd.Flags().GetStringSlice("targets") if err != nil { exitWithError(fmt.Errorf("unable to get `targets` from command line :%v", err)) } bv, err := getBenchmarkVersion(kubeVersion, benchmarkVersion, getPlatformInfo(), viper.GetViper()) if err != nil { exitWithError(fmt.Errorf("unable to get benchmark version. error: %v", err)) } glog.V(2).Infof("Checking targets %v for %v", targets, bv) benchmarkVersionToTargetsMap, err := loadTargetMapping(viper.GetViper()) if err != nil { exitWithError(fmt.Errorf("error loading targets: %v", err)) } valid, err := validTargets(bv, targets, viper.GetViper()) if err != nil { exitWithError(fmt.Errorf("error validating targets: %v", err)) } if len(targets) > 0 && !valid { exitWithError(fmt.Errorf(`The specified --targets "%s" are not configured for the CIS Benchmark %s\n Valid targets %v`, strings.Join(targets, ","), bv, benchmarkVersionToTargetsMap[bv])) } // Merge version-specific config if any. path := filepath.Join(cfgDir, bv) err = mergeConfig(path) if err != nil { exitWithError(fmt.Errorf("Error in mergeConfig: %v\n", err)) } err = run(targets, bv) if err != nil { exitWithError(fmt.Errorf("Error in run: %v\n", err)) } os.Exit(exitCodeSelection(controlsCollection)) }, } func run(targets []string, benchmarkVersion string) (err error) { yamlFiles, err := getTestYamlFiles(targets, benchmarkVersion) if err != nil { return err } glog.V(3).Infof("Running tests from files %v\n", yamlFiles) for _, yamlFile := range yamlFiles { _, name := filepath.Split(yamlFile) testType := check.NodeType(strings.Split(name, ".")[0]) runChecks(testType, yamlFile, detecetedKubeVersion) } writeOutput(controlsCollection) return nil } func getTestYamlFiles(targets []string, benchmarkVersion string) (yamlFiles []string, err error) { // Check that the specified targets have corresponding YAML files in the config directory configFileDirectory := filepath.Join(cfgDir, benchmarkVersion) for _, target := range targets { filename := translate(target) + ".yaml" file := filepath.Join(configFileDirectory, filename) if _, err := os.Stat(file); err != nil { return nil, fmt.Errorf("file %s not found for version %s", filename, benchmarkVersion) } yamlFiles = append(yamlFiles, file) } // If no targets were specified, we will run tests from all the files in the directory if len(yamlFiles) == 0 { yamlFiles, err = getYamlFilesFromDir(configFileDirectory) if err != nil { return nil, err } } return yamlFiles, err } func translate(target string) string { return strings.Replace(strings.ToLower(target), "worker", "node", -1) } ================================================ FILE: cmd/run_test.go ================================================ package cmd import ( "os" "path/filepath" "testing" ) func TestGetTestYamlFiles(t *testing.T) { cases := []struct { name string targets []string benchmark string succeed bool expCount int }{ { name: "Specify two targets", targets: []string{"one", "two"}, benchmark: "benchmark", succeed: true, expCount: 2, }, { name: "Specify a target that doesn't exist", targets: []string{"one", "missing"}, benchmark: "benchmark", succeed: false, }, { name: "No targets specified - should return everything except config.yaml", targets: []string{}, benchmark: "benchmark", succeed: true, expCount: 3, }, { name: "Specify benchmark that doesn't exist", targets: []string{"one"}, benchmark: "missing", succeed: false, }, } // Set up temp config directory var err error cfgDir, err = os.MkdirTemp("", "kube-bench-test") if err != nil { t.Fatalf("Failed to create temp directory") } defer os.RemoveAll(cfgDir) d := filepath.Join(cfgDir, "benchmark") err = os.Mkdir(d, 0766) if err != nil { t.Fatalf("Failed to create temp dir") } // We never expect config.yaml to be returned for _, filename := range []string{"one.yaml", "two.yaml", "three.yaml", "config.yaml"} { err = os.WriteFile(filepath.Join(d, filename), []byte("hello world"), 0666) if err != nil { t.Fatalf("error writing temp file %s: %v", filename, err) } } for _, c := range cases { t.Run(c.name, func(t *testing.T) { yamlFiles, err := getTestYamlFiles(c.targets, c.benchmark) if err != nil && c.succeed { t.Fatalf("Error %v", err) } if err == nil && !c.succeed { t.Fatalf("Expected failure") } if len(yamlFiles) != c.expCount { t.Fatalf("Expected %d, got %d", c.expCount, len(yamlFiles)) } }) } } func TestTranslate(t *testing.T) { cases := []struct { name string original string expected string }{ { name: "keep", original: "controlplane", expected: "controlplane", }, { name: "translate", original: "worker", expected: "node", }, { name: "translateLower", original: "Worker", expected: "node", }, { name: "Lower", original: "ETCD", expected: "etcd", }, } for _, c := range cases { t.Run(c.name, func(t *testing.T) { ret := translate(c.original) if ret != c.expected { t.Fatalf("Expected %q, got %q", c.expected, ret) } }) } } ================================================ FILE: cmd/securityHub.go ================================================ package cmd import ( "context" "fmt" "log" "github.com/aquasecurity/kube-bench/internal/findings" "github.com/aws/aws-sdk-go-v2/config" "github.com/aws/aws-sdk-go-v2/service/securityhub" "github.com/aws/aws-sdk-go-v2/service/securityhub/types" "github.com/spf13/viper" ) // REGION ... const REGION = "AWS_REGION" func writeFinding(in []types.AwsSecurityFinding) error { r := viper.GetString(REGION) if len(r) == 0 { return fmt.Errorf("%s not set", REGION) } cfg, err := config.LoadDefaultConfig(context.Background(), config.WithRegion(r)) if err != nil { return err } svc := securityhub.NewFromConfig(cfg) p := findings.New(*svc) out, perr := p.PublishFinding(in) print(out) return perr } func print(out *findings.PublisherOutput) { if out.SuccessCount > 0 { log.Printf("Number of findings that were successfully imported:%v\n", out.SuccessCount) } if out.FailedCount > 0 { log.Printf("Number of findings that failed to import:%v\n", out.FailedCount) for _, f := range out.FailedFindings { log.Printf("ID:%s", *f.Id) log.Printf("Message:%s", *f.ErrorMessage) log.Printf("Error Code:%s", *f.ErrorCode) } } } ================================================ FILE: cmd/testdata/controlsCollection.json ================================================ [ { "id": "2", "version": "1.15", "text": "Etcd Node Configuration", "node_type": "etcd", "tests": [ { "section": "2", "pass": 7, "fail": 0, "warn": 0, "info": 0, "desc": "Etcd Node Configuration Files", "results": [ { "test_number": "2.1", "test_desc": "Ensure that the --cert-file and --key-file arguments are set as appropriate (Scored)", "audit": "/bin/ps -ef | /bin/grep etcd | /bin/grep -v grep", "AuditConfig": "", "type": "", "remediation": "Follow the etcd service documentation and configure TLS encryption.\nThen, edit the etcd pod specification file /etc/kubernetes/manifests/etcd.yaml\non the master node and set the below parameters.\n--cert-file=\n--key-file=\n", "test_info": [ "Follow the etcd service documentation and configure TLS encryption.\nThen, edit the etcd pod specification file /etc/kubernetes/manifests/etcd.yaml\non the master node and set the below parameters.\n--cert-file=\n--key-file=\n" ], "status": "PASS", "actual_value": "root 3277 3218 3 Apr19 ? 03:57:52 etcd --advertise-client-urls=https://192.168.64.4:2379 --cert-file=/var/lib/minikube/certs/etcd/server.crt --client-cert-auth=true --data-dir=/var/lib/minikube/etcd --initial-advertise-peer-urls=https://192.168.64.4:2380 --initial-cluster=minikube=https://192.168.64.4:2380 --key-file=/var/lib/minikube/certs/etcd/server.key --listen-client-urls=https://127.0.0.1:2379,https://192.168.64.4:2379 --listen-metrics-urls=http://127.0.0.1:2381 --listen-peer-urls=https://192.168.64.4:2380 --name=minikube --peer-cert-file=/var/lib/minikube/certs/etcd/peer.crt --peer-client-cert-auth=true --peer-key-file=/var/lib/minikube/certs/etcd/peer.key --peer-trusted-ca-file=/var/lib/minikube/certs/etcd/ca.crt --snapshot-count=10000 --trusted-ca-file=/var/lib/minikube/certs/etcd/ca.crt\nroot 4624 4605 8 Apr21 ? 04:55:10 kube-apiserver --advertise-address=192.168.64.4 --allow-privileged=true --authorization-mode=Node,RBAC --client-ca-file=/var/lib/minikube/certs/ca.crt --enable-admission-plugins=NamespaceLifecycle,LimitRanger,ServiceAccount,DefaultStorageClass,DefaultTolerationSeconds,NodeRestriction,MutatingAdmissionWebhook,ValidatingAdmissionWebhook,ResourceQuota,PodSecurityPolicy --enable-bootstrap-token-auth=true --etcd-cafile=/var/lib/minikube/certs/etcd/ca.crt --etcd-certfile=/var/lib/minikube/certs/apiserver-etcd-client.crt --etcd-keyfile=/var/lib/minikube/certs/apiserver-etcd-client.key --etcd-servers=https://127.0.0.1:2379 --insecure-port=0 --kubelet-client-certificate=/var/lib/minikube/certs/apiserver-kubelet-client.crt --kubelet-client-key=/var/lib/minikube/certs/apiserver-kubelet-client.key --kubelet-preferred-address-types=InternalIP,ExternalIP,Hostname --proxy-client-cert-file=/var/lib/minikube/certs/front-proxy-client.crt --proxy-client-key-file=/var/lib/minikube/certs/front-proxy-client.key --requestheader-allowed-names=front-proxy-client --requestheader-client-ca-file=/var/lib/minikube/certs/front-proxy-ca.crt --requestheader-extra-headers-prefix=X-Remote-Extra- --requestheader-group-headers=X-Remote-Group --requestheader-username-headers=X-Remote-User --secure-port=8443 --service-account-key-file=/var/lib/minikube/certs/sa.pub --service-cluster-ip-range=10.96.0.0/12 --tls-cert-file=/var/lib/minikube/certs/apiserver.crt --tls-private-key-file=/var/lib/minikube/certs/apiserver.key\n", "scored": true, "expected_result": "'--cert-file' is present AND '--key-file' is present" } ] } ], "total_pass": 7, "total_fail": 0, "total_warn": 0, "total_info": 0 }, { "id": "3", "version": "1.5", "text": "Control Plane Configuration", "node_type": "controlplane", "tests": [ { "section": "3.1", "pass": 0, "fail": 0, "warn": 1, "info": 0, "desc": "Authentication and Authorization", "results": [ { "test_number": "3.1.1", "test_desc": "Client certificate authentication should not be used for users (Not Scored)", "audit": "", "AuditConfig": "", "type": "manual", "remediation": "Alternative mechanisms provided by Kubernetes such as the use of OIDC should be\nimplemented in place of client certificates.\n", "test_info": [ "Alternative mechanisms provided by Kubernetes such as the use of OIDC should be\nimplemented in place of client certificates.\n" ], "status": "WARN", "actual_value": "", "scored": false, "expected_result": "", "reason": "Test marked as a manual test" } ] } ], "total_pass": 0, "total_fail": 0, "total_warn": 3, "total_info": 0 }, { "id": "1", "version": "1.5", "text": "Master Node Security Configuration", "node_type": "master", "tests": [ { "section": "1.1", "pass": 15, "fail": 1, "warn": 5, "info": 0, "desc": "Master Node Configuration Files", "results": [ { "test_number": "1.1.1", "test_desc": "Ensure that the API server pod specification file permissions are set to 644 or more restrictive (Scored)", "audit": "/bin/sh -c 'if test -e /etc/kubernetes/manifests/kube-apiserver.yaml; then stat -c permissions=%a /etc/kubernetes/manifests/kube-apiserver.yaml; fi'", "AuditConfig": "", "type": "", "remediation": "Run the below command (based on the file location on your system) on the\nmaster node.\nFor example, chmod 644 /etc/kubernetes/manifests/kube-apiserver.yaml\n", "test_info": [ "Run the below command (based on the file location on your system) on the\nmaster node.\nFor example, chmod 644 /etc/kubernetes/manifests/kube-apiserver.yaml\n" ], "status": "PASS", "actual_value": "permissions=600\n", "scored": true, "expected_result": "bitmask '600' AND '644'" } ] } ], "total_pass": 42, "total_fail": 12, "total_warn": 11, "total_info": 0 } ] ================================================ FILE: cmd/testdata/passedControlsCollection.json ================================================ [ { "id": "2", "version": "1.15", "text": "Etcd Node Configuration", "node_type": "etcd", "tests": [ { "section": "2", "pass": 7, "fail": 0, "warn": 0, "info": 0, "desc": "Etcd Node Configuration Files", "results": [ { "test_number": "2.1", "test_desc": "Ensure that the --cert-file and --key-file arguments are set as appropriate (Scored)", "audit": "/bin/ps -ef | /bin/grep etcd | /bin/grep -v grep", "AuditConfig": "", "type": "", "remediation": "Follow the etcd service documentation and configure TLS encryption.\nThen, edit the etcd pod specification file /etc/kubernetes/manifests/etcd.yaml\non the master node and set the below parameters.\n--cert-file=\n--key-file=\n", "test_info": [ "Follow the etcd service documentation and configure TLS encryption.\nThen, edit the etcd pod specification file /etc/kubernetes/manifests/etcd.yaml\non the master node and set the below parameters.\n--cert-file=\n--key-file=\n" ], "status": "PASS", "actual_value": "root 3277 3218 3 Apr19 ? 03:57:52 etcd --advertise-client-urls=https://192.168.64.4:2379 --cert-file=/var/lib/minikube/certs/etcd/server.crt --client-cert-auth=true --data-dir=/var/lib/minikube/etcd --initial-advertise-peer-urls=https://192.168.64.4:2380 --initial-cluster=minikube=https://192.168.64.4:2380 --key-file=/var/lib/minikube/certs/etcd/server.key --listen-client-urls=https://127.0.0.1:2379,https://192.168.64.4:2379 --listen-metrics-urls=http://127.0.0.1:2381 --listen-peer-urls=https://192.168.64.4:2380 --name=minikube --peer-cert-file=/var/lib/minikube/certs/etcd/peer.crt --peer-client-cert-auth=true --peer-key-file=/var/lib/minikube/certs/etcd/peer.key --peer-trusted-ca-file=/var/lib/minikube/certs/etcd/ca.crt --snapshot-count=10000 --trusted-ca-file=/var/lib/minikube/certs/etcd/ca.crt\nroot 4624 4605 8 Apr21 ? 04:55:10 kube-apiserver --advertise-address=192.168.64.4 --allow-privileged=true --authorization-mode=Node,RBAC --client-ca-file=/var/lib/minikube/certs/ca.crt --enable-admission-plugins=NamespaceLifecycle,LimitRanger,ServiceAccount,DefaultStorageClass,DefaultTolerationSeconds,NodeRestriction,MutatingAdmissionWebhook,ValidatingAdmissionWebhook,ResourceQuota,PodSecurityPolicy --enable-bootstrap-token-auth=true --etcd-cafile=/var/lib/minikube/certs/etcd/ca.crt --etcd-certfile=/var/lib/minikube/certs/apiserver-etcd-client.crt --etcd-keyfile=/var/lib/minikube/certs/apiserver-etcd-client.key --etcd-servers=https://127.0.0.1:2379 --insecure-port=0 --kubelet-client-certificate=/var/lib/minikube/certs/apiserver-kubelet-client.crt --kubelet-client-key=/var/lib/minikube/certs/apiserver-kubelet-client.key --kubelet-preferred-address-types=InternalIP,ExternalIP,Hostname --proxy-client-cert-file=/var/lib/minikube/certs/front-proxy-client.crt --proxy-client-key-file=/var/lib/minikube/certs/front-proxy-client.key --requestheader-allowed-names=front-proxy-client --requestheader-client-ca-file=/var/lib/minikube/certs/front-proxy-ca.crt --requestheader-extra-headers-prefix=X-Remote-Extra- --requestheader-group-headers=X-Remote-Group --requestheader-username-headers=X-Remote-User --secure-port=8443 --service-account-key-file=/var/lib/minikube/certs/sa.pub --service-cluster-ip-range=10.96.0.0/12 --tls-cert-file=/var/lib/minikube/certs/apiserver.crt --tls-private-key-file=/var/lib/minikube/certs/apiserver.key\n", "scored": true, "expected_result": "'--cert-file' is present AND '--key-file' is present" } ] } ], "total_pass": 7, "total_fail": 0, "total_warn": 0, "total_info": 0 }, { "id": "3", "version": "1.5", "text": "Control Plane Configuration", "node_type": "controlplane", "tests": [ { "section": "3.1", "pass": 0, "fail": 0, "warn": 1, "info": 0, "desc": "Authentication and Authorization", "results": [ { "test_number": "3.1.1", "test_desc": "Client certificate authentication should not be used for users (Not Scored)", "audit": "", "AuditConfig": "", "type": "manual", "remediation": "Alternative mechanisms provided by Kubernetes such as the use of OIDC should be\nimplemented in place of client certificates.\n", "test_info": [ "Alternative mechanisms provided by Kubernetes such as the use of OIDC should be\nimplemented in place of client certificates.\n" ], "status": "WARN", "actual_value": "", "scored": false, "expected_result": "", "reason": "Test marked as a manual test" } ] } ], "total_pass": 0, "total_fail": 0, "total_warn": 3, "total_info": 0 } ] ================================================ FILE: cmd/testdata/result.json ================================================ { "Controls": [ { "id": "1", "version": "1.5", "text": "Master Node Security Configuration", "node_type": "master", "tests": [ { "section": "1.1", "pass": 15, "fail": 1, "warn": 5, "info": 0, "desc": "Master Node Configuration Files", "results": [ { "test_number": "1.1.1", "test_desc": "Ensure that the API server pod specification file permissions are set to 644 or more restrictive (Scored)", "audit": "/bin/sh -c 'if test -e /etc/kubernetes/manifests/kube-apiserver.yaml; then stat -c permissions=%a /etc/kubernetes/manifests/kube-apiserver.yaml; fi'", "AuditConfig": "", "type": "", "remediation": "Run the below command (based on the file location on your system) on the\nmaster node.\nFor example, chmod 644 /etc/kubernetes/manifests/kube-apiserver.yaml\n", "test_info": [ "Run the below command (based on the file location on your system) on the\nmaster node.\nFor example, chmod 644 /etc/kubernetes/manifests/kube-apiserver.yaml\n" ], "status": "PASS", "actual_value": "permissions=600\n", "scored": true, "expected_result": "bitmask '600' AND '644'" } ] } ], "total_pass": 42, "total_fail": 12, "total_warn": 11, "total_info": 0 }, { "id": "2", "version": "1.15", "text": "Etcd Node Configuration", "node_type": "etcd", "tests": [ { "section": "2", "pass": 7, "fail": 0, "warn": 0, "info": 0, "desc": "Etcd Node Configuration Files", "results": [ { "test_number": "2.1", "test_desc": "Ensure that the --cert-file and --key-file arguments are set as appropriate (Scored)", "audit": "/bin/ps -ef | /bin/grep etcd | /bin/grep -v grep", "AuditConfig": "", "type": "", "remediation": "Follow the etcd service documentation and configure TLS encryption.\nThen, edit the etcd pod specification file /etc/kubernetes/manifests/etcd.yaml\non the master node and set the below parameters.\n--cert-file=\n--key-file=\n", "test_info": [ "Follow the etcd service documentation and configure TLS encryption.\nThen, edit the etcd pod specification file /etc/kubernetes/manifests/etcd.yaml\non the master node and set the below parameters.\n--cert-file=\n--key-file=\n" ], "status": "PASS", "actual_value": "root 3277 3218 3 Apr19 ? 03:57:52 etcd --advertise-client-urls=https://192.168.64.4:2379 --cert-file=/var/lib/minikube/certs/etcd/server.crt --client-cert-auth=true --data-dir=/var/lib/minikube/etcd --initial-advertise-peer-urls=https://192.168.64.4:2380 --initial-cluster=minikube=https://192.168.64.4:2380 --key-file=/var/lib/minikube/certs/etcd/server.key --listen-client-urls=https://127.0.0.1:2379,https://192.168.64.4:2379 --listen-metrics-urls=http://127.0.0.1:2381 --listen-peer-urls=https://192.168.64.4:2380 --name=minikube --peer-cert-file=/var/lib/minikube/certs/etcd/peer.crt --peer-client-cert-auth=true --peer-key-file=/var/lib/minikube/certs/etcd/peer.key --peer-trusted-ca-file=/var/lib/minikube/certs/etcd/ca.crt --snapshot-count=10000 --trusted-ca-file=/var/lib/minikube/certs/etcd/ca.crt\nroot 4624 4605 8 Apr21 ? 04:55:10 kube-apiserver --advertise-address=192.168.64.4 --allow-privileged=true --authorization-mode=Node,RBAC --client-ca-file=/var/lib/minikube/certs/ca.crt --enable-admission-plugins=NamespaceLifecycle,LimitRanger,ServiceAccount,DefaultStorageClass,DefaultTolerationSeconds,NodeRestriction,MutatingAdmissionWebhook,ValidatingAdmissionWebhook,ResourceQuota,PodSecurityPolicy --enable-bootstrap-token-auth=true --etcd-cafile=/var/lib/minikube/certs/etcd/ca.crt --etcd-certfile=/var/lib/minikube/certs/apiserver-etcd-client.crt --etcd-keyfile=/var/lib/minikube/certs/apiserver-etcd-client.key --etcd-servers=https://127.0.0.1:2379 --insecure-port=0 --kubelet-client-certificate=/var/lib/minikube/certs/apiserver-kubelet-client.crt --kubelet-client-key=/var/lib/minikube/certs/apiserver-kubelet-client.key --kubelet-preferred-address-types=InternalIP,ExternalIP,Hostname --proxy-client-cert-file=/var/lib/minikube/certs/front-proxy-client.crt --proxy-client-key-file=/var/lib/minikube/certs/front-proxy-client.key --requestheader-allowed-names=front-proxy-client --requestheader-client-ca-file=/var/lib/minikube/certs/front-proxy-ca.crt --requestheader-extra-headers-prefix=X-Remote-Extra- --requestheader-group-headers=X-Remote-Group --requestheader-username-headers=X-Remote-User --secure-port=8443 --service-account-key-file=/var/lib/minikube/certs/sa.pub --service-cluster-ip-range=10.96.0.0/12 --tls-cert-file=/var/lib/minikube/certs/apiserver.crt --tls-private-key-file=/var/lib/minikube/certs/apiserver.key\n", "scored": true, "expected_result": "'--cert-file' is present AND '--key-file' is present" } ] } ], "total_pass": 7, "total_fail": 0, "total_warn": 0, "total_info": 0 }, { "id": "3", "version": "1.5", "text": "Control Plane Configuration", "node_type": "controlplane", "tests": [ { "section": "3.1", "pass": 0, "fail": 0, "warn": 1, "info": 0, "desc": "Authentication and Authorization", "results": [ { "test_number": "3.1.1", "test_desc": "Client certificate authentication should not be used for users (Not Scored)", "audit": "", "AuditConfig": "", "type": "manual", "remediation": "Alternative mechanisms provided by Kubernetes such as the use of OIDC should be\nimplemented in place of client certificates.\n", "test_info": [ "Alternative mechanisms provided by Kubernetes such as the use of OIDC should be\nimplemented in place of client certificates.\n" ], "status": "WARN", "actual_value": "", "scored": false, "expected_result": "", "reason": "Test marked as a manual test" } ] } ], "total_pass": 0, "total_fail": 0, "total_warn": 3, "total_info": 0 } ], "Totals": { "total_pass": 49, "total_fail": 12, "total_warn": 14, "total_info": 0 } } ================================================ FILE: cmd/testdata/result_no_totals.json ================================================ [ { "id": "1", "version": "1.5", "text": "Master Node Security Configuration", "node_type": "master", "tests": [ { "section": "1.1", "pass": 15, "fail": 1, "warn": 5, "info": 0, "desc": "Master Node Configuration Files", "results": [ { "test_number": "1.1.1", "test_desc": "Ensure that the API server pod specification file permissions are set to 644 or more restrictive (Scored)", "audit": "/bin/sh -c 'if test -e /etc/kubernetes/manifests/kube-apiserver.yaml; then stat -c permissions=%a /etc/kubernetes/manifests/kube-apiserver.yaml; fi'", "AuditConfig": "", "type": "", "remediation": "Run the below command (based on the file location on your system) on the\nmaster node.\nFor example, chmod 644 /etc/kubernetes/manifests/kube-apiserver.yaml\n", "test_info": [ "Run the below command (based on the file location on your system) on the\nmaster node.\nFor example, chmod 644 /etc/kubernetes/manifests/kube-apiserver.yaml\n" ], "status": "PASS", "actual_value": "permissions=600\n", "scored": true, "expected_result": "bitmask '600' AND '644'" } ] } ], "total_pass": 42, "total_fail": 12, "total_warn": 11, "total_info": 0 }, { "id": "2", "version": "1.15", "text": "Etcd Node Configuration", "node_type": "etcd", "tests": [ { "section": "2", "pass": 7, "fail": 0, "warn": 0, "info": 0, "desc": "Etcd Node Configuration Files", "results": [ { "test_number": "2.1", "test_desc": "Ensure that the --cert-file and --key-file arguments are set as appropriate (Scored)", "audit": "/bin/ps -ef | /bin/grep etcd | /bin/grep -v grep", "AuditConfig": "", "type": "", "remediation": "Follow the etcd service documentation and configure TLS encryption.\nThen, edit the etcd pod specification file /etc/kubernetes/manifests/etcd.yaml\non the master node and set the below parameters.\n--cert-file=\n--key-file=\n", "test_info": [ "Follow the etcd service documentation and configure TLS encryption.\nThen, edit the etcd pod specification file /etc/kubernetes/manifests/etcd.yaml\non the master node and set the below parameters.\n--cert-file=\n--key-file=\n" ], "status": "PASS", "actual_value": "root 3277 3218 3 Apr19 ? 03:57:52 etcd --advertise-client-urls=https://192.168.64.4:2379 --cert-file=/var/lib/minikube/certs/etcd/server.crt --client-cert-auth=true --data-dir=/var/lib/minikube/etcd --initial-advertise-peer-urls=https://192.168.64.4:2380 --initial-cluster=minikube=https://192.168.64.4:2380 --key-file=/var/lib/minikube/certs/etcd/server.key --listen-client-urls=https://127.0.0.1:2379,https://192.168.64.4:2379 --listen-metrics-urls=http://127.0.0.1:2381 --listen-peer-urls=https://192.168.64.4:2380 --name=minikube --peer-cert-file=/var/lib/minikube/certs/etcd/peer.crt --peer-client-cert-auth=true --peer-key-file=/var/lib/minikube/certs/etcd/peer.key --peer-trusted-ca-file=/var/lib/minikube/certs/etcd/ca.crt --snapshot-count=10000 --trusted-ca-file=/var/lib/minikube/certs/etcd/ca.crt\nroot 4624 4605 8 Apr21 ? 04:55:10 kube-apiserver --advertise-address=192.168.64.4 --allow-privileged=true --authorization-mode=Node,RBAC --client-ca-file=/var/lib/minikube/certs/ca.crt --enable-admission-plugins=NamespaceLifecycle,LimitRanger,ServiceAccount,DefaultStorageClass,DefaultTolerationSeconds,NodeRestriction,MutatingAdmissionWebhook,ValidatingAdmissionWebhook,ResourceQuota,PodSecurityPolicy --enable-bootstrap-token-auth=true --etcd-cafile=/var/lib/minikube/certs/etcd/ca.crt --etcd-certfile=/var/lib/minikube/certs/apiserver-etcd-client.crt --etcd-keyfile=/var/lib/minikube/certs/apiserver-etcd-client.key --etcd-servers=https://127.0.0.1:2379 --insecure-port=0 --kubelet-client-certificate=/var/lib/minikube/certs/apiserver-kubelet-client.crt --kubelet-client-key=/var/lib/minikube/certs/apiserver-kubelet-client.key --kubelet-preferred-address-types=InternalIP,ExternalIP,Hostname --proxy-client-cert-file=/var/lib/minikube/certs/front-proxy-client.crt --proxy-client-key-file=/var/lib/minikube/certs/front-proxy-client.key --requestheader-allowed-names=front-proxy-client --requestheader-client-ca-file=/var/lib/minikube/certs/front-proxy-ca.crt --requestheader-extra-headers-prefix=X-Remote-Extra- --requestheader-group-headers=X-Remote-Group --requestheader-username-headers=X-Remote-User --secure-port=8443 --service-account-key-file=/var/lib/minikube/certs/sa.pub --service-cluster-ip-range=10.96.0.0/12 --tls-cert-file=/var/lib/minikube/certs/apiserver.crt --tls-private-key-file=/var/lib/minikube/certs/apiserver.key\n", "scored": true, "expected_result": "'--cert-file' is present AND '--key-file' is present" } ] } ], "total_pass": 7, "total_fail": 0, "total_warn": 0, "total_info": 0 }, { "id": "3", "version": "1.5", "text": "Control Plane Configuration", "node_type": "controlplane", "tests": [ { "section": "3.1", "pass": 0, "fail": 0, "warn": 1, "info": 0, "desc": "Authentication and Authorization", "results": [ { "test_number": "3.1.1", "test_desc": "Client certificate authentication should not be used for users (Not Scored)", "audit": "", "AuditConfig": "", "type": "manual", "remediation": "Alternative mechanisms provided by Kubernetes such as the use of OIDC should be\nimplemented in place of client certificates.\n", "test_info": [ "Alternative mechanisms provided by Kubernetes such as the use of OIDC should be\nimplemented in place of client certificates.\n" ], "status": "WARN", "actual_value": "", "scored": false, "expected_result": "", "reason": "Test marked as a manual test" } ] } ], "total_pass": 0, "total_fail": 0, "total_warn": 3, "total_info": 0 } ] ================================================ FILE: cmd/util.go ================================================ package cmd import ( "context" "encoding/json" "fmt" "os" "os/exec" "path/filepath" "regexp" "strconv" "strings" "github.com/aquasecurity/kube-bench/check" "github.com/fatih/color" "github.com/golang/glog" "github.com/spf13/viper" "golang.org/x/exp/slices" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/kubernetes" "k8s.io/client-go/rest" ) // Print colors var colors = map[check.State]*color.Color{ check.PASS: color.New(color.FgGreen), check.FAIL: color.New(color.FgRed), check.WARN: color.New(color.FgYellow), check.INFO: color.New(color.FgBlue), } var ( psFunc func(string) string statFunc func(string) (os.FileInfo, error) getBinariesFunc func(*viper.Viper, check.NodeType) (map[string]string, error) TypeMap = map[string][]string{ "ca": {"cafile", "defaultcafile"}, "kubeconfig": {"kubeconfig", "defaultkubeconfig"}, "service": {"svc", "defaultsvc"}, "config": {"confs", "defaultconf"}, "datadir": {"datadirs", "defaultdatadir"}, } ) func init() { psFunc = ps statFunc = os.Stat getBinariesFunc = getBinaries } type Platform struct { Name string Version string } func (p Platform) String() string { return fmt.Sprintf("Platform{ Name: %s Version: %s }", p.Name, p.Version) } func exitWithError(err error) { fmt.Fprintf(os.Stderr, "\n%v\n", err) // flush before exit non-zero glog.Flush() os.Exit(1) } func cleanIDs(list string) map[string]bool { list = strings.Trim(list, ",") ids := strings.Split(list, ",") set := make(map[string]bool) for _, id := range ids { id = strings.Trim(id, " ") set[id] = true } return set } // ps execs out to the ps command; it's separated into a function so we can write tests func ps(proc string) string { // TODO: truncate proc to 15 chars // See https://github.com/aquasecurity/kube-bench/issues/328#issuecomment-506813344 glog.V(2).Info(fmt.Sprintf("ps - proc: %q", proc)) cmd := exec.Command("/bin/ps", "-C", proc, "-o", "cmd", "--no-headers") out, err := cmd.Output() if err != nil { glog.V(2).Info(fmt.Errorf("%s: %s", cmd.Args, err)) } glog.V(2).Info(fmt.Sprintf("ps - returning: %q", string(out))) return string(out) } // getBinaries finds which of the set of candidate executables are running. // It returns an error if one mandatory executable is not running. func getBinaries(v *viper.Viper, nodetype check.NodeType) (map[string]string, error) { binmap := make(map[string]string) for _, component := range v.GetStringSlice("components") { s := v.Sub(component) if s == nil { continue } optional := s.GetBool("optional") bins := s.GetStringSlice("bins") if len(bins) > 0 { bin, err := findExecutable(bins) if err != nil && !optional { glog.V(1).Info(buildComponentMissingErrorMessage(nodetype, component, bins)) return nil, fmt.Errorf("unable to detect running programs for component %q", component) } // Default the executable name that we'll substitute to the name of the component if bin == "" { bin = component glog.V(2).Info(fmt.Sprintf("Component %s not running", component)) } else { glog.V(2).Info(fmt.Sprintf("Component %s uses running binary %s", component, bin)) } binmap[component] = bin } } return binmap, nil } // getConfigFilePath locates the config files we should be using for CIS version func getConfigFilePath(benchmarkVersion string, filename string) (path string, err error) { glog.V(2).Info(fmt.Sprintf("Looking for config specific CIS version %q", benchmarkVersion)) path = filepath.Join(cfgDir, benchmarkVersion) file := filepath.Join(path, filename) glog.V(2).Info(fmt.Sprintf("Looking for file: %s", file)) if _, err := os.Stat(file); err != nil { glog.V(2).Infof("error accessing config file: %q error: %v\n", file, err) return "", fmt.Errorf("no test files found <= benchmark version: %s", benchmarkVersion) } return path, nil } // getYamlFilesFromDir returns a list of yaml files in the specified directory, ignoring config.yaml func getYamlFilesFromDir(path string) (names []string, err error) { err = filepath.Walk(path, func(path string, info os.FileInfo, err error) error { if err != nil { return err } _, name := filepath.Split(path) if name != "" && name != "config.yaml" && filepath.Ext(name) == ".yaml" { names = append(names, path) } return nil }) return names, err } // decrementVersion decrements the version number // We want to decrement individually even through versions where we don't supply test files // just in case someone wants to specify their own test files for that version func decrementVersion(version string) string { split := strings.Split(version, ".") if len(split) < 2 { return "" } minor, err := strconv.Atoi(split[1]) if err != nil { return "" } if minor <= 1 { return "" } split[1] = strconv.Itoa(minor - 1) return strings.Join(split, ".") } // getFiles finds which of the set of candidate files exist func getFiles(v *viper.Viper, fileType string) map[string]string { filemap := make(map[string]string) mainOpt := TypeMap[fileType][0] defaultOpt := TypeMap[fileType][1] for _, component := range v.GetStringSlice("components") { s := v.Sub(component) if s == nil { continue } // See if any of the candidate files exist file := findConfigFile(s.GetStringSlice(mainOpt)) if file == "" { if s.IsSet(defaultOpt) { file = s.GetString(defaultOpt) glog.V(2).Info(fmt.Sprintf("Using default %s file name '%s' for component %s", fileType, file, component)) } else { // Default the file name that we'll substitute to the name of the component glog.V(2).Info(fmt.Sprintf("Missing %s file for %s", fileType, component)) file = component } } else { glog.V(2).Info(fmt.Sprintf("Component %s uses %s file '%s'", component, fileType, file)) } filemap[component] = file } return filemap } // verifyBin checks that the binary specified is running func verifyBin(bin string) bool { // Strip any quotes bin = strings.Trim(bin, "'\"") // bin could consist of more than one word // We'll search for running processes with the first word, and then check the whole // proc as supplied is included in the results proc := strings.Fields(bin)[0] out := psFunc(proc) // There could be multiple lines in the ps output // The binary needs to be the first word in the ps output, except that it could be preceded by a path // e.g. /usr/bin/kubelet is a match for kubelet // but apiserver is not a match for kube-apiserver reFirstWord := regexp.MustCompile(`^(\S*\/)*` + bin) lines := strings.Split(out, "\n") for _, l := range lines { glog.V(3).Info(fmt.Sprintf("reFirstWord.Match(%s)", l)) if reFirstWord.Match([]byte(l)) { return true } } return false } // fundConfigFile looks through a list of possible config files and finds the first one that exists func findConfigFile(candidates []string) string { for _, c := range candidates { _, err := statFunc(c) if err == nil { return c } if !os.IsNotExist(err) && !strings.HasSuffix(err.Error(), "not a directory") { exitWithError(fmt.Errorf("error looking for file %s: %v", c, err)) } } return "" } // findExecutable looks through a list of possible executable names and finds the first one that's running func findExecutable(candidates []string) (string, error) { for _, c := range candidates { if verifyBin(c) { return c, nil } glog.V(1).Info(fmt.Sprintf("executable '%s' not running", c)) } return "", fmt.Errorf("no candidates running") } func multiWordReplace(s string, subname string, sub string) string { f := strings.Fields(sub) if len(f) > 1 { sub = "'" + sub + "'" } return strings.Replace(s, subname, sub, -1) } const missingKubectlKubeletMessage = ` Unable to find the programs kubectl or kubelet in the PATH. These programs are used to determine which version of Kubernetes is running. Make sure the /usr/local/mount-from-host/bin directory is mapped to the container, either in the job.yaml file, or Docker command. For job.yaml: ... - name: usr-bin mountPath: /usr/local/mount-from-host/bin ... For docker command: docker -v $(which kubectl):/usr/local/mount-from-host/bin/kubectl .... Alternatively, you can specify the version with --version kube-bench --version ... ` func getKubeVersion() (*KubeVersion, error) { kubeConfig, err := rest.InClusterConfig() if err != nil { glog.V(3).Infof("Error fetching cluster config: %s", err) } isRKE := false isAKS := false if err == nil && kubeConfig != nil { k8sClient, err := kubernetes.NewForConfig(kubeConfig) if err != nil { glog.V(3).Infof("Failed to fetch k8sClient object from kube config : %s", err) } if err == nil { isRKE, err = IsRKE(context.Background(), k8sClient) if err != nil { glog.V(3).Infof("Error detecting RKE cluster: %s", err) } isAKS, err = IsAKS(context.Background(), k8sClient) if err != nil { glog.V(3).Infof("Error detecting AKS cluster: %s", err) } } } if k8sVer, err := getKubeVersionFromRESTAPI(); err == nil { glog.V(2).Info(fmt.Sprintf("Kubernetes REST API Reported version: %s", k8sVer)) if isRKE { k8sVer.GitVersion = k8sVer.GitVersion + "-rancher1" } if isAKS { k8sVer.GitVersion = k8sVer.GitVersion + "-aks1" } return k8sVer, nil } // These executables might not be on the user's path. _, err = exec.LookPath("kubectl") if err != nil { glog.V(3).Infof("Error locating kubectl: %s", err) _, err = exec.LookPath("kubelet") if err != nil { glog.V(3).Infof("Error locating kubelet: %s", err) // Search for the kubelet binary all over the filesystem and run the first match to get the kubernetes version cmd := exec.Command("/bin/sh", "-c", "`find / -type f -executable -name kubelet 2>/dev/null | grep -m1 .` --version") out, err := cmd.CombinedOutput() if err == nil { glog.V(3).Infof("Found kubelet and query kubernetes version is: %s", string(out)) return getVersionFromKubeletOutput(string(out)), nil } glog.Warning(missingKubectlKubeletMessage) glog.V(1).Info("unable to find the programs kubectl or kubelet in the PATH") glog.V(1).Infof("Cant detect version, assuming default %s", defaultKubeVersion) return &KubeVersion{baseVersion: defaultKubeVersion}, nil } return getKubeVersionFromKubelet(), nil } return getKubeVersionFromKubectl(), nil } func getKubeVersionFromKubectl() *KubeVersion { cmd := exec.Command("kubectl", "version", "-o", "json") out, err := cmd.CombinedOutput() if err != nil { glog.V(2).Infof("Failed to query kubectl: %s", err) glog.V(2).Info(err) } return getVersionFromKubectlOutput(string(out)) } func getKubeVersionFromKubelet() *KubeVersion { cmd := exec.Command("kubelet", "--version") out, err := cmd.CombinedOutput() if err != nil { glog.V(2).Infof("Failed to query kubelet: %s", err) glog.V(2).Info(err) } return getVersionFromKubeletOutput(string(out)) } func getVersionFromKubectlOutput(s string) *KubeVersion { glog.V(2).Infof("Kubectl output: %s", s) type versionResult struct { ServerVersion VersionResponse } vrObj := &versionResult{} if err := json.Unmarshal([]byte(s), vrObj); err != nil { glog.V(2).Info(err) if strings.Contains(s, "The connection to the server") { msg := fmt.Sprintf(`Warning: Kubernetes version was not auto-detected because kubectl could not connect to the Kubernetes server. This may be because the kubeconfig information is missing or has credentials that do not match the server. Assuming default version %s`, defaultKubeVersion) fmt.Fprintln(os.Stderr, msg) } glog.V(1).Info(fmt.Sprintf("Unable to get Kubernetes version from kubectl, using default version: %s", defaultKubeVersion)) return &KubeVersion{baseVersion: defaultKubeVersion} } sv := vrObj.ServerVersion return &KubeVersion{ Major: sv.Major, Minor: sv.Minor, GitVersion: sv.GitVersion, } } func getVersionFromKubeletOutput(s string) *KubeVersion { glog.V(2).Infof("Kubelet output: %s", s) serverVersionRe := regexp.MustCompile(`Kubernetes v(\d+.\d+)`) subs := serverVersionRe.FindStringSubmatch(s) if len(subs) < 2 { glog.V(1).Info(fmt.Sprintf("Unable to get Kubernetes version from kubelet, using default version: %s", defaultKubeVersion)) return &KubeVersion{baseVersion: defaultKubeVersion} } return &KubeVersion{baseVersion: subs[1]} } func makeSubstitutions(s string, ext string, m map[string]string) (string, []string) { substitutions := make([]string, 0) for k, v := range m { subst := "$" + k + ext if v == "" { glog.V(2).Info(fmt.Sprintf("No substitution for '%s'\n", subst)) continue } glog.V(2).Info(fmt.Sprintf("Substituting %s with '%s'\n", subst, v)) beforeS := s s = multiWordReplace(s, subst, v) if beforeS != s { substitutions = append(substitutions, v) } } return s, substitutions } func isEmpty(str string) bool { return strings.TrimSpace(str) == "" } func buildComponentMissingErrorMessage(nodetype check.NodeType, component string, bins []string) string { errMessageTemplate := ` Unable to detect running programs for component %q The following %q programs have been searched, but none of them have been found: %s These program names are provided in the config.yaml, section '%s.%s.bins' ` var componentRoleName, componentType string switch nodetype { case check.NODE: componentRoleName = "worker node" componentType = "node" case check.ETCD: componentRoleName = "etcd node" componentType = "etcd" default: componentRoleName = "master node" componentType = "master" } binList := "" for _, bin := range bins { binList = fmt.Sprintf("%s\t- %s\n", binList, bin) } return fmt.Sprintf(errMessageTemplate, component, componentRoleName, binList, componentType, component) } func getPlatformInfo() Platform { openShiftInfo := getOpenShiftInfo() if openShiftInfo.Name != "" && openShiftInfo.Version != "" { return openShiftInfo } kv, err := getKubeVersion() if err != nil { glog.V(2).Info(err) return Platform{} } return getPlatformInfoFromVersion(kv.GitVersion) } func getPlatformInfoFromVersion(s string) Platform { versionRe := regexp.MustCompile(`v(\d+\.\d+)\.\d+[-+](\w+)(?:[.\-+]*)\w+`) subs := versionRe.FindStringSubmatch(s) if len(subs) < 3 { return Platform{} } return Platform{ Name: subs[2], Version: subs[1], } } func IsAKS(ctx context.Context, k8sClient kubernetes.Interface) (bool, error) { nodes, err := k8sClient.CoreV1().Nodes().List(ctx, metav1.ListOptions{Limit: 1}) if err != nil { return false, err } if len(nodes.Items) == 0 { return false, nil } node := nodes.Items[0] labels := node.Labels if _, exists := labels["kubernetes.azure.com/cluster"]; exists { return true, nil } if strings.HasPrefix(node.Spec.ProviderID, "azure://") { return true, nil } return false, nil } func getPlatformBenchmarkVersion(platform Platform) string { glog.V(3).Infof("getPlatformBenchmarkVersion platform: %s", platform) switch platform.Name { case "eks": return eksBenchmark(platform.Version) case "aks": return aksBenchmark(platform.Version) case "gke": return gkeBenchmark(platform.Version) case "ocp": return ocpBenchmark(platform.Version) case "k3s": return k3sBenchmark(platform.Version) case "rancher": return rkeBenchmark(platform.Version) case "rke2r": return rke2Benchmark(platform.Version) case "aliyun": return "ack-1.0" case "vmware": return "tkgi-1.2.53" default: return "" } } func eksBenchmark(version string) string { switch version { case "1.15", "1.16", "1.17", "1.18", "1.19": return "eks-1.0.1" case "1.29", "1.30", "1.31": return "eks-1.7.0" case "1.32", "1.33", "1.34": return "eks-1.8.0" default: return "eks-1.5.0" } } func aksBenchmark(version string) string { switch version { case "1.19", "1.20", "1.21", "1.22", "1.23", "1.24": return "aks-1.0" case "1.29", "1.30", "1.31": return "aks-1.7" default: return "aks-1.8" } } func gkeBenchmark(version string) string { switch version { case "1.15", "1.16", "1.17", "1.18", "1.19": return "gke-1.0" case "1.28", "1.29", "1.30": return "gke-1.6.0" case "1.31", "1.32", "1.33", "1.34": return "gke-1.8.0" default: return "gke-1.2.0" } } func ocpBenchmark(version string) string { switch version { case "3.10": return "rh-0.7" case "4.1": return "rh-1.0" case "4.11": return "rh-1.4" case "4.13": return "rh-1.8" default: return "" } } func k3sBenchmark(version string) string { switch version { case "1.23": return "k3s-cis-1.23" case "1.24": return "k3s-cis-1.24" case "1.25", "1.26", "1.27": return "k3s-cis-1.7" default: return "" } } func rkeBenchmark(version string) string { switch version { case "1.23": return "rke-cis-1.23" case "1.24": return "rke-cis-1.24" case "1.25", "1.26", "1.27": return "rke-cis-1.7" default: return "rke-cis-1.7" } } func rke2Benchmark(version string) string { switch version { case "1.23": return "rke2-cis-1.23" case "1.24": return "rke2-cis-1.24" case "1.25": return "rke2-cis-1.7" case "1.26", "1.27": return "rke2-cis-1.8" default: return "rke2-cis-1.8" } } func getOpenShiftInfo() Platform { glog.V(1).Info("Checking for oc") _, err := exec.LookPath("oc") if err == nil { cmd := exec.Command("oc", "version") out, err := cmd.CombinedOutput() if err == nil { versionRe := regexp.MustCompile(`oc v(\d+\.\d+)`) subs := versionRe.FindStringSubmatch(string(out)) if len(subs) < 1 { versionRe = regexp.MustCompile(`Client Version:\s*(\d+\.\d+)`) subs = versionRe.FindStringSubmatch(string(out)) } if len(subs) > 1 { glog.V(2).Infof("OCP output '%s' \nplatform is %s \nocp %v", string(out), getPlatformInfoFromVersion(string(out)), subs[1]) ocpBenchmarkVersion, err := getOcpValidVersion(subs[1]) if err == nil { return Platform{Name: "ocp", Version: ocpBenchmarkVersion} } else { glog.V(1).Infof("Can't get getOcpValidVersion: %v", err) } } else { glog.V(1).Infof("Can't parse version output: %v", subs) } } else { glog.V(1).Infof("Can't use oc command: %v", err) } } else { glog.V(1).Infof("Can't find oc command: %v", err) } return Platform{} } func getOcpValidVersion(ocpVer string) (string, error) { ocpOriginal := ocpVer valid := []string{"3.10", "4.1", "4.11", "4.13"} for !isEmpty(ocpVer) { glog.V(3).Info(fmt.Sprintf("getOcpBenchmarkVersion check for ocp: %q \n", ocpVer)) if slices.Contains(valid, ocpVer) { glog.V(1).Info(fmt.Sprintf("getOcpBenchmarkVersion found valid version for ocp: %q \n", ocpVer)) return ocpVer, nil } ocpVer = decrementVersion(ocpVer) } glog.V(1).Info(fmt.Sprintf("getOcpBenchmarkVersion unable to find a match for: %q", ocpOriginal)) return "", fmt.Errorf("unable to find a matching Benchmark Version match for ocp version: %s", ocpOriginal) } // IsRKE Identifies if the cluster belongs to Rancher Distribution RKE func IsRKE(ctx context.Context, k8sClient kubernetes.Interface) (bool, error) { // if there are windows nodes then this should not be counted as rke.linux windowsNodes, err := k8sClient.CoreV1().Nodes().List(ctx, metav1.ListOptions{ Limit: 1, LabelSelector: "kubernetes.io/os=windows", }) if err != nil { return false, err } if len(windowsNodes.Items) != 0 { return false, nil } // Any node created by RKE should have the annotation, so just grab 1 nodes, err := k8sClient.CoreV1().Nodes().List(ctx, metav1.ListOptions{Limit: 1}) if err != nil { return false, err } if len(nodes.Items) == 0 { return false, nil } annos := nodes.Items[0].Annotations if _, ok := annos["rke.cattle.io/external-ip"]; ok { return true, nil } if _, ok := annos["rke.cattle.io/internal-ip"]; ok { return true, nil } return false, nil } ================================================ FILE: cmd/util_test.go ================================================ // Copyright © 2017 Aqua Security Software Ltd. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package cmd import ( "errors" "os" "path/filepath" "reflect" "sort" "strconv" "testing" "github.com/magiconair/properties/assert" "github.com/aquasecurity/kube-bench/check" "github.com/spf13/viper" ) var ( g string e []error eIndex int ) func fakeps(proc string) string { return g } func fakestat(file string) (os.FileInfo, error) { err := e[eIndex] eIndex++ return nil, err } func TestVerifyBin(t *testing.T) { cases := []struct { proc string psOut string exp bool }{ {proc: "single", psOut: "single", exp: true}, {proc: "single", psOut: "", exp: false}, {proc: "two words", psOut: "two words", exp: true}, {proc: "two words", psOut: "", exp: false}, {proc: "cmd", psOut: "cmd param1 param2", exp: true}, {proc: "cmd param", psOut: "cmd param1 param2", exp: true}, {proc: "cmd param", psOut: "cmd", exp: false}, {proc: "cmd", psOut: "cmd x \ncmd y", exp: true}, {proc: "cmd y", psOut: "cmd x \ncmd y", exp: true}, {proc: "cmd", psOut: "/usr/bin/cmd", exp: true}, {proc: "cmd", psOut: "kube-cmd", exp: false}, {proc: "cmd", psOut: "/usr/bin/kube-cmd", exp: false}, } psFunc = fakeps for id, c := range cases { t.Run(strconv.Itoa(id), func(t *testing.T) { g = c.psOut v := verifyBin(c.proc) if v != c.exp { t.Fatalf("Expected %v got %v", c.exp, v) } }) } } func TestFindExecutable(t *testing.T) { cases := []struct { candidates []string // list of executables we'd consider psOut string // fake output from ps exp string // the one we expect to find in the (fake) ps output expErr bool }{ {candidates: []string{"one", "two", "three"}, psOut: "two", exp: "two"}, {candidates: []string{"one", "two", "three"}, psOut: "two three", exp: "two"}, {candidates: []string{"one double", "two double", "three double"}, psOut: "two double is running", exp: "two double"}, {candidates: []string{"one", "two", "three"}, psOut: "blah", expErr: true}, {candidates: []string{"one double", "two double", "three double"}, psOut: "two", expErr: true}, {candidates: []string{"apiserver", "kube-apiserver"}, psOut: "kube-apiserver", exp: "kube-apiserver"}, {candidates: []string{"apiserver", "kube-apiserver", "hyperkube-apiserver"}, psOut: "kube-apiserver", exp: "kube-apiserver"}, } psFunc = fakeps for id, c := range cases { t.Run(strconv.Itoa(id), func(t *testing.T) { g = c.psOut e, err := findExecutable(c.candidates) if e != c.exp { t.Fatalf("Expected %v got %v", c.exp, e) } if err == nil && c.expErr { t.Fatalf("Expected error") } if err != nil && !c.expErr { t.Fatalf("Didn't expect error: %v", err) } }) } } func TestGetBinaries(t *testing.T) { cases := []struct { config map[string]interface{} psOut string exp map[string]string expectErr bool }{ { config: map[string]interface{}{"components": []string{"apiserver"}, "apiserver": map[string]interface{}{"bins": []string{"apiserver", "kube-apiserver"}}}, psOut: "kube-apiserver", exp: map[string]string{"apiserver": "kube-apiserver"}, expectErr: false, }, { // "thing" is not in the list of components config: map[string]interface{}{"components": []string{"apiserver"}, "apiserver": map[string]interface{}{"bins": []string{"apiserver", "kube-apiserver"}}, "thing": map[string]interface{}{"bins": []string{"something else", "thing"}}}, psOut: "kube-apiserver thing", exp: map[string]string{"apiserver": "kube-apiserver"}, expectErr: false, }, { // "anotherthing" in list of components but doesn't have a definition config: map[string]interface{}{"components": []string{"apiserver", "anotherthing"}, "apiserver": map[string]interface{}{"bins": []string{"apiserver", "kube-apiserver"}}, "thing": map[string]interface{}{"bins": []string{"something else", "thing"}}}, psOut: "kube-apiserver thing", exp: map[string]string{"apiserver": "kube-apiserver"}, expectErr: false, }, { // more than one component config: map[string]interface{}{"components": []string{"apiserver", "thing"}, "apiserver": map[string]interface{}{"bins": []string{"apiserver", "kube-apiserver"}}, "thing": map[string]interface{}{"bins": []string{"something else", "thing"}}}, psOut: "kube-apiserver \nthing", exp: map[string]string{"apiserver": "kube-apiserver", "thing": "thing"}, expectErr: false, }, { // default binary to component name config: map[string]interface{}{"components": []string{"apiserver", "thing"}, "apiserver": map[string]interface{}{"bins": []string{"apiserver", "kube-apiserver"}}, "thing": map[string]interface{}{"bins": []string{"something else", "thing"}, "optional": true}}, psOut: "kube-apiserver \notherthing some params", exp: map[string]string{"apiserver": "kube-apiserver", "thing": "thing"}, expectErr: false, }, { // missing mandatory component config: map[string]interface{}{"components": []string{"apiserver", "thing"}, "apiserver": map[string]interface{}{"bins": []string{"apiserver", "kube-apiserver"}}, "thing": map[string]interface{}{"bins": []string{"something else", "thing"}, "optional": true}}, psOut: "otherthing some params", exp: map[string]string{"apiserver": "kube-apiserver", "thing": "thing"}, expectErr: true, }, } v := viper.New() psFunc = fakeps for id, c := range cases { t.Run(strconv.Itoa(id), func(t *testing.T) { g = c.psOut for k, val := range c.config { v.Set(k, val) } m, err := getBinaries(v, check.MASTER) if c.expectErr { if err == nil { t.Fatal("Got nil Expected error") } } else if !reflect.DeepEqual(m, c.exp) { t.Fatalf("Got %v\nExpected %v", m, c.exp) } }) } } func TestMultiWordReplace(t *testing.T) { cases := []struct { input string sub string subname string output string }{ {input: "Here's a file with no substitutions", sub: "blah", subname: "blah", output: "Here's a file with no substitutions"}, {input: "Here's a file with a substitution", sub: "blah", subname: "substitution", output: "Here's a file with a blah"}, {input: "Here's a file with multi-word substitutions", sub: "multi word", subname: "multi-word", output: "Here's a file with 'multi word' substitutions"}, {input: "Here's a file with several several substitutions several", sub: "blah", subname: "several", output: "Here's a file with blah blah substitutions blah"}, } for id, c := range cases { t.Run(strconv.Itoa(id), func(t *testing.T) { s := multiWordReplace(c.input, c.subname, c.sub) if s != c.output { t.Fatalf("Expected %s got %s", c.output, s) } }) } } func Test_getVersionFromKubectlOutput(t *testing.T) { ver := getVersionFromKubectlOutput(`{ "serverVersion": { "major": "1", "minor": "8", "gitVersion": "v1.8.0" } }`) if ver.BaseVersion() != "1.8" { t.Fatalf("Expected 1.8 got %s", ver.BaseVersion()) } ver = getVersionFromKubectlOutput("Something completely different") if ver.BaseVersion() != defaultKubeVersion { t.Fatalf("Expected %s got %s", defaultKubeVersion, ver.BaseVersion()) } } func TestFindConfigFile(t *testing.T) { cases := []struct { input []string statResults []error exp string }{ {input: []string{"myfile"}, statResults: []error{nil}, exp: "myfile"}, {input: []string{"thisfile", "thatfile"}, statResults: []error{os.ErrNotExist, nil}, exp: "thatfile"}, {input: []string{"thisfile", "thatfile"}, statResults: []error{os.ErrNotExist, os.ErrNotExist}, exp: ""}, {input: []string{"thisfile", "/etc/dummy/thatfile"}, statResults: []error{os.ErrNotExist, errors.New("stat /etc/dummy/thatfile: not a directory")}, exp: ""}, } statFunc = fakestat for id, c := range cases { t.Run(strconv.Itoa(id), func(t *testing.T) { e = c.statResults eIndex = 0 conf := findConfigFile(c.input) if conf != c.exp { t.Fatalf("Got %s expected %s", conf, c.exp) } }) } } func TestGetConfigFiles(t *testing.T) { cases := []struct { config map[string]interface{} exp map[string]string statResults []error }{ { config: map[string]interface{}{"components": []string{"apiserver"}, "apiserver": map[string]interface{}{"confs": []string{"apiserver", "kube-apiserver"}}}, statResults: []error{os.ErrNotExist, nil}, exp: map[string]string{"apiserver": "kube-apiserver"}, }, { // Component "thing" isn't included in the list of components config: map[string]interface{}{ "components": []string{"apiserver"}, "apiserver": map[string]interface{}{"confs": []string{"apiserver", "kube-apiserver"}}, "thing": map[string]interface{}{"confs": []string{"/my/file/thing"}}, }, statResults: []error{os.ErrNotExist, nil}, exp: map[string]string{"apiserver": "kube-apiserver"}, }, { // More than one component config: map[string]interface{}{ "components": []string{"apiserver", "thing"}, "apiserver": map[string]interface{}{"confs": []string{"apiserver", "kube-apiserver"}}, "thing": map[string]interface{}{"confs": []string{"/my/file/thing"}}, }, statResults: []error{os.ErrNotExist, nil, nil}, exp: map[string]string{"apiserver": "kube-apiserver", "thing": "/my/file/thing"}, }, { // Default thing to specified default config config: map[string]interface{}{ "components": []string{"apiserver", "thing"}, "apiserver": map[string]interface{}{"confs": []string{"apiserver", "kube-apiserver"}}, "thing": map[string]interface{}{"confs": []string{"/my/file/thing"}, "defaultconf": "another/thing"}, }, statResults: []error{os.ErrNotExist, nil, os.ErrNotExist}, exp: map[string]string{"apiserver": "kube-apiserver", "thing": "another/thing"}, }, { // Default thing to component name config: map[string]interface{}{ "components": []string{"apiserver", "thing"}, "apiserver": map[string]interface{}{"confs": []string{"apiserver", "kube-apiserver"}}, "thing": map[string]interface{}{"confs": []string{"/my/file/thing"}}, }, statResults: []error{os.ErrNotExist, nil, os.ErrNotExist}, exp: map[string]string{"apiserver": "kube-apiserver", "thing": "thing"}, }, } v := viper.New() statFunc = fakestat for id, c := range cases { t.Run(strconv.Itoa(id), func(t *testing.T) { for k, val := range c.config { v.Set(k, val) } e = c.statResults eIndex = 0 m := getFiles(v, "config") if !reflect.DeepEqual(m, c.exp) { t.Fatalf("Got %v\nExpected %v", m, c.exp) } }) } } func TestGetServiceFiles(t *testing.T) { cases := []struct { config map[string]interface{} exp map[string]string statResults []error }{ { config: map[string]interface{}{ "components": []string{"kubelet"}, "kubelet": map[string]interface{}{"svc": []string{"kubelet", "10-kubeadm.conf"}}, }, statResults: []error{os.ErrNotExist, nil}, exp: map[string]string{"kubelet": "10-kubeadm.conf"}, }, { // Component "thing" isn't included in the list of components config: map[string]interface{}{ "components": []string{"kubelet"}, "kubelet": map[string]interface{}{"svc": []string{"kubelet", "10-kubeadm.conf"}}, "thing": map[string]interface{}{"svc": []string{"/my/file/thing"}}, }, statResults: []error{os.ErrNotExist, nil}, exp: map[string]string{"kubelet": "10-kubeadm.conf"}, }, { // More than one component config: map[string]interface{}{ "components": []string{"kubelet", "thing"}, "kubelet": map[string]interface{}{"svc": []string{"kubelet", "10-kubeadm.conf"}}, "thing": map[string]interface{}{"svc": []string{"/my/file/thing"}}, }, statResults: []error{os.ErrNotExist, nil, nil}, exp: map[string]string{"kubelet": "10-kubeadm.conf", "thing": "/my/file/thing"}, }, { // Default thing to specified default service config: map[string]interface{}{ "components": []string{"kubelet", "thing"}, "kubelet": map[string]interface{}{"svc": []string{"kubelet", "10-kubeadm.conf"}}, "thing": map[string]interface{}{"svc": []string{"/my/file/thing"}, "defaultsvc": "another/thing"}, }, statResults: []error{os.ErrNotExist, nil, os.ErrNotExist}, exp: map[string]string{"kubelet": "10-kubeadm.conf", "thing": "another/thing"}, }, { // Default thing to component name config: map[string]interface{}{ "components": []string{"kubelet", "thing"}, "kubelet": map[string]interface{}{"svc": []string{"kubelet", "10-kubeadm.conf"}}, "thing": map[string]interface{}{"svc": []string{"/my/file/thing"}}, }, statResults: []error{os.ErrNotExist, nil, os.ErrNotExist}, exp: map[string]string{"kubelet": "10-kubeadm.conf", "thing": "thing"}, }, } v := viper.New() statFunc = fakestat for id, c := range cases { t.Run(strconv.Itoa(id), func(t *testing.T) { for k, val := range c.config { v.Set(k, val) } e = c.statResults eIndex = 0 m := getFiles(v, "service") if !reflect.DeepEqual(m, c.exp) { t.Fatalf("Got %v\nExpected %v", m, c.exp) } }) } } func TestGetDatadirFiles(t *testing.T) { var err error datadir, err := os.MkdirTemp("", "kube-bench-test-etcd-data-dir") if err != nil { t.Fatalf("Failed to create temp directory") } defer os.RemoveAll(datadir) cases := []struct { config map[string]interface{} exp map[string]string statResults []error }{ { config: map[string]interface{}{ "components": []string{"etcd"}, "etcd": map[string]interface{}{"datadirs": []string{datadir}, "defaultdatadir": "/var/lib/etcd/default.etcd"}, }, statResults: []error{nil}, exp: map[string]string{"etcd": datadir}, }, // fallback to defaultdatadir { config: map[string]interface{}{ "components": []string{"etcd"}, "etcd": map[string]interface{}{"datadirs": []string{"/path/to/etcd/data.etcd"}, "defaultdatadir": "/var/lib/etcd/default.etcd"}, }, statResults: []error{os.ErrNotExist}, exp: map[string]string{"etcd": "/var/lib/etcd/default.etcd"}, }, } v := viper.New() statFunc = fakestat for id, c := range cases { t.Run(strconv.Itoa(id), func(t *testing.T) { for k, val := range c.config { v.Set(k, val) } e = c.statResults eIndex = 0 m := getFiles(v, "datadir") if !reflect.DeepEqual(m, c.exp) { t.Fatalf("Got %v\nExpected %v", m, c.exp) } }) } } func TestMakeSubsitutions(t *testing.T) { cases := []struct { input string subst map[string]string exp string expectedSubs []string }{ {input: "Replace $thisbin", subst: map[string]string{"this": "that"}, exp: "Replace that", expectedSubs: []string{"that"}}, {input: "Replace $thisbin", subst: map[string]string{"this": "that", "here": "there"}, exp: "Replace that", expectedSubs: []string{"that"}}, {input: "Replace $thisbin and $herebin", subst: map[string]string{"this": "that", "here": "there"}, exp: "Replace that and there", expectedSubs: []string{"that", "there"}}, } for _, c := range cases { t.Run(c.input, func(t *testing.T) { s, subs := makeSubstitutions(c.input, "bin", c.subst) if s != c.exp { t.Fatalf("Got %s expected %s", s, c.exp) } sort.Strings(subs) assert.Equal(t, c.expectedSubs, subs) }) } } func TestGetConfigFilePath(t *testing.T) { var err error cfgDir, err = os.MkdirTemp("", "kube-bench-test") if err != nil { t.Fatalf("Failed to create temp directory") } defer os.RemoveAll(cfgDir) d := filepath.Join(cfgDir, "cis-1.4") err = os.Mkdir(d, 0766) if err != nil { t.Fatalf("Failed to create temp dir") } err = os.WriteFile(filepath.Join(d, "master.yaml"), []byte("hello world"), 0666) if err != nil { t.Logf("Failed to create temp file") } cases := []struct { benchmarkVersion string succeed bool exp string }{ {benchmarkVersion: "cis-1.4", succeed: true, exp: d}, {benchmarkVersion: "cis-1.5", succeed: false, exp: ""}, {benchmarkVersion: "1.1", succeed: false, exp: ""}, } for _, c := range cases { t.Run(c.benchmarkVersion, func(t *testing.T) { path, err := getConfigFilePath(c.benchmarkVersion, "/master.yaml") if c.succeed { if err != nil { t.Fatalf("Error %v", err) } if path != c.exp { t.Fatalf("Got %s expected %s", path, c.exp) } } else { if err == nil { t.Fatalf("Expected Error, but none") } } }) } } func TestDecrementVersion(t *testing.T) { cases := []struct { kubeVersion string succeed bool exp string }{ {kubeVersion: "1.13", succeed: true, exp: "1.12"}, {kubeVersion: "1.15", succeed: true, exp: "1.14"}, {kubeVersion: "1.11", succeed: true, exp: "1.10"}, {kubeVersion: "1.1", succeed: true, exp: ""}, {kubeVersion: "invalid", succeed: false, exp: ""}, } for _, c := range cases { rv := decrementVersion(c.kubeVersion) if c.succeed { if c.exp != rv { t.Fatalf("decrementVersion(%q) - Got %q expected %s", c.kubeVersion, rv, c.exp) } } else { if len(rv) > 0 { t.Fatalf("decrementVersion(%q) - Expected empty string but Got %s", c.kubeVersion, rv) } } } } func TestGetYamlFilesFromDir(t *testing.T) { cfgDir, err := os.MkdirTemp("", "kube-bench-test") if err != nil { t.Fatalf("Failed to create temp directory") } defer os.RemoveAll(cfgDir) d := filepath.Join(cfgDir, "cis-1.4") err = os.Mkdir(d, 0766) if err != nil { t.Fatalf("Failed to create temp dir") } err = os.WriteFile(filepath.Join(d, "something.yaml"), []byte("hello world"), 0666) if err != nil { t.Fatalf("error writing file %v", err) } err = os.WriteFile(filepath.Join(d, "config.yaml"), []byte("hello world"), 0666) if err != nil { t.Fatalf("error writing file %v", err) } files, err := getYamlFilesFromDir(d) if err != nil { t.Fatalf("Unexpected error: %v", err) } if len(files) != 1 { t.Fatalf("Expected to find one file, found %d", len(files)) } if files[0] != filepath.Join(d, "something.yaml") { t.Fatalf("Expected to find something.yaml, found %s", files[0]) } } func Test_getPlatformNameFromKubectlOutput(t *testing.T) { type args struct { s string } tests := []struct { name string args args want Platform }{ { name: "eks", args: args{s: "v1.17.9-eks-4c6976"}, want: Platform{Name: "eks", Version: "1.17"}, }, { name: "gke", args: args{s: "v1.17.6-gke.1"}, want: Platform{Name: "gke", Version: "1.17"}, }, { name: "ack", args: args{s: "v1.18.8-aliyun.1"}, want: Platform{Name: "aliyun", Version: "1.18"}, }, { name: "unknown", args: args{s: "v1.17.6"}, want: Platform{}, }, { name: "empty string", args: args{s: ""}, want: Platform{}, }, { name: "k3s", args: args{s: "v1.27.6+k3s1"}, want: Platform{Name: "k3s", Version: "1.27"}, }, { name: "rancher1", args: args{s: "v1.25.13-rancher1-1"}, want: Platform{Name: "rancher1", Version: "1.25"}, }, { name: "rke2", args: args{s: "v1.27.6+rke2r1"}, want: Platform{Name: "rke2r", Version: "1.27"}, }, { name: "aks", args: args{s: "v1.27.6+aks1"}, want: Platform{Name: "aks", Version: "1.27"}, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := getPlatformInfoFromVersion(tt.args.s) assert.Equal(t, tt.want, got) }) } } func Test_getPlatformBenchmarkVersion(t *testing.T) { type args struct { platform Platform } tests := []struct { name string args args want string }{ { name: "eks 1.31", args: args{ platform: Platform{Name: "eks", Version: "1.31"}, }, want: "eks-1.7.0", }, { name: "eks 1.29", args: args{ platform: Platform{Name: "eks", Version: "1.29"}, }, want: "eks-1.7.0", }, { name: "eks 1.30", args: args{ platform: Platform{Name: "eks", Version: "1.30"}, }, want: "eks-1.7.0", }, { name: "eks 1.32", args: args{ platform: Platform{Name: "eks", Version: "1.32"}, }, want: "eks-1.8.0", }, { name: "eks 1.24", args: args{ platform: Platform{Name: "eks", Version: "1.24"}, }, want: "eks-1.5.0", }, { name: "gke 1.19", args: args{ platform: Platform{Name: "gke", Version: "1.19"}, }, want: "gke-1.0", }, { name: "gke 1.20", args: args{ platform: Platform{Name: "gke", Version: "1.20"}, }, want: "gke-1.2.0", }, { name: "gke 1.22", args: args{ platform: Platform{Name: "gke", Version: "1.22"}, }, want: "gke-1.2.0", }, { name: "gke 1.28", args: args{ platform: Platform{Name: "gke", Version: "1.28"}, }, want: "gke-1.6.0", }, { name: "gke 1.31", args: args{ platform: Platform{Name: "gke", Version: "1.31"}, }, want: "gke-1.8.0", }, { name: "aliyun", args: args{ platform: Platform{Name: "aliyun"}, }, want: "ack-1.0", }, { name: "unknown", args: args{ platform: Platform{Name: "rh"}, }, want: "", }, { name: "empty", args: args{ platform: Platform{}, }, want: "", }, { name: "openshift3", args: args{ platform: Platform{Name: "ocp", Version: "3.10"}, }, want: "rh-0.7", }, { name: "openshift4", args: args{ platform: Platform{Name: "ocp", Version: "4.11"}, }, want: "rh-1.4", }, { name: "openshift4", args: args{ platform: Platform{Name: "ocp", Version: "4.13"}, }, want: "rh-1.8", }, { name: "openshift4", args: args{ platform: Platform{Name: "ocp", Version: "4.1"}, }, want: "rh-1.0", }, { name: "k3s", args: args{ platform: Platform{Name: "k3s", Version: "1.27"}, }, want: "k3s-cis-1.7", }, { name: "rancher1", args: args{ platform: Platform{Name: "rancher", Version: "1.27"}, }, want: "rke-cis-1.7", }, { name: "rke2", args: args{ platform: Platform{Name: "rke2r", Version: "1.25"}, }, want: "rke2-cis-1.7", }, { name: "rke2", args: args{ platform: Platform{Name: "rke2r", Version: "1.26"}, }, want: "rke2-cis-1.8", }, { name: "aks", args: args{ platform: Platform{Name: "aks", Version: "1.29"}, }, want: "aks-1.7", }, { name: "aks", args: args{ platform: Platform{Name: "aks", Version: "1.32"}, }, want: "aks-1.8", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { if got := getPlatformBenchmarkVersion(tt.args.platform); got != tt.want { t.Errorf("getPlatformBenchmarkVersion() = %v, want %v", got, tt.want) } }) } } func Test_getOcpValidVersion(t *testing.T) { cases := []struct { openShiftVersion string succeed bool exp string }{ {openShiftVersion: "3.11", succeed: true, exp: "3.10"}, {openShiftVersion: "3.10", succeed: true, exp: "3.10"}, {openShiftVersion: "2.9", succeed: false, exp: ""}, {openShiftVersion: "4.1", succeed: true, exp: "4.1"}, {openShiftVersion: "4.5", succeed: true, exp: "4.1"}, {openShiftVersion: "4.6", succeed: true, exp: "4.1"}, {openShiftVersion: "invalid", succeed: false, exp: ""}, } for _, c := range cases { ocpVer, _ := getOcpValidVersion(c.openShiftVersion) if c.succeed { if c.exp != ocpVer { t.Errorf("getOcpValidVersion(%q) - Got %q expected %s", c.openShiftVersion, ocpVer, c.exp) } } else { if len(ocpVer) > 0 { t.Errorf("getOcpValidVersion(%q) - Expected empty string but Got %s", c.openShiftVersion, ocpVer) } } } } ================================================ FILE: cmd/version.go ================================================ package cmd import ( "fmt" "github.com/spf13/cobra" ) var KubeBenchVersion string // versionCmd represents the version command var versionCmd = &cobra.Command{ Use: "version", Short: "Shows the version of kube-bench.", Long: `Shows the version of kube-bench.`, Run: func(cmd *cobra.Command, args []string) { fmt.Println(KubeBenchVersion) }, } func init() { RootCmd.AddCommand(versionCmd) } ================================================ FILE: codecov.yml ================================================ --- coverage: status: project: default: target: auto # auto compares coverage to the previous base commit threshold: 1% ================================================ FILE: docs/architecture.md ================================================ ## Test config YAML representation The tests (or "controls") are maintained in YAML documents. There are different versions of these test YAML files reflecting different [versions and platforms of the CIS Kubernetes Benchmark](./platforms.md). You will find more information about the test file YAML definitions in our [controls documentation](./controls.md). ## Kube-bench benchmarks The test files for the various versions of Benchmarks can be found in directories with same name as the Benchmark versions under the `cfg` directory next to the kube-bench executable, for example `./cfg/cis-1.5` will contain all test files for [CIS Kubernetes Benchmark v1.5.1](https://workbench.cisecurity.org/benchmarks/4892) which are: master.yaml, controlplane.yaml, node.yaml, etcd.yaml, policies.yaml and config.yaml Check the contents of the benchmark directory under `cfg` to see which targets are available for that benchmark. Each file except `config.yaml` represents a target (also known as a `control` in other parts of this documentation). The following table shows the valid targets based on the CIS Benchmark version. | CIS Benchmark | Targets | |----------------------|---------| | cis-1.5 | master, controlplane, node, etcd, policies | | cis-1.6 | master, controlplane, node, etcd, policies | | cis-1.20 | master, controlplane, node, etcd, policies | | cis-1.23 | master, controlplane, node, etcd, policies | | cis-1.24 | master, controlplane, node, etcd, policies | | cis-1.7 | master, controlplane, node, etcd, policies | | cis-1.8 | master, controlplane, node, etcd, policies | | cis-1.9 | master, controlplane, node, etcd, policies | | cis-1.10 | master, controlplane, node, etcd, policies | | cis-1.11 | master, controlplane, node, etcd, policies | | cis-1.12 | master, controlplane, node, etcd, policies | | gke-1.0 | master, controlplane, node, etcd, policies, managedservices | | gke-1.2.0 | controlplane, node, policies, managedservices | | gke-1.6.0 | controlplane, node, policies, managedservices | | eks-1.0.1 | controlplane, node, policies, managedservices | | eks-1.1.0 | controlplane, node, policies, managedservices | | eks-1.2.0 | controlplane, node, policies, managedservices | | eks-1.5.0 | controlplane, node, policies, managedservices | | ack-1.0 | master, controlplane, node, etcd, policies, managedservices | | aks-1.0 | controlplane, node, policies, managedservices | | aks-1.7 | controlplane, node, policies, managedservices | | rh-0.7 | master,node| | rh-1.0 | master, controlplane, node, etcd, policies | | cis-1.6-k3s | master, controlplane, node, etcd, policies | | cis-1.24-microk8s | master, controlplane, node, etcd, policies | The following table shows the valid DISA STIG versions | STIG | Targets | |----------------------------|---------| | eks-stig-kubernetes-v1r6 | master, controlplane, node, policies, managedservices | ================================================ FILE: docs/asff.md ================================================ # Integrating kube-bench with AWS Security Hub You can configure kube-bench with the `--asff` to send findings to AWS Security Hub. There are some additional steps required so that kube-bench has information and permissions to send these findings. ## Enable the AWS Security Hub integration * You will need AWS Security Hub to be enabled in your account * In the Security Hub console, under Integrations, search for kube-bench

* Click on `Accept findings`. This gives information about the IAM permissions required to send findings to your Security Hub account. kube-bench runs within a pod on your EKS cluster, and will need to be associated with a Role that has these permissions. ## Configure permissions in an IAM Role * Grant these permissions to the IAM Role that the kube-bench pod will be associated with. There are two options: * You can run the kube-bench pod under a specific [service account associated with an IAM role](https://docs.aws.amazon.com/eks/latest/userguide/iam-roles-for-service-accounts.html) that has these permissions to write Security Hub findings. * Alternatively the pod can be granted permissions specified by the Role that your [EKS node group uses](https://docs.aws.amazon.com/eks/latest/userguide/managed-node-groups.html). Here is an example IAM Policy that you can attach to your EKS node group's IAM Role: ```json { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": "securityhub:BatchImportFindings", "Resource": [ "arn:aws:securityhub:us-east-1::product/aqua-security/kube-bench" ] } ] } ``` ### Modify the job configuration * Modify the kube-bench Configmap in `job-eks-asff.yaml` to specify the AWS account, AWS region, and the EKS Cluster ARN. * In the same file, modify the image specifed in the Job to use the kube-bench image pushed to your ECR * [Optional] - If you have created a dedicated IAM role to be used with kube-bench as described above in [Configure permissions in an IAM Role](#configure-permissions-in-an-iam-role), you will need to add the IAM role arn to the kube-bench ServiceAccount in `job-eks-asff.yaml`. * Make sure that `job-eks-asff.yaml` specifies the container image you just pushed to your ECR registry. You can now run kube-bench as a pod in your cluster: `kubectl apply -f job-eks-asff.yaml` Findings will be generated for any kube-bench test that generates a `[FAIL]` or `[WARN]` output. If all tests pass, no findings will be generated. However, it's recommended that you consult the pod log output to check whether any findings were generated but could not be written to Security Hub.

[eks-instructions]: ../README.md#running-in-an-EKS-cluster ================================================ FILE: docs/controls.md ================================================ # Test and config files `kube-bench` runs checks specified in `controls` files that are a YAML representation of the CIS Kubernetes Benchmark checks (or other distribution-specific hardening guides). ## Controls `controls` is a YAML document that contains checks that must be run against a specific Kubernetes node type, master or node and version. `controls` is the fundamental input to `kube-bench`. The following is an example of a basic `controls`: ```yml --- controls: id: 1 text: "Master Node Security Configuration" type: "master" groups: - id: 1.1 text: API Server checks: - id: 1.1.1 text: "Ensure that the --allow-privileged argument is set (Scored)" audit: "ps -ef | grep kube-apiserver | grep -v grep" tests: bin_op: or test_items: - flag: "--allow-privileged" set: true - flag: "--some-other-flag" set: false remediation: "Edit the /etc/kubernetes/config file on the master node and set the KUBE_ALLOW_PRIV parameter to '--allow-privileged=false'" scored: true - id: 1.2 text: Scheduler checks: - id: 1.2.1 text: "Ensure that the --profiling argument is set to false (Scored)" audit: "ps -ef | grep kube-scheduler | grep -v grep" tests: bin_op: and test_items: - flag: "--profiling" set: true - flag: "--some-other-flag" set: false remediation: "Edit the /etc/kubernetes/config file on the master node and set the KUBE_ALLOW_PRIV parameter to '--allow-privileged=false'" scored: true ``` `controls` is composed of a hierarchy of groups, sub-groups and checks. Each of the `controls` components have an id and a text description which are displayed in the `kube-bench` output. `type` specifies what kubernetes node type a `controls` is for. Possible values for `type` are `master` and `node`. ## Groups `groups` is a list of subgroups that test the various Kubernetes components that run on the node type specified in the `controls`. For example, one subgroup checks parameters passed to the API server binary, while another subgroup checks parameters passed to the controller-manager binary. ```yml groups: - id: 1.1 text: API Server # ... - id: 1.2 text: Scheduler # ... ``` These subgroups have `id`, `text` fields which serve the same purposes described in the previous paragraphs. The most important part of the subgroup is the `checks` field which is the collection of actual `check`s that form the subgroup. This is an example of a subgroup and checks in the subgroup. ```yml id: 1.1 text: API Server checks: - id: 1.1.1 text: "Ensure that the --allow-privileged argument is set (Scored)" audit: "ps -ef | grep kube-apiserver | grep -v grep" tests: # ... - id: 1.1.2 text: "Ensure that the --anonymous-auth argument is set to false (Not Scored)" audit: "ps -ef | grep kube-apiserver | grep -v grep" tests: # ... ``` `kube-bench` supports running a subgroup by specifying the subgroup `id` on the command line, with the flag `--group` or `-g`. ## Check The CIS Kubernetes Benchmark recommends configurations to harden Kubernetes components. These recommendations are usually configuration options and can be specified by flags to Kubernetes binaries, or in configuration files. The Benchmark also provides commands to audit a Kubernetes installation, identify places where the cluster security can be improved, and steps to remediate these identified problems. In `kube-bench`, `check` objects embody these recommendations. This an example `check` object: ```yml id: 1.1.1 text: "Ensure that the --anonymous-auth argument is set to false (Not Scored)" audit: "ps -ef | grep kube-apiserver | grep -v grep" tests: test_items: - flag: "--anonymous-auth" compare: op: eq value: false set: true remediation: | Edit the API server pod specification file kube-apiserver on the master node and set the below parameter. --anonymous-auth=false scored: false ``` A `check` object has an `id`, a `text`, an `audit`, a `tests`, `remediation` and `scored` fields. `kube-bench` supports running individual checks by specifying the check's `id` as a comma-delimited list on the command line with the `--check` flag. The `audit` field specifies the command to run for a check. The output of this command is then evaluated for conformance with the CIS Kubernetes Benchmark recommendation. The audit is evaluated against criteria specified by the `tests` object. `tests` contain `bin_op` and `test_items`. `test_items` specify the criteria(s) the `audit` command's output should meet to pass a check. This criteria is made up of keywords extracted from the output of the `audit` command and operations that compare these keywords against values expected by the CIS Kubernetes Benchmark. There are three ways to run and extract keywords from the output of the command used, | Command | Output var | |---|---| | `audit` | `flag` | | `audit_config` | `path` | | `audit_env` | `env` | `flag` is used when the keyword is a command-line flag. The associated `audit` command could be any binaries available on the system like `ps` command and a `grep` for the binary whose flag we are checking: ```sh ps -ef | grep somebinary | grep -v grep ``` Here is an example usage of the `flag` option: ```yml # ... audit: "ps -ef | grep kube-apiserver | grep -v grep" tests: test_items: - flag: "--anonymous-auth" # ... ``` `path` is used when the keyword is an option set in a JSON or YAML config file. The associated `audit_command` command is usually `cat /path/to/config-yaml-or-json`. For example: ```yml # ... text: "Ensure that the --anonymous-auth argument is set to false (Not Scored)" audit: "cat /path/to/some/config" tests: test_items: - path: "{.someoption.value}" # ... ``` `env` is used to check if the value is present within a specified environment variable. The presence of `env` is treated as an OR operation, if both `flag` and `env` are supplied it will use either to attempt pass the check. The command used for checking the environment variables of a process **is generated by default**. If the command being generated is causing errors, you can override the command used by setting `audit_env` on the check. Similarly, if you don't want the environment checking command to be generated or run at all, specify `disableEnvTesting` as true on the check. The example below will check if the flag `--auto-tls` is equal to false *OR* `ETCD_AUTO_TLS` is equal to false ```yml test_items: - flag: "--auto-tls" env: "ETCD_AUTO_TLS" compare: op: eq value: false ``` **Note:** flag, path and env will act as OR if more then one present. `test_item` compares the output of the audit command and keywords using the `set` and `compare` fields. ```yml test_items: - flag: "--anonymous-auth" compare: op: eq value: false set: true ``` `set` checks if a keyword is present in the output of the audit command or a config file. The possible values for `set` are true and false. If `set` is true, the check passes only if the keyword is present in the output of the audit command, or config file. If `set` is false, the check passes only if the keyword is not present in the output of the audit command, or config file. `set` is true by default. `compare` has two fields `op` and `value` to compare keywords with expected value. `op` specifies which operation is used for the comparison, and `value` specifies the value to compare against. > To use `compare`, `set` must true. The comparison will be ignored if `set` is > false The `op` (operations) currently supported in `kube-bench` are: - `eq`: tests if the keyword is equal to the compared value. - `noteq`: tests if the keyword is unequal to the compared value. - `gt`: tests if the keyword is greater than the compared value. - `gte`: tests if the keyword is greater than or equal to the compared value. - `lt`: tests if the keyword is less than the compared value. - `lte`: tests if the keyword is less than or equal to the compared value. - `has`: tests if the keyword contains the compared value. - `nothave`: tests if the keyword does not contain the compared value. - `regex`: tests if the flag value matches the compared value regular expression. When defining regular expressions in YAML it is generally easier to wrap them in single quotes, for example `'^[abc]$'`, to avoid issues with string escaping. - `bitmask` : tests if keyward is bitmasked with the compared value, common usege is for comparing file permissions in linux. ## Omitting checks If you decide that a recommendation is not appropriate for your environment, you can choose to omit it by editing the test YAML file to give it the check type `skip` as in this example: ```yaml checks: - id: 2.1.1 text: "Ensure that the --allow-privileged argument is set to false (Scored)" type: "skip" scored: true ``` No tests will be run for this check and the output will be marked [INFO]. ## Configuration and Variables Kubernetes component configuration and binary file locations and names vary based on cluster deployment methods and Kubernetes distribution used. For this reason, the locations of these binaries and config files are configurable by editing the `cfg/config.yaml` file and these binaries and files can be referenced in a `controls` file via variables. The `cfg/config.yaml` file is a global configuration file. Configuration files can be created for specific Kubernetes versions (distributions). Values in the version-specific config overwrite similar values in `cfg/config.yaml`. For example, the kube-apiserver in Red Hat OCP distribution is run as `hypershift openshift-kube-apiserver` instead of the default `kube-apiserver`. This difference can be specified by editing the `master.apiserver.defaultbin` entry `cfg/rh-0.7/config.yaml`. Below is the structure of `cfg/config.yaml`: ``` nodetype |-- components |-- component1 |-- component1 |-- bins |-- defaultbin (optional) |-- confs |-- defaultconf (optional) |-- svcs |-- defaultsvc (optional) |-- kubeconfig |-- defaultkubeconfig (optional) ``` Every node type has a subsection that specifies the main configuration items. - `components`: A list of components for the node type. For example master will have an entry for **apiserver**, **scheduler** and **controllermanager**. Each component has the following entries: - `bins`: A list of candidate binaries for a component. `kube-bench` checks this list and selects the **first** binary that is running on the node. If none of the binaries in `bins` list is running, `kube-bench` checks if the binary specified by `defaultbin` is running and terminates if none of the binaries in both `bins` and `defaultbin` is running. The selected binary for a component can be referenced in `controls` using a variable in the form `$bin`. In the example below, we reference the selected API server binary with the variable `$apiserverbin` in an `audit` command. ```yml id: 1.1.1 text: "Ensure that the --anonymous-auth argument is set to false (Scored)" audit: "ps -ef | grep $apiserverbin | grep -v grep" # ... ``` - `confs`: A list of candidate configuration files for a component. `kube-bench` checks this list and selects the **first** config file that is found on the node. If none of the config files exists, `kube-bench` defaults conf to the value of `defaultconf`. The selected config for a component can be referenced in `controls` using a variable in the form `$conf`. In the example below, we reference the selected API server config file with the variable `$apiserverconf` in an `audit` command. ```yml id: 1.4.1 text: "Ensure that the API server pod specification file permissions are set to 644 or more restrictive (Scored)" audit: "/bin/sh -c 'if test -e $apiserverconf; then stat -c %a $apiserverconf; fi'" ``` - `svcs`: A list of candidate unitfiles for a component. `kube-bench` checks this list and selects the **first** unitfile that is found on the node. If none of the unitfiles exists, `kube-bench` defaults unitfile to the value of `defaultsvc`. The selected unitfile for a component can be referenced in `controls` via a variable in the form `$svc`. In the example below, the selected kubelet unitfile is referenced with `$kubeletsvc` in the `remediation` of the `check`. ```yml id: 2.1.1 # ... remediation: | Edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_SYSTEM_PODS_ARGS variable. --allow-privileged=false Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service # ... ``` - `kubeconfig`: A list of candidate kubeconfig files for a component. `kube-bench` checks this list and selects the **first** file that is found on the node. If none of the files exists, `kube-bench` defaults kubeconfig to the value of `defaultkubeconfig`. The selected kubeconfig for a component can be referenced in `controls` with a variable in the form `$kubeconfig`. In the example below, the selected kubelet kubeconfig is referenced with `$kubeletkubeconfig` in the `audit` command. ```yml id: 2.2.1 text: "Ensure that the kubelet.conf file permissions are set to 644 or more restrictive (Scored)" audit: "/bin/sh -c 'if test -e $kubeletkubeconfig; then stat -c %a $kubeletkubeconfig; fi'" # ... ``` ================================================ FILE: docs/flags-and-commands.md ================================================ ## Commands Command | Description --- | --- help | Prints help about any command run | List of components to run version | Print kube-bench version ## Flags Flag | Description --- | --- --alsologtostderr | log to standard error as well as files --asff | Send findings to AWS Security Hub for any benchmark tests that fail or that generate a warning. See [this page][kube-bench-aws-security-hub] for more information on how to enable the kube-bench integration with AWS Security Hub. --benchmark | Manually specify CIS benchmark version -c, --check | A comma-delimited list of checks to run as specified in Benchmark document. --config | config file (default is ./cfg/config.yaml) --exit-code | Specify the exit code for when checks fail --group | Run all the checks under this comma-delimited list of groups. --include-test-output | Prints the actual result when test fails. --json | Prints the results as JSON --junit | Prints the results as JUnit --log_backtrace_at traceLocation | when logging hits line file:N, emit a stack trace (default :0) --logtostderr | log to standard error instead of files --noremediations | Disable printing of remediations section to stdout. --noresults | Disable printing of results section to stdout. --nototals | Disable calculating and printing of totals for failed, passed, ... checks across all sections --outputfile | Writes the results to output file when run with --json or --junit --pgsql | Save the results to PostgreSQL --scored | Run the scored CIS checks (default true) --skip string | List of comma separated values of checks to be skipped --stderrthreshold severity | logs at or above this threshold go to stderr (default 2) -v, --v Level | log level for V logs (default 0) --unscored | Run the unscored CIS checks (default true) --version string | Manually specify Kubernetes version, automatically detected if unset --vmodule moduleSpec | comma-separated list of pattern=N settings for file-filtered logging ### Examples #### Report kube-bench findings to AWS Security Hub You can configure kube-bench with the `--asff` option to send findings to AWS Security Hub for any benchmark tests that fail or that generate a warning. See [this page](asff.md) for more information on how to enable the kube-bench integration with AWS Security Hub. #### Specifying the benchmark or Kubernetes version `kube-bench` uses the Kubernetes API, or access to the `kubectl` or `kubelet` executables to try to determine the Kubernetes version, and hence which benchmark to run. If you wish to override this, or if none of these methods are available, you can specify either the Kubernetes version or CIS Benchmark as a command line parameter. You can specify a particular version of Kubernetes by setting the `--version` flag or with the `KUBE_BENCH_VERSION` environment variable. The value of `--version` takes precedence over the value of `KUBE_BENCH_VERSION`. For example, run kube-bench using the tests for Kubernetes version 1.13: ``` kube-bench --version 1.13 ``` You can specify `--benchmark` to run a specific CIS Benchmark version: ``` kube-bench --benchmark cis-1.5 ``` **Note:** It is an error to specify both `--version` and `--benchmark` flags together #### Specifying Benchmark sections If you want to run specific CIS Benchmark sections (i.e master, node, etcd, etc...) you can use the `run --targets` subcommand. ``` kube-bench run --targets master,node ``` or ``` kube-bench run --targets master,node,etcd,policies ``` If no targets are specified, `kube-bench` will determine the appropriate targets based on the CIS Benchmark version and the components detected on the node. The detection is done by verifying which components are running, as defined in the config files (see [Configuration](controls.md#configuration-and-variables). #### Run specific check or group `kube-bench` supports running individual checks by specifying the check's `id` as a comma-delimited list on the command line with the `--check` | `-c` flag. `kube-bench --check="1.1.1,1.1.2,1.2.1,1.3.3"` `kube-bench` supports running all checks under group by specifying the group's `id` as a comma-delimited list on the command line with the `--group` | `-g` flag. `kube-bench --check="1.1,2.2"` Will run all checks 1.1.X and 2.2.X. #### Skip specific check or group `kube-bench` supports skipping checks or groups by specifying the `id` as a comma-delimited list on the command line with the `--skip` flag. `kube-bench --skip="1.1,1.2.1,1.3.3"` Will skip 1.1.X group and individual checks 1.2.1, 1.3.3. Skipped checks returns [INFO] output. #### Exit code `kube-bench` supports using uniqe exit code when failing a check or more. `kube-bench --exit-code 42` Will return 42 if one check or more failed, and 0 incase none failed. **Note:** [WARN] is not [FAIL]. #### Output manipulation flags There are four output states: - [PASS] indicates that the test was run successfully, and passed. - [FAIL] indicates that the test was run successfully, and failed. The remediation output describes how to correct the configuration, or includes an error message describing why the test could not be run. - [WARN] means this test needs further attention, for example it is a test that needs to be run manually. Check the remediation output for further information. - [INFO] is informational output that needs no further action. Note: - Some tests with `Automated` in their description must still be run manually - If the user has to run a test manually, this always generates WARN - If the test is Scored, and kube-bench was unable to run the test, this generates FAIL (because the test has not been passed, and as a Scored test, if it doesn't pass then it must be considered a failure). - If the test is Not Scored, and kube-bench was unable to run the test, this generates WARN. - If the test is Scored, type is empty, and there are no `test_items` present, it generates a WARN. This is to highlight tests that appear to be incompletely defined. `kube-bench` supports multiple output manipulation flags. `kube-bench --include-test-output` will print failing checks output in the results section ``` [INFO] 1 Master Node Security Configuration [INFO] 1.1 Master Node Configuration Files [FAIL] 1.1.1 Ensure that the API server pod specification file permissions are set to 644 or more restrictive (Automated) **permissions=777** ``` **Note:** `--noresults` `--noremediations` and `--include-test-output` **will not** effect the json output but only stdout. Only `--nototals` will effect the json output and thats because it will not call the function to calculate totals. #### Troubleshooting Running `kube-bench` with the `-v 3` parameter will generate debug logs that can be very helpful for debugging problems. If you are using one of the example `job*.yaml` files, you will need to edit the `command` field, for example `["kube-bench", "-v", "3"]`. Once the job has run, the logs can be retrieved using `kubectl logs` on the job's pod. ================================================ FILE: docs/index.md ================================================ [download]: https://img.shields.io/github/downloads/aquasecurity/kube-bench/total?logo=github [release-img]: https://img.shields.io/github/release/aquasecurity/kube-bench.svg?logo=github [release]: https://github.com/aquasecurity/kube-bench/releases [docker-pull]: https://img.shields.io/docker/pulls/aquasec/kube-bench?logo=docker&label=docker%20pulls%20%2F%20kube-bench [docker]: https://hub.docker.com/r/aquasec/kube-bench [cov-img]: https://codecov.io/github/aquasecurity/kube-bench/branch/main/graph/badge.svg [cov]: https://codecov.io/github/aquasecurity/kube-bench [report-card-img]: https://goreportcard.com/badge/github.com/aquasecurity/kube-bench [report-card]: https://goreportcard.com/report/github.com/aquasecurity/kube-bench ![Kube-bench Logo](images/kube-bench.jpg) [![GitHub Release][release-img]][release] [![Downloads][download]][release] [![Docker Pulls][docker-pull]][docker] [![Go Report Card][report-card-img]][report-card] [![Build Status](https://github.com/aquasecurity/kube-bench/workflows/Build/badge.svg?branch=main)](https://github.com/aquasecurity/kube-bench/actions) [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://github.com/aquasecurity/kube-bench/blob/main/LICENSE) [![Coverage Status][cov-img]][cov] # Kube-bench kube-bench is a Go application that checks whether Kubernetes is deployed securely by running the checks documented in the [CIS Kubernetes Benchmark](https://www.cisecurity.org/benchmark/kubernetes/). Tests are configured with YAML files, making this tool easy to update as test specifications evolve. 1. kube-bench implements the [CIS Kubernetes Benchmark](https://www.cisecurity.org/benchmark/kubernetes/) as closely as possible. Please raise issues here if kube-bench is not correctly implementing the test as described in the Benchmark. To report issues in the Benchmark itself (for example, tests that you believe are inappropriate), please join the [CIS community](https://cisecurity.org). 1. There is not a one-to-one mapping between releases of Kubernetes and releases of the CIS benchmark. See [CIS Kubernetes Benchmark support](#cis-kubernetes-benchmark-support) to see which releases of Kubernetes are covered by different releases of the benchmark. 1. It is impossible to inspect the master nodes of managed clusters, e.g. GKE, EKS, AKS and ACK, using kube-bench as one does not have access to such nodes, although it is still possible to use kube-bench to check worker node configuration in these environments. For help and more information go to our [github discussions q&a](https://github.com/aquasecurity/kube-bench/discussions/categories/q-a) ================================================ FILE: docs/installation.md ================================================ ## Installation You can choose to * Run kube-bench from inside a container (sharing PID namespace with the host). See [Running inside a container](./running.md#running-inside-a-container) for additional details. * Run a container that installs kube-bench on the host, and then run kube-bench directly on the host. See [Installing from a container](#installing-from-a-container) for additional details. * install the latest binaries from the [Releases page](https://github.com/aquasecurity/kube-bench/releases), though please note that you also need to download the config and test files from the `cfg` directory. See [Download and Install binaries](#download-and-install-binaries) for details. * Compile it from source. See [Installing from sources](#installing-from-sources) for details. ### Download and Install binaries It is possible to manually install and run kube-bench release binaries. In order to do that, you must have access to your Kubernetes cluster nodes. Note that if you're using one of the managed Kubernetes services (e.g. EKS, AKS, GKE, ACK, OCP), you will not have access to the master nodes of your cluster and you can’t perform any tests on the master nodes. First, log into one of the nodes using SSH. Install kube-bench binary for your platform using the commands below. Note that there may be newer releases available. See [releases page](https://github.com/aquasecurity/kube-bench/releases). Ubuntu/Debian: ``` KUBE_BENCH_VERSION=0.10.1 curl -L https://github.com/aquasecurity/kube-bench/releases/download/v${KUBE_BENCH_VERSION}/kube-bench_${KUBE_BENCH_VERSION}_linux_amd64.deb -o kube-bench_${KUBE_BENCH_VERSION}_linux_amd64.deb sudo apt install ./kube-bench_${KUBE_BENCH_VERSION}_linux_amd64.deb -f ``` RHEL: ``` KUBE_BENCH_VERSION=0.10.1 curl -L https://github.com/aquasecurity/kube-bench/releases/download/v${KUBE_BENCH_VERSION}/kube-bench_${KUBE_BENCH_VERSION}_linux_amd64.rpm -o kube-bench_${KUBE_BENCH_VERSION}_linux_amd64.rpm sudo yum install kube-bench_${KUBE_BENCH_VERSION}_linux_amd64.rpm -y ``` Alternatively, you can manually download and extract the kube-bench binary: ``` KUBE_BENCH_VERSION=0.10.1 curl -L https://github.com/aquasecurity/kube-bench/releases/download/v${KUBE_BENCH_VERSION}/kube-bench_${KUBE_BENCH_VERSION}_linux_amd64.tar.gz -o kube-bench_${KUBE_BENCH_VERSION}_linux_amd64.tar.gz tar -xvf kube-bench_${KUBE_BENCH_VERSION}_linux_amd64.tar.gz ``` You can then run kube-bench directly: ``` kube-bench ``` If you manually downloaded the kube-bench binary (using curl command above), you have to specify the location of configuration directory and file. For example: ``` ./kube-bench --config-dir `pwd`/cfg --config `pwd`/cfg/config.yaml ``` See previous section on [Running kube-bench](./running.md#running-kube-bench) for further details on using the kube-bench binary. ### Installing from sources If Go is installed on the target machines, you can simply clone this repository and run as follows (assuming your [`GOPATH` is set](https://github.com/golang/go/wiki/GOPATH)) as per this example: ```shell # Create a target directory for the clone, inside the $GOPATH mkdir -p $GOPATH/src/github.com/aquasecurity/kube-bench # Clone this repository, using SSH git clone git@github.com:aquasecurity/kube-bench.git $GOPATH/src/github.com/aquasecurity/kube-bench # Install the pre-requisites go get github.com/aquasecurity/kube-bench # Change to the kube-bench directory cd $GOPATH/src/github.com/aquasecurity/kube-bench # Build the kube-bench binary go build -o kube-bench . # See all supported options ./kube-bench --help # Run all checks ./kube-bench ``` ### Installing from a container This command copies the kube-bench binary and configuration files to your host from the Docker container: **binaries compiled for linux-x86-64 only (so they won't run on macOS or Windows)** ``` docker run --rm -v `pwd`:/host docker.io/aquasec/kube-bench:latest install ``` You can then run `./kube-bench`. ================================================ FILE: docs/platforms.md ================================================ ## CIS Kubernetes Benchmark support kube-bench runs industry standard benchmark tests for Kubernetes. Most of our supported benchmarks are defined in either of the following: * [CIS Kubernetes Benchmarks](https://www.cisecurity.org/benchmark/kubernetes/) * [STIG Document Library](https://public.cyber.mil/stigs/downloads) Other benchmarks are defined by hardening guides. | Source | Kubernetes Benchmark | kube-bench config | Kubernetes versions | |:------:|:--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:-------------------------|:--------------------| | CIS | [1.5.1](https://workbench.cisecurity.org/benchmarks/4892) | cis-1.5 | 1.15 | | CIS | [1.6.0](https://workbench.cisecurity.org/benchmarks/4834) | cis-1.6 | 1.16-1.18 | | CIS | [1.20](https://workbench.cisecurity.org/benchmarks/6246) | cis-1.20 | 1.19-1.21 | | CIS | [1.23](https://workbench.cisecurity.org/benchmarks/7532) | cis-1.23 | 1.22-1.23 | | CIS | [1.24](https://workbench.cisecurity.org/benchmarks/10873) | cis-1.24 | 1.24 | | CIS | [1.7](https://workbench.cisecurity.org/benchmarks/11107) | cis-1.7 | 1.25 | | CIS | [1.8](https://workbench.cisecurity.org/benchmarks/12958) | cis-1.8 | 1.26 | | CIS | [1.9](https://workbench.cisecurity.org/benchmarks/16828) | cis-1.9 | 1.27 | | CIS | [1.10](https://workbench.cisecurity.org/benchmarks/17568) | cis-1.10 | 1.28 | | CIS | [1.11](https://workbench.cisecurity.org/benchmarks/21709) | cis-1.11 | 1.29-1.31 | | CIS | [1.12](https://workbench.cisecurity.org/benchmarks/22107) | cis-1.12 | 1.32-1.34 | | CIS | [GKE 1.0.0](https://workbench.cisecurity.org/benchmarks/4536) | gke-1.0 | GKE | | CIS | [GKE 1.2.0](https://workbench.cisecurity.org/benchmarks/7534) | gke-1.2.0 | GKE | | CIS | [GKE 1.6.0](https://workbench.cisecurity.org/benchmarks/16093) | gke-1.6.0 | GKE | | CIS | [EKS 1.0.1](https://workbench.cisecurity.org/benchmarks/6041) | eks-1.0.1 | EKS | | CIS | [EKS 1.1.0](https://workbench.cisecurity.org/benchmarks/6248) | eks-1.1.0 | EKS | | CIS | [EKS 1.2.0](https://workbench.cisecurity.org/benchmarks/9681) | eks-1.2.0 | EKS | | CIS | [EKS 1.5.0](https://workbench.cisecurity.org/benchmarks/17733) | eks-1.5.0 | EKS | | CIS | [ACK 1.0.0](https://workbench.cisecurity.org/benchmarks/6467) | ack-1.0 | ACK | | CIS | [AKS 1.0.0](https://workbench.cisecurity.org/benchmarks/6347) | aks-1.0 | AKS | | CIS | [AKS 1.7.0](https://workbench.cisecurity.org/benchmarks/20359) | aks-1.7 | AKS | | RHEL | Red Hat OpenShift hardening guide | rh-0.7 | OCP 3.10-3.11 | | CIS | [OCP4 1.1.0](https://workbench.cisecurity.org/benchmarks/6778) | rh-1.0 | OCP 4.1- | | CIS | [1.6.0-k3s](https://docs.rancher.cn/docs/k3s/security/self-assessment/_index) | cis-1.6-k3s | k3s v1.16-v1.24 | | DISA | [Kubernetes Ver 1, Rel 6](https://dl.dod.cyber.mil/wp-content/uploads/stigs/zip/U_Kubernetes_V1R6_STIG.zip) | eks-stig-kubernetes-v1r6 | EKS | | CIS | [TKGI 1.2.53](https://network.pivotal.io/products/p-compliance-scanner#/releases/1248397) | tkgi-1.2.53 | vmware | | CIS | [1.7.0-rke](https://ranchermanager.docs.rancher.com/v2.7/reference-guides/rancher-security/hardening-guides/rke1-hardening-guide/rke1-self-assessment-guide-with-cis-v1.7-k8s-v1.25-v1.26-v1.27) | rke-cis-1.7 | rke v1.25-v1.27 | | CIS | [1.7.0-rke2](https://ranchermanager.docs.rancher.com/v2.7/reference-guides/rancher-security/hardening-guides/rke2-hardening-guide/rke2-self-assessment-guide-with-cis-v1.7-k8s-v1.25-v1.26-v1.27) | rke2-cis-1.6 | rke2 v1.25-v1.27 | | CIS | [1.7.0-k3s](https://ranchermanager.docs.rancher.com/v2.7/reference-guides/rancher-security/hardening-guides/k3s-hardening-guide/k3s-self-assessment-guide-with-cis-v1.7-k8s-v1.25-v1.26-v1.27) | k3s-cis-1.7 | k3s v1.25-v1.27 | ================================================ FILE: docs/running.md ================================================ ## Running kube-bench If you run kube-bench directly from the command line you may need to be root / sudo to have access to all the config files. By default kube-bench attempts to auto-detect the running version of Kubernetes, and map this to the corresponding CIS Benchmark version. For example, Kubernetes version 1.15 is mapped to CIS Benchmark version `cis-1.15` which is the benchmark version valid for Kubernetes 1.15. kube-bench also attempts to identify the components running on the node, and uses this to determine which tests to run (for example, only running the master node tests if the node is running an API server). **Please note** It is impossible to inspect the master nodes of managed clusters, e.g. GKE, EKS, AKS and ACK, using kube-bench as one does not have access to such nodes, although it is still possible to use kube-bench to check worker node configuration in these environments. ### Running inside a container You can avoid installing kube-bench on the host by running it inside a container using the host PID namespace and mounting the `/etc` and `/var` directories where the configuration and other files are located on the host so that kube-bench can check their existence and permissions. ``` docker run --pid=host -v /etc:/etc:ro -v /var:/var:ro -t docker.io/aquasec/kube-bench:latest --version 1.18 ``` > Note: the tests require either the kubelet or kubectl binary in the path in order to auto-detect the Kubernetes version. You can pass `-v $(which kubectl):/usr/local/mount-from-host/bin/kubectl` to resolve this. You will also need to pass in kubeconfig credentials. For example: ``` docker run --pid=host -v /etc:/etc:ro -v /var:/var:ro -v $(which kubectl):/usr/local/mount-from-host/bin/kubectl -v ~/.kube:/.kube -e KUBECONFIG=/.kube/config -t docker.io/aquasec/kube-bench:latest ``` You can use your own configs by mounting them over the default ones in `/opt/kube-bench/cfg/` ``` docker run --pid=host -v /etc:/etc:ro -v /var:/var:ro -t -v path/to/my-config.yaml:/opt/kube-bench/cfg/config.yaml -v $(which kubectl):/usr/local/mount-from-host/bin/kubectl -v ~/.kube:/.kube -e KUBECONFIG=/.kube/config docker.io/aquasec/kube-bench:latest ``` ### Running in a Kubernetes cluster You can run kube-bench inside a pod, but it will need access to the host's PID namespace in order to check the running processes, as well as access to some directories on the host where config files and other files are stored. The `job.yaml` file (available in the root directory of the repository) can be applied to run the tests as a Kubernetes `Job`. For example: ```bash $ kubectl apply -f job.yaml job.batch/kube-bench created $ kubectl get pods NAME READY STATUS RESTARTS AGE kube-bench-j76s9 0/1 ContainerCreating 0 3s # Wait for a few seconds for the job to complete $ kubectl get pods NAME READY STATUS RESTARTS AGE kube-bench-j76s9 0/1 Completed 0 11s # The results are held in the pod's logs kubectl logs kube-bench-j76s9 [INFO] 1 Master Node Security Configuration [INFO] 1.1 API Server ... ``` To run tests on the master node, the pod needs to be scheduled on that node. This involves setting a nodeSelector and tolerations in the pod spec. The default labels applied to master nodes has changed since Kubernetes 1.11, so if you are using an older version you may need to modify the nodeSelector and tolerations to run the job on the master node. ### Running in an AKS cluster 1. Create an AKS cluster(e.g. 1.13.7) with RBAC enabled, otherwise there would be 4 failures 1. Use the [kubectl-enter plugin](https://github.com/kvaps/kubectl-enter) to shell into a node ` kubectl-enter {node-name} ` or ssh to one agent node could open nsg 22 port and assign a public ip for one agent node (only for testing purpose) 1. Run CIS benchmark to view results: ``` docker run --rm -v `pwd`:/host docker.io/aquasec/kube-bench:latest install ./kube-bench ``` kube-bench cannot be run on AKS master nodes ### Running CIS benchmark in an EKS cluster There is a `job-eks.yaml` file for running the kube-bench node checks on an EKS cluster. The significant difference on EKS is that it's not possible to schedule jobs onto the master node, so master checks can't be performed 1. To create an EKS Cluster refer to [Getting Started with Amazon EKS](https://docs.aws.amazon.com/eks/latest/userguide/getting-started.html) in the *Amazon EKS User Guide* - Information on configuring `eksctl`, `kubectl` and the AWS CLI is within 2. Create an [Amazon Elastic Container Registry (ECR)](https://docs.aws.amazon.com/AmazonECR/latest/userguide/what-is-ecr.html) repository to host the kube-bench container image ``` aws ecr create-repository --repository-name k8s/kube-bench --image-tag-mutability MUTABLE ``` 3. Download, build and push the kube-bench container image to your ECR repo ``` git clone https://github.com/aquasecurity/kube-bench.git cd kube-bench aws ecr get-login-password --region | docker login --username AWS --password-stdin .dkr.ecr..amazonaws.com make build-docker IMAGE_NAME=k8s/kube-bench docker tag k8s/kube-bench:latest .dkr.ecr..amazonaws.com/k8s/kube-bench:latest docker push .dkr.ecr..amazonaws.com/k8s/kube-bench:latest ``` 4. Copy the URI of your pushed image, the URI format is like this: `.dkr.ecr..amazonaws.com/k8s/kube-bench:latest` 5. Replace the `image` value in `job-eks.yaml` with the URI from Step 4 6. Run the kube-bench job on a Pod in your Cluster: `kubectl apply -f job-eks.yaml` 7. Find the Pod that was created, it *should* be in the `default` namespace: `kubectl get pods --all-namespaces` 8. Retrieve the value of this Pod and output the report, note the Pod name will vary: `kubectl logs kube-bench-` - You can save the report for later reference: `kubectl logs kube-bench- > kube-bench-report.txt` ### Running DISA STIG in an EKS cluster There is a `job-eks-stig.yaml` file for running the kube-bench node checks on an EKS cluster. The significant difference on EKS is that it's not possible to schedule jobs onto the master node, so master checks can't be performed 1. To create an EKS Cluster refer to [Getting Started with Amazon EKS](https://docs.aws.amazon.com/eks/latest/userguide/getting-started.html) in the *Amazon EKS User Guide* - Information on configuring `eksctl`, `kubectl` and the AWS CLI is within 2. Create an [Amazon Elastic Container Registry (ECR)](https://docs.aws.amazon.com/AmazonECR/latest/userguide/what-is-ecr.html) repository to host the kube-bench container image ``` aws ecr create-repository --repository-name k8s/kube-bench --image-tag-mutability MUTABLE ``` 3. Download, build and push the kube-bench container image to your ECR repo ``` git clone https://github.com/aquasecurity/kube-bench.git cd kube-bench aws ecr get-login-password --region | docker login --username AWS --password-stdin .dkr.ecr..amazonaws.com docker build -t k8s/kube-bench . docker tag k8s/kube-bench:latest .dkr.ecr..amazonaws.com/k8s/kube-bench:latest docker push .dkr.ecr..amazonaws.com/k8s/kube-bench:latest ``` 4. Copy the URI of your pushed image, the URI format is like this: `.dkr.ecr..amazonaws.com/k8s/kube-bench:latest` 5. Replace the `image` value in `job-eks-stig.yaml` with the URI from Step 4 6. Run the kube-bench job on a Pod in your Cluster: `kubectl apply -f job-eks-stig.yaml` 7. Find the Pod that was created, it *should* be in the `default` namespace: `kubectl get pods --all-namespaces` 8. Retrieve the value of this Pod and output the report, note the Pod name will vary: `kubectl logs kube-bench-` - You can save the report for later reference: `kubectl logs kube-bench- > kube-bench-report.txt` ### Running on OpenShift | OpenShift Hardening Guide | kube-bench config | | ------------------------- | ----------------- | | ocp-3.10 + | rh-0.7 | | ocp-4.1 + | rh-1.0 | kube-bench includes a set of test files for Red Hat's OpenShift hardening guide for OCP 3.10 and 4.1. To run this you will need to specify `--benchmark rh-07`, or `--version ocp-3.10` or,`--version ocp-4.5` or `--benchmark rh-1.0` `kube-bench` supports auto-detection, when you run the `kube-bench` command it will autodetect if running in openshift environment. Since running `kube-bench` requires elevated privileges, the `privileged` SecurityContextConstraint needs to be applied to the ServiceAccount used for the `Job`: ``` oc create namespace kube-bench oc adm policy add-scc-to-user privileged --serviceaccount default oc apply -f job.yaml ``` ### Running in a GKE cluster | CIS Benchmark | Targets | | ------------- | ----------------------------------------------------------- | | gke-1.0 | master, controlplane, node, etcd, policies, managedservices | | gke-1.2.0 | master, controlplane, node, policies, managedservices | | gke-1.6.0 | master, controlplane, node, policies, managedservices | kube-bench includes benchmarks for GKE. To run this you will need to specify `--benchmark gke-1.0`, `--benchmark gke-1.2.0` or `--benchmark gke-1.6.0` when you run the `kube-bench` command. To run the benchmark as a job in your GKE cluster apply the included `job-gke.yaml`. ``` kubectl apply -f job-gke.yaml ``` ### Running in a ACK cluster | CIS Benchmark | Targets | | ------------- | ----------------------------------------------------------- | | ack-1.0 | master, controlplane, node, etcd, policies, managedservices | kube-bench includes benchmarks for Alibaba Cloud Container Service For Kubernetes (ACK). To run this you will need to specify `--benchmark ack-1.0` when you run the `kube-bench` command. To run the benchmark as a job in your ACK cluster apply the included `job-ack.yaml`. ``` kubectl apply -f job-ack.yaml ``` ### Running in a VMware TKGI cluster | CIS Benchmark | Targets | |---------------|--------------------------------------------| | tkgi-1.2.53 | master, etcd, controlplane, node, policies | kube-bench includes benchmarks for VMware tkgi platform. To run this you will need to specify `--benchmark tkgi-1.2.53` when you run the `kube-bench` command. To run the benchmark as a job in your VMware tkgi cluster apply the included `job-tkgi.yaml`. ``` kubectl apply -f job-tkgi.yaml ``` ### Running in a Rancher RKE cluster | CIS Benchmark | Targets | |---------------|--------------------------------------------| | rke-cis-1.7 | master, etcd, controlplane, node, policies | kube-bench includes benchmarks for Rancher RKE platform. To run this you will need to specify `--benchmark rke-cis-1.7` when you run the `kube-bench` command. ### Running in a Rancher RKE2 cluster | CIS Benchmark | Targets | |---------------|--------------------------------------------| | rke2-cis-1.7 | master, etcd, controlplane, node, policies | kube-bench includes benchmarks for Rancher RKE2 platform. To run this you will need to specify `--benchmark rke2-cis-1.7` when you run the `kube-bench` command. ### Running in a Rancher K3s cluster | CIS Benchmark | Targets | |---------------|--------------------------------------------| | k3s-cis-1.7 | master, etcd, controlplane, node, policies | kube-bench includes benchmarks for Rancher K3S platform. To run this you will need to specify `--benchmark k3s-cis-1.7` when you run the `kube-bench` command. ================================================ FILE: entrypoint.sh ================================================ #!/bin/sh -e if [ "$1" == "install" ]; then if [ -d /host ]; then mkdir -p /host/cfg/ yes | cp -rf cfg/* /host/cfg/ yes | cp -rf /usr/local/bin/kube-bench /host/ echo "===============================================" echo "kube-bench is now installed on your host " echo "Run ./kube-bench to perform a security check " echo "===============================================" else echo "Usage:" echo " install: docker run --rm -v \`pwd\`:/host aquasec/kube-bench install" echo " run: docker run --rm --pid=host aquasec/kube-bench [command]" exit fi else exec kube-bench "$@" fi ================================================ FILE: fipsonly.go ================================================ //go:build fipsonly package main import ( _ "crypto/tls/fipsonly" ) ================================================ FILE: go.mod ================================================ module github.com/aquasecurity/kube-bench go 1.25.0 require ( github.com/aws/aws-sdk-go-v2 v1.41.4 github.com/aws/aws-sdk-go-v2/config v1.32.12 github.com/aws/aws-sdk-go-v2/service/securityhub v1.68.2 github.com/fatih/color v1.18.0 github.com/golang/glog v1.2.5 github.com/magiconair/properties v1.8.10 github.com/onsi/ginkgo v1.16.5 github.com/pkg/errors v0.9.1 github.com/spf13/cobra v1.10.2 github.com/spf13/viper v1.21.0 github.com/stretchr/testify v1.11.1 golang.org/x/exp v0.0.0-20250718183923-645b1fa84792 gopkg.in/yaml.v2 v2.4.0 gorm.io/driver/postgres v1.6.0 gorm.io/gorm v1.31.1 k8s.io/apimachinery v0.35.2 k8s.io/client-go v0.35.2 ) require ( github.com/aws/aws-sdk-go-v2/credentials v1.19.12 // indirect github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.20 // indirect github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.20 // indirect github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.20 // indirect github.com/aws/aws-sdk-go-v2/internal/ini v1.8.6 // indirect github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.7 // indirect github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.20 // indirect github.com/aws/aws-sdk-go-v2/service/signin v1.0.8 // indirect github.com/aws/aws-sdk-go-v2/service/sso v1.30.13 // indirect github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.17 // indirect github.com/aws/aws-sdk-go-v2/service/sts v1.41.9 // indirect github.com/aws/smithy-go v1.24.2 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/emicklei/go-restful/v3 v3.12.2 // indirect github.com/fsnotify/fsnotify v1.9.0 // indirect github.com/fxamacker/cbor/v2 v2.9.0 // indirect github.com/go-logr/logr v1.4.3 // indirect github.com/go-openapi/jsonpointer v0.21.0 // indirect github.com/go-openapi/jsonreference v0.20.2 // indirect github.com/go-openapi/swag v0.23.0 // indirect github.com/go-viper/mapstructure/v2 v2.4.0 // indirect github.com/google/gnostic-models v0.7.0 // indirect github.com/google/uuid v1.6.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/jackc/pgpassfile v1.0.0 // indirect github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect github.com/jackc/pgx/v5 v5.6.0 // indirect github.com/jackc/puddle/v2 v2.2.2 // indirect github.com/jinzhu/inflection v1.0.0 // indirect github.com/jinzhu/now v1.1.5 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/mailru/easyjson v0.7.7 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/pelletier/go-toml/v2 v2.2.4 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/sagikazarmark/locafero v0.11.0 // indirect github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 // indirect github.com/spf13/afero v1.15.0 // indirect github.com/spf13/cast v1.10.0 // indirect github.com/spf13/pflag v1.0.10 // indirect github.com/stretchr/objx v0.5.2 // indirect github.com/subosito/gotenv v1.6.0 // indirect github.com/x448/float16 v0.8.4 // indirect go.yaml.in/yaml/v2 v2.4.3 // indirect go.yaml.in/yaml/v3 v3.0.4 // indirect golang.org/x/crypto v0.45.0 // indirect golang.org/x/net v0.47.0 // indirect golang.org/x/oauth2 v0.30.0 // indirect golang.org/x/sync v0.18.0 // indirect golang.org/x/sys v0.38.0 // indirect golang.org/x/term v0.37.0 // indirect golang.org/x/text v0.31.0 // indirect golang.org/x/time v0.9.0 // indirect google.golang.org/protobuf v1.36.8 // indirect gopkg.in/evanphx/json-patch.v4 v4.13.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect k8s.io/api v0.35.2 // indirect k8s.io/klog/v2 v2.130.1 // indirect k8s.io/kube-openapi v0.0.0-20250910181357-589584f1c912 // indirect k8s.io/utils v0.0.0-20251002143259-bc988d571ff4 // indirect sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 // indirect sigs.k8s.io/randfill v1.0.0 // indirect sigs.k8s.io/structured-merge-diff/v6 v6.3.0 // indirect sigs.k8s.io/yaml v1.6.0 // indirect ) ================================================ FILE: go.sum ================================================ github.com/Masterminds/semver/v3 v3.4.0 h1:Zog+i5UMtVoCU8oKka5P7i9q9HgrJeGzI9SA1Xbatp0= github.com/Masterminds/semver/v3 v3.4.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM= github.com/aws/aws-sdk-go-v2 v1.41.4 h1:10f50G7WyU02T56ox1wWXq+zTX9I1zxG46HYuG1hH/k= github.com/aws/aws-sdk-go-v2 v1.41.4/go.mod h1:mwsPRE8ceUUpiTgF7QmQIJ7lgsKUPQOUl3o72QBrE1o= github.com/aws/aws-sdk-go-v2/config v1.32.12 h1:O3csC7HUGn2895eNrLytOJQdoL2xyJy0iYXhoZ1OmP0= github.com/aws/aws-sdk-go-v2/config v1.32.12/go.mod h1:96zTvoOFR4FURjI+/5wY1vc1ABceROO4lWgWJuxgy0g= github.com/aws/aws-sdk-go-v2/credentials v1.19.12 h1:oqtA6v+y5fZg//tcTWahyN9PEn5eDU/Wpvc2+kJ4aY8= github.com/aws/aws-sdk-go-v2/credentials v1.19.12/go.mod h1:U3R1RtSHx6NB0DvEQFGyf/0sbrpJrluENHdPy1j/3TE= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.20 h1:zOgq3uezl5nznfoK3ODuqbhVg1JzAGDUhXOsU0IDCAo= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.20/go.mod h1:z/MVwUARehy6GAg/yQ1GO2IMl0k++cu1ohP9zo887wE= github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.20 h1:CNXO7mvgThFGqOFgbNAP2nol2qAWBOGfqR/7tQlvLmc= github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.20/go.mod h1:oydPDJKcfMhgfcgBUZaG+toBbwy8yPWubJXBVERtI4o= github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.20 h1:tN6W/hg+pkM+tf9XDkWUbDEjGLb+raoBMFsTodcoYKw= github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.20/go.mod h1:YJ898MhD067hSHA6xYCx5ts/jEd8BSOLtQDL3iZsvbc= github.com/aws/aws-sdk-go-v2/internal/ini v1.8.6 h1:qYQ4pzQ2Oz6WpQ8T3HvGHnZydA72MnLuFK9tJwmrbHw= github.com/aws/aws-sdk-go-v2/internal/ini v1.8.6/go.mod h1:O3h0IK87yXci+kg6flUKzJnWeziQUKciKrLjcatSNcY= github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.7 h1:5EniKhLZe4xzL7a+fU3C2tfUN4nWIqlLesfrjkuPFTY= github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.7/go.mod h1:x0nZssQ3qZSnIcePWLvcoFisRXJzcTVvYpAAdYX8+GI= github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.20 h1:2HvVAIq+YqgGotK6EkMf+KIEqTISmTYh5zLpYyeTo1Y= github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.20/go.mod h1:V4X406Y666khGa8ghKmphma/7C0DAtEQYhkq9z4vpbk= github.com/aws/aws-sdk-go-v2/service/securityhub v1.68.2 h1:FqGvGaRzvB6JLJvzNoBpyHkqe0pDzsan5FXLYvdfRi8= github.com/aws/aws-sdk-go-v2/service/securityhub v1.68.2/go.mod h1:gPgOaKWvECFTmnY8oC/cvFHud/KCa/TOBEwO5dNrL7k= github.com/aws/aws-sdk-go-v2/service/signin v1.0.8 h1:0GFOLzEbOyZABS3PhYfBIx2rNBACYcKty+XGkTgw1ow= github.com/aws/aws-sdk-go-v2/service/signin v1.0.8/go.mod h1:LXypKvk85AROkKhOG6/YEcHFPoX+prKTowKnVdcaIxE= github.com/aws/aws-sdk-go-v2/service/sso v1.30.13 h1:kiIDLZ005EcKomYYITtfsjn7dtOwHDOFy7IbPXKek2o= github.com/aws/aws-sdk-go-v2/service/sso v1.30.13/go.mod h1:2h/xGEowcW/g38g06g3KpRWDlT+OTfxxI0o1KqayAB8= github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.17 h1:jzKAXIlhZhJbnYwHbvUQZEB8KfgAEuG0dc08Bkda7NU= github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.17/go.mod h1:Al9fFsXjv4KfbzQHGe6V4NZSZQXecFcvaIF4e70FoRA= github.com/aws/aws-sdk-go-v2/service/sts v1.41.9 h1:Cng+OOwCHmFljXIxpEVXAGMnBia8MSU6Ch5i9PgBkcU= github.com/aws/aws-sdk-go-v2/service/sts v1.41.9/go.mod h1:LrlIndBDdjA/EeXeyNBle+gyCwTlizzW5ycgWnvIxkk= github.com/aws/smithy-go v1.24.2 h1:FzA3bu/nt/vDvmnkg+R8Xl46gmzEDam6mZ1hzmwXFng= github.com/aws/smithy-go v1.24.2/go.mod h1:YE2RhdIuDbA5E5bTdciG9KrW3+TiEONeUWCqxX9i1Fc= github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/emicklei/go-restful/v3 v3.12.2 h1:DhwDP0vY3k8ZzE0RunuJy8GhNpPL6zqLkDf9B/a0/xU= github.com/emicklei/go-restful/v3 v3.12.2/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM= github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k= github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= github.com/fxamacker/cbor/v2 v2.9.0 h1:NpKPmjDBgUfBms6tr6JZkTHtfFGcMKsw3eGcmD/sapM= github.com/fxamacker/cbor/v2 v2.9.0/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ= github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs= github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ= github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY= github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE= github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k= github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE= github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs= github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= github.com/golang/glog v1.2.5 h1:DrW6hGnjIhtvhOIiAKT6Psh/Kd/ldepEa81DKeiRJ5I= github.com/golang/glog v1.2.5/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/google/gnostic-models v0.7.0 h1:qwTtogB15McXDaNqTZdzPJRHvaVJlAl+HVQnLmJEJxo= github.com/google/gnostic-models v0.7.0/go.mod h1:whL5G0m6dmc5cPxKc5bdKdEN3UjI7OUGxBlw57miDrQ= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/pprof v0.0.0-20250403155104-27863c87afa6 h1:BHT72Gu3keYf3ZEu2J0b1vyeLSOYI8bm5wbJM/8yDe8= github.com/google/pprof v0.0.0-20250403155104-27863c87afa6/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo= github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= github.com/jackc/pgx/v5 v5.6.0 h1:SWJzexBzPL5jb0GEsrPMLIsi/3jOo7RHlzTjcAeDrPY= github.com/jackc/pgx/v5 v5.6.0/go.mod h1:DNZ/vlrUnhWCoFGxHAG8U2ljioxukquj7utPDgtQdTw= github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo= github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4= github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ= github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/magiconair/properties v1.8.10 h1:s31yESBquKXCV9a/ScB3ESkOjUYYv+X0rg8SYxI99mE= github.com/magiconair/properties v1.8.10/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee h1:W5t00kpgFdJifH4BDsTlE89Zl93FEloxaWZfGcifgq8= github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= github.com/onsi/ginkgo/v2 v2.27.2 h1:LzwLj0b89qtIy6SSASkzlNvX6WktqurSHwkk2ipF/Ns= github.com/onsi/ginkgo/v2 v2.27.2/go.mod h1:ArE1D/XhNXBXCBkKOLkbsb2c81dQHCRcF5zwn/ykDRo= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/onsi/gomega v1.38.2 h1:eZCjf2xjZAqe+LeWvKb5weQ+NcPwX84kqJ0cZNxok2A= github.com/onsi/gomega v1.38.2/go.mod h1:W2MJcYxRGV63b418Ai34Ud0hEdTVXq9NW9+Sx6uXf3k= github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4= github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/sagikazarmark/locafero v0.11.0 h1:1iurJgmM9G3PA/I+wWYIOw/5SyBtxapeHDcg+AAIFXc= github.com/sagikazarmark/locafero v0.11.0/go.mod h1:nVIGvgyzw595SUSUE6tvCp3YYTeHs15MvlmU87WwIik= github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 h1:+jumHNA0Wrelhe64i8F6HNlS8pkoyMv5sreGx2Ry5Rw= github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8/go.mod h1:3n1Cwaq1E1/1lhQhtRK2ts/ZwZEhjcQeJQ1RuC6Q/8U= github.com/spf13/afero v1.15.0 h1:b/YBCLWAJdFWJTN9cLhiXXcD7mzKn9Dm86dNnfyQw1I= github.com/spf13/afero v1.15.0/go.mod h1:NC2ByUVxtQs4b3sIUphxK0NioZnmxgyCrfzeuq8lxMg= github.com/spf13/cast v1.10.0 h1:h2x0u2shc1QuLHfxi+cTJvs30+ZAHOGRic8uyGTDWxY= github.com/spf13/cast v1.10.0/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo= github.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU= github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4= github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk= github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/viper v1.21.0 h1:x5S+0EU27Lbphp4UKm1C+1oQO+rKx36vfCoaVebLFSU= github.com/spf13/viper v1.21.0/go.mod h1:P0lhsswPGWD/1lZJ9ny3fYnVqxiegrlNrEmgLjbTCAY= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= go.yaml.in/yaml/v2 v2.4.3 h1:6gvOSjQoTB3vt1l+CU+tSyi/HOjfOjRLJ4YwYZGwRO0= go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8= go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q= golang.org/x/crypto v0.45.0/go.mod h1:XTGrrkGJve7CYK7J8PEww4aY7gM3qMCElcJQ8n8JdX4= golang.org/x/exp v0.0.0-20250718183923-645b1fa84792 h1:R9PFI6EUdfVKgwKjZef7QIwGcBKu86OEFpJ9nUEP2l4= golang.org/x/exp v0.0.0-20250718183923-645b1fa84792/go.mod h1:A+z0yzpGtvnG90cToK5n2tu8UJVP2XUATh+r+sfOOOc= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.29.0 h1:HV8lRxZC4l2cr3Zq1LvtOsi/ThTgWnUk/y64QSs8GwA= golang.org/x/mod v0.29.0/go.mod h1:NyhrlYXJ2H4eJiRy/WDBO6HMqZQ6q9nk4JzS3NuCK+w= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY= golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU= golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI= golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I= golang.org/x/sync v0.18.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc= golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= golang.org/x/term v0.37.0 h1:8EGAD0qCmHYZg6J17DvsMy9/wJ7/D/4pV/wfnld5lTU= golang.org/x/term v0.37.0/go.mod h1:5pB4lxRNYYVZuTLmy8oR2BH8dflOR+IbTYFD8fi3254= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM= golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM= golang.org/x/time v0.9.0 h1:EsRrnYcQiGH+5FfbgvV4AP7qEZstoyrHB0DzarOQ4ZY= golang.org/x/time v0.9.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.38.0 h1:Hx2Xv8hISq8Lm16jvBZ2VQf+RLmbd7wVUsALibYI/IQ= golang.org/x/tools v0.38.0/go.mod h1:yEsQ/d/YK8cjh0L6rZlY8tgtlKiBNTL14pGDJPJpYQs= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.36.8 h1:xHScyCOEuuwZEc6UtSOvPbAT4zRh0xcNRYekJwfqyMc= google.golang.org/protobuf v1.36.8/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/evanphx/json-patch.v4 v4.13.0 h1:czT3CmqEaQ1aanPc5SdlgQrrEIb8w/wwCvWWnfEbYzo= gopkg.in/evanphx/json-patch.v4 v4.13.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gorm.io/driver/postgres v1.6.0 h1:2dxzU8xJ+ivvqTRph34QX+WrRaJlmfyPqXmoGVjMBa4= gorm.io/driver/postgres v1.6.0/go.mod h1:vUw0mrGgrTK+uPHEhAdV4sfFELrByKVGnaVRkXDhtWo= gorm.io/gorm v1.31.1 h1:7CA8FTFz/gRfgqgpeKIBcervUn3xSyPUmr6B2WXJ7kg= gorm.io/gorm v1.31.1/go.mod h1:XyQVbO2k6YkOis7C2437jSit3SsDK72s7n7rsSHd+Gs= k8s.io/api v0.35.2 h1:tW7mWc2RpxW7HS4CoRXhtYHSzme1PN1UjGHJ1bdrtdw= k8s.io/api v0.35.2/go.mod h1:7AJfqGoAZcwSFhOjcGM7WV05QxMMgUaChNfLTXDRE60= k8s.io/apimachinery v0.35.2 h1:NqsM/mmZA7sHW02JZ9RTtk3wInRgbVxL8MPfzSANAK8= k8s.io/apimachinery v0.35.2/go.mod h1:jQCgFZFR1F4Ik7hvr2g84RTJSZegBc8yHgFWKn//hns= k8s.io/client-go v0.35.2 h1:YUfPefdGJA4aljDdayAXkc98DnPkIetMl4PrKX97W9o= k8s.io/client-go v0.35.2/go.mod h1:4QqEwh4oQpeK8AaefZ0jwTFJw/9kIjdQi0jpKeYvz7g= k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk= k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= k8s.io/kube-openapi v0.0.0-20250910181357-589584f1c912 h1:Y3gxNAuB0OBLImH611+UDZcmKS3g6CthxToOb37KgwE= k8s.io/kube-openapi v0.0.0-20250910181357-589584f1c912/go.mod h1:kdmbQkyfwUagLfXIad1y2TdrjPFWp2Q89B3qkRwf/pQ= k8s.io/utils v0.0.0-20251002143259-bc988d571ff4 h1:SjGebBtkBqHFOli+05xYbK8YF1Dzkbzn+gDM4X9T4Ck= k8s.io/utils v0.0.0-20251002143259-bc988d571ff4/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 h1:IpInykpT6ceI+QxKBbEflcR5EXP7sU1kvOlxwZh5txg= sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730/go.mod h1:mdzfpAEoE6DHQEN0uh9ZbOCuHbLK5wOm7dK4ctXE9Tg= sigs.k8s.io/randfill v1.0.0 h1:JfjMILfT8A6RbawdsK2JXGBR5AQVfd+9TbzrlneTyrU= sigs.k8s.io/randfill v1.0.0/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY= sigs.k8s.io/structured-merge-diff/v6 v6.3.0 h1:jTijUJbW353oVOd9oTlifJqOGEkUw2jB/fXCbTiQEco= sigs.k8s.io/structured-merge-diff/v6 v6.3.0/go.mod h1:M3W8sfWvn2HhQDIbGWj3S099YozAsymCo/wrT5ohRUE= sigs.k8s.io/yaml v1.6.0 h1:G8fkbMSAFqgEFgh4b1wmtzDnioxFCUgTZhlbj5P9QYs= sigs.k8s.io/yaml v1.6.0/go.mod h1:796bPqUfzR/0jLAl6XjHl3Ck7MiyVv8dbTdyT3/pMf4= ================================================ FILE: hack/debug.yaml ================================================ --- # use this pod with: kubectl run ubuntu -it --pid=host -- /bin/bash # this allows you to debug what is running on the host. apiVersion: v1 kind: Pod metadata: name: ubuntu spec: hostPID: true containers: - name: ubuntu image: ubuntu command: ["/bin/bash", "-c", "--"] args: ["while true; do sleep 30; done;"] env: - name: PATH value: "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/local/mount-from-host/bin" volumeMounts: - name: var-lib-kubelet mountPath: /var/lib/kubelet - name: etc-systemd mountPath: /etc/systemd - name: etc-kubernetes mountPath: /etc/kubernetes # /usr/local/mount-from-host/bin is mounted to access kubectl / kubelet, for auto-detecting the Kubernetes version. # You can omit this mount if you specify --version as part of the command. - name: usr-bin mountPath: /usr/local/mount-from-host/bin - name: kind-bin mountPath: /kind/bin resources: limits: memory: "128Mi" cpu: "500m" volumes: - name: var-lib-kubelet hostPath: path: "/var/lib/kubelet" - name: etc-systemd hostPath: path: "/etc/systemd" - name: etc-kubernetes hostPath: path: "/etc/kubernetes" - name: usr-bin hostPath: path: "/usr/bin" - name: kind-bin hostPath: path: "/kind/bin" ================================================ FILE: hack/kind-stig.test.yaml ================================================ --- apiVersion: batch/v1 kind: Job metadata: name: kube-bench spec: template: metadata: labels: app: kube-bench spec: hostPID: true containers: - name: kube-bench image: docker.io/aquasec/kube-bench:latest command: [ "kube-bench", "run", "--benchmark", "eks-stig-kubernetes-v1r6", ] volumeMounts: - name: var-lib-etcd mountPath: /var/lib/etcd - name: var-lib-kubelet mountPath: /var/lib/kubelet - name: etc-systemd mountPath: /etc/systemd - name: etc-kubernetes mountPath: /etc/kubernetes # /usr/local/mount-from-host/bin is mounted to access kubectl / kubelet, for auto-detecting the Kubernetes version. # You can omit this mount if you specify --version as part of the command. - name: usr-bin mountPath: /usr/local/mount-from-host/bin - name: kind-bin mountPath: /kind/bin restartPolicy: Never volumes: - name: var-lib-etcd hostPath: path: "/var/lib/etcd" - name: var-lib-kubelet hostPath: path: "/var/lib/kubelet" - name: etc-systemd hostPath: path: "/etc/systemd" - name: etc-kubernetes hostPath: path: "/etc/kubernetes" - name: usr-bin hostPath: path: "/usr/bin" - name: kind-bin hostPath: path: "/kind/bin" ================================================ FILE: hack/kind-stig.yaml ================================================ --- apiVersion: batch/v1 kind: Job metadata: name: kube-bench spec: template: metadata: labels: app: kube-bench spec: hostPID: true containers: - name: kube-bench image: docker.io/aquasec/kube-bench:${VERSION} command: [ "kube-bench", "run", "--benchmark", "eks-stig-kubernetes-v1r6", ] volumeMounts: - name: var-lib-etcd mountPath: /var/lib/etcd - name: var-lib-kubelet mountPath: /var/lib/kubelet - name: etc-systemd mountPath: /etc/systemd - name: etc-kubernetes mountPath: /etc/kubernetes # /usr/local/mount-from-host/bin is mounted to access kubectl / kubelet, for auto-detecting the Kubernetes version. # You can omit this mount if you specify --version as part of the command. - name: usr-bin mountPath: /usr/local/mount-from-host/bin - name: kind-bin mountPath: /kind/bin restartPolicy: Never volumes: - name: var-lib-etcd hostPath: path: "/var/lib/etcd" - name: var-lib-kubelet hostPath: path: "/var/lib/kubelet" - name: etc-systemd hostPath: path: "/etc/systemd" - name: etc-kubernetes hostPath: path: "/etc/kubernetes" - name: usr-bin hostPath: path: "/usr/bin" - name: kind-bin hostPath: path: "/kind/bin" ================================================ FILE: hack/kind.yaml ================================================ --- apiVersion: batch/v1 kind: Job metadata: name: kube-bench spec: template: metadata: labels: app: kube-bench spec: hostPID: true containers: - name: kube-bench image: docker.io/aquasec/kube-bench:${VERSION} command: ["kube-bench"] volumeMounts: - name: var-lib-etcd mountPath: /var/lib/etcd - name: var-lib-kubelet mountPath: /var/lib/kubelet - name: etc-systemd mountPath: /etc/systemd - name: etc-kubernetes mountPath: /etc/kubernetes # /usr/local/mount-from-host/bin is mounted to access kubectl / kubelet, for auto-detecting the Kubernetes version. # You can omit this mount if you specify --version as part of the command. - name: usr-bin mountPath: /usr/local/mount-from-host/bin - name: kind-bin mountPath: /kind/bin restartPolicy: Never volumes: - name: var-lib-etcd hostPath: path: "/var/lib/etcd" - name: var-lib-kubelet hostPath: path: "/var/lib/kubelet" - name: etc-systemd hostPath: path: "/etc/systemd" - name: etc-kubernetes hostPath: path: "/etc/kubernetes" - name: usr-bin hostPath: path: "/usr/bin" - name: kind-bin hostPath: path: "/kind/bin" ================================================ FILE: hack/node_only.yaml ================================================ --- node: components: - kubelet - proxy # kubernetes is a component to cover the config file /etc/kubernetes/config that is referred to in the benchmark - kubernetes kubernetes: defaultconf: "/etc/kubernetes/config" kubelet: cafile: - "/etc/kubernetes/pki/ca.crt" - "/etc/kubernetes/certs/ca.crt" - "/etc/kubernetes/cert/ca.pem" svc: # These paths must also be included # in the 'confs' property below - "/etc/systemd/system/kubelet.service.d/10-kubeadm.conf" - "/etc/systemd/system/kubelet.service" - "/lib/systemd/system/kubelet.service" bins: - "hyperkube kubelet" - "kubelet" kubeconfig: - "/etc/kubernetes/kubelet.conf" - "/var/lib/kubelet/kubeconfig" - "/etc/kubernetes/kubelet-kubeconfig" confs: - "/var/lib/kubelet/config.yaml" - "/var/lib/kubelet/config.yml" - "/etc/kubernetes/kubelet/kubelet-config.json" - "/home/kubernetes/kubelet-config.yaml" - "/home/kubernetes/kubelet-config.yml" - "/etc/default/kubelet" ## Due to the fact that the kubelet might be configured ## without a kubelet-config file, we use a work-around ## of pointing to the systemd service file (which can also ## hold kubelet configuration). ## Note: The following paths must match the one under 'svc' - "/etc/systemd/system/kubelet.service.d/10-kubeadm.conf" - "/etc/systemd/system/kubelet.service" - "/lib/systemd/system/kubelet.service" defaultconf: "/var/lib/kubelet/config.yaml" defaultsvc: "/etc/systemd/system/kubelet.service.d/10-kubeadm.conf" defaultkubeconfig: "/etc/kubernetes/kubelet.conf" defaultcafile: "/etc/kubernetes/pki/ca.crt" proxy: bins: - "kube-proxy" - "hyperkube proxy" - "hyperkube kube-proxy" - "proxy" confs: - /etc/kubernetes/proxy - /etc/kubernetes/addons/kube-proxy-daemonset.yaml kubeconfig: - /etc/kubernetes/kubelet-kubeconfig svc: - "/lib/systemd/system/kube-proxy.service" defaultconf: /etc/kubernetes/addons/kube-proxy-daemonset.yaml defaultkubeconfig: "/etc/kubernetes/proxy.conf" version_mapping: "1.15": "cis-1.5" "1.16": "cis-1.6" "1.17": "cis-1.6" "1.18": "cis-1.6" "1.19": "cis-1.6" "ocp-3.10": "rh-0.7" "ocp-3.11": "rh-0.7" ================================================ FILE: helper_scripts/check_files_owner_in_dir.sh ================================================ #!/usr/bin/env bash # This script is used to ensure the owner is set to root:root for # the given directory and all the files in it # # inputs: # $1 = /full/path/to/directory # # outputs: # true/false INPUT_DIR=$1 if [[ "${INPUT_DIR}" == "" ]]; then echo "false" exit fi if [[ $(stat -c %U:%G ${INPUT_DIR}) != "root:root" ]]; then echo "false" exit fi statInfoLines=$(stat -c "%n %U:%G" ${INPUT_DIR}/*) while read -r statInfoLine; do f=$(echo ${statInfoLine} | cut -d' ' -f1) p=$(echo ${statInfoLine} | cut -d' ' -f2) if [[ $(basename "$f" .pem) == "kube-etcd-"* ]]; then if [[ "$p" != "root:root" && "$p" != "etcd:etcd" ]]; then echo "false" exit fi else if [[ "$p" != "root:root" ]]; then echo "false" exit fi fi done <<< "${statInfoLines}" echo "true" exit ================================================ FILE: hooks/build ================================================ #!/bin/bash # $IMAGE_NAME var is injected into the build so the tag is correct. docker build --build-arg BUILD_DATE=`date -u +"%Y-%m-%dT%H:%M:%SZ"` \ --build-arg VCS_REF=`git rev-parse --short HEAD` \ --build-arg KUBEBENCH_VERSION=`git describe --tags --abbrev=0` \ -t $IMAGE_NAME . ================================================ FILE: integration/testdata/Expected_output.data ================================================ [INFO] 1 Master Node Security Configuration [INFO] 1.1 Master Node Configuration Files [PASS] 1.1.1 Ensure that the API server pod specification file permissions are set to 644 or more restrictive (Automated) [PASS] 1.1.2 Ensure that the API server pod specification file ownership is set to root:root (Automated) [PASS] 1.1.3 Ensure that the controller manager pod specification file permissions are set to 644 or more restrictive (Automated) [PASS] 1.1.4 Ensure that the controller manager pod specification file ownership is set to root:root (Automated) [PASS] 1.1.5 Ensure that the scheduler pod specification file permissions are set to 644 or more restrictive (Automated) [PASS] 1.1.6 Ensure that the scheduler pod specification file ownership is set to root:root (Automated) [PASS] 1.1.7 Ensure that the etcd pod specification file permissions are set to 644 or more restrictive (Automated) [PASS] 1.1.8 Ensure that the etcd pod specification file ownership is set to root:root (Automated) [WARN] 1.1.9 Ensure that the Container Network Interface file permissions are set to 644 or more restrictive (Manual) [WARN] 1.1.10 Ensure that the Container Network Interface file ownership is set to root:root (Manual) [PASS] 1.1.11 Ensure that the etcd data directory permissions are set to 700 or more restrictive (Automated) [FAIL] 1.1.12 Ensure that the etcd data directory ownership is set to etcd:etcd (Automated) [PASS] 1.1.13 Ensure that the admin.conf file permissions are set to 644 or more restrictive (Automated) [PASS] 1.1.14 Ensure that the admin.conf file ownership is set to root:root (Automated) [PASS] 1.1.15 Ensure that the scheduler.conf file permissions are set to 644 or more restrictive (Automated) [PASS] 1.1.16 Ensure that the scheduler.conf file ownership is set to root:root (Automated) [PASS] 1.1.17 Ensure that the controller-manager.conf file permissions are set to 644 or more restrictive (Automated) [PASS] 1.1.18 Ensure that the controller-manager.conf file ownership is set to root:root (Automated) [PASS] 1.1.19 Ensure that the Kubernetes PKI directory and file ownership is set to root:root (Automated) [PASS] 1.1.20 Ensure that the Kubernetes PKI certificate file permissions are set to 644 or more restrictive (Manual) [PASS] 1.1.21 Ensure that the Kubernetes PKI key file permissions are set to 600 (Manual) [INFO] 1.2 API Server [WARN] 1.2.1 Ensure that the --anonymous-auth argument is set to false (Manual) [PASS] 1.2.2 Ensure that the --token-auth-file parameter is not set (Automated) [PASS] 1.2.3 Ensure that the --kubelet-https argument is set to true (Automated) [PASS] 1.2.4 Ensure that the --kubelet-client-certificate and --kubelet-client-key arguments are set as appropriate (Automated) [FAIL] 1.2.5 Ensure that the --kubelet-certificate-authority argument is set as appropriate (Automated) [PASS] 1.2.6 Ensure that the --authorization-mode argument is not set to AlwaysAllow (Automated) [PASS] 1.2.7 Ensure that the --authorization-mode argument includes Node (Automated) [PASS] 1.2.8 Ensure that the --authorization-mode argument includes RBAC (Automated) [WARN] 1.2.9 Ensure that the admission control plugin EventRateLimit is set (Manual) [PASS] 1.2.10 Ensure that the admission control plugin AlwaysAdmit is not set (Automated) [WARN] 1.2.11 Ensure that the admission control plugin AlwaysPullImages is set (Manual) [WARN] 1.2.12 Ensure that the admission control plugin SecurityContextDeny is set if PodSecurityPolicy is not used (Manual) [PASS] 1.2.13 Ensure that the admission control plugin ServiceAccount is set (Automated) [PASS] 1.2.14 Ensure that the admission control plugin NamespaceLifecycle is set (Automated) [FAIL] 1.2.15 Ensure that the admission control plugin PodSecurityPolicy is set (Automated) [PASS] 1.2.16 Ensure that the admission control plugin NodeRestriction is set (Automated) [PASS] 1.2.17 Ensure that the --insecure-bind-address argument is not set (Automated) [PASS] 1.2.18 Ensure that the --insecure-port argument is set to 0 (Automated) [PASS] 1.2.19 Ensure that the --secure-port argument is not set to 0 (Automated) [FAIL] 1.2.20 Ensure that the --profiling argument is set to false (Automated) [FAIL] 1.2.21 Ensure that the --audit-log-path argument is set (Automated) [FAIL] 1.2.22 Ensure that the --audit-log-maxage argument is set to 30 or as appropriate (Automated) [FAIL] 1.2.23 Ensure that the --audit-log-maxbackup argument is set to 10 or as appropriate (Automated) [FAIL] 1.2.24 Ensure that the --audit-log-maxsize argument is set to 100 or as appropriate (Automated) [WARN] 1.2.25 Ensure that the --request-timeout argument is set as appropriate (Manual) [PASS] 1.2.26 Ensure that the --service-account-lookup argument is set to true (Automated) [PASS] 1.2.27 Ensure that the --service-account-key-file argument is set as appropriate (Automated) [PASS] 1.2.28 Ensure that the --etcd-certfile and --etcd-keyfile arguments are set as appropriate (Automated) [PASS] 1.2.29 Ensure that the --tls-cert-file and --tls-private-key-file arguments are set as appropriate (Automated) [PASS] 1.2.30 Ensure that the --client-ca-file argument is set as appropriate (Automated) [PASS] 1.2.31 Ensure that the --etcd-cafile argument is set as appropriate (Automated) [WARN] 1.2.32 Ensure that the --encryption-provider-config argument is set as appropriate (Manual) [WARN] 1.2.33 Ensure that encryption providers are appropriately configured (Manual) [WARN] 1.2.34 Ensure that the API Server only makes use of Strong Cryptographic Ciphers (Manual) [INFO] 1.3 Controller Manager [WARN] 1.3.1 Ensure that the --terminated-pod-gc-threshold argument is set as appropriate (Manual) [FAIL] 1.3.2 Ensure that the --profiling argument is set to false (Automated) [PASS] 1.3.3 Ensure that the --use-service-account-credentials argument is set to true (Automated) [PASS] 1.3.4 Ensure that the --service-account-private-key-file argument is set as appropriate (Automated) [PASS] 1.3.5 Ensure that the --root-ca-file argument is set as appropriate (Automated) [PASS] 1.3.6 Ensure that the RotateKubeletServerCertificate argument is set to true (Automated) [PASS] 1.3.7 Ensure that the --bind-address argument is set to 127.0.0.1 (Automated) [INFO] 1.4 Scheduler [FAIL] 1.4.1 Ensure that the --profiling argument is set to false (Automated) [PASS] 1.4.2 Ensure that the --bind-address argument is set to 127.0.0.1 (Automated) == Remediations master == 1.1.9 Run the below command (based on the file location on your system) on the master node. For example, chmod 644 1.1.10 Run the below command (based on the file location on your system) on the master node. For example, chown root:root 1.1.12 On the etcd server node, get the etcd data directory, passed as an argument --data-dir, from the below command: ps -ef | grep etcd Run the below command (based on the etcd data directory found above). For example, chown etcd:etcd /var/lib/etcd 1.2.1 Edit the API server pod specification file /etc/kubernetes/manifests/kube-apiserver.yaml on the master node and set the below parameter. --anonymous-auth=false 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 /etc/kubernetes/manifests/kube-apiserver.yaml on the master node and set the --kubelet-certificate-authority parameter to the path to the cert file for the certificate authority. --kubelet-certificate-authority= 1.2.9 Follow the Kubernetes documentation and set the desired limits in a configuration file. Then, edit the API server pod specification file /etc/kubernetes/manifests/kube-apiserver.yaml and set the below parameters. --enable-admission-plugins=...,EventRateLimit,... --admission-control-config-file= 1.2.11 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 include AlwaysPullImages. --enable-admission-plugins=...,AlwaysPullImages,... 1.2.12 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 include SecurityContextDeny, unless PodSecurityPolicy is already in place. --enable-admission-plugins=...,SecurityContextDeny,... 1.2.15 Follow the documentation and create Pod Security Policy objects as per your environment. Then, 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: --enable-admission-plugins=...,PodSecurityPolicy,... Then restart the API Server. 1.2.20 Edit the API server pod specification file /etc/kubernetes/manifests/kube-apiserver.yaml on the master node and set the below parameter. --profiling=false 1.2.21 Edit the API server pod specification file /etc/kubernetes/manifests/kube-apiserver.yaml on the master node and set the --audit-log-path parameter to a suitable path and file where you would like audit logs to be written, for example: --audit-log-path=/var/log/apiserver/audit.log 1.2.22 Edit the API server pod specification file /etc/kubernetes/manifests/kube-apiserver.yaml on the master node and set the --audit-log-maxage parameter to 30 or as an appropriate number of days: --audit-log-maxage=30 1.2.23 Edit the API server pod specification file /etc/kubernetes/manifests/kube-apiserver.yaml on the master node and set the --audit-log-maxbackup parameter to 10 or to an appropriate value. --audit-log-maxbackup=10 1.2.24 Edit the API server pod specification file /etc/kubernetes/manifests/kube-apiserver.yaml on the master node and set the --audit-log-maxsize parameter to an appropriate size in MB. For example, to set it as 100 MB: --audit-log-maxsize=100 1.2.25 Edit the API server pod specification file /etc/kubernetes/manifests/kube-apiserver.yaml and set the below parameter as appropriate and if needed. For example, --request-timeout=300s 1.2.32 Follow the Kubernetes documentation and configure a EncryptionConfig file. Then, edit the API server pod specification file /etc/kubernetes/manifests/kube-apiserver.yaml on the master node and set the --encryption-provider-config parameter to the path of that file: --encryption-provider-config= 1.2.33 Follow the Kubernetes documentation and configure a EncryptionConfig file. In this file, choose aescbc, kms or secretbox as the encryption provider. 1.2.34 Edit the API server pod specification file /etc/kubernetes/manifests/kube-apiserver.yaml on the master node and set the below parameter. --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 1.3.1 Edit the Controller Manager pod specification file /etc/kubernetes/manifests/kube-controller-manager.yaml on the master node and set the --terminated-pod-gc-threshold to an appropriate threshold, for example: --terminated-pod-gc-threshold=10 1.3.2 Edit the Controller Manager pod specification file /etc/kubernetes/manifests/kube-controller-manager.yaml on the master node and set the below parameter. --profiling=false 1.4.1 Edit the Scheduler pod specification file /etc/kubernetes/manifests/kube-scheduler.yaml file on the master node and set the below parameter. --profiling=false == Summary master == 43 checks PASS 10 checks FAIL 11 checks WARN 0 checks INFO [INFO] 2 Etcd Node Configuration [INFO] 2 Etcd Node Configuration Files [PASS] 2.1 Ensure that the --cert-file and --key-file arguments are set as appropriate (Automated) [PASS] 2.2 Ensure that the --client-cert-auth argument is set to true (Automated) [PASS] 2.3 Ensure that the --auto-tls argument is not set to true (Automated) [PASS] 2.4 Ensure that the --peer-cert-file and --peer-key-file arguments are set as appropriate (Automated) [PASS] 2.5 Ensure that the --peer-client-cert-auth argument is set to true (Automated) [PASS] 2.6 Ensure that the --peer-auto-tls argument is not set to true (Automated) [PASS] 2.7 Ensure that a unique Certificate Authority is used for etcd (Manual) == Summary etcd == 7 checks PASS 0 checks FAIL 0 checks WARN 0 checks INFO [INFO] 3 Control Plane Configuration [INFO] 3.1 Authentication and Authorization [WARN] 3.1.1 Client certificate authentication should not be used for users (Manual) [INFO] 3.2 Logging [WARN] 3.2.1 Ensure that a minimal audit policy is created (Manual) [WARN] 3.2.2 Ensure that the audit policy covers key security concerns (Manual) == Remediations controlplane == 3.1.1 Alternative mechanisms provided by Kubernetes such as the use of OIDC should be implemented in place of client certificates. 3.2.1 Create an audit policy file for your cluster. 3.2.2 Consider modification of the audit policy in use on the cluster to include these items, at a minimum. == Summary controlplane == 0 checks PASS 0 checks FAIL 3 checks WARN 0 checks INFO [INFO] 4 Worker Node Security Configuration [INFO] 4.1 Worker Node Configuration Files [PASS] 4.1.1 Ensure that the kubelet service file permissions are set to 644 or more restrictive (Automated) [PASS] 4.1.2 Ensure that the kubelet service file ownership is set to root:root (Automated) [WARN] 4.1.3 If proxy kubeconfig file exists ensure permissions are set to 644 or more restrictive (Manual) [WARN] 4.1.4 If proxy kubeconfig file exists ensure ownership is set to root:root (Manual) [PASS] 4.1.5 Ensure that the --kubeconfig kubelet.conf file permissions are set to 644 or more restrictive (Automated) [PASS] 4.1.6 Ensure that the --kubeconfig kubelet.conf file ownership is set to root:root (Automated) [PASS] 4.1.7 Ensure that the certificate authorities file permissions are set to 644 or more restrictive (Manual) [PASS] 4.1.8 Ensure that the client certificate authorities file ownership is set to root:root (Manual) [PASS] 4.1.9 Ensure that the kubelet --config configuration file has permissions set to 644 or more restrictive (Automated) [PASS] 4.1.10 Ensure that the kubelet --config configuration file ownership is set to root:root (Automated) [INFO] 4.2 Kubelet [PASS] 4.2.1 Ensure that the anonymous-auth argument is set to false (Automated) [PASS] 4.2.2 Ensure that the --authorization-mode argument is not set to AlwaysAllow (Automated) [PASS] 4.2.3 Ensure that the --client-ca-file argument is set as appropriate (Automated) [PASS] 4.2.4 Ensure that the --read-only-port argument is set to 0 (Manual) [PASS] 4.2.5 Ensure that the --streaming-connection-idle-timeout argument is not set to 0 (Manual) [FAIL] 4.2.6 Ensure that the --protect-kernel-defaults argument is set to true (Automated) [PASS] 4.2.7 Ensure that the --make-iptables-util-chains argument is set to true (Automated) [PASS] 4.2.8 Ensure that the --hostname-override argument is not set (Manual) [WARN] 4.2.9 Ensure that the --event-qps argument is set to 0 or a level which ensures appropriate event capture (Manual) [WARN] 4.2.10 Ensure that the --tls-cert-file and --tls-private-key-file arguments are set as appropriate (Manual) [PASS] 4.2.11 Ensure that the --rotate-certificates argument is not set to false (Automated) [PASS] 4.2.12 Verify that the RotateKubeletServerCertificate argument is set to true (Manual) [WARN] 4.2.13 Ensure that the Kubelet only makes use of Strong Cryptographic Ciphers (Manual) == Remediations node == 4.1.3 Run the below command (based on the file location on your system) on the each worker node. For example, chmod 644 /etc/kubernetes/proxy.conf 4.1.4 Run the below command (based on the file location on your system) on the each worker node. For example, chown root:root /etc/kubernetes/proxy.conf 4.2.6 If using a Kubelet config file, edit the file to set protectKernelDefaults: true. If using command line arguments, edit the kubelet service file /etc/systemd/system/kubelet.service.d/10-kubeadm.conf on each worker node and set the below parameter in KUBELET_SYSTEM_PODS_ARGS variable. --protect-kernel-defaults=true Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service 4.2.9 If using a Kubelet config file, edit the file to set eventRecordQPS: to an appropriate level. If using command line arguments, edit the kubelet service file /etc/systemd/system/kubelet.service.d/10-kubeadm.conf on each worker node and set the below parameter in KUBELET_SYSTEM_PODS_ARGS variable. Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service 4.2.10 If using a Kubelet config file, edit the file to set tlsCertFile to the location of the certificate file to use to identify this Kubelet, and tlsPrivateKeyFile to the location of the corresponding private key file. If using command line arguments, edit the kubelet service file /etc/systemd/system/kubelet.service.d/10-kubeadm.conf on each worker node and set the below parameters in KUBELET_CERTIFICATE_ARGS variable. --tls-cert-file= --tls-private-key-file= Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service 4.2.13 If using a Kubelet config file, edit the file to set tlsCipherSuites: to 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 or to a subset of these values. If using executable arguments, edit the kubelet service file /etc/systemd/system/kubelet.service.d/10-kubeadm.conf on each worker node and set the --tls-cipher-suites parameter as follows, or to a subset of these values. --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 Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service == Summary node == 17 checks PASS 1 checks FAIL 5 checks WARN 0 checks INFO [INFO] 5 Kubernetes Policies [INFO] 5.1 RBAC and Service Accounts [WARN] 5.1.1 Ensure that the cluster-admin role is only used where required (Manual) [WARN] 5.1.2 Minimize access to secrets (Manual) [WARN] 5.1.3 Minimize wildcard use in Roles and ClusterRoles (Manual) [WARN] 5.1.4 Minimize access to create pods (Manual) [WARN] 5.1.5 Ensure that default service accounts are not actively used. (Manual) [WARN] 5.1.6 Ensure that Service Account Tokens are only mounted where necessary (Manual) [WARN] 5.1.7 Avoid use of system:masters group (Manual) [WARN] 5.1.8 Limit use of the Bind, Impersonate and Escalate permissions in the Kubernetes cluster (Manual) [INFO] 5.2 Pod Security Policies [WARN] 5.2.1 Minimize the admission of privileged containers (Automated) [WARN] 5.2.2 Minimize the admission of containers wishing to share the host process ID namespace (Automated) [WARN] 5.2.3 Minimize the admission of containers wishing to share the host IPC namespace (Automated) [WARN] 5.2.4 Minimize the admission of containers wishing to share the host network namespace (Automated) [WARN] 5.2.5 Minimize the admission of containers with allowPrivilegeEscalation (Automated) [WARN] 5.2.6 Minimize the admission of root containers (Automated) [WARN] 5.2.7 Minimize the admission of containers with the NET_RAW capability (Automated) [WARN] 5.2.8 Minimize the admission of containers with added capabilities (Automated) [WARN] 5.2.9 Minimize the admission of containers with capabilities assigned (Manual) [INFO] 5.3 Network Policies and CNI [WARN] 5.3.1 Ensure that the CNI in use supports Network Policies (Manual) [WARN] 5.3.2 Ensure that all Namespaces have Network Policies defined (Manual) [INFO] 5.4 Secrets Management [WARN] 5.4.1 Prefer using secrets as files over secrets as environment variables (Manual) [WARN] 5.4.2 Consider external secret storage (Manual) [INFO] 5.5 Extensible Admission Control [WARN] 5.5.1 Configure Image Provenance using ImagePolicyWebhook admission controller (Manual) [INFO] 5.7 General Policies [WARN] 5.7.1 Create administrative boundaries between resources using namespaces (Manual) [WARN] 5.7.2 Ensure that the seccomp profile is set to docker/default in your pod definitions (Manual) [WARN] 5.7.3 Apply Security Context to Your Pods and Containers (Manual) [WARN] 5.7.4 The default namespace should not be used (Manual) == Remediations policies == 5.1.1 Identify all clusterrolebindings to the cluster-admin role. Check if they are used and if they need this role or if they could use a role with fewer privileges. Where possible, first bind users to a lower privileged role and then remove the clusterrolebinding to the cluster-admin role : kubectl delete clusterrolebinding [name] 5.1.2 Where possible, remove get, list and watch access to secret objects in the cluster. 5.1.3 Where possible replace any use of wildcards in clusterroles and roles with specific objects or actions. 5.1.4 Where possible, remove create access to pod objects in the cluster. 5.1.5 Create explicit service accounts wherever a Kubernetes workload requires specific access to the Kubernetes API server. Modify the configuration of each default service account to include this value automountServiceAccountToken: false 5.1.6 Modify the definition of pods and service accounts which do not need to mount service account tokens to disable it. 5.1.7 Remove the system:masters group from all users in the cluster. 5.1.8 Where possible, remove the impersonate, bind and escalate rights from subjects. 5.2.1 Create a PSP as described in the Kubernetes documentation, ensuring that the .spec.privileged field is omitted or set to false. 5.2.2 Create a PSP as described in the Kubernetes documentation, ensuring that the .spec.hostPID field is omitted or set to false. 5.2.3 Create a PSP as described in the Kubernetes documentation, ensuring that the .spec.hostIPC field is omitted or set to false. 5.2.4 Create a PSP as described in the Kubernetes documentation, ensuring that the .spec.hostNetwork field is omitted or set to false. 5.2.5 Create a PSP as described in the Kubernetes documentation, ensuring that the .spec.allowPrivilegeEscalation field is omitted or set to false. 5.2.6 Create a PSP as described in the Kubernetes documentation, ensuring that the .spec.runAsUser.rule is set to either MustRunAsNonRoot or MustRunAs with the range of UIDs not including 0. 5.2.7 Create a PSP as described in the Kubernetes documentation, ensuring that the .spec.requiredDropCapabilities is set to include either NET_RAW or ALL. 5.2.8 Ensure that allowedCapabilities is not present in PSPs for the cluster unless it is set to an empty array. 5.2.9 Review the use of capabilites in applications running on your cluster. Where a namespace contains applications which do not require any Linux capabities to operate consider adding a PSP which forbids the admission of containers which do not drop all capabilities. 5.3.1 If the CNI plugin in use does not support network policies, consideration should be given to making use of a different plugin, or finding an alternate mechanism for restricting traffic in the Kubernetes cluster. 5.3.2 Follow the documentation and create NetworkPolicy objects as you need them. 5.4.1 if possible, rewrite application code to read secrets from mounted secret files, rather than from environment variables. 5.4.2 Refer to the secrets management options offered by your cloud provider or a third-party secrets management solution. 5.5.1 Follow the Kubernetes documentation and setup image provenance. 5.7.1 Follow the documentation and create namespaces for objects in your deployment as you need them. 5.7.2 Use security context to enable the docker/default seccomp profile in your pod definitions. An example is as below: securityContext: seccompProfile: type: RuntimeDefault 5.7.3 Follow the Kubernetes documentation and apply security contexts to your pods. For a suggested list of security contexts, you may refer to the CIS Security Benchmark for Docker Containers. 5.7.4 Ensure that namespaces are created to allow for appropriate segregation of Kubernetes resources and that all new resources are created in a specific namespace. == Summary policies == 0 checks PASS 0 checks FAIL 26 checks WARN 0 checks INFO == Summary total == 67 checks PASS 11 checks FAIL 45 checks WARN 0 checks INFO ================================================ FILE: integration/testdata/Expected_output_stig.data ================================================ [INFO] 1 Control Plane Components == Summary master == 0 checks PASS 0 checks FAIL 0 checks WARN 0 checks INFO [INFO] 2 Control Plane Configuration [INFO] 2.1 DISA Category Code I [FAIL] V-242390 The Kubernetes API server must have anonymous authentication disabled (Automated) [FAIL] V-242400 The Kubernetes API server must have Alpha APIs disabled (Automated) [INFO] 2.2 DISA Category Code II [WARN] V-242381 The Kubernetes Controller Manager must create unique service accounts for each work payload. (Manual) [WARN] V-242402 The Kubernetes API Server must have an audit log path set (Manual) [WARN] V-242403 Kubernetes API Server must generate audit records (Manual) [WARN] V-242461 Kubernetes API Server audit logs must be enabled. (Manual) [WARN] V-242462 The Kubernetes API Server must be set to audit log max size. (Manual) [WARN] V-242463 The Kubernetes API Server must be set to audit log maximum backup. (Manual) [WARN] V-242464 The Kubernetes API Server audit log retention must be set. (Manual) [WARN] V-242465 The Kubernetes API Server audit log path must be set. (Manual) [WARN] V-242443 Kubernetes must contain the latest updates as authorized by IAVMs, CTOs, DTMs, and STIGs. (Manual) == Remediations controlplane == V-242390 If using a Kubelet config file, edit $kubeletconf to set authentication: anonymous: enabled to false. If using executable arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter in KUBELET_SYSTEM_PODS_ARGS variable. --anonymous-auth=false Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service V-242400 Edit any manifest files or $kubeletconf that contain the feature-gates setting with AllAlpha set to "true". Set the flag to "false" or remove the "AllAlpha" setting completely. Restart the kubelet service if the kubelet config file if the kubelet config file is changed. V-242381 Create explicit service accounts wherever a Kubernetes workload requires specific access to the Kubernetes API server. Modify the configuration of each default service account to include this value automountServiceAccountToken: false V-242402 Enable control plane logging for API Server, Audit, Authenticator, Controller Manager, and Scheduler. Ref: https://docs.aws.amazon.com/eks/latest/userguide/control-plane-logs.html V-242403 Enable control plane logging for API Server, Audit, Authenticator, Controller Manager, and Scheduler. Ref: https://docs.aws.amazon.com/eks/latest/userguide/control-plane-logs.html V-242461 Enable control plane logging for API Server, Audit, Authenticator, Controller Manager, and Scheduler. Ref: https://docs.aws.amazon.com/eks/latest/userguide/control-plane-logs.html V-242462 Enable control plane logging for API Server, Audit, Authenticator, Controller Manager, and Scheduler. Ref: https://docs.aws.amazon.com/eks/latest/userguide/control-plane-logs.html V-242463 Enable control plane logging for API Server, Audit, Authenticator, Controller Manager, and Scheduler. Ref: https://docs.aws.amazon.com/eks/latest/userguide/control-plane-logs.html V-242464 Enable control plane logging for API Server, Audit, Authenticator, Controller Manager, and Scheduler. Ref: https://docs.aws.amazon.com/eks/latest/userguide/control-plane-logs.html V-242465 Enable control plane logging for API Server, Audit, Authenticator, Controller Manager, and Scheduler. Ref: https://docs.aws.amazon.com/eks/latest/userguide/control-plane-logs.html V-242443 Upgrade Kubernetes to a supported version. Ref: https://docs.aws.amazon.com/eks/latest/userguide/update-cluster.html == Summary controlplane == 0 checks PASS 2 checks FAIL 9 checks WARN 0 checks INFO [INFO] 3 Worker Node Security Configuration [INFO] 3.1 DISA Category Code I [WARN] V-242387 The Kubernetes Kubelet must have the read-only port flag disabled (Manual) [PASS] V-242391 The Kubernetes Kubelet must have anonymous authentication disabled (Automated) [PASS] V-242392 The Kubernetes kubelet must enable explicit authorization (Automated) [FAIL] V-242397 The Kubernetes kubelet static PodPath must not enable static pods (Automated) [WARN] V-242415 Secrets in Kubernetes must not be stored as environment variables.(Manual) [FAIL] V-242434 Kubernetes Kubelet must enable kernel protection (Automated) [PASS] V-242435 Kubernetes must prevent non-privileged users from executing privileged functions (Automated) [FAIL] V-242393 Kubernetes Worker Nodes must not have sshd service running. (Automated) [FAIL] V-242394 Kubernetes Worker Nodes must not have the sshd service enabled. (Automated) [WARN] V-242395 Kubernetes dashboard must not be enabled. (Manual) [PASS] V-242398 Kubernetes DynamicAuditing must not be enabled. (Automated) [PASS] V-242399 Kubernetes DynamicKubeletConfig must not be enabled. (Automated) [PASS] V-242404 Kubernetes Kubelet must deny hostname override (Automated) [PASS] V-242406 The Kubernetes kubelet configuration file must be owned by root (Automated) [PASS] V-242407 The Kubernetes kubelet configuration files must have file permissions set to 644 or more restrictive (Automated) [WARN] V-242414 The Kubernetes cluster must use non-privileged host ports for user pods. (Manual) [WARN] V-242442 Kubernetes must remove old components after updated versions have been installed. (Manual) [WARN] V-242396 Kubernetes Kubectl cp command must give expected access and results. (Manual) == Remediations node == V-242387 If using a Kubelet config file, edit /var/lib/kubelet/config.yaml to set readOnlyPort to 0. If using command line arguments, edit the kubelet service file /etc/systemd/system/kubelet.service.d/10-kubeadm.conf on each worker node and set the below parameter in KUBELET_SYSTEM_PODS_ARGS variable. --read-only-port=0 Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service V-242397 Edit /var/lib/kubelet/config.yaml on each node to to remove the staticPodPath Based on your system, restart the kubelet service. For example, systemctl daemon-reload systemctl restart kubelet.service V-242415 Run the following command: kubectl get all -o jsonpath='{range .items[?(@..secretKeyRef)]} {.kind} {.metadata.name} {"\n"}{end}' -A If any of the values returned reference environment variables rewrite application code to read secrets from mounted secret files, rather than from environment variables. V-242434 If using a Kubelet config file, edit /var/lib/kubelet/config.yaml to set protectKernelDefaults: true. If using command line arguments, edit the kubelet service file /etc/systemd/system/kubelet.service.d/10-kubeadm.conf on each worker node and set the below parameter in KUBELET_SYSTEM_PODS_ARGS variable. --protect-kernel-defaults=true Based on your system, restart the kubelet service. For example: systemctl daemon-reload systemctl restart kubelet.service V-242393 To stop the sshd service, run the command: systemctl stop sshd V-242394 To disable the sshd service, run the command: chkconfig sshd off V-242395 Run the command: kubectl get pods --all-namespaces -l k8s-app=kubernetes-dashboard If any resources are returned, this is a finding. Fix Text: Delete the Kubernetes dashboard deployment with the following command: kubectl delete deployment kubernetes-dashboard --namespace=kube-system V-242414 For any of the pods that are using ports below 1024, reconfigure the pod to use a service to map a host non-privileged port to the pod port or reconfigure the image to use non-privileged ports. V-242442 To view all pods and the images used to create the pods, from the Master node, run the following command: kubectl get pods --all-namespaces -o jsonpath="{..image}" | \ tr -s '[[:space:]]' '\n' | \ sort | \ uniq -c Review the images used for pods running within Kubernetes. Remove any old pods that are using older images. V-242396 If any Worker nodes are not using kubectl version 1.12.9 or newer, this is a finding. Upgrade the Master and Worker nodes to the latest version of kubectl. == Summary node == 8 checks PASS 4 checks FAIL 6 checks WARN 0 checks INFO [INFO] 4 Policies [INFO] 4.1 Policies - DISA Category Code I [WARN] V-242381 The Kubernetes Controller Manager must create unique service accounts for each work payload. (Manual) [WARN] V-242383 User-managed resources must be created in dedicated namespaces. (Manual) [WARN] V-242417 Kubernetes must separate user functionality. (Manual) == Remediations policies == V-242381 Create explicit service accounts wherever a Kubernetes workload requires specific access to the Kubernetes API server. Modify the configuration of each default service account to include this value automountServiceAccountToken: false V-242383 Move any user-managed resources from the default, kube-public and kube-node-lease namespaces, to user namespaces. V-242417 Move any user pods that are present in the Kubernetes system namespaces to user specific namespaces. == Summary policies == 0 checks PASS 0 checks FAIL 3 checks WARN 0 checks INFO [INFO] 5 Managed Services [INFO] 5.1 DISA Category Code I [INFO] V-242386 The Kubernetes API server must have the insecure port flag disabled | Component of EKS Control Plane [INFO] V-242388 The Kubernetes API server must have the insecure bind address not set | Component of EKS Control Plane [WARN] V-242436 The Kubernetes API server must have the ValidatingAdmissionWebhook enabled (manual) [INFO] V-245542 Kubernetes API Server must disable basic authentication to protect information in transit | Component of EKS Control Plane [INFO] 5.2 DISA Category Code II [INFO] V-242376 The Kubernetes Controller Manager must use TLS 1.2, at a minimum | Component of EKS Control Plane [INFO] V-242377 The Kubernetes Scheduler must use TLS 1.2, at a minimum | Component of EKS Control Plane [INFO] V-242378 The Kubernetes API Server must use TLS 1.2, at a minimum | Component of EKS Control Plane [INFO] V-242379 The Kubernetes etcd must use TLS to protect the confidentiality of sensitive data during electronic dissemination | Component of EKS Control Plane [INFO] V-242380 The Kubernetes etcd must use TLS to protect the confidentiality of sensitive data during electronic dissemination | Component of EKS Control Plane [INFO] V-242382 The Kubernetes API Server must enable Node,RBAC as the authorization mode | Component of EKS Control Plane [INFO] V-242384 The Kubernetes Scheduler must have secure binding | Component of EKS Control Plane [INFO] V-242385 The Kubernetes Controller Manager must have secure binding | Component of EKS Control Plane [INFO] V-242389 The Kubernetes API server must have the secure port set | Component of EKS Control Plane [INFO] V-242401 The Kubernetes API Server must have an audit policy set | Component of EKS Control Plane [INFO] V-242402 The Kubernetes API Server must have an audit log path set | Component of EKS Control Plane [INFO] V-242403 Kubernetes API Server must generate audit records | Component of EKS Control Plane [INFO] V-242405 The Kubernetes manifests must be owned by root | Component of EKS Control Plane [INFO] V-242408 The Kubernetes manifests must have least privileges | Component of EKS Control Plane [INFO] V-242409 Kubernetes Controller Manager must disable profiling | Component of EKS Control Plane [INFO] V-242410 The Kubernetes API Server must enforce PPS that adhere to PPSM CAL | Component of EKS Control Plane [INFO] V-242411 The Kubernetes Scheduler must enforce PPS that adhere to PPSM CAL | Component of EKS Control Plane [INFO] V-242412 The Kubernetes Controllers must enforce PPS that adhere to PPSM CAL | Component of EKS Control Plane [INFO] V-242413 The Kubernetes etcd must enforce PPS that adhere to PPSM CAL | Component of EKS Control Plane [INFO] V-242418 The Kubernetes API server must use approved cipher suites | Component of EKS Control Plane [INFO] V-242419 Kubernetes API Server must have the SSL Certificate Authority set | Component of EKS Control Plane [INFO] V-242420 Kubernetes Kubelet must have the SSL Certificate Authority set | Component of EKS Control Plane [INFO] V-242421 Kubernetes Controller Manager must have the SSL Certificate Authority set | Component of EKS Control Plane [INFO] V-242422 Kubernetes API Server must have a certificate for communication | Component of EKS Control Plane [INFO] V-242423 Kubernetes etcd must enable client authentication to secure service | Component of EKS Control Plane [INFO] V-242424 Kubernetes etcd must enable client authentication to secure service | Component of EKS Control Plane [INFO] V-242425 Kubernetes Kubelet must enable tls-cert-file for client authentication to secure service | Component of EKS Control Plane [INFO] V-242426 Kubernetes etcd must enable client authentication to secure service | Component of EKS Control Plane [INFO] V-242427 Kubernetes etcd must have a key file for secure communication | Component of EKS Control Plane [INFO] V-242428 Kubernetes etcd must have a certificate for communication | Component of EKS Control Plane [INFO] V-242429 Kubernetes etcd must have the SSL Certificate Authority set | Component of EKS Control Plane [INFO] V-242430 Kubernetes etcd must have a certificate for communication | Component of EKS Control Plane [INFO] V-242431 Kubernetes etcd must have a key file for secure communication | Component of EKS Control Plane [INFO] V-242432 Kubernetes etcd must have peer-cert-file set for secure communication | Component of EKS Control Plane [INFO] V-242433 Kubernetes etcd must have a peer-key-file set for secure communication | Component of EKS Control Plane [INFO] V-242438 Kubernetes API Server must configure timeouts to limit attack surface | Component of EKS Control Plane [INFO] V-242444 The Kubernetes component manifests must be owned by root | Component of EKS Control Plane [INFO] V-242445 The Kubernetes component etcd must be owned by etcd | Component of EKS Control Plane [INFO] V-242446 The Kubernetes conf files must be owned by root | Component of EKS Control Plane [INFO] V-242447 The Kubernetes Kube Proxy must have file permissions set to 644 or more restrictive | Component of EKS Control Plane [INFO] V-242448 The Kubernetes Kube Proxy must be owned by root | Component of EKS Control Plane [INFO] V-242449 The Kubernetes Kubelet certificate authority file must have file permissions set to 644 or more restrictive | Component of EKS Control Plane [INFO] V-242450 The Kubernetes Kubelet certificate authority must be owned by root | Component of EKS Control Plane [INFO] V-242451 The Kubernetes component PKI must be owned by root | Component of EKS Control Plane [INFO] V-242452 The Kubernetes kubelet config must have file permissions set to 644 or more restrictive | Component of EKS Control Plane [INFO] V-242453 The Kubernetes kubelet config must be owned by root | Component of EKS Control Plane [INFO] V-242454 The Kubernetes kubeadm.conf must be owned by root | Component of EKS Control Plane [INFO] V-242455 The Kubernetes kubeadm.conf must have file permissions set to 644 or more restrictive | Component of EKS Control Plane [INFO] V-242456 The Kubernetes kubelet config must have file permissions set to 644 or more restrictive | Component of EKS Control Plane [INFO] V-242457 The Kubernetes kubelet config must be owned by root | Component of EKS Control Plane [INFO] V-242458 The Kubernetes API Server must have file permissions set to 644 or more restrictive | Component of EKS Control Plane [INFO] V-242459 The Kubernetes etcd must have file permissions set to 644 or more restrictive | Component of EKS Control Plane [INFO] V-242460 The Kubernetes admin.conf must have file permissions set to 644 or more restrictive | Component of EKS Control Plane [INFO] V-242466 The Kubernetes PKI CRT must have file permissions set to 644 or more restrictive | Component of EKS Control Plane [INFO] V-242467 The Kubernetes PKI keys must have file permissions set to 600 or more restrictive | Component of EKS Control Plane [INFO] V-242468 The Kubernetes API Server must prohibit communication using TLS version 1.0 and 1.1, and SSL 2.0 and 3.0 | Component of EKS Control Plane [INFO] V-245541 Kubernetes Kubelet must not disable timeouts | Component of EKS Control Plane [INFO] V-245543 Kubernetes API Server must disable token authentication to protect information in transit | Component of EKS Control Plane [INFO] V-245544 Kubernetes endpoints must use approved organizational certificate and key pair to protect information in transit | Component of EKS Control Plane == Remediations managedservices == V-242436 Amazon EKS version 1.18 and later automatically enable ValidatingAdmissionWebhook Ref: https://docs.aws.amazon.com/eks/latest/userguide/platform-versions.html == Summary managedservices == 0 checks PASS 0 checks FAIL 1 checks WARN 62 checks INFO == Summary total == 8 checks PASS 6 checks FAIL 19 checks WARN 62 checks INFO ================================================ FILE: internal/findings/doc.go ================================================ /* Package findings handles sending findings to Security Hub. */ package findings ================================================ FILE: internal/findings/publisher.go ================================================ package findings import ( "context" "github.com/aws/aws-sdk-go-v2/service/securityhub" "github.com/aws/aws-sdk-go-v2/service/securityhub/types" "github.com/pkg/errors" ) // A Publisher represents an object that publishes finds to AWS Security Hub. type Publisher struct { client securityhub.Client // AWS Security Hub Service Client } // A PublisherOutput represents an object that contains information about the service call. type PublisherOutput struct { // The number of findings that failed to import. // // FailedCount is a required field FailedCount int32 // The list of findings that failed to import. FailedFindings []types.ImportFindingsError // The number of findings that were successfully imported. // // SuccessCount is a required field SuccessCount int32 } // New creates a new Publisher. func New(client securityhub.Client) *Publisher { return &Publisher{ client: client, } } // PublishFinding publishes findings to AWS Security Hub Service func (p *Publisher) PublishFinding(finding []types.AwsSecurityFinding) (*PublisherOutput, error) { o := PublisherOutput{} i := securityhub.BatchImportFindingsInput{} i.Findings = finding var errs error // Split the slice into batches of 100 finding. batch := 100 for i := 0; i < len(finding); i += batch { i := securityhub.BatchImportFindingsInput{} i.Findings = finding r, err := p.client.BatchImportFindings(context.Background(), &i) // Process the batch. if err != nil { errs = errors.Wrap(err, "finding publish failed") } if r != nil { if *r.FailedCount != 0 { o.FailedCount += *r.FailedCount } if *r.SuccessCount != 0 { o.SuccessCount += *r.SuccessCount } o.FailedFindings = append(o.FailedFindings, r.FailedFindings...) } } return &o, errs } ================================================ FILE: job-ack.yaml ================================================ --- apiVersion: batch/v1 kind: Job metadata: name: kube-bench spec: template: spec: hostPID: true containers: - name: kube-bench image: docker.io/aquasec/kube-bench:latest command: [ "kube-bench", "run", "--targets", "node,policies,managedservices", "--benchmark", "ack-1.0", ] volumeMounts: - name: var-lib-kubelet mountPath: /var/lib/kubelet readOnly: true - name: etc-systemd mountPath: /etc/systemd readOnly: true - name: etc-kubernetes mountPath: /etc/kubernetes readOnly: true restartPolicy: Never volumes: - name: var-lib-kubelet hostPath: path: "/var/lib/kubelet" - name: etc-systemd hostPath: path: "/etc/systemd" - name: etc-kubernetes hostPath: path: "/etc/kubernetes" ================================================ FILE: job-aks.yaml ================================================ --- apiVersion: batch/v1 kind: Job metadata: name: kube-bench spec: template: spec: hostPID: true containers: - name: kube-bench image: docker.io/aquasec/kube-bench:latest command: ["kube-bench", "run", "--targets", "node", "--benchmark", "aks-1.7"] volumeMounts: - name: var-lib-kubelet mountPath: /var/lib/kubelet readOnly: true - name: etc-systemd mountPath: /etc/systemd readOnly: true - name: etc-default mountPath: /etc/default readOnly: true - name: etc-kubernetes mountPath: /etc/kubernetes readOnly: true restartPolicy: Never volumes: - name: var-lib-kubelet hostPath: path: "/var/lib/kubelet" - name: etc-systemd hostPath: path: "/etc/systemd" - name: etc-default hostPath: path: "/etc/default" - name: etc-kubernetes hostPath: path: "/etc/kubernetes" ================================================ FILE: job-eks-asff.yaml ================================================ --- apiVersion: v1 kind: ServiceAccount metadata: name: kube-bench # If using a dedicated IAM role for kube-bench, uncomment the annotations # block below and replace the ROLE_ARN # annotations: # eks.amazonaws.com/role-arn: "" --- apiVersion: v1 kind: ConfigMap metadata: name: kube-bench-eks-config data: config.yaml: | AWS_ACCOUNT: "" AWS_REGION: "" CLUSTER_ARN: "" --- apiVersion: batch/v1 kind: Job metadata: name: kube-bench spec: template: spec: hostPID: true containers: - name: kube-bench # Push the image to your ECR and then refer to it here # image: image: docker.io/aquasec/kube-bench:latest command: [ "kube-bench", "run", "--targets", "node", "--benchmark", "eks-1.2.0", "--asff", ] env: - name: NODE_NAME valueFrom: fieldRef: fieldPath: spec.nodeName volumeMounts: - name: var-lib-kubelet mountPath: /var/lib/kubelet readOnly: true - name: etc-systemd mountPath: /etc/systemd readOnly: true - name: etc-kubernetes mountPath: /etc/kubernetes readOnly: true - name: kube-bench-eks-config mountPath: "/opt/kube-bench/cfg/eks-1.2.0/config.yaml" subPath: config.yaml readOnly: true restartPolicy: Never serviceAccountName: kube-bench volumes: - name: var-lib-kubelet hostPath: path: "/var/lib/kubelet" - name: etc-systemd hostPath: path: "/etc/systemd" - name: etc-kubernetes hostPath: path: "/etc/kubernetes" - name: kube-bench-eks-config configMap: name: kube-bench-eks-config items: - key: config.yaml path: config.yaml ================================================ FILE: job-eks-stig.yaml ================================================ --- apiVersion: batch/v1 kind: Job metadata: name: kube-bench spec: template: spec: hostPID: true containers: - name: kube-bench # Push the image to your ECR and then refer to it here # image: image: docker.io/aquasec/kube-bench:latest # To send findings to AWS Security Hub, refer to `job-eks-asff.yaml` instead command: [ "kube-bench", "run", "--benchmark", "eks-stig-kubernetes-v1r6", ] volumeMounts: - name: var-lib-kubelet mountPath: /var/lib/kubelet readOnly: true - name: etc-systemd mountPath: /etc/systemd readOnly: true - name: etc-kubernetes mountPath: /etc/kubernetes readOnly: true restartPolicy: Never volumes: - name: var-lib-kubelet hostPath: path: "/var/lib/kubelet" - name: etc-systemd hostPath: path: "/etc/systemd" - name: etc-kubernetes hostPath: path: "/etc/kubernetes" ================================================ FILE: job-eks.yaml ================================================ --- apiVersion: batch/v1 kind: Job metadata: name: kube-bench spec: template: spec: hostPID: true containers: - name: kube-bench # Push the image to your ECR and then refer to it here # image: image: docker.io/aquasec/kube-bench:latest # To send findings to AWS Security Hub, refer to `job-eks-asff.yaml` instead command: [ "kube-bench", "run", "--targets", "node,policies,managedservices,controlplane", "--benchmark", "eks-1.5.0", ] volumeMounts: - name: var-lib-kubelet mountPath: /var/lib/kubelet readOnly: true - name: etc-systemd mountPath: /etc/systemd readOnly: true - name: etc-kubernetes mountPath: /etc/kubernetes readOnly: true restartPolicy: Never volumes: - name: var-lib-kubelet hostPath: path: "/var/lib/kubelet" - name: etc-systemd hostPath: path: "/etc/systemd" - name: etc-kubernetes hostPath: path: "/etc/kubernetes" ================================================ FILE: job-gke.yaml ================================================ --- apiVersion: batch/v1 kind: Job metadata: name: kube-bench spec: template: spec: hostPID: true containers: - name: kube-bench image: docker.io/aquasec/kube-bench:latest command: [ "kube-bench", "run", "--targets", "node,policies,managedservices", "--benchmark", "gke-1.2.0", ] volumeMounts: - name: var-lib-kubelet mountPath: /var/lib/kubelet readOnly: true - name: etc-systemd mountPath: /etc/systemd readOnly: true - name: etc-kubernetes mountPath: /etc/kubernetes readOnly: true - name: home-kubernetes mountPath: /home/kubernetes readOnly: true restartPolicy: Never volumes: - name: var-lib-kubelet hostPath: path: "/var/lib/kubelet" - name: etc-systemd hostPath: path: "/etc/systemd" - name: etc-kubernetes hostPath: path: "/etc/kubernetes" - name: home-kubernetes hostPath: path: "/home/kubernetes" ================================================ FILE: job-iks.yaml ================================================ --- apiVersion: batch/v1 kind: Job metadata: name: kube-bench spec: template: spec: hostPID: true containers: - name: kube-bench image: docker.io/aquasec/kube-bench:latest command: ["kube-bench", "run", "--targets", "node", "--version", "1.20"] volumeMounts: - name: var-lib-kubelet mountPath: /var/lib/kubelet readOnly: true - name: etc-systemd mountPath: /etc/systemd readOnly: true - name: etc-kubernetes mountPath: /etc/kubernetes readOnly: true restartPolicy: Never volumes: - name: var-lib-kubelet hostPath: path: "/var/lib/kubelet" - name: etc-systemd hostPath: path: "/lib/systemd" - name: etc-kubernetes hostPath: path: "/etc/kubernetes" - name: usr-bin hostPath: path: "/usr/bin" ================================================ FILE: job-master.yaml ================================================ --- apiVersion: batch/v1 kind: Job metadata: name: kube-bench-master spec: template: spec: hostPID: true affinity: nodeAffinity: requiredDuringSchedulingIgnoredDuringExecution: nodeSelectorTerms: - matchExpressions: - key: node-role.kubernetes.io/control-plane operator: Exists - matchExpressions: - key: node-role.kubernetes.io/master operator: Exists tolerations: - key: node-role.kubernetes.io/master operator: Exists effect: NoSchedule - key: node-role.kubernetes.io/control-plane operator: Exists effect: NoSchedule containers: - name: kube-bench image: docker.io/aquasec/kube-bench:latest command: ["kube-bench", "run", "--targets", "master"] volumeMounts: - name: var-lib-cni mountPath: /var/lib/cni readOnly: true - name: var-lib-etcd mountPath: /var/lib/etcd readOnly: true - name: var-lib-kubelet mountPath: /var/lib/kubelet readOnly: true - name: var-lib-kube-scheduler mountPath: /var/lib/kube-scheduler readOnly: true - name: var-lib-kube-controller-manager mountPath: /var/lib/kube-controller-manager readOnly: true - name: etc-systemd mountPath: /etc/systemd readOnly: true - name: lib-systemd mountPath: /lib/systemd/ readOnly: true - name: srv-kubernetes mountPath: /srv/kubernetes/ readOnly: true - name: etc-kubernetes mountPath: /etc/kubernetes readOnly: true # /usr/local/mount-from-host/bin is mounted to access kubectl / kubelet, for auto-detecting the Kubernetes version. # You can omit this mount if you specify --version as part of the command. - name: usr-bin mountPath: /usr/local/mount-from-host/bin readOnly: true - name: etc-cni-netd mountPath: /etc/cni/net.d/ readOnly: true - name: opt-cni-bin mountPath: /opt/cni/bin/ readOnly: true - name: etc-passwd mountPath: /etc/passwd readOnly: true - name: etc-group mountPath: /etc/group readOnly: true restartPolicy: Never volumes: - name: var-lib-cni hostPath: path: "/var/lib/cni" - name: var-lib-etcd hostPath: path: "/var/lib/etcd" - name: var-lib-kubelet hostPath: path: "/var/lib/kubelet" - name: var-lib-kube-scheduler hostPath: path: "/var/lib/kube-scheduler" - name: var-lib-kube-controller-manager hostPath: path: "/var/lib/kube-controller-manager" - name: etc-systemd hostPath: path: "/etc/systemd" - name: lib-systemd hostPath: path: "/lib/systemd" - name: srv-kubernetes hostPath: path: "/srv/kubernetes" - name: etc-kubernetes hostPath: path: "/etc/kubernetes" - name: usr-bin hostPath: path: "/usr/bin" - name: etc-cni-netd hostPath: path: "/etc/cni/net.d/" - name: opt-cni-bin hostPath: path: "/opt/cni/bin/" - name: etc-passwd hostPath: path: "/etc/passwd" - name: etc-group hostPath: path: "/etc/group" ================================================ FILE: job-node.yaml ================================================ --- apiVersion: batch/v1 kind: Job metadata: name: kube-bench-node spec: template: spec: hostPID: true containers: - name: kube-bench image: docker.io/aquasec/kube-bench:latest command: ["kube-bench", "run", "--targets", "node"] volumeMounts: - name: var-lib-cni mountPath: /var/lib/cni readOnly: true - name: var-lib-etcd mountPath: /var/lib/etcd readOnly: true - name: var-lib-kubelet mountPath: /var/lib/kubelet readOnly: true - name: var-lib-kube-scheduler mountPath: /var/lib/kube-scheduler readOnly: true - name: var-lib-kube-controller-manager mountPath: /var/lib/kube-controller-manager readOnly: true - name: etc-systemd mountPath: /etc/systemd readOnly: true - name: lib-systemd mountPath: /lib/systemd/ readOnly: true - name: srv-kubernetes mountPath: /srv/kubernetes/ readOnly: true - name: etc-kubernetes mountPath: /etc/kubernetes readOnly: true # /usr/local/mount-from-host/bin is mounted to access kubectl / kubelet, for auto-detecting the Kubernetes version. # You can omit this mount if you specify --version as part of the command. - name: usr-bin mountPath: /usr/local/mount-from-host/bin readOnly: true - name: etc-cni-netd mountPath: /etc/cni/net.d/ readOnly: true - name: opt-cni-bin mountPath: /opt/cni/bin/ readOnly: true restartPolicy: Never volumes: - name: var-lib-cni hostPath: path: "/var/lib/cni" - name: var-lib-etcd hostPath: path: "/var/lib/etcd" - name: var-lib-kubelet hostPath: path: "/var/lib/kubelet" - name: var-lib-kube-scheduler hostPath: path: "/var/lib/kube-scheduler" - name: var-lib-kube-controller-manager hostPath: path: "/var/lib/kube-controller-manager" - name: etc-systemd hostPath: path: "/etc/systemd" - name: lib-systemd hostPath: path: "/lib/systemd" - name: srv-kubernetes hostPath: path: "/srv/kubernetes" - name: etc-kubernetes hostPath: path: "/etc/kubernetes" - name: usr-bin hostPath: path: "/usr/bin" - name: etc-cni-netd hostPath: path: "/etc/cni/net.d/" - name: opt-cni-bin hostPath: path: "/opt/cni/bin/" ================================================ FILE: job-tkgi.yaml ================================================ --- apiVersion: batch/v1 kind: Job metadata: name: kube-bench spec: template: spec: hostPID: true containers: - name: kube-bench image: docker.io/aquasec/kube-bench:latest command: [ "kube-bench", "run", "--targets", "node,policies", "--benchmark", "tkgi-1.2.53", ] volumeMounts: - name: var-vcap-jobs mountPath: /var/vcap/jobs readOnly: true - name: var-vcap-data-jobs mountPath: /var/vcap/data/jobs readOnly: true - name: var-vcap-packages mountPath: /var/vcap/packages readOnly: true - name: var-vcap-store-etcd mountPath: /var/vcap/store/etcd readOnly: true - name: var-vcap-sys mountPath: /var/vcap/sys readOnly: true - name: var-vcap-data-sys mountPath: /var/vcap/data/sys readOnly: true - name: etc-kubernetes mountPath: /etc/kubernetes readOnly: true restartPolicy: Never volumes: - name: var-vcap-jobs hostPath: path: "/var/vcap/jobs" - name: var-vcap-data-jobs hostPath: path: "/var/vcap/data/jobs" - name: var-vcap-packages hostPath: path: "/var/vcap/packages" - name: var-vcap-store-etcd hostPath: path: "/var/vcap/store/etcd" - name: var-vcap-sys hostPath: path: "/var/vcap/sys" - name: var-vcap-data-sys hostPath: path: "/var/vcap/data/sys" - name: etc-kubernetes hostPath: path: "/etc/kubernetes" ================================================ FILE: job.yaml ================================================ --- apiVersion: batch/v1 kind: Job metadata: name: kube-bench spec: template: metadata: labels: app: kube-bench spec: containers: - command: ["kube-bench"] image: docker.io/aquasec/kube-bench:v0.15.0 name: kube-bench volumeMounts: - name: var-lib-cni mountPath: /var/lib/cni readOnly: true - mountPath: /var/lib/etcd name: var-lib-etcd readOnly: true - mountPath: /var/lib/kubelet name: var-lib-kubelet readOnly: true - mountPath: /var/lib/kube-scheduler name: var-lib-kube-scheduler readOnly: true - mountPath: /var/lib/kube-controller-manager name: var-lib-kube-controller-manager readOnly: true - mountPath: /etc/systemd name: etc-systemd readOnly: true - mountPath: /lib/systemd/ name: lib-systemd readOnly: true - mountPath: /srv/kubernetes/ name: srv-kubernetes readOnly: true - mountPath: /etc/kubernetes name: etc-kubernetes readOnly: true - mountPath: /usr/local/mount-from-host/bin name: usr-bin readOnly: true - mountPath: /etc/cni/net.d/ name: etc-cni-netd readOnly: true - mountPath: /opt/cni/bin/ name: opt-cni-bin readOnly: true hostPID: true restartPolicy: Never volumes: - name: var-lib-cni hostPath: path: /var/lib/cni - hostPath: path: /var/lib/etcd name: var-lib-etcd - hostPath: path: /var/lib/kubelet name: var-lib-kubelet - hostPath: path: /var/lib/kube-scheduler name: var-lib-kube-scheduler - hostPath: path: /var/lib/kube-controller-manager name: var-lib-kube-controller-manager - hostPath: path: /etc/systemd name: etc-systemd - hostPath: path: /lib/systemd name: lib-systemd - hostPath: path: /srv/kubernetes name: srv-kubernetes - hostPath: path: /etc/kubernetes name: etc-kubernetes - hostPath: path: /usr/bin name: usr-bin - hostPath: path: /etc/cni/net.d/ name: etc-cni-netd - hostPath: path: /opt/cni/bin/ name: opt-cni-bin ================================================ FILE: main.go ================================================ // Copyright © 2017 Aqua Security Software Ltd. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package main import ( "github.com/aquasecurity/kube-bench/cmd" ) func main() { cmd.Execute() } ================================================ FILE: makefile ================================================ SOURCES := $(shell find . -name '*.go') BINARY := kube-bench DOCKER_ORG ?= aquasec VERSION ?= $(shell git rev-parse --short=7 HEAD) KUBEBENCH_VERSION ?= $(shell git describe --tags --abbrev=0) IMAGE_NAME ?= $(DOCKER_ORG)/$(BINARY):$(VERSION) IMAGE_NAME_UBI ?= $(DOCKER_ORG)/$(BINARY):$(VERSION)-ubi GOOS ?= linux BUILD_OS := linux uname := $(shell uname -s) BUILDX_PLATFORM ?= linux/amd64,linux/arm64,linux/arm,linux/ppc64le,linux/s390x DOCKER_ORGS ?= aquasec public.ecr.aws/aquasecurity GOARCH ?= $@ KUBECTL_VERSION ?= 1.36.0-alpha.1 ARCH ?= $(shell go env GOARCH) ifneq ($(findstring Microsoft,$(shell uname -r)),) BUILD_OS := windows else ifeq ($(uname),Linux) BUILD_OS := linux else ifeq ($(uname),Darwin) BUILD_OS := darwin endif # kind cluster name to use KIND_PROFILE ?= kube-bench KIND_CONTAINER_NAME=$(KIND_PROFILE)-control-plane KIND_IMAGE ?= kindest/node:v1.21.1@sha256:69860bda5563ac81e3c0057d654b5253219618a22ec3a346306239bba8cfa1a6 # build a multi-arch image and push to Docker hub .PHONY: docker docker: set -xe; \ for org in $(DOCKER_ORGS); do \ docker buildx build --tag $${org}/kube-bench:${VERSION} \ --platform $(BUILDX_PLATFORM) --push . ; \ done build: $(BINARY) $(BINARY): $(SOURCES) GOOS=$(GOOS) CGO_ENABLED=0 go build -ldflags "-X github.com/aquasecurity/kube-bench/cmd.KubeBenchVersion=$(KUBEBENCH_VERSION)" -o $(BINARY) . build-fips: GOOS=$(GOOS) CGO_ENABLED=0 GOEXPERIMENT=boringcrypto go build -tags fipsonly -ldflags "-X github.com/aquasecurity/kube-bench/cmd.KubeBenchVersion=$(KUBEBENCH_VERSION)" -o $(BINARY) . # builds the current dev docker version build-docker: docker build --build-arg BUILD_DATE=$(shell date -u +"%Y-%m-%dT%H:%M:%SZ") \ --build-arg VCS_REF=$(VERSION) \ --build-arg KUBEBENCH_VERSION=$(KUBEBENCH_VERSION) \ --build-arg KUBECTL_VERSION=$(KUBECTL_VERSION) \ --build-arg TARGETARCH=$(ARCH) \ -t $(IMAGE_NAME) . build-docker-ubi: docker build -f Dockerfile.ubi --build-arg BUILD_DATE=$(shell date -u +"%Y-%m-%dT%H:%M:%SZ") \ --build-arg VCS_REF=$(VERSION) \ --build-arg KUBEBENCH_VERSION=$(KUBEBENCH_VERSION) \ --build-arg KUBECTL_VERSION=$(KUBECTL_VERSION) \ --build-arg TARGETARCH=$(ARCH) \ -t $(IMAGE_NAME_UBI) . # unit tests tests: GO111MODULE=on go test -vet all -short -race -timeout 30s -coverprofile=coverage.txt -covermode=atomic ./... integration-test: kind-test-cluster kind-run # creates a kind cluster to be used for development. HAS_KIND := $(shell command -v kind;) kind-test-cluster: ifndef HAS_KIND go get -u sigs.k8s.io/kind endif @if [ -z $$(kind get clusters | grep $(KIND_PROFILE)) ]; then\ echo "Could not find $(KIND_PROFILE) cluster. Creating...";\ kind create cluster --name $(KIND_PROFILE) --image $(KIND_IMAGE) --wait 5m;\ fi # pushes the current dev version to the kind cluster. kind-push: build-docker kind load docker-image $(IMAGE_NAME) --name $(KIND_PROFILE) # runs the current version on kind using a job and follow logs kind-run: KUBECONFIG = "./kubeconfig.kube-bench" kind-run: kind-push sed "s/\$${VERSION}/$(VERSION)/" ./hack/kind.yaml > ./hack/kind.test.yaml kind get kubeconfig --name="$(KIND_PROFILE)" > $(KUBECONFIG) -KUBECONFIG=$(KUBECONFIG) \ kubectl delete job kube-bench KUBECONFIG=$(KUBECONFIG) \ kubectl apply -f ./hack/kind.test.yaml && \ kubectl wait --for=condition=complete job.batch/kube-bench --timeout=60s && \ kubectl logs job/kube-bench > ./test.data && \ diff ./test.data integration/testdata/Expected_output.data kind-run-stig: KUBECONFIG = "./kubeconfig.kube-bench" kind-run-stig: kind-push sed "s/\$${VERSION}/$(VERSION)/" ./hack/kind-stig.yaml > ./hack/kind-stig.test.yaml kind get kubeconfig --name="$(KIND_PROFILE)" > $(KUBECONFIG) -KUBECONFIG=$(KUBECONFIG) \ kubectl delete job kube-bench KUBECONFIG=$(KUBECONFIG) \ kubectl apply -f ./hack/kind-stig.test.yaml && \ kubectl wait --for=condition=complete job.batch/kube-bench --timeout=60s && \ kubectl logs job/kube-bench > ./test.data && \ diff ./test.data integration/testdata/Expected_output_stig.data ================================================ FILE: mkdocs.yml ================================================ --- site_name: Kube-bench site_url: https://aquasecurity.github.io/kube-bench/ site_description: Checks whether Kubernetes is deployed according to security best practices as defined in the CIS Kubernetes Benchmark docs_dir: docs/ repo_name: GitHub repo_url: https://github.com/aquasecurity/kube-bench/ edit_uri: "" nav: - Overview: index.md - Getting Started: - Installation: installation.md - Platforms: platforms.md - How to run: running.md - ASFF: asff.md - Flags: flags-and-commands.md - Configuration Options: - Understanding the yamls: controls.md - Architecture: architecture.md - Contributing: CONTRIBUTING.md markdown_extensions: - pymdownx.highlight - pymdownx.superfences - admonition extra: generator: false version: method: mike provider: mike theme: name: material language: 'en' logo: images/kube-bench-logo-only.png plugins: - search - macros