Skip to content

Commit c39b820

Browse files
committed
Configure default user/password through conf.d
This commit stops the operator configuring the default username and password for the cluster through environment variables. Instead, these credentials are configured through `/etc/rabbitmq/conf.d/`. This is for two reasons: * RabbitMQ is moving away from configuration via env vars * The operator should not be coupled to functionality in the entrypoint script in the docker-library/rabbitmq Docker image, as this prevents other RabbitMQ images being used This effectively removes support for RabbitMQ images pre 3.8.4, where the ability to configure RabbitMQ via sysctl-style `*.conf` files in the conf.d directory.
1 parent 34d3c4c commit c39b820

File tree

6 files changed

+182
-137
lines changed

6 files changed

+182
-137
lines changed

controllers/rabbitmqcluster_controller_test.go

+3-7
Original file line numberDiff line numberDiff line change
@@ -1218,19 +1218,15 @@ var _ = Describe("RabbitmqclusterController", func() {
12181218
},
12191219
},
12201220
corev1.Volume{
1221-
Name: "rabbitmq-admin",
1221+
Name: "rabbitmq-confd",
12221222
VolumeSource: corev1.VolumeSource{
12231223
Secret: &corev1.SecretVolumeSource{
12241224
DefaultMode: &defaultMode,
12251225
SecretName: "rabbitmq-sts-override-rabbitmq-admin",
12261226
Items: []corev1.KeyToPath{
12271227
{
1228-
Key: "username",
1229-
Path: "username",
1230-
},
1231-
{
1232-
Key: "password",
1233-
Path: "password",
1228+
Key: "default_user.conf",
1229+
Path: "default_user.conf",
12341230
},
12351231
},
12361232
},

internal/resource/admin_secret.go

+39-4
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,12 @@
1010
package resource
1111

1212
import (
13+
"bytes"
14+
"encoding/base64"
15+
1316
rabbitmqv1beta1 "github.com/rabbitmq/cluster-operator/api/v1beta1"
1417
"github.com/rabbitmq/cluster-operator/internal/metadata"
18+
"gopkg.in/ini.v1"
1519
corev1 "k8s.io/api/core/v1"
1620
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
1721
"k8s.io/apimachinery/pkg/runtime"
@@ -31,6 +35,31 @@ func (builder *RabbitmqResourceBuilder) AdminSecret() *AdminSecretBuilder {
3135
}
3236
}
3337

38+
func generateDefaultUserConf(username, password []byte) ([]byte, error) {
39+
40+
ini.PrettySection = false // Remove trailing new line because default_user.conf has only a default section.
41+
cfg, err := ini.Load([]byte{})
42+
if err != nil {
43+
return nil, err
44+
}
45+
defaultSection := cfg.Section("")
46+
47+
if _, err := defaultSection.NewKey("default_user", string(username)); err != nil {
48+
return nil, err
49+
}
50+
51+
if _, err := defaultSection.NewKey("default_pass", string(password)); err != nil {
52+
return nil, err
53+
}
54+
55+
var userConfBuffer bytes.Buffer
56+
if cfg.WriteTo(&userConfBuffer); err != nil {
57+
return nil, err
58+
}
59+
60+
return userConfBuffer.Bytes(), nil
61+
}
62+
3463
func (builder *AdminSecretBuilder) UpdateRequiresStsRestart() bool {
3564
return false
3665
}
@@ -43,12 +72,17 @@ func (builder *AdminSecretBuilder) Update(object runtime.Object) error {
4372
}
4473

4574
func (builder *AdminSecretBuilder) Build() (runtime.Object, error) {
46-
username, err := randomEncodedString(24)
75+
username, err := randomBytes(24)
76+
if err != nil {
77+
return nil, err
78+
}
79+
80+
password, err := randomBytes(24)
4781
if err != nil {
4882
return nil, err
4983
}
5084

51-
password, err := randomEncodedString(24)
85+
defaultUserConf, err := generateDefaultUserConf(username, password)
5286
if err != nil {
5387
return nil, err
5488
}
@@ -60,8 +94,9 @@ func (builder *AdminSecretBuilder) Build() (runtime.Object, error) {
6094
},
6195
Type: corev1.SecretTypeOpaque,
6296
Data: map[string][]byte{
63-
"username": []byte(username),
64-
"password": []byte(password),
97+
"username": []byte(base64.URLEncoding.EncodeToString(username)),
98+
"password": []byte(base64.URLEncoding.EncodeToString(password)),
99+
"default_user.conf": []byte(base64.URLEncoding.EncodeToString(defaultUserConf)),
65100
},
66101
}, nil
67102
}

internal/resource/admin_secret_test.go

+70-48
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ package resource_test
1212
import (
1313
b64 "encoding/base64"
1414

15+
"gopkg.in/ini.v1"
1516
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
1617
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
1718

@@ -44,41 +45,62 @@ var _ = Describe("AdminSecret", func() {
4445
})
4546

4647
Context("Build with defaults", func() {
47-
BeforeEach(func() {
48+
It("creates the necessary admin secret", func() {
49+
var decodedUsername []byte
50+
var decodedPassword []byte
51+
var err error
52+
4853
obj, err := adminSecretBuilder.Build()
4954
Expect(err).NotTo(HaveOccurred())
5055
secret = obj.(*corev1.Secret)
51-
})
5256

53-
It("creates the secret with correct name and namespace", func() {
54-
Expect(secret.Name).To(Equal(instance.ChildResourceName("admin")))
55-
Expect(secret.Namespace).To(Equal("a namespace"))
56-
})
57+
By("creating the secret with correct name and namespace", func() {
58+
Expect(secret.Name).To(Equal(instance.ChildResourceName("admin")))
59+
Expect(secret.Namespace).To(Equal("a namespace"))
60+
})
5761

58-
It("creates a 'opaque' secret ", func() {
59-
Expect(secret.Type).To(Equal(corev1.SecretTypeOpaque))
60-
})
62+
By("creating a 'opaque' secret ", func() {
63+
Expect(secret.Type).To(Equal(corev1.SecretTypeOpaque))
64+
})
6165

62-
It("creates a rabbitmq username that is base64 encoded and 24 characters in length", func() {
63-
username, ok := secret.Data["username"]
64-
Expect(ok).NotTo(BeFalse())
65-
decodedUsername, err := b64.URLEncoding.DecodeString(string(username))
66-
Expect(err).NotTo(HaveOccurred())
67-
Expect(len(decodedUsername)).To(Equal(24))
66+
By("creating a rabbitmq username that is base64 encoded and 24 characters in length", func() {
67+
username, ok := secret.Data["username"]
68+
Expect(ok).NotTo(BeFalse())
69+
decodedUsername, err = b64.URLEncoding.DecodeString(string(username))
70+
Expect(err).NotTo(HaveOccurred())
71+
Expect(len(decodedUsername)).To(Equal(24))
6872

69-
})
73+
})
7074

71-
It("creates a rabbitmq password that is base64 encoded and 24 characters in length", func() {
72-
password, ok := secret.Data["password"]
73-
Expect(ok).NotTo(BeFalse())
74-
decodedPassword, err := b64.URLEncoding.DecodeString(string(password))
75-
Expect(err).NotTo(HaveOccurred())
76-
Expect(len(decodedPassword)).To(Equal(24))
75+
By("creating a rabbitmq password that is base64 encoded and 24 characters in length", func() {
76+
password, ok := secret.Data["password"]
77+
Expect(ok).NotTo(BeFalse())
78+
decodedPassword, err = b64.URLEncoding.DecodeString(string(password))
79+
Expect(err).NotTo(HaveOccurred())
80+
Expect(len(decodedPassword)).To(Equal(24))
81+
})
82+
83+
By("creating a default_user.conf file that contains the correct sysctl config format to be parsed by RabbitMQ", func() {
84+
defaultUserConf, ok := secret.Data["default_user.conf"]
85+
Expect(ok).NotTo(BeFalse())
86+
87+
decodedDefaultUserConf, err := b64.URLEncoding.DecodeString(string(defaultUserConf))
88+
Expect(err).NotTo(HaveOccurred())
89+
90+
cfg, err := ini.Load(decodedDefaultUserConf)
91+
Expect(err).NotTo(HaveOccurred())
92+
93+
Expect(cfg.Section("").HasKey("default_user")).To(BeTrue())
94+
Expect(cfg.Section("").HasKey("default_pass")).To(BeTrue())
95+
96+
Expect(cfg.Section("").Key("default_user").Value()).To(Equal(string(decodedUsername)))
97+
Expect(cfg.Section("").Key("default_pass").Value()).To(Equal(string(decodedPassword)))
98+
})
7799
})
78100
})
79101

80102
Context("Update with instance labels", func() {
81-
BeforeEach(func() {
103+
It("Updates the secret", func() {
82104
instance = rabbitmqv1beta1.RabbitmqCluster{
83105
ObjectMeta: metav1.ObjectMeta{
84106
Name: "rabbit-labelled",
@@ -102,26 +124,26 @@ var _ = Describe("AdminSecret", func() {
102124
}
103125
err := adminSecretBuilder.Update(secret)
104126
Expect(err).NotTo(HaveOccurred())
105-
})
106127

107-
It("adds new labels from the CR", func() {
108-
testLabels(secret.Labels)
109-
})
128+
By("adding new labels from the CR", func() {
129+
testLabels(secret.Labels)
130+
})
110131

111-
It("restores the default labels", func() {
112-
labels := secret.Labels
113-
Expect(labels["app.kubernetes.io/name"]).To(Equal(instance.Name))
114-
Expect(labels["app.kubernetes.io/component"]).To(Equal("rabbitmq"))
115-
Expect(labels["app.kubernetes.io/part-of"]).To(Equal("rabbitmq"))
116-
})
132+
By("restoring the default labels", func() {
133+
labels := secret.Labels
134+
Expect(labels["app.kubernetes.io/name"]).To(Equal(instance.Name))
135+
Expect(labels["app.kubernetes.io/component"]).To(Equal("rabbitmq"))
136+
Expect(labels["app.kubernetes.io/part-of"]).To(Equal("rabbitmq"))
137+
})
117138

118-
It("deletes the labels that are removed from the CR", func() {
119-
Expect(secret.Labels).NotTo(HaveKey("this-was-the-previous-label"))
139+
By("deleting the labels that are removed from the CR", func() {
140+
Expect(secret.Labels).NotTo(HaveKey("this-was-the-previous-label"))
141+
})
120142
})
121143
})
122144

123145
Context("Update with instance annotations", func() {
124-
BeforeEach(func() {
146+
It("updates the secret with the annotations", func() {
125147
instance = rabbitmqv1beta1.RabbitmqCluster{
126148
ObjectMeta: metav1.ObjectMeta{
127149
Name: "rabbit-labelled",
@@ -150,19 +172,19 @@ var _ = Describe("AdminSecret", func() {
150172
}
151173
err := adminSecretBuilder.Update(secret)
152174
Expect(err).NotTo(HaveOccurred())
153-
})
154-
155-
It("updates secret annotations on admin secret", func() {
156-
expectedAnnotations := map[string]string{
157-
"my-annotation": "i-like-this",
158-
"i-was-here-already": "please-dont-delete-me",
159-
"im-here-to-stay.kubernetes.io": "for-a-while",
160-
"kubernetes.io/name": "should-stay",
161-
"kubectl.kubernetes.io/name": "should-stay",
162-
"k8s.io/name": "should-stay",
163-
}
164175

165-
Expect(secret.Annotations).To(Equal(expectedAnnotations))
176+
By("updating secret annotations on admin secret", func() {
177+
expectedAnnotations := map[string]string{
178+
"my-annotation": "i-like-this",
179+
"i-was-here-already": "please-dont-delete-me",
180+
"im-here-to-stay.kubernetes.io": "for-a-while",
181+
"kubernetes.io/name": "should-stay",
182+
"kubectl.kubernetes.io/name": "should-stay",
183+
"k8s.io/name": "should-stay",
184+
}
185+
186+
Expect(secret.Annotations).To(Equal(expectedAnnotations))
187+
})
166188
})
167189
})
168190
})

internal/resource/erlang_cookie.go

+10-2
Original file line numberDiff line numberDiff line change
@@ -63,10 +63,18 @@ func (builder *ErlangCookieBuilder) Update(object runtime.Object) error {
6363
return nil
6464
}
6565

66-
func randomEncodedString(dataLen int) (string, error) {
66+
func randomBytes(dataLen int) ([]byte, error) {
6767
randomBytes := make([]byte, dataLen)
6868
if _, err := rand.Read(randomBytes); err != nil {
69+
return nil, err
70+
}
71+
return randomBytes, nil
72+
}
73+
74+
func randomEncodedString(dataLen int) (string, error) {
75+
generatedBytes, err := randomBytes(dataLen)
76+
if err != nil {
6977
return "", err
7078
}
71-
return base64.URLEncoding.EncodeToString(randomBytes), nil
79+
return base64.URLEncoding.EncodeToString(generatedBytes), nil
7280
}

internal/resource/statefulset.go

+28-30
Original file line numberDiff line numberDiff line change
@@ -258,24 +258,6 @@ func (builder *StatefulSetBuilder) podTemplateSpec(annotations, labels map[strin
258258
terminationGracePeriod := defaultGracePeriodTimeoutSeconds
259259

260260
volumes := []corev1.Volume{
261-
{
262-
Name: "rabbitmq-admin",
263-
VolumeSource: corev1.VolumeSource{
264-
Secret: &corev1.SecretVolumeSource{
265-
SecretName: builder.Instance.ChildResourceName(AdminSecretName),
266-
Items: []corev1.KeyToPath{
267-
{
268-
Key: "username",
269-
Path: "username",
270-
},
271-
{
272-
Key: "password",
273-
Path: "password",
274-
},
275-
},
276-
},
277-
},
278-
},
279261
{
280262
Name: "server-conf",
281263
VolumeSource: corev1.VolumeSource{
@@ -302,6 +284,20 @@ func (builder *StatefulSetBuilder) podTemplateSpec(annotations, labels map[strin
302284
EmptyDir: &corev1.EmptyDirVolumeSource{},
303285
},
304286
},
287+
{
288+
Name: "rabbitmq-confd",
289+
VolumeSource: corev1.VolumeSource{
290+
Secret: &corev1.SecretVolumeSource{
291+
SecretName: builder.Instance.ChildResourceName(AdminSecretName),
292+
Items: []corev1.KeyToPath{
293+
{
294+
Key: "default_user.conf",
295+
Path: "default_user.conf",
296+
},
297+
},
298+
},
299+
},
300+
},
305301
{
306302
Name: "rabbitmq-erlang-cookie",
307303
VolumeSource: corev1.VolumeSource{
@@ -378,10 +374,6 @@ func (builder *StatefulSetBuilder) podTemplateSpec(annotations, labels map[strin
378374
}
379375

380376
rabbitmqContainerVolumeMounts := []corev1.VolumeMount{
381-
{
382-
Name: "rabbitmq-admin",
383-
MountPath: "/opt/rabbitmq-secret/",
384-
},
385377
{
386378
Name: "persistence",
387379
MountPath: "/var/lib/rabbitmq/mnesia/",
@@ -390,6 +382,10 @@ func (builder *StatefulSetBuilder) podTemplateSpec(annotations, labels map[strin
390382
Name: "rabbitmq-etc",
391383
MountPath: "/etc/rabbitmq/",
392384
},
385+
{
386+
Name: "rabbitmq-confd",
387+
MountPath: "/etc/rabbitmq/conf.d/",
388+
},
393389
{
394390
Name: "rabbitmq-erlang-cookie",
395391
MountPath: "/var/lib/rabbitmq/",
@@ -509,6 +505,8 @@ func (builder *StatefulSetBuilder) podTemplateSpec(annotations, labels map[strin
509505
"&& chown 999:999 /etc/rabbitmq/advanced.config ; " +
510506
"cp /tmp/rabbitmq/rabbitmq-env.conf /etc/rabbitmq/rabbitmq-env.conf " +
511507
"&& chown 999:999 /etc/rabbitmq/rabbitmq-env.conf ; " +
508+
"cp /tmp/rabbitmq-admin/default_user.conf /etc/rabbitmq/conf.d/default_user.conf " +
509+
"&& chown 999:999 /etc/rabbitmq/conf.d/*.conf ; " +
512510
"cp /tmp/erlang-cookie-secret/.erlang.cookie /var/lib/rabbitmq/.erlang.cookie " +
513511
"&& chown 999:999 /var/lib/rabbitmq/.erlang.cookie " +
514512
"&& chmod 600 /var/lib/rabbitmq/.erlang.cookie ; " +
@@ -535,10 +533,18 @@ func (builder *StatefulSetBuilder) podTemplateSpec(annotations, labels map[strin
535533
Name: "plugins-conf",
536534
MountPath: "/tmp/rabbitmq-plugins/",
537535
},
536+
{
537+
Name: "rabbitmq-admin",
538+
MountPath: "/tmp/rabbitmq-admin/",
539+
},
538540
{
539541
Name: "rabbitmq-etc",
540542
MountPath: "/etc/rabbitmq/",
541543
},
544+
{
545+
Name: "rabbitmq-confd",
546+
MountPath: "/etc/rabbitmq/conf.d/",
547+
},
542548
{
543549
Name: "rabbitmq-erlang-cookie",
544550
MountPath: "/var/lib/rabbitmq/",
@@ -561,14 +567,6 @@ func (builder *StatefulSetBuilder) podTemplateSpec(annotations, labels map[strin
561567
Resources: *builder.Instance.Spec.Resources,
562568
Image: builder.Instance.Spec.Image,
563569
Env: []corev1.EnvVar{
564-
{
565-
Name: "RABBITMQ_DEFAULT_PASS_FILE",
566-
Value: "/opt/rabbitmq-secret/password",
567-
},
568-
{
569-
Name: "RABBITMQ_DEFAULT_USER_FILE",
570-
Value: "/opt/rabbitmq-secret/username",
571-
},
572570
{
573571
Name: "MY_POD_NAME",
574572
ValueFrom: &corev1.EnvVarSource{

0 commit comments

Comments
 (0)