Skip to content
This repository was archived by the owner on Apr 11, 2024. It is now read-only.

Commit 583014f

Browse files
authored
refactor: use a string type for Nutanix's AdditionalTrustBundle (#28)
* refactor: use a string type for Nutanix's AdditionalTrustBundle Use a string instead of a ConfigMap reference to make it easier for both the handlers to use, and the clients to set in the API. * fix: force insecure: false with additionalTrustBundle
1 parent d4768a4 commit 583014f

File tree

11 files changed

+126
-59
lines changed

11 files changed

+126
-59
lines changed

api/v1alpha1/nutanix_clusterconfig_types.go

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -49,11 +49,11 @@ type NutanixPrismCentralEndpointSpec struct {
4949
// +optional
5050
Insecure bool `json:"insecure"`
5151

52-
// A reference to the ConfigMap containing a PEM encoded x509 cert for the RootCA that was used to create
52+
// A base64 PEM encoded x509 cert for the RootCA that was used to create
5353
// the certificate for a Prism Central that uses certificates that were issued by a non-publicly trusted RootCA.
5454
// The trust bundle is added to the cert pool used to authenticate the TLS connection to the Prism Central.
5555
// +optional
56-
AdditionalTrustBundle *corev1.LocalObjectReference `json:"additionalTrustBundle,omitempty"`
56+
AdditionalTrustBundle *string `json:"additionalTrustBundle,omitempty"`
5757

5858
// A reference to the Secret for credential information for the target Prism Central instance
5959
Credentials corev1.LocalObjectReference `json:"credentials"`
@@ -82,19 +82,13 @@ func (NutanixPrismCentralEndpointSpec) VariableSchema() clusterv1.VariableSchema
8282
Type: "boolean",
8383
},
8484
"additionalTrustBundle": {
85-
Description: "A reference to the ConfigMap containing a PEM encoded x509 cert for the RootCA " +
85+
Description: "A base64 PEM encoded x509 cert for the RootCA " +
8686
"that was used to create the certificate for a Prism Central that uses certificates " +
8787
"that were issued by a non-publicly trusted RootCA." +
8888
"The trust bundle is added to the cert pool used to authenticate the TLS connection " +
8989
"to the Prism Central.",
90-
Type: "object",
91-
Properties: map[string]clusterv1.JSONSchemaProps{
92-
"name": {
93-
Description: "The name of the ConfigMap",
94-
Type: "string",
95-
},
96-
},
97-
Required: []string{"name"},
90+
Type: "string",
91+
Format: "byte",
9892
},
9993
"credentials": {
10094
Description: "A reference to the Secret for credential information" +

api/v1alpha1/zz_generated.deepcopy.go

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

docs/content/customization/nutanix/prism-central-endpoint.md

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ spec:
2929
3030
Applying this configuration will result in the following value being set:
3131
32-
- control-plane NutanixClusterTemplate:
32+
- `NutanixClusterTemplate`:
3333

3434
```yaml
3535
spec:
@@ -43,3 +43,47 @@ spec:
4343
kind: Secret
4444
name: secret-name
4545
```
46+
47+
### Provide an Optional Trusted CA Bundle
48+
49+
If the Prism Central endpoint uses a self-signed certificate, you can provide an additional trust bundle
50+
to be used by the Nutanix provider.
51+
This is a base64 PEM encoded x509 cert for the RootCA that was used to create the certificate for a Prism Central
52+
53+
See [Nutanix Security Guide] for more information.
54+
55+
```yaml
56+
apiVersion: cluster.x-k8s.io/v1beta1
57+
kind: Cluster
58+
metadata:
59+
name: <NAME>
60+
spec:
61+
topology:
62+
variables:
63+
- name: clusterConfig
64+
value:
65+
nutanix:
66+
prismCentralEndpoint:
67+
# ...
68+
additionalTrustBundle: "LS0...="
69+
```
70+
71+
Applying this configuration will result in the following value being set:
72+
73+
- `NutanixClusterTemplate`:
74+
75+
```yaml
76+
spec:
77+
template:
78+
spec:
79+
prismCentral:
80+
# ...
81+
additionalTrustBundle:
82+
kind: String
83+
data: |-
84+
-----BEGIN CERTIFICATE-----
85+
...
86+
-----END CERTIFICATE-----
87+
```
88+
89+
[Nutanix Security Guide]: https://portal.nutanix.com/page/documents/details?targetId=Nutanix-Security-Guide-v6_5:mul-security-ssl-certificate-pc-t.html

examples/capi-quick-start/nutanix-cluster-calico-crs.yaml

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,4 @@
11
apiVersion: v1
2-
binaryData:
3-
ca.crt: ${NUTANIX_ADDITIONAL_TRUST_BUNDLE=""}
4-
kind: ConfigMap
5-
metadata:
6-
labels:
7-
cluster.x-k8s.io/provider: nutanix
8-
name: ${CLUSTER_NAME}-pc-trusted-ca-bundle
9-
---
10-
apiVersion: v1
112
data:
123
nutanix-ccm.yaml: |
134
---

examples/capi-quick-start/nutanix-cluster-calico-helm-addon.yaml

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,4 @@
11
apiVersion: v1
2-
binaryData:
3-
ca.crt: ${NUTANIX_ADDITIONAL_TRUST_BUNDLE=""}
4-
kind: ConfigMap
5-
metadata:
6-
labels:
7-
cluster.x-k8s.io/provider: nutanix
8-
name: ${CLUSTER_NAME}-pc-trusted-ca-bundle
9-
---
10-
apiVersion: v1
112
data:
123
nutanix-ccm.yaml: |
134
---

examples/capi-quick-start/nutanix-cluster-cilium-crs.yaml

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,4 @@
11
apiVersion: v1
2-
binaryData:
3-
ca.crt: ${NUTANIX_ADDITIONAL_TRUST_BUNDLE=""}
4-
kind: ConfigMap
5-
metadata:
6-
labels:
7-
cluster.x-k8s.io/provider: nutanix
8-
name: ${CLUSTER_NAME}-pc-trusted-ca-bundle
9-
---
10-
apiVersion: v1
112
data:
123
nutanix-ccm.yaml: |
134
---

examples/capi-quick-start/nutanix-cluster-cilium-helm-addon.yaml

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,4 @@
11
apiVersion: v1
2-
binaryData:
3-
ca.crt: ${NUTANIX_ADDITIONAL_TRUST_BUNDLE=""}
4-
kind: ConfigMap
5-
metadata:
6-
labels:
7-
cluster.x-k8s.io/provider: nutanix
8-
name: ${CLUSTER_NAME}-pc-trusted-ca-bundle
9-
---
10-
apiVersion: v1
112
data:
123
nutanix-ccm.yaml: |
134
---

hack/examples/bases/nutanix/cluster/kustomization.yaml.tmpl

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,3 +28,9 @@ patches:
2828
- target:
2929
kind: Cluster
3030
path: ../../../patches/nutanix/initialize-variables.yaml
31+
32+
# Remove Additional Trust Bundle ConfigMap
33+
- target:
34+
kind: ConfigMap
35+
name: ".*-pc-trusted-ca-bundle"
36+
path: ../../../patches/nutanix/remove-additional-trust-bundle/cm.yaml
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# Copyright 2024 D2iQ, Inc. All rights reserved.
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
$patch: delete
5+
apiVersion: v1
6+
kind: ConfigMap
7+
metadata:
8+
name: ${CLUSTER_NAME}-pc-trusted-ca-bundle

pkg/handlers/nutanix/mutation/prismcentralendpoint/inject.go

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,13 @@ package prismcentralendpoint
55

66
import (
77
"context"
8+
"encoding/base64"
9+
"fmt"
810

911
"github.com/nutanix-cloud-native/prism-go-client/environment/credentials"
1012
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
1113
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
14+
"k8s.io/utils/ptr"
1215
runtimehooksv1 "sigs.k8s.io/cluster-api/exp/runtime/hooks/api/v1alpha1"
1316
ctrl "sigs.k8s.io/controller-runtime"
1417
"sigs.k8s.io/controller-runtime/pkg/client"
@@ -105,15 +108,27 @@ func (h *nutanixPrismCentralEndpoint) Mutate(
105108
Namespace: clusterKey.Namespace,
106109
},
107110
}
108-
if prismCentralEndpointVar.AdditionalTrustBundle != nil {
111+
additionalTrustBundle := ptr.Deref(prismCentralEndpointVar.AdditionalTrustBundle, "")
112+
if additionalTrustBundle != "" {
113+
var decoded []byte
114+
decoded, err = base64.StdEncoding.DecodeString(additionalTrustBundle)
115+
if err != nil {
116+
log.Error(err, "error decoding additional trust bundle")
117+
return fmt.Errorf("error decoding additional trust bundle: %w", err)
118+
}
109119
prismCentral.AdditionalTrustBundle = &credentials.NutanixTrustBundleReference{
110-
Kind: credentials.NutanixTrustBundleKindConfigMap,
111-
Name: prismCentralEndpointVar.AdditionalTrustBundle.Name,
112-
// Assume the ConfigMap is in the same namespace as Cluster
113-
Namespace: clusterKey.Namespace,
120+
Kind: credentials.NutanixTrustBundleKindString,
121+
Data: string(decoded),
114122
}
115123
}
116124

125+
// Always force insecure to false if additional trust bundle is provided.
126+
// This ensures that the trust bundle is actually used to validate the connection.
127+
if additionalTrustBundle != "" && prismCentral.Insecure {
128+
log.Info("AdditionalTrustBundle is provided, setting insecure to false")
129+
prismCentral.Insecure = false
130+
}
131+
117132
obj.Spec.Template.Spec.PrismCentral = prismCentral
118133

119134
return nil

pkg/handlers/nutanix/mutation/prismcentralendpoint/tests/generate_patches.go

Lines changed: 41 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@ import (
1717
"github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/common/pkg/testutils/capitest/request"
1818
)
1919

20+
//nolint:lll // just a long string
21+
const testCertBundle = "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUVjekNDQTF1Z0F3SUJBZ0lCQURBTkJna3Foa2lHOXcwQkFRUUZBRC4uQWtHQTFVRUJoTUNSMEl4CkV6QVJCZ05WQkFnVENsTnZiV1V0VTNSaGRHVXhGREFTQmdOVkJBb1RDMC4uMEVnVEhSa01UY3dOUVlEClZRUUxFeTVEYkdGemN5QXhJRkIxWW14cFl5QlFjbWx0WVhKNUlFTmxjbi4uWFJwYjI0Z1FYVjBhRzl5CmFYUjVNUlF3RWdZRFZRUURFd3RDWlhOMElFTkJJRXgwWkRBZUZ3MHdNRC4uVFV3TVRaYUZ3MHdNVEF5Ck1EUXhPVFV3TVRaYU1JR0hNUXN3Q1FZRFZRUUdFd0pIUWpFVE1CRUdBMS4uMjl0WlMxVGRHRjBaVEVVCk1CSUdBMVVFQ2hNTFFtVnpkQ0JEUVNCTWRHUXhOekExQmdOVkJBc1RMay4uREVnVUhWaWJHbGpJRkJ5CmFXMWhjbmtnUTJWeWRHbG1hV05oZEdsdmJpQkJkWFJvYjNKcGRIa3hGRC4uQU1UQzBKbGMzUWdRMEVnClRIUmtNSUlCSWpBTkJna3Foa2lHOXcwQkFRRUZBQU9DQVE4QU1JSUJDZy4uVHoybXI3U1ppQU1mUXl1CnZCak05T2lKalJhelhCWjFCalA1Q0UvV20vUnI1MDBQUksrTGg5eDVlSi4uL0FOQkUwc1RLMFpzREdNCmFrMm0xZzdvcnVJM2RZM1ZIcUl4RlR6MFRhMWQrTkFqd25MZTRuT2I3Ly4uazA1U2hoQnJKR0JLS3hiCjhuMTA0by81cDhIQXNaUGR6YkZNSXlOakp6Qk0ybzV5NUExM3dpTGl0RS4uZnlZa1F6YXhDdzBBd3psCmtWSGlJeUN1YUY0d2o1NzFwU3prdjZzdis0SURNYlQvWHBDbzhMNndUYS4uc2grZXRMRDZGdFRqWWJiCnJ2WjhSUU0xdGxLZG9NSGcycXhyYUFWKytITkJZbU5XczBkdUVkalViSi4uWEk5VHRuUzRvMUNrajdQCk9mbGppUUlEQVFBQm80SG5NSUhrTUIwR0ExVWREZ1FXQkJROHVyTUNSTC4uNUFrSXA5TkpISnc1VENCCnRBWURWUjBqQklHc01JR3BnQlE4dXJNQ1JMWVlNSFVLVTVBa0lwOU5KSC4uYVNCaWpDQmh6RUxNQWtHCkExVUVCaE1DUjBJeEV6QVJCZ05WQkFnVENsTnZiV1V0VTNSaGRHVXhGRC4uQW9UQzBKbGMzUWdRMEVnClRIUmtNVGN3TlFZRFZRUUxFeTVEYkdGemN5QXhJRkIxWW14cFl5QlFjbS4uRU5sY25ScFptbGpZWFJwCmIyNGdRWFYwYUc5eWFYUjVNUlF3RWdZRFZRUURFd3RDWlhOMElFTkJJRS4uREFNQmdOVkhSTUVCVEFECkFRSC9NQTBHQ1NxR1NJYjNEUUVCQkFVQUE0SUJBUUMxdVlCY3NTbmN3QS4uRENzUWVyNzcyQzJ1Y3BYCnhRVUUvQzBwV1dtNmdEa3dkNUQwRFNNREpScVYvd2VvWjR3QzZCNzNmNS4uYkxoR1lIYVhKZVNENktyClhjb093TGRTYUdtSllzbExLWkIzWklERXAwd1lUR2hndGViNkpGaVR0bi4uc2YyeGRyWWZQQ2lJQjdnCkJNQVY3R3pkYzRWc3BTNmxqckFoYmlpYXdkQmlRbFFtc0JlRno5SmtGNC4uYjNsOEJvR04rcU1hNTZZCkl0OHVuYTJnWTRsMk8vL29uODhyNUlXSmxtMUwwb0E4ZTRmUjJ5ckJIWC4uYWRzR2VGS2t5TnJ3R2kvCjd2UU1mWGRHc1JyWE5HUkduWCt2V0RaMy96V0kwam9EdENrTm5xRXBWbi4uSG9YCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0="
22+
2023
func TestGeneratePatches(
2124
t *testing.T,
2225
generatorFunc func() mutation.GeneratePatches,
@@ -31,20 +34,52 @@ func TestGeneratePatches(
3134
Name: "unset variable",
3235
},
3336
capitest.PatchTestDef{
34-
Name: "all fields set",
37+
Name: "all required fields set",
38+
Vars: []runtimehooksv1.Variable{
39+
capitest.VariableWithValue(
40+
variableName,
41+
v1alpha1.NutanixPrismCentralEndpointSpec{
42+
Host: "prism-central.nutanix.com",
43+
Port: 9441,
44+
Insecure: true,
45+
Credentials: corev1.LocalObjectReference{
46+
Name: "credentials",
47+
},
48+
},
49+
variablePath...,
50+
),
51+
},
52+
RequestItem: request.NewNutanixClusterTemplateRequestItem(""),
53+
ExpectedPatchMatchers: []capitest.JSONPatchMatcher{
54+
{
55+
Operation: "replace",
56+
Path: "/spec/template/spec/prismCentral",
57+
ValueMatcher: gomega.SatisfyAll(
58+
gomega.HaveKeyWithValue(
59+
"address",
60+
gomega.BeEquivalentTo("prism-central.nutanix.com"),
61+
),
62+
gomega.HaveKeyWithValue("port", gomega.BeEquivalentTo(9441)),
63+
gomega.HaveKeyWithValue("insecure", true),
64+
gomega.HaveKey("credentialRef"),
65+
gomega.Not(gomega.HaveKey("additionalTrustBundle")),
66+
),
67+
},
68+
},
69+
},
70+
capitest.PatchTestDef{
71+
Name: "additional trust bundle is set",
3572
Vars: []runtimehooksv1.Variable{
3673
capitest.VariableWithValue(
3774
variableName,
3875
v1alpha1.NutanixPrismCentralEndpointSpec{
3976
Host: "prism-central.nutanix.com",
4077
Port: 9441,
41-
Insecure: false,
78+
Insecure: true,
4279
Credentials: corev1.LocalObjectReference{
4380
Name: "credentials",
4481
},
45-
AdditionalTrustBundle: ptr.To(corev1.LocalObjectReference{
46-
Name: "bundle",
47-
}),
82+
AdditionalTrustBundle: ptr.To(testCertBundle),
4883
},
4984
variablePath...,
5085
),
@@ -60,6 +95,7 @@ func TestGeneratePatches(
6095
gomega.BeEquivalentTo("prism-central.nutanix.com"),
6196
),
6297
gomega.HaveKeyWithValue("port", gomega.BeEquivalentTo(9441)),
98+
// Assert the insecure field was set to false as the additional trust bundle is set
6399
gomega.HaveKeyWithValue("insecure", false),
64100
gomega.HaveKey("credentialRef"),
65101
gomega.HaveKey("additionalTrustBundle"),

0 commit comments

Comments
 (0)