diff --git a/Documentation/CRDs/specification.md b/Documentation/CRDs/specification.md
index ed9c73bb6..ce61b98c4 100644
--- a/Documentation/CRDs/specification.md
+++ b/Documentation/CRDs/specification.md
@@ -3120,6 +3120,19 @@ CSICephFSSpec
CephFS defines CSI Driver settings for CephFS driver.
+
+
+skipUserCreation
+
+bool
+
+ |
+
+(Optional)
+ SkipUserCreation determines whether CSI users and their associated secrets should be skipped.
+If set to true, the user must manually manage these secrets.
+ |
+
Capacity
diff --git a/deploy/charts/rook-ceph/templates/resources.yaml b/deploy/charts/rook-ceph/templates/resources.yaml
index feddc45bd..434d25821 100644
--- a/deploy/charts/rook-ceph/templates/resources.yaml
+++ b/deploy/charts/rook-ceph/templates/resources.yaml
@@ -1560,6 +1560,11 @@ spec:
description: Enables read affinity for CSI driver.
type: boolean
type: object
+ skipUserCreation:
+ description: |-
+ SkipUserCreation determines whether CSI users and their associated secrets should be skipped.
+ If set to true, the user must manually manage these secrets.
+ type: boolean
type: object
dashboard:
description: Dashboard settings
diff --git a/deploy/examples/crds.yaml b/deploy/examples/crds.yaml
index 13f6dba30..6b17ef685 100644
--- a/deploy/examples/crds.yaml
+++ b/deploy/examples/crds.yaml
@@ -1558,6 +1558,11 @@ spec:
description: Enables read affinity for CSI driver.
type: boolean
type: object
+ skipUserCreation:
+ description: |-
+ SkipUserCreation determines whether CSI users and their associated secrets should be skipped.
+ If set to true, the user must manually manage these secrets.
+ type: boolean
type: object
dashboard:
description: Dashboard settings
diff --git a/pkg/apis/ceph.rook.io/v1/types.go b/pkg/apis/ceph.rook.io/v1/types.go
index 538feac96..a2cb12a06 100755
--- a/pkg/apis/ceph.rook.io/v1/types.go
+++ b/pkg/apis/ceph.rook.io/v1/types.go
@@ -255,6 +255,10 @@ type CSIDriverSpec struct {
// CephFS defines CSI Driver settings for CephFS driver.
// +optional
CephFS CSICephFSSpec `json:"cephfs,omitempty"`
+ // SkipUserCreation determines whether CSI users and their associated secrets should be skipped.
+ // If set to true, the user must manually manage these secrets.
+ // +optional
+ SkipUserCreation bool `json:"skipUserCreation,omitempty"`
}
// CSICephFSSpec defines the settings for CephFS CSI driver.
diff --git a/pkg/operator/ceph/csi/secrets.go b/pkg/operator/ceph/csi/secrets.go
index 86739101f..28c424d02 100644
--- a/pkg/operator/ceph/csi/secrets.go
+++ b/pkg/operator/ceph/csi/secrets.go
@@ -17,6 +17,8 @@ limitations under the License.
package csi
import (
+ "fmt"
+
"github.com/pkg/errors"
"github.com/rook/rook/pkg/clusterd"
"github.com/rook/rook/pkg/daemon/ceph/client"
@@ -172,6 +174,13 @@ func createOrUpdateCSISecret(clusterInfo *client.ClusterInfo, csiRBDProvisionerS
// CreateCSISecrets creates all the Kubernetes CSI Secrets
func CreateCSISecrets(context *clusterd.Context, clusterInfo *client.ClusterInfo) error {
+ if clusterInfo.CSIDriverSpec.SkipUserCreation {
+ if err := deleteOwnedCSISecretsByCephCluster(context, clusterInfo); err != nil {
+ return err
+ }
+ logger.Info("CSI user creation is disabled; skipping user and secret creation")
+ return nil
+ }
k := keyring.GetSecretStore(context, clusterInfo, clusterInfo.OwnerInfo)
// Create CSI RBD Provisioner Ceph key
@@ -205,3 +214,26 @@ func CreateCSISecrets(context *clusterd.Context, clusterInfo *client.ClusterInfo
return nil
}
+
+func deleteOwnedCSISecretsByCephCluster(context *clusterd.Context, clusterInfo *client.ClusterInfo) error {
+ ownerRef := metav1.OwnerReference{
+ APIVersion: "ceph.rook.io/v1",
+ Kind: "CephCluster",
+ Name: clusterInfo.NamespacedName().Name,
+ }
+ secrets := []string{CsiRBDNodeSecret, CsiRBDProvisionerSecret, CsiCephFSNodeSecret, CsiCephFSProvisionerSecret}
+ for _, secretName := range secrets {
+ err := k8sutil.DeleteSecretIfOwnedBy(
+ clusterInfo.Context,
+ context.Clientset,
+ secretName,
+ clusterInfo.Namespace,
+ ownerRef,
+ )
+ if err != nil {
+ return fmt.Errorf("failed to delete secret %q: %w", secretName, err)
+ }
+ }
+
+ return nil
+}
diff --git a/pkg/operator/ceph/csi/secrets_test.go b/pkg/operator/ceph/csi/secrets_test.go
index 7b3d2116a..61f3e6cf0 100644
--- a/pkg/operator/ceph/csi/secrets_test.go
+++ b/pkg/operator/ceph/csi/secrets_test.go
@@ -17,9 +17,17 @@ limitations under the License.
package csi
import (
+ "context"
"testing"
+ "github.com/rook/rook/pkg/clusterd"
+ "github.com/rook/rook/pkg/daemon/ceph/client"
+ "github.com/rook/rook/pkg/operator/ceph/config/keyring"
+ "github.com/rook/rook/pkg/operator/k8sutil"
"github.com/stretchr/testify/assert"
+ apierrors "k8s.io/apimachinery/pkg/api/errors"
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+ k8sfake "k8s.io/client-go/kubernetes/fake"
)
func TestCephCSIKeyringRBDNodeCaps(t *testing.T) {
@@ -41,3 +49,78 @@ func TestCephCSIKeyringCephFSProvisionerCaps(t *testing.T) {
caps := cephCSIKeyringCephFSProvisionerCaps()
assert.Equal(t, caps, []string{"mon", "allow r, allow command 'osd blocklist'", "mgr", "allow rw", "osd", "allow rw tag cephfs metadata=*", "mds", "allow *"})
}
+
+func Test_deleteOwnedCSISecretsByCephCluster(t *testing.T) {
+ const (
+ namespace = "rook-ceph"
+ cephCluster = "my-ceph-cluster"
+ )
+
+ // Create fake client
+ clientset := k8sfake.NewSimpleClientset()
+
+ // Cluster context
+ clusterContext := &clusterd.Context{
+ Clientset: clientset,
+ }
+
+ clusterInfo := &client.ClusterInfo{
+ OwnerInfo: k8sutil.NewOwnerInfoWithOwnerRef(&metav1.OwnerReference{
+ APIVersion: "ceph.rook.io/v1",
+ Kind: "CephCluster",
+ Name: cephCluster,
+ }, namespace),
+ }
+ clusterInfo.Namespace = namespace
+ clusterInfo.SetName(cephCluster)
+
+ ctx := &clusterd.Context{
+ Clientset: clientset,
+ }
+
+ k := keyring.GetSecretStore(ctx, clusterInfo, clusterInfo.OwnerInfo)
+
+ // create csi secrets
+ err := createOrUpdateCSISecret(clusterInfo, "", "", "", "", k)
+ if err != nil {
+ t.Fatalf("unexpected error: %v", err)
+ }
+
+ // Remove ownerRef from the CsiCephFSNodeSecret to ensure it won't get deleted
+ secret, err := clientset.CoreV1().Secrets(namespace).Get(context.TODO(), CsiCephFSProvisionerSecret, metav1.GetOptions{})
+ if err != nil {
+ t.Fatalf("unexpected error: %v", err)
+ }
+
+ secret.OwnerReferences = nil
+ secret.SetOwnerReferences([]metav1.OwnerReference{
+ {
+ APIVersion: "ceph.rook.io/v1",
+ Kind: "CephClient",
+ Name: "test-client",
+ },
+ })
+ _, err = clientset.CoreV1().Secrets(namespace).Update(context.TODO(), secret, metav1.UpdateOptions{})
+ if err != nil {
+ t.Fatalf("unexpected error: %v", err)
+ }
+
+ err = deleteOwnedCSISecretsByCephCluster(clusterContext, clusterInfo)
+ if err != nil {
+ t.Fatalf("unexpected error: %v", err)
+ }
+
+ // Verify owned secret is deleted
+ secrets := []string{CsiRBDNodeSecret, CsiRBDProvisionerSecret, CsiCephFSNodeSecret}
+ for _, secretName := range secrets {
+ _, err = clientset.CoreV1().Secrets(namespace).Get(context.TODO(), secretName, metav1.GetOptions{})
+ if err == nil {
+ t.Errorf("expected owned secret %q to be deleted", secretName)
+ }
+ }
+ // Verify unowned secret still exists
+ _, err = clientset.CoreV1().Secrets(namespace).Get(context.TODO(), CsiCephFSProvisionerSecret, metav1.GetOptions{})
+ if err != nil && apierrors.IsNotFound(err) {
+ t.Errorf("expected unowned secret %q to still exist", CsiCephFSProvisionerSecret)
+ }
+}