> ## 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 (from any machine with kubectl access)**
           ```bash theme={null}
           kubectl get all -n default
           ```

        2. **Identify each controller-backed workload and its type (from any machine with kubectl access)**
           ```bash theme={null}
           kubectl get deploy,sts,ds,job,cronjob,replicaset,svc,ingress -n default -o wide
           ```

        3. **Create appropriate target namespaces (from any machine with kubectl access)**\
           Replace `team-a` / `team-b` with your desired namespace names and repeat as needed:
           ```bash theme={null}
           kubectl create namespace team-a
           kubectl create namespace team-b
           ```

        4. **Export and re‑apply controller-based workloads into the target namespace(s) (from any machine with kubectl access)**\
           Example for moving a Deployment named `web` from `default` to `team-a` (repeat per workload, adjusting names and target namespace):
           ```bash theme={null}
           # Export manifest
           kubectl get deployment web -n default -o yaml > web-deployment.yaml

           # Edit: change metadata.namespace: default  ->  team-a
           sed -i 's/namespace: default/namespace: team-a/' web-deployment.yaml

           # Apply into new namespace
           kubectl apply -f web-deployment.yaml

           # Once new Pods are Running and healthy, delete the old workload in default
           kubectl delete deployment web -n default
           ```

        5. **Handle standalone Pods and Services in `default` (from any machine with kubectl access)**\
           For any Pods not managed by a controller, recreate them in the correct namespace (usually by adding `metadata.namespace` to their original manifest) and then delete from `default`. Example for a standalone Pod and a Service:
           ```bash theme={null}
           # Export
           kubectl get pod legacy-pod -n default -o yaml > legacy-pod.yaml
           kubectl get service legacy-svc -n default -o yaml > legacy-svc.yaml

           # Edit both files: add or change
           # metadata:
           #   namespace: team-a

           # Recreate in the new namespace
           kubectl apply -f legacy-pod.yaml
           kubectl apply -f legacy-svc.yaml

           # Delete from default
           kubectl delete pod legacy-pod -n default
           kubectl delete service legacy-svc -n default
           ```

        6. **Verification: confirm `default` has zero Pods (from any machine with kubectl access)**
           ```bash theme={null}
           pods=$(kubectl get pods -n default -o json)
           kubectl get namespace default -o json | jq -r --argjson pods "$pods" '
             .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="Using kubectl">
        ```bash theme={null}
        # 1) Identify workloads running in the default namespace
        # Run on: any machine with kubectl access

        kubectl get all -n default

        # (Optional) For more detail:
        kubectl get deploy,sts,ds,job,cronjob,pod,svc,ingress -n default -o wide
        ```

        ```bash theme={null}
        # 2) Create a purpose-specific namespace (example: "team-a")
        # Run on: any machine with kubectl access

        cat << 'EOF' | kubectl apply -f -
        apiVersion: v1
        kind: Namespace
        metadata:
          name: team-a
          labels:
            tenant: team-a
        EOF
        ```

        ```bash theme={null}
        # 3) Export existing workload manifests from "default" namespace
        #    and save them locally for editing.
        # Run on: any machine with kubectl access

        # Example for all common controllers/services:
        kubectl get deploy,sts,ds,job,cronjob,svc,ingress -n default -o yaml > default-workloads.yaml
        ```

        ```bash theme={null}
        # 4) Edit the exported file so workloads target the new namespace
        #    - Change: metadata.namespace: default  --->  metadata.namespace: team-a
        #    - Ensure any RoleBindings/ConfigMaps/Secrets referenced are present in team-a
        # Run locally with your preferred editor

        sed -i 's/namespace: default/namespace: team-a/g' default-workloads.yaml
        ```

        ```bash theme={null}
        # 5) Apply the updated manifests to create workloads in the new namespace
        # Run on: any machine with kubectl access

        kubectl apply -f default-workloads.yaml
        ```

        ```bash theme={null}
        # 6) Confirm workloads are healthy in the new namespace
        # Run on: any machine with kubectl access

        kubectl get all -n team-a
        ```

        ```bash theme={null}
        # 7) Delete workloads from the default namespace
        #    (Only after confirming step 6 is successful.)
        # Run on: any machine with kubectl access

        kubectl delete deploy,sts,ds,job,cronjob,svc,ingress -n default --all
        kubectl delete pod -n default --all
        ```

        ```bash theme={null}
        # 8) Verification: re-run the benchmark-style audit to ensure default has zero pods
        # Run on: any machine with kubectl access

        pods=$(kubectl get pods -n default -o json)
        kubectl get namespace default -o json | jq -r --argjson pods "$pods" '
          .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:
        # - Lists all Kubernetes workload objects in the default namespace
        # - Creates per-workload namespaces if they do not exist
        # - Moves the objects and their associated ServiceAccounts into new namespaces
        # - Leaves the default namespace empty of workloads
        #
        # Requirements:
        # - Run from any machine with kubectl access and cluster-admin privileges.
        # - kubectl, jq, and bash must be installed.
        #
        # NOTE:
        # - This does NOT delete or recreate Pods directly; it moves controllers
        #   (Deployments, StatefulSets, etc.). Their Pods will be re-created in
        #   the new namespaces once controllers are recreated.
        # - Native Job/CronJob status/history is not preserved (spec is moved).

        NAMESPACE_FROM="default"

        # Map old->new namespaces. By default, derive new namespace name from
        # workload name; customize this logic as needed.
        derive_target_namespace() {
          local kind="$1" name="$2"
          # Example strategy: "<kind>-<name>"
          # Lowercase and replace invalid chars with '-'
          echo "${kind,,}-$name" | tr '[:upper:]' '[:lower:]' | tr -c 'a-z0-9-.' '-'
        }

        require() {
          command -v "$1" >/dev/null 2>&1 || {
            echo "ERROR: '$1' is required but not installed." >&2
            exit 1
          }
        }

        require kubectl
        require jq

        echo "Checking for workloads in namespace '${NAMESPACE_FROM}'..."

        # Supported workload kinds to move
        WORKLOAD_KINDS=(
          deployment
          statefulset
          daemonset
          job
          cronjob
          replicaset
          replicationcontroller
        )

        # Get all workload names and kinds in default namespace
        workloads_json=$(kubectl get "${WORKLOAD_KINDS[@]}" \
          -n "${NAMESPACE_FROM}" -o json 2>/dev/null || echo '{"items":[]}')

        workload_count=$(echo "$workloads_json" | jq '.items | length')
        if [ "$workload_count" -eq 0 ]; then
          echo "No workload objects found in namespace '${NAMESPACE_FROM}'. Nothing to move."
        else
          echo "Found ${workload_count} workload object(s) in namespace '${NAMESPACE_FROM}'."

          # Process each workload
          echo "$workloads_json" | jq -c '.items[]' | while read -r item; do
            kind=$(echo "$item" | jq -r '.kind')
            apiVersion=$(echo "$item" | jq -r '.apiVersion')
            name=$(echo "$item" | jq -r '.metadata.name')
            saName=$(echo "$item" | jq -r '.spec.template.spec.serviceAccountName // "default"')

            target_ns=$(derive_target_namespace "$kind" "$name")

            echo
            echo "Processing ${kind}/${name} from namespace '${NAMESPACE_FROM}' -> '${target_ns}'"

            # Ensure target namespace exists
            if ! kubectl get namespace "$target_ns" >/dev/null 2>&1; then
              echo "  Creating namespace: ${target_ns}"
              kubectl create namespace "$target_ns"
            else
              echo "  Namespace ${target_ns} already exists."
            fi

            # Ensure ServiceAccount exists in target namespace (if not default)
            if [ "$saName" != "default" ]; then
              echo "  Ensuring ServiceAccount '${saName}' exists in '${target_ns}'"
              if ! kubectl get sa "$saName" -n "$target_ns" >/dev/null 2>&1; then
                # Export SA from default and apply to target namespace
                echo "  Creating ServiceAccount '${saName}' in '${target_ns}' from source spec"
                kubectl get sa "$saName" -n "$NAMESPACE_FROM" -o json \
                | jq 'del(.metadata.namespace, .metadata.resourceVersion, .metadata.uid, .metadata.creationTimestamp, .metadata.annotations["kubectl.kubernetes.io/last-applied-configuration"])' \
                | jq ".metadata.namespace = \"${target_ns}\"" \
                | kubectl apply -f -
              else
                echo "  ServiceAccount '${saName}' already exists in '${target_ns}'."
              fi
            else
              echo "  Using default ServiceAccount in target namespace."
            fi

            # Export workload manifest, adjust namespace, and re-apply
            echo "  Exporting and recreating ${kind}/${name} in '${target_ns}'"
            kubectl get "$kind" "$name" -n "$NAMESPACE_FROM" -o json \
            | jq 'del(
                .metadata.namespace,
                .metadata.resourceVersion,
                .metadata.uid,
                .metadata.selfLink,
                .metadata.creationTimestamp,
                .metadata.generation,
                .metadata.annotations["kubectl.kubernetes.io/last-applied-configuration"],
                .status
              )' \
            | jq ".metadata.namespace = \"${target_ns}\"" \
            | kubectl apply -f -

            # Delete original workload only after successful apply
            echo "  Deleting original ${kind}/${name} from '${NAMESPACE_FROM}'"
            kubectl delete "$kind" "$name" -n "$NAMESPACE_FROM" --ignore-not-found=true
          done
        fi

        echo
        echo "Verifying that no Pods remain in the '${NAMESPACE_FROM}' namespace..."

        pods_json=$(kubectl get pods -n "${NAMESPACE_FROM}" -o json)
        kubectl get namespace "${NAMESPACE_FROM}" -o json | jq -r --argjson pods "$pods_json" '
          .metadata as $m
          | (($m.labels // {}) | to_entries | map("\(.key):\(.value)") | join(",")) as $labels
          | (($pods.items // []) | length) as $count
          | "kind=Namespace name=\($m.name) 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
        echo "Script completed."
        ```
      </Accordion>
    </AccordionGroup>
  </Tab>
</Tabs>
