Overview

The Governance and risk framework in Red Hat Advanced Cluster Management for Kubernetes has a powerful feature to deploy a set of policies across managed clusters of your choice. To make the policy framework even more powerful, there is a growing open source community, with a reusable set of useful policies, that is available to contribute and use. These policies can enforce specific configuration settings or monitor different security aspects of your Kubernetes clusters.

To get started with this community, let’s take a look at how to contribute a policy, and then how to use the contributed policies in a Red Hat Advanced Cluster Management installation. The best way to use the contributed policies is with GitOps, which is an automated way to track, manage, and control your policies with a Git repository such as GitHub.

Getting Started

The collection of policies can be found in the policy-collection repository on GitHub. Policies are grouped into the following two folders:

  • The stable folder contains policies that are supported by Red Hat Advanced Cluster Management.
  • The community folder contains policies that are contributed to and by the open source community.

Policies in each of these folders are further organized according to NIST Special Publication 800-53.

To start, we must have our own fork to develop our own policies. We can select and customize the set of policies that we’d like to have deployed to our clusters with GitOps. Navigate to the policy-collection repository and create your own fork of the repository. See the GitHub documentation for more information about forks:

git-fork-1

Copy the URL for the newly forked policy-collection Git repository: 

git-clone (1)

Now, clone the fork to your local environment with the following command:

git clone https://github.com/<your-username>/policy-collection.git

Contributing a custom policy

In this section, learn how to create a custom policy, validate that the custom policy is added, and add your policy to the policy-collection repository.

Create

For our example, we’ll create a policy to verify least privilege roles by checking that no role exists in the default namespace. We’ll apply it to clusters that have the label environment:dev.

We’ll start with stable/AC-Access-Control/policy-role.yaml to use as a guide. Learn more about the policy structure, see Policy overview in the Red Hat Advanced Cluster Management for Kubernetes documentation.

Use NIST Special Publication 800-53 to determine the correct NIST 800-53 Control Family for the policy. For our example policy, least privilege roles is a part of the AC - Access Control.

Complete the following steps to create a custom policy:

  1. Create a new branch to develop a custom policy:

    git checkout -b no-wildcard-roles
  2. Create a new YAML file in the community folder that corresponds to our Security Control Family. Our policy is in policy-collection/community/AC-Access-Control. View the following policy for this activity:

    apiVersion: policy.open-cluster-management.io/v1
    kind: Policy
    metadata:
    name: policy-disallowed-roles
    annotations:
    policy.open-cluster-management.io/standards: NIST SP 800-53
    policy.open-cluster-management.io/categories: AC Access Control
    policy.open-cluster-management.io/controls: AC-6 Least Privilege
    spec:
    remediationAction: inform
    disabled: false
    policy-templates:
    - objectDefinition:
    apiVersion: policy.open-cluster-management.io/v1
    kind: ConfigurationPolicy
    metadata:
    name: policy-disallowed-roles-sample-role
    spec:
    remediationAction: inform # will be overridden by remediationAction in parent policy
    severity: high
    namespaceSelector:
    exclude: ["kube-*"]
    include: ["default"]
    object-templates:
    - complianceType: mustnothave
    objectDefinition:
    apiVersion: rbac.authorization.k8s.io/v1
    kind: Role
    rules:
    - apiGroups:
    - '*'
    resources:
    - '*'
    verbs:
    - '*'
    ---
    apiVersion: policy.open-cluster-management.io/v1
    kind: PlacementBinding
    metadata:
    name: binding-policy-disallowed-roles
    placementRef:
    name: placement-policy-disallowed-roles
    kind: PlacementRule
    apiGroup: apps.open-cluster-management.io
    subjects:
    - name: policy-disallowed-roles
    kind: Policy
    apiGroup: policy.open-cluster-management.io
    ---
    apiVersion: apps.open-cluster-management.io/v1
    kind: PlacementRule
    metadata:
    name: placement-policy-disallowed-roles
    spec:
    clusterConditions:
    - status: "True"
    type: ManagedClusterConditionAvailable
    clusterSelector:
    matchExpressions:
    - {key: environment, operator: In, values: ["dev"]}

    You'll notice that the YAML file you created contains three resources (separated by ---), which are required components of a policy:

    • Policy: Defines the policy(s) to deploy and their actions.
    • PlacementBinding: Binds the PlacementRule to our policy so that it can be propagated.
    • PlacementRule: Defines the clusters where the policy is applied. By default, policies that are in the community use a standard PlacementRule that applies the policy on each managed cluster with the following label: environment: dev. View the format of the matchExpressions in the previous sample policy.
  3. Update the table in community README with the custom policy details by adding a new row to the table corresponding to the Security Control Family that we identified in the previous step. Provide the appropriate information in the format that is presented. For more about formatting the content, see GitHub’s Markdown guide. Your entry might resemble the following syntax:

    [policy-name](./control-family/path/to/yaml) | <description> | <prerequisites>

Validate

To validate the creation of your policy, use the Red Hat Advanced Cluster Management web console to create a new policy using our custom YAML file.

  1. Log in to the Red Hat Advanced Cluster Management web console, click the navigation menu, and select Govern risk: 

    rhacm-nav
  2. Click the Create policy button to create a new policy. Replace the entire contents of the policy editor with the contents of the policy you have created: 
    rhacm-create-1
  3. Click Create to create the policy on the hub cluster and propagate it to the selected managed clusters. Make sure the policy is created successfully and alerts on non-compliant clusters having the environment:dev label as expected.

    Note: You can also validate your policy using GitOps instead of manually creating the policy. See the Using GitOps section for more details on deploying your policies with GitOps.

It is important to remember to set your policy to only inform users of policy violations by default and not enforce. You can test the enforcement of the policy if your policy supports it. For more information on which policies support enforcement, see Policy controllers.

Be sure that your contributed policy is set to enforce if the intent of your policy requires something to be created. For example, a policy that creates an operator would be expected to enforce creation of the operator. On the other hand, it is recommended for a configuration policy to be set to inform by default so that the policy does not change cluster resources automatically when it is applied.

Contribute

Our contribution is ready to go! All that’s left to do is to create a pull request that can be reviewed by the Red Hat Advanced Cluster Management team. We have two file modifications in this case, the community/README.md and our custom YAML file in its Security Family Control folder. Let’s create a commit that can be used to populate a pull request.

  • First return to the policy-collection directory and add all changed files with the following command:

    git add .
    • Run git status and make sure all of the changes that you expect have been added to the commit. If you make any additional changes, you’ll need to make sure to run git add again for those files.
  • Next, commit the changes with a descriptive message with the following command:

    git commit -m "create a least privilege policy that looks for a role using wildcards"
  • Push the changes to your repository by running the following command:

    git push origin no-wildcard-roles
  • Copy and paste the URL to the pull request into your browser, which is provided in the output of the push command. Push changes from your fork back to the open-cluster-management/policy-collection repository.

    Alternatively, view the policy-collection compare page, select compare across forks, and select your fork and branch from the drop-down list.

    Tip: Provide comments in the pull request to help describe any details about your policy that may be useful to reviewers.

  • Add the policy-collection OWNERS as reviewers to the pull request so that they receive notifications about the pull request: 

    git-pr-addreviewers
  • After the pull request is reviewed and approved, select Squash and Merge in your pull request to merge the changes into the master branch of the repository: 

    git-pr-squashandmerge

Using GitOps

Contributing your own policy is only part of the rewards of working with Red Hat Advanced Cluster Management. Now let’s use our policy along with other contributed policies, to create a set of policies to apply to managed clusters using a GitOps flow. GitOps allows us to automate and track policy updates and creation through a Git repository.

Many policies have been contributed to the policy-collection repository, but there may be some you don’t want to use with your managed clusters. You can use the repository you have already forked, or you can create a new fork to build your own custom set of policies for your managed clusters.

Customize

Customize your local repository by consolidating the stable and community policies into a single folder and removing the policies you do not want to use.

Make sure you're in your local policy-collection repository on the master branch (or your main default branch for GitOps). Create a new directory in the repository to hold the policies you want to deploy:

mkdir my-policies

Next, let’s copy all of the community and stable policies into this directory. Start with the community policies first, in case the stable folder contains duplicates of what is available in the community. Run the following commands to copy the policies:

cp -R community/* my-policies/
cp -R stable/* my-policies/

Now that you have all of the policies in a single parent directory structure, you can make all the edits you want to these policies.

Tips:

  • It is recommended to remove the policies you are not planning to use.
  • Spend some time going through the policies to make sure you understand the following criteria for each policy:
    • Purpose: Understand what the policy does.

    • Remediation Action: Does the policy only inform you of compliance, or enforce the policy and make changes? Look at spec.remediationAction. If changes are enforced, make sure you understand the functional expectation. Remember to check which policies support enforcement. For more information, view the Validate section.

      Note: The spec.remediationAction set for the Policy overrides any remediation action that is set in the individual spec.policy-templates.

    • Placement: What clusters is the policy deployed to? By default, most policies target the clusters with the environment: dev label. Some policies may target OpenShift clusters or another label. You can update or add additional labels to include other clusters. When there is no specific value, the policy is applied to all of your clusters. You can also create multiple copies of a policy and customize each one if you want to use a policy configured one way for one set of clusters and configured another way for another set of clusters.

Commit

After you are satisfied with the changes you have made to the my-policies directory, push your changes to Git so that they can be accessed by your cluster. Complete the following steps to make a commit:

  1. From your terminal, run git status to view your recent changes. All of the changes should be in the my-policies directory, which is our new directory. Add your new directory to the list of changes to be committed with the following command:

    git add my-policies/
  2. Commit the changes. Feel free to customize the message as desired, run the following command:

    git commit -m “Policies to deploy to the hub cluster”
  3. Push the changes to your Git repository’s main default branch used for GitOps (this command uses the master branch):

    git push origin master

    Note: For this example we are not detailing how Git should be used, but showing the basics of how to use policies with GitOps, so you may have a different workflow to get changes to the desired branch of the repository.

Deploy

After pushing your changes, it’s time to deploy the policies to your Red Hat Advanced Cluster Management installation. Post-deployment, your hub cluster is connected to your Git repository so that any further changes to your chosen branch of the Git repository is reflected in your cluster. From the policy-collection folder, run cd deploy and use these steps:

  1. Make sure your command line interface (CLI) is configured to create resources on the correct cluster with the following command: oc cluster-info. The output of the command should display the API server details for the cluster where Red Hat Advanced Cluster Management is installed. If the correct URL is not displayed, configure your CLI to point to the correct cluster.

  2. Create a namespace where your policies are created. Alternatively, you can select an existing namespace, but since these are automated you'll likely want to have them in a dedicated namespace to control access and identify them readily. Run the following command to create a namespace: oc create namespace policy-namespace.

  3. Run this command to deploy the policies to your cluster. Replace <your-repository> with your Git username or repository name:

    ./deploy.sh -u https://github.com/<your-repository>/policy-collection -p my-policies -n policy-namespace

    For reference, the full list of arguments for the deploy.sh script uses the following syntax:

    ./deploy.sh [-u <url>] [-b <branch>] [-p <path/to/dir>] [-n <namespace>] [-a|--name <resource-name>]

    View the explanation for each argument:

    • URL: The URL to the repository that you forked from the main policy-collection repository (default URL is https://github.com/open-cluster-management/policy-collection.git).

    • Branch: Branch of the Git repository to point to (default branch is master).

    • Subdirectory Path: The subdirectory path you created to contain the policies you want to use. In the previous sample, we used the my-policies subdirectory, but you could also specify a path like my-policies/AC-Access-Control (defaults to stable).

    • Namespace: The namespace where the resources and policies are created on the Red Hat Advanced Cluster Management hub cluster. These instructions use the policy-namespace namespace (defaults to policies).

    • Name Prefix: Prefix for the Channel and Subscription resources (defaults to demo-stable-policies).

    The deploy.sh script creates Channel and Subscription resources in your hub cluster. The Channel connects to the Git repository, and the Subscription specifies the data to bring to the cluster through the Channel. In this way, all policies defined in the specified subdirectory is created on your hub. After the policies are created by the Subscription, Red Hat Advanced Cluster Management analyzes the policies and creates additional policy resources in the namespace associated with each managed cluster that the policy is applied to, based on the defined PlacementRule. The policy is then copied down to the managed cluster from its respective managed cluster namespace on the hub. In that way, the policies in your Git repository are pushed to all managed clusters that have labels that match the clusterSelector defined in a policy's PlacementRule.

After running the deploy.sh script, anyone with access to the repository can commit changes to the branch, which pushes new policies or changes to existing policies on your clusters. Policies have permissions to perform any configuration you define, so be sure you know who has access to your repository's branch. It is recommended to create a process so that changes in your Git repository do not get into the branch associated with the Channel, unless proper validation and reviews have been performed.

Verify

After you complete the previous steps, log in to the Red Hat Advanced Cluster Management web console. Expand the navigation menu and select “Govern risk”: 

rhacm-nav

The policies from your repository should be present and you might need to investigate any issues, so further refinements can be made to the policies. At this point, review each policy and ask yourself the following questions:

  • Do I know why this policy is compliant or non-compliant on the clusters it was distributed to?

  • Are the policies applied to the correct clusters?

  • If this policy was not distributed to any clusters, do I know why?

If taking a look at these questions for each policy results in more changes, go back to your my-policies directory and make those changes. Then commit those changes and push them to your repository’s branch used for this GitOps. The changes are applied to Red Hat Advanced Cluster Management automatically. It is not required to run the deploy.sh script again. Continue iterating on the policies until you are satisfied with the operation of the policies.

Identifying

Generally you’ll want your GitOps policies to reside in their own namespace so you can identify them readily and control access to them. However, policies created using GitOps also have annotations that are applied automatically. Take a look at any of your policies created by GitOps and notice annotations similar to the following information:

apps.open-cluster-management.io/hosting-deployable: policies/deploy-stable-policies-Policy-policy-role9

apps.open-cluster-management.io/hosting-subscription: policies/demo-policies

apps.open-cluster-management.io/sync-source: subgbk8s-policies/demo-policies

GitOps annotations are valuable to see which subscription created the policy. However, you may want to add your own labels to your policies so you can write runtime queries that select policies based on labels. For example, you can add a label to a policy with the following command:

oc label policy <policy-name> -n <policy-namespace> <key>=<value>

Then you can query policies that have labels with the following command:

oc get policies -n <policy-namespace> -l <key>=<value>

Conclusion

Hopefully, it is easily understood how to contribute new policies to the policy-collection repository and how to take advantage of what policies are available today using GitOps. As you develop and use policies to manage clusters with Red Hat Advanced Cluster Management, remember to contribute the policies that you create back to the community--we’ll get stronger together!

For more details on using policies with Red Hat Advanced Cluster Management, see the blog Comply to standards using policy based governance of Red Hat Advanced Cluster Management for Kubernetes.