From fa08a51ea78a4680d32cff9bb3948d42da5f571d Mon Sep 17 00:00:00 2001 From: Veronika Fisarova Date: Thu, 2 Oct 2025 15:55:14 +0200 Subject: [PATCH] Application Credential support --- api/go.mod | 2 + controllers/barbican_controller.go | 12 ++ controllers/barbicanapi_controller.go | 54 +++++++- .../barbicankeystonelistener_controller.go | 6 +- controllers/barbicanworker_controller.go | 3 +- go.mod | 2 + go.sum | 4 +- templates/barbican/config/00-default.conf | 8 +- tests/functional/barbican_controller_test.go | 117 ++++++++++++++++++ 9 files changed, 200 insertions(+), 8 deletions(-) diff --git a/api/go.mod b/api/go.mod index 4ed9d7d1..c3afa066 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/barbican_controller.go b/controllers/barbican_controller.go index 9a63639b..c1a705e3 100644 --- a/controllers/barbican_controller.go +++ b/controllers/barbican_controller.go @@ -717,6 +717,18 @@ func (r *BarbicanReconciler) generateServiceConfig( "EnableSecureRBAC": instance.Spec.BarbicanAPI.EnableSecureRBAC, } + templateParameters["UseApplicationCredentials"] = false + // Try to get Application Credential for this service (via keystone api helper) + if acData, err := keystonev1.GetApplicationCredentialFromSecret(ctx, r.Client, instance.Namespace, barbican.ServiceName); err != nil { + Log.Error(err, "Failed to get ApplicationCredential for service", "service", barbican.ServiceName) + return err + } else if acData != nil { + templateParameters["UseApplicationCredentials"] = true + templateParameters["ACID"] = acData.ID + templateParameters["ACSecret"] = acData.Secret + Log.Info("Using ApplicationCredentials auth", "service", barbican.ServiceName) + } + // To avoid a json parsing error in kolla files, we always need to set PKCS11ClientDataPath // This gets overridden in the PKCS11 section below if needed. templateParameters["PKCS11ClientDataPath"] = barbicanv1beta1.DefaultPKCS11ClientDataPath diff --git a/controllers/barbicanapi_controller.go b/controllers/barbicanapi_controller.go index 9e5f5ed3..4f469c70 100644 --- a/controllers/barbicanapi_controller.go +++ b/controllers/barbicanapi_controller.go @@ -630,6 +630,19 @@ func (r *BarbicanAPIReconciler) reconcileNormal(ctx context.Context, instance *b Log.Info(fmt.Sprintf("[API] Got secrets '%s'", instance.Name)) + // Verify Application Credentials if available + ctrlResult, err = keystonev1.VerifyApplicationCredentialsForService( + ctx, + r.Client, + instance.Namespace, + barbican.ServiceName, + &configVars, + 10*time.Second, + ) + if (err != nil || ctrlResult != ctrl.Result{}) { + return ctrlResult, err + } + // // TLS input validation // @@ -1026,6 +1039,42 @@ func (r *BarbicanAPIReconciler) SetupWithManager(mgr ctrl.Manager) error { return err } + // 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 barbican AC secret by name pattern (ac-barbican-secret) + expectedSecretName := keystonev1.GetACSecretName("barbican") + if name == expectedSecretName { + // get all BarbicanAPI CRs in this namespace + barbicanAPIs := &barbicanv1beta1.BarbicanAPIList{} + listOpts := []client.ListOption{ + client.InNamespace(ns), + } + if err := r.List(context.Background(), barbicanAPIs, listOpts...); err != nil { + return nil + } + + // Enqueue reconcile for all barbican API instances + for _, cr := range barbicanAPIs.Items { + objKey := client.ObjectKey{ + Namespace: ns, + Name: cr.Name, + } + result = append(result, reconcile.Request{NamespacedName: objKey}) + } + } + + return result + } + return ctrl.NewControllerManagedBy(mgr). For(&barbicanv1beta1.BarbicanAPI{}). Owns(&corev1.Service{}). @@ -1037,9 +1086,12 @@ func (r *BarbicanAPIReconciler) SetupWithManager(mgr ctrl.Manager) error { handler.EnqueueRequestsFromMapFunc(r.findObjectsForSrc), builder.WithPredicates(predicate.ResourceVersionChangedPredicate{}), ). + Watches(&corev1.Secret{}, + handler.EnqueueRequestsFromMapFunc(acSecretFn)). Watches(&topologyv1.Topology{}, handler.EnqueueRequestsFromMapFunc(r.findObjectsForSrc), - builder.WithPredicates(predicate.GenerationChangedPredicate{})). + builder.WithPredicates(predicate.GenerationChangedPredicate{}), + ). Complete(r) } diff --git a/controllers/barbicankeystonelistener_controller.go b/controllers/barbicankeystonelistener_controller.go index 1c377e86..774f9ce7 100644 --- a/controllers/barbicankeystonelistener_controller.go +++ b/controllers/barbicankeystonelistener_controller.go @@ -29,8 +29,6 @@ import ( "github.com/openstack-k8s-operators/barbican-operator/pkg/barbican" "github.com/openstack-k8s-operators/barbican-operator/pkg/barbicankeystonelistener" topologyv1 "github.com/openstack-k8s-operators/infra-operator/apis/topology/v1beta1" - - // keystonev1 "github.com/openstack-k8s-operators/keystone-operator/api/v1beta1" "github.com/openstack-k8s-operators/lib-common/modules/common" "github.com/openstack-k8s-operators/lib-common/modules/common/condition" "github.com/openstack-k8s-operators/lib-common/modules/common/deployment" @@ -750,8 +748,10 @@ func (r *BarbicanKeystoneListenerReconciler) SetupWithManager(mgr ctrl.Manager) ). Watches(&topologyv1.Topology{}, handler.EnqueueRequestsFromMapFunc(r.findObjectsForSrc), - builder.WithPredicates(predicate.GenerationChangedPredicate{})). + builder.WithPredicates(predicate.GenerationChangedPredicate{}), + ). Complete(r) + } func (r *BarbicanKeystoneListenerReconciler) findObjectsForSrc(ctx context.Context, src client.Object) []reconcile.Request { diff --git a/controllers/barbicanworker_controller.go b/controllers/barbicanworker_controller.go index bd016302..51af7b13 100644 --- a/controllers/barbicanworker_controller.go +++ b/controllers/barbicanworker_controller.go @@ -771,7 +771,8 @@ func (r *BarbicanWorkerReconciler) SetupWithManager(mgr ctrl.Manager) error { ). Watches(&topologyv1.Topology{}, handler.EnqueueRequestsFromMapFunc(r.findObjectsForSrc), - builder.WithPredicates(predicate.GenerationChangedPredicate{})). + builder.WithPredicates(predicate.GenerationChangedPredicate{}), + ). Complete(r) } diff --git a/go.mod b/go.mod index a3ea1ae6..44e4f4fa 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 208cb929..93f00760 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= @@ -100,8 +102,6 @@ github.com/openshift/api v0.0.0-20250711200046-c86d80652a9e h1:E1OdwSpqWuDPCedyU github.com/openshift/api v0.0.0-20250711200046-c86d80652a9e/go.mod h1:Shkl4HanLwDiiBzakv+con/aMGnVE2MAGvoKp5oyYUo= 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/common v0.6.1-0.20251027074416-ab5c045dbe00 h1:Xih6tYYqiDVllo4fDGHqTPL+M2biO5YLOUmbiTqrW/I= github.com/openstack-k8s-operators/lib-common/modules/common v0.6.1-0.20251027074416-ab5c045dbe00/go.mod h1:PMoNILOdQ1Ij7DyrKgljN6RAiq8pFM2AGsUb6mcxe98= github.com/openstack-k8s-operators/lib-common/modules/openstack v0.6.1-0.20251021145236-2b84ec9fd9bb h1:wToXqX7AS1JV3Kna7RcJfkRart8rSGun2biKNfyY6Zg= diff --git a/templates/barbican/config/00-default.conf b/templates/barbican/config/00-default.conf index e19793c2..24ae575e 100644 --- a/templates/barbican/config/00-default.conf +++ b/templates/barbican/config/00-default.conf @@ -14,10 +14,16 @@ connection={{ .DatabaseConnection }} [keystone_authtoken] auth_version = v3 auth_url={{ .KeystoneAuthURL }} -auth_type=password +auth_type = {{ if .UseApplicationCredentials }}v3applicationcredential{{ else }}password{{ end }} + +{{ if .UseApplicationCredentials -}} +application_credential_id = {{ .ACID }} +application_credential_secret = {{ .ACSecret }} +{{- else -}} username={{ .ServiceUser }} user_domain_name=Default password = {{ .ServicePassword }} +{{- end }} project_name=service project_domain_name=Default interface = internal diff --git a/tests/functional/barbican_controller_test.go b/tests/functional/barbican_controller_test.go index 63ee44f7..c8cd369b 100644 --- a/tests/functional/barbican_controller_test.go +++ b/tests/functional/barbican_controller_test.go @@ -15,9 +15,11 @@ import ( controllers "github.com/openstack-k8s-operators/barbican-operator/controllers" "github.com/openstack-k8s-operators/barbican-operator/pkg/barbican" topologyv1 "github.com/openstack-k8s-operators/infra-operator/apis/topology/v1beta1" + keystonev1 "github.com/openstack-k8s-operators/keystone-operator/api/v1beta1" condition "github.com/openstack-k8s-operators/lib-common/modules/common/condition" mariadb_test "github.com/openstack-k8s-operators/mariadb-operator/api/test/helpers" corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/types" ) @@ -1589,6 +1591,121 @@ var _ = Describe("Barbican controller", func() { }) }) + When("An ApplicationCredential is created for Barbican", func() { + var ( + acName string + acSecretName string + servicePasswordSecret string + passwordSelector string + ) + BeforeEach(func() { + servicePasswordSecret = "ac-test-osp-secret" //nolint:gosec // G101 + passwordSelector = "BarbicanPassword" + + DeferCleanup(k8sClient.Delete, ctx, + CreateBarbicanMessageBusSecret( + barbicanTest.Instance.Namespace, + barbicanTest.RabbitmqSecretName, + ), + ) + DeferCleanup(k8sClient.Delete, ctx, + CreateBarbicanSecret( + barbicanTest.Instance.Namespace, servicePasswordSecret)) + + spec := GetDefaultBarbicanSpec() + spec["secret"] = servicePasswordSecret + spec["simpleCryptoBackendSecret"] = servicePasswordSecret + DeferCleanup(th.DeleteInstance, + CreateBarbican(barbicanTest.Instance, spec)) + DeferCleanup( + mariadb.DeleteDBService, + mariadb.CreateDBService( + barbicanTest.Instance.Namespace, + GetBarbican(barbicanTest.Instance).Spec.DatabaseInstance, + corev1.ServiceSpec{ + Ports: []corev1.ServicePort{{Port: 3306}}})) + + DeferCleanup(keystone.DeleteKeystoneAPI, + keystone.CreateKeystoneAPI(barbicanTest.Instance.Namespace), + ) + + acName = fmt.Sprintf("ac-%s", barbican.ServiceName) + acSecretName = acName + "-secret" + secret := &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: barbicanTest.Instance.Namespace, + Name: acSecretName, + }, + Data: map[string][]byte{ + "AC_ID": []byte("test-ac-id"), + "AC_SECRET": []byte("test-ac-secret"), + }, + } + DeferCleanup(k8sClient.Delete, ctx, secret) + Expect(k8sClient.Create(ctx, secret)).To(Succeed()) + + ac := &keystonev1.KeystoneApplicationCredential{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: barbicanTest.Instance.Namespace, + Name: acName, + }, + Spec: keystonev1.KeystoneApplicationCredentialSpec{ + UserName: barbican.ServiceName, + Secret: servicePasswordSecret, + PasswordSelector: passwordSelector, + Roles: []string{"admin", "member"}, + AccessRules: []keystonev1.ACRule{{Service: "identity", Method: "POST", Path: "/auth/tokens"}}, + ExpirationDays: 30, + GracePeriodDays: 5, + }, + } + DeferCleanup(k8sClient.Delete, ctx, ac) + Expect(k8sClient.Create(ctx, ac)).To(Succeed()) + + fetched := &keystonev1.KeystoneApplicationCredential{} + key := types.NamespacedName{Namespace: ac.Namespace, Name: ac.Name} + Expect(k8sClient.Get(ctx, key, fetched)).To(Succeed()) + + fetched.Status.SecretName = acSecretName + now := metav1.Now() + readyCond := condition.Condition{ + Type: condition.ReadyCondition, + Status: corev1.ConditionTrue, + Reason: condition.ReadyReason, + Message: condition.ReadyMessage, + LastTransitionTime: now, + } + fetched.Status.Conditions = condition.Conditions{readyCond} + Expect(k8sClient.Status().Update(ctx, fetched)).To(Succeed()) + + infra.SimulateTransportURLReady(barbicanTest.BarbicanTransportURL) + mariadb.SimulateMariaDBAccountCompleted(barbicanTest.BarbicanDatabaseAccount) + mariadb.SimulateMariaDBDatabaseCompleted(barbicanTest.BarbicanDatabaseName) + + th.SimulateJobSuccess(barbicanTest.BarbicanDBSync) + + keystone.SimulateKeystoneEndpointReady(barbicanTest.BarbicanKeystoneEndpoint) + }) + + It("should render ApplicationCredential auth in 00-default.conf", func() { + keystone.SimulateKeystoneEndpointReady(barbicanTest.BarbicanKeystoneEndpoint) + + Eventually(func(g Gomega) { + cfgSecret := th.GetSecret(barbicanTest.BarbicanAPIConfigSecret) + g.Expect(cfgSecret).NotTo(BeNil()) + + conf := string(cfgSecret.Data["00-default.conf"]) + + g.Expect(conf).To(ContainSubstring( + "application_credential_id = test-ac-id"), + ) + g.Expect(conf).To(ContainSubstring( + "application_credential_secret = test-ac-secret"), + ) + }, timeout, interval).Should(Succeed()) + }) + }) + // Run MariaDBAccount suite tests. these are pre-packaged ginkgo tests // that exercise standard account create / update patterns that should be // common to all controllers that ensure MariaDBAccount CRs.