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

# The Default Namespace Should Not Be Used

### More Info:

Using the default namespace for user resources hampers resource segregation and fine-grained RBAC. Move user-defined objects into dedicated namespaces.

### Risk Level

Low

### Address

Security

### Compliance Standards

* CIS EKS

### Triage and Remediation

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

    <AccordionGroup>
      <Accordion title="Manual Steps" defaultOpen="true">
        1. **Identify user resources in the default namespace**
           * Run on: any machine with kubectl access
           ```sh theme={null}
           kubectl get $(kubectl api-resources --verbs=list --namespaced=true -o name | paste -sd, -) \
             --ignore-not-found -n default
           ```

        2. **Create one or more dedicated namespaces to move resources into**
           * Run on: any machine with kubectl access
           ```sh theme={null}
           kubectl create namespace app-namespace
           # repeat with other namespace names as needed
           ```

        3. **Move workload resources (Deployments, StatefulSets, Services, etc.) out of default**
           * Run on: any machine with kubectl access\
             Export each object from `default`, remove cluster-assigned fields, and re-create it in the new namespace. Example for a Deployment:
           ```sh theme={null}
           kubectl get deployment my-app -n default -o yaml > /tmp/my-app-deploy.yaml
           sed -i '/^\s*namespace: /d' /tmp/my-app-deploy.yaml
           sed -i '/^\s*resourceVersion: /d;/^\s*uid: /d;/^\s*selfLink: /d;/^\s*creationTimestamp: /d;/^\s*managedFields: /,/^\s*ownerReferences: /d' /tmp/my-app-deploy.yaml
           kubectl delete deployment my-app -n default
           kubectl apply -n app-namespace -f /tmp/my-app-deploy.yaml
           ```
           Repeat this pattern for other namespaced resource kinds (e.g., `statefulset`, `daemonset`, `service`, `ingress`, `cronjob`, `job`, `configmap`, `secret`, `pvc`), adjusting the kind and name in the commands.

        4. **Update references that point to the default namespace**
           * Run on: any machine with kubectl access\
             Search manifests or Helm values you use to deploy resources and update any explicit `namespace: default` fields, or `-n default` CLI flags, to the new namespace:
           ```sh theme={null}
           grep -R --line-number "namespace: default" .
           grep -R --line-number "\-n default" .
           ```
           Redeploy with the corrected manifests/Helm charts so future resources are created in the dedicated namespaces.

        5. **Review and adjust RBAC for new namespaces**
           * Run on: any machine with kubectl access\
             Bind roles to users/service accounts in the new namespaces instead of `default`. Example:
           ```sh theme={null}
           kubectl get role,rolebinding -n default
           # create equivalent RBAC in the new namespace
           kubectl create rolebinding app-admin-binding \
             --clusterrole=edit \
             --user your-user@example.com \
             -n app-namespace
           ```

        6. **Verify no user resources remain in the default namespace**
           * Run on: any machine with kubectl access
           ```sh theme={null}
           output=$(kubectl get $(kubectl api-resources --verbs=list --namespaced=true -o name | paste -sd, -) --ignore-not-found -n default 2>/dev/null | grep -v "^kubernetes ")
           if [ -z "$output" ]; then
             echo "NO_USER_RESOURCES_IN_DEFAULT"
           else
             echo "USER_RESOURCES_IN_DEFAULT_FOUND: $output"
           fi
           ```
      </Accordion>

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

        1. Identify user resources in the `default` namespace

        ```bash theme={null}
        kubectl get $(kubectl api-resources --verbs=list --namespaced=true -o name | paste -sd, -) \
          --ignore-not-found -n default
        ```

        2. For each resource type, decide a target namespace and create it if needed (example: `team-a`)

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

        3. Move namespaced resources out of `default` using `kubectl get -o yaml | sed | kubectl apply`

        Example for Deployments:

        ```bash theme={null}
        kubectl get deploy -n default -o yaml \
          | sed 's/namespace: default/namespace: team-a/g' \
          | sed '/^  resourceVersion:/d;/^  uid:/d;/^  creationTimestamp:/d;/^  selfLink:/d;/^  managedFields:/d' \
          | kubectl apply -f -
        kubectl delete deploy -n default --all
        ```

        Example for Services (be careful with in-cluster DNS names and consumers):

        ```bash theme={null}
        kubectl get svc -n default -o yaml \
          | sed 's/namespace: default/namespace: team-a/g' \
          | sed '/^  resourceVersion:/d;/^  uid:/d;/^  creationTimestamp:/d;/^  selfLink:/d;/^  clusterIP:/d;/^  clusterIPs:/d;/^  ipFamilies:/d;/^  ipFamilyPolicy:/d;/^  managedFields:/d' \
          | kubectl apply -f -
        kubectl delete svc -n default --all
        ```

        Example for ConfigMaps:

        ```bash theme={null}
        kubectl get configmap -n default -o yaml \
          | sed 's/namespace: default/namespace: team-a/g' \
          | sed '/^  resourceVersion:/d;/^  uid:/d;/^  creationTimestamp:/d;/^  selfLink:/d;/^  managedFields:/d' \
          | kubectl apply -f -
        kubectl delete configmap -n default --all
        ```

        Example for Secrets (excluding default service account tokens; filter by name as needed):

        ```bash theme={null}
        kubectl get secret -n default -o yaml \
          | sed 's/namespace: default/namespace: team-a/g' \
          | sed '/^  resourceVersion:/d;/^  uid:/d;/^  creationTimestamp:/d;/^  selfLink:/d;/^  managedFields:/d' \
          | kubectl apply -f -
        # then delete only the secrets you intentionally moved, e.g.:
        # kubectl delete secret -n default my-app-secret
        ```

        Repeat the same pattern for other resource kinds present in `default` (e.g., `statefulset`, `job`, `cronjob`, `ingress`, `role`, `rolebinding`, `networkpolicy`, etc.):

        ```bash theme={null}
        kubectl get <kind> -n default -o yaml \
          | sed 's/namespace: default/namespace: <target-namespace>/g' \
          | sed '/^  resourceVersion:/d;/^  uid:/d;/^  creationTimestamp:/d;/^  selfLink:/d;/^  managedFields:/d' \
          | kubectl apply -f -
        kubectl delete <kind> -n default --all
        ```

        4. Verification (same logic as the audit):

        ```bash theme={null}
        output=$(kubectl get $(kubectl api-resources --verbs=list --namespaced=true -o name | paste -sd, -) --ignore-not-found -n default 2>/dev/null | grep -v "^kubernetes ")
        if [ -z "$output" ]; then
          echo "NO_USER_RESOURCES_IN_DEFAULT"
        else
          echo "USER_RESOURCES_IN_DEFAULT_FOUND: $output"
        fi
        ```
      </Accordion>

      <Accordion title="Automation">
        ```bash theme={null}
        #!/usr/bin/env bash
        #
        # migrate-default-namespace-resources.sh
        #
        # Purpose:
        #   - Detect user-defined namespaced resources in the "default" namespace.
        #   - Create a target namespace (by default "apps") if it does not exist.
        #   - Migrate supported resource types from "default" to the target namespace.
        #   - Verify that no user-defined resources remain in the "default" namespace.
        #
        # Run on:
        #   - Any machine with kubectl access and appropriate RBAC.
        #
        # Requirements:
        #   - bash, kubectl, jq (for JSON processing)
        #
        # Notes:
        #   - This script is idempotent: safe to re-run.
        #   - Some resources cannot be moved (e.g. kubernetes Service, certain CRDs).
        #   - Carefully review and test in non-production before using on critical clusters.

        set -euo pipefail

        TARGET_NAMESPACE="${TARGET_NAMESPACE:-apps}"  # change via env if desired
        DRY_RUN="${DRY_RUN:-false}"                   # set DRY_RUN=true to see planned actions

        #-------------------------------------------------------------------------------
        # Helper: run kubectl with common flags
        #-------------------------------------------------------------------------------
        _k() {
          kubectl "$@"
        }

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

        if ! command -v jq >/dev/null 2>&1; then
          echo "ERROR: jq not found in PATH (required for JSON manipulation)" >&2
          exit 1
        fi

        echo "Using target namespace: ${TARGET_NAMESPACE}"
        echo "DRY_RUN: ${DRY_RUN}"

        #-------------------------------------------------------------------------------
        # Step 1: Ensure target namespace exists
        #-------------------------------------------------------------------------------
        if ! _k get namespace "${TARGET_NAMESPACE}" >/dev/null 2>&1; then
          echo "Target namespace '${TARGET_NAMESPACE}' does not exist. Creating..."
          if [ "${DRY_RUN}" = "true" ]; then
            echo "DRY_RUN: kubectl create namespace ${TARGET_NAMESPACE}"
          else
            _k create namespace "${TARGET_NAMESPACE}"
          fi
        else
          echo "Target namespace '${TARGET_NAMESPACE}' already exists."
        fi

        #-------------------------------------------------------------------------------
        # Step 2: Discover namespaced resource types and objects in default namespace
        #-------------------------------------------------------------------------------
        echo "Discovering namespaced resource types with resources in 'default'..."

        API_RESOURCES=$(
          _k api-resources --verbs=list --namespaced=true -o name | paste -sd, -
        )

        # Raw output of all namespaced resources in default (may include headers)
        RAW_OUTPUT=$(
          _k get "${API_RESOURCES}" --ignore-not-found -n default 2>/dev/null || true
        )

        # Filter out the default "kubernetes" Service line
        FILTERED_OUTPUT=$(echo "${RAW_OUTPUT}" | grep -v "^kubernetes " || true)

        if [ -z "${FILTERED_OUTPUT}" ]; then
          echo "NO_USER_RESOURCES_IN_DEFAULT"
          exit 0
        fi

        echo "USER_RESOURCES_IN_DEFAULT_FOUND:"
        echo "${FILTERED_OUTPUT}"
        echo

        #-------------------------------------------------------------------------------
        # Step 3: Define which resource types we will migrate
        #-------------------------------------------------------------------------------
        # This list is intentionally conservative. Add more as needed, after testing.
        MIGRATABLE_KINDS=(
          pods
          deployments.apps
          replicasets.apps
          statefulsets.apps
          daemonsets.apps
          jobs.batch
          cronjobs.batch
          services
          configmaps
          secrets
          serviceaccounts
          roles.rbac.authorization.k8s.io
          rolebindings.rbac.authorization.k8s.io
          ingresses.networking.k8s.io
        )

        # Build a regex for grep -E
        MIGRATABLE_REGEX="$(printf '%s\n' "${MIGRATABLE_KINDS[@]}" | paste -sd'|' -)"

        #-------------------------------------------------------------------------------
        # Step 4: Collect full resource identifiers (kind/name) from default namespace
        #-------------------------------------------------------------------------------
        echo "Collecting resources in 'default' that match supported kinds..."

        RESOURCE_LIST=$(
          echo "${FILTERED_OUTPUT}" \
            | awk 'NR>1 {print $1,$2}' \
            | awk '{print $1"/"$2}' \
            | grep -E "${MIGRATABLE_REGEX}" || true
        )

        if [ -z "${RESOURCE_LIST}" ]; then
          echo "No supported resource types found to migrate; manual review needed."
          exit 0
        fi

        echo "Resources to consider for migration:"
        echo "${RESOURCE_LIST}"
        echo

        #-------------------------------------------------------------------------------
        # Step 5: Migrate each resource
        #-------------------------------------------------------------------------------
        migrate_resource() {
          local res="$1" # e.g. deployments.apps/my-deployment

          local kind="${res%%/*}"
          local name="${res##*/}"

          echo "Processing ${kind}/${name} in namespace 'default'..."

          # Skip the default "kubernetes" Service explicitly, just in case it appears
          if [ "${kind}" = "services" ] && [ "${name}" = "kubernetes" ]; then
            echo "  Skipping core Service 'kubernetes'."
            return 0
          fi

          # Fetch the object as JSON
          local json
          if ! json=$(_k get "${kind}" "${name}" -n default -o json 2>/dev/null); then
            echo "  WARNING: Unable to get ${kind}/${name} from 'default'; skipping."
            return 0
          fi

          # Update metadata.namespace, remove resourceVersion, uid, etc.
          local patched
          patched=$(echo "${json}" \
            | jq '
                .metadata.namespace = "'"${TARGET_NAMESPACE}"'" |
                del(
                  .metadata.uid,
                  .metadata.resourceVersion,
                  .metadata.selfLink,
                  .metadata.creationTimestamp,
                  .metadata.managedFields,
                  .status
                )
              ')

          # Check if object already exists in target namespace (idempotency)
          if _k get "${kind}" "${name}" -n "${TARGET_NAMESPACE}" >/dev/null 2>&1; then
            echo "  ${kind}/${name} already exists in '${TARGET_NAMESPACE}'. Skipping create."
          else
            echo "  Creating ${kind}/${name} in '${TARGET_NAMESPACE}'..."
            if [ "${DRY_RUN}" = "true" ]; then
              echo "DRY_RUN: kubectl apply -f -"
              echo "${patched}"
            else
              echo "${patched}" | _k apply -f -
            fi
          fi

          # Delete original from default
          echo "  Deleting original ${kind}/${name} from 'default'..."
          if [ "${DRY_RUN}" = "true" ]; then
            echo "DRY_RUN: kubectl delete ${kind} ${name} -n default"
          else
            _k delete "${kind}" "${name}" -n default --ignore-not-found
          fi

          echo "  Done with ${kind}/${name}."
        }

        echo "Starting migration of resources from 'default' to '${TARGET_NAMESPACE}'..."
        while read -r res; do
          [ -n "${res}" ] || continue
          migrate_resource "${res}"
        done <<< "${RESOURCE_LIST}"

        echo
        echo "Migration pass complete."

        #-------------------------------------------------------------------------------
        # Step 6: Verification (same logic as the audit command)
        #-------------------------------------------------------------------------------
        echo "Verifying that no user-defined resources remain in 'default'..."

        VERIFY_OUTPUT=$(
          output=$(
            _k get "$(_k api-resources --verbs=list --namespaced=true -o name | paste -sd, -)" \
              --ignore-not-found -n default 2>/dev/null \
            | grep -v "^kubernetes " || true
          )
          if [ -z "$output" ]; then
            echo "NO_USER_RESOURCES_IN_DEFAULT"
          else
            echo "USER_RESOURCES_IN_DEFAULT_FOUND: $output"
          fi
        )

        echo "${VERIFY_OUTPUT}"

        if echo "${VERIFY_OUTPUT}" | grep -q "NO_USER_RESOURCES_IN_DEFAULT"; then
          exit 0
        else
          echo "NOTE: Some resources remain in 'default'. Manual review and migration may be required."
          exit 1
        fi
        ```
      </Accordion>
    </AccordionGroup>
  </Tab>
</Tabs>
