diff --git a/community/CM-Configuration-Management/acm-volsync-hub-backup/README.md b/community/CM-Configuration-Management/acm-volsync-hub-backup/README.md new file mode 100644 index 000000000..18fd934bc --- /dev/null +++ b/community/CM-Configuration-Management/acm-volsync-hub-backup/README.md @@ -0,0 +1,240 @@ +# ACM Hub PV backup and restore using volsync + +ACM Hub PVC backup and restore with volsync using ACM policies + +Hub PVC with the `cluster.open-cluster-management.io/volsync` label are being backed up and could be restored on another hub using these volsync policies. The PVC label's value can be any string. + +``` +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: global-hub-postgres-0 + namespace: global-hub + labels: + cluster.open-cluster-management.io/volsync: gh +spec: +``` + +------ + +- [List of PolicySets](#list-of-policysets) +- [List of Policies](#list-of-policies) +- [Policies configuration files](#policies-configuration-files) + - [Backup hub policies](#backup-hub-policies) + - [Restore hub policies](#restore-hub-policies) +- [Scenario](#scenario) +- [References](#references) + +------ + + + +## List of PolicySets + +PolicySet | Description +-------------------------------------------| ----------- +[acm-volsync-policyset](./acm-volsync-policyset.yaml) | This PolicySet is used to place the volsync policies on the hub, using the placement which matches the `local-cluster` or any managed cluster with the `is-hub=true` label. Using this label the policy can be placed on any managed cluster where the ACM operator is installed. + +![Volsync PolisySet](images/policyset.png) + +## List of Policies + +Policy | Description +-------------------------------------------| ----------- +[acm-volsync-config](./acm-volsync-config.yaml) | Trigerred to run on the hub only if the hub has any PVCs with the `cluster.open-cluster-management.io/volsync` label. It installs the volsync-addon on the hub or any managed cluster matching the `acm-volsync-policyset` PolicySet's placement. It reports on volsync missing configuration: reports if the user had not create the `restic-secret` Secret and `volsync-config` ConfigMap resources under the PolicySet namespace. The Secret is used by volsync to connect to the storage location where the PVC snapshot is stored. The ConfigMap is used to define the ReplicationSource configuration, as defined [here](https://access.redhat.com/login?redirectTo=https%3A%2F%2Faccess.redhat.com%2Fdocumentation%2Fen-us%2Fred_hat_advanced_cluster_management_for_kubernetes%2F2.8%2Fhtml%2Fbusiness_continuity%2Fbusiness-cont-overview%23restic-backup-volsync). +[acm-volsync-source](./acm-volsync-source.yaml) | Creates a volsync ReplicationSource for all PVCs with the `cluster.open-cluster-management.io/volsync` label. +[acm-volsync-destination](./acm-volsync-destination.yaml) | In a restore hub backup operation, when the credentials backup is restored on a new hub, it creates a volsync ReplicationDestination for all PVCs with the `cluster.open-cluster-management.io/volsync` label. This is because the `acm-volsync-source` creates a set of configuration ConfigMaps defining the PVCs for which a snapshot is stored. These ConfigMaps have the `cluster.open-cluster-management.io/backup` backup label so thy are backed up by the hub credentials backup. They are used to recreate the PVCs on the restore hub. + + +### Policies + +![Volsync Policies](images/policies.png) + + +### Configuration Policy + +The `acm-volsync-config` Policy validates the configuration for both types of hubs ( backup or restore ). If any PVC is found with the cluster.open-cluster-management.io/volsync label, it installs the volsync addon and verifies the user had created the restic-secret used to connect to the storage where the snapshot are saved. + + +![Volsync Config Policy](images/config_policy.png) + +### Backup Hub Policies + +Volsync Source Policy: + +![Volsync Source Policy](images/backup_source_policy.png) + +Volsync Source Policy Templates: + +![Volsync Source Policy](images/backup_source_policy_1.png) + +Policy acm-volsync-destination is not running since this is identified as a backup hub : + +![Volsync Destination Policy](images/backup_dest_policy.png) + +### Restore Hub Policies + +Volsync Destination Policy: + +![Volsync Destination Policy](images/restore_dest_policy.png) + +Volsync Destination Policy Templates: + +![Volsync Destination Policy Templates](images/restore_dest_policy_1.png) + +Policy acm-volsync-source is not running since this is identified as a restore hub : + +![Volsync Destination Policy](images/restore_source_policy.png) + +## Policies configuration files + + +volsync label cluster.open-cluster-management.io/volsync set on PVC + +``` +kind: PersistentVolumeClaim +apiVersion: v1 +metadata: + name: mongo-storage + namespace: pacman-restore + finalizers: + - kubernetes.io/pvc-protection + labels: + cluster.open-cluster-management.io/volsync: pacman-restore +spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 8Gi + volumeName: pvc-3b5b2975-77a4-452f-b14f-8eefed7454a5 + storageClassName: gp3-csi + volumeMode: Filesystem +``` + + +### Created by the ACM user + +#### volsync-config + +Created by the user on the backup hub. +Used to define the volsync ReplicationSource configuration, as defined [here](https://access.redhat.com/login?redirectTo=https%3A%2F%2Faccess.redhat.com%2Fdocumentation%2Fen-us%2Fred_hat_advanced_cluster_management_for_kubernetes%2F2.8%2Fhtml%2Fbusiness_continuity%2Fbusiness-cont-overview%23restic-backup-volsync) + + +``` +kind: ConfigMap +apiVersion: v1 +metadata: + name: volsync-config + namespace: open-cluster-management-backup + labels: + cluster.open-cluster-management.io/backup: volsync +data: + cacheCapacity: 1Gi + copyMethod: Snapshot + pruneIntervalDays: '2' + repository: restic-secret-vb + retain_daily: '2' + retain_hourly: '3' + retain_monthly: '1' + trigger_schedule: 0 */2 * * * +``` + + +#### restic-secret + +Created by the user + +Used to define the volsync ReplicationSource configuration, as defined [here](https://access.redhat.com/login?redirectTo=https%3A%2F%2Faccess.redhat.com%2Fdocumentation%2Fen-us%2Fred_hat_advanced_cluster_management_for_kubernetes%2F2.8%2Fhtml%2Fbusiness_continuity%2Fbusiness-cont-overview%23restic-backup-volsync) + + +``` +kind: Secret +apiVersion: v1 +metadata: + name: restic-secret + namespace: open-cluster-management-backup + labels: + cluster.open-cluster-management.io/backup: volsync +data: + AWS_ACCESS_KEY_ID: a2V5 + AWS_SECRET_ACCESS_KEY: a2V5 + RESTIC_PASSWORD: a2V5 + RESTIC_REPOSITORY: >- + czM6aHR0cDovL21pbmlvLm1pbmlvLnN2Yy5jbHVzdGVyLmxvY2FsOjkwMDAvbXktYnVja2V0 +type: Opaque +``` + + +### Generated by the policy + +#### volsync-config-info- + +Created by the volsync policy on the backup hub, for each PVC; uses the PVCs settings +This resource is backed up and used by the volsync ReplicationDestination to recreate the PV on the restore hub. + +``` +kind: ConfigMap +apiVersion: v1 +metadata: + name: volsync-config-info-mongo-storage + namespace: pacman-ns + labels: + cluster.open-cluster-management.io/backup: volsync +data: + resources.accessModes: ReadWriteOnce + resources.requests.storage: 8Gi + storageClassName: gp3-csi + volumeMode: Filesystem +``` + + +#### volsync-config-pvcs + +Created by the policy on the backup hub; lists all PVCs that need to be restored. This resource is backed up + +``` +kind: ConfigMap +apiVersion: v1 +metadata: + name: volsync-config-pvcs + namespace: open-cluster-management-backup + labels: + app: volsync-config-pvcs + cluster.open-cluster-management.io/backup: volsync + data: + pvcs: 'pacman-restore#mongo-storage##pacman-vb#mongo-storage##pacman#mongo-storage' +``` + + + + +## Scenario + +ACM components installed on the hub +Add the cluster.open-cluster-management.io/volsync label to the PVC to be backed up + + +ACM user, on Primary hub: +1. Enables backup on MultiClusterHub. This installs the hub backup component +2. The user manually installs the policy from the community project + - The above are open to review and decision +3. Creates a BackupSchedule + - The volsync policy informs the user if missing the volsync restic-secret secret and volsync-config ConfigMap +3. User creates the restic-secret secret and volsync-config ConfigMap +4. Policy installs volsync addon on hub and creates the volsync `ReplicationSources` for all PVCs with the volsync label + + +ACM user, on Restore hub: +5. Enables backup on MultiClusterHub. This installs the hub backup component + - The user manually installs the policy from the community project + - The above are open to review and decision +6. Creates an ACM Restore resource and restores passive data + - The policy creates the volsync `ReplicationDestination` for all PVCs defined in the restored volsync-config-pvcs ConfigMap + +## References +- [Volsync](https://access.redhat.com/login?redirectTo=https%3A%2F%2Faccess.redhat.com%2Fdocumentation%2Fen-us%2Fred_hat_advanced_cluster_management_for_kubernetes%2F2.8%2Fhtml%2Fbusiness_continuity%2Fbusiness-cont-overview%23restic-backup-volsync) + + + + diff --git a/community/CM-Configuration-Management/acm-volsync-hub-backup/acm-volsync-config.yaml b/community/CM-Configuration-Management/acm-volsync-hub-backup/acm-volsync-config.yaml new file mode 100644 index 000000000..a6aa6b03a --- /dev/null +++ b/community/CM-Configuration-Management/acm-volsync-hub-backup/acm-volsync-config.yaml @@ -0,0 +1,209 @@ +# The volsync-config ConfigMap and secret must be defined under the open-cluster-management-backup ns, +# if a BackupSchedule is running and there are PVCs with the volsync label. +# The volsync ConfigMap is in this format: +## +#kind: ConfigMap +#apiVersion: v1 +#metadata: +# name: volsync-config +# namespace: open-cluster-management-backup +# labels: +# cluster.open-cluster-management.io/backup: volsync +#data: +# cacheCapacity: 2Gi +# copyMethod: Snapshot +# pruneIntervalDays: '2' +# repository: restic-secret +# retain_daily: '2' +# retain_hourly: '3' +# retain_monthly: '1' +# trigger_schedule: 0 */2 * * * +## +# The ConfigMap values are used by the ReplicationSource resources, as documented here: +# https://access.redhat.com/documentation/en-us/red_hat_advanced_cluster_management_for_kubernetes/2.8/html/business_continuity/business-cont-overview#restic-backup-volsync +# +# The secret MUST contain only this value RESTIC_REPOSITORY ( RESTIC_REPOSITORY for each PVC will be generated from this ), pointing to a common folder for all PVCs: +# +#kind: Secret +#apiVersion: v1 +#metadata: +# name: restic-secret +# namespace: open-cluster-management-backup +# labels: +# cluster.open-cluster-management.io/backup: volsync +#data: +# AWS_ACCESS_KEY_ID: a2V5X2lk +# AWS_SECRET_ACCESS_KEY: a2V5 +# RESTIC_PASSWORD: YWJjMTIz +# RESTIC_REPOSITORY: >- +# czM6aHR0cDovL21pbmlvLm1pbmlvLnN2Yy5jbHVzdGVyLmxvY2FsOjkwMDAvbXktYnVja2V0 + +# +# The volsync secret is also defined in the doc above +apiVersion: policy.open-cluster-management.io/v1 +kind: Policy +metadata: + name: acm-volsync-config + namespace: open-cluster-management-backup + annotations: + policy.open-cluster-management.io/categories: CA Security Assessment and Authorization + policy.open-cluster-management.io/controls: CA-2 Security Assessments, CA-7 Continuous Monitoring + policy.open-cluster-management.io/standards: NIST SP 800-53 +spec: + disabled: false + policy-templates: + - objectDefinition: + apiVersion: policy.open-cluster-management.io/v1 + kind: ConfigurationPolicy + metadata: + name: check-backup-schedule + spec: + object-templates-raw: | + {{- /* Specify the parameters */ -}} + {{- $api := "cluster.open-cluster-management.io/v1beta1" }} + {{- $velero_api := "velero.io/v1" }} + {{- $kind_schedule := "Schedule" }} + {{- $schedule_label := "cluster.open-cluster-management.io/backup-schedule-type, cluster.open-cluster-management.io/backup-schedule-type in (resources)"}} + {{- $kind_restore := "Restore" }} + {{- $ns := "open-cluster-management-backup" }} + {{- $volsync_map := "volsync-config" }} + {{- $volsync_label := "cluster.open-cluster-management.io/volsync" }} + {{- $pv_claim_cond := gt (len ( lookup "v1" "PersistentVolumeClaim" "" "" $volsync_label).items ) 0 }} + {{- $volsync_pvcs := "volsync-config-pvcs" }} + + {{- /* check if volsync_pvcs configmap exists */ -}} + {{- $volsync_pvcs_map := lookup "v1" "ConfigMap" $ns $volsync_pvcs }} + {{- $volsync_pvcs_exists := eq $volsync_pvcs_map.metadata.name $volsync_pvcs }} + {{- /* and it was created by a restore operation */ -}} + + {{- $backup_name := "" }} + {{ if $volsync_pvcs_exists }} + {{- $backup_name = (index $volsync_pvcs_map.metadata.labels "velero.io/backup-name") }} + {{- end }} + + {{- /* Backup Schedule must be running if this is not a restore hub */ -}} + {{ if and (or (not $volsync_pvcs_exists) (not $backup_name) ) $pv_claim_cond }} + + - complianceType: musthave + objectDefinition: + apiVersion: {{ $api }} + kind: BackupSchedule + metadata: + namespace: {{ $ns }} + status: + phase: Enabled + {{- end }} + remediationAction: inform + severity: high + - objectDefinition: + apiVersion: policy.open-cluster-management.io/v1 + kind: ConfigurationPolicy + metadata: + name: check-storage-configmap + spec: + object-templates-raw: | + {{- /* Specify the parameters */ -}} + {{- $api := "cluster.open-cluster-management.io/v1beta1" }} + {{- $velero_api := "velero.io/v1" }} + {{- $kind_schedule := "Schedule" }} + {{- $schedule_label := "cluster.open-cluster-management.io/backup-schedule-type, cluster.open-cluster-management.io/backup-schedule-type in (resources)"}} + {{- $kind_restore := "Restore" }} + {{- $ns := "open-cluster-management-backup" }} + {{- $volsync_secret := "restic-secret" }} + {{- $volsync_map := "volsync-config" }} + {{- $volsync_label := "cluster.open-cluster-management.io/volsync" }} + {{- $pv_claim_cond := gt (len ( lookup "v1" "PersistentVolumeClaim" "" "" $volsync_label).items ) 0 }} + {{- $volsync_pvcs := "volsync-config-pvcs" }} + + {{- $volsync_backup_cond := gt (len ( lookup $velero_api $kind_schedule $ns "" $schedule_label).items ) 0 }} + {{- $volsync_restore_cond := eq ( lookup "v1" "ConfigMap" $ns $volsync_pvcs ).metadata.name $volsync_pvcs }} + + {{- /* The volsync-config ConfigMap and secret must be defined under the open-cluster-management-backup ns, if a BackupSchedule is running and there are PVCs with the volsync label. */ -}} + {{- /* Or for a restore hub, there is a restore resource and a volsync pvc config map */ -}} + + {{ if or (and $pv_claim_cond $volsync_backup_cond) $volsync_restore_cond }} + - complianceType: musthave + objectDefinition: + apiVersion: v1 + kind: ConfigMap + metadata: + name: {{ $volsync_map }} + namespace: {{ $ns }} + labels: + cluster.open-cluster-management.io/backup: volsync + - complianceType: musthave + objectDefinition: + apiVersion: v1 + kind: Secret + metadata: + name: {{ $volsync_secret }} + namespace: {{ $ns }} + labels: + cluster.open-cluster-management.io/backup: volsync + type: Opaque + {{- end }} + remediationAction: inform + severity: high + - objectDefinition: + apiVersion: policy.open-cluster-management.io/v1 + kind: ConfigurationPolicy + metadata: + name: create-volsync-addon + spec: + object-templates-raw: | + {{- /* Specify the parameters */ -}} + {{- $api := "cluster.open-cluster-management.io/v1beta1" }} + {{- $velero_api := "velero.io/v1" }} + {{- $kind_schedule := "Schedule" }} + {{- $schedule_label := "cluster.open-cluster-management.io/backup-schedule-type, cluster.open-cluster-management.io/backup-schedule-type in (resources)"}} + {{- $kind_restore := "Restore" }} + {{- $ns := "open-cluster-management-backup" }} + {{- $volsync_map := "volsync-config" }} + {{- $volsync_label := "cluster.open-cluster-management.io/volsync" }} + {{- $pv_claim_cond := gt (len ( lookup "v1" "PersistentVolumeClaim" "" "" $volsync_label).items ) 0 }} + {{- $volsync_backup_cond := gt (len ( lookup $velero_api $kind_schedule $ns "" $schedule_label).items ) 0 }} + {{- $volsync_pvcs := "volsync-config-pvcs" }} + {{- $volsync_restore_cond := eq ( lookup "v1" "ConfigMap" $ns $volsync_pvcs ).metadata.name $volsync_pvcs }} + {{- $has_local_cluster_ns := eq (lookup "cluster.open-cluster-management.io/v1" "ManagedCluster" "" "local-cluster").metadata.name "local-cluster" }} + {{- $is_hub := "is-hub" }} + {{- $local_cls := "local-cluster" }} + + {{- /* Create the volsync addon - if BackupSchedule exists and there are PVCs with volsync label */ -}} + {{ if or (and $pv_claim_cond $volsync_backup_cond) $volsync_restore_cond }} + {{- range $hub_managed_cls := (lookup "cluster.open-cluster-management.io/v1" "ManagedCluster" "" "" $is_hub).items }} + {{ if not ( eq $hub_managed_cls.metadata.name $local_cls ) }} + - complianceType: musthave + objectDefinition: + apiVersion: addon.open-cluster-management.io/v1alpha1 + kind: ManagedClusterAddOn + metadata: + name: volsync + namespace: {{ $hub_managed_cls.metadata.name }} + labels: + cluster.open-cluster-management.io/backup: volsync + spec: + installNamespace: open-cluster-management-agent-addon + status: + conditions: + - status: 'True' + {{- end }} + {{- end }} + {{ if $has_local_cluster_ns }} + - complianceType: musthave + objectDefinition: + apiVersion: addon.open-cluster-management.io/v1alpha1 + kind: ManagedClusterAddOn + metadata: + name: volsync + namespace: {{ $local_cls }} + labels: + cluster.open-cluster-management.io/backup: volsync + spec: + installNamespace: open-cluster-management-agent-addon + status: + conditions: + - status: 'True' + {{- end }} + {{- end }} + remediationAction: enforce + severity: high \ No newline at end of file diff --git a/community/CM-Configuration-Management/acm-volsync-hub-backup/acm-volsync-cr-destination.yaml b/community/CM-Configuration-Management/acm-volsync-hub-backup/acm-volsync-cr-destination.yaml new file mode 100644 index 000000000..1d84c22cd --- /dev/null +++ b/community/CM-Configuration-Management/acm-volsync-hub-backup/acm-volsync-cr-destination.yaml @@ -0,0 +1,247 @@ +apiVersion: policy.open-cluster-management.io/v1 +kind: Policy +metadata: + name: acm-volsync-destination + namespace: open-cluster-management-backup + annotations: + policy.open-cluster-management.io/categories: CA Security Assessment and Authorization + policy.open-cluster-management.io/controls: CA-2 Security Assessments, CA-7 Continuous Monitoring + policy.open-cluster-management.io/standards: NIST SP 800-53 +spec: + disabled: false + dependencies: + - apiVersion: policy.open-cluster-management.io/v1 + compliance: Compliant + kind: Policy + name: acm-volsync-config + policy-templates: + - objectDefinition: + apiVersion: policy.open-cluster-management.io/v1 + kind: ConfigurationPolicy + metadata: + name: create-replication-destination + spec: + object-templates-raw: | + {{- /* Specify the parameters */ -}} + {{- $api := "cluster.open-cluster-management.io/v1beta1" }} + {{- $velero_api := "velero.io/v1" }} + {{- $kind_schedule := "Schedule" }} + {{- $schedule_label := "cluster.open-cluster-management.io/backup-schedule-type, cluster.open-cluster-management.io/backup-schedule-type in (resources)"}} + {{- $kind_restore := "Restore" }} + {{- $ns := "open-cluster-management-backup" }} + {{- $volsync_secret := "restic-secret" }} + + {{- /* common volsync config; to define different config for a PVC, create a volsync-config-pvc-ns-pvcname configMap */ -}} + {{- $volsync_map := "volsync-config" }} + {{- $volsync_label := "cluster.open-cluster-management.io/volsync" }} + + {{- $volsync_backup_cond := gt (len ( lookup $velero_api $kind_schedule $ns "" $schedule_label).items ) 0 }} + {{- $restore_label := "velero.io/backup-name" }} + {{- $backup_name_prefix := "acm-credentials-schedule-" }} + {{- $volsync_pvcs := "volsync-config-pvcs" }} + + {{- /* Create the volsync ReplicationDestination and secret - if Restore exists, PVC is created by a Restore and no Backup is running */ -}} + {{- /* Use the volsync-config-pvcns-pvcname config instead of the default volsync-config map, if such map exists under the $ns */ -}} + + {{- /* volsync-config map should exist and have a backup-name label */ -}} + {{- $volsync_pvcs_map := lookup "v1" "ConfigMap" $ns $volsync_pvcs }} + {{- $volsync_restore_cond := eq $volsync_pvcs_map.metadata.name $volsync_pvcs }} + + {{- $backup_name := "" }} + {{ if $volsync_restore_cond }} + {{- $backup_name = (index $volsync_pvcs_map.metadata.labels "velero.io/backup-name") }} + {{- end }} + + {{ if and ($backup_name) ( not $volsync_backup_cond ) ($volsync_restore_cond) }} + + {{- $restore_name := (index $volsync_pvcs_map.metadata.labels "velero.io/restore-name") }} + {{- $restore_timestamp_trim := trimAll $backup_name_prefix $backup_name }} + + {{- $acm_restore_name_fixed := split $backup_name_prefix $restore_name }} + {{- $acm_restore_name := (cat $acm_restore_name_fixed._0 $backup_name) | replace " " "" }} + + {{- $acm_restore := lookup $velero_api $kind_restore $ns $acm_restore_name }} + + {{- /* the acm restore should exist, so this is the passive hub */ -}} + {{ if and (eq $acm_restore.metadata.name $acm_restore_name) }} + {{- range $pvc_data := split "##" (fromConfigMap $ns $volsync_pvcs "pvcs") }} + + {{- $pvc_list := splitn "#" 2 $pvc_data }} + {{- $pvc_namespace := $pvc_list._0 }} + {{- $pvc_name := $pvc_list._1 }} + + {{- $pvc_config_name := ( (cat $volsync_map "-" $pvc_namespace "-" $pvc_name ) | replace " " "" ) }} + {{ if eq ( lookup "v1" "ConfigMap" $ns $pvc_config_name ).metadata.name $pvc_config_name }} + {{ $volsync_map = $pvc_config_name }} + {{- end }} + {{- $secret_name := fromConfigMap $ns $volsync_map "repository" }} + {{- $pvc_config_info_name := ( (cat $volsync_map "-info-" $pvc_name ) | replace " " "" ) }} + + {{ if eq ( lookup "v1" "ConfigMap" $pvc_namespace $pvc_config_info_name ).metadata.name $pvc_config_info_name }} + + {{- $storageClassPVC := fromConfigMap $pvc_namespace $pvc_config_info_name "storageClassName" }} + {{- range $changeStorageMap := ( lookup "v1" "ConfigMap" $ns "" "velero.io/change-storage-class" ).items }} + {{ $mappingClass := fromConfigMap $ns $changeStorageMap.metadata.name $storageClassPVC }} + {{- if not (eq $mappingClass "")}} + {{ $storageClassPVC = $mappingClass }} + {{- end }} + {{- end }} + + - complianceType: musthave + objectDefinition: + kind: PersistentVolumeClaim + apiVersion: v1 + metadata: + name: {{ $pvc_name }} + namespace: {{ $pvc_namespace }} + labels: + {{ $volsync_label }}: volsync + spec: + storageClassName: {{ $storageClassPVC }} + resources: + requests: + storage: '{{ fromConfigMap $pvc_namespace $pvc_config_info_name "resources.requests.storage" }}' + volumeMode: '{{ fromConfigMap $pvc_namespace $pvc_config_info_name "volumeMode" }}' + {{ $accessModes := trimAll " " (fromConfigMap $pvc_namespace $pvc_config_info_name "resources.accessModes") }} + {{- if not (eq $accessModes "" ) }} + accessModes: + {{- range $modes := split " " $accessModes }} + - {{ $modes }} + {{- end }} + {{- end }} + {{- $secretName := ( (cat $pvc_name "-" (fromConfigMap $ns $volsync_map "repository") ) | replace " " "" ) }} + {{- /* truncate from the front, the Dest name, if the string is longer than 50 chars ; a job batch starting with volsync-dst- is generated from this name and it must be less than 63 chars */ -}} + {{- $rd_name := trunc -50 (cat $pvc_name $restore_timestamp_trim | replace " " "") }} + + {{- $common_restic_repo := ( lookup "v1" "Secret" $ns $volsync_secret ).data.RESTIC_REPOSITORY | base64dec }} + + - complianceType: musthave + objectDefinition: + apiVersion: v1 + kind: Secret + metadata: + name: '{{ $secretName }}' + namespace: {{ $pvc_namespace }} + data: + {{- range $key, $value := ( lookup "v1" "Secret" $ns $volsync_secret ).data }} + {{- if not (eq $key "RESTIC_REPOSITORY")}} + {{ $key }}: {{ $value }} + {{- end }} + {{- end }} + RESTIC_REPOSITORY: {{ ( ( (cat $common_restic_repo "/" $pvc_namespace "-" $pvc_name ) | replace " " "" ) | base64enc ) }} + type: Opaque + - complianceType: musthave + objectDefinition: + kind: ReplicationDestination + apiVersion: volsync.backube/v1alpha1 + metadata: + name: {{ $rd_name }} + namespace: {{ $pvc_namespace }} + labels: + "restore-name": {{ $restore_name }} + "backup-name": {{ $backup_name }} + spec: + restic: + repository: {{ $secretName }} + destinationPVC: {{ $pvc_name }} + copyMethod: Direct + trigger: + manual: restore-once + {{- end }} + {{- end }} + {{- end }} + {{- end }} + remediationAction: enforce + severity: high + - objectDefinition: + apiVersion: policy.open-cluster-management.io/v1 + kind: ConfigurationPolicy + metadata: + name: check-destination-replication-error + spec: + object-templates-raw: | + {{- $volsync_label := "cluster.open-cluster-management.io/volsync" }} + {{- $api := "cluster.open-cluster-management.io/v1beta1" }} + {{- $velero_api := "velero.io/v1" }} + {{- $kind_schedule := "Schedule" }} + {{- $ns := "open-cluster-management-backup" }} + {{- $kind_restore := "Restore" }} + {{- $schedule_label := "cluster.open-cluster-management.io/backup-schedule-type, cluster.open-cluster-management.io/backup-schedule-type in (resources)"}} + {{- $volsync_backup_cond := gt (len ( lookup $velero_api $kind_schedule $ns "" $schedule_label).items ) 0 }} + + {{- /* volsync-config-pvcs map should exist */ -}} + {{- $volsync_pvcs := "volsync-config-pvcs" }} + {{- $volsync_pvcs_map := lookup "v1" "ConfigMap" $ns $volsync_pvcs }} + {{- $volsync_restore_cond := eq $volsync_pvcs_map.metadata.name $volsync_pvcs }} + + {{- /* The volsync destination-source should not be in error state */ -}} + {{ if and ( not $volsync_backup_cond ) ($volsync_restore_cond)}} + {{- $backup_name := (index $volsync_pvcs_map.metadata.labels "velero.io/backup-name") }} + + {{- range $rd := (lookup "volsync.backube/v1alpha1" "ReplicationDestination" "" "" "backup-name").items }} + - complianceType: mustnothave + objectDefinition: + apiVersion: volsync.backube/v1alpha1 + kind: ReplicationDestination + metadata: + namespace: {{ $rd.metadata.namespace }} + labels: + backup-name: {{ $backup_name }} + status: + conditions: + - reason: Error + type: Synchronizing + {{- end }} + {{- end }} + remediationAction: inform + severity: high + - objectDefinition: + apiVersion: policy.open-cluster-management.io/v1 + kind: ConfigurationPolicy + metadata: + name: check-destination-replication-success + spec: + object-templates-raw: | + {{- $volsync_label := "cluster.open-cluster-management.io/volsync" }} + {{- $api := "cluster.open-cluster-management.io/v1beta1" }} + {{- $velero_api := "velero.io/v1" }} + {{- $kind_schedule := "Schedule" }} + {{- $ns := "open-cluster-management-backup" }} + {{- $kind_restore := "Restore" }} + {{- $schedule_label := "cluster.open-cluster-management.io/backup-schedule-type, cluster.open-cluster-management.io/backup-schedule-type in (resources)"}} + {{- $volsync_backup_cond := gt (len ( lookup $velero_api $kind_schedule $ns "" $schedule_label).items ) 0 }} + + {{- /* volsync-config-pvcs map should exist */ -}} + {{- $volsync_pvcs := "volsync-config-pvcs" }} + {{- $volsync_pvcs_map := lookup "v1" "ConfigMap" $ns $volsync_pvcs }} + {{- $volsync_restore_cond := eq $volsync_pvcs_map.metadata.name $volsync_pvcs }} + + {{- /* The volsync destination-source should be successful */ -}} + {{ if and ( not $volsync_backup_cond ) ($volsync_restore_cond)}} + {{- $backup_name := (index $volsync_pvcs_map.metadata.labels "velero.io/backup-name") }} + + {{- range $rd := (lookup "volsync.backube/v1alpha1" "ReplicationDestination" "" "" "backup-name").items }} + - complianceType: musthave + objectDefinition: + apiVersion: volsync.backube/v1alpha1 + kind: ReplicationDestination + metadata: + namespace: {{ $rd.metadata.namespace }} + labels: + backup-name: {{ $backup_name }} + status: + latestMoverStatus: + result: Successful + - complianceType: musthave + objectDefinition: + apiVersion: v1 + kind: PersistentVolumeClaim + metadata: + namespace: {{ $rd.metadata.namespace }} + name: {{ $rd.spec.restic.destinationPVC }} + status: + phase: Bound + {{- end }} + {{- end }} + remediationAction: inform + severity: high \ No newline at end of file diff --git a/community/CM-Configuration-Management/acm-volsync-hub-backup/acm-volsync-cr-source.yaml b/community/CM-Configuration-Management/acm-volsync-hub-backup/acm-volsync-cr-source.yaml new file mode 100644 index 000000000..c6102744f --- /dev/null +++ b/community/CM-Configuration-Management/acm-volsync-hub-backup/acm-volsync-cr-source.yaml @@ -0,0 +1,232 @@ +apiVersion: policy.open-cluster-management.io/v1 +kind: Policy +metadata: + name: acm-volsync-source + namespace: open-cluster-management-backup + annotations: + policy.open-cluster-management.io/categories: CA Security Assessment and Authorization + policy.open-cluster-management.io/controls: CA-2 Security Assessments, CA-7 Continuous Monitoring + policy.open-cluster-management.io/standards: NIST SP 800-53 +spec: + disabled: false + dependencies: + - apiVersion: policy.open-cluster-management.io/v1 + compliance: Compliant + kind: Policy + name: acm-volsync-config + policy-templates: + - objectDefinition: + apiVersion: policy.open-cluster-management.io/v1 + kind: ConfigurationPolicy + metadata: + name: create-replication-source + spec: + object-templates-raw: | + {{- /* Specify the parameters */ -}} + {{- $api := "cluster.open-cluster-management.io/v1beta1" }} + {{- $velero_api := "velero.io/v1" }} + {{- $kind_schedule := "Schedule" }} + {{- $schedule_label := "cluster.open-cluster-management.io/backup-schedule-type, cluster.open-cluster-management.io/backup-schedule-type in (resources)"}} + {{- $ns := "open-cluster-management-backup" }} + {{- $volsync_secret := "restic-secret" }} + {{- $volsync_map := "volsync-config" }} + {{- $volsync_label := "cluster.open-cluster-management.io/volsync" }} + {{- $pv_claim_cond := gt (len ( lookup "v1" "PersistentVolumeClaim" "" "" $volsync_label).items ) 0 }} + {{- $volsync_backup_cond := gt (len ( lookup $velero_api $kind_schedule $ns "" $schedule_label).items ) 0 }} + {{- $volsync_pvcs := "volsync-config-pvcs" }} + + {{- /* Create the volsync ReplicationSource and secret - if BackupSchedule exists ; delete ReplicationSource otherwise */ -}} + {{ if $volsync_backup_cond }} + {{- range $rs := (lookup "volsync.backube/v1alpha1" "ReplicationSource" "" "" $volsync_label).items }} + {{ $pvc_rs := lookup "v1" "PersistentVolumeClaim" $rs.metadata.namespace $rs.metadata.name $volsync_label}} + {{ if or (not (eq $pvc_rs.metadata.name $rs.metadata.name)) (eq (index $pvc_rs.metadata.labels $volsync_label) "")}} + - complianceType: mustnothave + objectDefinition: + kind: ReplicationSource + apiVersion: volsync.backube/v1alpha1 + metadata: + name: {{ $rs.metadata.name }} + namespace: {{ $rs.metadata.namespace }} + labels: + {{ $volsync_label }}: acm + - complianceType: mustnothave + objectDefinition: + kind: ConfigMap + apiVersion: v1 + metadata: + name: {{( (cat $volsync_map "-info-" $rs.metadata.name ) | replace " " "") }} + namespace: {{ $rs.metadata.namespace }} + {{- end }} + {{- end }} + + {{- $volsync_pvcs_str := "" }} + {{- range $pvc := (lookup "v1" "PersistentVolumeClaim" "" "" $volsync_label).items }} + + {{- if eq $pvc.status.phase "Bound" }} + {{- /* Use the volsync-config-pvcns-pvcname config instead of the default volsync-config map, if such map exists under the $ns */ -}} + {{- $pvc_config_name := ( (cat $volsync_map "-" $pvc.metadata.namespace "-" $pvc.metadata.name ) | replace " " "" ) }} + + {{- $pvc_config_info_name := ( (cat $volsync_map "-info-" $pvc.metadata.name ) | replace " " "" ) }} + + - complianceType: musthave + objectDefinition: + apiVersion: v1 + kind: ConfigMap + metadata: + name: {{ $pvc_config_info_name }} + namespace: {{ $pvc.metadata.namespace }} + labels: + cluster.open-cluster-management.io/backup: volsync + data: + {{- if not ( eq $pvc.spec.storageClassName "") }} + storageClassName: {{ $pvc.spec.storageClassName }} + {{- end }} + {{- if not ( eq $pvc.spec.volumeMode "") }} + volumeMode: {{ $pvc.spec.volumeMode }} + {{- end }} + {{- if not ( eq $pvc.spec.resources.requests.storage "") }} + resources.requests.storage: {{ $pvc.spec.resources.requests.storage }} + {{- end }} + {{- if not (empty $pvc.spec.accessModes ) }} + {{ $am_val := ""}} + {{- range $av := $pvc.spec.accessModes }} + {{ $am_val = cat $am_val $av }} + {{- end }} + resources.accessModes: {{ $am_val }} + {{- end }} + + {{ if eq ( lookup "v1" "ConfigMap" $ns $pvc_config_name ).metadata.name $pvc_config_name }} + {{ $volsync_map = $pvc_config_name }} + {{- end }} + + {{- $volsync_pvcs_str = ( (cat $volsync_pvcs_str "##" $pvc.metadata.namespace "#" $pvc.metadata.name ) | replace " " "" ) }} + {{- $common_restic_repo := ( lookup "v1" "Secret" $ns $volsync_secret ).data.RESTIC_REPOSITORY | base64dec }} + {{- $secretName := ( (cat $pvc.metadata.name "-" (fromConfigMap $ns $volsync_map "repository") ) | replace " " "" ) }} + - complianceType: musthave + objectDefinition: + apiVersion: v1 + kind: Secret + metadata: + name: '{{ $secretName }}' + namespace: {{ $pvc.metadata.namespace }} + data: + {{- range $key, $value := ( lookup "v1" "Secret" $ns $volsync_secret ).data }} + {{- if not (eq $key "RESTIC_REPOSITORY")}} + {{ $key }}: {{ $value }} + {{- end }} + {{- end }} + RESTIC_REPOSITORY: {{ ( ( (cat $common_restic_repo "/" $pvc.metadata.namespace "-" $pvc.metadata.name ) | replace " " "" ) | base64enc ) }} + type: Opaque + - complianceType: musthave + objectDefinition: + kind: ReplicationSource + apiVersion: volsync.backube/v1alpha1 + metadata: + name: {{ $pvc.metadata.name }} + namespace: {{ $pvc.metadata.namespace }} + labels: + {{ $volsync_label }}: acm + spec: + restic: + cacheCapacity: '{{ fromConfigMap $ns $volsync_map "cacheCapacity" }}' + copyMethod: '{{ fromConfigMap $ns $volsync_map "copyMethod" }}' + pruneIntervalDays: '{{ fromConfigMap $ns $volsync_map "pruneIntervalDays" | toInt }}' + repository: '{{ $secretName }}' + retain: + daily: '{{ fromConfigMap $ns $volsync_map "retain_daily" | toInt }}' + hourly: '{{ fromConfigMap $ns $volsync_map "retain_hourly" | toInt }}' + monthly: '{{ fromConfigMap $ns $volsync_map "retain_monthly" | toInt }}' + sourcePVC: {{ $pvc.metadata.name }} + trigger: + schedule: '{{ fromConfigMap $ns $volsync_map "trigger_schedule" }}' + {{- end }} + {{- end }} + - complianceType: musthave + objectDefinition: + apiVersion: v1 + kind: ConfigMap + metadata: + name: {{ $volsync_pvcs }} + namespace: {{ $ns }} + labels: + cluster.open-cluster-management.io/backup: volsync + app: {{ $volsync_pvcs }} + data: + pvcs: {{ trimAll "##" $volsync_pvcs_str }} + {{- else }} + {{- range $pvc := (lookup "v1" "PersistentVolumeClaim" "" "" $volsync_label).items }} + - complianceType: mustnothave + objectDefinition: + kind: ReplicationSource + apiVersion: volsync.backube/v1alpha1 + metadata: + name: {{ $pvc.metadata.name }} + namespace: {{ $pvc.metadata.namespace }} + labels: + {{ $volsync_label }}: acm + spec: + sourcePVC: {{ $pvc.metadata.name }} + {{- end }} + {{- end }} + remediationAction: enforce + severity: high + - objectDefinition: + apiVersion: policy.open-cluster-management.io/v1 + kind: ConfigurationPolicy + metadata: + name: check-source-replication-success + spec: + object-templates-raw: | + {{- $volsync_label := "cluster.open-cluster-management.io/volsync" }} + {{- $ns := "open-cluster-management-backup" }} + {{- $schedule_label := "cluster.open-cluster-management.io/backup-schedule-type, cluster.open-cluster-management.io/backup-schedule-type in (resources)"}} + {{- $velero_api := "velero.io/v1" }} + {{- $kind_schedule := "Schedule" }} + {{- $volsync_backup_cond := gt (len ( lookup $velero_api $kind_schedule $ns "" $schedule_label).items ) 0 }} + + {{- /* The volsync replication-source should be successful */ -}} + {{- range $pvc := (lookup "v1" "PersistentVolumeClaim" "" "" $volsync_label).items }} + {{ if $volsync_backup_cond }} + - complianceType: musthave + objectDefinition: + apiVersion: volsync.backube/v1alpha1 + kind: ReplicationSource + metadata: + namespace: {{ $pvc.metadata.namespace }} + status: + latestMoverStatus: + result: Successful + {{- end }} + {{- end }} + remediationAction: inform + severity: high + - objectDefinition: + apiVersion: policy.open-cluster-management.io/v1 + kind: ConfigurationPolicy + metadata: + name: check-source-replication-errors + spec: + object-templates-raw: | + {{- $volsync_label := "cluster.open-cluster-management.io/volsync" }} + {{- $ns := "open-cluster-management-backup" }} + {{- $schedule_label := "cluster.open-cluster-management.io/backup-schedule-type, cluster.open-cluster-management.io/backup-schedule-type in (resources)"}} + {{- $velero_api := "velero.io/v1" }} + {{- $kind_schedule := "Schedule" }} + {{- $volsync_backup_cond := gt (len ( lookup $velero_api $kind_schedule $ns "" $schedule_label).items ) 0 }} + + {{- /* The volsync replication-source should not be failed */ -}} + {{- range $pvc := (lookup "v1" "PersistentVolumeClaim" "" "" $volsync_label).items }} + {{ if $volsync_backup_cond }} + - complianceType: mustnothave + objectDefinition: + apiVersion: volsync.backube/v1alpha1 + kind: ReplicationSource + metadata: + namespace: {{ $pvc.metadata.namespace }} + status: + latestMoverStatus: + result: Failed + {{- end }} + {{- end }} + remediationAction: inform + severity: high \ No newline at end of file diff --git a/community/CM-Configuration-Management/acm-volsync-hub-backup/acm-volsync-placement.yaml b/community/CM-Configuration-Management/acm-volsync-hub-backup/acm-volsync-placement.yaml new file mode 100644 index 000000000..7a5b59a7d --- /dev/null +++ b/community/CM-Configuration-Management/acm-volsync-hub-backup/acm-volsync-placement.yaml @@ -0,0 +1,37 @@ +--- +apiVersion: cluster.open-cluster-management.io/v1beta1 +kind: Placement +metadata: + name: volsync-placement + namespace: open-cluster-management-backup +spec: + predicates: + - requiredClusterSelector: + labelSelector: + matchExpressions: + - key: name + operator: In + values: + - local-cluster + - requiredClusterSelector: + labelSelector: + matchExpressions: + - key: is-hub + operator: In + values: + - 'true' +--- +apiVersion: policy.open-cluster-management.io/v1 +kind: PlacementBinding +metadata: + name: volsync-placement + namespace: open-cluster-management-backup +placementRef: + name: volsync-placement + apiGroup: cluster.open-cluster-management.io + kind: Placement +subjects: + - name: acm-volsync + apiGroup: policy.open-cluster-management.io + kind: PolicySet + diff --git a/community/CM-Configuration-Management/acm-volsync-hub-backup/acm-volsync-policyset.yaml b/community/CM-Configuration-Management/acm-volsync-hub-backup/acm-volsync-policyset.yaml new file mode 100644 index 000000000..4e6f4b307 --- /dev/null +++ b/community/CM-Configuration-Management/acm-volsync-hub-backup/acm-volsync-policyset.yaml @@ -0,0 +1,11 @@ +apiVersion: policy.open-cluster-management.io/v1beta1 +kind: PolicySet +metadata: + name: acm-volsync + namespace: open-cluster-management-backup +spec: + description: backup support for PVC with cluster.open-cluster-management.io/volsync label + policies: + - acm-volsync-config + - acm-volsync-source + - acm-volsync-destination \ No newline at end of file diff --git a/community/CM-Configuration-Management/acm-volsync-hub-backup/images/backup_dest_policy.png b/community/CM-Configuration-Management/acm-volsync-hub-backup/images/backup_dest_policy.png new file mode 100644 index 000000000..301444b2a Binary files /dev/null and b/community/CM-Configuration-Management/acm-volsync-hub-backup/images/backup_dest_policy.png differ diff --git a/community/CM-Configuration-Management/acm-volsync-hub-backup/images/backup_source_policy.png b/community/CM-Configuration-Management/acm-volsync-hub-backup/images/backup_source_policy.png new file mode 100644 index 000000000..8687cfcdb Binary files /dev/null and b/community/CM-Configuration-Management/acm-volsync-hub-backup/images/backup_source_policy.png differ diff --git a/community/CM-Configuration-Management/acm-volsync-hub-backup/images/backup_source_policy_1.png b/community/CM-Configuration-Management/acm-volsync-hub-backup/images/backup_source_policy_1.png new file mode 100644 index 000000000..13dccdfce Binary files /dev/null and b/community/CM-Configuration-Management/acm-volsync-hub-backup/images/backup_source_policy_1.png differ diff --git a/community/CM-Configuration-Management/acm-volsync-hub-backup/images/config_policy.png b/community/CM-Configuration-Management/acm-volsync-hub-backup/images/config_policy.png new file mode 100644 index 000000000..56abbf692 Binary files /dev/null and b/community/CM-Configuration-Management/acm-volsync-hub-backup/images/config_policy.png differ diff --git a/community/CM-Configuration-Management/acm-volsync-hub-backup/images/policies.png b/community/CM-Configuration-Management/acm-volsync-hub-backup/images/policies.png new file mode 100644 index 000000000..24510da16 Binary files /dev/null and b/community/CM-Configuration-Management/acm-volsync-hub-backup/images/policies.png differ diff --git a/community/CM-Configuration-Management/acm-volsync-hub-backup/images/policyset.png b/community/CM-Configuration-Management/acm-volsync-hub-backup/images/policyset.png new file mode 100644 index 000000000..d1e58454e Binary files /dev/null and b/community/CM-Configuration-Management/acm-volsync-hub-backup/images/policyset.png differ diff --git a/community/CM-Configuration-Management/acm-volsync-hub-backup/images/restore_dest_policy.png b/community/CM-Configuration-Management/acm-volsync-hub-backup/images/restore_dest_policy.png new file mode 100644 index 000000000..632ac6ab8 Binary files /dev/null and b/community/CM-Configuration-Management/acm-volsync-hub-backup/images/restore_dest_policy.png differ diff --git a/community/CM-Configuration-Management/acm-volsync-hub-backup/images/restore_dest_policy_1.png b/community/CM-Configuration-Management/acm-volsync-hub-backup/images/restore_dest_policy_1.png new file mode 100644 index 000000000..e2be35bba Binary files /dev/null and b/community/CM-Configuration-Management/acm-volsync-hub-backup/images/restore_dest_policy_1.png differ diff --git a/community/CM-Configuration-Management/acm-volsync-hub-backup/images/restore_source_policy.png b/community/CM-Configuration-Management/acm-volsync-hub-backup/images/restore_source_policy.png new file mode 100644 index 000000000..b85a32d9a Binary files /dev/null and b/community/CM-Configuration-Management/acm-volsync-hub-backup/images/restore_source_policy.png differ