> ## Documentation Index
> Fetch the complete documentation index at: https://cloudanix.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# API Server Should Use Strong Cryptographic Ciphers

### More Info:

Verifies that --tls-cipher-suites is restricted to strong cipher suites so the API server does not negotiate weak or deprecated TLS ciphers.

### Risk Level

Medium

### Address

Security

### Compliance Standards

* CIS Kubernetes

### Triage and Remediation

<Tabs>
  <Tab title="Remediation">
    ### Remediation

    <AccordionGroup>
      <Accordion title="Manual Steps" defaultOpen="true">
        1. **Back up the existing manifest** (run on every control plane node):
           ```bash theme={null}
           sudo cp -a /etc/kubernetes/manifests/kube-apiserver.yaml /etc/kubernetes/manifests/kube-apiserver.yaml.bak.$(date +%F-%H%M%S)
           ```

        2. **Edit the API server manifest to set strong ciphers** (run on every control plane node):\
           Open the file with a text editor:
           ```bash theme={null}
           sudo vi /etc/kubernetes/manifests/kube-apiserver.yaml
           ```
           In the `command:` or `args:` list for `kube-apiserver`, add or replace the existing `--tls-cipher-suites` flag so it is exactly:
           ```yaml theme={null}
           - --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
           ```
           Save and exit.\
           **Operational impact:** because this is a static pod manifest under `/etc/kubernetes/manifests`, the kubelet will automatically restart the API server with the new configuration.

        3. **Confirm the API server pod has restarted** (run on any machine with `kubectl` access):
           ```bash theme={null}
           kubectl -n kube-system get pods -l component=kube-apiserver -o wide
           ```
           Ensure the `READY` status is `1/1` and the `AGE` reflects a recent restart compared to the time of your edit.

        4. **Verify the running process uses only the configured strong ciphers** (run on every control plane node):
           ```bash theme={null}
           /bin/ps -ef | grep kube-apiserver | grep -v grep | tr ' ' '\n' | grep -- '--tls-cipher-suites'
           ```
           Confirm the output exactly matches:
           ```text theme={null}
           --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
           ```
      </Accordion>

      <Accordion title="Using kubectl">
        kubectl cannot modify the API server’s host-level configuration or its static pod manifest at `/etc/kubernetes/manifests/kube-apiserver.yaml` on the control plane nodes. To remediate this finding, follow the guidance in the Manual Steps section to edit the manifest directly on each control plane node.
      </Accordion>

      <Accordion title="Automation">
        ```bash theme={null}
        #!/usr/bin/env bash
        #
        # Harden kube-apiserver TLS cipher suites on all control plane nodes.
        #
        # Usage:
        #   1) Create an inventory file with one control-plane node per line, e.g.:
        #        /root/controlplanes.txt
        #      Each line must be usable by ssh (hostname or IP).
        #   2) Run:
        #        bash harden_apiserver_ciphers.sh /root/controlplanes.txt
        #
        # Requirements:
        #   - Passwordless SSH (or ssh-agent) from this machine to each control-plane node.
        #   - Script must be run from a machine that can SSH into control-plane nodes.
        #   - Remote nodes must store kube-apiserver manifest at:
        #       /etc/kubernetes/manifests/kube-apiserver.yaml

        set -euo pipefail

        INVENTORY_FILE="${1:-}"
        if [[ -z "$INVENTORY_FILE" || ! -f "$INVENTORY_FILE" ]]; then
          echo "Usage: $0 /path/to/controlplane_inventory.txt" >&2
          exit 1
        fi

        # Desired cipher suite list (exactly as per benchmark remediation)
        DESIRED_CIPHERS="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"

        REMOTE_MANIFEST="/etc/kubernetes/manifests/kube-apiserver.yaml"
        BACKUP_DIR="/etc/kubernetes/manifests/backup-tls-ciphers"

        while IFS= read -r NODE || [[ -n "$NODE" ]]; do
          [[ -z "$NODE" ]] && continue
          echo "=== Processing control-plane node: $NODE ==="

          ssh "$NODE" "set -euo pipefail

            if [[ ! -f '$REMOTE_MANIFEST' ]]; then
              echo 'ERROR: $REMOTE_MANIFEST not found on node; skipping' >&2
              exit 1
            fi

            mkdir -p '$BACKUP_DIR'

            # Backup only once per unique content (idempotent-ish)
            TS=\$(date +%Y%m%d%H%M%S)
            SUM=\$(sha256sum '$REMOTE_MANIFEST' | awk '{print \$1}')
            BACKUP_FILE='$BACKUP_DIR'/kube-apiserver.yaml.\$TS.\$SUM
            if ! ls '$BACKUP_DIR'/kube-apiserver.yaml.*.\$SUM >/dev/null 2>&1; then
              cp '$REMOTE_MANIFEST' \"\$BACKUP_FILE\"
              echo 'Created backup: '\$BACKUP_FILE
            else
              echo 'Matching backup already exists for current manifest hash'
            fi

            # Work on a temporary file
            TMP_MANIFEST=\$(mktemp)
            cp '$REMOTE_MANIFEST' \"\$TMP_MANIFEST\"

            # Remove any existing --tls-cipher-suites entries (idempotent)
            sed -i '/--tls-cipher-suites=/d' \"\$TMP_MANIFEST\"

            # Insert the required flag under the kube-apiserver container args.
            # This assumes standard kubeadm-style static pod with YAML 'args:' list.
            # If an args list already exists, append; otherwise, create one.
            if grep -q '^- kube-apiserver' \"\$TMP_MANIFEST\"; then
              # Ensure args: key exists for the kube-apiserver container
              if ! awk '
                \$1 == \"-\" && \$2 == \"name:kube-apiserver\" {found=1}
                /args:/ && found==1 {print; exit 0}
              ' \"\$TMP_MANIFEST\" >/dev/null 2>&1; then
                # Try generic insertion of args: list under the kube-apiserver container
                awk '
                  /name: kube-apiserver/ && !added {
                    print
                    print \"    args:\"
                    added=1
                    next
                  }
                  {print}
                ' \"\$TMP_MANIFEST\" > \"\${TMP_MANIFEST}.new\" && mv \"\${TMP_MANIFEST}.new\" \"\$TMP_MANIFEST\"
              fi

              # Now append the cipher suites argument under args:
              # We add it only if not already present with the exact desired list.
              if ! grep -q \"--tls-cipher-suites=$DESIRED_CIPHERS\" \"\$TMP_MANIFEST\"; then
                awk -v ciphers=\"$DESIRED_CIPHERS\" '
                  /name: kube-apiserver/ {in_apiserver=1}
                  in_apiserver && /args:/ {
                    print
                    print \"    - --tls-cipher-suites=\" ciphers
                    in_apiserver=0
                    next
                  }
                  {print}
                ' \"\$TMP_MANIFEST\" > \"\${TMP_MANIFEST}.new\" && mv \"\${TMP_MANIFEST}.new\" \"\$TMP_MANIFEST\"
              fi
            fi

            # Move updated manifest into place (this restarts the kube-apiserver static pod)
            cp \"\$TMP_MANIFEST\" '$REMOTE_MANIFEST'
            rm -f \"\$TMP_MANIFEST\"

            echo 'Updated $REMOTE_MANIFEST with strong TLS cipher suites; kube-apiserver static pod will restart.'

            # Verification (adapted from audit command)
            # Allow some time for kube-apiserver to restart
            for i in \$(seq 1 30); do
              if /bin/ps -ef | grep kube-apiserver | grep -v grep >/dev/null 2>&1; then
                break
              fi
              sleep 2
            done

            echo 'Current kube-apiserver process and cipher suites flag:'
            /bin/ps -ef | grep kube-apiserver | grep -v grep | sed -e 's/--tls-cipher-suites/\\n  --tls-cipher-suites/g'

            if /bin/ps -ef | grep kube-apiserver | grep -v grep | grep -- '--tls-cipher-suites=$DESIRED_CIPHERS' >/dev/null 2>&1; then
              echo 'VERIFIED: kube-apiserver is running with the expected --tls-cipher-suites value.'
              exit 0
            else
              echo 'WARNING: kube-apiserver process does not show the exact expected --tls-cipher-suites value.' >&2
              exit 1
            fi
          " || {
            echo "Node $NODE: remediation or verification failed" >&2
          }

          echo
        done < "$INVENTORY_FILE"
        ```
      </Accordion>
    </AccordionGroup>
  </Tab>
</Tabs>
