> ## 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 Admission Control Plugin AlwaysPullImages Is Set

### More Info:

Always pull images.

### 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. **Open the kube-apiserver static pod manifest for editing**\
           On **every control plane node**:
           ```bash theme={null}
           sudo vi /etc/kubernetes/manifests/kube-apiserver.yaml
           ```

        2. **Locate the `--enable-admission-plugins` argument**\
           In the `spec.containers[0].command` list, find the line starting with:
           ```yaml theme={null}
           - --enable-admission-plugins=
           ```

        3. **Ensure `AlwaysPullImages` is included in the plugins list**\
           Edit that line so it includes `AlwaysPullImages` somewhere in the comma-separated list, for example:
           ```yaml theme={null}
           - --enable-admission-plugins=NamespaceLifecycle,NodeRestriction,AlwaysPullImages
           ```
           If the argument is not present at all, add a new line in the `command` list:
           ```yaml theme={null}
           - --enable-admission-plugins=AlwaysPullImages
           ```

        4. **Save the file and allow kubelet to restart the API server**\
           Save and exit the editor. The kubelet will automatically detect the manifest change and restart the `kube-apiserver` pod. Be aware this briefly restarts the API server on this node.

        5. **Verify the kube-apiserver process includes `AlwaysPullImages`**\
           After 10–30 seconds, on **the same control plane node**, run:
           ```bash theme={null}
           /bin/ps -ef | grep kube-apiserver | grep -v grep
           ```
           Confirm the output contains an `--enable-admission-plugins=` argument whose value includes `AlwaysPullImages`.
      </Accordion>

      <Accordion title="Using kubectl">
        kubectl cannot be used to enable the `AlwaysPullImages` admission plugin because this setting is defined in the kube-apiserver static pod manifest on the host. To remediate this finding, edit `/etc/kubernetes/manifests/kube-apiserver.yaml` directly on every control plane node as described in the Manual Steps section.
      </Accordion>

      <Accordion title="Automation">
        ```bash theme={null}
        #!/usr/bin/env bash
        #
        # Automation: Ensure kube-apiserver has AlwaysPullImages enabled
        # Scope: every control plane node (run on each control plane node via SSH or automation)
        #
        # This script:
        #  1. Backs up /etc/kubernetes/manifests/kube-apiserver.yaml
        #  2. Ensures --enable-admission-plugins includes AlwaysPullImages
        #  3. Leaves other flags unchanged
        #  4. Verifies the running kube-apiserver process includes AlwaysPullImages
        #
        # NOTE: Editing the static pod manifest under /etc/kubernetes/manifests
        #       will cause the kube-apiserver pod to be restarted automatically by kubelet.

        set -euo pipefail

        MANIFEST="/etc/kubernetes/manifests/kube-apiserver.yaml"
        BACKUP_DIR="/etc/kubernetes/manifests/backup-$(date +%Y%m%d)"
        BACKUP_FILE="${BACKUP_DIR}/kube-apiserver.yaml.$(date +%H%M%S)"

        require_root() {
          if [ "$(id -u)" -ne 0 ]; then
            echo "ERROR: This script must be run as root on each control plane node." >&2
            exit 1
          fi
        }

        backup_manifest() {
          if [ ! -f "$MANIFEST" ]; then
            echo "ERROR: Manifest $MANIFEST not found. Is this a static-pod control plane?" >&2
            exit 1
          fi

          mkdir -p "$BACKUP_DIR"
          cp -p "$MANIFEST" "$BACKUP_FILE"
          echo "Backup created at: $BACKUP_FILE"
        }

        ensure_always_pull_images() {
          # If flag already includes AlwaysPullImages, do nothing
          if grep -q -- '--enable-admission-plugins=.*AlwaysPullImages' "$MANIFEST"; then
            echo "--enable-admission-plugins already includes AlwaysPullImages; no change needed."
            return 0
          fi

          # Case 1: existing --enable-admission-plugins flag without AlwaysPullImages
          if grep -q -- '--enable-admission-plugins=' "$MANIFEST"; then
            # Append AlwaysPullImages to the list, handling trailing commas
            # Example: --enable-admission-plugins=NamespaceLifecycle,NodeRestriction
            # becomes: --enable-admission-plugins=NamespaceLifecycle,NodeRestriction,AlwaysPullImages
            sed -i \
              -e 's/\(--enable-admission-plugins=[^",]*\)\(".*\|\s*$\)/\1,AlwaysPullImages\2/' \
              "$MANIFEST"

            # Fallback in case the above pattern misses quoted args, try a more generic append
            if ! grep -q -- '--enable-admission-plugins=.*AlwaysPullImages' "$MANIFEST"; then
              sed -i \
                -e 's/\(--enable-admission-plugins=[^ ]*\)/\1,AlwaysPullImages/' \
                "$MANIFEST"
            fi

            echo "Updated existing --enable-admission-plugins to include AlwaysPullImages."
            return 0
          fi

          # Case 2: no --enable-admission-plugins flag present; add a new one
          # Insert into the container command args list.
          # This is conservative: it looks for the first occurrence of "kube-apiserver"
          # command line and adds a new line after it.
          if grep -q 'kube-apiserver' "$MANIFEST"; then
            # Try to detect YAML style: arguments as separate list items (common in kubeadm)
            if grep -q '^- --advertise-address' "$MANIFEST"; then
              # Insert a new list item line after the kube-apiserver command or near other flags
              # Here we append near the top of the flag list.
              sed -i \
                -e '/^- --advertise-address/a\  - --enable-admission-plugins=AlwaysPullImages' \
                "$MANIFEST" || true
            else
              # Generic: append a new arg line once under the kube-apiserver container args.
              # This may need manual review if manifest is heavily customized.
              awk '
                /kube-apiserver/ && in_container == 0 { in_container=1; print; next }
                in_container == 1 && $1 ~ /^-$/ {
                  print "    - --enable-admission-plugins=AlwaysPullImages"
                  in_container=2
                }
                { print }
              ' "$MANIFEST" > "${MANIFEST}.tmp" && mv "${MANIFEST}.tmp" "$MANIFEST"
            fi

            echo "Added new --enable-admission-plugins=AlwaysPullImages flag."
          else
            echo "WARNING: Could not locate kube-apiserver command in manifest; manual review required." >&2
          fi
        }

        wait_for_apiserver_restart() {
          echo "Waiting for kube-apiserver process to pick up new configuration..."
          # Wait up to 120 seconds for process args to reflect AlwaysPullImages
          local timeout=120
          local interval=5
          local elapsed=0

          while [ "$elapsed" -lt "$timeout" ]; do
            if /bin/ps -ef | grep kube-apiserver | grep -v grep | grep -q 'enable-admission-plugins=.*AlwaysPullImages'; then
              echo "kube-apiserver process now includes AlwaysPullImages."
              return 0
            fi
            sleep "$interval"
            elapsed=$((elapsed + interval))
          done

          echo "WARNING: Timed out waiting for kube-apiserver to restart with AlwaysPullImages. Check pod and kubelet status." >&2
          return 1
        }

        verify() {
          echo "Verification: checking running kube-apiserver process..."
          if /bin/ps -ef | grep kube-apiserver | grep -v grep | grep -q 'enable-admission-plugins=.*AlwaysPullImages'; then
            echo "PASS: AlwaysPullImages is enabled on kube-apiserver."
            return 0
          else
            echo "FAIL: AlwaysPullImages not found in kube-apiserver arguments." >&2
            echo "Run: /bin/ps -ef | grep kube-apiserver | grep -v grep" >&2
            return 1
          fi
        }

        main() {
          require_root
          backup_manifest
          ensure_always_pull_images
          # kubelet will notice the manifest change and restart the static pod automatically.
          wait_for_apiserver_restart || true
          verify
        }

        main "$@"
        ```
      </Accordion>
    </AccordionGroup>
  </Tab>
</Tabs>

### Additional Reading:

* [https://kubernetes.io/docs/admin/kube-apiserver/](https://kubernetes.io/docs/admin/kube-apiserver/)
