> ## 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 DenyServiceExternalIPs Is Not Set

### More Info:

This admission controller rejects all net-new usage of the Service field externalIPs

### 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. On every control plane node, back up the existing manifest before editing:
           ```bash theme={null}
           sudo cp -a /etc/kubernetes/manifests/kube-apiserver.yaml /etc/kubernetes/manifests/kube-apiserver.yaml.bak
           ```

        2. On every control plane node, open the API server static pod manifest for editing:
           ```bash theme={null}
           sudo vi /etc/kubernetes/manifests/kube-apiserver.yaml
           ```

        3. In the `spec.containers[0].command` section, locate the `--enable-admission-plugins` flag. Remove `DenyServiceExternalIPs` from the comma-separated list (leave the rest of the plugins unchanged), then save and exit.\
           Example before:
           ```yaml theme={null}
           - --enable-admission-plugins=NamespaceLifecycle,LimitRanger,DenyServiceExternalIPs,ServiceAccount
           ```
           Example after:
           ```yaml theme={null}
           - --enable-admission-plugins=NamespaceLifecycle,LimitRanger,ServiceAccount
           ```
           Note: Saving this file will cause the kubelet to restart the `kube-apiserver` static pod.

        4. If `DenyServiceExternalIPs` is present as its own flag (older/custom configurations), remove that line entirely:
           ```yaml theme={null}
           # Remove a line such as:
           - --DenyServiceExternalIPs
           ```

        5. Wait for the kube-apiserver pod to be recreated and become Ready (run on any machine with kubectl access):
           ```bash theme={null}
           kubectl get pods -n kube-system -l component=kube-apiserver -o wide
           ```

        6. Verify on every control plane node that the `kube-apiserver` process is now running without `DenyServiceExternalIPs`:
           ```bash theme={null}
           /bin/ps -ef | grep kube-apiserver | grep -v grep | grep -i DenyServiceExternalIPs || echo "OK: DenyServiceExternalIPs not set"
           ```
      </Accordion>

      <Accordion title="Using kubectl">
        kubectl cannot change the `DenyServiceExternalIPs` admission plugin because it is configured via the kube-apiserver static pod manifest on each control plane node (`/etc/kubernetes/manifests/kube-apiserver.yaml`). To remediate this finding, make the change directly on the host as described in the Manual Steps section.
      </Accordion>

      <Accordion title="Automation">
        ```bash theme={null}
        #!/usr/bin/env bash
        #
        # Automation: Remove DenyServiceExternalIPs admission plugin from kube-apiserver
        # Scope: Run on every control plane node (with access to /etc/kubernetes/manifests)
        # Usage: sudo ./fix_deny_service_external_ips.sh

        set -euo pipefail

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

        echo "[INFO] Checking for kube-apiserver manifest at ${MANIFEST}"
        if [[ ! -f "${MANIFEST}" ]]; then
          echo "[ERROR] kube-apiserver manifest not found at ${MANIFEST}"
          exit 1
        fi

        echo "[INFO] Creating backup directory ${BACKUP_DIR}"
        mkdir -p "${BACKUP_DIR}"
        cp -p "${MANIFEST}" "${BACKUP_DIR}/"

        # Function to normalize and remove DenyServiceExternalIPs from a comma-separated list
        remove_plugin_from_csv() {
          local csv="$1"
          local plugin="DenyServiceExternalIPs"

          # Split, trim, and rebuild without the target plugin
          awk -v csv="${csv}" -v plugin="${plugin}" '
            BEGIN {
              n = split(csv, arr, ",");
              out = "";
              for (i = 1; i <= n; i++) {
                gsub(/^[[:space:]]+|[[:space:]]+$/, "", arr[i]);
                if (arr[i] == "" || arr[i] == plugin) continue;
                if (out != "") out = out "," arr[i];
                else out = arr[i];
              }
              print out;
            }'
        }

        TMP_MANIFEST="$(mktemp)"
        cp "${MANIFEST}" "${TMP_MANIFEST}"

        echo "[INFO] Processing admission plugin flags in ${MANIFEST}"

        # Handle --enable-admission-plugins (legacy flag)
        if grep -q -- "--enable-admission-plugins=" "${TMP_MANIFEST}"; then
          echo "[INFO] Found --enable-admission-plugins flag; updating if necessary"
          current_csv=$(sed -n 's/.*--enable-admission-plugins=\([^" ]*\).*/\1/p' "${TMP_MANIFEST}" | head -n1 || true)
          if [[ -n "${current_csv}" ]]; then
            new_csv=$(remove_plugin_from_csv "${current_csv}")
            if [[ "${new_csv}" != "${current_csv}" ]]; then
              sed -i "s#--enable-admission-plugins=${current_csv}#--enable-admission-plugins=${new_csv}#g" "${TMP_MANIFEST}"
              echo "[INFO] Removed DenyServiceExternalIPs from --enable-admission-plugins"
            else
              echo "[INFO] DenyServiceExternalIPs not present in --enable-admission-plugins"
            fi
          fi
        else
          echo "[INFO] --enable-admission-plugins flag not present (nothing to change there)"
        fi

        # Handle --admission-control-config-file (if used), which may include DenyServiceExternalIPs by name
        # NOTE: This script DOES NOT edit that file, only warns if present.
        if grep -q -- "--admission-control-config-file=" "${TMP_MANIFEST}"; then
          echo "[WARN] --admission-control-config-file is in use."
          echo "[WARN] If DenyServiceExternalIPs is configured inside that file, review and remove it manually."
        fi

        # Detect any direct use of DenyServiceExternalIPs in args (defensive)
        if grep -q "DenyServiceExternalIPs" "${TMP_MANIFEST}"; then
          echo "[INFO] DenyServiceExternalIPs still referenced directly; ensuring it is not part of any simple CSV flags"

          # Generic clean-up attempt for any argument like --something=...,DenyServiceExternalIPs,...
          # This remains idempotent and only removes the named plugin where it is a list member.
          perl -pi -e '
            my $plugin = "DenyServiceExternalIPs";
            s{
              (--[a-zA-Z0-9\-]+=)   # flag start
              ([^" \n]+)            # CSV value
            }{
              my ($flag, $csv) = ($1, $2);
              my @parts = split /,/, $csv;
              @parts = grep { $_ ne $plugin } map { s/^\s+|\s+$//gr } @parts;
              my $newcsv = join(",", @parts);
              $flag . $newcsv;
            }egx;
          ' "${TMP_MANIFEST}"

          # Re-check if any literal remains (might be in complex YAML/config sections)
          if grep -q "DenyServiceExternalIPs" "${TMP_MANIFEST}"; then
            echo "[WARN] DenyServiceExternalIPs still present after automated CSV cleanup."
            echo "[WARN] Inspect ${TMP_MANIFEST} manually and remove DenyServiceExternalIPs from enabled admission plugins."
          else
            echo "[INFO] All CSV references to DenyServiceExternalIPs removed."
          fi
        else
          echo "[INFO] DenyServiceExternalIPs not referenced directly in manifest (nothing more to do)."
        fi

        echo "[INFO] Installing updated manifest"
        cp "${TMP_MANIFEST}" "${MANIFEST}"
        rm -f "${TMP_MANIFEST}"

        echo "[INFO] kube-apiserver static pod manifest updated."
        echo "[INFO] NOTE: The kube-apiserver static pod will be restarted automatically by kubelet."

        echo "[INFO] Waiting for kube-apiserver process to reflect updated configuration..."
        sleep 20

        echo "[INFO] Verifying that DenyServiceExternalIPs is not set in kube-apiserver process"
        if /bin/ps -ef | grep kube-apiserver | grep -v grep | grep -q "DenyServiceExternalIPs"; then
          echo "[ERROR] Verification failed: DenyServiceExternalIPs still appears in kube-apiserver process arguments."
          echo "[INFO] Current kube-apiserver command line:"
          /bin/ps -ef | grep kube-apiserver | grep -v grep
          exit 2
        else
          echo "[INFO] Verification succeeded: DenyServiceExternalIPs is not set in kube-apiserver process."
        fi
        ```
      </Accordion>
    </AccordionGroup>
  </Tab>
</Tabs>

### Additional Reading:

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