Skip to content
This repository has been archived by the owner on Jul 11, 2023. It is now read-only.

feat(certs): create MRC on install #4747

Merged
merged 6 commits into from
May 25, 2022
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
3 changes: 3 additions & 0 deletions charts/osm/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,9 @@ The following table lists the configurable parameters of the osm chart and their
| osm.vault.port | int | `8200` | port to use to connect to Vault |
| osm.vault.protocol | string | `"http"` | protocol to use to connect to Vault |
| osm.vault.role | string | `"openservicemesh"` | Vault role to be used by Open Service Mesh |
| osm.vault.secret | object | `{"key":"token","name":"osm-vault-token"}` | The Kubernetes secret storing the Vault token used in OSM |
| osm.vault.secret.key | string | `"token"` | The Kubernetes secret key with the value bring the Vault token |
| osm.vault.secret.name | string | `"osm-vault-token"` | The Kubernetes secret name storing the Vault token used in OSM |
| osm.vault.token | string | `""` | token that should be used to connect to Vault |
| osm.webhookConfigNamePrefix | string | `"osm-webhook"` | Prefix used in name of the webhook configuration resources |
| smi.validateTrafficTarget | bool | `true` | Enables validation of SMI Traffic Target |
Expand Down
1 change: 1 addition & 0 deletions charts/osm/templates/cleanup-hook.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ spec:
kubectl replace -f /osm-crds;
kubectl delete --ignore-not-found meshconfig -n '{{ include "osm.namespace" . }}' osm-mesh-config;
kubectl delete --ignore-not-found secret -n '{{ include "osm.namespace" . }}' {{ .Values.osm.caBundleSecretName }};
kubectl delete --ignore-not-found meshrootcertificate -n '{{ include "osm.namespace" . }}' osm-mesh-root-certificate;
kubectl delete mutatingwebhookconfiguration -l app.kubernetes.io/name=openservicemesh.io,app.kubernetes.io/instance={{ .Values.osm.meshName }},app.kubernetes.io/version={{ .Chart.AppVersion }},app=osm-injector --ignore-not-found;
kubectl delete validatingwebhookconfiguration -l app.kubernetes.io/name=openservicemesh.io,app.kubernetes.io/instance={{ .Values.osm.meshName }},app.kubernetes.io/version={{ .Chart.AppVersion }},app=osm-controller --ignore-not-found;
nodeSelector:
Expand Down
43 changes: 43 additions & 0 deletions charts/osm/templates/preset-mesh-root-certificate.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: preset-mesh-root-certificate
namespace: {{ include "osm.namespace" . }}
data:
preset-mesh-root-certificate.json: |
{
"provider": {
{{- if eq (.Values.osm.certificateProvider.kind | lower) "tresor"}}
"tresor": {
"ca": {
"secretRef": {
"name": {{.Values.osm.caBundleSecretName | mustToJson}},
"namespace": "{{include "osm.namespace" .}}"
}
}
}
{{- end}}
{{- if eq (.Values.osm.certificateProvider.kind | lower) "cert-manager"}}
"certManager": {
"issuerName": {{.Values.osm.certmanager.issuerName | mustToJson}},
"issuerKind": {{.Values.osm.certmanager.issuerKind | mustToJson}},
"issuerGroup": {{.Values.osm.certmanager.issuerGroup | mustToJson}}
}
{{- end}}
{{- if eq (.Values.osm.certificateProvider.kind | lower) "vault"}}
"vault": {
"token": {
"secretKeyRef": {
"name": {{.Values.osm.vault.secret.name | mustToJson}},
"key": {{.Values.osm.vault.secret.key | mustToJson}},
"namespace": "{{include "osm.namespace" .}}"
}
},
"host": {{.Values.osm.vault.host | mustToJson}},
"role": {{.Values.osm.vault.role | mustToJson}},
"protocol": {{.Values.osm.vault.protocol | mustToJson}},
"port": {{.Values.osm.vault.port | mustToJson}}
}
{{- end}}
}
}
20 changes: 20 additions & 0 deletions charts/osm/values.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -1292,6 +1292,26 @@
"title": "Hashicorp Vault's role schema",
"description": "Role to use with Vault",
"type": "string"
},
"secret": {
"$id": "#/properties/osm/properties/vault/properties/secret",
"type": "object",
"title": "Vault token secret schema",
"description": "Vault token secret reference parameters",
"properties": {
"name": {
"$id": "#/properties/osm/properties/vault/properties/secret/properties/name",
"title": "Vault token secret name schema",
"description": "Name of the Kubernetes Secret to store the vault token",
"type": "string"
},
"key": {
"$id": "#/properties/osm/properties/vault/properties/secret/properties/key",
"title": "Vault token secret key schema",
"description": "Name of the Kubernetes Secret key with the value of the vault token",
"type": "string"
}
}
}
},
"examples": [
Expand Down
6 changes: 6 additions & 0 deletions charts/osm/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,12 @@ osm:
token: ""
# -- Vault role to be used by Open Service Mesh
role: openservicemesh
# -- The Kubernetes secret storing the Vault token used in OSM
secret:
# -- The Kubernetes secret name storing the Vault token used in OSM
name: osm-vault-token
# -- The Kubernetes secret key with the value bring the Vault token
key: token

#
# -- cert-manager.io configuration
Expand Down
1 change: 1 addition & 0 deletions cmd/cli/uninstall_mesh.go
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,7 @@ func (d *uninstallMeshCmd) uninstallCustomResourceDefinitions() error {
"egresses.policy.openservicemesh.io",
"ingressbackends.policy.openservicemesh.io",
"meshconfigs.config.openservicemesh.io",
"meshRootCertificate.config.openservicemesh.io",
"upstreamtrafficsettings.policy.openservicemesh.io",
"retries.policy.openservicemesh.io",
"multiclusterservices.config.openservicemesh.io",
Expand Down
4 changes: 0 additions & 4 deletions cmd/osm-bootstrap/crds/config_mesh_root_certificate.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -60,14 +60,10 @@ spec:
description: Cert-manager provider configuration
type: object
required:
- secretName
- issuerName
- issuerKind
- issuerGroup
properties:
secretName:
description: The name of the kubernetes secret containing the root certificate
type: string
issuerName:
description: The name of the Issuer or ClusterIssuer resource
type: string
Expand Down
98 changes: 86 additions & 12 deletions cmd/osm-bootstrap/osm-bootstrap.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,12 @@ import (
)

const (
meshConfigName = "osm-mesh-config"
presetMeshConfigName = "preset-mesh-config"
presetMeshConfigJSONKey = "preset-mesh-config.json"
meshConfigName = "osm-mesh-config"
presetMeshConfigName = "preset-mesh-config"
presetMeshConfigJSONKey = "preset-mesh-config.json"
meshRootCertificateName = "osm-mesh-root-certificate"
presetMeshRootCertificateName = "preset-mesh-root-certificate"
presetMeshRootCertificateJSONKey = "preset-mesh-root-certificate.json"
)

var (
Expand Down Expand Up @@ -76,9 +79,9 @@ var (
)

type bootstrap struct {
kubeClient kubernetes.Interface
meshConfigClient configClientset.Interface
namespace string
kubeClient kubernetes.Interface
configClient configClientset.Interface
namespace string
}

func init() {
Expand Down Expand Up @@ -156,9 +159,9 @@ func main() {
}

bootstrap := bootstrap{
kubeClient: kubeClient,
meshConfigClient: configClient,
namespace: osmNamespace,
kubeClient: kubeClient,
configClient: configClient,
namespace: osmNamespace,
}

err = bootstrap.ensureMeshConfig()
Expand All @@ -167,6 +170,12 @@ func main() {
return
}

err = bootstrap.ensureMeshRootCertificate()
if err != nil {
log.Fatal().Err(err).Msgf("Error setting up default MeshRootCertificate %s from ConfigMap %s", meshRootCertificateName, presetMeshRootCertificateName)
return
}

err = bootstrap.initiatilizeKubernetesEventsRecorder()
if err != nil {
log.Fatal().Err(err).Msg("Error initializing Kubernetes events recorder")
Expand Down Expand Up @@ -253,7 +262,7 @@ func (b *bootstrap) createDefaultMeshConfig() error {
if err != nil {
return err
}
if _, err := b.meshConfigClient.ConfigV1alpha2().MeshConfigs(b.namespace).Create(context.TODO(), defaultMeshConfig, metav1.CreateOptions{}); err == nil {
if _, err := b.configClient.ConfigV1alpha2().MeshConfigs(b.namespace).Create(context.TODO(), defaultMeshConfig, metav1.CreateOptions{}); err == nil {
log.Info().Msgf("MeshConfig (%s) created in namespace %s", meshConfigName, b.namespace)
return nil
}
Expand All @@ -267,7 +276,7 @@ func (b *bootstrap) createDefaultMeshConfig() error {
}

func (b *bootstrap) ensureMeshConfig() error {
config, err := b.meshConfigClient.ConfigV1alpha2().MeshConfigs(b.namespace).Get(context.TODO(), meshConfigName, metav1.GetOptions{})
config, err := b.configClient.ConfigV1alpha2().MeshConfigs(b.namespace).Get(context.TODO(), meshConfigName, metav1.GetOptions{})
if apierrors.IsNotFound(err) {
// create a default mesh config since it was not found
return b.createDefaultMeshConfig()
Expand All @@ -281,7 +290,7 @@ func (b *bootstrap) ensureMeshConfig() error {
if err := util.CreateApplyAnnotation(config, unstructured.UnstructuredJSONScheme); err != nil {
return err
}
if _, err := b.meshConfigClient.ConfigV1alpha2().MeshConfigs(b.namespace).Update(context.TODO(), config, metav1.UpdateOptions{}); err != nil {
if _, err := b.configClient.ConfigV1alpha2().MeshConfigs(b.namespace).Update(context.TODO(), config, metav1.UpdateOptions{}); err != nil {
return err
}
}
Expand Down Expand Up @@ -355,3 +364,68 @@ func buildDefaultMeshConfig(presetMeshConfigMap *corev1.ConfigMap) (*configv1alp

return config, util.CreateApplyAnnotation(config, unstructured.UnstructuredJSONScheme)
}

func (b *bootstrap) ensureMeshRootCertificate() error {
meshRootCertificateList, err := b.configClient.ConfigV1alpha2().MeshRootCertificates(b.namespace).List(context.TODO(), metav1.ListOptions{})
if err != nil {
return err
}

if len(meshRootCertificateList.Items) != 0 {
return nil
}

// create a MeshRootCertificate since none were found
return b.createMeshRootCertificate()
}

func (b *bootstrap) createMeshRootCertificate() error {
// find preset config map to build the MeshRootCertificate from
presetMeshRootCertificate, err := b.kubeClient.CoreV1().ConfigMaps(b.namespace).Get(context.TODO(), presetMeshRootCertificateName, metav1.GetOptions{})
if err != nil {
return err
}

// Create a MeshRootCertificate
defaultMeshRootCertificate, err := buildMeshRootCertificate(presetMeshRootCertificate)
if err != nil {
return err
}
_, err = b.configClient.ConfigV1alpha2().MeshRootCertificates(b.namespace).Create(context.TODO(), defaultMeshRootCertificate, metav1.CreateOptions{})
if apierrors.IsAlreadyExists(err) {
log.Info().Msgf("MeshRootCertificate already exists in %s. Skip creating.", b.namespace)
return nil
}
if err != nil {
return err
}

log.Info().Msgf("Successfully created MeshRootCertificate %s in %s.", meshRootCertificateName, b.namespace)
return nil
}

func buildMeshRootCertificate(presetMeshRootCertificateConfigMap *corev1.ConfigMap) (*configv1alpha2.MeshRootCertificate, error) {
presetMeshRootCertificate := presetMeshRootCertificateConfigMap.Data[presetMeshRootCertificateJSONKey]
presetMeshRootCertificateSpec := configv1alpha2.MeshRootCertificateSpec{}
err := json.Unmarshal([]byte(presetMeshRootCertificate), &presetMeshRootCertificateSpec)
if err != nil {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this function returns an error, so prefer to return the error vs log.Fatal.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point. I was following the same pattern we have for the MeshConfig, but that makes sense. For my own understanding, should a fatal log only be used when a function doesn't return an error? I would consider this error irrecoverable and I think log.Fatal fits here in that sense.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ya I'd say that I try to keep errors opaque. So there's either an error or a warning. A warning might get logged (but not returned), and the caller can decide what to do when the call to the function fails.

ie: an error just means the call failed, the caller decides what to do.

Another rule that's touted a lot is: only panic/exit in package main. All other packages should return errors. The typical exception is convenience functions that begin with Must, ie: text.MustParse

return nil, fmt.Errorf("error converting preset-mesh-root-certificate json string to MeshRootCertificate object: %w", err)
}

mrc := &configv1alpha2.MeshRootCertificate{
TypeMeta: metav1.TypeMeta{
Kind: "MeshRootCertificate",
APIVersion: "config.openservicemesh.io/configv1alpha2",
},
ObjectMeta: metav1.ObjectMeta{
Name: meshRootCertificateName,
},
Spec: presetMeshRootCertificateSpec,
Status: configv1alpha2.MeshRootCertificateStatus{
State: constants.MRCStateComplete,
RotationStage: constants.MRCStageIssuing,
},
}

return mrc, util.CreateApplyAnnotation(mrc, unstructured.UnstructuredJSONScheme)
}
Loading