Skip to content

Commit

Permalink
Merge pull request #665 from stuggi/tlse_always_create_ca
Browse files Browse the repository at this point in the history
[tls] always create CA certs
  • Loading branch information
openshift-merge-bot[bot] authored Feb 16, 2024
2 parents b5af965 + 13c490f commit 9b67a28
Show file tree
Hide file tree
Showing 8 changed files with 143 additions and 92 deletions.
7 changes: 3 additions & 4 deletions apis/bases/core.openstack.org_openstackcontrolplanes.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -16109,19 +16109,18 @@ spec:
properties:
caBundleSecretName:
type: string
endpoint:
additionalProperties:
caList:
items:
properties:
expires:
format: date-time
type: string
name:
type: string
required:
- expires
- name
type: object
type: object
type: array
type: object
type: object
type: object
Expand Down
9 changes: 4 additions & 5 deletions apis/core/v1beta1/openstackcontrolplane_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -253,7 +253,7 @@ type Override struct {
TLS *TLSServiceOverride `json:"tls,omitempty"`
}

// TLSServiceOverride overrides tls parameters for publioc endpoint
// TLSServiceOverride overrides tls parameters for public endpoint
type TLSServiceOverride struct {
// +kubebuilder:validation:Optional
// Name of a Secret in the same Namespace as the service, containing the server's private key, public certificate
Expand Down Expand Up @@ -660,14 +660,13 @@ type OpenStackControlPlaneStatus struct {

// TLSStatus defines the observed state of TLS
type TLSStatus struct {
Endpoint map[service.Endpoint]TLSCAStatus `json:"endpoint,omitempty"`
tls.Ca `json:",inline"`
CAList []TLSCAStatus `json:"caList,omitempty"`
tls.Ca `json:",inline"`
}

// TLSCAStatus defines the observed state of TLS
type TLSCAStatus struct {
Name string `json:"name"`
// +kubebuilder:validation:Format="date-time"
Name string `json:"name"`
Expires string `json:"expires"`
}

Expand Down
10 changes: 4 additions & 6 deletions apis/core/v1beta1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -16109,19 +16109,18 @@ spec:
properties:
caBundleSecretName:
type: string
endpoint:
additionalProperties:
caList:
items:
properties:
expires:
format: date-time
type: string
name:
type: string
required:
- expires
- name
type: object
type: object
type: array
type: object
type: object
type: object
Expand Down
103 changes: 47 additions & 56 deletions pkg/openstack/ca.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,24 +29,20 @@ import (

// ReconcileCAs -
func ReconcileCAs(ctx context.Context, instance *corev1.OpenStackControlPlane, helper *helper.Helper) (ctrl.Result, error) {
Log := GetLogger(ctx)

// create selfsigned-issuer
issuerReq := certmanager.SelfSignedIssuer(
"selfsigned-issuer",
instance.GetNamespace(),
map[string]string{},
)

// Note (mschuppert) - right now additional custom CA certs can only be passed to the services if
// tls is enabled, otherwise CA bundle creation will be skipped and no bundle will be passed to the
// service CAs.
if !instance.Spec.TLS.Enabled(service.EndpointInternal) && !instance.Spec.TLS.Enabled(service.EndpointPublic) {
// we are not deleting certificates if tls gets disabled
instance.Status.Conditions.Remove(corev1.OpenStackControlPlaneCAReadyCondition)

return ctrl.Result{}, nil
}
// Note (mschuppert) - we always create required CAs and CA bundle, even if TLS should be not enabled.
// This is to allow easy switch to enable TLS later and also be able to distribute the bundle as a pre
// step for adoption

helper.GetLogger().Info("Reconciling CAs", "Namespace", instance.Namespace, "Name", issuerReq.Name)
Log.Info("Reconciling CAs", "Namespace", instance.Namespace, "Name", issuerReq.Name)

issuer := certmanager.NewIssuer(issuerReq, 5)
ctrlResult, err := issuer.CreateOrPatch(ctx, helper)
Expand Down Expand Up @@ -97,53 +93,49 @@ func ReconcileCAs(ctx context.Context, instance *corev1.OpenStackControlPlane, h
}
}

if instance.Status.TLS.Endpoint == nil {
instance.Status.TLS.Endpoint = map[service.Endpoint]corev1.TLSCAStatus{}
}
// create RootCA cert and Issuer that uses the generated CA certificate to issue certs
for endpoint, config := range instance.Spec.TLS.Endpoint {
if config.Enabled {
instance.Status.TLS.CAList = []corev1.TLSCAStatus{}

labels := map[string]string{}
if endpoint == service.EndpointInternal {
labels[certmanager.RootCAIssuerInternalLabel] = ""
}
caName := tls.DefaultCAPrefix + string(endpoint)
// always create a root CA and issuer for the endpoint as we can
// not expect that all services are yet configured to be provided with
// a custom secret holding the cert/private key
caCert, ctrlResult, err := createRootCACertAndIssuer(
ctx,
instance,
helper,
issuerReq,
caName,
labels,
)
if err != nil {
return ctrlResult, err
} else if (ctrlResult != ctrl.Result{}) {
return ctrlResult, nil
}

err = bundle.getCertsFromPEM(caCert)
if err != nil {
return ctrl.Result{}, err
}
err = caOnlyBundle.getCertsFromPEM(caCert)
if err != nil {
return ctrl.Result{}, err
}
// create RootCA cert and Issuer that uses the generated CA certificate to issue certs
for _, endpoint := range []service.Endpoint{service.EndpointPublic, service.EndpointInternal} {
labels := map[string]string{}
if endpoint == service.EndpointInternal {
labels[certmanager.RootCAIssuerInternalLabel] = ""
}
caName := tls.DefaultCAPrefix + string(endpoint)
// always create a root CA and issuer for the endpoint as we can
// not expect that all services are yet configured to be provided with
// a custom secret holding the cert/private key
caCert, ctrlResult, err := createRootCACertAndIssuer(
ctx,
instance,
helper,
issuerReq,
caName,
labels,
)
if err != nil {
return ctrlResult, err
} else if (ctrlResult != ctrl.Result{}) {
return ctrlResult, nil
}

status := corev1.TLSCAStatus{
Name: caName,
}
if len(caOnlyBundle.certs) > 0 {
status.Expires = caOnlyBundle.certs[0].expire.Format(time.RFC3339)
}
err = bundle.getCertsFromPEM(caCert)
if err != nil {
return ctrl.Result{}, err
}
err = caOnlyBundle.getCertsFromPEM(caCert)
if err != nil {
return ctrl.Result{}, err
}

instance.Status.TLS.Endpoint[endpoint] = status
status := corev1.TLSCAStatus{
Name: caName,
}
if len(caOnlyBundle.certs) > 0 {
status.Expires = caOnlyBundle.certs[0].expire.Format(time.RFC3339)
}

instance.Status.TLS.CAList = append(instance.Status.TLS.CAList, status)
}
instance.Status.Conditions.MarkTrue(corev1.OpenStackControlPlaneCAReadyCondition, corev1.OpenStackControlPlaneCAReadyMessage)

Expand All @@ -161,7 +153,7 @@ func ReconcileCAs(ctx context.Context, instance *corev1.OpenStackControlPlane, h
err.Error()))
if k8s_errors.IsNotFound(err) {
timeout := time.Second * 10
helper.GetLogger().Info(fmt.Sprintf("Certificate %s not found, reconcile in %s", instance.Spec.TLS.CaBundleSecretName, timeout.String()))
Log.Info(fmt.Sprintf("Certificate %s not found, reconcile in %s", instance.Spec.TLS.CaBundleSecretName, timeout.String()))

return ctrl.Result{RequeueAfter: timeout}, nil
}
Expand All @@ -184,7 +176,7 @@ func ReconcileCAs(ctx context.Context, instance *corev1.OpenStackControlPlane, h
// if the DownstreamTLSCABundlePath does not exist in the operator image,
// check for UpstreamTLSCABundlePath
if errors.Is(err, os.ErrNotExist) {
helper.GetLogger().Info(fmt.Sprintf("Downstream CA bundle not found using: %s", tls.UpstreamTLSCABundlePath))
Log.Info(fmt.Sprintf("Downstream CA bundle not found using: %s", tls.UpstreamTLSCABundlePath))
caBundle, err = getOperatorCABundle(tls.UpstreamTLSCABundlePath)
if err != nil {
return ctrl.Result{}, err
Expand Down Expand Up @@ -433,7 +425,6 @@ func (cab *caBundle) getCertsFromPEM(PEMdata []byte) error {
}

// if cert is not already in bundle list add it
// validate of nextip is already in a reservation and its not us
f := func(c caCert) bool {
return c.hash == blockHash
}
Expand Down
5 changes: 3 additions & 2 deletions pkg/openstack/rabbitmq.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
condition "github.com/openstack-k8s-operators/lib-common/modules/common/condition"
"github.com/openstack-k8s-operators/lib-common/modules/common/helper"
"github.com/openstack-k8s-operators/lib-common/modules/common/service"
"github.com/openstack-k8s-operators/lib-common/modules/common/tls"
"github.com/openstack-k8s-operators/lib-common/modules/common/util"
rabbitmqv2 "github.com/rabbitmq/cluster-operator/v2/api/v1beta1"

Expand Down Expand Up @@ -176,7 +177,7 @@ func reconcileRabbitMQ(

if instance.Spec.TLS.Enabled(service.EndpointInternal) {
certRequest := certmanager.CertificateRequest{
IssuerName: instance.Status.TLS.Endpoint[service.EndpointInternal].Name,
IssuerName: tls.DefaultCAPrefix + string(service.EndpointInternal),
CertName: fmt.Sprintf("%s-svc", rabbitmq.Name),
Hostnames: []string{hostname},
}
Expand Down Expand Up @@ -229,7 +230,7 @@ func reconcileRabbitMQ(
}

if tlsCert != "" {
rabbitmq.Spec.TLS.CaSecretName = instance.Status.TLS.Endpoint[service.EndpointInternal].Name
rabbitmq.Spec.TLS.CaSecretName = tls.DefaultCAPrefix + string(service.EndpointInternal)
rabbitmq.Spec.TLS.SecretName = tlsCert
// disable non tls listeners
rabbitmq.Spec.TLS.DisableNonTLSListeners = true
Expand Down
55 changes: 55 additions & 0 deletions tests/functional/base_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,11 @@ limitations under the License.
package functional_test

import (
"encoding/base64"

. "github.com/onsi/gomega"

k8s_corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/types"
"sigs.k8s.io/controller-runtime/pkg/client"

Expand All @@ -33,7 +36,9 @@ type Names struct {
KeystoneAPIName types.NamespacedName
MemcachedName types.NamespacedName
DBName types.NamespacedName
DBCertName types.NamespacedName
DBCell1Name types.NamespacedName
DBCell1CertName types.NamespacedName
RabbitMQName types.NamespacedName
RabbitMQCell1Name types.NamespacedName
ServiceAccountName types.NamespacedName
Expand Down Expand Up @@ -87,10 +92,18 @@ func CreateNames(openstackControlplaneName types.NamespacedName) Names {
Namespace: openstackControlplaneName.Namespace,
Name: "openstack",
},
DBCertName: types.NamespacedName{
Namespace: openstackControlplaneName.Namespace,
Name: "cert-openstack-svc",
},
DBCell1Name: types.NamespacedName{
Namespace: openstackControlplaneName.Namespace,
Name: "openstack-cell1",
},
DBCell1CertName: types.NamespacedName{
Namespace: openstackControlplaneName.Namespace,
Name: "cert-openstack-cell1-svc",
},
RabbitMQName: types.NamespacedName{
Namespace: openstackControlplaneName.Namespace,
Name: "rabbitmq",
Expand Down Expand Up @@ -274,3 +287,45 @@ func OpenStackControlPlaneConditionGetter(name types.NamespacedName) condition.C
instance := GetOpenStackControlPlane(name)
return instance.Status.Conditions
}

func CreatePublicCACertSecret(name types.NamespacedName) *k8s_corev1.Secret {
certBase64 := "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJlekNDQVNLZ0F3SUJBZ0lRTkhER1lzQnM3OThpYkREN3EvbzJsakFLQmdncWhrak9QUVFEQWpBZU1Sd3cKR2dZRFZRUURFeE55YjI5MFkyRXRhM1YwZEd3dGNIVmliR2xqTUI0WERUSTBNREV4TlRFd01UVXpObG9YRFRNMApNREV4TWpFd01UVXpObG93SGpFY01Cb0dBMVVFQXhNVGNtOXZkR05oTFd0MWRIUnNMWEIxWW14cFl6QlpNQk1HCkJ5cUdTTTQ5QWdFR0NDcUdTTTQ5QXdFSEEwSUFCRDc4YXZYcWhyaEM1dzhzOVdrZDRJcGJlRXUwM0NSK1hYVWQKa0R6T1J5eGE5d2NjSWREaXZiR0pqSkZaVFRjVm1ianExQk1Zc2pyMTJVSUU1RVQzVmxxalFqQkFNQTRHQTFVZApEd0VCL3dRRUF3SUNwREFQQmdOVkhSTUJBZjhFQlRBREFRSC9NQjBHQTFVZERnUVdCQlRLSml6V1VKOWVVS2kxCmRzMGxyNmM2c0Q3RUJEQUtCZ2dxaGtqT1BRUURBZ05IQURCRUFpQklad1lxNjFCcU1KYUI2VWNGb1JzeGVjd0gKNXovek1PZHJPeWUwbU5pOEpnSWdRTEI0d0RLcnBmOXRYMmxvTSswdVRvcEFEU1lJbnJjZlZ1NEZCdVlVM0lnPQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg=="
keyBase64 := "LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSUptbGNLUEl1RitFc3RhYkxnVmowZkNhdzFTK09xNnJPU3M0U3pMQkJGYVFvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFUHZ4cTllcUd1RUxuRHl6MWFSM2dpbHQ0UzdUY0pINWRkUjJRUE01SExGcjNCeHdoME9LOQpzWW1Na1ZsTk54V1p1T3JVRXhpeU92WFpRZ1RrUlBkV1dnPT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo=="

cert, _ := base64.StdEncoding.DecodeString(certBase64)
key, _ := base64.StdEncoding.DecodeString(keyBase64)

s := &k8s_corev1.Secret{}
Eventually(func(g Gomega) {
s = th.CreateSecret(
name,
map[string][]byte{
"ca.crt": []byte(cert),
"tls.crt": []byte(cert),
"tls.key": []byte(key),
})
}, timeout, interval).Should(Succeed())

return s
}

func CreateInternalCACertSecret(name types.NamespacedName) *k8s_corev1.Secret {
certBase64 := "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJmekNDQVNhZ0F3SUJBZ0lRUWxlcTNZcDBtU2kwVDNiTm03Q29UVEFLQmdncWhrak9QUVFEQWpBZ01SNHcKSEFZRFZRUURFeFZ5YjI5MFkyRXRhM1YwZEd3dGFXNTBaWEp1WVd3d0hoY05NalF3TVRFMU1URTBOelUwV2hjTgpNelF3TVRFeU1URTBOelUwV2pBZ01SNHdIQVlEVlFRREV4VnliMjkwWTJFdGEzVjBkR3d0YVc1MFpYSnVZV3d3CldUQVRCZ2NxaGtqT1BRSUJCZ2dxaGtqT1BRTUJCd05DQUFTRk9rNHJPUldVUGhoTjUrK09EN1I2MW5Gb1lBY0QKenpvUS91SW93NktjeGhwRWNQTDFxb3ZZUGxUYUJabEh3c2FpNE50VHA4aDA1RHVRSGZKOE9JNXFvMEl3UURBTwpCZ05WSFE4QkFmOEVCQU1DQXFRd0R3WURWUjBUQVFIL0JBVXdBd0VCL3pBZEJnTlZIUTRFRmdRVXE3TGtFSk1TCm1MOVpKWjBSOUluKzZkclhycEl3Q2dZSUtvWkl6ajBFQXdJRFJ3QXdSQUlnVlN1K00ydnZ3QlF3eTJHMVlhdkkKQld2RGtSNlRla0I5U0VqdzJIblRSMWtDSUZSNFNkWGFPQkFGWjVHa2RLWCtSY2IzaDFIZm52eFJEVW96bTl2agphenp3Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K=="
keyBase64 := "LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSUV3dlQ2dFZMUWRrVnlXUDV1VnJ3RWRyZ0VLK3drdmttRjFKa0xNYzJCUVFvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFaFRwT0t6a1ZsRDRZVGVmdmpnKzBldFp4YUdBSEE4ODZFUDdpS01PaW5NWWFSSER5OWFxTAoyRDVVMmdXWlI4TEdvdURiVTZmSWRPUTdrQjN5ZkRpT2FnPT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo=="

cert, _ := base64.StdEncoding.DecodeString(certBase64)
key, _ := base64.StdEncoding.DecodeString(keyBase64)

s := &k8s_corev1.Secret{}
Eventually(func(g Gomega) {
s = th.CreateSecret(
name,
map[string][]byte{
"ca.crt": []byte(cert),
"tls.crt": []byte(cert),
"tls.key": []byte(key),
})
}, timeout, interval).Should(Succeed())

return s
}
Loading

0 comments on commit 9b67a28

Please sign in to comment.