Skip to main content

The Default Namespace Should Not Be Used

More Info:​

Kubernetes provides a default namespace, where objects are placed if no namespace is specified for them. Placing objects in this namespace makes application of RBAC and other controls more difficult.

Risk Level​

Low

Address​

Security

Compliance Standards​

  • APRA CPS 234 (Australia)
  • BSI C5 (Germany)
  • Brazil LGPD
  • CCPA / CPRA (California)
  • CIS AKS
  • CIS Critical Security Controls v8
  • CMMC 2.0
  • CSA Cloud Controls Matrix v4
  • DPDPA
  • Digital Operational Resilience Act (EU)
  • Essential 8
  • ISO/IEC 27017
  • ISO/IEC 27018
  • ISO/IEC 27701
  • KSA PDPL
  • MAS Technology Risk Management (Singapore)
  • MITRE ATT&CK (Cloud)
  • NIS2 Directive
  • NIST SP 800-171
  • NYDFS 23 NYCRR 500
  • SWIFT Customer Security Controls Framework
  • Sarbanes-Oxley IT General Controls
  • UK NCSC Cyber Assessment Framework

Triage and Remediation​

Remediation​

Manual Steps
  1. List all non-system resources in the default namespace
    Run on: any machine with kubectl access

    kubectl get all -n default
  2. Create target namespaces for workloads (if they don’t already exist)
    Run on: any machine with kubectl access
    Repeat for each logical application or component:

    kubectl create namespace my-namespace

    (If the namespace exists, this will fail harmlessly; you can ignore the error.)

  3. Migrate workload resources out of the default namespace
    Run on: any machine with kubectl access
    For each resource in default that you want to keep (example: deployment my-app to my-namespace):

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

    kubectl delete deployment my-app -n default

    Repeat the same pattern for other namespaced resource types in default (e.g., service, statefulset, daemonset, job, cronjob, configmap, secret, pvc), adjusting the kind and name each time:

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

    kubectl delete service my-service -n default
  4. Update any manifests or automation to stop using the default namespace
    Run on: any machine with access to your manifest/CI/CD repos

    • Edit Kubernetes manifests to either:
      • Remove explicit namespace: default fields, or
      • Replace them with the appropriate namespace: my-namespace.
    • Update CI/CD or scripts that call kubectl with -n default (or no -n when they rely on default) to use the correct namespaces, for example:
      kubectl apply -n my-namespace -f my-app-deployment.yaml
  5. Optionally restrict or monitor use of the default namespace
    Run on: any machine with kubectl access

    • Create an admission control policy (e.g., a ValidatingWebhookConfiguration or Gatekeeper/kyverno policy) to alert or block new objects in the default namespace.
    • This is cluster- and tool-specific and must be designed according to your existing policy tooling.
  6. Verify the default namespace is no longer used for 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. Identify all user workloads currently in the default namespace (excluding the built‑in kubernetes service):
kubectl get all -n default
kubectl get configmaps,secrets,ingress,networkpolicies,serviceaccounts,role,rolebinding -n default
  1. Create a replacement namespace (example name prod-apps; adjust as needed):
cat << 'EOF' | kubectl apply -f -
apiVersion: v1
kind: Namespace
metadata:
name: prod-apps
EOF
  1. For each workload type in default, re‑apply its manifest into the new namespace, then delete from default. Below are examples; repeat for all relevant resources after exporting their current YAML.

Deployments:

kubectl get deployment -n default -o yaml \
| sed 's/namespace: default/namespace: prod-apps/' \
| kubectl apply -f -
kubectl delete deployment -n default --all

StatefulSets:

kubectl get statefulset -n default -o yaml \
| sed 's/namespace: default/namespace: prod-apps/' \
| kubectl apply -f -
kubectl delete statefulset -n default --all

DaemonSets:

kubectl get daemonset -n default -o yaml \
| sed 's/namespace: default/namespace: prod-apps/' \
| kubectl apply -f -
kubectl delete daemonset -n default --all

Services (excluding the built‑in kubernetes service):

kubectl get service -n default --field-selector metadata.name!=kubernetes -o yaml \
| sed 's/namespace: default/namespace: prod-apps/' \
| kubectl apply -f -
kubectl delete service -n default --all --ignore-not-found

Ingress:

kubectl get ingress -n default -o yaml \
| sed 's/namespace: default/namespace: prod-apps/' \
| kubectl apply -f -
kubectl delete ingress -n default --all

ConfigMaps and Secrets (non‑service‑account):

kubectl get configmap -n default -o yaml \
| sed 's/namespace: default/namespace: prod-apps/' \
| kubectl apply -f -
kubectl delete configmap -n default --all

kubectl get secret -n default --field-selector 'type!=kubernetes.io/service-account-token' -o yaml \
| sed 's/namespace: default/namespace: prod-apps/' \
| kubectl apply -f -
kubectl delete secret -n default --all --field-selector 'type!=kubernetes.io/service-account-token'

ServiceAccounts, Roles, RoleBindings (if present and not cluster‑default ones you intentionally keep):

kubectl get serviceaccount -n default -o yaml \
| sed 's/namespace: default/namespace: prod-apps/' \
| kubectl apply -f -
kubectl delete serviceaccount -n default --all

kubectl get role -n default -o yaml \
| sed 's/namespace: default/namespace: prod-apps/' \
| kubectl apply -f -
kubectl delete role -n default --all

kubectl get rolebinding -n default -o yaml \
| sed 's/namespace: default/namespace: prod-apps/' \
| kubectl apply -f -
kubectl delete rolebinding -n default --all
  1. Ensure future workloads use non‑default namespaces by updating your manifests’ metadata:
metadata:
name: my-app
namespace: prod-apps
  1. 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
# Purpose: Ensure the default namespace is not used for user workloads.
# Runs on: any machine with kubectl access and current-context set to the target cluster.

set -euo pipefail

# CONFIGURATION: list target namespaces and resource types to migrate.
# Edit as needed; safe to re-run.
TARGET_NAMESPACE_PREFIX="app-"
TARGET_INFRA_NAMESPACE="infra-system"
RESOURCE_TYPES=(
deployments
statefulsets
daemonsets
cronjobs
jobs
pods
services
configmaps
secrets
serviceaccounts
roles
rolebindings
)

kubectl_get() {
kubectl "$@" 2>/dev/null
}

ensure_namespace() {
local ns="$1"
if ! kubectl_get get namespace "${ns}" -o name >/dev/null; then
kubectl create namespace "${ns}"
fi
}

move_resource() {
local type="$1" name="$2" src_ns="$3" dst_ns="$4"

# Skip if already gone (idempotence)
if ! kubectl_get get "${type}" "${name}" -n "${src_ns}" -o name >/dev/null; then
return 0
fi

# Export, rewrite namespace, re-apply into target namespace
kubectl_get get "${type}" "${name}" -n "${src_ns}" -o yaml \
| sed -E "s/^( )?namespace: ${src_ns}$/\1namespace: ${dst_ns}/" \
| kubectl apply -f -

# Delete from default after successful apply
kubectl delete "${type}" "${name}" -n "${src_ns}" --ignore-not-found
}

echo "[INFO] Ensuring target namespaces exist..."
# Example strategy: one namespace per app (deployment) and a shared infra namespace.
ensure_namespace "${TARGET_INFRA_NAMESPACE}"

# Create namespaces based on deployment names (app-<name>)
for deploy in $(kubectl_get get deployments -n default -o jsonpath='{.items[*].metadata.name}'); do
ensure_namespace "${TARGET_NAMESPACE_PREFIX}${deploy}"
done

echo "[INFO] Migrating resources from 'default' namespace..."

# Migrate workload and infra resources
for type in "${RESOURCE_TYPES[@]}"; do
names=$(kubectl_get get "${type}" -n default -o jsonpath='{.items[*].metadata.name}')
for name in $names; do
# Decide destination namespace:
# - If there is a deployment with the same name, use app-<name>
# - Otherwise, consider it infra and move to infra-system
if kubectl_get get deployment "${name}" -n default -o name >/dev/null 2>&1; then
dst_ns="${TARGET_NAMESPACE_PREFIX}${name}"
else
dst_ns="${TARGET_INFRA_NAMESPACE}"
fi

ensure_namespace "${dst_ns}"
echo "[INFO] Moving ${type}/${name} from default -> ${dst_ns}"
move_resource "${type}" "${name}" default "${dst_ns}" || {
echo "[WARN] Failed to move ${type}/${name} from default. Skipping."
}
done
done

echo "[INFO] Cleaning up completed Jobs in default (if any)..."
kubectl delete jobs -n default --field-selector status.successful>0 --ignore-not-found >/dev/null 2>&1 || true

echo "[INFO] Verification: checking that default namespace is unused (except core service/kubernetes)..."
output=$(kubectl_get get all -n default --no-headers 2>/dev/null | grep -v '^service\s\+kubernetes\s' || true)
if [ -z "$output" ]; then
echo "DEFAULT_NAMESPACE_UNUSED"
exit 0
else
echo "DEFAULT_NAMESPACE_IN_USE"
echo "[DETAILS]"
echo "${output}"
exit 1
fi