> ## 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.

# Ensure Encryption Provider Config Argument Is Appropriate

### More Info:

Encrypt etcd key-value store.

### Risk Level

Low

### Address

Security

### Compliance Standards

* CIS Kubernetes

### Triage and Remediation

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

    <AccordionGroup>
      <Accordion title="Manual Steps" defaultOpen="true">
        1. **Create the EncryptionConfig file** (on every control plane node)
           ```bash theme={null}
           sudo mkdir -p /etc/kubernetes/encryption
           sudo chmod 700 /etc/kubernetes/encryption

           cat << 'EOF' | sudo tee /etc/kubernetes/encryption/encryption-config.yaml >/dev/null
           apiVersion: apiserver.config.k8s.io/v1
           kind: EncryptionConfiguration
           resources:
             - resources:
                 - secrets
                 - configmaps
                 - persistentvolumeclaims
               providers:
                 - aescbc:
                     keys:
                       - name: key1
                         secret: REPLACE_WITH_BASE64_32_BYTE_KEY
                 - identity: {}
           EOF
           sudo chmod 600 /etc/kubernetes/encryption/encryption-config.yaml
           ```

        2. **Generate a strong AES key and insert it into the config** (on every control plane node)
           ```bash theme={null}
           KEY=$(head -c 32 /dev/urandom | base64)
           sudo sed -i "s/REPLACE_WITH_BASE64_32_BYTE_KEY/${KEY}/" /etc/kubernetes/encryption/encryption-config.yaml
           ```

        3. **Edit the kube-apiserver static pod manifest to add the flag** (on every control plane node)\
           Open the manifest in an editor:
           ```bash theme={null}
           sudo vi /etc/kubernetes/manifests/kube-apiserver.yaml
           ```
           Under `spec.containers[0].command`, add this line as its own list item (or update it if present):
           ```yaml theme={null}
           - --encryption-provider-config=/etc/kubernetes/encryption/encryption-config.yaml
           ```
           Save and exit. Editing this file will automatically restart the kube-apiserver container.

        4. **(If needed) Mount the directory into the kube-apiserver pod** (on every control plane node)\
           In the same `/etc/kubernetes/manifests/kube-apiserver.yaml`, ensure you have:\
           Under `spec.volumes`:
           ```yaml theme={null}
           - name: encryption-config
             hostPath:
               path: /etc/kubernetes/encryption
               type: DirectoryOrCreate
           ```
           Under `spec.containers[0].volumeMounts`:
           ```yaml theme={null}
           - name: encryption-config
             mountPath: /etc/kubernetes/encryption
             readOnly: true
           ```
           Save the file; kubelet will restart the kube-apiserver again if modified.

        5. **Optionally re-encrypt existing stored data** (on any machine with kubectl access)\
           This requires a deliberate operational decision (downtime risk, backup/restore planning). To prepare and review impact:
           ```bash theme={null}
           kubectl get secrets --all-namespaces
           kubectl get configmaps --all-namespaces
           kubectl get pvc --all-namespaces
           ```
           Plan a maintenance window and follow the official documentation (`kube-apiserver` `--encryption-provider-config-automatic-reload` / `kube-apiserver` re-encryption procedures) before forcing re-encryption of existing resources.

        6. **Verify the kube-apiserver is running with the encryption-provider-config flag** (on every control plane node)
           ```bash theme={null}
           /bin/ps -ef | grep kube-apiserver | grep -v grep | grep -- '--encryption-provider-config=/etc/kubernetes/encryption/encryption-config.yaml'
           ```
           The command should return the kube-apiserver process line containing the `--encryption-provider-config` argument with the expected path.
      </Accordion>

      <Accordion title="Using kubectl">
        kubectl cannot modify the kube-apiserver static pod manifest or its `--encryption-provider-config` flag; this setting is controlled directly on each control plane node in `/etc/kubernetes/manifests/kube-apiserver.yaml`. To remediate this finding, follow the guidance in the Manual Steps section on those nodes.
      </Accordion>

      <Accordion title="Automation">
        ```bash theme={null}
        #!/usr/bin/env bash
        #
        # Automation: Configure --encryption-provider-config on kube-apiserver
        #
        # Run on: every control plane node (as root)
        # Usage: sudo ./configure-apiserver-encryption.sh
        #
        # This script:
        #   - Creates an EncryptionConfig file if missing (simple aes-gcm key)
        #   - Binds it into the kube-apiserver static pod if not already present
        #   - Adds/updates --encryption-provider-config flag
        #   - Is idempotent and safe to re-run
        #   - Verifies kube-apiserver is running with the flag set
        #
        # NOTE: Editing /etc/kubernetes/manifests/kube-apiserver.yaml will restart the
        #       kube-apiserver static pod automatically via kubelet.

        set -euo pipefail

        APISERVER_MANIFEST="/etc/kubernetes/manifests/kube-apiserver.yaml"
        ENCRYPTION_CONFIG_PATH="/etc/kubernetes/pki/encryption-config.yaml"
        BACKUP_SUFFIX="$(date +%Y%m%d%H%M%S)"

        # ---------- helper functions ----------

        backup_file() {
          local file="$1"

          if [ ! -f "$file" ]; then
            echo "ERROR: Required file not found: $file" >&2
            exit 1
          fi

          # Only back up once per run; keep original if already backed up earlier
          local backup="${file}.bak.${BACKUP_SUFFIX}"
          cp -p "$file" "$backup"
          echo "Backup created: $backup"
        }

        ensure_dir() {
          local dir="$1"
          if [ ! -d "$dir" ]; then
            mkdir -p "$dir"
            chmod 0700 "$dir"
          fi
        }

        # Generate a random 32-byte base64 key (deterministic per run, but safe)
        generate_encryption_key() {
          openssl rand -base64 32
        }

        # ---------- 1. Create EncryptionConfig file if missing ----------

        create_encryption_config_if_missing() {
          if [ -f "$ENCRYPTION_CONFIG_PATH" ]; then
            echo "EncryptionConfig already exists at $ENCRYPTION_CONFIG_PATH; leaving as is."
            return 0
          fi

          echo "Creating new EncryptionConfig at $ENCRYPTION_CONFIG_PATH"

          ensure_dir "$(dirname "$ENCRYPTION_CONFIG_PATH")"

          local key
          key="$(generate_encryption_key)"

          cat > "$ENCRYPTION_CONFIG_PATH" <<EOF
        apiVersion: apiserver.config.k8s.io/v1
        kind: EncryptionConfiguration
        resources:
          - resources:
              - secrets
              - configmaps
            providers:
              - aescbc:
                  keys:
                    - name: key1
                      secret: ${key}
              - identity: {}
        EOF

          chmod 0600 "$ENCRYPTION_CONFIG_PATH"
          echo "EncryptionConfig created with aes-cbc provider for secrets and configmaps."
        }

        # ---------- 2. Ensure volume & volumeMount for EncryptionConfig ----------

        ensure_apiserver_volume_and_mount() {
          local manifest="$APISERVER_MANIFEST"
          local vol_name="encryption-config"
          local vol_mount_path
          vol_mount_path="$(dirname "$ENCRYPTION_CONFIG_PATH")"

          # Ensure volume definition exists
          if grep -q "name: ${vol_name}" "$manifest"; then
            echo "Volume '${vol_name}' already defined in kube-apiserver manifest."
          else
            echo "Adding volume '${vol_name}' to kube-apiserver manifest."
            backup_file "$manifest"

            # Insert under 'volumes:'; if not found, append at end
            if grep -qE '^[[:space:]]*volumes:' "$manifest"; then
              # Append a new volume entry after the 'volumes:' line
              awk -v VNAME="$vol_name" -v VPATH="$vol_mount_path" '
                BEGIN {added=0}
                /^[[:space:]]*volumes:/ {
                  print
                  print "    - name: " VNAME
                  print "      hostPath:"
                  print "        path: " VPATH
                  print "        type: DirectoryOrCreate"
                  added=1
                  next
                }
                {print}
                END {
                  if (added == 0) {
                    # If volumes: section not matched above (edge case)
                    print "volumes:"
                    print "  - name: " VNAME
                    print "    hostPath:"
                    print "      path: " VPATH
                    print "      type: DirectoryOrCreate"
                  }
                }
              ' "$manifest" > "${manifest}.tmp" && mv "${manifest}.tmp" "$manifest"
            else
              # No volumes: section at all; append a minimal one
              {
                cat "$manifest"
                echo ""
                echo "volumes:"
                echo "  - name: ${vol_name}"
                echo "    hostPath:"
                echo "      path: ${vol_mount_path}"
                echo "      type: DirectoryOrCreate"
              } > "${manifest}.tmp"
              mv "${manifest}.tmp" "$manifest"
            fi
          fi

          # Ensure volumeMount exists in container spec
          if grep -q "mountPath: ${vol_mount_path}" "$manifest"; then
            echo "VolumeMount for '${vol_name}' at '${vol_mount_path}' already present."
          else
            echo "Adding volumeMount for '${vol_name}' to kube-apiserver container."
            backup_file "$manifest"

            # Insert under 'volumeMounts:' for the kube-apiserver container
            awk -v VNAME="$vol_name" -v VPATH="$vol_mount_path" '
              BEGIN {inContainer=0; inVolumeMounts=0; added=0}
              /name: kube-apiserver/ && $1 ~ /name:/ {
                print
                inContainer=1
                next
              }
              inContainer == 1 && /^[[:space:]]*volumeMounts:/ {
                print
                print "        - mountPath: " VPATH
                print "          name: " VNAME
                print "          readOnly: true"
                inVolumeMounts=1
                added=1
                next
              }
              inContainer == 1 && inVolumeMounts == 0 && /^[[:space:]]*- command:/ {
                # No volumeMounts section; insert one before command
                print "      volumeMounts:"
                print "        - mountPath: " VPATH
                print "          name: " VNAME
                print "          readOnly: true"
                inVolumeMounts=1
                added=1
                print
                next
              }
              inContainer == 1 && /^[[:space:]]*image:/ {
                # End of container header without volumeMounts; add before image
                if (added == 0) {
                  print "      volumeMounts:"
                  print "        - mountPath: " VPATH
                  print "          name: " VNAME
                  print "          readOnly: true"
                  added=1
                }
                print
                next
              }
              /^[[:space:]]*-/ && !/name: kube-apiserver/ && inContainer==1 {
                # Start of another container; stop container context
                inContainer=0
              }
              {print}
            ' "$manifest" > "${manifest}.tmp" && mv "${manifest}.tmp" "$manifest"
          fi
        }

        # ---------- 3. Ensure --encryption-provider-config flag ----------

        ensure_encryption_flag() {
          local manifest="$APISERVER_MANIFEST"
          local flag="--encryption-provider-config=${ENCRYPTION_CONFIG_PATH}"

          if grep -q -- "$flag" "$manifest"; then
            echo "kube-apiserver manifest already contains ${flag}"
            return 0
          fi

          echo "Adding/updating ${flag} in kube-apiserver manifest."
          backup_file "$manifest"

          # If an old encryption-provider-config is present, replace it
          if grep -q -- "--encryption-provider-config=" "$manifest"; then
            sed -E "s#--encryption-provider-config=[^[:space:]]*#${flag}#g" \
              "$manifest" > "${manifest}.tmp"
            mv "${manifest}.tmp" "$manifest"
            return 0
          fi

          # Otherwise, add it into the existing command list
          awk -v FLAG="$flag" '
            BEGIN {inContainer=0; inCommand=0; added=0}
            /name: kube-apiserver/ && $1 ~ /name:/ {
              print
              inContainer=1
              next
            }
            inContainer == 1 && /^[[:space:]]*command:/ {
              print
              inCommand=1
              next
            }
            inContainer == 1 && inCommand == 1 && /^[[:space:]]*-/ {
              # Append flag after the last existing command entry
              lastLine=$0
              # Look ahead: if next non-empty non-comment line is still a command item, just print
              # We handle after the loop by appending FLAG as a new item if not added.
              print
              next
            }
            inContainer == 1 && inCommand == 1 && !/^[[:space:]]*-/ {
              # command list ended; insert FLAG before this line
              if (added == 0) {
                print "    - " FLAG
                added=1
              }
              inCommand=0
              print
              next
            }
            /^[[:space:]]*-/ && !/name: kube-apiserver/ && inContainer==1 {
              inContainer=0
            }
            {print}
            END {
              if (inCommand == 1 && added == 0) {
                # command block hit EOF; append FLAG as a new item
                print "    - " FLAG
              }
            }
          ' "$manifest" > "${manifest}.tmp" && mv "${manifest}.tmp" "$manifest"
        }

        # ---------- 4. Main ----------

        if [ "$(id -u)" -ne 0 ]; then
          echo "ERROR: This script must be run as root." >&2
          exit 1
        fi

        if [ ! -f "$APISERVER_MANIFEST" ]; then
          echo "ERROR: kube-apiserver manifest not found at $APISERVER_MANIFEST" >&2
          exit 1
        fi

        create_encryption_config_if_missing
        ensure_apiserver_volume_and_mount
        ensure_encryption_flag

        echo "Changes applied. kubelet will automatically restart kube-apiserver static pod if manifest changed."
        echo "Waiting 30 seconds for kube-apiserver to restart (if needed)..."
        sleep 30

        # ---------- 5. Verification ----------

        echo "Verification: checking kube-apiserver process for --encryption-provider-config flag"
        if /bin/ps -ef | grep kube-apiserver | grep -v grep | grep -q -- "--encryption-provider-config=${ENCRYPTION_CONFIG_PATH}"; then
          echo "SUCCESS: kube-apiserver is running with --encryption-provider-config=${ENCRYPTION_CONFIG_PATH}"
          exit 0
        else
          echo "WARNING: kube-apiserver process does not yet show --encryption-provider-config=${ENCRYPTION_CONFIG_PATH}" >&2
          echo "Run the following manually to inspect the current process:"
          echo "  /bin/ps -ef | grep kube-apiserver | grep -v grep"
          exit 1
        fi
        ```
      </Accordion>
    </AccordionGroup>
  </Tab>
</Tabs>

### Additional Reading:

* [https://github.com/kubernetes/features/issues/92](https://github.com/kubernetes/features/issues/92)
