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

# No Workloads Should Run In The default Namespace

### More Info:

Verifies the default namespace has no workloads so RBAC, quotas and NetworkPolicies can be scoped per tenant.

### Risk Level

Medium

### Address

Security

### Compliance Standards

* Cloudanix Best Practice

### Triage and Remediation

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

    <AccordionGroup>
      <Accordion title="Manual Steps" defaultOpen="true">
        1. List all workloads in the `default` namespace (run on any machine with kubectl access):
           ```bash theme={null}
           kubectl get all -n default
           ```

        2. For each workload type in `default`, export its manifest to a file (replace placeholders with actual names from step 1; run on any machine with kubectl access):

           Deployments:

           ```bash theme={null}
           kubectl get deployment <deployment-name> -n default -o yaml > /tmp/deployment-<deployment-name>.yaml
           ```

           StatefulSets:

           ```bash theme={null}
           kubectl get statefulset <statefulset-name> -n default -o yaml > /tmp/statefulset-<statefulset-name>.yaml
           ```

           DaemonSets:

           ```bash theme={null}
           kubectl get daemonset <daemonset-name> -n default -o yaml > /tmp/daemonset-<daemonset-name>.yaml
           ```

           Jobs/CronJobs:

           ```bash theme={null}
           kubectl get job <job-name> -n default -o yaml > /tmp/job-<job-name>.yaml
           kubectl get cronjob <cronjob-name> -n default -o yaml > /tmp/cronjob-<cronjob-name>.yaml
           ```

        3. Edit each exported manifest to target a purpose-specific namespace (run on any machine with kubectl access):
           ```bash theme={null}
           sed -i 's/namespace: default/namespace: <target-namespace>/' /tmp/deployment-<deployment-name>.yaml
           ```
           If the `metadata.namespace` field is missing, add it under `metadata:`:
           ```yaml theme={null}
           metadata:
             name: <deployment-name>
             namespace: <target-namespace>
           ```
           Repeat for each manifest. Ensure the `<target-namespace>` already exists, or create it:
           ```bash theme={null}
           kubectl create namespace <target-namespace>
           ```

        4. Apply the modified manifests into the new namespace (run on any machine with kubectl access):
           ```bash theme={null}
           kubectl apply -f /tmp/deployment-<deployment-name>.yaml
           kubectl apply -f /tmp/statefulset-<statefulset-name>.yaml
           kubectl apply -f /tmp/daemonset-<daemonset-name>.yaml
           kubectl apply -f /tmp/job-<job-name>.yaml
           kubectl apply -f /tmp/cronjob-<cronjob-name>.yaml
           ```

        5. After confirming the workloads are running correctly in the new namespace, delete the originals from `default` (run on any machine with kubectl access):
           ```bash theme={null}
           kubectl delete deployment <deployment-name> -n default
           kubectl delete statefulset <statefulset-name> -n default
           kubectl delete daemonset <daemonset-name> -n default
           kubectl delete job <job-name> -n default
           kubectl delete cronjob <cronjob-name> -n default
           ```

        6. Verification (derived from the audit command; run on any machine with kubectl access):
           ```bash theme={null}
           { kubectl get pods -n default -o json
             kubectl get namespace default -o json
           } | jq -rs '
             .[0] as $pods | .[1] |
             .metadata as $m
             | (($m.labels // {}) | to_entries | map("\(.key):\(.value)") | join(",")) as $labels
             | (($pods.items // []) | length) as $count
             | "kind=Namespace name=default uid=\($m.uid) apiVersion=v1"
               + (if ($m.creationTimestamp // "") == "" then "" else " created=\($m.creationTimestamp)" end)
               + (if $labels == "" then "" else " labels=\($labels)" end)
               + " podCount=\($count)"
               + " is_compliant=\(if $count == 0 then "true" else "false" end)"'
           ```
           Confirm that `podCount=0` and `is_compliant=true`.
      </Accordion>

      <Accordion title="Using kubectl">
        On any machine with kubectl access:

        1. Identify all workload types in the `default` namespace

        ```bash theme={null}
        kubectl get all -n default
        kubectl get configmap,secret,serviceaccount,role,rolebinding -n default
        ```

        2. Choose or create a purpose-specific namespace (example: `team-a`)

        ```bash theme={null}
        kubectl create namespace team-a
        ```

        3. Export existing workloads from `default` and edit their namespace

        ```bash theme={null}
        kubectl get deploy,sts,ds,job,cronjob,svc,ingress \
          -n default -o yaml > /tmp/default-workloads.yaml
        ```

        Edit `/tmp/default-workloads.yaml`:

        * For every object, set:

        ```yaml theme={null}
        metadata:
          namespace: team-a
        ```

        * Remove runtime-only fields under `metadata` such as:
          * `creationTimestamp`
          * `resourceVersion`
          * `uid`
          * `annotations` that are managed by controllers (e.g. `deployment.kubernetes.io/revision`)
          * `generation`
        * Remove status sections:

        ```yaml theme={null}
        status: {}
        # or delete the entire status: block
        ```

        4. Apply workloads into the new namespace

        ```bash theme={null}
        kubectl apply -f /tmp/default-workloads.yaml
        ```

        5. Delete old workloads from the `default` namespace

        Be careful to delete only what you intentionally moved; do not delete the `kubernetes` Service.

        ```bash theme={null}
        kubectl delete deploy,sts,ds,job,cronjob,ingress \
          --all -n default

        # Optionally, delete non-core Services (leave the "kubernetes" ClusterIP service)
        kubectl get svc -n default
        kubectl delete svc <service-name-1> <service-name-2> -n default
        ```

        6. Verify no pods remain in the `default` namespace (benchmark audit)

        ```bash theme={null}
        { kubectl get pods -n default -o json
          kubectl get namespace default -o json
        } | jq -rs '
          .[0] as $pods | .[1] |
          .metadata as $m
          | (($m.labels // {}) | to_entries | map("\(.key):\(.value)") | join(",")) as $labels
          | (($pods.items // []) | length) as $count
          | "kind=Namespace name=default uid=\($m.uid) apiVersion=v1"
            + (if ($m.creationTimestamp // "") == "" then "" else " created=\($m.creationTimestamp)" end)
            + (if $labels == "" then "" else " labels=\($labels)" end)
            + " podCount=\($count)"
            + " is_compliant=\(if $count == 0 then "true" else "false" end)"'
        ```
      </Accordion>

      <Accordion title="Automation">
        ```bash theme={null}
        #!/usr/bin/env bash
        set -euo pipefail

        # This script:
        # - Finds all workloads in the "default" namespace
        # - Creates a dedicated namespace per workload owner (Deployment/StatefulSet/Job/CronJob/Pod)
        # - Moves the workloads into those namespaces
        # - Is idempotent and safe to re-run
        #
        # Run from any machine with kubectl access to the EKS cluster.

        TARGET_NAMESPACE_PREFIX="team" # used only when no better name can be inferred

        # Require kubectl
        if ! command -v kubectl >/dev/null 2>&1; then
          echo "kubectl not found in PATH" >&2
          exit 1
        fi

        # Check access
        if ! kubectl get ns default >/dev/null 2>&1; then
          echo "Cannot access the cluster or 'default' namespace" >&2
          exit 1
        fi

        timestamp() { date -u +"%Y%m%d-%H%M%S"; }

        # Create namespace if it does not exist
        ensure_namespace() {
          local ns="$1"
          if ! kubectl get namespace "${ns}" >/dev/null 2>&1; then
            echo "Creating namespace ${ns}"
            kubectl create namespace "${ns}"
          fi
        }

        # Derive a target namespace from labels/owner; fall back to ${TARGET_NAMESPACE_PREFIX}-<name>
        derive_namespace() {
          local kind="$1" name="$2"
          local ns

          # Try using a "team" label if present
          ns="$(kubectl get "${kind}" "${name}" -n default -o jsonpath='{.metadata.labels.team}' 2>/dev/null || true)"
          if [[ -n "${ns}" ]]; then
            echo "${ns}"
            return
          fi

          # Try using an "app" label
          ns="$(kubectl get "${kind}" "${name}" -n default -o jsonpath='{.metadata.labels.app}' 2>/dev/null || true)"
          if [[ -n "${ns}" ]]; then
            echo "${ns}"
            return
          fi

          # Fallback
          echo "${TARGET_NAMESPACE_PREFIX}-${name}"
        }

        # Move a workload object to a namespace:
        # - Export manifest
        # - Change metadata.namespace
        # - Apply to target namespace
        # - Delete original object from default
        move_object() {
          local kind="$1" name="$2" src_ns="$3" dst_ns="$4"

          if [[ "${src_ns}" == "${dst_ns}" ]]; then
            echo "Skipping ${kind}/${name}; already in namespace ${src_ns}"
            return
          fi

          echo "Moving ${kind}/${name} from ${src_ns} to ${dst_ns}"

          ensure_namespace "${dst_ns}"

          # Export object without status, then modify namespace and apply
          tmpfile="$(mktemp)"
          kubectl get "${kind}" "${name}" -n "${src_ns}" -o yaml \
            | sed '/^status:/,$d' \
            | sed "s/^  namespace: ${src_ns}$/  namespace: ${dst_ns}/" \
            > "${tmpfile}"

          # If no explicit namespace field, inject it
          if ! grep -q '^  namespace:' "${tmpfile}"; then
            sed -i "0,/^metadata:$/s//metadata:\n  namespace: ${dst_ns}/" "${tmpfile}"
          fi

          kubectl apply -f "${tmpfile}"
          kubectl delete "${kind}" "${name}" -n "${src_ns}" --cascade=orphan --wait=true
          rm -f "${tmpfile}"
        }

        # 1. Handle controllers (Deployments, StatefulSets, DaemonSets, Jobs, CronJobs)
        echo "Discovering controllers in 'default' namespace..."

        declare -A SEEN_OWNERS=()

        for kind in deployment statefulset daemonset job cronjob; do
          items="$(kubectl get "${kind}" -n default -o jsonpath='{range .items[*]}{.metadata.name}{"\n"}{end}' 2>/dev/null || true)"
          if [[ -z "${items}" ]]; then
            continue
          fi
          while read -r name; do
            [[ -z "${name}" ]] && continue
            key="${kind}/${name}"
            if [[ -n "${SEEN_OWNERS[${key}]:-}" ]]; then
              continue
            fi
            SEEN_OWNERS[${key}]=1
            dst_ns="$(derive_namespace "${kind}" "${name}")"
            move_object "${kind}" "${name}" default "${dst_ns}"
          done <<< "${items}"
        done

        # 2. Handle standalone Pods (no controller owner)
        echo "Discovering standalone Pods in 'default' namespace..."

        pods_json="$(kubectl get pods -n default -o json 2>/dev/null || echo '{}')"
        pod_names="$(echo "${pods_json}" | jq -r '.items[] | select(.metadata.ownerReferences == null or .metadata.ownerReferences | length == 0) | .metadata.name' 2>/dev/null || true)"

        if [[ -n "${pod_names}" ]]; then
          while read -r pod; do
            [[ -z "${pod}" ]] && continue
            dst_ns="$(derive_namespace pod "${pod}")"

            echo "Moving standalone Pod/${pod} from default to ${dst_ns}"
            ensure_namespace "${dst_ns}"

            tmpfile="$(mktemp)"
            kubectl get pod "${pod}" -n default -o yaml \
              | sed '/^status:/,$d' \
              | sed "s/^  namespace: default$/  namespace: ${dst_ns}/" \
              > "${tmpfile}"

            if ! grep -q '^  namespace:' "${tmpfile}"; then
              sed -i "0,/^metadata:$/s//metadata:\n  namespace: ${dst_ns}/" "${tmpfile}"
            fi

            kubectl apply -f "${tmpfile}"
            kubectl delete pod "${pod}" -n default --wait=true
            rm -f "${tmpfile}"
          done <<< "${pod_names}"
        fi

        # 3. Verification (same logic as the audit)
        echo "Verifying that no Pods remain in 'default' namespace..."

        verify_json="$(
        { kubectl get pods -n default -o json 2>/dev/null || echo '{"items":[]}';
          kubectl get namespace default -o json;
        } | jq -rs '
          .[0] as $pods | .[1] |
          .metadata as $m
          | (($m.labels // {}) | to_entries | map("\(.key):\(.value)") | join(",")) as $labels
          | (($pods.items // []) | length) as $count
          | "kind=Namespace name=default uid=\($m.uid) apiVersion=v1"
            + (if ($m.creationTimestamp // "") == "" then "" else " created=\($m.creationTimestamp)" end)
            + (if $labels == "" then "" else " labels=\($labels)" end)
            + " podCount=\($count)"
            + " is_compliant=\(if $count == 0 then "true" else "false" end)"'
        )"

        echo "${verify_json}"

        if echo "${verify_json}" | grep -q 'is_compliant=true'; then
          echo "Success: no workloads are running in the 'default' namespace."
        else
          echo "Warning: some workloads still exist in the 'default' namespace; manual review required." >&2
          exit 2
        fi
        ```
      </Accordion>
    </AccordionGroup>
  </Tab>
</Tabs>
