From 38cb117545b0ecd8c7da52843f1d5ef3984b1e89 Mon Sep 17 00:00:00 2001 From: Christian Schwede Date: Mon, 26 Feb 2024 23:17:31 +0100 Subject: [PATCH] Add Barbican support Creates a Barbican secret for Swift if needed and configures Barbican middlewares in the Swift proxy. The required middlewares are disabled by default and require Barbican. They can be enabled in the CR: [...] kind: SwiftProxy spec: enableEncryption: True [...] --- .../swift.openstack.org_swiftproxies.yaml | 4 + api/bases/swift.openstack.org_swifts.yaml | 4 + api/v1beta1/swiftproxy_types.go | 5 + .../swift.openstack.org_swiftproxies.yaml | 4 + .../crd/bases/swift.openstack.org_swifts.yaml | 4 + config/rbac/role.yaml | 8 ++ controllers/swift_controller.go | 1 + controllers/swiftproxy_controller.go | 25 ++++ go.mod | 5 +- go.sum | 2 + main.go | 2 + pkg/swiftproxy/barbican.go | 115 ++++++++++++++++++ pkg/swiftproxy/const.go | 3 + pkg/swiftproxy/templates.go | 2 + templates/common/ring-sync.sh | 2 +- .../swiftproxy/config/00-proxy-server.conf | 9 +- templates/swiftproxy/config/keymaster.conf | 8 ++ 17 files changed, 199 insertions(+), 4 deletions(-) create mode 100644 pkg/swiftproxy/barbican.go create mode 100644 templates/swiftproxy/config/keymaster.conf diff --git a/api/bases/swift.openstack.org_swiftproxies.yaml b/api/bases/swift.openstack.org_swiftproxies.yaml index bcde61aa..3c585d38 100644 --- a/api/bases/swift.openstack.org_swiftproxies.yaml +++ b/api/bases/swift.openstack.org_swiftproxies.yaml @@ -57,6 +57,10 @@ spec: description: DefaultConfigOverwrite - can be used to add additionalfiles. Those get added to the service config dir in /etc/-conf.d type: object + encryptionEnabled: + default: false + description: Encrypts new objects at rest + type: boolean memcachedServers: default: "" description: List of memcached servers. diff --git a/api/bases/swift.openstack.org_swifts.yaml b/api/bases/swift.openstack.org_swifts.yaml index 62d9b831..b5f93a36 100644 --- a/api/bases/swift.openstack.org_swifts.yaml +++ b/api/bases/swift.openstack.org_swifts.yaml @@ -80,6 +80,10 @@ spec: description: DefaultConfigOverwrite - can be used to add additionalfiles. Those get added to the service config dir in /etc/-conf.d type: object + encryptionEnabled: + default: false + description: Encrypts new objects at rest + type: boolean memcachedServers: default: "" description: List of memcached servers. diff --git a/api/v1beta1/swiftproxy_types.go b/api/v1beta1/swiftproxy_types.go index 2e78c4a0..8752f585 100644 --- a/api/v1beta1/swiftproxy_types.go +++ b/api/v1beta1/swiftproxy_types.go @@ -86,6 +86,11 @@ type SwiftProxySpec struct { // DefaultConfigOverwrite - can be used to add additionalfiles. Those get // added to the service config dir in /etc/-conf.d DefaultConfigOverwrite map[string]string `json:"defaultConfigOverwrite,omitempty"` + + // +kubebuilder:validation:Optional + // +kubebuilder:default=false + // Encrypts new objects at rest + EncryptionEnabled bool `json:"encryptionEnabled"` } // ProxyOverrideSpec to override the generated manifest of several child resources. diff --git a/config/crd/bases/swift.openstack.org_swiftproxies.yaml b/config/crd/bases/swift.openstack.org_swiftproxies.yaml index bcde61aa..3c585d38 100644 --- a/config/crd/bases/swift.openstack.org_swiftproxies.yaml +++ b/config/crd/bases/swift.openstack.org_swiftproxies.yaml @@ -57,6 +57,10 @@ spec: description: DefaultConfigOverwrite - can be used to add additionalfiles. Those get added to the service config dir in /etc/-conf.d type: object + encryptionEnabled: + default: false + description: Encrypts new objects at rest + type: boolean memcachedServers: default: "" description: List of memcached servers. diff --git a/config/crd/bases/swift.openstack.org_swifts.yaml b/config/crd/bases/swift.openstack.org_swifts.yaml index 62d9b831..b5f93a36 100644 --- a/config/crd/bases/swift.openstack.org_swifts.yaml +++ b/config/crd/bases/swift.openstack.org_swifts.yaml @@ -80,6 +80,10 @@ spec: description: DefaultConfigOverwrite - can be used to add additionalfiles. Those get added to the service config dir in /etc/-conf.d type: object + encryptionEnabled: + default: false + description: Encrypts new objects at rest + type: boolean memcachedServers: default: "" description: List of memcached servers. diff --git a/config/rbac/role.yaml b/config/rbac/role.yaml index eddab010..23ef1e31 100644 --- a/config/rbac/role.yaml +++ b/config/rbac/role.yaml @@ -63,6 +63,14 @@ rules: - patch - update - watch +- apiGroups: + - barbican.openstack.org + resources: + - barbicanapis + verbs: + - get + - list + - watch - apiGroups: - batch resources: diff --git a/controllers/swift_controller.go b/controllers/swift_controller.go index 1e775dbb..d02e2087 100644 --- a/controllers/swift_controller.go +++ b/controllers/swift_controller.go @@ -433,6 +433,7 @@ func (r *SwiftReconciler) proxyCreateOrUpdate(ctx context.Context, instance *swi MemcachedServers: memcachedServers, TLS: instance.Spec.SwiftProxy.TLS, DefaultConfigOverwrite: instance.Spec.SwiftProxy.DefaultConfigOverwrite, + EncryptionEnabled: instance.Spec.SwiftProxy.EncryptionEnabled, } deployment := &swiftv1.SwiftProxy{ diff --git a/controllers/swiftproxy_controller.go b/controllers/swiftproxy_controller.go index 6a907b32..17b75e08 100644 --- a/controllers/swiftproxy_controller.go +++ b/controllers/swiftproxy_controller.go @@ -78,6 +78,7 @@ type SwiftProxyReconciler struct { //+kubebuilder:rbac:groups=core,resources=secrets,verbs=get;list;watch;create;update;patch;delete; //+kubebuilder:rbac:groups=k8s.cni.cncf.io,resources=network-attachment-definitions,verbs=get;list;watch //+kubebuilder:rbac:groups=memcached.openstack.org,resources=memcacheds,verbs=get;list;watch; +//+kubebuilder:rbac:groups=barbican.openstack.org,resources=barbicanapis,verbs=get;list;watch // Reconcile is part of the main kubernetes reconciliation loop which aims to // move the current state of the cluster closer to the desired state. @@ -429,6 +430,29 @@ func (r *SwiftProxyReconciler) Reconcile(ctx context.Context, req ctrl.Request) envVars[sps.Name] = env.SetValue(hash) password := string(sps.Data[instance.Spec.PasswordSelectors.Service]) + secretRef := "" + if instance.Spec.EncryptionEnabled { + secretRef, err = swiftproxy.GetBarbicanSecret(instance, helper, keystonePublicURL, password) + if err != nil { + if apierrors.IsNotFound(err) { + instance.Status.Conditions.Set(condition.FalseCondition( + condition.InputReadyCondition, + condition.RequestedReason, + condition.SeverityInfo, + condition.InputReadyWaitingMessage)) + r.Log.Error(err, "Failed to get secretRef from Barbican") + return ctrl.Result{RequeueAfter: time.Duration(10) * time.Second}, nil + } + instance.Status.Conditions.Set(condition.FalseCondition( + condition.InputReadyCondition, + condition.ErrorReason, + condition.SeverityWarning, + condition.InputReadyErrorMessage, + err.Error())) + return ctrl.Result{}, err + } + } + instance.Status.Conditions.MarkTrue(condition.InputReadyCondition, condition.InputReadyMessage) // Create a Secret populated with content from templates/ @@ -439,6 +463,7 @@ func (r *SwiftProxyReconciler) Reconcile(ctx context.Context, req ctrl.Request) keystoneInternalURL, password, instance.Spec.MemcachedServers, + secretRef, ) err = secret.EnsureSecrets(ctx, helper, instance, tpl, &envVars) if err != nil { diff --git a/go.mod b/go.mod index 2b0adebc..ff0cc187 100644 --- a/go.mod +++ b/go.mod @@ -4,13 +4,16 @@ go 1.20 require ( github.com/go-logr/logr v1.4.1 + github.com/gophercloud/gophercloud v1.9.0 github.com/k8snetworkplumbingwg/network-attachment-definition-client v1.4.0 github.com/onsi/ginkgo/v2 v2.15.0 github.com/onsi/gomega v1.31.1 + github.com/openstack-k8s-operators/barbican-operator/api v0.0.0-20240226165142-cb4e05f187dc github.com/openstack-k8s-operators/dataplane-operator/api v0.3.1-0.20240219024506-c76c45e69237 github.com/openstack-k8s-operators/infra-operator/apis v0.3.1-0.20240218132212-ad757a2f5bab github.com/openstack-k8s-operators/keystone-operator/api v0.3.1-0.20240216173228-eec429bcc776 github.com/openstack-k8s-operators/lib-common/modules/common v0.3.1-0.20240216173409-86913e6d5885 + github.com/openstack-k8s-operators/lib-common/modules/openstack v0.3.1-0.20240216173409-86913e6d5885 github.com/openstack-k8s-operators/swift-operator/api v0.0.0-00010101000000-000000000000 k8s.io/api v0.28.7 k8s.io/apimachinery v0.28.7 @@ -39,7 +42,6 @@ require ( github.com/google/gofuzz v1.2.0 // indirect github.com/google/pprof v0.0.0-20230510103437-eeec1cb781c3 // indirect github.com/google/uuid v1.6.0 // indirect - github.com/gophercloud/gophercloud v1.9.0 // indirect github.com/imdario/mergo v0.3.16 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect @@ -50,7 +52,6 @@ require ( github.com/modern-go/reflect2 v1.0.2 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/openshift/api v3.9.0+incompatible // indirect - github.com/openstack-k8s-operators/lib-common/modules/openstack v0.3.1-0.20240216173409-86913e6d5885 // indirect github.com/openstack-k8s-operators/lib-common/modules/storage v0.3.1-0.20240216173409-86913e6d5885 // indirect github.com/openstack-k8s-operators/openstack-baremetal-operator/api v0.3.1-0.20240218185734-5d372a0fd380 // indirect github.com/pkg/errors v0.9.1 // indirect diff --git a/go.sum b/go.sum index 8958563f..bada2f2d 100644 --- a/go.sum +++ b/go.sum @@ -78,6 +78,8 @@ github.com/onsi/gomega v1.31.1 h1:KYppCUK+bUgAZwHOu7EXVBKyQA6ILvOESHkn/tgoqvo= github.com/onsi/gomega v1.31.1/go.mod h1:y40C95dwAD1Nz36SsEnxvfFe8FFfNxzI5eJ0EYGyAy0= github.com/openshift/api v0.0.0-20230414143018-3367bc7e6ac7 h1:rncLxJBpFGqBztyxCMwNRnMjhhIDOWHJowi6q8G6koI= github.com/openshift/api v0.0.0-20230414143018-3367bc7e6ac7/go.mod h1:ctXNyWanKEjGj8sss1KjjHQ3ENKFm33FFnS5BKaIPh4= +github.com/openstack-k8s-operators/barbican-operator/api v0.0.0-20240226165142-cb4e05f187dc h1:vVHP9FoB7xeqFWNho6E5V6Q5Aqp2shAeC8FE/wHUwK4= +github.com/openstack-k8s-operators/barbican-operator/api v0.0.0-20240226165142-cb4e05f187dc/go.mod h1:BJoXL5gDpxJ4qCPKwfCjg6TG3Gm9cjeVA8qoDSFVBNI= github.com/openstack-k8s-operators/dataplane-operator/api v0.3.1-0.20240219024506-c76c45e69237 h1:CBcDpN4bSG+qmssnMKYFnXqLCdAFnmVHeLRHQlo/HeY= github.com/openstack-k8s-operators/dataplane-operator/api v0.3.1-0.20240219024506-c76c45e69237/go.mod h1:1M1q8AhjgB9P/Jxr4WodQxwv+C5AsVh9O6ZZlWiSEe4= github.com/openstack-k8s-operators/infra-operator/apis v0.3.1-0.20240218132212-ad757a2f5bab h1:zdI202C0TtXz5UPB+uTdxZtIaFseEDV0HtQ/MmX6hQU= diff --git a/main.go b/main.go index d897ba49..563505c2 100644 --- a/main.go +++ b/main.go @@ -43,6 +43,7 @@ import ( "github.com/openstack-k8s-operators/lib-common/modules/common/util" networkv1 "github.com/k8snetworkplumbingwg/network-attachment-definition-client/pkg/apis/k8s.cni.cncf.io/v1" + barbicanv1 "github.com/openstack-k8s-operators/barbican-operator/api/v1beta1" dataplanev1 "github.com/openstack-k8s-operators/dataplane-operator/api/v1beta1" memcachedv1 "github.com/openstack-k8s-operators/infra-operator/apis/memcached/v1beta1" infranetworkv1 "github.com/openstack-k8s-operators/infra-operator/apis/network/v1beta1" @@ -64,6 +65,7 @@ func init() { utilruntime.Must(networkv1.AddToScheme(scheme)) utilruntime.Must(infranetworkv1.AddToScheme(scheme)) utilruntime.Must(dataplanev1.AddToScheme(scheme)) + utilruntime.Must(barbicanv1.AddToScheme(scheme)) //+kubebuilder:scaffold:scheme } diff --git a/pkg/swiftproxy/barbican.go b/pkg/swiftproxy/barbican.go new file mode 100644 index 00000000..f4941956 --- /dev/null +++ b/pkg/swiftproxy/barbican.go @@ -0,0 +1,115 @@ +/* + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package swiftproxy + +import ( + "context" + + "github.com/openstack-k8s-operators/lib-common/modules/common/helper" + "github.com/openstack-k8s-operators/lib-common/modules/common/secret" + "github.com/openstack-k8s-operators/lib-common/modules/openstack" + + swiftv1 "github.com/openstack-k8s-operators/swift-operator/api/v1beta1" + + gophercloud "github.com/gophercloud/gophercloud" + gopherstack "github.com/gophercloud/gophercloud/openstack" + "github.com/gophercloud/gophercloud/openstack/keymanager/v1/orders" + "github.com/gophercloud/gophercloud/openstack/keymanager/v1/secrets" +) + +func GetBarbicanSecret(instance *swiftv1.SwiftProxy, h *helper.Helper, keystonePublicURL string, password string) (string, error) { + secretRef := "" + + caCerts := []string{} + if len(instance.Spec.TLS.CaBundleSecretName) > 0 { + cacertSecret, _, err := secret.GetSecret(context.TODO(), h, instance.Spec.TLS.CaBundleSecretName, instance.Namespace) + if err != nil { + return secretRef, err + } + for _, cert := range cacertSecret.Data { + caCerts = append(caCerts, string(cert)) + } + } + + authOpts := openstack.AuthOpts{ + AuthURL: keystonePublicURL, + Username: instance.Spec.ServiceUser, + Password: password, + DomainName: "Default", + TenantName: "service", + TLS: &openstack.TLSConfig{ + CACerts: caCerts, + }, + } + + providerClient, err := openstack.GetOpenStackProvider(authOpts) + if err != nil { + return secretRef, err + } + + keyManagerClient, err := gopherstack.NewKeyManagerV1(providerClient, gophercloud.EndpointOpts{}) + if err != nil { + return secretRef, err + } + + // Find secret for Swift + listOpts := secrets.ListOpts{Name: BarbicanSecretName} + allPages, err := secrets.List(keyManagerClient, listOpts).AllPages() + if err != nil { + return secretRef, err + } + allSecrets, err := secrets.ExtractSecrets(allPages) + if err != nil { + return secretRef, err + } + for _, secret := range allSecrets { + if secret.Status == "ACTIVE" { + secretRef = secret.SecretRef + } + } + + // No secret found so far, create a new one + if secretRef == "" { + createOpts := orders.CreateOpts{ + Type: orders.KeyOrder, + Meta: orders.MetaOpts{ + Name: BarbicanSecretName, + Algorithm: "aes", + BitLength: 256, + Mode: "ctr", + }, + } + _, err := orders.Create(keyManagerClient, createOpts).Extract() + if err != nil { + return secretRef, err + } + + allPages, err = secrets.List(keyManagerClient, listOpts).AllPages() + if err != nil { + return secretRef, err + } + allSecrets, err = secrets.ExtractSecrets(allPages) + if err != nil { + return secretRef, err + } + for _, secret := range allSecrets { + if secret.Status == "ACTIVE" { + secretRef = secret.SecretRef + } + } + } + return secretRef, err +} diff --git a/pkg/swiftproxy/const.go b/pkg/swiftproxy/const.go index e8a97413..9514a6aa 100644 --- a/pkg/swiftproxy/const.go +++ b/pkg/swiftproxy/const.go @@ -18,4 +18,7 @@ package swiftproxy const ( // ComponentName - ComponentName = "swift-proxy" + + // Same as in OOO to be backward compatible + BarbicanSecretName = "swift_root_secret_uuid" ) diff --git a/pkg/swiftproxy/templates.go b/pkg/swiftproxy/templates.go index 66784601..e95c2b9c 100644 --- a/pkg/swiftproxy/templates.go +++ b/pkg/swiftproxy/templates.go @@ -33,6 +33,7 @@ func SecretTemplates( keystoneInternalURL string, password string, memcachedServers string, + secretRef string, ) []util.Template { templateParameters := make(map[string]interface{}) templateParameters["ServiceUser"] = instance.Spec.ServiceUser @@ -40,6 +41,7 @@ func SecretTemplates( templateParameters["KeystonePublicURL"] = keystonePublicURL templateParameters["KeystoneInternalURL"] = keystoneInternalURL templateParameters["MemcachedServers"] = memcachedServers + templateParameters["SecretRef"] = secretRef // create httpd vhost template parameters httpdVhostConfig := map[string]interface{}{} diff --git a/templates/common/ring-sync.sh b/templates/common/ring-sync.sh index f9e48e7d..8797e652 100755 --- a/templates/common/ring-sync.sh +++ b/templates/common/ring-sync.sh @@ -5,7 +5,7 @@ MTIME="0" # This is done only initially, will be done by Kolla eventually mkdir /etc/swift/account-server.conf.d /etc/swift/container-server.conf.d /etc/swift/object-server.conf.d /etc/swift/object-expirer.conf.d /etc/swift/proxy-server.conf.d -cp -t /etc/swift/ /var/lib/config-data/swiftconf/swift.conf /var/lib/config-data/default/internal-client.conf /var/lib/config-data/default/rsyncd.conf /var/lib/config-data/default/dispersion.conf +cp -t /etc/swift/ /var/lib/config-data/swiftconf/swift.conf /var/lib/config-data/default/internal-client.conf /var/lib/config-data/default/rsyncd.conf /var/lib/config-data/default/dispersion.conf /var/lib/config-data/default/keymaster.conf cp -t /etc/swift/account-server.conf.d/ /var/lib/config-data/default/*account-server*.conf cp -t /etc/swift/container-server.conf.d/ /var/lib/config-data/default/*container-server*.conf diff --git a/templates/swiftproxy/config/00-proxy-server.conf b/templates/swiftproxy/config/00-proxy-server.conf index 62529f46..342b50a6 100644 --- a/templates/swiftproxy/config/00-proxy-server.conf +++ b/templates/swiftproxy/config/00-proxy-server.conf @@ -3,7 +3,7 @@ bind_host = 127.0.0.1 bind_port = 8081 [pipeline:main] -pipeline = catch_errors gatekeeper healthcheck proxy-logging cache listing_formats container_sync bulk tempurl ratelimit formpost authtoken s3api s3token keystone staticweb copy container-quotas account-quotas slo dlo versioned_writes proxy-logging proxy-server +pipeline = catch_errors gatekeeper healthcheck proxy-logging cache listing_formats container_sync bulk tempurl ratelimit formpost authtoken s3api s3token keystone staticweb copy container-quotas account-quotas slo dlo versioned_writes {{ if .SecretRef }} kms_keymaster encryption {{end}} proxy-logging proxy-server [app:proxy-server] use = egg:swift#proxy @@ -90,3 +90,10 @@ auth_uri = {{ .KeystonePublicURL }}/v3 [filter:staticweb] use = egg:swift#staticweb + +[filter:kms_keymaster] +use = egg:swift#kms_keymaster +keymaster_config_path = /etc/swift/keymaster.conf + +[filter:encryption] +use = egg:swift#encryption diff --git a/templates/swiftproxy/config/keymaster.conf b/templates/swiftproxy/config/keymaster.conf new file mode 100644 index 00000000..1a579984 --- /dev/null +++ b/templates/swiftproxy/config/keymaster.conf @@ -0,0 +1,8 @@ +[kms_keymaster] +key_id = {{ .SecretRef }} +username = {{ .ServiceUser }} +password = {{ .ServicePassword }} +project_name = service +auth_endpoint = {{ .KeystonePublicURL }} +project_domain_name = Default +user_domain_name = Default