Skip to main content

Tenant Namespaces Should Have A ResourceQuota

More Info:

Advisory: create a ResourceQuota per tenant namespace to bound aggregate CPU, memory and object counts, preventing one tenant from starving others.

Risk Level

Low

Address

Security

Compliance Standards

  • Cloudanix Best Practice

Triage and Remediation

Remediation

Manual Steps
  1. Identify tenant namespaces (decision step)
    On any machine with kubectl access, list namespaces and decide which are “tenant” namespaces (per your org’s multi-tenancy model; usually app or team namespaces, not kube-*, default, or system namespaces):

    kubectl get ns --show-labels

    Optionally, if you label tenant namespaces (recommended), e.g. tenant=true, list them:

    kubectl get ns -l tenant=true
  2. Check which tenant namespaces lack a ResourceQuota
    For each tenant namespace you identified, check for existing ResourceQuota objects:

    kubectl get resourcequota -n <TENANT_NAMESPACE>

    Note which tenant namespaces return No resources found or have quotas that clearly do not bound CPU, memory, and object counts.

  3. Review existing ResourceQuota coverage and sufficiency (decision step)
    For tenant namespaces that do have a ResourceQuota, inspect them to ensure they bound at least aggregate CPU, memory, and key object counts (e.g. pods, services, PVCs, configmaps, secrets):

    kubectl describe resourcequota -n <TENANT_NAMESPACE>

    Decide whether limits are appropriate for the tenant (not too low to break workloads, not so high that tenants can starve others).

  4. Design quota policies per tenant type (decision step)
    Outside the cluster (on any admin workstation), define standard quota “profiles” for different tenant types (e.g. small/medium/large) that include:

    • requests.cpu, requests.memory, limits.cpu, limits.memory
    • pods, services, persistentvolumeclaims, configmaps, secrets, etc.
      Document which profile each tenant namespace should receive based on current and projected usage.
  5. Create or update ResourceQuota manifests for tenant namespaces
    On any machine with kubectl access, create or adjust ResourceQuota objects according to your profiles. Example manifest (adjust values per your decisions):

    cat << 'EOF' > tenant-resourcequota.yaml
    apiVersion: v1
    kind: ResourceQuota
    metadata:
    name: tenant-quota
    namespace: <TENANT_NAMESPACE>
    spec:
    hard:
    requests.cpu: "4"
    requests.memory: "8Gi"
    limits.cpu: "8"
    limits.memory: "16Gi"
    pods: "50"
    services: "10"
    persistentvolumeclaims: "20"
    configmaps: "50"
    secrets: "50"
    EOF

    Apply for each tenant namespace (edit the manifest namespace or generate one per namespace):

    kubectl apply -f tenant-resourcequota.yaml
  6. Verify quotas are in place and effective
    On any machine with kubectl access, confirm that every tenant namespace now has an appropriate ResourceQuota and that usage is tracked:

    # List quotas per tenant namespace
    kubectl get resourcequota -n <TENANT_NAMESPACE>

    # Confirm hard limits and current usage
    kubectl describe resourcequota -n <TENANT_NAMESPACE>

    Re-run this check for all tenant namespaces; any tenant namespace without a ResourceQuota, or with quotas that don’t bound CPU, memory, and object counts, should be revisited using steps 3–5.

Using kubectl
# 1) List all namespaces to identify tenant namespaces
# Run on: any machine with kubectl access
kubectl get namespaces -o custom-columns=NAME:.metadata.name \
--no-headers | sort

Review this list and decide which are tenant namespaces (for example, team or project namespaces) versus system/internal namespaces (e.g. kube-system, kube-public, kube-node-lease, istio-system, ingress-nginx, etc.).

# 2) For a given tenant namespace, check for ResourceQuota objects
# Replace TENANT_NS with an actual tenant namespace name
kubectl get resourcequota -n TENANT_NS

Problem indication:

  • If this command returns No resources found in TENANT_NS namespace, the tenant namespace has no ResourceQuota and is in violation of this control.
  • If it returns one or more ResourceQuota objects, you must still review them (next step) to decide whether the quotas are adequate.
# 3) Inspect details of each ResourceQuota in the tenant namespace
# First, list the names:
kubectl get resourcequota -n TENANT_NS -o name

# Then, for each quota (example name: rq-team-a), describe it:
kubectl describe resourcequota rq-team-a -n TENANT_NS

Problem indication (requires human judgment):

  • Hard limits missing for CPU or memory (e.g. no entries for limits.cpu, limits.memory, requests.cpu, requests.memory) mean aggregate resource usage is not bounded.
  • Missing object count limits (e.g. pods, configmaps, secrets, services, persistentvolumeclaims) mean the namespace could still starve others via object count exhaustion.
  • Very high or effectively unlimited values (e.g. 0 where that means “no limit”, or values that exceed node/cluster capacity) indicate quotas are not meaningfully constraining the tenant.
# 4) Optional: summarize quotas for all namespaces to spot gaps at a glance
kubectl get resourcequota --all-namespaces

Problem indication:

  • Any tenant namespace that does not appear in this list lacks a ResourceQuota and should be reviewed and corrected.
  • Namespaces that appear with quotas but you know are not tenants (e.g. system namespaces) are not relevant to this control and can be ignored.
Automation
#!/usr/bin/env bash
#
# Report tenant namespaces that do NOT have a ResourceQuota
# and summarize quotas for those that do.
#
# Run from: any machine with kubectl access and the correct context.
# Requires: kubectl, jq

set -euo pipefail

# 1) Define how to identify "tenant" namespaces.
# Adjust this selector to match YOUR environment.
# Examples:
# - All non-system namespaces:
# NS_SELECTOR='!name in (kube-system,kube-public,kube-node-lease,default)'
# - Label-based (preferred):
# NS_SELECTOR='tenant=true'
#
# Below uses label-based selection. Update to match your labels.
NS_SELECTOR='tenant=true'

echo "==> Discovering tenant namespaces (selector: ${NS_SELECTOR})"
TENANT_NAMESPACES=$(kubectl get ns -l "${NS_SELECTOR}" -o jsonpath='{range .items[*]}{.metadata.name}{"\n"}{end}')

if [[ -z "${TENANT_NAMESPACES}" ]]; then
echo "No tenant namespaces found with selector '${NS_SELECTOR}'."
exit 0
fi

echo ""
echo "==> Per-namespace ResourceQuota summary"
echo "Namespace,HasResourceQuota,QuotaName,Hard.cpu,Hard.memory,Hard.pods,Hard.configmaps,Hard.secrets,Hard.services,Hard.persistentvolumeclaims"

MISSING_COUNT=0

for ns in ${TENANT_NAMESPACES}; do
RQ_JSON=$(kubectl get resourcequota -n "${ns}" -o json 2>/dev/null || echo '{}')
RQ_COUNT=$(echo "${RQ_JSON}" | jq '.items | length')

if [[ "${RQ_COUNT}" -eq 0 ]]; then
# No ResourceQuota in this tenant namespace
echo "${ns},NO,,,,,,,"
((MISSING_COUNT++))
continue
fi

# Print one line per ResourceQuota in the namespace
echo "${RQ_JSON}" | jq -r --arg ns "${ns}" '
.items[] as $rq |
[
$ns,
"YES",
$rq.metadata.name,
($rq.spec.hard.cpu // ""),
($rq.spec.hard.memory // ""),
($rq.spec.hard.pods // ""),
($rq.spec.hard.configmaps // ""),
($rq.spec.hard.secrets // ""),
($rq.spec.hard.services // ""),
($rq.spec.hard.persistentvolumeclaims // "")
] | @csv
'
done

echo ""
echo "==> Summary"
TOTAL_NS=$(echo "${TENANT_NAMESPACES}" | wc -l | tr -d ' ')
echo "Total tenant namespaces: ${TOTAL_NS}"
echo "Namespaces without any ResourceQuota: ${MISSING_COUNT}"

if [[ "${MISSING_COUNT}" -gt 0 ]]; then
echo ""
echo "Problem indication:"
echo "- Any line with 'HasResourceQuota' == 'NO' in the CSV output"
echo " (e.g. 'tenant-a,NO,....') marks a tenant namespace that lacks a ResourceQuota."
echo "- These namespaces should be reviewed and given an appropriate ResourceQuota"
echo " to bound aggregate CPU, memory, and object counts."
fi

What output indicates a problem

  • In the CSV section, any row where the second column (HasResourceQuota) is NO indicates a tenant namespace that does not have any ResourceQuota objects defined.
  • The final summary line Namespaces without any ResourceQuota: N:
    • If N > 0, those namespaces are not constrained and should be reviewed and given an appropriate ResourceQuota per the benchmark guidance.