|
| 1 | +--- |
| 2 | +title: Operator Policy |
| 3 | +weight: 5 |
| 4 | +hide_summary: true |
| 5 | +--- |
| 6 | + |
| 7 | +The `OperatorPolicy` defines a desired state for operators managed by the Operator Lifecycle Manager (OLM) on managed clusters. The Operator policy controller is provided by Open Cluster Management and runs on managed clusters as part of the `config-policy-controller` deployment. |
| 8 | + |
| 9 | +View the [Policy API concepts]({{< ref "docs/getting-started/integration/policy-controllers/policy#managed-cluster-policy-controllers" >}}) page to learn more about the `OperatorPolicy` API. |
| 10 | + |
| 11 | +## Prerequisites |
| 12 | + |
| 13 | +You must meet the following prerequisites to use the Operator policy controller: |
| 14 | + |
| 15 | +- Ensure [`kubectl`](https://kubernetes.io/docs/tasks/tools/install-kubectl) and [`kustomize`](https://kubectl.docs.kubernetes.io/installation/kustomize/) are installed. |
| 16 | + |
| 17 | +- Ensure [Golang](https://golang.org/doc/install) is installed, if you are planning to install from the source. |
| 18 | + |
| 19 | +- Ensure the `open-cluster-management` _policy framework_ is installed. See [Policy Framework]({{< ref "docs/getting-started/integration/policy-controllers/policy-framework" >}}) for more information. |
| 20 | + |
| 21 | +- Ensure Operator Lifecycle Manager (OLM) is installed on the managed clusters where you plan to deploy operators. See the [OLM Quick Start guide](https://olm.operatorframework.io/docs/getting-started/) for installation instructions. |
| 22 | + |
| 23 | +## Installing the operator policy controller |
| 24 | + |
| 25 | +The operator policy controller and the configuration policy controller run in the same `config-policy-controller` pod on managed clusters. The operator policy controller is disabled by default. |
| 26 | + |
| 27 | +Steps 1 and 2 will install the configuration policy controller, matching the steps in [Installing the configuration policy controller]({{< ref "docs/getting-started/integration/policy-controllers/configuration-policy">}}). |
| 28 | + |
| 29 | +If configuration policy controller is already installed, complete Step 3 onwards. |
| 30 | + |
| 31 | +### Deploy via Clusteradm CLI |
| 32 | + |
| 33 | +Ensure `clusteradm` CLI is installed and is newer than v0.3.0. Download and extract the [clusteradm binary](https://github.com/open-cluster-management-io/clusteradm/releases/latest). For more details see the [clusteradm GitHub page](https://github.com/open-cluster-management-io/clusteradm/blob/main/README.md#quick-start). |
| 34 | + |
| 35 | +1. Deploy the configuration policy controller (which includes the operator policy controller) to the managed clusters: |
| 36 | + |
| 37 | + ```Shell |
| 38 | + $ clusteradm addon enable addon --names config-policy-controller --clusters <cluster_name> --context ${CTX_HUB_CLUSTER} |
| 39 | + Deploying config-policy-controller add-on to namespaces open-cluster-management-agent-addon of managed cluster: <cluster_name> |
| 40 | + ``` |
| 41 | + |
| 42 | +2. Ensure the pod is running on the managed cluster with the following command: |
| 43 | + |
| 44 | + ```Shell |
| 45 | + $ kubectl get pods -n open-cluster-management-agent-addon --context ${CTX_MANAGED_CLUSTER} |
| 46 | + NAME READY STATUS RESTARTS AGE |
| 47 | + config-policy-controller-7f8fb64d8c-pmfx4 1/1 Running 0 44s |
| 48 | + ``` |
| 49 | + |
| 50 | +3. To enable operator policy controller on managed cluster `cluster1`, annotate the ManagedClusterAddon in the hub cluster: |
| 51 | + |
| 52 | + ```Shell |
| 53 | + $ kubectl annotate -n cluster1 managedclusteraddon config-policy-controller operator-policy-disabled=false --context ${CTX_HUB_CLUSTER} |
| 54 | + managedclusteraddon.addon.open-cluster-management.io/config-policy-controller annotated |
| 55 | + ``` |
| 56 | + |
| 57 | +4. On the managed cluster, confirm the operator policy controller was enabled by examining the `config-policy-controller` pod container arguments: |
| 58 | + |
| 59 | + ```Shell |
| 60 | + $ kubectl get pods -n open-cluster-management-agent-addon --context ${CTX_MANAGED_CLUSTER} |
| 61 | + NAME READY STATUS RESTARTS AGE |
| 62 | + config-policy-controller-5888b6cbc5-lvwdj 1/1 Running 0 30s |
| 63 | + |
| 64 | + $ kubectl describe pod config-policy-controller-5888b6cbc5-lvwdj -n open-cluster-management-agent-addon --context ${CTX_MANAGED_CLUSTER} | grep enable-operator-policy |
| 65 | + - --enable-operator-policy=true |
| 66 | + ``` |
| 67 | + |
| 68 | +## Sample operator policy |
| 69 | + |
| 70 | +After a successful deployment, test the policy framework and operator policy controller with a sample policy. |
| 71 | + |
| 72 | +For more information on how to use an `OperatorPolicy`, read the [Policy API concept section]({{< ref "docs/getting-started/integration/policy-controllers/policy-framework#policy" >}}). |
| 73 | + |
| 74 | +### Example: Deploy an external secrets operator |
| 75 | + |
| 76 | +The following example deploys an external secrets operator to a managed cluster using an `OperatorPolicy`. The ESO operator was chosen arbitrarily for the example. |
| 77 | + |
| 78 | +1. Create a Policy in Inform mode to scan for an external secrets operator in managed cluster `cluster1` in the `default` namespace. The `Policy` remediation action of `inform` will override the remediation action of the `OperatorPolicy`: |
| 79 | + |
| 80 | + ```Yaml |
| 81 | + apiVersion: policy.open-cluster-management.io/v1 |
| 82 | + kind: Policy |
| 83 | + metadata: |
| 84 | + name: policy-eso |
| 85 | + spec: |
| 86 | + remediationAction: inform |
| 87 | + disabled: false |
| 88 | + policy-templates: |
| 89 | + - objectDefinition: |
| 90 | + apiVersion: policy.open-cluster-management.io/v1beta1 |
| 91 | + kind: OperatorPolicy |
| 92 | + metadata: |
| 93 | + name: policy-eso |
| 94 | + spec: |
| 95 | + remediationAction: inform |
| 96 | + severity: medium |
| 97 | + complianceType: musthave |
| 98 | + upgradeApproval: None |
| 99 | + operatorGroup: |
| 100 | + namespace: default |
| 101 | + name: external-secrets-operator-group |
| 102 | + targetNamespaces: |
| 103 | + - default |
| 104 | + subscription: |
| 105 | + namespace: default |
| 106 | + name: external-secrets-operator |
| 107 | + channel: alpha |
| 108 | + source: operatorhubio-catalog |
| 109 | + sourceNamespace: olm |
| 110 | + startingCSV: external-secrets-operator.v0.11.0 |
| 111 | + versions: |
| 112 | + - external-secrets-operator.v0.11.0 |
| 113 | + --- |
| 114 | + apiVersion: policy.open-cluster-management.io/v1 |
| 115 | + kind: PlacementBinding |
| 116 | + metadata: |
| 117 | + name: binding-policy-eso |
| 118 | + placementRef: |
| 119 | + name: placement-policy-eso |
| 120 | + kind: Placement |
| 121 | + apiGroup: cluster.open-cluster-management.io |
| 122 | + subjects: |
| 123 | + - name: policy-eso |
| 124 | + kind: Policy |
| 125 | + apiGroup: policy.open-cluster-management.io |
| 126 | + --- |
| 127 | + apiVersion: cluster.open-cluster-management.io/v1beta1 |
| 128 | + kind: Placement |
| 129 | + metadata: |
| 130 | + name: placement-policy-eso |
| 131 | + spec: |
| 132 | + predicates: |
| 133 | + - requiredClusterSelector: |
| 134 | + celSelector: |
| 135 | + celExpressions: |
| 136 | + - managedCluster.metadata.name == "cluster1" |
| 137 | + ``` |
| 138 | +
|
| 139 | +2. Apply the policy to the hub cluster: |
| 140 | +
|
| 141 | + ```Shell |
| 142 | + $ kubectl apply -n default -f policy-eso.yaml --context ${CTX_HUB_CLUSTER} |
| 143 | + policy.policy.open-cluster-management.io/policy-eso created |
| 144 | + placementbinding.policy.open-cluster-management.io/binding-policy-eso created |
| 145 | + placement.cluster.open-cluster-management.io/placement-policy-eso created |
| 146 | + ``` |
| 147 | + |
| 148 | +3. Ensure the `default` namespace has a `ManagedClusterSetBinding` for a `ManagedClusterSet` with at least one managed cluster resource. See [Bind ManagedClusterSet to a namespace]({{< ref "docs/concepts/cluster-inventory/managedclusterset#bind-managedclusterset-to-a-namespace" >}}) for more information. |
| 149 | + |
| 150 | +4. Verify the managed cluster is selected by the `Placement`: |
| 151 | + |
| 152 | + ```Shell |
| 153 | + $ kubectl get -n default placementdecision placement-policy-eso-decision-1 -o yaml --context ${CTX_HUB_CLUSTER} |
| 154 | + |
| 155 | + ... |
| 156 | + status: |
| 157 | + decisions: |
| 158 | + - clusterName: cluster1 |
| 159 | + ``` |
| 160 | + |
| 161 | + The output shows the managed cluster `cluster1` is selected. |
| 162 | + |
| 163 | +5. Verify the Policy was propagated to the managed cluster: |
| 164 | + |
| 165 | + ```Shell |
| 166 | + $ kubectl get policy -A --context ${CTX_MANAGED_CLUSTER} |
| 167 | + NAMESPACE NAME REMEDIATION ACTION COMPLIANCE STATE AGE |
| 168 | + cluster1 default.policy-eso inform NonCompliant 11s |
| 169 | + ``` |
| 170 | + |
| 171 | +6. The policy is `NonCompliant`. Inspect the policy status: |
| 172 | + |
| 173 | + ```Shell |
| 174 | + $ kubectl describe policy default.policy-eso --context ${CTX_MANAGED_CLUSTER} -n cluster1 |
| 175 | + ... |
| 176 | + Status: |
| 177 | + Compliant: NonCompliant |
| 178 | + Details: |
| 179 | + Compliant: NonCompliant |
| 180 | + History: |
| 181 | + Event Name: default.policy-eso.18ab8e1a8ff67343 |
| 182 | + Last Timestamp: 2026-05-01T21:25:22Z |
| 183 | + Message: NonCompliant; the policy spec is valid, ... the Subscription required by the policy was not found ... |
| 184 | + ``` |
| 185 | + |
| 186 | + The policy is in `NonCompliant` state because the external secrets operator was not found on the managed cluster in the `default` namespace. |
| 187 | + |
| 188 | +7. To automatically install the operator on the managed cluster, patch the policy remediation action to `enforce`. Note the top-level Policy remediation action `enforce` will override the OperatorPolicy remediation action. |
| 189 | + |
| 190 | + ```Shell |
| 191 | + $ kubectl patch policy policy-eso -n default --context ${CTX_HUB_CLUSTER} --type=merge -p '{"spec": {"remediationAction": "enforce"}}' |
| 192 | + policy.policy.open-cluster-management.io/policy-eso patched |
| 193 | + ``` |
| 194 | + |
| 195 | +8. Verify the external secrets operator subscription was created: |
| 196 | + |
| 197 | + ```Shell |
| 198 | + $ kubectl get subscription -n default --context ${CTX_MANAGED_CLUSTER} |
| 199 | + NAME PACKAGE SOURCE CHANNEL |
| 200 | + external-secrets-operator external-secrets-operator operatorhubio-catalog alpha |
| 201 | + ``` |
| 202 | + |
| 203 | + The output shows the external secrets operator subscription is active. |
| 204 | + |
| 205 | +9. Verify the external secrets operator deployment is running: |
| 206 | + |
| 207 | + ```Shell |
| 208 | + $ kubectl get deployment -n default --context ${CTX_MANAGED_CLUSTER} |
| 209 | + NAME READY UP-TO-DATE AVAILABLE AGE |
| 210 | + external-secrets-operator-controller-manager 1/1 1 1 10m |
| 211 | + ``` |
| 212 | + |
| 213 | + The output shows the external secrets operator is deployed and running. |
| 214 | + |
| 215 | +### Cleanup: Remove the example operator |
| 216 | + |
| 217 | +By default, the operator policy controller will delete most of the objects created by the policy when the OperatorPolicy `spec.complianceType` is changed to `mustnothave` AND the policy `remediationAction` is set to `enforce`. |
| 218 | + |
| 219 | +1. Optional: edit the OperatorPolicy `spec.removalBehavior` to customize the objects to keep or delete. The default settings are: |
| 220 | + |
| 221 | + ```Yaml |
| 222 | + removalBehavior: |
| 223 | + clusterServiceVersions: Delete |
| 224 | + customResourceDefinitions: Keep |
| 225 | + subscriptions: Delete |
| 226 | + operatorGroups: DeleteIfUnused |
| 227 | + ``` |
| 228 | + |
| 229 | +2. Edit the `complianceType` to `mustnothave` in the OperatorPolicy spec, then re-apply the policy: |
| 230 | + |
| 231 | + ```Yaml |
| 232 | + apiVersion: policy.open-cluster-management.io/v1 |
| 233 | + kind: Policy |
| 234 | + metadata: |
| 235 | + name: policy-eso |
| 236 | + spec: |
| 237 | + remediationAction: enforce # Ensure remediationAction is set to `enforce` |
| 238 | + disabled: false |
| 239 | + policy-templates: |
| 240 | + - objectDefinition: |
| 241 | + apiVersion: policy.open-cluster-management.io/v1beta1 |
| 242 | + kind: OperatorPolicy |
| 243 | + metadata: |
| 244 | + name: policy-eso |
| 245 | + spec: |
| 246 | + complianceType: mustnothave # Edit this line to `mustnothave` |
| 247 | + ``` |
| 248 | + |
| 249 | + ```Shell |
| 250 | + $ kubectl apply -n default -f policy-eso.yaml --context ${CTX_HUB_CLUSTER} |
| 251 | + policy.policy.open-cluster-management.io/policy-eso configured |
| 252 | + placementbinding.policy.open-cluster-management.io/binding-policy-eso unchanged |
| 253 | + placement.cluster.open-cluster-management.io/placement-policy-eso unchanged |
| 254 | + ``` |
| 255 | + |
| 256 | +3. Verify the external secrets operator and all other objects created by the Policy were deleted in the managed cluster: |
| 257 | + |
| 258 | + ```Shell |
| 259 | + $ kubectl get deployment -n default --context ${CTX_MANAGED_CLUSTER} |
| 260 | + No resources found in default namespace. |
| 261 | +
|
| 262 | + $ kubectl get subscription -n default --context ${CTX_MANAGED_CLUSTER} |
| 263 | + No resources found in default namespace. |
| 264 | +
|
| 265 | + # repeat for other objects |
| 266 | + ``` |
| 267 | + |
| 268 | +4. Delete the Policy on the hub cluster. |
| 269 | + |
| 270 | + ```Shell |
| 271 | + $ kubectl delete -n default policy policy-eso --context ${CTX_HUB_CLUSTER} |
| 272 | + policy.policy.open-cluster-management.io "policy-eso" deleted |
| 273 | + ``` |
| 274 | + |
| 275 | +## Additional resources |
| 276 | + |
| 277 | +- [Policy API Concepts]({{< ref "docs/getting-started/integration/policy-controllers/policy" >}}) |
| 278 | +- [Policy Collection - Community examples](https://github.com/open-cluster-management-io/policy-collection) |
| 279 | +- [Operator Lifecycle Manager (OLM) documentation](https://olm.operatorframework.io/docs/) |
| 280 | +- [OperatorHub.io registry](https://operatorhub.io/) |
0 commit comments