diff --git a/api/go.mod b/api/go.mod index f22a448..bf0557d 100644 --- a/api/go.mod +++ b/api/go.mod @@ -104,3 +104,5 @@ replace k8s.io/component-base => k8s.io/component-base v0.31.13 //allow-merging replace github.com/rabbitmq/cluster-operator/v2 => github.com/openstack-k8s-operators/rabbitmq-cluster-operator/v2 v2.6.1-0.20250929174222-a0d328fa4dec //allow-merging replace k8s.io/kube-openapi => k8s.io/kube-openapi v0.0.0-20250627150254-e9823e99808e //allow-merging + +replace github.com/openstack-k8s-operators/keystone-operator/api => github.com/Deydra71/keystone-operator/api v0.0.0-20251103091514-244e15fe5d63 diff --git a/controllers/swift_common.go b/controllers/swift_common.go index a56148c..5d1ec0c 100644 --- a/controllers/swift_common.go +++ b/controllers/swift_common.go @@ -22,14 +22,14 @@ import ( "fmt" "time" + topologyv1 "github.com/openstack-k8s-operators/infra-operator/apis/topology/v1beta1" "github.com/openstack-k8s-operators/lib-common/modules/common/condition" + "github.com/openstack-k8s-operators/lib-common/modules/common/env" "github.com/openstack-k8s-operators/lib-common/modules/common/helper" "github.com/openstack-k8s-operators/lib-common/modules/common/secret" - "k8s.io/apimachinery/pkg/types" - topologyv1 "github.com/openstack-k8s-operators/infra-operator/apis/topology/v1beta1" - "github.com/openstack-k8s-operators/lib-common/modules/common/env" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/log" diff --git a/controllers/swiftproxy_controller.go b/controllers/swiftproxy_controller.go index 1fe96f7..3f1909b 100644 --- a/controllers/swiftproxy_controller.go +++ b/controllers/swiftproxy_controller.go @@ -512,6 +512,19 @@ func (r *SwiftProxyReconciler) Reconcile(ctx context.Context, req ctrl.Request) return ctrlResult, err } + // Check for Application Credentials + ctrlResult, err = keystonev1.VerifyApplicationCredentialsForService( + ctx, + r.Client, + instance.Namespace, + "swift", + &envVars, + 10*time.Second, + ) + if (err != nil || ctrlResult != ctrl.Result{}) { + return ctrlResult, err + } + // Get the service password and pass it to the template sps, _, err := secret.GetSecret(ctx, helper, instance.Spec.Secret, instance.Namespace) if err != nil { @@ -578,6 +591,20 @@ func (r *SwiftProxyReconciler) Reconcile(ctx context.Context, req ctrl.Request) return ctrl.Result{}, err } + // Get Application Credential data if available + useAC := false + acID := "" + acSecret := "" + // Try to get Application Credential for this service (via keystone api helper) + if acData, err := keystonev1.GetApplicationCredentialFromSecret(ctx, r.Client, instance.Namespace, swift.ServiceName); err != nil { + Log.Error(err, "Failed to get ApplicationCredential for service", "service", swift.ServiceName) + } else if acData != nil { + useAC = true + acID = acData.ID + acSecret = acData.Secret + Log.Info("Using ApplicationCredentials auth", "service", swift.ServiceName) + } + // Create a Secret populated with content from templates/ tpl := swiftproxy.SecretTemplates( instance, @@ -591,6 +618,9 @@ func (r *SwiftProxyReconciler) Reconcile(ctx context.Context, req ctrl.Request) os.GetRegion(), transportURLString, instance.Spec.APITimeout, + useAC, + acID, + acSecret, ) err = secret.EnsureSecrets(ctx, helper, instance, tpl, &envVars) if err != nil { @@ -846,6 +876,42 @@ func (r *SwiftProxyReconciler) SetupWithManager(ctx context.Context, mgr ctrl.Ma return nil } + // Application Credential secret watching function + acSecretFn := func(_ context.Context, o client.Object) []reconcile.Request { + name := o.GetName() + ns := o.GetNamespace() + result := []reconcile.Request{} + + // Only handle Secret objects + if _, isSecret := o.(*corev1.Secret); !isSecret { + return nil + } + + // Check if this is a swift AC secret by name pattern (ac-swift-secret) + expectedSecretName := keystonev1.GetACSecretName("swift") + if name == expectedSecretName { + // get all SwiftProxy CRs in this namespace + swiftProxies := &swiftv1beta1.SwiftProxyList{} + listOpts := []client.ListOption{ + client.InNamespace(ns), + } + if err := r.List(context.Background(), swiftProxies, listOpts...); err != nil { + return nil + } + + // Enqueue reconcile for all swift proxy instances + for _, cr := range swiftProxies.Items { + objKey := client.ObjectKey{ + Namespace: ns, + Name: cr.Name, + } + result = append(result, reconcile.Request{NamespacedName: objKey}) + } + } + + return result + } + return ctrl.NewControllerManagedBy(mgr). For(&swiftv1beta1.SwiftProxy{}). Owns(&corev1.Secret{}). @@ -859,6 +925,8 @@ func (r *SwiftProxyReconciler) SetupWithManager(ctx context.Context, mgr ctrl.Ma handler.EnqueueRequestsFromMapFunc(r.findObjectsForSrc), builder.WithPredicates(predicate.ResourceVersionChangedPredicate{}), ). + Watches(&corev1.Secret{}, + handler.EnqueueRequestsFromMapFunc(acSecretFn)). Watches(&memcachedv1.Memcached{}, handler.EnqueueRequestsFromMapFunc(memcachedFn)). Watches(&topologyv1.Topology{}, diff --git a/go.mod b/go.mod index 6b8f9d3..9dee59c 100644 --- a/go.mod +++ b/go.mod @@ -115,3 +115,5 @@ replace k8s.io/component-base => k8s.io/component-base v0.31.13 //allow-merging replace github.com/rabbitmq/cluster-operator/v2 => github.com/openstack-k8s-operators/rabbitmq-cluster-operator/v2 v2.6.1-0.20250929174222-a0d328fa4dec //allow-merging replace k8s.io/kube-openapi => k8s.io/kube-openapi v0.0.0-20250627150254-e9823e99808e //allow-merging + +replace github.com/openstack-k8s-operators/keystone-operator/api => github.com/Deydra71/keystone-operator/api v0.0.0-20251103091514-244e15fe5d63 diff --git a/go.sum b/go.sum index e27c2b2..fa6b5ad 100644 --- a/go.sum +++ b/go.sum @@ -1,3 +1,5 @@ +github.com/Deydra71/keystone-operator/api v0.0.0-20251103091514-244e15fe5d63 h1:ug2YPMQJ/+0ifOjFyaPx1YtX0zsVnL02pB2ngacYviw= +github.com/Deydra71/keystone-operator/api v0.0.0-20251103091514-244e15fe5d63/go.mod h1:FMFoO4MjEQ85JpdLtDHxYSZxvJ9KzHua+HdKhpl0KRI= github.com/Masterminds/semver/v3 v3.4.0 h1:Zog+i5UMtVoCU8oKka5P7i9q9HgrJeGzI9SA1Xbatp0= github.com/Masterminds/semver/v3 v3.4.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= @@ -102,8 +104,6 @@ github.com/openstack-k8s-operators/barbican-operator/api v0.6.1-0.20251029145459 github.com/openstack-k8s-operators/barbican-operator/api v0.6.1-0.20251029145459-c75418746c62/go.mod h1:EabVtheR6RVS/6XVpp0Hs1LP7aYyp1CdNMdXj73kTOM= github.com/openstack-k8s-operators/infra-operator/apis v0.6.1-0.20251030184102-82d2cbaafd35 h1:QFFGu93A+XCvDUxZIgfBE4gB5hEdVQAIw+E8dF1kP/E= github.com/openstack-k8s-operators/infra-operator/apis v0.6.1-0.20251030184102-82d2cbaafd35/go.mod h1:qq8BCRxTEmLRriUsQ4HeDUzqltWg32MQPDTMhgbBGK4= -github.com/openstack-k8s-operators/keystone-operator/api v0.6.1-0.20251027074845-ed8154b20ad1 h1:QohvX44nxoV2GwvvOURGXYyDuCn4SCrnwubTKJtzehY= -github.com/openstack-k8s-operators/keystone-operator/api v0.6.1-0.20251027074845-ed8154b20ad1/go.mod h1:FMFoO4MjEQ85JpdLtDHxYSZxvJ9KzHua+HdKhpl0KRI= github.com/openstack-k8s-operators/lib-common/modules/ansible v0.6.1-0.20251027074416-ab5c045dbe00 h1:4vmcoWP0WPoiAe+Awa6o84rNyZDax0uIOR72dEamSxc= github.com/openstack-k8s-operators/lib-common/modules/ansible v0.6.1-0.20251027074416-ab5c045dbe00/go.mod h1:tXxVkkk8HlATwTmDA5RTP3b+c8apfuMM15mZ2wW5iNs= github.com/openstack-k8s-operators/lib-common/modules/common v0.6.1-0.20251027074416-ab5c045dbe00 h1:Xih6tYYqiDVllo4fDGHqTPL+M2biO5YLOUmbiTqrW/I= diff --git a/pkg/swiftproxy/templates.go b/pkg/swiftproxy/templates.go index 307c80e..1829929 100644 --- a/pkg/swiftproxy/templates.go +++ b/pkg/swiftproxy/templates.go @@ -40,6 +40,9 @@ func SecretTemplates( keystoneRegion string, transportURL string, apiTimeout int, + useApplicationCredentials bool, + applicationCredentialID string, + applicationCredentialSecret string, ) []util.Template { templateParameters := make(map[string]any) templateParameters["ServiceUser"] = instance.Spec.ServiceUser @@ -54,6 +57,13 @@ func SecretTemplates( templateParameters["TransportURL"] = transportURL templateParameters["APITimeout"] = apiTimeout + // Application Credential parameters + templateParameters["UseApplicationCredentials"] = useApplicationCredentials + if useApplicationCredentials { + templateParameters["ApplicationCredentialID"] = applicationCredentialID + templateParameters["ApplicationCredentialSecret"] = applicationCredentialSecret + } + // MTLS params if mc.Status.MTLSCert != "" { templateParameters["MemcachedAuthCert"] = fmt.Sprint(memcachedv1.CertMountPath()) diff --git a/templates/swiftproxy/config/00-proxy-server.conf b/templates/swiftproxy/config/00-proxy-server.conf index 9aef5b1..f98cf11 100644 --- a/templates/swiftproxy/config/00-proxy-server.conf +++ b/templates/swiftproxy/config/00-proxy-server.conf @@ -80,12 +80,18 @@ project_reader_roles = SwiftProjectReader paste.filter_factory = keystonemiddleware.auth_token:filter_factory www_authenticate_uri = {{ .KeystonePublicURL }} auth_url = {{ .KeystonePublicURL }} +{{ if .UseApplicationCredentials -}} +auth_type = v3applicationcredential +application_credential_id = {{ .ApplicationCredentialID }} +application_credential_secret = {{ .ApplicationCredentialSecret }} +{{- else -}} auth_plugin=password project_domain_id = default user_domain_id = default project_name = service username = {{ .ServiceUser }} password = {{ .ServicePassword }} +{{- end }} delay_auth_decision = True [filter:s3api] @@ -108,8 +114,14 @@ use = egg:swift#encryption [filter:ceilometer] paste.filter_factory = ceilometermiddleware.swift:filter_factory auth_url = {{ .KeystonePublicURL }} +{{ if .UseApplicationCredentials -}} +auth_type = v3applicationcredential +application_credential_id = {{ .ApplicationCredentialID }} +application_credential_secret = {{ .ApplicationCredentialSecret }} +{{- else -}} password = {{ .ServicePassword }} username = {{ .ServiceUser }} +{{- end }} region_name = {{ .KeystoneRegion }} url = {{ .TransportURL }} project_name = service