Skip to main content

The Default Namespace Should Not Be Used

More Info:​

Using the default namespace for user workloads makes it harder to apply access controls and network policies. Place workloads in dedicated namespaces instead.

Risk Level​

Low

Address​

Security

Compliance Standards​

  • CIS AKS

Triage and Remediation​

Remediation​

Manual Steps
  1. List all user resources in the default namespace

    • Run on: any machine with kubectl access
    kubectl get all -n default
    kubectl get configmap,secret,ingress,priorityclass,networkpolicy,serviceaccount,role,rolebinding -n default
  2. Create one or more dedicated namespaces for your workloads (per app/team as needed)

    • Run on: any machine with kubectl access
    kubectl create namespace my-app-namespace
    # repeat with other namespace names as required
  3. Migrate workload controllers (Deployment/StatefulSet/DaemonSet/Job/CronJob) out of default

    • Run on: any machine with kubectl access
    • For each controller in default, choose a target namespace and run:
    # Example for a Deployment named my-app
    kubectl get deployment my-app -n default -o yaml \
    | sed 's/namespace: default/namespace: my-app-namespace/' \
    | kubectl apply -f -

    kubectl delete deployment my-app -n default
    • Use the same pattern for statefulset, daemonset, job, and cronjob resources.
  4. Migrate Services, Ingresses, and supporting objects (ConfigMaps, Secrets, ServiceAccounts, Roles, RoleBindings, NetworkPolicies)

    • Run on: any machine with kubectl access
    • For each resource in default, re-apply into the target namespace and then delete from default. Examples:
    # Service
    kubectl get service my-service -n default -o yaml \
    | sed 's/namespace: default/namespace: my-app-namespace/' \
    | kubectl apply -f -
    kubectl delete service my-service -n default

    # ConfigMap
    kubectl get configmap my-config -n default -o yaml \
    | sed 's/namespace: default/namespace: my-app-namespace/' \
    | kubectl apply -f -
    kubectl delete configmap my-config -n default

    # Secret
    kubectl get secret my-secret -n default -o yaml \
    | sed 's/namespace: default/namespace: my-app-namespace/' \
    | kubectl apply -f -
    kubectl delete secret my-secret -n default

    # Ingress
    kubectl get ingress my-ingress -n default -o yaml \
    | sed 's/namespace: default/namespace: my-app-namespace/' \
    | kubectl apply -f -
    kubectl delete ingress my-ingress -n default
    • Repeat for other names/resources until kubectl get all -n default only shows the kubernetes Service.
  5. Update clients/manifests to stop using the default namespace

    • Run on: any machine with kubectl access
    • Ensure your manifests declare the correct namespace:
    metadata:
    name: my-app
    namespace: my-app-namespace
    • If you use a kubeconfig context, set a non-default namespace:
    kubectl config set-context --current --namespace=my-app-namespace
  6. Verification (default namespace has no user workloads)

    • 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:

  1. Create replacement namespaces for workloads currently in default
    Example (adjust names as needed):

    kubectl create namespace app-frontend
    kubectl create namespace app-backend

    Or via manifest:

    cat << 'EOF' | kubectl apply -f -
    apiVersion: v1
    kind: Namespace
    metadata:
    name: app-frontend
    ---
    apiVersion: v1
    kind: Namespace
    metadata:
    name: app-backend
    EOF
  2. List all user resources in the default namespace (to plan migration):

    kubectl get all -n default -o wide
  3. For each user workload in default, reapply it into a non-default namespace, then delete the original.
    Example for a deployment:

    # Recreate in new namespace
    kubectl get deployment my-app -n default -o yaml \
    | sed 's/namespace: default/namespace: app-frontend/' \
    | kubectl apply -f -

    # Delete from default namespace
    kubectl delete deployment my-app -n default

    Example for a service:

    kubectl get service my-app-svc -n default -o yaml \
    | sed 's/namespace: default/namespace: app-frontend/' \
    | kubectl apply -f -

    kubectl delete service my-app-svc -n default

    Example for a configmap:

    kubectl get configmap my-config -n default -o yaml \
    | sed 's/namespace: default/namespace: app-frontend/' \
    | kubectl apply -f -

    kubectl delete configmap my-config -n default
  4. For resources that may not have an explicit namespace: field (e.g., some autogenerated manifests), you can set it during reapply:

    kubectl get deployment my-other-app -n default -o yaml \
    | sed '/^ namespace: /d' \
    | sed 's/^metadata:$/metadata:\n namespace: app-backend/' \
    | kubectl apply -f -

    kubectl delete deployment my-other-app -n default
  5. Repeat step 3 for all user-defined objects in default (deployments, statefulsets, daemonsets, jobs, cronjobs, services, ingresses, configmaps, secrets, PVCs, etc.), making sure interdependent resources are moved into the same new namespace.

  6. Verification (CIS-aligned):

    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
#
# Purpose:
# Ensure the "default" namespace is not used for user workloads by:
# - Creating a target namespace (if it does not exist)
# - Moving namespaced workloads out of "default"
# - Deleting the originals from "default"
# - Verifying no non-kubernetes service resources remain in "default"
#
# Requirements:
# - Run on any machine with kubectl access and appropriate RBAC.
# - kubectl must be configured (KUBECONFIG or in-cluster config).
#
# Usage:
# ./migrate-default-namespace.sh my-namespace
#
# Notes:
# - Idempotent: safe to re-run; resources already migrated will be skipped.
# - Only handles namespaced workload-style resources and services.
# - The script assumes that anything in "default" (except service/kubernetes)
# should be moved. Review before running in production.

set -euo pipefail

TARGET_NS="${1:-}"
if [ -z "$TARGET_NS" ]; then
echo "ERROR: target namespace argument is required."
echo "Usage: $0 <target-namespace>"
exit 1
fi

echo ">>> Ensuring target namespace '$TARGET_NS' exists"
if ! kubectl get namespace "$TARGET_NS" >/dev/null 2>&1; then
kubectl create namespace "$TARGET_NS"
else
echo "Namespace '$TARGET_NS' already exists; continuing."
fi

echo ">>> Detecting user-defined resources in 'default' namespace"

# Resource kinds to migrate; adjust as needed for your cluster
RESOURCE_KINDS=(
deployments
statefulsets
daemonsets
replicasets
jobs
cronjobs
pods
services
configmaps
secrets
serviceaccounts
ingresses
networkpolicies
roles
rolebindings
persistentvolumeclaims
)

# Build a list of "kind/name" in default excluding the built-in "kubernetes" service
mapfile -t RESOURCES_IN_DEFAULT < <(
for kind in "${RESOURCE_KINDS[@]}"; do
# Skip if kind is not supported by the API server
if ! kubectl api-resources --namespaced -o name 2>/dev/null | grep -qx "$kind"; then
continue
fi
kubectl get "$kind" -n default --no-headers 2>/dev/null | awk -v k="$kind" '
NF>=1 {
name=$1;
# Exclude core "kubernetes" service
if (!(k=="services" && name=="kubernetes")) {
print k "/" name;
}
}'
done
)

if [ "${#RESOURCES_IN_DEFAULT[@]}" -eq 0 ]; then
echo "No user-defined resources found in 'default' (other than service/kubernetes). Nothing to migrate."
else
echo "Found the following resources in 'default' to migrate to '$TARGET_NS':"
printf ' %s\n' "${RESOURCES_IN_DEFAULT[@]}"

echo ">>> Migrating resources from 'default' to '$TARGET_NS'"

for rn in "${RESOURCES_IN_DEFAULT[@]}"; do
kind="${rn%%/*}"
name="${rn##*/}"

echo "Processing $kind/$name ..."

# Check if the resource already exists in the target namespace with same name and kind
if kubectl get "$kind" "$name" -n "$TARGET_NS" >/dev/null 2>&1; then
echo " Skipping: $kind/$name already exists in namespace '$TARGET_NS'."
# Optionally, delete the source if desired. For safety, we leave it for manual review.
continue
fi

# Export YAML, rewrite namespace, apply to target, then delete from default
if ! kubectl get "$kind" "$name" -n default -o yaml >/dev/null 2>&1; then
echo " WARNING: $kind/$name no longer exists in 'default'; skipping."
continue
fi

echo " Migrating to '$TARGET_NS' ..."
kubectl get "$kind" "$name" -n default -o yaml \
| sed -E "s/^( )?namespace: default$/\1namespace: $TARGET_NS/" \
| kubectl apply -f -

echo " Deleting original from 'default' ..."
kubectl delete "$kind" "$name" -n default --ignore-not-found
done
fi

echo ">>> Verification: checking that 'default' namespace has no user workloads"

output="$(
kubectl get all -n default --no-headers 2>/dev/null \
| grep -v '^service[[:space:]]\+kubernetes[[:space:]]' || true
)"

if [ -z "$output" ]; then
echo "DEFAULT_NAMESPACE_UNUSED"
echo "Verification passed: default namespace has no user-defined workloads."
else
echo "DEFAULT_NAMESPACE_IN_USE"
echo "Verification FAILED: resources still exist in 'default' (excluding service/kubernetes):"
echo "$output"
echo "Review and migrate or delete these remaining resources manually."
exit 2
fi