Skip to content

Commit 41bbbbf

Browse files
ferozjillaableaseChunyiLyu
authored
Share TLS config with management, amqp 1.0, (web) mqtt and (web) stomp plugins (#451)
* Share TLS config with the management, amqp 1.0, (web) mqtt and (web) stomp plugins (#451) This commit: - Supports TLS for management - Supports TLS for rabbtmq_(web)_mqtt and rabbitmq_(web)_stomp - Sets hardcoded path for CA certificate to 'ca.crt' - Adds system tests to verify remote access via HTTPS to the MGMT dashboard, mqtt, and stomp plugins Co-authored-by: Alex Blease <[email protected]> Co-authored-by: Chunyi Lyu <[email protected]>
1 parent f6f9bc8 commit 41bbbbf

File tree

15 files changed

+752
-128
lines changed

15 files changed

+752
-128
lines changed

api/v1beta1/rabbitmqcluster_types.go

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -238,12 +238,9 @@ type TLSSpec struct {
238238
// The Secret must store these as tls.key and tls.crt, respectively.
239239
SecretName string `json:"secretName,omitempty"`
240240
// Name of a Secret in the same Namespace as the RabbitmqCluster, containing the Certificate Authority's public certificate for TLS.
241-
// This can be the same as SecretName.
242-
// Used for mTLS.
241+
// The Secret must store this as ca.crt.
242+
// Used for mTLS, and TLS for rabbitmq_web_stomp and rabbitmq_web_mqtt.
243243
CaSecretName string `json:"caSecretName,omitempty"`
244-
// The Secret defined in CaSecretName must store the Certificate Authority's public certificate under the key specified in CaCertName.
245-
// Used for mTLS.
246-
CaCertName string `json:"caCertName,omitempty"`
247244
}
248245

249246
// kubebuilder validating tags 'Pattern' and 'MaxLength' must be specified on string type.

config/crd/bases/rabbitmq.com_rabbitmqclusters.yaml

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3734,15 +3734,11 @@ spec:
37343734
type: integer
37353735
tls:
37363736
properties:
3737-
caCertName:
3738-
description: The Secret defined in CaSecretName must store the
3739-
Certificate Authority's public certificate under the key specified
3740-
in CaCertName. Used for mTLS.
3741-
type: string
37423737
caSecretName:
37433738
description: Name of a Secret in the same Namespace as the RabbitmqCluster,
37443739
containing the Certificate Authority's public certificate for
3745-
TLS. This can be the same as SecretName. Used for mTLS.
3740+
TLS. The Secret must store this as ca.crt. Used for mTLS, and
3741+
TLS for rabbitmq_web_stomp and rabbitmq_web_mqtt.
37463742
type: string
37473743
secretName:
37483744
description: Name of a Secret in the same Namespace as the RabbitmqCluster,

controllers/rabbitmqcluster_controller_test.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,10 @@ package controllers_test
1313
import (
1414
"context"
1515
"fmt"
16-
"k8s.io/utils/pointer"
1716
"time"
1817

18+
"k8s.io/utils/pointer"
19+
1920
"k8s.io/apimachinery/pkg/util/intstr"
2021

2122
. "github.com/onsi/ginkgo"
@@ -1066,7 +1067,7 @@ var _ = Describe("RabbitmqClusterController", func() {
10661067
TargetPort: amqpTargetPort,
10671068
},
10681069
corev1.ServicePort{
1069-
Name: "management",
1070+
Name: "http",
10701071
Port: 15672,
10711072
Protocol: corev1.ProtocolTCP,
10721073
TargetPort: managementTargetPort,
@@ -1190,7 +1191,7 @@ func waitForClusterDeletion(ctx context.Context, rabbitmqCluster *rabbitmqv1beta
11901191

11911192
}
11921193

1193-
func verifyError(ctx context.Context, rabbitmqCluster *rabbitmqv1beta1.RabbitmqCluster, expectedError string) {
1194+
func verifyTLSErrorEvents(ctx context.Context, rabbitmqCluster *rabbitmqv1beta1.RabbitmqCluster, expectedError string) {
11941195
tlsEventTimeout := 5 * time.Second
11951196
tlsRetry := 1 * time.Second
11961197
Eventually(func() string { return aggregateEventMsgs(ctx, rabbitmqCluster, "TLSError") }, tlsEventTimeout, tlsRetry).Should(ContainSubstring(expectedError))

controllers/reconcile_tls.go

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package controllers
33
import (
44
"context"
55
"fmt"
6+
67
rabbitmqv1beta1 "github.com/rabbitmq/cluster-operator/api/v1beta1"
78
corev1 "k8s.io/api/core/v1"
89
"k8s.io/apimachinery/pkg/api/errors"
@@ -11,48 +12,49 @@ import (
1112
)
1213

1314
func (r *RabbitmqClusterReconciler) checkTLSSecrets(ctx context.Context, rabbitmqCluster *rabbitmqv1beta1.RabbitmqCluster) (ctrl.Result, error) {
14-
logger := r.Log
1515
secretName := rabbitmqCluster.Spec.TLS.SecretName
16-
logger.Info("TLS set, looking for secret", "secret", secretName, "namespace", rabbitmqCluster.Namespace)
16+
r.Log.Info("TLS enabled, looking for secret", "secret", secretName, "namespace", rabbitmqCluster.Namespace)
1717

1818
// check if secret exists
1919
secret := &corev1.Secret{}
2020
if err := r.Get(ctx, types.NamespacedName{Namespace: rabbitmqCluster.Namespace, Name: secretName}, secret); err != nil {
2121
r.Recorder.Event(rabbitmqCluster, corev1.EventTypeWarning, "TLSError",
22-
fmt.Sprintf("Failed to get TLS secret %v in namespace %v: %v", secretName, rabbitmqCluster.Namespace, err.Error()))
22+
fmt.Sprintf("Failed to get TLS secret %s in namespace %s: %v", secretName, rabbitmqCluster.Namespace, err.Error()))
23+
r.Log.Error(err, "Error setting up TLS", "namespace", rabbitmqCluster.Namespace, "name", rabbitmqCluster.Name)
2324
return ctrl.Result{}, err
2425
}
2526
// check if secret has the right keys
2627
_, hasTLSKey := secret.Data["tls.key"]
2728
_, hasTLSCert := secret.Data["tls.crt"]
2829
if !hasTLSCert || !hasTLSKey {
29-
r.Recorder.Event(rabbitmqCluster, corev1.EventTypeWarning, "TLSError",
30-
fmt.Sprintf("The TLS secret %v in namespace %v must have the fields tls.crt and tls.key", secretName, rabbitmqCluster.Namespace))
31-
32-
return ctrl.Result{}, errors.NewBadRequest("The TLS secret must have the fields tls.crt and tls.key")
30+
err := errors.NewBadRequest(fmt.Sprintf("TLS secret %s in namespace %s does not have the fields tls.crt and tls.key", secretName, rabbitmqCluster.Namespace))
31+
r.Recorder.Event(rabbitmqCluster, corev1.EventTypeWarning, "TLSError", err.Error())
32+
r.Log.Error(err, "Error setting up TLS", "namespace", rabbitmqCluster.Namespace, "name", rabbitmqCluster.Name)
33+
return ctrl.Result{}, err
3334
}
3435

3536
// Mutual TLS: check if CA certificate is stored in a separate secret
3637
if rabbitmqCluster.MutualTLSEnabled() {
3738
if !rabbitmqCluster.SingleTLSSecret() {
3839
secretName := rabbitmqCluster.Spec.TLS.CaSecretName
39-
logger.Info("mutual TLS set, looking for CA certificate secret", "secret", secretName, "namespace", rabbitmqCluster.Namespace)
40+
r.Log.Info("mutual TLS enabled, looking for CA certificate secret", "secret", secretName, "namespace", rabbitmqCluster.Namespace)
4041

4142
// check if secret exists
4243
secret = &corev1.Secret{}
4344
if err := r.Get(ctx, types.NamespacedName{Namespace: rabbitmqCluster.Namespace, Name: secretName}, secret); err != nil {
4445
r.Recorder.Event(rabbitmqCluster, corev1.EventTypeWarning, "TLSError",
4546
fmt.Sprintf("Failed to get CA certificate secret %v in namespace %v: %v", secretName, rabbitmqCluster.Namespace, err.Error()))
47+
r.Log.Error(err, "Error setting up TLS", "namespace", rabbitmqCluster.Namespace, "name", rabbitmqCluster.Name)
4648
return ctrl.Result{}, err
4749
}
4850
}
49-
// Mutual TLS: verify that CA certificate is present in secret
50-
_, hasCaCert := secret.Data[rabbitmqCluster.Spec.TLS.CaCertName]
51-
if !hasCaCert {
52-
r.Recorder.Event(rabbitmqCluster, corev1.EventTypeWarning, "TLSError",
53-
fmt.Sprintf("The TLS secret %v in namespace %v must have the field %v", rabbitmqCluster.Spec.TLS.CaSecretName, rabbitmqCluster.Namespace, rabbitmqCluster.Spec.TLS.CaCertName))
5451

55-
return ctrl.Result{}, errors.NewBadRequest(fmt.Sprintf("The TLS secret must have the field %s", rabbitmqCluster.Spec.TLS.CaCertName))
52+
// Mutual TLS: verify that CA certificate is present in secret
53+
if _, hasCaCert := secret.Data["ca.crt"]; !hasCaCert {
54+
err := errors.NewBadRequest(fmt.Sprintf("TLS secret %s in namespace %s does not have the field ca.crt", rabbitmqCluster.Spec.TLS.CaSecretName, rabbitmqCluster.Namespace))
55+
r.Recorder.Event(rabbitmqCluster, corev1.EventTypeWarning, "TLSError", err.Error())
56+
r.Log.Error(err, "Error setting up TLS", "namespace", rabbitmqCluster.Namespace, "name", rabbitmqCluster.Name)
57+
return ctrl.Result{}, err
5658
}
5759
}
5860
return ctrl.Result{}, nil

controllers/reconcile_tls_test.go

Lines changed: 25 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package controllers_test
33
import (
44
"context"
55
"fmt"
6+
67
rabbitmqv1beta1 "github.com/rabbitmq/cluster-operator/api/v1beta1"
78

89
. "github.com/onsi/ginkgo"
@@ -32,11 +33,10 @@ var _ = Describe("Reconcile TLS", func() {
3233

3334
Context("Mutual TLS with single secret", func() {
3435
It("Deploys successfully", func() {
35-
tlsSecretWithCACert(ctx, "tls-secret", defaultNamespace, "caCERT")
36+
tlsSecretWithCACert(ctx, "tls-secret", defaultNamespace)
3637
tlsSpec := rabbitmqv1beta1.TLSSpec{
3738
SecretName: "tls-secret",
3839
CaSecretName: "tls-secret",
39-
CaCertName: "caCERT",
4040
}
4141
cluster = rabbitmqClusterWithTLS(ctx, "mutual-tls-success", defaultNamespace, tlsSpec)
4242
waitForClusterCreation(ctx, cluster, client)
@@ -45,23 +45,33 @@ var _ = Describe("Reconcile TLS", func() {
4545
Expect(err).NotTo(HaveOccurred())
4646
volumeMount := corev1.VolumeMount{
4747
Name: "rabbitmq-tls",
48-
MountPath: "/etc/rabbitmq-tls/caCERT",
49-
SubPath: "caCERT",
48+
MountPath: "/etc/rabbitmq-tls/ca.crt",
49+
SubPath: "ca.crt",
5050
ReadOnly: true,
5151
}
5252
Expect(sts.Spec.Template.Spec.Containers[0].VolumeMounts).To(ContainElement(volumeMount))
5353
})
5454

5555
It("Does not deploy if the cert name does not match the contents of the secret", func() {
56-
tlsSecretWithCACert(ctx, "tls-secret-missing", defaultNamespace, "ca.c")
56+
tlsData := map[string]string{
57+
"tls.crt": "this is a tls cert",
58+
"tls.key": "this is a tls key",
59+
"wrong-key": "certificate",
60+
}
61+
62+
_, err := createSecret(ctx, "tls-secret-missing", defaultNamespace, tlsData)
63+
64+
if !apierrors.IsAlreadyExists(err) {
65+
Expect(err).NotTo(HaveOccurred())
66+
}
67+
5768
tlsSpec := rabbitmqv1beta1.TLSSpec{
5869
SecretName: "tls-secret-missing",
5970
CaSecretName: "tls-secret-missing",
60-
CaCertName: "ca.crt",
6171
}
6272
cluster = rabbitmqClusterWithTLS(ctx, "tls-secret-missing", defaultNamespace, tlsSpec)
6373

64-
verifyError(ctx, cluster, fmt.Sprintf("The TLS secret tls-secret-missing in namespace %s must have the field ca.crt", defaultNamespace))
74+
verifyTLSErrorEvents(ctx, cluster, fmt.Sprintf("TLS secret tls-secret-missing in namespace %s does not have the field ca.crt", defaultNamespace))
6575
})
6676
})
6777

@@ -72,10 +82,9 @@ var _ = Describe("Reconcile TLS", func() {
7282
tlsSpec := rabbitmqv1beta1.TLSSpec{
7383
SecretName: "rabbitmq-tls-secret-does-not-exist",
7484
CaSecretName: "ca-cert-secret",
75-
CaCertName: "ca.crt",
7685
}
7786
cluster = rabbitmqClusterWithTLS(ctx, "rabbitmq-tls-secret-does-not-exist", defaultNamespace, tlsSpec)
78-
verifyError(ctx, cluster, "Failed to get CA certificate secret")
87+
verifyTLSErrorEvents(ctx, cluster, "Failed to get CA certificate secret")
7988

8089
_, err := clientSet.AppsV1().StatefulSets(cluster.Namespace).Get(ctx, cluster.ChildResourceName("server"), metav1.GetOptions{})
8190
Expect(err).To(HaveOccurred())
@@ -105,10 +114,9 @@ var _ = Describe("Reconcile TLS", func() {
105114
tlsSpec := rabbitmqv1beta1.TLSSpec{
106115
SecretName: "tls-secret",
107116
CaSecretName: "ca-cert-secret-invalid",
108-
CaCertName: "ca.crt",
109117
}
110118
cluster = rabbitmqClusterWithTLS(ctx, "rabbitmq-mutual-tls-missing", defaultNamespace, tlsSpec)
111-
verifyError(ctx, cluster, fmt.Sprintf("The TLS secret ca-cert-secret-invalid in namespace %s must have the field ca.crt", defaultNamespace))
119+
verifyTLSErrorEvents(ctx, cluster, fmt.Sprintf("TLS secret ca-cert-secret-invalid in namespace %s does not have the field ca.crt", defaultNamespace))
112120
})
113121
})
114122
})
@@ -163,7 +171,7 @@ var _ = Describe("Reconcile TLS", func() {
163171
})
164172

165173
It("fails to deploy the RabbitmqCluster", func() {
166-
verifyError(ctx, cluster, fmt.Sprintf("The TLS secret rabbitmq-tls-malformed in namespace %s must have the fields tls.crt and tls.key", defaultNamespace))
174+
verifyTLSErrorEvents(ctx, cluster, fmt.Sprintf("TLS secret rabbitmq-tls-malformed in namespace %s does not have the fields tls.crt and tls.key", defaultNamespace))
167175
})
168176
})
169177

@@ -175,7 +183,7 @@ var _ = Describe("Reconcile TLS", func() {
175183
}
176184
cluster = rabbitmqClusterWithTLS(ctx, "rabbitmq-tls-secret-does-not-exist", defaultNamespace, tlsSpec)
177185

178-
verifyError(ctx, cluster, "Failed to get TLS secret")
186+
verifyTLSErrorEvents(ctx, cluster, "Failed to get TLS secret")
179187

180188
_, err := clientSet.AppsV1().StatefulSets(cluster.Namespace).Get(ctx, cluster.ChildResourceName("server"), metav1.GetOptions{})
181189
Expect(err).To(HaveOccurred())
@@ -192,15 +200,14 @@ var _ = Describe("Reconcile TLS", func() {
192200
statefulSet(ctx, cluster)
193201
})
194202
})
195-
196203
})
197204
})
198205

199-
func tlsSecretWithCACert(ctx context.Context, secretName, namespace, caCertName string) {
206+
func tlsSecretWithCACert(ctx context.Context, secretName, namespace string) {
200207
tlsData := map[string]string{
201-
"tls.crt": "this is a tls cert",
202-
"tls.key": "this is a tls key",
203-
caCertName: "certificate",
208+
"tls.crt": "this is a tls cert",
209+
"tls.key": "this is a tls key",
210+
"ca.crt": "certificate",
204211
}
205212

206213
_, err := createSecret(ctx, secretName, namespace, tlsData)

go.mod

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ module github.com/rabbitmq/cluster-operator
33
go 1.15
44

55
require (
6-
cloud.google.com/go v0.47.0 // indirect
76
github.com/Azure/go-autorest/autorest v0.9.2 // indirect
87
github.com/Azure/go-autorest/autorest/adal v0.8.0 // indirect
98
github.com/cespare/xxhash/v2 v2.1.1 // indirect
@@ -13,9 +12,9 @@ require (
1312
github.com/go-logr/logr v0.1.0
1413
github.com/go-logr/zapr v0.1.1 // indirect
1514
github.com/go-stomp/stomp v2.0.8+incompatible
16-
github.com/golang/groupcache v0.0.0-20191027212112-611e8accdfc9 // indirect
1715
github.com/gophercloud/gophercloud v0.5.0 // indirect
1816
github.com/mattn/go-colorable v0.1.4 // indirect
17+
github.com/michaelklishin/rabbit-hole/v2 v2.5.0
1918
github.com/onsi/ginkgo v1.14.2
2019
github.com/onsi/gomega v1.10.3
2120
github.com/pelletier/go-toml v1.8.1 // indirect
@@ -26,7 +25,6 @@ require (
2625
go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5 // indirect
2726
go.uber.org/multierr v1.2.0 // indirect
2827
golang.org/x/net v0.0.0-20201010224723-4f7140c49acb
29-
golang.org/x/oauth2 v0.0.0-20191122200657-5d9234df094c // indirect
3028
golang.org/x/time v0.0.0-20191024005414-555d28b269f0 // indirect
3129
gopkg.in/ini.v1 v1.62.0
3230
k8s.io/api v0.18.6

0 commit comments

Comments
 (0)