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
Remediation
Manual Steps
-
List all workloads in the
defaultnamespace (run on any machine with kubectl access):kubectl get all -n default -
For each Deployment/StatefulSet/DaemonSet/Job/CronJob in
default, export its manifest to a file and edit the namespace (run on any machine with kubectl access). Example for a Deployment namedmy-app:kubectl get deploy my-app -n default -o yaml > my-app.yamlEdit
my-app.yamland change:metadata:namespace: defaultto:
metadata:namespace: my-tenant-namespaceEnsure
my-tenant-namespaceexists:kubectl create namespace my-tenant-namespace -
Apply the updated workload manifest in the new namespace (run on any machine with kubectl access):
kubectl apply -f my-app.yaml -
Once you confirm the workload is Running in the new namespace, delete the old object from
default(run on any machine with kubectl access). Example:kubectl get deploy my-app -n my-tenant-namespacekubectl delete deploy my-app -n default -
Repeat steps 2–4 for all remaining controllers and standalone Pods in
default. For a standalone Pod calledmy-pod:kubectl get pod my-pod -n default -o yaml > my-pod.yaml# edit namespace: default -> my-tenant-namespacekubectl apply -f my-pod.yamlkubectl get pod my-pod -n my-tenant-namespacekubectl delete pod my-pod -n default -
Verification (run on any machine with kubectl access):
{ kubectl get pods -n default -o jsonkubectl 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
podCount=0andis_compliant=true.
Using kubectl
On any machine with kubectl access:
- Identify workloads currently in the
defaultnamespace
kubectl get all -n default
- For each workload type, export its manifest from
defaultand prepare it for a new namespace. Replacemy-namespacewith your target namespace name (which should already exist or be created ahead of time):
Deployments:
kubectl get deployment -n default -o name | while read obj; do
name="${obj##*/}"
kubectl get "$obj" -n default -o yaml \
| sed '/namespace: default/d' \
| sed "s/^metadata:/metadata:\n namespace: my-namespace/" \
> "deployment-${name}.yaml"
done
StatefulSets:
kubectl get statefulset -n default -o name | while read obj; do
name="${obj##*/}"
kubectl get "$obj" -n default -o yaml \
| sed '/namespace: default/d' \
| sed "s/^metadata:/metadata:\n namespace: my-namespace/" \
> "statefulset-${name}.yaml"
done
DaemonSets:
kubectl get daemonset -n default -o name | while read obj; do
name="${obj##*/}"
kubectl get "$obj" -n default -o yaml \
| sed '/namespace: default/d' \
| sed "s/^metadata:/metadata:\n namespace: my-namespace/" \
> "daemonset-${name}.yaml"
done
Jobs:
kubectl get job -n default -o name | while read obj; do
name="${obj##*/}"
kubectl get "$obj" -n default -o yaml \
| sed '/namespace: default/d' \
| sed "s/^metadata:/metadata:\n namespace: my-namespace/" \
> "job-${name}.yaml"
done
CronJobs:
kubectl get cronjob -n default -o name | while read obj; do
name="${obj##*/}"
kubectl get "$obj" -n default -o yaml \
| sed '/namespace: default/d' \
| sed "s/^metadata:/metadata:\n namespace: my-namespace/" \
> "cronjob-${name}.yaml"
done
ReplicaSets (if you have standalone ones you want to keep):
kubectl get rs -n default -o name | while read obj; do
name="${obj##*/}"
kubectl get "$obj" -n default -o yaml \
| sed '/namespace: default/d' \
| sed "s/^metadata:/metadata:\n namespace: my-namespace/" \
> "replicaset-${name}.yaml"
done
Services, ConfigMaps, Secrets, and ServiceAccounts that belong with those workloads should also be moved:
Services:
kubectl get svc -n default -o name | while read obj; do
name="${obj##*/}"
kubectl get "$obj" -n default -o yaml \
| sed '/namespace: default/d' \
| sed "s/^metadata:/metadata:\n namespace: my-namespace/" \
> "svc-${name}.yaml"
done
ConfigMaps:
kubectl get configmap -n default -o name | while read obj; do
name="${obj##*/}"
kubectl get "$obj" -n default -o yaml \
| sed '/namespace: default/d' \
| sed "s/^metadata:/metadata:\n namespace: my-namespace/" \
> "configmap-${name}.yaml"
done
Secrets (exclude built‑ins you don’t want to move, adjust selector as needed):
kubectl get secret -n default --no-headers | awk '{print $1}' | while read name; do
kubectl get secret "$name" -n default -o yaml \
| sed '/namespace: default/d' \
| sed "s/^metadata:/metadata:\n namespace: my-namespace/" \
> "secret-${name}.yaml"
done
ServiceAccounts (excluding the default one):
kubectl get sa -n default --no-headers | awk '$1!="default"{print $1}' | while read name; do
kubectl get sa "$name" -n default -o yaml \
| sed '/namespace: default/d' \
| sed "s/^metadata:/metadata:\n namespace: my-namespace/" \
> "sa-${name}.yaml"
done
- Apply the generated manifests into the new namespace
kubectl apply -f . -n my-namespace
(From a directory containing only the YAMLs you intend to move.)
- After confirming the workloads are running correctly in
my-namespace, delete them fromdefault.
Deployments, StatefulSets, DaemonSets, Jobs, CronJobs:
kubectl delete deployment,statefulset,daemonset,job,cronjob,rs,svc,configmap,secret,sa \
-n default --all
If you need more control, delete selected objects by name instead of --all.
- Verification (from 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)"'
Automation
#!/usr/bin/env bash
set -euo pipefail
# Automation for: No Workloads Should Run In The default Namespace (AKS)
# Runs on: any machine with kubectl access and current-context pointing to the target cluster.
# CONFIGURATION: set a non-default target namespace where workloads will be moved.
TARGET_NAMESPACE="workloads"
echo "==> Ensuring target namespace '${TARGET_NAMESPACE}' exists"
if ! kubectl get namespace "${TARGET_NAMESPACE}" >/dev/null 2>&1; then
kubectl create namespace "${TARGET_NAMESPACE}"
fi
echo "==> Checking for pods in the 'default' namespace"
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."
else
echo "Found ${POD_COUNT} pod(s) in 'default' namespace. Moving workloads to '${TARGET_NAMESPACE}'."
# Move Deployments
echo "-> Handling Deployments"
kubectl get deploy -n default -o name | while read -r deploy; do
[ -z "${deploy}" ] && continue
echo " - Moving ${deploy}"
kubectl get "${deploy}" -n default -o yaml \
| sed -e "s/namespace: default/namespace: ${TARGET_NAMESPACE}/" \
| kubectl apply -n "${TARGET_NAMESPACE}" -f -
kubectl delete "${deploy}" -n default --ignore-not-found
done
# Move StatefulSets
echo "-> Handling StatefulSets"
kubectl get statefulset -n default -o name | while read -r sts; do
[ -z "${sts}" ] && continue
echo " - Moving ${sts}"
kubectl get "${sts}" -n default -o yaml \
| sed -e "s/namespace: default/namespace: ${TARGET_NAMESPACE}/" \
| kubectl apply -n "${TARGET_NAMESPACE}" -f -
kubectl delete "${sts}" -n default --ignore-not-found
done
# Move DaemonSets
echo "-> Handling DaemonSets"
kubectl get daemonset -n default -o name | while read -r ds; do
[ -z "${ds}" ] && continue
echo " - Moving ${ds}"
kubectl get "${ds}" -n default -o yaml \
| sed -e "s/namespace: default/namespace: ${TARGET_NAMESPACE}/" \
| kubectl apply -n "${TARGET_NAMESPACE}" -f -
kubectl delete "${ds}" -n default --ignore-not-found
done
# Move ReplicaSets (only those not owned by a higher-level controller)
echo "-> Handling standalone ReplicaSets"
kubectl get rs -n default -o jsonpath='{range .items[?(@.metadata.ownerReferences==null)]}{.metadata.name}{"\n"}{end}' \
| while read -r rs; do
[ -z "${rs}" ] && continue
echo " - Moving replicaset/${rs}"
kubectl get rs "${rs}" -n default -o yaml \
| sed -e "s/namespace: default/namespace: ${TARGET_NAMESPACE}/" \
| kubectl apply -n "${TARGET_NAMESPACE}" -f -
kubectl delete rs "${rs}" -n default --ignore-not-found
done
# Move standalone Pods (those not controlled by a workload controller)
echo "-> Handling standalone Pods"
kubectl get pods -n default -o jsonpath='{range .items[?(@.metadata.ownerReferences==null)]}{.metadata.name}{"\n"}{end}' \
| while read -r pod; do
[ -z "${pod}" ] && continue
echo " - Moving pod/${pod}"
kubectl get pod "${pod}" -n default -o yaml \
| sed -e "/^status:$/,\$d" \
-e "s/namespace: default/namespace: ${TARGET_NAMESPACE}/" \
| kubectl apply -n "${TARGET_NAMESPACE}" -f -
kubectl delete pod "${pod}" -n default --ignore-not-found
done
fi
echo "==> Verification (CIS CBP C3.2 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 "==> Completed"