Skip to main content

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​

Remediation​

Manual Steps
  1. List all workloads in the default namespace (run on any machine with kubectl access):

    kubectl get all -n default
  2. For each workload type in default, export its manifests so you can recreate them in a new namespace (replace NEWNAMESPACE with your chosen name, created beforehand if needed):

    kubectl get deploy,sts,ds,job,cronjob -n default -o yaml > /tmp/default-workloads.yaml
    sed -i 's/namespace: default/namespace: NEWNAMESPACE/g' /tmp/default-workloads.yaml
  3. Create the target namespace if it does not already exist:

    kubectl create namespace NEWNAMESPACE
  4. Apply the modified manifests into the new namespace (run on any machine with kubectl access):

    kubectl apply -f /tmp/default-workloads.yaml
  5. After confirming the workloads are running correctly in NEWNAMESPACE, delete the old workloads from default (run on any machine with kubectl access):

    kubectl delete deploy,sts,ds,job,cronjob -n default --all
  6. Verification (run on any machine with kubectl access):

    { 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)"'
Using kubectl

On any machine with kubectl access:

  1. Identify workloads running in the default namespace
kubectl get all -n default
  1. For each workload type, export its manifest, modify the namespace, and re‑create it. Examples:
  • Deployments:
kubectl get deploy -n default -o yaml > default-deployments.yaml

Edit default-deployments.yaml:

  • Remove status: sections.
  • For every object, set metadata.namespace: <new-namespace-name> (for example team-a).

Then apply and delete originals:

kubectl apply -f default-deployments.yaml
kubectl delete deploy -n default --all
  • StatefulSets:
kubectl get statefulset -n default -o yaml > default-statefulsets.yaml
# edit as above: remove status, change metadata.namespace
kubectl apply -f default-statefulsets.yaml
kubectl delete statefulset -n default --all
  • DaemonSets:
kubectl get daemonset -n default -o yaml > default-daemonsets.yaml
# edit as above
kubectl apply -f default-daemonsets.yaml
kubectl delete daemonset -n default --all
  • Jobs/CronJobs:
kubectl get job -n default -o yaml > default-jobs.yaml || true
kubectl get cronjob -n default -o yaml > default-cronjobs.yaml || true
# edit as above
kubectl apply -f default-jobs.yaml default-cronjobs.yaml
kubectl delete job -n default --all
kubectl delete cronjob -n default --all
  • Services and related objects (to keep networking functioning):
kubectl get svc,endpoints,ingress,configmap,secret,serviceaccount -n default -o yaml > default-services-and-support.yaml || true
# edit as above
kubectl apply -f default-services-and-support.yaml
kubectl delete svc,endpoints,ingress,configmap,secret,serviceaccount -n default --all
  1. If needed, create the new namespace first:
cat <<'EOF' | kubectl apply -f -
apiVersion: v1
kind: Namespace
metadata:
name: team-a
EOF
  1. Verification
kubectl get pods -n default
Automation
#!/usr/bin/env bash
# Remediate CBP C3.2: ensure no workloads run in the default namespace.
# Runs from any machine with kubectl access.

set -euo pipefail

echo "=== Checking kubectl context ==="
kubectl cluster-info >/dev/null

# 1. List all Pod-owning controllers in default
echo "=== Discovering controllers in 'default' namespace ==="
deploys=$(kubectl get deploy -n default -o name || true)
sts=$(kubectl get statefulset -n default -o name || true)
daemonsets=$(kubectl get daemonset -n default -o name || true)
jobs=$(kubectl get job -n default -o name || true)
cronjobs=$(kubectl get cronjob -n default -o name || true)
replicasets=$(kubectl get rs -n default -o name || true)
replicationcontrollers=$(kubectl get rc -n default -o name || true)

controllers=()
for c in $deploys $sts $daemonsets $jobs $cronjobs $replicasets $replicationcontrollers; do
controllers+=("$c")
done

if [ ${#controllers[@]} -eq 0 ]; then
echo "No controllers found in 'default' namespace."
else
echo "Found controllers in 'default':"
printf ' %s\n' "${controllers[@]}"
fi

# 2. Determine target namespace
TARGET_NS="${TARGET_NAMESPACE:-workloads}"
if [ -z "${TARGET_NS}" ]; then
echo "TARGET_NAMESPACE is empty; refusing to continue." >&2
exit 1
fi

# 3. Create target namespace if not present
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

# 4. Move controllers from default to target namespace
# This deletes the object in default and recreates it in TARGET_NS
# while preserving spec via export-like behavior.
if [ ${#controllers[@]} -gt 0 ]; then
echo "=== Moving controllers from 'default' to '${TARGET_NS}' ==="
fi

for c in "${controllers[@]}"; do
kind=${c%%/*}
name=${c##*/}
echo "Processing ${kind}/${name}"

# Export resource YAML without cluster-scoped metadata
tmpfile=$(mktemp)
kubectl get "${kind}" "${name}" -n default -o json \
| jq '
del(
.metadata.namespace,
.metadata.uid,
.metadata.resourceVersion,
.metadata.selfLink,
.metadata.creationTimestamp,
.metadata.generation,
.metadata.managedFields,
.metadata.ownerReferences,
.status
)
| .metadata.annotations |= ( . // {} | del(."kubectl.kubernetes.io/last-applied-configuration") )
' \
| kubectl neat 2>/dev/null || true > "${tmpfile}" || {
# fallback if kubectl-neat is not installed
kubectl get "${kind}" "${name}" -n default -o yaml \
| sed -e '/^ resourceVersion:/d' \
-e '/^ uid:/d' \
-e '/^ selfLink:/d' \
-e '/^ creationTimestamp:/d' \
-e '/^ generation:/d' \
-e '/^ managedFields:/,/^[^ ]/d' \
-e '/^status:/,/^[^ ]/d' \
> "${tmpfile}"
}

# Ensure namespace is set to TARGET_NS
if ! grep -q '^ namespace:' "${tmpfile}"; then
# Insert namespace under metadata:
awk -v ns="${TARGET_NS}" '
/^metadata:/ { print; print " namespace: " ns; next }
{ print }
' "${tmpfile}" > "${tmpfile}.ns" && mv "${tmpfile}.ns" "${tmpfile}"
else
sed -i "s/^ namespace: .*/ namespace: ${TARGET_NS}/" "${tmpfile}"
fi

echo " Applying ${kind}/${name} to namespace '${TARGET_NS}'"
kubectl apply -n "${TARGET_NS}" -f "${tmpfile}"

echo " Deleting original ${kind}/${name} from 'default'"
kubectl delete "${kind}" "${name}" -n default --ignore-not-found=true

rm -f "${tmpfile}"
done

# 5. Handle any remaining naked Pods in default
echo "=== Handling standalone Pods in 'default' namespace ==="
pods_json=$(kubectl get pods -n default -o json || echo '{"items":[]}')
pod_count=$(echo "${pods_json}" | jq '.items | length')
if [ "${pod_count}" -gt 0 ]; then
echo "Found ${pod_count} standalone Pod(s) in 'default'. Recreating in '${TARGET_NS}'."
echo "${pods_json}" | jq -c '.items[]' | while read -r pod; do
name=$(echo "${pod}" | jq -r '.metadata.name')
tmpfile=$(mktemp)

echo "${pod}" \
| jq '
del(
.metadata.namespace,
.metadata.uid,
.metadata.resourceVersion,
.metadata.selfLink,
.metadata.creationTimestamp,
.metadata.generation,
.metadata.managedFields,
.metadata.ownerReferences,
.status
)
| .metadata.annotations |= ( . // {} | del(."kubectl.kubernetes.io/last-applied-configuration") )
' > "${tmpfile}"

# Set namespace to TARGET_NS
if ! grep -q '^ namespace:' "${tmpfile}"; then
awk -v ns="${TARGET_NS}" '
/^metadata:/ { print; print " namespace: " ns; next }
{ print }
' "${tmpfile}" > "${tmpfile}.ns" && mv "${tmpfile}.ns" "${tmpfile}"
else
sed -i "s/^ namespace: .*/ namespace: ${TARGET_NS}/" "${tmpfile}"
fi

echo " Recreating Pod/${name} in '${TARGET_NS}'"
# Best-effort delete and recreate
kubectl delete pod "${name}" -n default --ignore-not-found=true --grace-period=0 --force || true
kubectl apply -n "${TARGET_NS}" -f "${tmpfile}" || {
echo " Warning: failed to recreate Pod/${name} in '${TARGET_NS}'."
}
rm -f "${tmpfile}"
done
else
echo "No standalone Pods found in 'default'."
fi

# 6. Verification (adapted from audit command)
echo "=== Verification: ensuring no workloads in 'default' namespace ==="
{ 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)"'