diff --git a/tests/functional/barbican_controller_test.go b/tests/functional/barbican_controller_test.go index 2ee9af4..333a62b 100644 --- a/tests/functional/barbican_controller_test.go +++ b/tests/functional/barbican_controller_test.go @@ -12,6 +12,7 @@ import ( . "github.com/openstack-k8s-operators/lib-common/modules/common/test/helpers" barbicanv1beta1 "github.com/openstack-k8s-operators/barbican-operator/api/v1beta1" + controllers "github.com/openstack-k8s-operators/barbican-operator/controllers" "github.com/openstack-k8s-operators/barbican-operator/pkg/barbican" condition "github.com/openstack-k8s-operators/lib-common/modules/common/condition" mariadb_test "github.com/openstack-k8s-operators/mariadb-operator/api/test/helpers" @@ -431,6 +432,185 @@ var _ = Describe("Barbican controller", func() { }) }) + When("A Barbican with HSM is created", func() { + BeforeEach(func() { + DeferCleanup(k8sClient.Delete, ctx, CreateHSMLoginSecret(barbicanTest.Instance.Namespace, HSMLoginSecret)) + DeferCleanup(k8sClient.Delete, ctx, CreateHSMCertsSecret(barbicanTest.Instance.Namespace, HSMCertsSecret)) + + DeferCleanup(th.DeleteInstance, CreateBarbican(barbicanTest.Instance, GetHSMBarbicanSpec())) + DeferCleanup(k8sClient.Delete, ctx, CreateBarbicanMessageBusSecret(barbicanTest.Instance.Namespace, barbicanTest.RabbitmqSecretName)) + infra.SimulateTransportURLReady(barbicanTest.BarbicanTransportURL) + DeferCleanup(k8sClient.Delete, ctx, CreateKeystoneAPISecret(barbicanTest.Instance.Namespace, SecretName)) + DeferCleanup( + mariadb.DeleteDBService, + mariadb.CreateDBService( + barbicanTest.Instance.Namespace, + GetBarbican(barbicanTest.Instance).Spec.DatabaseInstance, + corev1.ServiceSpec{ + Ports: []corev1.ServicePort{{Port: 3306}}, + }, + ), + ) + mariadb.SimulateMariaDBAccountCompleted(barbicanTest.BarbicanDatabaseAccount) + mariadb.SimulateMariaDBDatabaseCompleted(barbicanTest.BarbicanDatabaseName) + DeferCleanup(keystone.DeleteKeystoneAPI, keystone.CreateKeystoneAPI(barbicanTest.Instance.Namespace)) + th.SimulateJobSuccess(barbicanTest.BarbicanDBSync) + DeferCleanup(th.DeleteInstance, CreateBarbicanAPI(barbicanTest.Instance, GetHSMBarbicanAPISpec())) + th.SimulateJobSuccess(barbicanTest.BarbicanP11Prep) + }) + + It("Creates BarbicanAPI", func() { + keystone.SimulateKeystoneEndpointReady(barbicanTest.BarbicanKeystoneEndpoint) + + th.ExpectCondition( + barbicanTest.Instance, + ConditionGetterFunc(BarbicanAPIConditionGetter), + condition.TLSInputReadyCondition, + corev1.ConditionTrue, + ) + + BarbicanAPIExists(barbicanTest.Instance) + + d := th.GetDeployment(barbicanTest.BarbicanAPI) + // Check the resulting deployment fields + Expect(int(*d.Spec.Replicas)).To(Equal(1)) + + Expect(d.Spec.Template.Spec.Volumes).To(HaveLen(4)) + Expect(d.Spec.Template.Spec.Containers).To(HaveLen(2)) + + container := d.Spec.Template.Spec.Containers[1] + + Expect(container.ReadinessProbe.HTTPGet.Scheme).To(Equal(corev1.URISchemeHTTP)) + Expect(container.LivenessProbe.HTTPGet.Scheme).To(Equal(corev1.URISchemeHTTP)) + + // Checking the HSM container + Expect(container.Name).To(Equal(barbican.ComponentAPI)) + foundMount := false + indexMount := 0 + for index, volumeMount := range container.VolumeMounts { + if volumeMount.Name == barbican.LunaVolume { + foundMount = true + indexMount = index + break + } + } + Expect(foundMount).To(BeTrue()) + Expect(container.VolumeMounts[indexMount].MountPath).To(Equal(HSMCertificatesMountPoint)) + }) + + It("Verifies the PKCS11 struct is in good shape", func() { + Barbican := GetBarbican(barbicanTest.Instance) + Expect(Barbican.Spec.EnabledSecretStores).Should(Equal([]barbicanv1beta1.SecretStore{"pkcs11"})) + Expect(Barbican.Spec.GlobalDefaultSecretStore).Should(Equal(barbicanv1beta1.SecretStore("pkcs11"))) + + pkcs11 := Barbican.Spec.PKCS11 + Expect(pkcs11.SlotId).Should(Equal(HSMSlotID)) + Expect(pkcs11.LibraryPath).Should(Equal(HSMLibraryPath)) + Expect(pkcs11.CertificatesMountPoint).Should(Equal(HSMCertificatesMountPoint)) + Expect(pkcs11.LoginSecret).Should(Equal(HSMLoginSecret)) + Expect(pkcs11.CertificatesSecret).Should(Equal(HSMCertsSecret)) + Expect(pkcs11.MKEKLabel).Should(Equal(HSMMKEKLabel)) + Expect(pkcs11.HMACLabel).Should(Equal(HSMHMACLabel)) + Expect(pkcs11.ServerAddress).Should(Equal(HSMServerAddress)) + Expect(pkcs11.ClientAddress).Should(Equal(HSMClientAddress)) + Expect(pkcs11.Type).Should(Equal(HSMType)) + }) + + It("Checks if the two relevant secrets have the right contents", func() { + hsmSecret := th.GetSecret(barbicanTest.BarbicanHSMLoginSecret) + Expect(hsmSecret).ShouldNot(BeNil()) + confHSM := hsmSecret.Data["hsmLogin"] + Expect(confHSM).To( + ContainSubstring("12345678")) + + certsSecret := th.GetSecret(barbicanTest.BarbicanHSMCertsSecret) + Expect(certsSecret).ShouldNot(BeNil()) + confCA := certsSecret.Data["CACert.pem"] + Expect(confCA).To( + ContainSubstring("dummy-data")) + confServer := certsSecret.Data[HSMServerAddress+"Server.pem"] + Expect(confServer).To( + ContainSubstring("dummy-data")) + + confClient := certsSecret.Data[HSMClientAddress+"Client.pem"] + Expect(confClient).To( + ContainSubstring("dummy-data")) + confKey := certsSecret.Data[HSMClientAddress+"Client.key"] + Expect(confKey).To( + ContainSubstring("dummy-data")) + }) + + It("Verifies if 00-default.conf, barbican-api-config.json and Chrystoki.conf have the right contents.", func() { + confSecret := th.GetSecret(barbicanTest.BarbicanConfigSecret) + Expect(confSecret).ShouldNot(BeNil()) + + conf := confSecret.Data["Chrystoki.conf"] + Expect(conf).To( + ContainSubstring("Chrystoki2")) + Expect(conf).To( + ContainSubstring("LunaSA Client")) + Expect(conf).To( + ContainSubstring("ProtectedAuthenticationPathFlagStatus = 0")) + Expect(conf).To( + ContainSubstring("ClientPrivKeyFile = " + HSMCertificatesMountPoint + "/" + HSMClientAddress + "Key.pem")) + Expect(conf).To( + ContainSubstring("ClientCertFile = " + HSMCertificatesMountPoint + "/" + HSMClientAddress + ".pem")) + Expect(conf).To( + ContainSubstring("ServerCAFile = " + HSMCertificatesMountPoint + "/CACert.pem")) + + conf = confSecret.Data["00-default.conf"] + Expect(conf).To( + ContainSubstring("[secretstore:pkcs11]")) + Expect(conf).To( + ContainSubstring("plugin_name = PKCS11")) + Expect(conf).To( + ContainSubstring("slot_id = " + HSMSlotID)) + + conf = confSecret.Data["barbican-api-config.json"] + Expect(conf).To( + ContainSubstring("/var/lib/config-data/default/Chrystoki.conf")) + Expect(conf).To( + ContainSubstring("/usr/local/luna/Chrystoki.conf")) + }) + + It("Checks if the P11PreJob successfully executed", func() { + BarbicanExists(barbicanTest.Instance) + + th.ExpectCondition( + barbicanTest.Instance, + ConditionGetterFunc(BarbicanConditionGetter), + controllers.P11PrepReadyCondition, + corev1.ConditionTrue, + ) + + // Checking if both, the volume mount name and its mount path match the specified values. + var elemLuna, elemScript = 0, 0 + for index, mount := range th.GetJob(barbicanTest.BarbicanP11Prep).Spec.Template.Spec.Containers[0].VolumeMounts { + if mount.Name == barbican.LunaVolume { + elemLuna = index + } else if mount.Name == barbican.ScriptVolume { + elemScript = index + } + } + + volume := th.GetJob(barbicanTest.BarbicanP11Prep).Spec.Template.Spec.Containers[0].VolumeMounts[elemLuna].Name + mountPath := th.GetJob(barbicanTest.BarbicanP11Prep).Spec.Template.Spec.Containers[0].VolumeMounts[elemLuna].MountPath + + Eventually(func(g Gomega) { + g.Expect(volume).To(Equal(barbican.LunaVolume)) + g.Expect(mountPath).To(Equal(HSMCertificatesMountPoint)) + }, timeout, interval).Should(Succeed()) + + volume = th.GetJob(barbicanTest.BarbicanP11Prep).Spec.Template.Spec.Containers[0].VolumeMounts[elemScript].Name + mountPath = th.GetJob(barbicanTest.BarbicanP11Prep).Spec.Template.Spec.Containers[0].VolumeMounts[elemScript].MountPath + + Eventually(func(g Gomega) { + g.Expect(volume).To(Equal(barbican.ScriptVolume)) + g.Expect(mountPath).To(Equal(P11PrepMountPoint)) + }, 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. diff --git a/tests/functional/barbican_test_data.go b/tests/functional/barbican_test_data.go index 0557877..575e7b6 100644 --- a/tests/functional/barbican_test_data.go +++ b/tests/functional/barbican_test_data.go @@ -54,6 +54,7 @@ type BarbicanTestData struct { BarbicanDatabaseName types.NamespacedName BarbicanDatabaseAccount types.NamespacedName BarbicanDBSync types.NamespacedName + BarbicanP11Prep types.NamespacedName BarbicanAPI types.NamespacedName BarbicanRole types.NamespacedName BarbicanRoleBinding types.NamespacedName @@ -65,6 +66,8 @@ type BarbicanTestData struct { BarbicanServiceInternal types.NamespacedName BarbicanConfigSecret types.NamespacedName BarbicanAPIConfigSecret types.NamespacedName + BarbicanHSMLoginSecret types.NamespacedName + BarbicanHSMCertsSecret types.NamespacedName BarbicanConfigScripts types.NamespacedName BarbicanConfigMapData types.NamespacedName BarbicanScheduler types.NamespacedName @@ -97,6 +100,10 @@ func GetBarbicanTestData(barbicanName types.NamespacedName) BarbicanTestData { Namespace: barbicanName.Namespace, Name: fmt.Sprintf("%s-db-sync", barbicanName.Name), }, + BarbicanP11Prep: types.NamespacedName{ + Namespace: barbicanName.Namespace, + Name: fmt.Sprintf("%s-p11-prep", barbicanName.Name), + }, BarbicanAPI: types.NamespacedName{ Namespace: barbicanName.Namespace, Name: fmt.Sprintf("%s-api-api", barbicanName.Name), @@ -142,6 +149,16 @@ func GetBarbicanTestData(barbicanName types.NamespacedName) BarbicanTestData { Namespace: barbicanName.Namespace, Name: fmt.Sprintf("%s-%s", barbicanName.Name, "api-config-data"), }, + // This secret stores the password to connect to the HSM. + BarbicanHSMLoginSecret: types.NamespacedName{ + Namespace: barbicanName.Namespace, + Name: "hsm-login", + }, + // This secret stores the certificates used to interact with the HSM. + BarbicanHSMCertsSecret: types.NamespacedName{ + Namespace: barbicanName.Namespace, + Name: "hsm-certs", + }, BarbicanConfigScripts: types.NamespacedName{ Namespace: barbicanName.Namespace, Name: fmt.Sprintf("%s-%s", barbicanName.Name, "scripts"), diff --git a/tests/functional/base_test.go b/tests/functional/base_test.go index 40de094..c0930f8 100644 --- a/tests/functional/base_test.go +++ b/tests/functional/base_test.go @@ -123,7 +123,14 @@ func BarbicanKeystoneListenerNotExists(name types.NamespacedName) { }, timeout, interval).Should(Succeed()) } -// ========== TLS Stuff ============== +func BarbicanExists(name types.NamespacedName) { + Consistently(func(g Gomega) { + instance := &barbicanv1.Barbican{} + err := k8sClient.Get(ctx, name, instance) + g.Expect(k8s_errors.IsNotFound(err)).To(BeFalse()) + }, timeout, interval).Should(Succeed()) +} + func BarbicanAPIConditionGetter(name types.NamespacedName) condition.Conditions { instance := GetBarbicanAPI(name) return instance.Status.Conditions @@ -145,6 +152,7 @@ func GetBarbicanAPI(name types.NamespacedName) *barbicanv1.BarbicanAPI { return instance } +// ========== TLS Stuff ============== func GetTLSBarbicanSpec() map[string]interface{} { return map[string]interface{}{ "databaseInstance": "openstack", @@ -172,6 +180,57 @@ func GetTLSBarbicanAPISpec() map[string]interface{} { return spec } +// ========== End of TLS Stuff ============ + +// ========== HSM Stuff ============ +func GetHSMBarbicanSpec() map[string]interface{} { + spec := GetDefaultBarbicanSpec() + maps.Copy(spec, map[string]interface{}{ + "enabledSecretStores": []string{"pkcs11"}, + "globalDefaultSecretStore": "pkcs11", + "pkcs11": map[string]interface{}{ + "slotId": HSMSlotID, + "libraryPath": HSMLibraryPath, + "certificatesMountPoint": HSMCertificatesMountPoint, + "loginSecret": HSMLoginSecret, + "certificatesSecret": HSMCertsSecret, + "MKEKLabel": HSMMKEKLabel, + "HMACLabel": HSMHMACLabel, + "serverAddress": HSMServerAddress, + "clientAddress": HSMClientAddress, + "type": HSMType, + }, + }) + return spec +} + +func GetHSMBarbicanAPISpec() map[string]interface{} { + return GetDefaultBarbicanAPISpec() +} + +func CreateHSMLoginSecret(namespace string, name string) *corev1.Secret { + return th.CreateSecret( + types.NamespacedName{Namespace: namespace, Name: name}, + map[string][]byte{ + "hsmLogin": []byte("12345678"), + }, + ) +} + +func CreateHSMCertsSecret(namespace string, name string) *corev1.Secret { + return th.CreateSecret( + types.NamespacedName{Namespace: namespace, Name: name}, + map[string][]byte{ + "CACert.pem": []byte("dummy-data"), + HSMServerAddress + "Server.pem": []byte("dummy-data"), + HSMClientAddress + "Client.pem": []byte("dummy-data"), + HSMClientAddress + "Client.key": []byte("dummy-data"), + }, + ) +} + +// ========== End of HSM Stuff ============ + func GetDefaultBarbicanAPISpec() map[string]interface{} { return map[string]interface{}{ "secret": SecretName, diff --git a/tests/functional/suite_test.go b/tests/functional/suite_test.go index 9db6eca..7b111eb 100644 --- a/tests/functional/suite_test.go +++ b/tests/functional/suite_test.go @@ -69,6 +69,19 @@ const ( SecretName = "test-osp-secret" interval = time.Millisecond * 200 + + // HSM Constants + HSMType = "luna" // Using them Luna model without any specific selection criteria. + HSMLibraryPath = "/usr/local/luna/libs/64/libCryptoki2.so" + HSMCertificatesMountPoint = "/usr/local/luna/config/certs" + HSMSlotID = "1" + HSMMKEKLabel = "MKEKLabel" + HSMHMACLabel = "HMACLabel" + HSMServerAddress = "192.168.0.1" + HSMClientAddress = "192.168.0.2" + HSMLoginSecret = "hsm-login" + HSMCertsSecret = "hsm-certs" + P11PrepMountPoint = "/usr/local/bin/container-scripts" ) func TestAPIs(t *testing.T) {