More Info:
Verifies the default namespace has no workloads so RBAC, quotas and NetworkPolicies can be scoped per tenant.Risk Level
MediumAddress
SecurityCompliance Standards
- Cloudanix Best Practice
Triage and Remediation
- Remediation
Remediation
Manual Steps
Manual Steps
-
List all workloads in the
defaultnamespace (from any machine with kubectl access)kubectl get all -n default -
Identify each controller-backed workload and its type (from any machine with kubectl access)
kubectl get deploy,sts,ds,job,cronjob,replicaset,svc,ingress -n default -o wide -
Create appropriate target namespaces (from any machine with kubectl access)
Replaceteam-a/team-bwith your desired namespace names and repeat as needed:kubectl create namespace team-a kubectl create namespace team-b -
Export and re‑apply controller-based workloads into the target namespace(s) (from any machine with kubectl access)
Example for moving a Deployment namedwebfromdefaulttoteam-a(repeat per workload, adjusting names and target namespace):# 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 -
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 addingmetadata.namespaceto their original manifest) and then delete fromdefault. Example for a standalone Pod and a Service:# 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 -
Verification: confirm
defaulthas zero Pods (from 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)"'
Using kubectl
Using kubectl
# 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
# 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
# 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
# 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
# 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
# 6) Confirm workloads are healthy in the new namespace
# Run on: any machine with kubectl access
kubectl get all -n team-a
# 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
# 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)"'
Automation
Automation
#!/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."

