Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
107 changes: 107 additions & 0 deletions community/CM-Configuration-Management/policy-node-image-lifecycle.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
# Prerequisite: create a ConfigMap on the hub cluster in each managed cluster's
# namespace BEFORE enforcing this policy. The hub templates below read from it.
#
# apiVersion: v1
# kind: ConfigMap
# metadata:
# name: image-gc-config
# namespace: <managed-cluster-name> # e.g. "prod-cluster-1"
# data:
# imageGCHighThresholdPercent: "65"
# imageGCLowThresholdPercent: "50"
# imageMinimumGCAge: "5m"
#
# The prune script's MAX_AGE_DAYS (default: 7) is set inside the MachineConfig.
# To change it, re-encode the script with the desired value and update the base64 blob.
#
# remediationAction is set to inform by default because enforcing triggers an
# MCP rollout that reboots worker nodes. Set to enforce only after reviewing
# the ConfigMap values and scheduling an appropriate maintenance window.
apiVersion: policy.open-cluster-management.io/v1
kind: Policy
metadata:
name: policy-node-image-lifecycle
annotations:
policy.open-cluster-management.io/standards: NIST SP 800-53
policy.open-cluster-management.io/categories: CM Configuration Management
policy.open-cluster-management.io/controls: CM-2 Baseline Configuration, SI-2 Flaw Remediation
spec:
remediationAction: inform
disabled: false
Comment thread
coderabbitai[bot] marked this conversation as resolved.
policy-templates:
- objectDefinition:
apiVersion: policy.open-cluster-management.io/v1
kind: ConfigurationPolicy
metadata:
name: policy-kubelet-image-gc
spec:
remediationAction: inform
severity: medium
object-templates:
- complianceType: musthave
objectDefinition:
apiVersion: machineconfiguration.openshift.io/v1
kind: KubeletConfig
metadata:
name: image-gc-policy
spec:
machineConfigPoolSelector:
matchLabels:
pools.operator.machineconfiguration.openshift.io/worker: ""
kubeletConfig:
imageGCHighThresholdPercent: '{{hub fromConfigMap .ManagedClusterName "image-gc-config" "imageGCHighThresholdPercent" | toInt hub}}'
imageGCLowThresholdPercent: '{{hub fromConfigMap .ManagedClusterName "image-gc-config" "imageGCLowThresholdPercent" | toInt hub}}'
imageMinimumGCAge: '{{hub fromConfigMap .ManagedClusterName "image-gc-config" "imageMinimumGCAge" hub}}'
- objectDefinition:
apiVersion: policy.open-cluster-management.io/v1
kind: ConfigurationPolicy
metadata:
name: policy-image-prune-timer
spec:
remediationAction: inform
severity: medium
object-templates:
- complianceType: musthave
objectDefinition:
apiVersion: machineconfiguration.openshift.io/v1
kind: MachineConfig
metadata:
name: 99-worker-image-gc-prune
labels:
machineconfiguration.openshift.io/role: worker
spec:
config:
ignition:
version: 3.2.0
storage:
files:
- path: /usr/local/bin/image-gc-prune.sh
mode: 0755
contents:
source: data:text/plain;charset=utf-8;base64,IyEvYmluL2Jhc2gKIyBQcnVuZSB1bnJlZmVyZW5jZWQgY29udGFpbmVyIGltYWdlcyBvbGRlciB0aGFuIE1BWF9BR0VfREFZUwpNQVhfQUdFX0RBWVM9NwpOT1c9JChkYXRlICslcykKQ1VUT0ZGPSQoKE5PVyAtIE1BWF9BR0VfREFZUyAqIDg2NDAwKSkKUkVNT1ZFRD0wClNLSVBQRUQ9MApSRUZFUkVOQ0VEPTAKVU5QQVJTQUJMRT0wCkZBSUxFRD0wCgojIEJ1aWxkIHNldCBvZiBpbWFnZSBJRHMgcmVmZXJlbmNlZCBieSBhbnkgY29udGFpbmVyIChvbmUgY2FsbCwgbm90IE4pCkNPTlRBSU5FUl9JTUFHRVM9JChjcmljdGwgcHMgLWEgLW8ganNvbiAyPi9kZXYvbnVsbCBcCiAgfCBqcSAtciAnLmNvbnRhaW5lcnNbXT8gfCAuaW1hZ2VSZWYgLy8gZW1wdHknIDI+L2Rldi9udWxsIFwKICB8IHNlZCAnc3wuKkB8fDsgc3xec2hhMjU2Onx8JyB8IHNvcnQgLXUpCgpmb3IgSU1HX0lEIGluICQoY3JpY3RsIGltYWdlcyAtcSk7IGRvCiAgU0hPUlRfSUQ9JChlY2hvICIkSU1HX0lEIiB8IHNlZCAnc3xec2hhMjU2Onx8JykKICBpZiBlY2hvICIkQ09OVEFJTkVSX0lNQUdFUyIgfCBncmVwIC1xRiAiJFNIT1JUX0lEIjsgdGhlbgogICAgUkVGRVJFTkNFRD0kKChSRUZFUkVOQ0VEICsgMSkpCiAgICBjb250aW51ZQogIGZpCgogICMgRXh0cmFjdCBjcmVhdGlvbiB0aW1lc3RhbXAgKGpxIGZvciBKU09OLCBkYXRlIC1kIGZvciBwYXJzaW5nKQogIFRTPSQoY3JpY3RsIGluc3BlY3RpICIkSU1HX0lEIiAyPi9kZXYvbnVsbCBcCiAgICB8IGpxIC1yICcoLmluZm8uaW1hZ2VTcGVjLmNyZWF0ZWQgLy8gLnN0YXR1cy5jcmVhdGVkQXQgLy8gZW1wdHkpJyAyPi9kZXYvbnVsbCkKICBDUkVBVEVEX1RTPSQoZGF0ZSAtZCAiJFRTIiArJXMgMj4vZGV2L251bGwgfHwgZWNobyAwKQoKICBpZiBbICIkQ1JFQVRFRF9UUyIgLWVxIDAgXTsgdGhlbgogICAgVU5QQVJTQUJMRT0kKChVTlBBUlNBQkxFICsgMSkpCiAgICBjb250aW51ZQogIGZpCgogIGlmIFsgIiRDUkVBVEVEX1RTIiAtbHQgIiRDVVRPRkYiIF07IHRoZW4KICAgIGlmIGNyaWN0bCBybWkgIiRJTUdfSUQiIDI+JjE7IHRoZW4KICAgICAgUkVNT1ZFRD0kKChSRU1PVkVEICsgMSkpCiAgICBlbHNlCiAgICAgIEZBSUxFRD0kKChGQUlMRUQgKyAxKSkKICAgIGZpCiAgZWxzZQogICAgU0tJUFBFRD0kKChTS0lQUEVEICsgMSkpCiAgZmkKZG9uZQoKbG9nZ2VyIC10IGltYWdlLWdjLXBydW5lICJyZWZlcmVuY2VkPSRSRUZFUkVOQ0VEIHJlbW92ZWQ9JFJFTU9WRUQgc2tpcHBlZD0kU0tJUFBFRCB1bnBhcnNhYmxlPSRVTlBBUlNBQkxFIGZhaWxlZD0kRkFJTEVEIgo=

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It should be possible to do the base64 encoding in the policy engine, although it takes a few tricks with templates, and some changing of quote types. Basically, the construction is {{- list <code lines> "" | join "\n" | base64enc -}}

Honestly, after wrangling this together, I'm not sure if it's worth it... But at the very least, the script should be documented in a comment here in the collection, and if it's been copied from somewhere else, a link back to the source.

Suggested change
source: data:text/plain;charset=utf-8;base64,IyEvYmluL2Jhc2gKIyBQcnVuZSB1bnJlZmVyZW5jZWQgY29udGFpbmVyIGltYWdlcyBvbGRlciB0aGFuIE1BWF9BR0VfREFZUwpNQVhfQUdFX0RBWVM9NwpOT1c9JChkYXRlICslcykKQ1VUT0ZGPSQoKE5PVyAtIE1BWF9BR0VfREFZUyAqIDg2NDAwKSkKUkVNT1ZFRD0wClNLSVBQRUQ9MApSRUZFUkVOQ0VEPTAKVU5QQVJTQUJMRT0wCkZBSUxFRD0wCgojIEJ1aWxkIHNldCBvZiBpbWFnZSBJRHMgcmVmZXJlbmNlZCBieSBhbnkgY29udGFpbmVyIChvbmUgY2FsbCwgbm90IE4pCkNPTlRBSU5FUl9JTUFHRVM9JChjcmljdGwgcHMgLWEgLW8ganNvbiAyPi9kZXYvbnVsbCBcCiAgfCBqcSAtciAnLmNvbnRhaW5lcnNbXT8gfCAuaW1hZ2VSZWYgLy8gZW1wdHknIDI+L2Rldi9udWxsIFwKICB8IHNlZCAnc3wuKkB8fDsgc3xec2hhMjU2Onx8JyB8IHNvcnQgLXUpCgpmb3IgSU1HX0lEIGluICQoY3JpY3RsIGltYWdlcyAtcSk7IGRvCiAgU0hPUlRfSUQ9JChlY2hvICIkSU1HX0lEIiB8IHNlZCAnc3xec2hhMjU2Onx8JykKICBpZiBlY2hvICIkQ09OVEFJTkVSX0lNQUdFUyIgfCBncmVwIC1xRiAiJFNIT1JUX0lEIjsgdGhlbgogICAgUkVGRVJFTkNFRD0kKChSRUZFUkVOQ0VEICsgMSkpCiAgICBjb250aW51ZQogIGZpCgogICMgRXh0cmFjdCBjcmVhdGlvbiB0aW1lc3RhbXAgKGpxIGZvciBKU09OLCBkYXRlIC1kIGZvciBwYXJzaW5nKQogIFRTPSQoY3JpY3RsIGluc3BlY3RpICIkSU1HX0lEIiAyPi9kZXYvbnVsbCBcCiAgICB8IGpxIC1yICcoLmluZm8uaW1hZ2VTcGVjLmNyZWF0ZWQgLy8gLnN0YXR1cy5jcmVhdGVkQXQgLy8gZW1wdHkpJyAyPi9kZXYvbnVsbCkKICBDUkVBVEVEX1RTPSQoZGF0ZSAtZCAiJFRTIiArJXMgMj4vZGV2L251bGwgfHwgZWNobyAwKQoKICBpZiBbICIkQ1JFQVRFRF9UUyIgLWVxIDAgXTsgdGhlbgogICAgVU5QQVJTQUJMRT0kKChVTlBBUlNBQkxFICsgMSkpCiAgICBjb250aW51ZQogIGZpCgogIGlmIFsgIiRDUkVBVEVEX1RTIiAtbHQgIiRDVVRPRkYiIF07IHRoZW4KICAgIGlmIGNyaWN0bCBybWkgIiRJTUdfSUQiIDI+JjE7IHRoZW4KICAgICAgUkVNT1ZFRD0kKChSRU1PVkVEICsgMSkpCiAgICBlbHNlCiAgICAgIEZBSUxFRD0kKChGQUlMRUQgKyAxKSkKICAgIGZpCiAgZWxzZQogICAgU0tJUFBFRD0kKChTS0lQUEVEICsgMSkpCiAgZmkKZG9uZQoKbG9nZ2VyIC10IGltYWdlLWdjLXBydW5lICJyZWZlcmVuY2VkPSRSRUZFUkVOQ0VEIHJlbW92ZWQ9JFJFTU9WRUQgc2tpcHBlZD0kU0tJUFBFRCB1bnBhcnNhYmxlPSRVTlBBUlNBQkxFIGZhaWxlZD0kRkFJTEVEIgo=
source: 'data:text/plain;charset=utf-8;base64,{{- list
"#!/bin/bash"
"# Prune unreferenced container images older than MAX_AGE_DAYS"
"MAX_AGE_DAYS=7"
"NOW=$(date +%s)"
"CUTOFF=$((NOW - MAX_AGE_DAYS * 86400))"
"REMOVED=0"
"SKIPPED=0"
"REFERENCED=0"
"UNPARSABLE=0"
"FAILED=0"
""
"# Build set of image IDs referenced by any container (one call, not N)"
"CONTAINER_IMAGES=$(crictl ps -a -o json 2>/dev/null \\"
" | jq -r \".containers[]? | .imageRef // empty\" 2>/dev/null \\"
" | sed \"s|.*@||; s|^sha256:||\" | sort -u)"
""
"for IMG_ID in $(crictl images -q); do"
" SHORT_ID=$(echo \"$IMG_ID\" | sed \"s|^sha256:||\")"
" if echo \"$CONTAINER_IMAGES\" | grep -qF \"$SHORT_ID\"; then"
" REFERENCED=$((REFERENCED + 1))"
" continue"
" fi"
""
" # Extract creation timestamp (jq for JSON, date -d for parsing)"
" TS=$(crictl inspecti \"$IMG_ID\" 2>/dev/null \\"
" | jq -r \"(.info.imageSpec.created // .status.createdAt // empty)\" 2>/dev/null)"
" CREATED_TS=$(date -d \"$TS\" +%s 2>/dev/null || echo 0)"
""
" if [ \"$CREATED_TS\" -eq 0 ]; then"
" UNPARSABLE=$((UNPARSABLE + 1))"
" continue"
" fi"
""
" if [ \"$CREATED_TS\" -lt \"$CUTOFF\" ]; then"
" if crictl rmi \"$IMG_ID\" 2>&1; then"
" REMOVED=$((REMOVED + 1))"
" else"
" FAILED=$((FAILED + 1))"
" fi"
" else"
" SKIPPED=$((SKIPPED + 1))"
" fi"
"done"
""
"logger -t image-gc-prune \"referenced=$REFERENCED removed=$REMOVED skipped=$SKIPPED unparsable=$UNPARSABLE failed=$FAILED\""
""
| join "\n" | base64enc -}}'

systemd:
units:
- name: image-gc-prune.service
enabled: false
contents: |
[Unit]
Description=Prune unreferenced container images older than 7 days
Wants=crio.service
After=crio.service

[Service]
Type=oneshot
ExecStart=/usr/local/bin/image-gc-prune.sh
- name: image-gc-prune.timer
enabled: true
contents: |
[Unit]
Description=Weekly container image prune

[Timer]
OnCalendar=Sun *-*-* 02:00:00
RandomizedDelaySec=3600
Persistent=true

[Install]
WantedBy=timers.target
1 change: 1 addition & 0 deletions community/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ Policy | Description | Prerequisites
[Install OpenShift-Gitops](./CM-Configuration-Management/policy-openshift-gitops.yaml) | Use this policy to install the Red Hat OpenShift GitOps operator which can be used to install and configure Gitops, Tekton and ArgoCD | Requires OpenShift 4.x. Check the [documentation](https://docs.openshift.com/container-platform/latest/cicd/gitops/gitops-release-notes.html) for more information.
[Configure OpenShift-Gitops with the Policy Generator](./CM-Configuration-Management/policy-openshift-gitops-policygenerator.yaml) | Use this policy to configure the Red Hat OpenShift GitOps operator to run the policy generator | Requires OpenShift 4.x and requires the Openshift GitOps operator to be installed. Check the [documentation](https://access.redhat.com/documentation/en-us/red_hat_advanced_cluster_management_for_kubernetes/2.7/html/governance/governance#policy-gen-install-on-openshift-gitops) for more details.
[Configure OpenShift Image-Pruner](./CM-Configuration-Management/policy-resiliency-image-pruner.yaml) | Use this policy to configure the OpenShift Image-Pruner | OpenShift 4.x is required. Check the [documentation](https://docs.openshift.com/container-platform/latest/applications/pruning-objects.html) for more information
[Configure Node Image Lifecycle](./CM-Configuration-Management/policy-node-image-lifecycle.yaml) | Use this policy to tune kubelet image garbage collection thresholds and deploy a weekly CronJob to prune unused container images from worker nodes. Prevents stale images from prior cluster versions accumulating on nodes, which can cause false positives in image-based security scanners. | OpenShift 4.8+ is required.
[Policy to configure a POD Disruption Budget](./CM-Configuration-Management/policy-engineering-pod-disruption-budget.yaml) | use this policy to configure a Pod Disruption Budget| Check Kubernetes [documentation](https://kubernetes.io/docs/tasks/run-application/configure-pdb/) for more information
[Policy to configure a cluster autoscaler](./CM-Configuration-Management/policy-autoscaler.yaml) | Use this policy to configure a `ClusterAutoscaler`. | OpenShift 4.x is required. Check the OpenShift [documentation](https://docs.openshift.com/container-platform/latest/post_installation_configuration/cluster-tasks.html#informational-resources_post-install-cluster-tasks) for more information.
[Policy to configure Ingress Controller](./CM-Configuration-Management/policy-ingress-controller.yaml) | Use this policy to configure the IngressController | OpenShift 4.x is required. Check the OpenShift [documentation](https://docs.openshift.com/container-platform/latest/post_installation_configuration/cluster-tasks.html#informational-resources_post-install-cluster-tasks) for more information on how to customize this policy.
Expand Down