Support for adding ExternalCaCert secrets (#576)

This commit is contained in:
Lenin Alevski
2021-02-02 18:49:40 -06:00
committed by GitHub
parent 6b02f472e6
commit 1c6a29bc20
6 changed files with 205 additions and 7 deletions

View File

@@ -35,6 +35,9 @@ import (
// swagger:model tlsConfiguration // swagger:model tlsConfiguration
type TLSConfiguration struct { type TLSConfiguration struct {
// ca certificates
CaCertificates []string `json:"ca_certificates"`
// console // console
Console *KeyPairConfiguration `json:"console,omitempty"` Console *KeyPairConfiguration `json:"console,omitempty"`

View File

@@ -253,6 +253,15 @@ const AddTenant = ({
encoded_cert: "", encoded_cert: "",
}, },
]); ]);
const [caCertificates, setCaCertificates] = useState<KeyPair[]>([
{
id: Date.now().toString(),
key: "",
cert: "",
encoded_key: "",
encoded_cert: "",
},
]);
const [consoleKeyVal, setConsoleKeyVal] = useState<string>(""); const [consoleKeyVal, setConsoleKeyVal] = useState<string>("");
const [consoleCertVal, setConsoleCertVal] = useState<string>(""); const [consoleCertVal, setConsoleCertVal] = useState<string>("");
const [serverKeyVal, setServerKeyVal] = useState<string>(""); const [serverKeyVal, setServerKeyVal] = useState<string>("");
@@ -264,7 +273,49 @@ const AddTenant = ({
const [vaultCAVal, setVaultCAVal] = useState<string>(""); const [vaultCAVal, setVaultCAVal] = useState<string>("");
const [gemaltoCAVal, setGemaltoCAVal] = useState<string>(""); const [gemaltoCAVal, setGemaltoCAVal] = useState<string>("");
// Certificates functions // CA Certificates functions
const addCaCertificate = () => {
setCaCertificates((currentCertificates) => [
...currentCertificates,
{
id: Date.now().toString(),
key: "",
cert: "",
encoded_key: "",
encoded_cert: "",
},
]);
};
const deleteCaCertificate = (id: string) => {
if (caCertificates.length > 1) {
setCaCertificates(
caCertificates.filter((item: KeyPair) => item.id !== id)
);
}
};
const addFileToCaCertificates = (
id: string,
key: string,
fileName: string,
value: string
) => {
setCaCertificates(
caCertificates.map((item: KeyPair) => {
if (item.id === id) {
return {
...item,
[key]: fileName,
[`encoded_${key}`]: value,
};
}
return item;
})
);
};
// KeyPair Certificates functions
const addKeyPair = () => { const addKeyPair = () => {
setMinioCertificates((currentCertificates) => [ setMinioCertificates((currentCertificates) => [
...currentCertificates, ...currentCertificates,
@@ -941,6 +992,16 @@ const AddTenant = ({
let tenantCerts: any = null; let tenantCerts: any = null;
let consoleCerts: any = null; let consoleCerts: any = null;
let caCerts: any = null;
if (caCertificates.length > 0) {
caCerts = {
ca_certificates: caCertificates
.map((keyPair: KeyPair) => keyPair.encoded_cert)
.filter((keyPair) => keyPair),
};
}
if (minioCertificates.length > 0) { if (minioCertificates.length > 0) {
tenantCerts = { tenantCerts = {
minio: minioCertificates minio: minioCertificates
@@ -961,12 +1022,13 @@ const AddTenant = ({
}; };
} }
if (tenantCerts || consoleCerts) { if (tenantCerts || consoleCerts || caCerts) {
dataSend = { dataSend = {
...dataSend, ...dataSend,
tls: { tls: {
...tenantCerts, ...tenantCerts,
...consoleCerts, ...consoleCerts,
...caCerts,
}, },
}; };
} }
@@ -1775,6 +1837,60 @@ const AddTenant = ({
<br /> <br />
</Grid> </Grid>
</Grid> </Grid>
<Grid container>
<Grid item xs={12}>
<Typography
variant="overline"
display="block"
gutterBottom
>
CA Certificates
</Typography>
</Grid>
{caCertificates.map((keyPair: KeyPair) => (
<React.Fragment key={keyPair.id}>
<Grid item xs={10}>
<FileSelector
onChange={(encodedValue, fileName) => {
addFileToCaCertificates(
keyPair.id,
"cert",
fileName,
encodedValue
);
}}
accept=".cer,.crt,.cert,.pem"
id="tlsCert"
name="tlsCert"
label="Cert"
value={keyPair.cert}
/>
</Grid>
<Grid item xs={1}>
<Button
onClick={() => {
deleteCaCertificate(keyPair.id);
}}
color="secondary"
>
Remove
</Button>
</Grid>
</React.Fragment>
))}
<Grid item xs={12}>
<Button onClick={addCaCertificate} color="primary">
Add More
</Button>
</Grid>
</Grid>
<Grid container>
<Grid item xs={12}>
<br />
<Divider />
<br />
</Grid>
</Grid>
<Grid container> <Grid container>
<Grid item xs={12}> <Grid item xs={12}>
<Typography <Typography

View File

@@ -701,7 +701,29 @@ func getTenantCreatedResponse(session *models.Principal, params admin_api.Create
minInst.Spec.KES.Annotations = tenantReq.Encryption.Annotations minInst.Spec.KES.Annotations = tenantReq.Encryption.Annotations
minInst.Spec.KES.NodeSelector = tenantReq.Encryption.NodeSelector minInst.Spec.KES.NodeSelector = tenantReq.Encryption.NodeSelector
} }
// External TLS CA certificates for MinIO
if tenantReq.TLS != nil && len(tenantReq.TLS.CaCertificates) > 0 {
var caCertificates []tenantSecret
for i, caCertificate := range tenantReq.TLS.CaCertificates {
certificateContent, err := base64.StdEncoding.DecodeString(caCertificate)
if err != nil {
return nil, prepareError(errorGeneric, nil, err)
}
caCertificates = append(caCertificates, tenantSecret{
Name: fmt.Sprintf("ca-certificate-%d", i),
Content: map[string][]byte{
"public.crt": certificateContent,
},
})
}
if len(caCertificates) > 0 {
certificateSecrets, err := createOrReplaceSecrets(ctx, &k8sClient, ns, caCertificates, tenantName)
if err != nil {
return nil, prepareError(errorGeneric, nil, err)
}
minInst.Spec.ExternalCaCertSecret = certificateSecrets
}
}
// optionals are set below // optionals are set below
var consoleAccess string var consoleAccess string
var consoleSecret string var consoleSecret string

View File

@@ -227,17 +227,58 @@ func getKESConfiguration(ctx context.Context, clientSet K8sClientI, ns string, e
return kesConfiguration, nil return kesConfiguration, nil
} }
type tenantSecret struct {
Name string
Content map[string][]byte
}
// createOrReplaceSecrets receives an array of Tenant Secrets to be stored as k8s secrets
func createOrReplaceSecrets(ctx context.Context, clientSet K8sClientI, ns string, secrets []tenantSecret, tenantName string) ([]*miniov2.LocalCertificateReference, error) {
var k8sSecrets []*miniov2.LocalCertificateReference
for _, secret := range secrets {
if len(secret.Content) > 0 && secret.Name != "" {
// delete secret with same name if exists
err := clientSet.deleteSecret(ctx, ns, secret.Name, metav1.DeleteOptions{})
if err != nil {
// log the error if any and continue
log.Println(err)
}
imm := true
k8sSecret := &corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: secret.Name,
Labels: map[string]string{
miniov2.TenantLabel: tenantName,
},
},
Type: corev1.SecretTypeOpaque,
Immutable: &imm,
Data: secret.Content,
}
_, err = clientSet.createSecret(ctx, ns, k8sSecret, metav1.CreateOptions{})
if err != nil {
return nil, err
}
k8sSecrets = append(k8sSecrets, &miniov2.LocalCertificateReference{
Name: secret.Name,
Type: "Opaque",
})
}
}
return k8sSecrets, nil
}
// createOrReplaceExternalCertSecrets receives an array of KeyPairs (public and private key), encoded in base64, decode it and generate an equivalent number of kubernetes // createOrReplaceExternalCertSecrets receives an array of KeyPairs (public and private key), encoded in base64, decode it and generate an equivalent number of kubernetes
// secrets to be used by the miniov2 for TLS encryption // secrets to be used by the miniov2 for TLS encryption
func createOrReplaceExternalCertSecrets(ctx context.Context, clientSet K8sClientI, ns string, keyPairs []*models.KeyPairConfiguration, secretName, tenantName string) ([]*miniov2.LocalCertificateReference, error) { func createOrReplaceExternalCertSecrets(ctx context.Context, clientSet K8sClientI, ns string, keyPairs []*models.KeyPairConfiguration, secretName, tenantName string) ([]*miniov2.LocalCertificateReference, error) {
var keyPairSecrets []*miniov2.LocalCertificateReference var keyPairSecrets []*miniov2.LocalCertificateReference
for i, keyPair := range keyPairs { for i, keyPair := range keyPairs {
secretName := fmt.Sprintf("%s-%d", secretName, i) keyPairSecretName := fmt.Sprintf("%s-%d", secretName, i)
if keyPair == nil || keyPair.Crt == nil || keyPair.Key == nil || *keyPair.Crt == "" || *keyPair.Key == "" { if keyPair == nil || keyPair.Crt == nil || keyPair.Key == nil || *keyPair.Crt == "" || *keyPair.Key == "" {
return nil, errors.New("certificate files must not be empty") return nil, errors.New("certificate files must not be empty")
} }
// delete secret with same name if exists // delete secret with same name if exists
err := clientSet.deleteSecret(ctx, ns, fmt.Sprintf("%s-%d", secretName, i), metav1.DeleteOptions{}) err := clientSet.deleteSecret(ctx, ns, keyPairSecretName, metav1.DeleteOptions{})
if err != nil { if err != nil {
// log the error if any and continue // log the error if any and continue
log.Println(err) log.Println(err)
@@ -253,7 +294,7 @@ func createOrReplaceExternalCertSecrets(ctx context.Context, clientSet K8sClient
} }
externalTLSCertificateSecret := &corev1.Secret{ externalTLSCertificateSecret := &corev1.Secret{
ObjectMeta: metav1.ObjectMeta{ ObjectMeta: metav1.ObjectMeta{
Name: secretName, Name: keyPairSecretName,
Labels: map[string]string{ Labels: map[string]string{
miniov2.TenantLabel: tenantName, miniov2.TenantLabel: tenantName,
}, },
@@ -271,7 +312,7 @@ func createOrReplaceExternalCertSecrets(ctx context.Context, clientSet K8sClient
} }
// Certificates used by the minio instance // Certificates used by the minio instance
keyPairSecrets = append(keyPairSecrets, &miniov2.LocalCertificateReference{ keyPairSecrets = append(keyPairSecrets, &miniov2.LocalCertificateReference{
Name: secretName, Name: keyPairSecretName,
Type: "kubernetes.io/tls", Type: "kubernetes.io/tls",
}) })
} }

View File

@@ -5267,6 +5267,12 @@ func init() {
"tlsConfiguration": { "tlsConfiguration": {
"type": "object", "type": "object",
"properties": { "properties": {
"ca_certificates": {
"type": "array",
"items": {
"type": "string"
}
},
"console": { "console": {
"type": "object", "type": "object",
"$ref": "#/definitions/keyPairConfiguration" "$ref": "#/definitions/keyPairConfiguration"
@@ -11159,6 +11165,12 @@ func init() {
"tlsConfiguration": { "tlsConfiguration": {
"type": "object", "type": "object",
"properties": { "properties": {
"ca_certificates": {
"type": "array",
"items": {
"type": "string"
}
},
"console": { "console": {
"type": "object", "type": "object",
"$ref": "#/definitions/keyPairConfiguration" "$ref": "#/definitions/keyPairConfiguration"

View File

@@ -3130,6 +3130,10 @@ definitions:
console: console:
type: object type: object
$ref: "#/definitions/keyPairConfiguration" $ref: "#/definitions/keyPairConfiguration"
ca_certificates:
type: array
items:
type: string
idpConfiguration: idpConfiguration:
type: object type: object