This guide covers installing OPA Gatekeeper, applying the StellarNode admission policies, testing them, and verifying that the operator's reconciliation loop continues to work correctly.
- Kubernetes cluster (1.25+)
kubectlconfigured with cluster-admin privileges- OPA Gatekeeper v3.13 or later
kubectl apply -f https://raw.githubusercontent.com/open-policy-agent/gatekeeper/v3.13.0/deploy/gatekeeper.yamlWait for Gatekeeper to be ready:
kubectl -n gatekeeper-system rollout status deployment/gatekeeper-controller-manager
kubectl -n gatekeeper-system rollout status deployment/gatekeeper-auditApply the Gatekeeper Config resource first to exclude system namespaces from all policy evaluation:
kubectl apply -f config/manifests/gatekeeper/gatekeeper-config.yamlApply all three ConstraintTemplates:
kubectl apply -f config/manifests/gatekeeper/resource-limits-template.yaml
kubectl apply -f config/manifests/gatekeeper/approved-registries-template.yaml
kubectl apply -f config/manifests/gatekeeper/required-labels-template.yamlWait for the ConstraintTemplates to be fully reconciled before applying Constraints (the CRDs they define must exist first):
kubectl wait --for=condition=Ready constrainttemplate/resourcelimits --timeout=60s
kubectl wait --for=condition=Ready constrainttemplate/approvedregistries --timeout=60s
kubectl wait --for=condition=Ready constrainttemplate/requiredlabels --timeout=60sApply all three Constraints:
kubectl apply -f config/manifests/gatekeeper/resource-limits-constraint.yaml
kubectl apply -f config/manifests/gatekeeper/approved-registries-constraint.yaml
kubectl apply -f config/manifests/gatekeeper/required-labels-constraint.yamlOr apply the entire directory at once (after ConstraintTemplates are ready):
kubectl apply -f config/manifests/gatekeeper/Verify all constraints are active:
kubectl get constraintExpected output:
NAME ENFORCEMENT-ACTION TOTAL-VIOLATIONS
stellarnode-approved-registries deny 0
stellarnode-required-labels deny 0
stellarnode-resource-limits deny 0
# test-resource-limits-bad-cpu.yaml
apiVersion: stellar.org/v1alpha1
kind: StellarNode
metadata:
name: test-bad-cpu
namespace: default
labels:
app.kubernetes.io/part-of: stellar
stellar.org/team: platform
stellar.org/environment: test
spec:
version: "ghcr.io/stellar/stellar-node:latest"
resources:
limits:
cpu: "8" # exceeds max_cpu of "4"
memory: "4Gi"kubectl apply -f test-resource-limits-bad-cpu.yamlExpected denial:
Error from server (Forbidden): error when creating "test-resource-limits-bad-cpu.yaml":
admission webhook "validation.gatekeeper.sh" denied the request:
[stellarnode-resource-limits] StellarNode 'test-bad-cpu' cpu limit '8' exceeds maximum '4'
# test-resource-limits-bad-memory.yaml
apiVersion: stellar.org/v1alpha1
kind: StellarNode
metadata:
name: test-bad-memory
namespace: default
labels:
app.kubernetes.io/part-of: stellar
stellar.org/team: platform
stellar.org/environment: test
spec:
version: "ghcr.io/stellar/stellar-node:latest"
resources:
limits:
cpu: "2"
memory: "16Gi" # exceeds max_memory of "8Gi"kubectl apply -f test-resource-limits-bad-memory.yamlExpected denial:
Error from server (Forbidden): ...
[stellarnode-resource-limits] StellarNode 'test-bad-memory' memory limit '16Gi' exceeds maximum '8Gi'
# test-resource-limits-missing.yaml
apiVersion: stellar.org/v1alpha1
kind: StellarNode
metadata:
name: test-no-resources
namespace: default
labels:
app.kubernetes.io/part-of: stellar
stellar.org/team: platform
stellar.org/environment: test
spec:
version: "ghcr.io/stellar/stellar-node:latest"kubectl apply -f test-resource-limits-missing.yamlExpected denial:
Error from server (Forbidden): ...
[stellarnode-resource-limits] StellarNode 'test-no-resources' must specify spec.resources.limits
# test-resource-limits-good.yaml
apiVersion: stellar.org/v1alpha1
kind: StellarNode
metadata:
name: test-good-resources
namespace: default
labels:
app.kubernetes.io/part-of: stellar
stellar.org/team: platform
stellar.org/environment: test
spec:
version: "ghcr.io/stellar/stellar-node:latest"
resources:
limits:
cpu: "2"
memory: "4Gi"kubectl apply -f test-resource-limits-good.yamlExpected result: stellarnode.stellar.org/test-good-resources created
# test-registry-bad.yaml
apiVersion: stellar.org/v1alpha1
kind: StellarNode
metadata:
name: test-bad-registry
namespace: default
labels:
app.kubernetes.io/part-of: stellar
stellar.org/team: platform
stellar.org/environment: test
spec:
version: "docker.io/untrusted/stellar-node:latest" # not in approved list
resources:
limits:
cpu: "2"
memory: "4Gi"kubectl apply -f test-registry-bad.yamlExpected denial:
Error from server (Forbidden): ...
[stellarnode-approved-registries] StellarNode 'test-bad-registry' image registry
'docker.io/untrusted/stellar-node:latest' is not in the approved list:
["docker.io/stellar", "ghcr.io/stellar", "registry.stellar.org"]
# test-registry-good.yaml
apiVersion: stellar.org/v1alpha1
kind: StellarNode
metadata:
name: test-good-registry
namespace: default
labels:
app.kubernetes.io/part-of: stellar
stellar.org/team: platform
stellar.org/environment: test
spec:
version: "ghcr.io/stellar/stellar-node:v1.2.3"
resources:
limits:
cpu: "2"
memory: "4Gi"kubectl apply -f test-registry-good.yamlExpected result: stellarnode.stellar.org/test-good-registry created
# test-labels-bad.yaml
apiVersion: stellar.org/v1alpha1
kind: StellarNode
metadata:
name: test-bad-labels
namespace: default
labels:
app.kubernetes.io/part-of: stellar
# missing stellar.org/team and stellar.org/environment
spec:
version: "ghcr.io/stellar/stellar-node:latest"
resources:
limits:
cpu: "2"
memory: "4Gi"kubectl apply -f test-labels-bad.yamlExpected denial:
Error from server (Forbidden): ...
[stellarnode-required-labels] StellarNode 'test-bad-labels' is missing required labels:
["stellar.org/team", "stellar.org/environment"]
# test-labels-none.yaml
apiVersion: stellar.org/v1alpha1
kind: StellarNode
metadata:
name: test-no-labels
namespace: default
spec:
version: "ghcr.io/stellar/stellar-node:latest"
resources:
limits:
cpu: "2"
memory: "4Gi"kubectl apply -f test-labels-none.yamlExpected denial:
Error from server (Forbidden): ...
[stellarnode-required-labels] StellarNode 'test-no-labels' is missing required labels:
["app.kubernetes.io/part-of", "stellar.org/team", "stellar.org/environment"]
# test-labels-good.yaml
apiVersion: stellar.org/v1alpha1
kind: StellarNode
metadata:
name: test-good-labels
namespace: default
labels:
app.kubernetes.io/part-of: stellar
stellar.org/team: platform
stellar.org/environment: production
spec:
version: "ghcr.io/stellar/stellar-node:latest"
resources:
limits:
cpu: "2"
memory: "4Gi"kubectl apply -f test-labels-good.yamlExpected result: stellarnode.stellar.org/test-good-labels created
After applying the policies, confirm the operator continues to reconcile StellarNode resources normally.
kubectl get stellarnodes --all-namespacesExpected output shows nodes in a Ready or Running state — the same as before policies were applied:
NAMESPACE NAME STATUS AGE
stellar my-stellar-node Running 5m
kubectl -n stellar logs -l app=stellar-operator --tail=50Look for normal reconciliation messages. There should be no admission webhook denial errors in the operator logs. Example healthy output:
INFO stellar_operator::controller > reconciling StellarNode my-stellar-node
INFO stellar_operator::controller > StellarNode my-stellar-node reconciled successfully
If you see errors like admission webhook "validation.gatekeeper.sh" denied the request, the operator's namespace exemption may not be configured correctly — see the Operator Exemption section below.
Gatekeeper's audit controller periodically evaluates all existing resources against active Constraints and records violations. No action is required to enable audit mode — it runs automatically.
kubectl get constraintThe TOTAL-VIOLATIONS column shows how many existing resources violate each policy.
kubectl describe constraint stellarnode-resource-limits
kubectl describe constraint stellarnode-approved-registries
kubectl describe constraint stellarnode-required-labelsThe status.violations section lists each violating resource:
status:
violations:
- enforcementAction: deny
group: stellar.org
kind: StellarNode
message: "StellarNode 'legacy-node' cpu limit '10' exceeds maximum '4'"
name: legacy-node
namespace: production
version: v1alpha1The default audit interval is 60 seconds. To change it, edit the Gatekeeper controller deployment:
kubectl -n gatekeeper-system edit deployment gatekeeper-audit
# Add or modify: --audit-interval=30 (seconds)The stellar-operator service account must never be blocked by the policies it governs. Exemption is implemented at two levels.
All three Constraint manifests include spec.match.excludedNamespaces: [stellar]. Resources in the stellar namespace — where the stellar-operator service account runs — are excluded from policy evaluation entirely.
This is sufficient when the operator only creates StellarNodes in the stellar namespace.
If the operator creates StellarNodes in namespaces other than stellar, namespace exclusion alone is not enough. Add the stellar-operator service account to the Gatekeeper Config exemptUsers list:
# config/manifests/gatekeeper/gatekeeper-config.yaml (updated)
apiVersion: config.gatekeeper.sh/v1alpha1
kind: Config
metadata:
name: config
namespace: gatekeeper-system
spec:
match:
- excludedNamespaces:
- kube-system
- gatekeeper-system
processes:
- "*"
exemptUsers:
- "system:serviceaccount:stellar:stellar-operator"Apply the updated config:
kubectl apply -f config/manifests/gatekeeper/gatekeeper-config.yamlIf the operator's service account name or namespace is customized, update the
exemptUsersentry accordingly. The format issystem:serviceaccount:<namespace>:<service-account-name>. The default issystem:serviceaccount:stellar:stellar-operator.
If the operator namespace is not stellar, update the spec.match.excludedNamespaces field in each Constraint manifest before applying:
spec:
match:
excludedNamespaces:
- <your-operator-namespace> # replace "stellar" with your namespaceBy default, Gatekeeper's ValidatingWebhookConfiguration is installed with failurePolicy: Ignore. This means that if the Gatekeeper pod is unavailable (e.g., during an upgrade or outage), admission requests are allowed through rather than blocked.
To change to strict enforcement — where all admission requests are denied if Gatekeeper is unreachable — patch the webhook configuration:
kubectl patch validatingwebhookconfiguration gatekeeper-validating-webhook-configuration \
--type='json' \
-p='[{"op":"replace","path":"/webhooks/0/failurePolicy","value":"Fail"},
{"op":"replace","path":"/webhooks/1/failurePolicy","value":"Fail"}]'Warning: Setting
failurePolicy: Failmeans any Gatekeeper downtime will block all StellarNode CREATE and UPDATE operations cluster-wide. Ensure Gatekeeper is running with sufficient replicas and pod disruption budgets before enabling strict mode.
To revert to the default:
kubectl patch validatingwebhookconfiguration gatekeeper-validating-webhook-configuration \
--type='json' \
-p='[{"op":"replace","path":"/webhooks/0/failurePolicy","value":"Ignore"},
{"op":"replace","path":"/webhooks/1/failurePolicy","value":"Ignore"}]'