Skip to content

Commit e16a926

Browse files
Alevskdvaldivia
andauthored
Add support for loading multiple TLS certificates (#304)
- update operator version to latest version - create tenant endpoint now supports multiple TLS certificates for MinIO TLS configuration - update certificates endpoint now support multiple TLS certificates Co-authored-by: Daniel Valdivia <hola@danielvaldivia.com>
1 parent 78f4978 commit e16a926

File tree

8 files changed

+141
-86
lines changed

8 files changed

+141
-86
lines changed

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ require (
1919
github.com/minio/mc v0.0.0-20201001165056-7f2df96e4821
2020
github.com/minio/minio v0.0.0-20200927172404-27d9bd04e544
2121
github.com/minio/minio-go/v7 v7.0.6-0.20200923173112-bc846cb9b089
22-
github.com/minio/operator v0.0.0-20200923155125-e7077234373b
22+
github.com/minio/operator v0.0.0-20200930213302-ab2bbdfae96c
2323
github.com/pquerna/cachecontrol v0.0.0-20180517163645-1555304b9b35 // indirect
2424
github.com/secure-io/sio-go v0.3.1
2525
github.com/stretchr/testify v1.6.1

go.sum

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -800,6 +800,8 @@ github.com/minio/minio-go/v7 v7.0.6-0.20200923173112-bc846cb9b089 h1:9DDs/Gc3fNH
800800
github.com/minio/minio-go/v7 v7.0.6-0.20200923173112-bc846cb9b089/go.mod h1:CSt2ETZNs+bIIhWTse0mcZKZWMGrFU7Er7RR0TmkDYk=
801801
github.com/minio/operator v0.0.0-20200923155125-e7077234373b h1:k9u35u4vUi9Lxl/CDYyLU/OVN5M1CG8HQsOC8XTJHxc=
802802
github.com/minio/operator v0.0.0-20200923155125-e7077234373b/go.mod h1:6lavbNo2YuJWeQR5bZYsEWdbpRCO2KrTyfQ0PtC/AN4=
803+
github.com/minio/operator v0.0.0-20200930213302-ab2bbdfae96c h1:OIKdzEJDFmUokbJ1rIdlr3kcfsBfXelYgSCTN/+Ppec=
804+
github.com/minio/operator v0.0.0-20200930213302-ab2bbdfae96c/go.mod h1:6lavbNo2YuJWeQR5bZYsEWdbpRCO2KrTyfQ0PtC/AN4=
803805
github.com/minio/selfupdate v0.3.1 h1:BWEFSNnrZVMUWXbXIgLDNDjbejkmpAmZvy/nCz1HlEs=
804806
github.com/minio/selfupdate v0.3.1/go.mod h1:b8ThJzzH7u2MkF6PcIra7KaXO9Khf6alWPvMSyTDCFM=
805807
github.com/minio/sha256-simd v0.1.1 h1:5QHSlgo3nt5yKOJrC7W8w7X+NFl8cMPZm96iu8kKUJU=

models/tls_configuration.go

Lines changed: 15 additions & 6 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

restapi/admin_tenants.go

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -576,14 +576,14 @@ func getTenantCreatedResponse(session *models.Principal, params admin_api.Create
576576
minInst.Spec.RequestAutoCert = *tenantReq.EnableTLS
577577
}
578578

579-
if !minInst.Spec.RequestAutoCert && tenantReq.TLS != nil && tenantReq.TLS.Minio != nil {
579+
if !minInst.Spec.RequestAutoCert && tenantReq.TLS != nil && len(tenantReq.TLS.Minio) > 0 {
580580
// User provided TLS certificates for MinIO
581581
isEncryptionEnabled = true
582582
// disable autoCert
583583
minInst.Spec.RequestAutoCert = false
584584
// Certificates used by the MinIO instance
585585
externalCertSecretName := fmt.Sprintf("%s-instance-external-certificates", secretName)
586-
externalCertSecret, err := createOrReplaceExternalCertSecret(ctx, &k8sClient, ns, tenantReq.TLS.Minio, externalCertSecretName, tenantName)
586+
externalCertSecret, err := createOrReplaceExternalCertSecrets(ctx, &k8sClient, ns, tenantReq.TLS.Minio, externalCertSecretName, tenantName)
587587
if err != nil {
588588
return nil, prepareError(err)
589589
}
@@ -599,10 +599,14 @@ func getTenantCreatedResponse(session *models.Principal, params admin_api.Create
599599
// KES client mTLSCertificates used by MinIO instance, only if autoCert is not enabled
600600
if !minInst.Spec.RequestAutoCert {
601601
tenantExternalClientCertSecretName := fmt.Sprintf("%s-tenant-external-client-cert", secretName)
602-
minInst.Spec.ExternalClientCertSecret, err = createOrReplaceExternalCertSecret(ctx, &k8sClient, ns, tenantReq.Encryption.Client, tenantExternalClientCertSecretName, tenantName)
602+
certificates := []*models.KeyPairConfiguration{tenantReq.Encryption.Client}
603+
certificateSecrets, err := createOrReplaceExternalCertSecrets(ctx, &k8sClient, ns, certificates, tenantExternalClientCertSecretName, tenantName)
603604
if err != nil {
604605
return nil, prepareError(errorGeneric)
605606
}
607+
if len(certificateSecrets) > 0 {
608+
minInst.Spec.ExternalClientCertSecret = certificateSecrets[0]
609+
}
606610
}
607611
// KES configuration for Tenant instance
608612
minInst.Spec.KES, err = getKESConfiguration(ctx, &k8sClient, ns, tenantReq.Encryption, secretName, tenantName, minInst.Spec.RequestAutoCert)
@@ -687,11 +691,14 @@ func getTenantCreatedResponse(session *models.Principal, params admin_api.Create
687691
if !minInst.Spec.RequestAutoCert && tenantReq.TLS != nil && tenantReq.TLS.Console != nil {
688692
// Certificates used by the console instance
689693
externalCertSecretName := fmt.Sprintf("%s-console-external-certificates", secretName)
690-
externalCertSecret, err := createOrReplaceExternalCertSecret(ctx, &k8sClient, ns, tenantReq.TLS.Console, externalCertSecretName, tenantName)
694+
certificates := []*models.KeyPairConfiguration{tenantReq.TLS.Console}
695+
externalCertSecret, err := createOrReplaceExternalCertSecrets(ctx, &k8sClient, ns, certificates, externalCertSecretName, tenantName)
691696
if err != nil {
692697
return nil, prepareError(errorGeneric)
693698
}
694-
minInst.Spec.Console.ExternalCertSecret = externalCertSecret
699+
if len(externalCertSecret) > 0 {
700+
minInst.Spec.Console.ExternalCertSecret = externalCertSecret[0]
701+
}
695702
}
696703

697704
// Set Labels, Annotations and Node Selector for Console

restapi/admin_tenants_helper.go

Lines changed: 59 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ func tenantUpdateCertificates(ctx context.Context, operatorClient OperatorClient
5151
if tenant.ExternalCert() && body.Minio != nil {
5252
minioCertSecretName := fmt.Sprintf("%s-instance-external-certificates", secretName)
5353
// update certificates
54-
if _, err := createOrReplaceExternalCertSecret(ctx, clientSet, namespace, body.Minio, minioCertSecretName, tenantName); err != nil {
54+
if _, err := createOrReplaceExternalCertSecrets(ctx, clientSet, namespace, body.Minio, minioCertSecretName, tenantName); err != nil {
5555
return err
5656
}
5757
// restart MinIO pods
@@ -66,7 +66,8 @@ func tenantUpdateCertificates(ctx context.Context, operatorClient OperatorClient
6666
if tenant.ConsoleExternalCert() && tenant.HasConsoleEnabled() && body.Console != nil {
6767
consoleCertSecretName := fmt.Sprintf("%s-console-external-certificates", secretName)
6868
// update certificates
69-
if _, err := createOrReplaceExternalCertSecret(ctx, clientSet, namespace, body.Console, consoleCertSecretName, tenantName); err != nil {
69+
certificates := []*models.KeyPairConfiguration{body.Console}
70+
if _, err := createOrReplaceExternalCertSecrets(ctx, clientSet, namespace, certificates, consoleCertSecretName, tenantName); err != nil {
7071
return err
7172
}
7273
// restart Console pods
@@ -119,15 +120,17 @@ func tenantUpdateEncryption(ctx context.Context, operatorClient OperatorClientI,
119120
if tenant.KESExternalCert() && body.Server != nil {
120121
kesExternalCertSecretName := fmt.Sprintf("%s-kes-external-cert", secretName)
121122
// update certificates
122-
if _, err := createOrReplaceExternalCertSecret(ctx, clientSet, namespace, body.Server, kesExternalCertSecretName, tenantName); err != nil {
123+
certificates := []*models.KeyPairConfiguration{body.Server}
124+
if _, err := createOrReplaceExternalCertSecrets(ctx, clientSet, namespace, certificates, kesExternalCertSecretName, tenantName); err != nil {
123125
return err
124126
}
125127
}
126128
// check if Tenant is deployed with external client certificates and user provided new client keypaiir
127129
if tenant.ExternalClientCert() && body.Client != nil {
128130
tenantExternalClientCertSecretName := fmt.Sprintf("%s-tenant-external-client-cert", secretName)
129131
// Update certificates
130-
if _, err := createOrReplaceExternalCertSecret(ctx, clientSet, namespace, body.Client, tenantExternalClientCertSecretName, tenantName); err != nil {
132+
certificates := []*models.KeyPairConfiguration{body.Client}
133+
if _, err := createOrReplaceExternalCertSecrets(ctx, clientSet, namespace, certificates, tenantExternalClientCertSecretName, tenantName); err != nil {
131134
return err
132135
}
133136
// Restart MinIO pods to mount the new client secrets
@@ -202,12 +205,15 @@ func getKESConfiguration(ctx context.Context, clientSet K8sClientI, ns string, e
202205
}
203206
// Generate server certificates for KES only if autoCert is disabled
204207
if !autoCert {
205-
kesExternalCertSecret, err := createOrReplaceExternalCertSecret(ctx, clientSet, ns, encryptionCfg.Server, kesExternalCertSecretName, tenantName)
208+
certificates := []*models.KeyPairConfiguration{encryptionCfg.Server}
209+
certificateSecrets, err := createOrReplaceExternalCertSecrets(ctx, clientSet, ns, certificates, kesExternalCertSecretName, tenantName)
206210
if err != nil {
207211
return nil, err
208212
}
209-
// External TLS certificates used by KES
210-
kesConfiguration.ExternalCertSecret = kesExternalCertSecret
213+
if len(certificateSecrets) > 0 {
214+
// External TLS certificates used by KES
215+
kesConfiguration.ExternalCertSecret = certificateSecrets[0]
216+
}
211217
}
212218
// Prepare kesConfiguration for KES
213219
serverConfigSecret, clientCertSecret, err := createOrReplaceKesConfigurationSecrets(ctx, clientSet, ns, encryptionCfg, kesConfigurationSecretName, kesClientCertSecretName, tenantName)
@@ -221,50 +227,54 @@ func getKESConfiguration(ctx context.Context, clientSet K8sClientI, ns string, e
221227
return kesConfiguration, nil
222228
}
223229

224-
// createOrReplaceExternalCertSecret receives a keypair, public and private key, encoded in base64, decode it and generate a new kubernetes secret
225-
// to be used by the operator for TLS encryption
226-
func createOrReplaceExternalCertSecret(ctx context.Context, clientSet K8sClientI, ns string, keyPair *models.KeyPairConfiguration, secretName, tenantName string) (*operator.LocalCertificateReference, error) {
227-
if keyPair == nil || keyPair.Crt == nil || keyPair.Key == nil || *keyPair.Crt == "" || *keyPair.Key == "" {
228-
return nil, errors.New("certificate files must not be empty")
229-
}
230-
// delete secret with same name if exists
231-
err := clientSet.deleteSecret(ctx, ns, secretName, metav1.DeleteOptions{})
232-
if err != nil {
233-
// log the error if any and continue
234-
log.Println(err)
235-
}
236-
imm := true
237-
tlsCrt, err := base64.StdEncoding.DecodeString(*keyPair.Crt)
238-
if err != nil {
239-
return nil, err
240-
}
241-
tlsKey, err := base64.StdEncoding.DecodeString(*keyPair.Key)
242-
if err != nil {
243-
return nil, err
244-
}
245-
externalTLSCertificateSecret := &corev1.Secret{
246-
ObjectMeta: metav1.ObjectMeta{
247-
Name: secretName,
248-
Labels: map[string]string{
249-
operator.TenantLabel: tenantName,
230+
// createOrReplaceExternalCertSecrets receives an array of KeyPairs (public and private key), encoded in base64, decode it and generate an equivalent number of kubernetes
231+
// secrets to be used by the operator for TLS encryption
232+
func createOrReplaceExternalCertSecrets(ctx context.Context, clientSet K8sClientI, ns string, keyPairs []*models.KeyPairConfiguration, secretName, tenantName string) ([]*operator.LocalCertificateReference, error) {
233+
var keyPairSecrets []*operator.LocalCertificateReference
234+
for _, keyPair := range keyPairs {
235+
if keyPair == nil || keyPair.Crt == nil || keyPair.Key == nil || *keyPair.Crt == "" || *keyPair.Key == "" {
236+
return nil, errors.New("certificate files must not be empty")
237+
}
238+
// delete secret with same name if exists
239+
err := clientSet.deleteSecret(ctx, ns, secretName, metav1.DeleteOptions{})
240+
if err != nil {
241+
// log the error if any and continue
242+
log.Println(err)
243+
}
244+
imm := true
245+
tlsCrt, err := base64.StdEncoding.DecodeString(*keyPair.Crt)
246+
if err != nil {
247+
return nil, err
248+
}
249+
tlsKey, err := base64.StdEncoding.DecodeString(*keyPair.Key)
250+
if err != nil {
251+
return nil, err
252+
}
253+
externalTLSCertificateSecret := &corev1.Secret{
254+
ObjectMeta: metav1.ObjectMeta{
255+
Name: secretName,
256+
Labels: map[string]string{
257+
operator.TenantLabel: tenantName,
258+
},
250259
},
251-
},
252-
Type: corev1.SecretTypeTLS,
253-
Immutable: &imm,
254-
Data: map[string][]byte{
255-
"tls.crt": tlsCrt,
256-
"tls.key": tlsKey,
257-
},
258-
}
259-
_, err = clientSet.createSecret(ctx, ns, externalTLSCertificateSecret, metav1.CreateOptions{})
260-
if err != nil {
261-
return nil, err
260+
Type: corev1.SecretTypeTLS,
261+
Immutable: &imm,
262+
Data: map[string][]byte{
263+
"tls.crt": tlsCrt,
264+
"tls.key": tlsKey,
265+
},
266+
}
267+
_, err = clientSet.createSecret(ctx, ns, externalTLSCertificateSecret, metav1.CreateOptions{})
268+
if err != nil {
269+
return nil, err
270+
}
271+
// Certificates used by the minio instance
272+
keyPairSecrets = append(keyPairSecrets, &operator.LocalCertificateReference{
273+
Name: secretName,
274+
Type: "kubernetes.io/tls",
275+
})
262276
}
263-
// Certificates used by the minio instance
264-
return &operator.LocalCertificateReference{
265-
Name: secretName,
266-
Type: "kubernetes.io/tls",
267-
}, nil
277+
return keyPairSecrets, nil
268278
}
269279

270280
func createOrReplaceKesConfigurationSecrets(ctx context.Context, clientSet K8sClientI, ns string, encryptionCfg *models.EncryptionConfiguration, kesConfigurationSecretName, kesClientCertSecretName, tenantName string) (*corev1.LocalObjectReference, *operator.LocalCertificateReference, error) {

restapi/admin_tenants_helper_test.go

Lines changed: 41 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -83,22 +83,32 @@ func Test_tenantUpdateCertificates(t *testing.T) {
8383
wantErr: true,
8484
},
8585
{
86-
name: "error replacing external certs for tenant because of missing keypair",
86+
name: "error replacing external certs for tenant because of empty keypair values",
8787
args: args{
8888
ctx: context.Background(),
8989
opClient: opClient,
9090
clientSet: k8sClient,
9191
namespace: "",
9292
params: admin_api.TenantUpdateCertificateParams{
9393
Body: &models.TLSConfiguration{
94-
Minio: &models.KeyPairConfiguration{},
94+
Minio: []*models.KeyPairConfiguration{
95+
{
96+
Crt: nil,
97+
Key: nil,
98+
},
99+
},
95100
},
96101
},
102+
mockDeletePodCollection: func(ctx context.Context, namespace string, opts metav1.DeleteOptions, listOpts metav1.ListOptions) error {
103+
return nil
104+
},
97105
mockTenantGet: func(ctx context.Context, namespace string, tenantName string, options metav1.GetOptions) (*operator.Tenant, error) {
98106
return &operator.Tenant{
99107
Spec: operator.TenantSpec{
100-
ExternalCertSecret: &operator.LocalCertificateReference{
101-
Name: "secret",
108+
ExternalCertSecret: []*operator.LocalCertificateReference{
109+
{
110+
Name: "secret",
111+
},
102112
},
103113
},
104114
}, nil
@@ -115,17 +125,21 @@ func Test_tenantUpdateCertificates(t *testing.T) {
115125
namespace: "",
116126
params: admin_api.TenantUpdateCertificateParams{
117127
Body: &models.TLSConfiguration{
118-
Minio: &models.KeyPairConfiguration{
119-
Crt: &badCrt,
120-
Key: &badKey,
128+
Minio: []*models.KeyPairConfiguration{
129+
{
130+
Crt: &badCrt,
131+
Key: &badKey,
132+
},
121133
},
122134
},
123135
},
124136
mockTenantGet: func(ctx context.Context, namespace string, tenantName string, options metav1.GetOptions) (*operator.Tenant, error) {
125137
return &operator.Tenant{
126138
Spec: operator.TenantSpec{
127-
ExternalCertSecret: &operator.LocalCertificateReference{
128-
Name: "secret",
139+
ExternalCertSecret: []*operator.LocalCertificateReference{
140+
{
141+
Name: "secret",
142+
},
129143
},
130144
},
131145
}, nil
@@ -145,17 +159,21 @@ func Test_tenantUpdateCertificates(t *testing.T) {
145159
namespace: "",
146160
params: admin_api.TenantUpdateCertificateParams{
147161
Body: &models.TLSConfiguration{
148-
Minio: &models.KeyPairConfiguration{
149-
Crt: &crt,
150-
Key: &key,
162+
Minio: []*models.KeyPairConfiguration{
163+
{
164+
Crt: &crt,
165+
Key: &key,
166+
},
151167
},
152168
},
153169
},
154170
mockTenantGet: func(ctx context.Context, namespace string, tenantName string, options metav1.GetOptions) (*operator.Tenant, error) {
155171
return &operator.Tenant{
156172
Spec: operator.TenantSpec{
157-
ExternalCertSecret: &operator.LocalCertificateReference{
158-
Name: "secret",
173+
ExternalCertSecret: []*operator.LocalCertificateReference{
174+
{
175+
Name: "secret",
176+
},
159177
},
160178
},
161179
}, nil
@@ -178,17 +196,21 @@ func Test_tenantUpdateCertificates(t *testing.T) {
178196
namespace: "",
179197
params: admin_api.TenantUpdateCertificateParams{
180198
Body: &models.TLSConfiguration{
181-
Minio: &models.KeyPairConfiguration{
182-
Crt: &crt,
183-
Key: &key,
199+
Minio: []*models.KeyPairConfiguration{
200+
{
201+
Crt: &crt,
202+
Key: &key,
203+
},
184204
},
185205
},
186206
},
187207
mockTenantGet: func(ctx context.Context, namespace string, tenantName string, options metav1.GetOptions) (*operator.Tenant, error) {
188208
return &operator.Tenant{
189209
Spec: operator.TenantSpec{
190-
ExternalCertSecret: &operator.LocalCertificateReference{
191-
Name: "secret",
210+
ExternalCertSecret: []*operator.LocalCertificateReference{
211+
{
212+
Name: "secret",
213+
},
192214
},
193215
},
194216
}, nil

0 commit comments

Comments
 (0)