> ## 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 manifests to files so you can recreate them in a new namespace (replace WORKLOAD and NAME accordingly; run on any machine with kubectl access):
           ```bash theme={null}
           # Example for a Deployment
           kubectl get deployment NAME -n default -o yaml > NAME-deploy.yaml

           # Example for a StatefulSet
           kubectl get statefulset NAME -n default -o yaml > NAME-sts.yaml

           # Example for a DaemonSet
           kubectl get daemonset NAME -n default -o yaml > NAME-ds.yaml

           # Example for a Job
           kubectl get job NAME -n default -o yaml > NAME-job.yaml

           # Example for a CronJob
           kubectl get cronjob NAME -n default -o yaml > NAME-cronjob.yaml

           # Example for a Service
           kubectl get service NAME -n default -o yaml > NAME-svc.yaml
           ```

        3. Edit each exported manifest file to set a purpose-specific namespace and remove default-assigned fields (run on any machine with kubectl access):
           * In each YAML file, under `metadata`, set:
             ```yaml theme={null}
             namespace: my-tenant-namespace
             ```
           * Remove the following fields if present to avoid conflicts when recreating:
             * `metadata: { uid, resourceVersion, selfLink, creationTimestamp, managedFields, ownerReferences }`
             * `status` sections
           * Save the edited files.

        4. Create the new namespace if it does not already exist (run on any machine with kubectl access):
           ```bash theme={null}
           kubectl create namespace my-tenant-namespace
           ```

        5. Recreate workloads in the new namespace, then delete them from `default` (run on any machine with kubectl access):
           ```bash theme={null}
           # Apply the edited manifests in the new namespace
           kubectl apply -f NAME-deploy.yaml
           kubectl apply -f NAME-sts.yaml
           kubectl apply -f NAME-ds.yaml
           kubectl apply -f NAME-job.yaml
           kubectl apply -f NAME-cronjob.yaml
           kubectl apply -f NAME-svc.yaml

           # After confirming the workloads are running correctly in the new namespace,
           # delete the old workloads from the default namespace
           kubectl delete deployment NAME -n default
           kubectl delete statefulset NAME -n default
           kubectl delete daemonset NAME -n default
           kubectl delete job NAME -n default
           kubectl delete cronjob NAME -n default
           kubectl delete service NAME -n default
           ```

        6. Verification (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 workloads in the `default` namespace

        ```bash theme={null}
        kubectl get all -n default
        ```

        2. For each workload type, export its manifest from `default` and save it to a file, then edit the namespace field.

        Example for a deployment named `my-app`:

        ```bash theme={null}
        # Export existing manifest
        kubectl get deployment my-app -n default -o yaml > my-app-deploy.yaml

        # Edit the manifest: in metadata, set
        #   namespace: my-tenant-namespace
        # If no namespace field exists under metadata, add:
        #   namespace: my-tenant-namespace
        #
        # Also review and update any other namespaced references (ConfigMaps, Services, RBAC, etc.)
        ```

        3. Create the target namespace if it does not already exist:

        ```bash theme={null}
        kubectl create namespace my-tenant-namespace
        ```

        4. Apply the updated manifest into the new namespace:

        ```bash theme={null}
        kubectl apply -f my-app-deploy.yaml
        ```

        5. Once you have recreated all needed workloads in their new, purpose-specific namespaces and confirmed they are running correctly, delete the originals from the `default` namespace.

        Examples by resource type:

        ```bash theme={null}
        # Deployments
        kubectl delete deployment my-app -n default

        # StatefulSets
        kubectl delete statefulset my-stateful-app -n default

        # DaemonSets
        kubectl delete daemonset my-daemon -n default

        # CronJobs
        kubectl delete cronjob my-cronjob -n default

        # Jobs (if still present)
        kubectl delete job my-job -n default

        # Services, ConfigMaps, Secrets, etc., that were only for these workloads
        kubectl delete service my-app -n default
        kubectl delete configmap my-app-config -n default
        kubectl delete secret my-app-secret -n default
        ```

        Repeat this export–edit–apply–delete process for every workload that currently runs in the `default` namespace, moving each into an appropriate purpose-specific namespace.

        6. Verification (pod count in `default` should be zero):

        ```bash theme={null}
        kubectl get pods -n default
        ```
      </Accordion>

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

        # This script:
        # - Finds all workload resources (Pods, Deployments, ReplicaSets, StatefulSets,
        #   DaemonSets, Jobs, CronJobs) in the "default" namespace.
        # - For each, creates a copy in a target namespace and deletes the original.
        # - Verifies that the "default" namespace has no Pods remaining.
        #
        # REQUIREMENTS:
        # - Run on any machine with kubectl access and permissions to list/get/create/delete
        #   resources cluster-wide.
        # - kubectl must be configured to point at the target GKE cluster.
        #
        # USAGE:
        #   ./move-default-workloads.sh <target-namespace>
        #
        # The target namespace MUST exist beforehand and be prepared with appropriate
        # RBAC, ResourceQuotas, and NetworkPolicies.

        if [[ $# -ne 1 ]]; then
          echo "Usage: $0 <target-namespace>" >&2
          exit 1
        fi

        TARGET_NS="$1"

        echo "==> Verifying target namespace '${TARGET_NS}' exists"
        if ! kubectl get namespace "${TARGET_NS}" >/dev/null 2>&1; then
          echo "ERROR: target namespace '${TARGET_NS}' does not exist." >&2
          echo "Create it first, for example:" >&2
          echo "  kubectl create namespace ${TARGET_NS}" >&2
          exit 1
        fi

        echo "==> Checking for workloads in the 'default' namespace"
        # If there are no pods, we consider the check already compliant and exit early.
        POD_COUNT="$(kubectl get pods -n default --no-headers 2>/dev/null | wc -l | tr -d ' ')"
        if [[ "${POD_COUNT}" -eq 0 ]]; then
          echo "No pods found in 'default' namespace. Nothing to move."
          echo "Cluster is already compliant with respect to this control."
          exit 0
        fi

        echo "Workloads found in 'default' namespace. Beginning migration to '${TARGET_NS}'."

        # Resource types to migrate.
        # These cover the common workload controllers; adjust if you use additional kinds.
        RESOURCE_KINDS=(
          "deployment.apps"
          "replicaset.apps"
          "statefulset.apps"
          "daemonset.apps"
          "job.batch"
          "cronjob.batch"
          "pod"          # standalone pods not managed by controllers
        )

        for KIND in "${RESOURCE_KINDS[@]}"; do
          echo "==> Processing kind: ${KIND}"
          # List resource names; ignore "No resources found" errors.
          MAPFILE -t RESOURCES < <(kubectl get "${KIND}" -n default -o name 2>/dev/null || true)
          if [[ "${#RESOURCES[@]}" -eq 0 ]]; then
            echo "  No ${KIND} resources in 'default' namespace."
            continue
          fi

          for RES in "${RESOURCES[@]}"; do
            NAME="${RES#*/}" # strip the kind prefix, e.g. deployment.apps/my-deploy -> my-deploy

            echo "  -> Migrating ${KIND} '${NAME}' from 'default' to '${TARGET_NS}'"

            # Check if it already exists in the target namespace (idempotency).
            if kubectl get "${KIND}" -n "${TARGET_NS}" "${NAME}" >/dev/null 2>&1; then
              echo "     Target '${KIND}/${NAME}' already exists in '${TARGET_NS}'."
              echo "     Skipping creation; will only ensure original is removed."
            else
              # Export the object from 'default', adjust namespace, and recreate in target namespace.
              # Strip fields that should not be migrated (status, resourceVersion, uid, etc.).
              echo "     Creating '${KIND}/${NAME}' in '${TARGET_NS}'"
              kubectl get "${KIND}" -n default "${NAME}" -o json \
              | jq '
                  del(
                    .metadata.uid,
                    .metadata.resourceVersion,
                    .metadata.selfLink,
                    .metadata.creationTimestamp,
                    .metadata.generation,
                    .metadata.annotations."kubectl.kubernetes.io/last-applied-configuration",
                    .status
                  )
                  | .metadata.namespace = "'"${TARGET_NS}"'"
                ' \
              | kubectl apply -f -
            fi

            # Delete original from default namespace (idempotent: delete succeeds even if already gone).
            echo "     Deleting original '${KIND}/${NAME}' from 'default'"
            kubectl delete "${KIND}" -n default "${NAME}" --ignore-not-found=true
          done
        done

        echo "==> Waiting for any terminating pods in 'default' to fully disappear"
        # Wait loop: break when no pods remain.
        for i in {1..30}; do
          REMAINING="$(kubectl get pods -n default --no-headers 2>/dev/null | wc -l | tr -d ' ')"
          if [[ "${REMAINING}" -eq 0 ]]; then
            break
          fi
          echo "  ${REMAINING} pod(s) still present in 'default'; rechecking in 10s..."
          sleep 10
        done

        echo "==> Final verification (matches benchmark audit style)"
        { 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)"'

        echo "==> Migration complete."
        ```
      </Accordion>
    </AccordionGroup>
  </Tab>
</Tabs>
