The Default Namespace Should Not Be Used
More Info:​
Placing resources in the default namespace prevents applying tailored policies and RBAC boundaries. New resources should be created in specific, purpose-built namespaces.
Risk Level​
Low
Address​
Security
Compliance Standards​
- CIS GKE
Triage and Remediation​
- Remediation
Remediation​
Manual Steps
-
List all non-system workloads in the default namespace
- Run on: any machine with kubectl access
kubectl get all -n default -
Decide target namespaces and create them if needed
- Run on: any machine with kubectl access
- Replace
team-a,team-bwith meaningful names for your org:
kubectl create namespace team-akubectl create namespace team-b- Repeat as required for each logical application/team boundary.
-
For each non-system resource in
default, export its manifest and re‑create it in the correct namespace- Run on: any machine with kubectl access
- Example for a deployment
myappyou want inteam-a:
kubectl get deployment myapp -n default -o yaml \| sed 's/namespace: default/namespace: team-a/' \| sed '/resourceVersion:/d;/uid:/d;/creationTimestamp:/d;/selfLink:/d;/managedFields:/d' \| kubectl apply -f -kubectl delete deployment myapp -n default- Use the same pattern for other types (e.g.,
svc,statefulset,daemonset,job,cronjob), adjusting the kind and name:
# example for a servicekubectl get service myapp-svc -n default -o yaml \| sed 's/namespace: default/namespace: team-a/' \| sed '/resourceVersion:/d;/uid:/d;/creationTimestamp:/d;/selfLink:/d;/managedFields:/d' \| kubectl apply -f -kubectl delete service myapp-svc -n default -
For workloads that must remain cluster-scoped or in
kube-system, do not move them- Run on: any machine with kubectl access
- Before moving anything, confirm it is not a core component:
kubectl get all -A | grep -E 'kube-system|kube-public|kube-node-lease'- If you are unsure whether an object in
defaultis application-specific or required by an addon, consult your platform documentation or application owners before moving it.
-
Update CI/CD, Helm values, and manifests so new resources are not created in
default- In all deployment manifests, ensure either:
metadata.namespace: <desired-namespace>is set, or- You deploy with an explicit namespace flag, for example:
kubectl apply -n team-a -f app.yaml
- For Helm charts, set a non-default namespace:
helm install myapp ./chart-path --namespace team-a --create-namespace
- In all deployment manifests, ensure either:
-
Verification: confirm the default namespace has no non-system resources
- Run on: any machine with kubectl access
output=$(kubectl get all -n default --no-headers 2>/dev/null | grep -v '^service\s\+kubernetes\s' || true)if [ -z "$output" ]; then echo "DEFAULT_NAMESPACE_UNUSED"; else echo "DEFAULT_NAMESPACE_IN_USE"; fi
Using kubectl
On any machine with kubectl access:
- List what is currently in the default namespace (for planning and decisions):
kubectl get all -n default
kubectl get configmap,secret,sa,role,rolebinding,networkpolicy -n default
- Create purpose-specific namespaces (example:
team-aandplatform):
cat << 'EOF' > namespaces.yaml
apiVersion: v1
kind: Namespace
metadata:
name: team-a
---
apiVersion: v1
kind: Namespace
metadata:
name: platform
EOF
kubectl apply -f namespaces.yaml
- For each workload in
default, re-create it in an appropriate namespace. Example for a deployment and service currently in default:
Export manifests (edit to adjust metadata.namespace and any references before applying):
kubectl get deploy my-app -n default -o yaml > my-app-deploy.yaml
kubectl get svc my-app -n default -o yaml > my-app-svc.yaml
Edit both files:
- Change
metadata.namespace: defaulttometadata.namespace: team-a - Remove cluster-assigned fields (
status,metadata.resourceVersion,metadata.uid,metadata.creationTimestamp,metadata.managedFields) and anyownerReferencesthat no longer apply.
Then apply in the target namespace:
kubectl apply -f my-app-deploy.yaml
kubectl apply -f my-app-svc.yaml
- After new workloads are running in non-default namespaces and you have validated functionality, delete objects from the default namespace (except the
kubernetesservice):
# Delete non-kubernetes services in default
kubectl get svc -n default --no-headers | awk '$1 != "kubernetes" {print $1}' | \
xargs -r kubectl delete svc -n default
# Delete deployments, replicasets, pods, jobs, cronjobs, daemonsets, statefulsets
kubectl delete deploy,rs,pod,job,cronjob,ds,sts --all -n default
# Optionally clean up non-system configmaps/secrets/serviceaccounts, after review:
kubectl delete configmap --all -n default
kubectl delete secret --all -n default
kubectl delete sa --all -n default
kubectl delete role,rolebinding,networkpolicy --all -n default
-
Update your workflows so new resources are not created in
default, for example by always specifying-n <namespace>ormetadata.namespacein manifests, and by configuring tooling defaults. -
Verification (same logic as the audit):
output=$(kubectl get all -n default --no-headers 2>/dev/null | grep -v '^service\s\+kubernetes\s' || true)
if [ -z "$output" ]; then echo "DEFAULT_NAMESPACE_UNUSED"; else echo "DEFAULT_NAMESPACE_IN_USE"; fi
Automation
#!/usr/bin/env bash
set -euo pipefail
# This script requires:
# - kubectl configured and authorized
# - jq and kubectl-neat (optional but recommended for cleaner manifests)
# CONFIGURATION: map old namespace "default" -> new target namespaces by label selector
# Each entry: "label_selector:new_namespace"
# Example: move all pods with app=web to namespace web, etc.
# Adjust to your environment and policies.
MAPPINGS=(
"app=web:web"
"app=api:api"
)
# Helper: ensure a namespace exists
ensure_namespace() {
local ns="$1"
if ! kubectl get namespace "${ns}" >/dev/null 2>&1; then
kubectl create namespace "${ns}"
fi
}
# Helper: move namespaced resources from default to a given namespace based on label selector
migrate_resources() {
local selector="$1"
local target_ns="$2"
ensure_namespace "${target_ns}"
# List all resource types that are namespaced
local types
types=$(kubectl api-resources --namespaced=true --verbs=list -o name)
for t in $types; do
# Skip resources that shouldn't be moved generically
case "$t" in
events.k8s.io/*|events|leases.coordination.k8s.io|leases.coordination.k8s.io/*) continue ;;
esac
# Get resources in default matching selector
local names
names=$(kubectl get "$t" -n default -l "${selector}" -o jsonpath='{range .items[*]}{.metadata.name}{"\n"}{end}' 2>/dev/null || true)
[ -z "$names" ] && continue
for name in $names; do
# Export resource manifest
if command -v kubectl-neat >/dev/null 2>&1; then
manifest=$(kubectl get "$t" "$name" -n default -o yaml | kubectl-neat)
else
manifest=$(kubectl get "$t" "$name" -n default -o yaml)
fi
# Patch namespace and remove fields that prevent re-create
cleaned=$(printf '%s\n' "$manifest" \
| sed -E '/^(status:|metadata:)/,/^[^ ]/!b' \
| sed -E '/^ (creationTimestamp|resourceVersion|uid|selfLink|generation|managedFields|ownerReferences):/d' \
| sed -E 's/namespace: default/namespace: '"${target_ns}"'/' )
# Apply in target namespace and delete original
printf '%s\n' "$cleaned" | kubectl apply -f -
kubectl delete "$t" "$name" -n default --ignore-not-found
done
done
}
# MAIN
# 1. Migrate resources from default namespace according to mappings
for entry in "${MAPPINGS[@]}"; do
IFS=":" read -r selector ns <<<"$entry"
migrate_resources "$selector" "$ns"
done
# 2. Final verification: ensure default namespace has no workload resources
output=$(kubectl get all -n default --no-headers 2>/dev/null | grep -v '^service\s\+kubernetes\s' || true)
if [ -z "$output" ]; then
echo "DEFAULT_NAMESPACE_UNUSED"
else
echo "DEFAULT_NAMESPACE_IN_USE"
echo "The following resources remain in the default namespace:"
echo "$output"
echo "Review and decide manually where each should be moved, then re-run this script after updating MAPPINGS."
fi