Skip to main content

Create Administrative Boundaries Between Resources Using

More Info:

Use namespaces to isolate your Kubernetes objects.

Risk Level

Low

Address

Security

Compliance Standards

  • APRA CPS 234 (Australia)
  • BSI C5 (Germany)
  • Brazil LGPD
  • CCPA / CPRA (California)
  • CIS Critical Security Controls v8
  • CIS GKE
  • 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)
  • 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. Inventory existing namespaces and workloads

    • Run (on any machine with kubectl access):
      kubectl get namespaces
      kubectl get all --all-namespaces
    • Identify which applications, teams, and environments (dev/test/stage/prod) currently share a namespace (especially default).
  2. Identify isolation requirements and desired boundaries

    • With your application/ops teams, decide how you want to separate:
      • By environment: e.g. dev, stage, prod
      • By team or business unit: e.g. payments, analytics
      • By application: e.g. web-frontend, orders-service
    • Note which existing workloads in default or other shared namespaces should move into their own dedicated namespace(s).
  3. Check for objects incorrectly living in shared/default namespaces

    • List objects in default and any “catch-all” namespaces:
      kubectl get all -n default
      kubectl get all -n kube-system
    • Flag any non-platform, business workloads in kube-system (they should usually be moved) and any critical production workloads in default (they typically deserve dedicated namespaces).
  4. Design and create the required namespaces

    • Define the namespaces you need (from step 2), including labels/annotations for ownership and environment.
    • Create them:
      kubectl create namespace prod-payments
      kubectl create namespace prod-frontend
      kubectl label namespace prod-payments env=prod team=payments
      kubectl label namespace prod-frontend env=prod team=frontend
    • Adjust names and labels to match your chosen scheme.
  5. Plan and migrate workloads into appropriate namespaces

    • For each workload identified in step 3, update its manifests to set metadata.namespace to the new target namespace and adjust any in-namespace references (ConfigMaps, Secrets, Services, RoleBindings, NetworkPolicies, etc.).
    • Apply the updated manifests:
      kubectl apply -f updated-manifests/
    • Decommission the old objects from the previous namespace once the new ones are verified:
      kubectl delete -f old-manifests/
  6. Verify namespace-based boundaries are in place

    • Confirm namespaces and workload placement:
      kubectl get namespaces
      kubectl get all -n prod-payments
      kubectl get all -n prod-frontend
    • Ensure that the default namespace no longer hosts critical or multi-tenant workloads and that each major application/team/environment has a clearly defined namespace boundary.
Using kubectl
# 1. List all namespaces and their age
# Run on: any machine with kubectl access
kubectl get namespaces -o wide

What to look for (possible problems)

  • Only default, kube-system, kube-public, and kube-node-lease exist, but many applications/workloads are running → suggests everything is in default with no boundaries.
  • A single custom namespace (e.g., prod) containing all non-system workloads for different teams/environments → coarse boundary, little separation.

# 2. See which namespaces are actually used by workload objects
kubectl get all --all-namespaces

What to look for (possible problems)

  • Most or all Deployments/Pods/Services are in the default namespace.
  • Critical system-like components or third-party platforms (logging, monitoring, CI agents) running in default rather than a dedicated namespace.
  • Mixed environments (dev, test, prod) or mixed teams using the same namespace.

# 3. Show which namespaces have RoleBindings/ClusterRoleBindings
kubectl get rolebindings,clusterrolebindings --all-namespaces

What to look for (possible problems)

  • Broad, powerful RoleBindings (e.g., binding cluster-admin or wide-scoped roles) applied in a namespace that mixes unrelated workloads.
  • Lack of any RoleBindings in non-system namespaces when multiple teams share the cluster, suggesting no real separation of administrative duties.

# 4. Inspect a specific high-usage namespace (example: default)
kubectl get all -n default
kubectl get rolebindings -n default
kubectl describe namespace default

What to look for (possible problems)

  • Many unrelated applications, teams, or environments all running in default.
  • No labels/annotations that indicate intended ownership, environment, or purpose, making it hard to enforce policies and boundaries.

# 5. Correlate namespaces to application ownership using labels
kubectl get namespaces --show-labels

What to look for (possible problems)

  • No labeling strategy (e.g., no team=..., env=..., app=...), making it unlikely namespaces are being used as deliberate administrative boundaries.
  • One namespace with labels suggesting it serves multiple distinct environments or teams (e.g., env=multi or a generic shared=true for many unrelated workloads).

Use the above outputs to answer, manually:

  • Are applications grouped into namespaces by environment, team, or function?
  • Are high-privilege permissions granted in namespaces that carry mixed or unrelated workloads?
  • Are there workloads still running in default that should be isolated into their own namespaces?
Automation
#!/usr/bin/env bash
# Report namespace usage and isolation signals across the cluster.
# Run on: any machine with kubectl access and current context set to target cluster.

set -euo pipefail

echo "== Cluster namespaces =="
kubectl get namespaces -o wide

echo
echo "== Namespaces by label (team/app/environment) =="
# Shows whether namespaces are tagged for ownership and purpose
kubectl get ns \
-o jsonpath='{range .items[*]}{.metadata.name}{"\t"}{.metadata.labels.team:="(no-team)"}{"\t"}{.metadata.labels.app:="(no-app)"}{"\t"}{.metadata.labels.environment:="(no-env)"}{"\n"}' \
| column -t

echo
echo "== Workloads per namespace (Deployments, StatefulSets, DaemonSets, Jobs, CronJobs, Pods) =="
for ns in $(kubectl get ns -o jsonpath='{range .items[*]}{.metadata.name}{"\n"}'); do
echo
echo "--- Namespace: ${ns} ---"
kubectl get deploy,sts,ds,job,cronjob,pod -n "${ns}" --ignore-not-found
done

echo
echo "== ClusterRoles and ClusterRoleBindings (cluster-wide permissions) =="
kubectl get clusterrole,clusterrolebinding -o wide

echo
echo "== RoleBindings that bind to ClusterRoles (potential cross-namespace power) =="
kubectl get rolebinding --all-namespaces -o json \
| jq -r '
.items[]
| .metadata.namespace as $ns
| .metadata.name as $rb
| .subjects[]? as $s
| .roleRef
| select(.kind=="ClusterRole")
| [$ns, $rb, .name]
| @tsv' 2>/dev/null \
| awk 'BEGIN {print "NAMESPACE\tROLEBINDING\tCLUSTERROLE"} {print}' \
| column -t

echo
echo "== ServiceAccounts used by Pods (by namespace) =="
for ns in $(kubectl get ns -o jsonpath='{range .items[*]}{.metadata.name}{"\n"}'); do
echo
echo "--- Namespace: ${ns} ---"
kubectl get pod -n "${ns}" -o jsonpath='{range .items[*]}{.metadata.name}{"\t"}{.spec.serviceAccountName}{"\n"}' 2>/dev/null \
| awk 'BEGIN {print "POD\tSERVICEACCOUNT"} {print}' \
| column -t || true
done

echo
echo "== NetworkPolicies per namespace (is there traffic isolation?) =="
for ns in $(kubectl get ns -o jsonpath='{range .items[*]}{.metadata.name}{"\n"}'); do
echo
echo "--- Namespace: ${ns} ---"
kubectl get networkpolicy -n "${ns}" --ignore-not-found
done

echo
echo "== Namespaces without any NetworkPolicy (potentially fully open) =="
all_ns=$(kubectl get ns -o jsonpath='{range .items[*]}{.metadata.name}{"\n"}')
np_ns=$(kubectl get networkpolicy --all-namespaces -o jsonpath='{range .items[*]}{.metadata.namespace}{"\n"}' | sort -u || true)
echo "${all_ns}" | while read -r ns; do
if ! echo "${np_ns}" | grep -qx "${ns}"; then
echo "${ns}"
fi
done

Explanation of what output may indicate a problem (to review manually):

  • Cluster namespaces:
    • Only default, kube-system, kube-public, kube-node-lease plus all workloads running in default may indicate missing administrative boundaries.
  • Namespaces by label:
    • Namespaces with (no-team) / (no-env) suggest lack of ownership and unclear separation.
  • Workloads per namespace:
    • Many unrelated applications co-located in the same namespace (especially default) indicate weak isolation.
  • ClusterRoles and ClusterRoleBindings:
    • Very broad cluster-wide roles (e.g., custom *admin* roles) bound to many subjects reduce the effectiveness of namespace boundaries.
  • RoleBindings that bind to ClusterRoles:
    • Numerous rolebindings to powerful ClusterRoles in many namespaces show that namespace boundaries are being bypassed by cluster-scoped permissions.
  • ServiceAccounts used by Pods:
    • Heavy reuse of the same high-privilege ServiceAccount across multiple namespaces/apps weakens separation.
  • NetworkPolicies per namespace and Namespaces without any NetworkPolicy:
    • Critical or multi-tenant namespaces with no NetworkPolicies likely have no network-level isolation between tenants or apps.

Additional Reading: