When securing an OpenShift Container Platform (OCP) cluster at an architecture level, you have to transition from basic perimeter security to a deep Defense-in-Depth model.
In a true multi-tenant environment, hardening requires a synchronized implementation of workload isolation (SCCs), network isolation (Network Policies), cryptographic identity validation (mTLS), and externalized secret management (HashiCorp Vault).
1. Workload Hardening: Security Context Constraints (SCCs)
Upstream Kubernetes uses standard Pod Security Admissions (PSA). OpenShift overlays a more powerful, native security layer called Security Context Constraints (SCCs). SCCs control what actions a pod can perform, what system privileges it can request, and what host-level assets it can touch.
By default, OpenShift blocks workloads from running as the root user or using host network namespaces via the restricted-v2 SCC.
Architectural Rule: The Least-Privilege Blueprint
If you are deploying an advanced infrastructural utility (like a log forwarder or storage driver) that requires deep host-level hooks, you must never globally lower your cluster’s security posture. Instead, you create an isolated, custom SCC tightly bound to a dedicated ServiceAccount:
YAML
apiVersion: security.openshift.io/v1kind: SecurityContextConstraintmetadata: name: privileged-infrastructure-sccspec: allowPrivilegedContainer: false # Still block raw root privilege escalation allowHostNetwork: true # Allow binding to host network adapters for sniffing allowHostPorts: true allowHostDirVolumePlugin: true # Allow mounting specific host log directories volumes: - hostPath - configMap - secret runAsUser: type: MustRunAsNonRoot # Force the application binary itself to run non-root seLinuxContext: type: MustRunAs # Enforce specific SELinux MLS labels on the container seLinuxOptions: level: "s0:c125,c456" # MCS Category isolation
To activate this for your infrastructure pod, you bind it via a local namespaced RoleBinding:
Bash
oc adm policy add-scc-to-user privileged-infrastructure-scc -z infra-collector-sa -n monitoring-infra
2. Network Isolation: Granular NetworkPolicies
Within an OpenShift cluster running the default OVNKubernetes CNI, all pods can communicate with all other pods across namespace boundaries by default. To enforce multi-tenant isolation, you must apply explicit Layer-3/4 packet-filtering rules.
The Zero-Trust Policy Framework
First, enforce a strict Default-Deny-All configuration within the target tenant namespace. Once traffic is locked down, you selectively punch holes to whitelist traffic pipelines.
The blueprint below showcases an enterprise policy that locks down a backend service—ensuring it only accepts traffic over port 8080 if it originates from an explicit ingress frontend microservice inside the cluster, while dropping all lateral cross-namespace discovery attempts:
YAML
apiVersion: networking.k8s.io/v1kind: NetworkPolicymetadata: name: api-tier-firewall namespace: e-commerce-prodspec: podSelector: matchLabels: tier: api-backend policyTypes: - Ingress ingress: - from: # Match pods ONLY if they are labeled frontend AND reside in a labeled production namespace - namespaceSelector: matchLabels: kubernetes.io/metadata.name: e-commerce-prod podSelector: matchLabels: tier: web-frontend ports: - protocol: TCP port: 8080
3. Cryptographic Data-in-Transit: Mutual TLS (mTLS)
Even with strict NetworkPolicies, traffic travelling between nodes travels over unencrypted internal cluster networks. If an attacker compromises an underlying node or network switch, they can sniff raw data. To prevent this, you implement Mutual TLS (mTLS) to cryptographically authenticate and encrypt every single payload packet.
Execution Vector: OpenShift Service Mesh (Istio)
The most elegant way to enforce mTLS at scale without forcing developers to manually write TLS handshake code into their Java or Node.js applications is via the OpenShift Service Mesh Operator (Istio).
When you inject the Istio sidecar proxy (Envoy) into your pod deployments, the sidecars intercept all inbound and outbound TCP traffic. The sidecars use certificates automatically provisioned and rotated by the Mesh control plane to build encrypted TLS tunnels between each other.
To secure your namespace completely, you apply a PeerAuthentication policy to force strict mTLS enforcement:
YAML
apiVersion: security.istio.io/v1beta1kind: PeerAuthenticationmetadata: name: default-strict-mtls namespace: payment-processingspec: mtls: mode: STRICT # Completely rejects any plain-text unencrypted traffic attempts
4. Secret Management: Externalized HashiCorp Vault Integration
Storing static base64-encoded cryptographic keys or database credentials inside native Kubernetes Secret resources means they are saved directly into the cluster’s etcd database. While etcd can be encrypted at rest, advanced enterprise security requires external compliance, automatic secret rotation, and dynamic credential generation via HashiCorp Vault.
The Architectural Pipeline: Vault Agent Injection
Instead of forcing your application code to explicitly call Vault APIs, you can use the Vault Secrets Operator or the Vault Agent Sidecar Injector.
When a pod spins up carrying specific annotations, the Vault mutating webhook intercepts the deployment and automatically mounts an ephemeral in-memory volume (tmpfs) directly inside your application container.
YAML
apiVersion: apps/v1kind: Deploymentmetadata: name: payment-gateway namespace: payment-processingspec: template: metadata: annotations: # 1. Activate the Vault Agent Sidecar Injector vault.hashicorp.com/agent-inject: "true" # 2. Reference the exact secret path inside Vault vault.hashicorp.com/agent-inject-secret-dbcred: "internal/data/database/config" # 3. Use a template string to render the credential directly into a flat properties file vault.hashicorp.com/agent-inject-template-dbcred: | {{- with secret "internal/data/database/config" -}} username={{ .Data.data.username }} password={{ .Data.data.password }} {{- end -}} # 4. Bind the authorization to a cluster ServiceAccount recognized by Vault's JWT engine vault.hashicorp.com/role: "payment-app-role" spec: serviceAccountName: payment-gateway-sa containers: - name: application image: quay.io/enterprise/payment-gw:v2 volumeMounts: # The application reads its database credentials directly from local file paths - mountPath: /vault/secrets name: vault-secrets
The Authentication Loop Under the Hood
- The pod boots up using the
payment-gateway-saServiceAccount. - The injected Vault Agent captures the pod’s native Kubernetes ServiceAccount JWT token from the local disk.
- The Agent ships this token over to the external HashiCorp Vault server’s
/v1/auth/kuberneteslogin endpoint. - Vault decrypts the token, queries the OpenShift token review API server to verify the token is legitimate and unexpired, confirms the pod’s identities match
payment-app-role, and returns a short-lived lease token. - The Agent uses this lease token to pull down the database keys, writes them into
/vault/secrets/dbcred, and constantly handles dynamic token rotations silently in the background.
Summary Hardening Matrix
| Security Layer | Component | Target Vulnerability | Operational Impact |
| Workload Layer | SCCs | Container breakouts, privilege escalation, host resource tampering. | Enforces non-root runtimes, isolates system directory visibility. |
| Network Layer | NetworkPolicies | Lateral network sniffing, port discovery, cross-tenant pollution. | Implements a strict default-deny firewall fabric across namespaces. |
| Transport Layer | mTLS (Service Mesh) | Man-in-the-Middle (MitM) snooping, unencrypted packet parsing on the wire. | Automates end-to-end cryptographic packet encryption via Envoy proxies. |
| Data Layer | HashiCorp Vault | Static etcd credentials leaks, cleartext git storage, compliance audit failures. | Externalizes configurations, injects ephemeral keys, rotates secrets dynamically. |