Lead Platform-Wide Security Governance (STRIDE PolicySet)
Duration: ~25 minutes
Overview
201-11 named the controls. This lab ships them as an RHACM PolicySet so every cluster in the Placement gets the same namespace, PSA, Quota, default-deny NetworkPolicy, and a ServiceAccount that does not automount a token.
That is the platform operating model: the security team owns the PolicySet; application teams consume stride-workloads. Inform first (report drift), then enforce (create the missing objects)—the same loop as 201-10, expanded to the STRIDE rows.
Why it matters
A threat model in a slide deck does not configure the next cluster. RHACM Policy objects do. DoD CC SRG, DISA STIG, and NIST SP 800-53 expect an approved baseline, not tribal knowledge. The Governance UI columns (standards, categories, controls) are how an ISSO traces a green cluster back to AC-6, CM-6, SC-7, and SC-5.
What does it solve
-
EoP — workloads landing in namespaces without restricted PSA
-
DoS — unbounded CPU/memory in a shared tenant namespace
-
Spoofing — app ServiceAccounts that still automount API tokens
-
Disclosure / lateral movement — namespaces with no default-deny NetworkPolicy
-
Drift — each cluster’s “baseline” living in a different wiki
Your Mission
Apply PolicySet stride-ocp-baseline, watch it NonCompliant while inform is on, enforce it so the cluster configures itself, then prove restricted PSA still denies a privileged pod.
| Policy in the set | STRIDE row | Objects |
|---|---|---|
|
Elevation of privilege |
Namespace |
|
Denial of service |
LimitRange + ResourceQuota |
|
Spoofing + Information disclosure |
default-deny NetworkPolicy + SA |
Prerequisites
-
Red Hat Advanced Cluster Management (or MCE with the governance policy add-on). CRD:
policies.policy.open-cluster-management.io -
A
ManagedCluster(hub is usuallylocal-cluster) -
Cluster-admin on the hub
-
201-10 recommended (same
policiesnamespace and ClusterSet binding)
Click each step only if you need a hint.
Part A: Hub inventory
Confirm RHACM Policy CRDs and local-cluster
If this fails, install Advanced Cluster Management for Kubernetes from OperatorHub (or ask the platform owner).
oc get crd policies.policy.open-cluster-management.io
oc get managedcluster
oc get managedcluster local-cluster --show-labels 2>/dev/null || oc get managedcluster --show-labels
CRD exists. local-cluster=true is the usual hub label.
|
Bind a ClusterSet into namespace policies
Skip if you already ran this in 201-10. Placement selects zero clusters until a ManagedClusterSetBinding exists.
cd ~/openshift-security-roadshow/setup/acm-lab
chmod +x bind-clusterset.sh
./bind-clusterset.sh
managedclustersetbinding created (or already configured) in policies.
|
Part B: Inform (report drift)
Apply the STRIDE PolicySet (inform)
Three Policies + a PolicySet + Placement. Remediation starts as inform—RHACM reports NonCompliant until the objects exist.
oc apply -f ~/openshift-security-roadshow/setup/acm-lab/policy-stride-ocp-baseline.yaml
Policies, PolicySet stride-ocp-baseline, Placement, and PlacementBinding created in policies.
|
Read PolicySet status (expect NonCompliant)
oc get policyset,policy,placement,placementbinding -n policies
oc get ns stride-workloads 2>/dev/null || echo "stride-workloads not created yet (inform does not remediate)"
COMPLIANT is NonCompliant (or empty for a few seconds). Namespace stride-workloads should not exist yet. Console: Fleet Management → Governance → PolicySets.
|
Part C: Enforce (configure the cluster from policy)
Switch the three Policies to enforce
The PolicySet groups them; each Policy still has its own remediationAction. Patch all three.
for p in policy-stride-ns-psa policy-stride-quota-limits policy-stride-netpol-sa; do
oc patch policy "$p" -n policies --type merge -p '{"spec":{"remediationAction":"enforce"}}'
done
Three patched messages. Wait 15–30 seconds.
|
Confirm the STRIDE objects exist
oc get policy -n policies -l app.kubernetes.io/component=301-01-acm
oc get ns stride-workloads --show-labels
oc get networkpolicy,limitrange,resourcequota,sa -n stride-workloads
oc get sa app -n stride-workloads -o jsonpath='{.automountServiceAccountToken}{"\n"}'
Policies Compliant. Namespace labels include pod-security.kubernetes.io/enforce=restricted. default-deny-ingress, bounded-defaults, bounded-quota, and SA app with automount false.
|
Part D: Admission still matches the baseline
Apply a privileged pod in stride-workloads (expect deny)
privileged: true violates restricted PSA (AC-6 / EoP).
oc apply -n stride-workloads -f - <<'EOF'
apiVersion: v1
kind: Pod
metadata:
name: privileged-denied
spec:
containers:
- name: app
image: registry.access.redhat.com/ubi9/ubi
securityContext:
privileged: true
command: ["sleep","5"]
EOF
Admission rejects the pod (Forbidden / violates PodSecurity). The PolicySet stayed Compliant—the deny is the baseline working.
|
Debrief
Platform governance is the STRIDE map as Policy: who may change SCC and ANP is a platform decision; app teams do not freestyle namespaces. Inform then enforce is how drift is corrected. 301-02 puts these YAML files in Git.
What breaks without this:
-
Threat model with no delivery mechanism → next cluster is a snowflake
-
Hand-crafted namespaces → STIG drift
-
Automount left on by default → stolen tokens (Spoofing) still work
PolicySet + Placement + inform then enforce. Do not rewrite cluster restricted SCC in this lab.
|
| GitOps of these same CRs is 301-02. Tenant NetworkPolicy still cannot beat platform ANP (301-03). |
