Skip to content
This repository was archived by the owner on May 28, 2021. It is now read-only.

Commit d42c413

Browse files
simonlordprydie
authored andcommitted
Group replication SSL (#115)
- SSL with autogenerated certs as standard (a feature of mysql, certs valid for 10 years) - Confirgurable with your own CA cert, tls cert and tls key via a tls secret
1 parent cb5811f commit d42c413

File tree

8 files changed

+126
-3
lines changed

8 files changed

+126
-3
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ dist/
33
bin/
44
.push-wcr*
55
.container-wcr*
6+
.push-iad*
7+
.container-iad*
68

79
test/system/runner.log
810
test/system/terraform/cluster/.terraform/
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
apiVersion: "mysql.oracle.com/v1"
2+
kind: MySQLCluster
3+
metadata:
4+
name: mysql
5+
spec:
6+
replicas: 3
7+
sslSecretRef:
8+
name: mysql-ssl-secret

examples/custom-ssl-secret.yaml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
apiVersion: v1
2+
kind: Secret
3+
metadata:
4+
name: mysql-ssl-secret
5+
type: Opaque
6+
data:
7+
ca.crt: <base64'd Root CA certificate>
8+
tls.crt: <base64'd server certificate>
9+
tls.key: <base64'd server private key>

pkg/apis/mysql/v1/cluster.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,11 @@ type MySQLClusterSpec struct {
9595
// ConfigRef allows a user to specify a custom configuration file for MySQL.
9696
// +optional
9797
ConfigRef *corev1.LocalObjectReference `json:"configRef,omitempty"`
98+
99+
// SSLSecretRef allows a user to specify custom CA certificate, server certificate
100+
// and server key for group replication SSL
101+
// +optional
102+
SSLSecretRef *corev1.LocalObjectReference `json:"sslSecretRef,omitempty"`
98103
}
99104

100105
// MySQLClusterPhase describes the state of the cluster.
@@ -203,6 +208,13 @@ func (c *MySQLCluster) RequiresSecret() bool {
203208
return c.Spec.SecretRef == nil
204209
}
205210

211+
// RequiresCustomSSLSetup returns true is the user has provided a secret
212+
// that contains CA cert, server cert and server key for group replication
213+
// SSL support
214+
func (c *MySQLCluster) RequiresCustomSSLSetup() bool {
215+
return c.Spec.SSLSecretRef != nil
216+
}
217+
206218
// GetObjectKind is required for codegen
207219
func (c *MySQLCluster) GetObjectKind() schema.ObjectKind {
208220
return &c.TypeMeta

pkg/apis/mysql/v1/cluster_test.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ package v1
1717
import (
1818
"testing"
1919

20+
"github.com/stretchr/testify/assert"
2021
corev1 "k8s.io/api/core/v1"
2122
"k8s.io/apimachinery/pkg/util/validation/field"
2223
)
@@ -84,3 +85,20 @@ func TestRequiresConfigMount(t *testing.T) {
8485
t.Errorf("Cluster with configRef should require a config mount")
8586
}
8687
}
88+
89+
func TestRequiresCustomSSLSetup(t *testing.T) {
90+
cluster := &MySQLCluster{}
91+
cluster.EnsureDefaults()
92+
93+
assert.False(t, cluster.RequiresCustomSSLSetup(), "Cluster without SSLSecretRef should not require custom SSL setup")
94+
95+
cluster = &MySQLCluster{
96+
Spec: MySQLClusterSpec{
97+
SSLSecretRef: &corev1.LocalObjectReference{
98+
Name: "custom-ssl-secret",
99+
},
100+
},
101+
}
102+
103+
assert.True(t, cluster.RequiresCustomSSLSetup(), "Cluster with SSLSecretRef should require custom SSL setup")
104+
}

pkg/controllers/cluster/manager/cluster_manager.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -258,7 +258,7 @@ func (m *ClusterManager) handleInstanceMissing(ctx context.Context, primaryAddr
258258
glog.V(4).Infof("Attempting to rejoin instance to cluster")
259259
if err := primarySh.RejoinInstanceToCluster(ctx, m.Instance.GetShellURI(), mysqlsh.Options{
260260
"ipWhitelist": whitelistCIDR,
261-
"memberSslMode": "DISABLED",
261+
"memberSslMode": "REQUIRED",
262262
}); err != nil {
263263
glog.Errorf("Failed to rejoin cluster: %v", err)
264264
return false
@@ -288,7 +288,7 @@ func (m *ClusterManager) handleInstanceNotFound(ctx context.Context, primaryAddr
288288
}
289289

290290
if err := psh.AddInstanceToCluster(ctx, m.Instance.GetShellURI(), mysqlsh.Options{
291-
"memberSslMode": "DISABLED",
291+
"memberSslMode": "REQUIRED",
292292
"ipWhitelist": whitelistCIDR,
293293
}); err != nil {
294294
glog.Errorf("Failed to add to cluster: %v", err)
@@ -324,7 +324,7 @@ func (m *ClusterManager) bootstrap(ctx context.Context) (*innodb.ClusterStatus,
324324
return nil, errors.Wrap(err, "getting CIDR to whitelist for GR")
325325
}
326326
opts := mysqlsh.Options{
327-
"memberSslMode": "DISABLED",
327+
"memberSslMode": "REQUIRED",
328328
"ipWhitelist": whitelistCIDR,
329329
}
330330
if m.Instance.MultiMaster {

pkg/resources/statefulsets/statefulset.go

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ const (
4444

4545
mySQLBackupVolumeName = "mysqlbackupvolume"
4646
mySQLVolumeName = "mysqlvolume"
47+
mySQLSSLVolumeName = "mysqlsslvolume"
4748

4849
replicationGroupPort = 13306
4950
)
@@ -82,6 +83,13 @@ func volumeMounts(cluster *api.MySQLCluster) []v1.VolumeMount {
8283
})
8384
}
8485

86+
if cluster.RequiresCustomSSLSetup() {
87+
mounts = append(mounts, v1.VolumeMount{
88+
Name: mySQLSSLVolumeName,
89+
MountPath: "/etc/ssl/mysql",
90+
})
91+
}
92+
8593
return mounts
8694
}
8795

@@ -177,6 +185,13 @@ func mysqlServerContainer(cluster *api.MySQLCluster, mysqlServerImage string, ro
177185
"--log-error-verbosity=3",
178186
}
179187

188+
if cluster.RequiresCustomSSLSetup() {
189+
args = append(args,
190+
"--ssl-ca=/etc/ssl/mysql/ca.crt",
191+
"--ssl-cert=/etc/ssl/mysql/tls.crt",
192+
"--ssl-key=/etc/ssl/mysql/tls.key")
193+
}
194+
180195
entryPointArgs := strings.Join(args, " ")
181196

182197
cmd := fmt.Sprintf(`
@@ -297,6 +312,39 @@ func NewForCluster(cluster *api.MySQLCluster, images operatoropts.Images, servic
297312
})
298313
}
299314

315+
if cluster.RequiresCustomSSLSetup() {
316+
podVolumes = append(podVolumes, v1.Volume{
317+
Name: mySQLSSLVolumeName,
318+
VolumeSource: v1.VolumeSource{
319+
Projected: &v1.ProjectedVolumeSource{
320+
Sources: []v1.VolumeProjection{
321+
v1.VolumeProjection{
322+
Secret: &v1.SecretProjection{
323+
LocalObjectReference: v1.LocalObjectReference{
324+
Name: cluster.Spec.SSLSecretRef.Name,
325+
},
326+
Items: []v1.KeyToPath{
327+
v1.KeyToPath{
328+
Key: "ca.crt",
329+
Path: "ca.crt",
330+
},
331+
v1.KeyToPath{
332+
Key: "tls.crt",
333+
Path: "tls.crt",
334+
},
335+
v1.KeyToPath{
336+
Key: "tls.key",
337+
Path: "tls.key",
338+
},
339+
},
340+
},
341+
},
342+
},
343+
},
344+
},
345+
})
346+
}
347+
300348
containers := []v1.Container{
301349
mysqlServerContainer(cluster, images.MySQLServerImage, rootPassword, serviceName, replicas, baseServerID),
302350
mysqlAgentContainer(cluster, images.MySQLAgentImage, rootPassword, serviceName, replicas)}

pkg/resources/statefulsets/statefulset_test.go

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import (
1818
"reflect"
1919
"testing"
2020

21+
"github.com/stretchr/testify/assert"
2122
"k8s.io/api/core/v1"
2223
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2324

@@ -139,3 +140,28 @@ func TestClusterCustomConfig(t *testing.T) {
139140
t.Errorf("Cluster is missing expected volume mount for custom config map")
140141
}
141142
}
143+
144+
func TestClusterCustomSSLSetup(t *testing.T) {
145+
cluster := &api.MySQLCluster{
146+
Spec: api.MySQLClusterSpec{
147+
SSLSecretRef: &v1.LocalObjectReference{
148+
Name: "my-ssl",
149+
},
150+
},
151+
}
152+
153+
statefulSet := NewForCluster(cluster, mockOperatorConfig().Images, "mycluster")
154+
containers := statefulSet.Spec.Template.Spec.Containers
155+
156+
var hasExpectedVolumeMount = false
157+
for _, container := range containers {
158+
for _, mount := range container.VolumeMounts {
159+
if mount.MountPath == "/etc/ssl/mysql" {
160+
hasExpectedVolumeMount = true
161+
break
162+
}
163+
}
164+
}
165+
166+
assert.True(t, hasExpectedVolumeMount, "Cluster is missing expected volume mount for custom SSL certs")
167+
}

0 commit comments

Comments
 (0)