From 4ce667fb5f83d12d4c6eb491fe3d8a845d9dc028 Mon Sep 17 00:00:00 2001 From: Mike Ditton Date: Thu, 29 Jan 2026 14:56:04 +0100 Subject: [PATCH 1/5] Add rclone as encryption proxy for cnpg backups This deploys rclone alongside vshnpostgresqlcnpg to act as an intermediate encryption proxy --- .../functions/common/backup/backup.go | 109 ++++++++++++++++++ .../functions/vshnpostgrescnpg/backup.go | 64 ++++------ .../functions/vshnpostgrescnpg/backup_test.go | 35 +++--- .../vshn-postgres/deploy/05_backup_cnpg.yaml | 3 + 4 files changed, 151 insertions(+), 60 deletions(-) diff --git a/pkg/comp-functions/functions/common/backup/backup.go b/pkg/comp-functions/functions/common/backup/backup.go index dda734d6b4..8dc53db95a 100644 --- a/pkg/comp-functions/functions/common/backup/backup.go +++ b/pkg/comp-functions/functions/common/backup/backup.go @@ -2,6 +2,7 @@ package backup import ( "context" + "encoding/json" "fmt" "strconv" "strings" @@ -10,6 +11,7 @@ import ( xpv1 "github.com/crossplane/crossplane-runtime/apis/common/v1" k8upv1 "github.com/k8up-io/k8up/v2/api/v1" "github.com/sethvargo/go-password/password" + xhelmv1 "github.com/vshn/appcat/v4/apis/helm/release/v1beta1" xkube "github.com/vshn/appcat/v4/apis/kubernetes/v1alpha2" appcatv1 "github.com/vshn/appcat/v4/apis/v1" "github.com/vshn/appcat/v4/pkg/comp-functions/functions/common" @@ -17,6 +19,7 @@ import ( corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + k8sruntime "k8s.io/apimachinery/pkg/runtime" "k8s.io/utils/ptr" controllerruntime "sigs.k8s.io/controller-runtime" ) @@ -427,3 +430,109 @@ func PatchConnectionSecretWithAllowDeletion(ctx context.Context, comp common.Inf return svc.SetDesiredKubeObject(secretObject, secretObjectName, runtime.KubeOptionAllowDeletion) } + +// RcloneProxyCredentials contains the backend credentials to connect to rclone +type RcloneProxyCredentials struct { + Region string + AccessID string + AccessKey string +} + +// DeployRcloneProxy deploys the rclone encryption proxy helm chart +func DeployRcloneProxy(ctx context.Context, svc *runtime.ServiceRuntime, comp common.InfoGetter) (*RcloneProxyCredentials, error) { + l := controllerruntime.LoggerFrom(ctx) + + // Get bucket connection details from observed composite resource + cd, err := svc.GetObservedComposedResourceConnectionDetails(comp.GetName() + "-backup") + if err != nil { + if err == runtime.ErrNotFound { + l.V(1).Info("Backup bucket connection details not found yet, skipping rclone proxy deployment") + return nil, nil + } + return nil, fmt.Errorf("cannot get backup bucket connection details: %w", err) + } + + bucket := string(cd["BUCKET_NAME"]) + region := string(cd["AWS_REGION"]) + accessID := string(cd["AWS_ACCESS_KEY_ID"]) + accessKey := string(cd["AWS_SECRET_ACCESS_KEY"]) + + // Determine bucket credentials secret name in instance namespace + bucketSecretName := credentialSecretName + "-" + comp.GetName() + + // Get chart configuration from service config + chartRepository := svc.Config.Data["rcloneproxyChartSource"] + chartVersion := svc.Config.Data["rcloneproxyChartVersion"] + chartName := svc.Config.Data["rcloneproxyChartName"] + + if chartRepository == "" || chartVersion == "" || chartName == "" { + return nil, fmt.Errorf("rclone chart configuration missing in service config (rcloneproxyChartSource, rcloneproxyChartVersion, rcloneproxyChartName)") + } + + // Prepare Helm values for rclone chart + values := map[string]any{ + "backend": map[string]any{ + "secretRef": map[string]any{ + "name": bucketSecretName, + "keys": map[string]any{ + "accessKeyID": "AWS_ACCESS_KEY_ID", + "accessKeySecret": "AWS_SECRET_ACCESS_KEY", + "endpoint": "ENDPOINT_URL", + "region": "AWS_REGION", + "bucket": "BUCKET_NAME", + }, + }, + }, + } + + // Marshal values to JSON + valueBytes, err := json.Marshal(values) + if err != nil { + return nil, fmt.Errorf("cannot marshal rclone helm values: %w", err) + } + + // Create Helm release for rclone proxy + releaseName := comp.GetName() + "-rclone" + release := &xhelmv1.Release{ + ObjectMeta: metav1.ObjectMeta{ + Name: releaseName, + }, + Spec: xhelmv1.ReleaseSpec{ + ForProvider: xhelmv1.ReleaseParameters{ + Chart: xhelmv1.ChartSpec{ + Repository: chartRepository, + Version: chartVersion, + Name: chartName, + }, + Namespace: comp.GetInstanceNamespace(), + ValuesSpec: xhelmv1.ValuesSpec{ + Values: k8sruntime.RawExtension{ + Raw: valueBytes, + }, + }, + }, + ResourceSpec: xpv1.ResourceSpec{ + ProviderConfigReference: &xpv1.Reference{ + Name: "helm", + }, + }, + }, + } + + // Set the desired Helm release + err = svc.SetDesiredComposedResourceWithName(release, releaseName) + if err != nil { + return nil, fmt.Errorf("cannot set desired rclone proxy helm release: %w", err) + } + + l.Info("Deployed rclone encryption proxy", + "releaseName", releaseName, + "namespace", comp.GetInstanceNamespace(), + "backendBucket", bucket) + + return &RcloneProxyCredentials{ + Region: region, + AccessID: accessID, + AccessKey: accessKey, + }, nil +} diff --git a/pkg/comp-functions/functions/vshnpostgrescnpg/backup.go b/pkg/comp-functions/functions/vshnpostgrescnpg/backup.go index aafc34ed36..845325bb0a 100644 --- a/pkg/comp-functions/functions/vshnpostgrescnpg/backup.go +++ b/pkg/comp-functions/functions/vshnpostgrescnpg/backup.go @@ -4,7 +4,6 @@ import ( "context" "fmt" "maps" - "strings" vshnv1 "github.com/vshn/appcat/v4/apis/vshn/v1" "github.com/vshn/appcat/v4/pkg/comp-functions/functions/common" @@ -12,15 +11,6 @@ import ( "github.com/vshn/appcat/v4/pkg/comp-functions/runtime" ) -// Backup bucket connection details -type backupCredentials struct { - endpoint string - bucket string - region string - accessId string - accessKey string -} - // Bootstrap backup (if enabled) func SetupBackup(ctx context.Context, svc *runtime.ServiceRuntime, comp *vshnv1.VSHNPostgreSQL, values map[string]any) error { // CreateObjectBucket has its own IsBackupEnabled to deal with bucket retention @@ -36,21 +26,24 @@ func SetupBackup(ctx context.Context, svc *runtime.ServiceRuntime, comp *vshnv1. } if comp.IsBackupEnabled() && comp.GetInstances() != 0 { - // Configure barman cloud plugin via helm values - if err := insertBackupValues(svc, comp, values); err != nil { - return err + // Deploy rclone encryption proxy and get backend credentials + proxyCreds, err := backup.DeployRcloneProxy(ctx, svc, comp) + if err != nil { + return fmt.Errorf("cannot deploy rclone encryption proxy: %w", err) + } + + // Configure barman cloud plugin via helm values with rclone proxy + if proxyCreds != nil { + if err := insertBackupValues(svc, comp, values, proxyCreds); err != nil { + return err + } } } return nil } // Add backup config to helm values -func insertBackupValues(svc *runtime.ServiceRuntime, comp *vshnv1.VSHNPostgreSQL, values map[string]any) error { - connectionDetails, err := getBackupBucketConnectionDetails(svc, comp) - if err != nil { - return err - } - +func insertBackupValues(svc *runtime.ServiceRuntime, comp *vshnv1.VSHNPostgreSQL, values map[string]any, proxyCreds *backup.RcloneProxyCredentials) error { retention := comp.GetBackupRetention() retentionDays := retention.KeepDaily if retentionDays <= 0 { @@ -76,20 +69,21 @@ func insertBackupValues(svc *runtime.ServiceRuntime, comp *vshnv1.VSHNPostgreSQL } cluster["plugins"] = clusterPlugins - // Configure backups using the barman cloud plugin + // Configure backups using the barman cloud plugin with rclone encryption proxy maps.Copy(values, map[string]any{ "backups": map[string]any{ "enabled": true, "provider": "s3", - "endpointURL": connectionDetails.endpoint, - "region": connectionDetails.region, + "endpointURL": "http://rcloneproxy:9095", + "region": proxyCreds.Region, "retentionPolicy": fmt.Sprintf("%dd", retentionDays), "s3": map[string]any{ - "bucket": connectionDetails.bucket, - "region": connectionDetails.region, + // rclone gets confused when the bucket name matches the one it's rooted at. Since this can be arbitrary we hardcode it + "bucket": "backup", + "region": proxyCreds.Region, "path": "/", - "accessKey": connectionDetails.accessId, - "secretKey": connectionDetails.accessKey, + "accessKey": proxyCreds.AccessID, + "secretKey": proxyCreds.AccessKey, }, "wal": map[string]any{ "compression": "gzip", @@ -118,24 +112,6 @@ func insertBackupValues(svc *runtime.ServiceRuntime, comp *vshnv1.VSHNPostgreSQL return nil } -func getBackupBucketConnectionDetails(svc *runtime.ServiceRuntime, comp *vshnv1.VSHNPostgreSQL) (backupCredentials, error) { - backupCredentials := backupCredentials{} - cd, err := svc.GetObservedComposedResourceConnectionDetails(comp.GetName() + "-backup") - if err != nil && err == runtime.ErrNotFound { - return backupCredentials, fmt.Errorf("backup bucket connection details not found") - } else if err != nil { - return backupCredentials, err - } - - endpoint, _ := strings.CutSuffix(string(cd["ENDPOINT_URL"]), "/") - backupCredentials.endpoint = endpoint - backupCredentials.bucket = string(cd["BUCKET_NAME"]) - backupCredentials.region = string(cd["AWS_REGION"]) - backupCredentials.accessId = string(cd["AWS_ACCESS_KEY_ID"]) - backupCredentials.accessKey = string(cd["AWS_SECRET_ACCESS_KEY"]) - return backupCredentials, nil -} - // Transform backup schedule according to robfig/cron (used by CNPG) // https://pkg.go.dev/github.com/robfig/cron#hdr-CRON_Expression_Format func transformSchedule(thisSchedule string) string { diff --git a/pkg/comp-functions/functions/vshnpostgrescnpg/backup_test.go b/pkg/comp-functions/functions/vshnpostgrescnpg/backup_test.go index 085f52037b..d633657a21 100644 --- a/pkg/comp-functions/functions/vshnpostgrescnpg/backup_test.go +++ b/pkg/comp-functions/functions/vshnpostgrescnpg/backup_test.go @@ -5,6 +5,7 @@ import ( "testing" "github.com/stretchr/testify/assert" + xhelmv1 "github.com/vshn/appcat/v4/apis/helm/release/v1beta1" appcatv1 "github.com/vshn/appcat/v4/apis/v1" "github.com/vshn/appcat/v4/pkg/comp-functions/runtime" ) @@ -44,24 +45,18 @@ func TestBackupBooststrapEnabled(t *testing.T) { assert.Equal(t, "s3", backupValues["provider"]) assert.Equal(t, "6d", backupValues["retentionPolicy"]) - // Bucket configuration - cd, err := getBackupBucketConnectionDetails(svc, comp) - assert.NoError(t, err) - assert.Equal(t, cd.endpoint, "https://s3.minio.local") // No trailing / - assert.Equal(t, cd.bucket, "backupBucket") - assert.Equal(t, cd.region, "rma") - assert.Equal(t, cd.accessId, "secretAccessId") - assert.Equal(t, cd.accessKey, "secretAccessKey") - - // Check endpoint, region (top-level), and S3 configuration - assert.Equal(t, cd.endpoint, backupValues["endpointURL"]) - assert.Equal(t, cd.region, backupValues["region"]) + // Check that rclone proxy endpoint is used (not direct S3) + assert.Equal(t, "http://rcloneproxy:9095", backupValues["endpointURL"]) + + // Region and credentials are passed through from the bucket + assert.Equal(t, "rma", backupValues["region"]) s3Config := backupValues["s3"].(map[string]any) - assert.Equal(t, cd.bucket, s3Config["bucket"]) - assert.Equal(t, cd.region, s3Config["region"]) + // Bucket is hardcoded because rclone gets confused when the bucket name matches the backend bucket + assert.Equal(t, "backup", s3Config["bucket"]) + assert.Equal(t, "rma", s3Config["region"]) assert.Equal(t, "/", s3Config["path"]) - assert.Equal(t, cd.accessId, s3Config["accessKey"]) - assert.Equal(t, cd.accessKey, s3Config["secretKey"]) + assert.Equal(t, "secretAccessId", s3Config["accessKey"]) + assert.Equal(t, "secretAccessKey", s3Config["secretKey"]) // Check WAL and data configuration walConfig := backupValues["wal"].(map[string]any) @@ -102,7 +97,15 @@ func TestBackupBooststrapEnabled(t *testing.T) { pluginConfig := scheduledBackups[0]["pluginConfiguration"].(map[string]string) assert.Equal(t, "barman-cloud.cloudnative-pg.io", pluginConfig["name"]) + // Check that backup bucket is created bucketName := comp.GetName() + "-backup" err = svc.GetDesiredComposedResourceByName(&appcatv1.XObjectBucket{}, bucketName) assert.NoError(t, err) + + // Check that rclone proxy Helm release is created + rcloneReleaseName := comp.GetName() + "-rclone" + rcloneRelease := &xhelmv1.Release{} + err = svc.GetDesiredComposedResourceByName(rcloneRelease, rcloneReleaseName) + assert.NoError(t, err, "rclone proxy Helm release should be created") + assert.Equal(t, comp.GetInstanceNamespace(), rcloneRelease.Spec.ForProvider.Namespace) } diff --git a/test/functions/vshn-postgres/deploy/05_backup_cnpg.yaml b/test/functions/vshn-postgres/deploy/05_backup_cnpg.yaml index 47ab09e4e5..bf653c2878 100644 --- a/test/functions/vshn-postgres/deploy/05_backup_cnpg.yaml +++ b/test/functions/vshn-postgres/deploy/05_backup_cnpg.yaml @@ -215,6 +215,9 @@ input: "memory": "64Mi"}}, "setDbopsResult": {"limits": {"cpu": "250m", "memory": "256Mi"}, "requests": {"cpu": "100m", "memory": "64Mi"}}}' providerEnabled: "true" + rcloneproxyChartSource: "oci://ghcr.io/vshn/appcat-charts/rcloneproxy" + rcloneproxyChartVersion: "0.0.1" + rcloneproxyChartName: "rcloneproxy" kind: ConfigMap metadata: annotations: {} From e258a2d7b8dc93d249dccbbfc811167a84ea89ae Mon Sep 17 00:00:00 2001 From: Mike Ditton Date: Mon, 2 Feb 2026 13:58:00 +0100 Subject: [PATCH 2/5] Configure security contexts for rclone --- .../functions/common/backup/backup.go | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/pkg/comp-functions/functions/common/backup/backup.go b/pkg/comp-functions/functions/common/backup/backup.go index 8dc53db95a..c813ba5107 100644 --- a/pkg/comp-functions/functions/common/backup/backup.go +++ b/pkg/comp-functions/functions/common/backup/backup.go @@ -485,6 +485,40 @@ func DeployRcloneProxy(ctx context.Context, svc *runtime.ServiceRuntime, comp co }, } + // Configure security contexts based on platform + isOpenshift := svc.GetBoolFromCompositionConfig("isOpenshift") + + if isOpenshift { + // OpenShift: disable explicit UID/GID, let SCC assign them + // But enable SELinux configuration + values["podSecurityContext"] = map[string]any{ + "enabled": true, + "fsGroup": nil, // Let OpenShift SCC assign + "fsGroupChangePolicy": "OnRootMismatch", + "seLinuxOptions": map[string]any{ + "type": "spc_t", + }, + } + values["containerSecurityContext"] = map[string]any{ + "enabled": true, + "runAsUser": nil, // Let OpenShift SCC assign + "runAsNonRoot": true, + "allowPrivilegeEscalation": false, + "readOnlyRootFilesystem": false, // rclone needs to write config + "capabilities": map[string]any{ + "drop": []string{"ALL"}, + }, + } + } else { + // Regular Kubernetes: explicitly set UID/GID + values["podSecurityContext"] = map[string]any{ + "enabled": true, + } + values["containerSecurityContext"] = map[string]any{ + "enabled": true, + } + } + // Marshal values to JSON valueBytes, err := json.Marshal(values) if err != nil { From 581a5ec9be76b03c97bfe93ca57420ec4b7a9c0d Mon Sep 17 00:00:00 2001 From: Mike Ditton Date: Wed, 4 Feb 2026 09:01:09 +0100 Subject: [PATCH 3/5] Allow deletion of rclone release This allows the rclone release to be deleted to ensure backups can be disabled --- .../functions/common/backup/backup.go | 14 +++++++++----- .../functions/vshnpostgrescnpg/backup_test.go | 5 +++-- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/pkg/comp-functions/functions/common/backup/backup.go b/pkg/comp-functions/functions/common/backup/backup.go index c813ba5107..ede6417962 100644 --- a/pkg/comp-functions/functions/common/backup/backup.go +++ b/pkg/comp-functions/functions/common/backup/backup.go @@ -526,10 +526,16 @@ func DeployRcloneProxy(ctx context.Context, svc *runtime.ServiceRuntime, comp co } // Create Helm release for rclone proxy - releaseName := comp.GetName() + "-rclone" release := &xhelmv1.Release{ ObjectMeta: metav1.ObjectMeta{ - Name: releaseName, + Labels: map[string]string{ + // needed to disable backups + runtime.WebhookAllowDeletionLabel: "true", + }, + Annotations: map[string]string{ + // Set stable external-name so Helm release name doesn't change on recreate + "crossplane.io/external-name": "rclone", + }, }, Spec: xhelmv1.ReleaseSpec{ ForProvider: xhelmv1.ReleaseParameters{ @@ -553,14 +559,12 @@ func DeployRcloneProxy(ctx context.Context, svc *runtime.ServiceRuntime, comp co }, } - // Set the desired Helm release - err = svc.SetDesiredComposedResourceWithName(release, releaseName) + err = svc.SetDesiredComposedResourceWithName(release, "rclone") if err != nil { return nil, fmt.Errorf("cannot set desired rclone proxy helm release: %w", err) } l.Info("Deployed rclone encryption proxy", - "releaseName", releaseName, "namespace", comp.GetInstanceNamespace(), "backendBucket", bucket) diff --git a/pkg/comp-functions/functions/vshnpostgrescnpg/backup_test.go b/pkg/comp-functions/functions/vshnpostgrescnpg/backup_test.go index d633657a21..40f1c0b0dd 100644 --- a/pkg/comp-functions/functions/vshnpostgrescnpg/backup_test.go +++ b/pkg/comp-functions/functions/vshnpostgrescnpg/backup_test.go @@ -103,9 +103,10 @@ func TestBackupBooststrapEnabled(t *testing.T) { assert.NoError(t, err) // Check that rclone proxy Helm release is created - rcloneReleaseName := comp.GetName() + "-rclone" rcloneRelease := &xhelmv1.Release{} - err = svc.GetDesiredComposedResourceByName(rcloneRelease, rcloneReleaseName) + err = svc.GetDesiredComposedResourceByName(rcloneRelease, "rclone") assert.NoError(t, err, "rclone proxy Helm release should be created") assert.Equal(t, comp.GetInstanceNamespace(), rcloneRelease.Spec.ForProvider.Namespace) + // Verify the stable Helm release name is set via external-name annotation + assert.Equal(t, "rclone", rcloneRelease.Annotations["crossplane.io/external-name"]) } From da5b93eed21959b30cf54f4ca03ec76bb9d21384 Mon Sep 17 00:00:00 2001 From: Mike Ditton Date: Thu, 5 Feb 2026 13:38:58 +0100 Subject: [PATCH 4/5] Set servername for barman object store This sets the server name so the backup can actually be restored. --- pkg/comp-functions/functions/vshnpostgrescnpg/backup.go | 2 +- pkg/comp-functions/functions/vshnpostgrescnpg/backup_test.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/comp-functions/functions/vshnpostgrescnpg/backup.go b/pkg/comp-functions/functions/vshnpostgrescnpg/backup.go index 845325bb0a..ceea8d30e7 100644 --- a/pkg/comp-functions/functions/vshnpostgrescnpg/backup.go +++ b/pkg/comp-functions/functions/vshnpostgrescnpg/backup.go @@ -57,7 +57,7 @@ func insertBackupValues(svc *runtime.ServiceRuntime, comp *vshnv1.VSHNPostgreSQL "isWALArchiver": true, "parameters": map[string]any{ "barmanObjectName": "postgresql-object-store", - "serverName": "", + "serverName": "postgresql", }, }} diff --git a/pkg/comp-functions/functions/vshnpostgrescnpg/backup_test.go b/pkg/comp-functions/functions/vshnpostgrescnpg/backup_test.go index 40f1c0b0dd..f223b8e3b5 100644 --- a/pkg/comp-functions/functions/vshnpostgrescnpg/backup_test.go +++ b/pkg/comp-functions/functions/vshnpostgrescnpg/backup_test.go @@ -83,7 +83,7 @@ func TestBackupBooststrapEnabled(t *testing.T) { assert.True(t, plugins[0]["isWALArchiver"].(bool)) pluginParams := plugins[0]["parameters"].(map[string]any) assert.Equal(t, "postgresql-object-store", pluginParams["barmanObjectName"]) - assert.Equal(t, "", pluginParams["serverName"]) + assert.Equal(t, "postgresql", pluginParams["serverName"]) // Check scheduled backups scheduledBackups := backupValues["scheduledBackups"].([]map[string]any) From c6931e4937a846db3f58e4ebf5f13e1a5d1819e5 Mon Sep 17 00:00:00 2001 From: Mike Ditton Date: Fri, 6 Feb 2026 14:24:23 +0100 Subject: [PATCH 5/5] Use isOpenshift parameter for rcloneproxy This uses the isOpenshift parameter to set the security contexts for openshift. --- .../functions/common/backup/backup.go | 51 ++++--------------- 1 file changed, 10 insertions(+), 41 deletions(-) diff --git a/pkg/comp-functions/functions/common/backup/backup.go b/pkg/comp-functions/functions/common/backup/backup.go index ede6417962..a032132af0 100644 --- a/pkg/comp-functions/functions/common/backup/backup.go +++ b/pkg/comp-functions/functions/common/backup/backup.go @@ -32,6 +32,13 @@ const ( BackupDisabledTimestampLabel = "appcat.vshn.io/backup-disabled-timestamp" ) +// RcloneProxyCredentials contains the backend credentials to connect to rclone +type RcloneProxyCredentials struct { + Region string + AccessID string + AccessKey string +} + // AddK8upBackup creates an S3 bucket and a K8up schedule according to the composition spec. // When backup is disabled, it only creates/preserves the bucket for retention but skips other backup objects. func AddK8upBackup(ctx context.Context, svc *runtime.ServiceRuntime, comp common.InfoGetter) error { @@ -431,13 +438,6 @@ func PatchConnectionSecretWithAllowDeletion(ctx context.Context, comp common.Inf runtime.KubeOptionAllowDeletion) } -// RcloneProxyCredentials contains the backend credentials to connect to rclone -type RcloneProxyCredentials struct { - Region string - AccessID string - AccessKey string -} - // DeployRcloneProxy deploys the rclone encryption proxy helm chart func DeployRcloneProxy(ctx context.Context, svc *runtime.ServiceRuntime, comp common.InfoGetter) (*RcloneProxyCredentials, error) { l := controllerruntime.LoggerFrom(ctx) @@ -470,6 +470,8 @@ func DeployRcloneProxy(ctx context.Context, svc *runtime.ServiceRuntime, comp co } // Prepare Helm values for rclone chart + isOpenshift := svc.GetBoolFromCompositionConfig("isOpenshift") + values := map[string]any{ "backend": map[string]any{ "secretRef": map[string]any{ @@ -483,40 +485,7 @@ func DeployRcloneProxy(ctx context.Context, svc *runtime.ServiceRuntime, comp co }, }, }, - } - - // Configure security contexts based on platform - isOpenshift := svc.GetBoolFromCompositionConfig("isOpenshift") - - if isOpenshift { - // OpenShift: disable explicit UID/GID, let SCC assign them - // But enable SELinux configuration - values["podSecurityContext"] = map[string]any{ - "enabled": true, - "fsGroup": nil, // Let OpenShift SCC assign - "fsGroupChangePolicy": "OnRootMismatch", - "seLinuxOptions": map[string]any{ - "type": "spc_t", - }, - } - values["containerSecurityContext"] = map[string]any{ - "enabled": true, - "runAsUser": nil, // Let OpenShift SCC assign - "runAsNonRoot": true, - "allowPrivilegeEscalation": false, - "readOnlyRootFilesystem": false, // rclone needs to write config - "capabilities": map[string]any{ - "drop": []string{"ALL"}, - }, - } - } else { - // Regular Kubernetes: explicitly set UID/GID - values["podSecurityContext"] = map[string]any{ - "enabled": true, - } - values["containerSecurityContext"] = map[string]any{ - "enabled": true, - } + "isOpenshift": isOpenshift, } // Marshal values to JSON