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