Skip to content

Configure default user/password through conf.d #346

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Sep 22, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ Manage [RabbitMQ](https://www.rabbitmq.com/) clusters deployed to [Kubernetes](h

## Supported Versions

The operator deploys RabbitMQ `3.8.8`, and requires a Kubernetes cluster of `1.16` or above.
The operator deploys RabbitMQ `3.8.8` by default, and supports versions from `3.8.4` upwards. The operator requires a Kubernetes cluster of `1.16` or above.

## Versioning

Expand Down
26 changes: 15 additions & 11 deletions controllers/rabbitmqcluster_controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1218,21 +1218,25 @@ var _ = Describe("RabbitmqclusterController", func() {
},
},
corev1.Volume{
Name: "rabbitmq-admin",
Name: "rabbitmq-confd",
VolumeSource: corev1.VolumeSource{
Secret: &corev1.SecretVolumeSource{
DefaultMode: &defaultMode,
SecretName: "rabbitmq-sts-override-rabbitmq-admin",
Items: []corev1.KeyToPath{
Projected: &corev1.ProjectedVolumeSource{
Sources: []corev1.VolumeProjection{
{
Key: "username",
Path: "username",
},
{
Key: "password",
Path: "password",
Secret: &corev1.SecretProjection{
LocalObjectReference: corev1.LocalObjectReference{
Name: "rabbitmq-sts-override-rabbitmq-admin",
},
Items: []corev1.KeyToPath{
{
Key: "default_user.conf",
Path: "default_user.conf",
},
},
},
},
},
DefaultMode: &defaultMode,
},
},
},
Expand Down
38 changes: 36 additions & 2 deletions internal/resource/admin_secret.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,11 @@
package resource

import (
"bytes"

rabbitmqv1beta1 "github.com/rabbitmq/cluster-operator/api/v1beta1"
"github.com/rabbitmq/cluster-operator/internal/metadata"
"gopkg.in/ini.v1"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
Expand All @@ -31,6 +34,31 @@ func (builder *RabbitmqResourceBuilder) AdminSecret() *AdminSecretBuilder {
}
}

func generateDefaultUserConf(username, password string) ([]byte, error) {

ini.PrettySection = false // Remove trailing new line because default_user.conf has only a default section.
cfg, err := ini.Load([]byte{})
if err != nil {
return nil, err
}
defaultSection := cfg.Section("")

if _, err := defaultSection.NewKey("default_user", username); err != nil {
return nil, err
}

if _, err := defaultSection.NewKey("default_pass", password); err != nil {
return nil, err
}

var userConfBuffer bytes.Buffer
if cfg.WriteTo(&userConfBuffer); err != nil {
return nil, err
}

return userConfBuffer.Bytes(), nil
}

func (builder *AdminSecretBuilder) UpdateRequiresStsRestart() bool {
return false
}
Expand All @@ -53,15 +81,21 @@ func (builder *AdminSecretBuilder) Build() (runtime.Object, error) {
return nil, err
}

defaultUserConf, err := generateDefaultUserConf(username, password)
if err != nil {
return nil, err
}

return &corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: builder.Instance.ChildResourceName(AdminSecretName),
Namespace: builder.Instance.Namespace,
},
Type: corev1.SecretTypeOpaque,
Data: map[string][]byte{
"username": []byte(username),
"password": []byte(password),
"username": []byte(username),
"password": []byte(password),
"default_user.conf": defaultUserConf,
},
}, nil
}
122 changes: 70 additions & 52 deletions internal/resource/admin_secret_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ package resource_test
import (
b64 "encoding/base64"

"gopkg.in/ini.v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"

Expand Down Expand Up @@ -44,41 +45,58 @@ var _ = Describe("AdminSecret", func() {
})

Context("Build with defaults", func() {
BeforeEach(func() {
It("creates the necessary admin secret", func() {
var username []byte
var password []byte
var ok bool

obj, err := adminSecretBuilder.Build()
Expect(err).NotTo(HaveOccurred())
secret = obj.(*corev1.Secret)
})

It("creates the secret with correct name and namespace", func() {
Expect(secret.Name).To(Equal(instance.ChildResourceName("admin")))
Expect(secret.Namespace).To(Equal("a namespace"))
})

It("creates a 'opaque' secret ", func() {
Expect(secret.Type).To(Equal(corev1.SecretTypeOpaque))
})

It("creates a rabbitmq username that is base64 encoded and 24 characters in length", func() {
username, ok := secret.Data["username"]
Expect(ok).NotTo(BeFalse())
decodedUsername, err := b64.URLEncoding.DecodeString(string(username))
Expect(err).NotTo(HaveOccurred())
Expect(len(decodedUsername)).To(Equal(24))

})

It("creates a rabbitmq password that is base64 encoded and 24 characters in length", func() {
password, ok := secret.Data["password"]
Expect(ok).NotTo(BeFalse())
decodedPassword, err := b64.URLEncoding.DecodeString(string(password))
Expect(err).NotTo(HaveOccurred())
Expect(len(decodedPassword)).To(Equal(24))
By("creating the secret with correct name and namespace", func() {
Expect(secret.Name).To(Equal(instance.ChildResourceName("admin")))
Expect(secret.Namespace).To(Equal("a namespace"))
})

By("creating a 'opaque' secret ", func() {
Expect(secret.Type).To(Equal(corev1.SecretTypeOpaque))
})

By("creating a rabbitmq username that is base64 encoded and 24 characters in length", func() {
username, ok = secret.Data["username"]
Expect(ok).NotTo(BeFalse(), "Failed to find a key \"username\" in the generated Secret")
decodedUsername, err := b64.URLEncoding.DecodeString(string(username))
Expect(err).NotTo(HaveOccurred())
Expect(len(decodedUsername)).To(Equal(24))
})

By("creating a rabbitmq password that is base64 encoded and 24 characters in length", func() {
password, ok = secret.Data["password"]
Expect(ok).NotTo(BeFalse(), "Failed to find a key \"password\" in the generated Secret")
decodedPassword, err := b64.URLEncoding.DecodeString(string(password))
Expect(err).NotTo(HaveOccurred())
Expect(len(decodedPassword)).To(Equal(24))
})

By("creating a default_user.conf file that contains the correct sysctl config format to be parsed by RabbitMQ", func() {
defaultUserConf, ok := secret.Data["default_user.conf"]
Expect(ok).NotTo(BeFalse(), "Failed to find a key \"default_user.conf\" in the generated Secret")

cfg, err := ini.Load(defaultUserConf)
Expect(err).NotTo(HaveOccurred())

Expect(cfg.Section("").HasKey("default_user")).To(BeTrue())
Expect(cfg.Section("").HasKey("default_pass")).To(BeTrue())

Expect(cfg.Section("").Key("default_user").Value()).To(Equal(string(username)))
Expect(cfg.Section("").Key("default_pass").Value()).To(Equal(string(password)))
})
})
})

Context("Update with instance labels", func() {
BeforeEach(func() {
It("Updates the secret", func() {
instance = rabbitmqv1beta1.RabbitmqCluster{
ObjectMeta: metav1.ObjectMeta{
Name: "rabbit-labelled",
Expand All @@ -102,26 +120,26 @@ var _ = Describe("AdminSecret", func() {
}
err := adminSecretBuilder.Update(secret)
Expect(err).NotTo(HaveOccurred())
})

It("adds new labels from the CR", func() {
testLabels(secret.Labels)
})
By("adding new labels from the CR", func() {
testLabels(secret.Labels)
})

It("restores the default labels", func() {
labels := secret.Labels
Expect(labels["app.kubernetes.io/name"]).To(Equal(instance.Name))
Expect(labels["app.kubernetes.io/component"]).To(Equal("rabbitmq"))
Expect(labels["app.kubernetes.io/part-of"]).To(Equal("rabbitmq"))
})
By("restoring the default labels", func() {
labels := secret.Labels
Expect(labels["app.kubernetes.io/name"]).To(Equal(instance.Name))
Expect(labels["app.kubernetes.io/component"]).To(Equal("rabbitmq"))
Expect(labels["app.kubernetes.io/part-of"]).To(Equal("rabbitmq"))
})

It("deletes the labels that are removed from the CR", func() {
Expect(secret.Labels).NotTo(HaveKey("this-was-the-previous-label"))
By("deleting the labels that are removed from the CR", func() {
Expect(secret.Labels).NotTo(HaveKey("this-was-the-previous-label"))
})
})
})

Context("Update with instance annotations", func() {
BeforeEach(func() {
It("updates the secret with the annotations", func() {
instance = rabbitmqv1beta1.RabbitmqCluster{
ObjectMeta: metav1.ObjectMeta{
Name: "rabbit-labelled",
Expand Down Expand Up @@ -150,19 +168,19 @@ var _ = Describe("AdminSecret", func() {
}
err := adminSecretBuilder.Update(secret)
Expect(err).NotTo(HaveOccurred())
})

It("updates secret annotations on admin secret", func() {
expectedAnnotations := map[string]string{
"my-annotation": "i-like-this",
"i-was-here-already": "please-dont-delete-me",
"im-here-to-stay.kubernetes.io": "for-a-while",
"kubernetes.io/name": "should-stay",
"kubectl.kubernetes.io/name": "should-stay",
"k8s.io/name": "should-stay",
}

Expect(secret.Annotations).To(Equal(expectedAnnotations))
By("updating secret annotations on admin secret", func() {
expectedAnnotations := map[string]string{
"my-annotation": "i-like-this",
"i-was-here-already": "please-dont-delete-me",
"im-here-to-stay.kubernetes.io": "for-a-while",
"kubernetes.io/name": "should-stay",
"kubectl.kubernetes.io/name": "should-stay",
"k8s.io/name": "should-stay",
}

Expect(secret.Annotations).To(Equal(expectedAnnotations))
})
})
})
})
56 changes: 26 additions & 30 deletions internal/resource/statefulset.go
Original file line number Diff line number Diff line change
Expand Up @@ -258,24 +258,6 @@ func (builder *StatefulSetBuilder) podTemplateSpec(annotations, labels map[strin
terminationGracePeriod := defaultGracePeriodTimeoutSeconds

volumes := []corev1.Volume{
{
Name: "rabbitmq-admin",
VolumeSource: corev1.VolumeSource{
Secret: &corev1.SecretVolumeSource{
SecretName: builder.Instance.ChildResourceName(AdminSecretName),
Items: []corev1.KeyToPath{
{
Key: "username",
Path: "username",
},
{
Key: "password",
Path: "password",
},
},
},
},
},
{
Name: "server-conf",
VolumeSource: corev1.VolumeSource{
Expand All @@ -302,6 +284,28 @@ func (builder *StatefulSetBuilder) podTemplateSpec(annotations, labels map[strin
EmptyDir: &corev1.EmptyDirVolumeSource{},
},
},
{
Name: "rabbitmq-confd",
VolumeSource: corev1.VolumeSource{
Projected: &corev1.ProjectedVolumeSource{
Sources: []corev1.VolumeProjection{
{
Secret: &corev1.SecretProjection{
LocalObjectReference: corev1.LocalObjectReference{
Name: builder.Instance.ChildResourceName(AdminSecretName),
},
Items: []corev1.KeyToPath{
{
Key: "default_user.conf",
Path: "default_user.conf",
},
},
},
},
},
},
},
},
{
Name: "rabbitmq-erlang-cookie",
VolumeSource: corev1.VolumeSource{
Expand Down Expand Up @@ -378,10 +382,6 @@ func (builder *StatefulSetBuilder) podTemplateSpec(annotations, labels map[strin
}

rabbitmqContainerVolumeMounts := []corev1.VolumeMount{
{
Name: "rabbitmq-admin",
MountPath: "/opt/rabbitmq-secret/",
},
{
Name: "persistence",
MountPath: "/var/lib/rabbitmq/mnesia/",
Expand All @@ -390,6 +390,10 @@ func (builder *StatefulSetBuilder) podTemplateSpec(annotations, labels map[strin
Name: "rabbitmq-etc",
MountPath: "/etc/rabbitmq/",
},
{
Name: "rabbitmq-confd",
MountPath: "/etc/rabbitmq/conf.d/",
},
{
Name: "rabbitmq-erlang-cookie",
MountPath: "/var/lib/rabbitmq/",
Expand Down Expand Up @@ -561,14 +565,6 @@ func (builder *StatefulSetBuilder) podTemplateSpec(annotations, labels map[strin
Resources: *builder.Instance.Spec.Resources,
Image: builder.Instance.Spec.Image,
Env: []corev1.EnvVar{
{
Name: "RABBITMQ_DEFAULT_PASS_FILE",
Value: "/opt/rabbitmq-secret/password",
},
{
Name: "RABBITMQ_DEFAULT_USER_FILE",
Value: "/opt/rabbitmq-secret/username",
},
{
Name: "MY_POD_NAME",
ValueFrom: &corev1.EnvVarSource{
Expand Down
Loading