Tenants
A complete Tenant combining the workload and networking best-practice baseline
Use this complete Tenant as a starting point for application namespaces. It
combines the compatible baseline policies from Workloads
and Networking into one ordered
spec.rules list. All namespaces in this Tenant receive the same baseline.
Included Policies
| Area | Combined configuration |
|---|
| Resources | Default CPU, memory, and ephemeral-storage requests; remove CPU limits; match memory limits to requests; default ephemeral-storage limits. |
| Capacity | Share one illustrative quota across all Tenant namespaces. |
| QoS | Deny BestEffort after resource mutation. |
| Placement | Use the default scheduler, exclude control-plane nodes, deny control-plane and wildcard tolerations, and reject Pods created with nodeName. |
| Workload types | Deny DaemonSets. |
| Pod isolation | Set hostUsers: false and manage the namespace’s Pod Security labels at restricted. |
| Images | Allow the approved registry path and require Always pull policy. |
| Priority | Default to and allow only tenant-standard. |
| Services | Allow only ClusterIP and deny every external IP. |
| Namespace trust | Deny adding or changing company.com/system on Tenant namespaces. |
Before Applying
- Replace
solar, solar-owner, and registry.example.com/solar with your
Tenant, owner, and approved registry path. Publish immutable image references
in that registry; the example’s registry matcher does not require a digest. - Size requests, storage defaults, and the shared quota from measured usage and
available capacity. The numbers below illustrate a profile, not a universal
production budget. There is deliberately no CPU-limit quota.
- Create the non-preempting
tenant-standard PriorityClass
before admitting workloads that use this Tenant. The Tenant references the
class; it does not create it. - Use Linux nodes with user namespace support.
Review kernel, runtime, and volume support before enabling
hostUsers: false.
For incompatible workloads, define a separate, reviewed namespace profile. - Set all three Pod Security version labels to a version supported by your
cluster. This example pins them to
v1.36. Applications must supply security
contexts that satisfy restricted; these rules do not generate those fields.
Reference
Explore the ordered rules below. Select a card to open its documentation, or
view, copy, and download the complete Tenant YAML, including ownership and
PriorityClass settings.
View complete Tenant YAML Owners, priority settings, and all rules
apiVersion: capsule.clastix.io/v1beta2
kind: Tenant
metadata:
name: solar
spec:
owners:
- kind: User
name: solar-owner
# Default and allow the tenant-standard PriorityClass.
priorityClasses:
default: tenant-standard
allowed: [tenant-standard]
rules:
# One shared budget across all namespaces in this Tenant.
- quota:
- name: application-budget
hard:
requests.cpu: "20"
requests.memory: 40Gi
limits.memory: 40Gi
requests.ephemeral-storage: 100Gi
limits.ephemeral-storage: 200Gi
pods: "100"
services: "100"
# Apply placement and user-namespace settings on Pod creation.
- mutate:
- action: merge
workloads:
security:
hostUsers: false
placement:
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: node-role.kubernetes.io/control-plane
operator: DoesNotExist
- key: node-role.kubernetes.io/master
operator: DoesNotExist
enforce:
action: allow
workloads:
targets: [pod]
placement:
schedulers:
- exact: [default-scheduler]
# Defaults for application containers, sidecars, and init containers.
- enforce:
action: deny
workloads:
targets: [pod/containers, pod/initcontainers]
resources:
requests:
cpu:
policy: Default
value: 250m
memory:
policy: Default
value: 512Mi
ephemeral-storage:
policy: Default
value: 1Gi
limits:
cpu:
policy: Remove
memory:
policy: MatchRequest
ephemeral-storage:
policy: Default
value: 2Gi
# Normalize limits when a workload supplies Pod-level resources.
- enforce:
action: deny
workloads:
targets: [pod]
resources:
limits:
cpu:
policy: Remove
memory:
policy: MatchRequest
# Deny BestEffort Pods after resource mutation.
- enforce:
action: deny
workloads:
targets: [pod]
qosClasses: [BestEffort]
# Reject control-plane and wildcard tolerations.
- enforce:
action: deny
workloads:
targets: [pod]
placement:
tolerations:
- key:
exact:
- node-role.kubernetes.io/control-plane
- node-role.kubernetes.io/master
- key: {exp: '^$'}
operators: [Exists]
# Check direct assignment only at creation; allow scheduler-bound updates.
- enforce:
action: deny
conditions:
- name: direct-node-assignment
expression: >-
request.operation == 'CREATE' && has(object.spec.nodeName) && object.spec.nodeName != ''
workloads:
targets: [pod]
# Keep kind-level denials separate from workload property checks.
- enforce:
action: deny
workloads:
targets: [daemonset]
# Allow approved images with Always pull policy.
- enforce:
action: allow
workloads:
targets:
- pod/containers
- pod/initcontainers
- pod/ephemeralcontainers
- pod/volumes
registries:
- exp: '^registry\.example\.com/solar/.+$'
policy: [Always]
# Allow only ClusterIP Services.
- enforce:
action: allow
services:
types: [ClusterIP]
# Deny all Service external IPs.
- enforce:
action: deny
services:
externalIPs: {}
# Apply and reconcile Pod Security labels on the Namespace itself.
- enforce:
action: allow
metadata:
- apiGroups: [v1]
kinds: [Namespace]
labels:
pod-security.kubernetes.io/enforce:
managed: restricted
required: true
values:
- exact: [restricted]
pod-security.kubernetes.io/enforce-version:
managed: v1.36
required: true
values:
- exact: [v1.36]
pod-security.kubernetes.io/warn:
managed: restricted
required: true
values:
- exact: [restricted]
pod-security.kubernetes.io/warn-version:
managed: v1.36
required: true
values:
- exact: [v1.36]
pod-security.kubernetes.io/audit:
managed: restricted
required: true
values:
- exact: [restricted]
pod-security.kubernetes.io/audit-version:
managed: v1.36
required: true
values:
- exact: [v1.36]
# Reserve this trusted-platform label for administrator-managed namespaces.
- enforce:
action: deny
metadata:
- apiGroups: [v1]
kinds: [Namespace]
labels:
company.com/system:
values:
- exp: ".*"
Namespace rulesIn declaration order
One shared budget across all namespaces in this Tenant.
- quota:
- name: application-budget
hard:
requests.cpu: "20"
requests.memory: 40Gi
limits.memory: 40Gi
requests.ephemeral-storage: 100Gi
limits.ephemeral-storage: 200Gi
pods: "100"
services: "100"
Apply placement and user-namespace settings on Pod creation.
- mutate:
- action: merge
workloads:
security:
hostUsers: false
placement:
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: node-role.kubernetes.io/control-plane
operator: DoesNotExist
- key: node-role.kubernetes.io/master
operator: DoesNotExist
enforce:
action: allow
workloads:
targets: [pod]
placement:
schedulers:
- exact: [default-scheduler]
Defaults for application containers, sidecars, and init containers.
- enforce:
action: deny
workloads:
targets: [pod/containers, pod/initcontainers]
resources:
requests:
cpu:
policy: Default
value: 250m
memory:
policy: Default
value: 512Mi
ephemeral-storage:
policy: Default
value: 1Gi
limits:
cpu:
policy: Remove
memory:
policy: MatchRequest
ephemeral-storage:
policy: Default
value: 2Gi
Normalize limits when a workload supplies Pod-level resources.
- enforce:
action: deny
workloads:
targets: [pod]
resources:
limits:
cpu:
policy: Remove
memory:
policy: MatchRequest
Deny BestEffort Pods after resource mutation.
- enforce:
action: deny
workloads:
targets: [pod]
qosClasses: [BestEffort]
Reject control-plane and wildcard tolerations.
- enforce:
action: deny
workloads:
targets: [pod]
placement:
tolerations:
- key:
exact:
- node-role.kubernetes.io/control-plane
- node-role.kubernetes.io/master
- key: {exp: '^$'}
operators: [Exists]
Check direct assignment only at creation; allow scheduler-bound updates.
- enforce:
action: deny
conditions:
- name: direct-node-assignment
expression: >-
request.operation == 'CREATE' && has(object.spec.nodeName) && object.spec.nodeName != ''
workloads:
targets: [pod]
Keep kind-level denials separate from workload property checks.
- enforce:
action: deny
workloads:
targets: [daemonset]
Allow approved images with Always pull policy.
- enforce:
action: allow
workloads:
targets:
- pod/containers
- pod/initcontainers
- pod/ephemeralcontainers
- pod/volumes
registries:
- exp: '^registry\.example\.com/solar/.+$'
policy: [Always]
Allow only ClusterIP Services.
- enforce:
action: allow
services:
types: [ClusterIP]
Deny all Service external IPs.
- enforce:
action: deny
services:
externalIPs: {}
Apply and reconcile Pod Security labels on the Namespace itself.
- enforce:
action: allow
metadata:
- apiGroups: [v1]
kinds: [Namespace]
labels:
pod-security.kubernetes.io/enforce:
managed: restricted
required: true
values:
- exact: [restricted]
pod-security.kubernetes.io/enforce-version:
managed: v1.36
required: true
values:
- exact: [v1.36]
pod-security.kubernetes.io/warn:
managed: restricted
required: true
values:
- exact: [restricted]
pod-security.kubernetes.io/warn-version:
managed: v1.36
required: true
values:
- exact: [v1.36]
pod-security.kubernetes.io/audit:
managed: restricted
required: true
values:
- exact: [restricted]
pod-security.kubernetes.io/audit-version:
managed: v1.36
required: true
values:
- exact: [v1.36]
Reserve this trusted-platform label for administrator-managed namespaces.
- enforce:
action: deny
metadata:
- apiGroups: [v1]
kinds: [Namespace]
labels:
company.com/system:
values:
- exp: ".*"
How the Policies Combine
Capsule applies mutations before validation. On Pod creation, the resource
rules supply missing requests and storage limits, remove CPU limits, and align
memory limits with requests. Explicit requests and storage limits survive the
Default policies. Placement mutation adds the control-plane exclusions and
sets hostUsers: false; enforcement then checks the resulting Pod.
The workload policies target Pods, including controller-created Pods. The
DaemonSet rule separately rejects the controller itself. Add explicit
controller targets if
you also want resource or image checks on saved controller templates. Keep the
DaemonSet denial and conditional direct-assignment denial separate: adding
workload properties to a targets-only rule changes its meaning.
The quota rule generates solar-application-budget, a
GlobalResourceQuota shared
across this Tenant’s namespaces. It bounds aggregate consumption; the resource
defaults do not impose maximum requests or reserve node capacity by themselves.
Retain capacity for failover and system services as described in
capacity planning.
The restricted Pod Security labels are managed values. The platform-label
denial checks added or changed metadata; unchanged existing values are skipped
on updates. Remove any existing tenant-controlled company.com/system labels
before using them as a network trust boundary.
The memory-overcommit and ExternalName examples on the individual pages are
deliberate exceptions. Add them only under reviewed namespace selectors, with
the selecting labels under platform control. Review rule order and scope
when composing further rules so a later decision does not unintentionally
relax the baseline.
Apply the native NetworkPolicy GlobalTenantResource
alongside this Tenant. It distributes default-deny, same-Tenant, DNS, and
platform-ingress policies; NetworkPolicies are not embedded in this Tenant
manifest. Review its CNI requirements and the permissions needed to prevent
tenants from adding policies that widen access.
Use the additional admission policies
for requirements such as PDB validation, emptyDir.sizeLimit, debugging access,
and certificate issuer restrictions. Those examples require their respective
admission mechanisms and are not installed by this Tenant.
Configure authentication, registry mirrors, and secret management
at platform level. Review existing workloads and Services before rollout;
admission policies do not remove previously admitted resources.