From b387080a9e6def8d9540e3bb5542e4c644bcf5dc Mon Sep 17 00:00:00 2001 From: Firas Ghanmi Date: Thu, 4 Jul 2024 16:29:47 +0200 Subject: [PATCH] Add TLS to Fulcio and CTlog services --- api/v1alpha1/common.go | 14 +++ api/v1alpha1/ctlog_types.go | 5 + api/v1alpha1/ctlog_types_test.go | 10 ++ api/v1alpha1/fulcio_types.go | 4 + api/v1alpha1/fulcio_types_test.go | 17 +++ api/v1alpha1/zz_generated.deepcopy.go | 42 +++++++ .../rhtas-operator.clusterserviceversion.yaml | 2 +- bundle/manifests/rhtas.redhat.com_ctlogs.yaml | 111 ++++++++++++++++ .../manifests/rhtas.redhat.com_fulcios.yaml | 111 ++++++++++++++++ .../rhtas.redhat.com_securesigns.yaml | 112 +++++++++++++++++ config/crd/bases/rhtas.redhat.com_ctlogs.yaml | 111 ++++++++++++++++ .../crd/bases/rhtas.redhat.com_fulcios.yaml | 111 ++++++++++++++++ .../bases/rhtas.redhat.com_securesigns.yaml | 112 +++++++++++++++++ .../controller/ctlog/actions/config_map.go | 79 ++++++++++++ .../controller/ctlog/actions/deployment.go | 102 +++++++++++++++ internal/controller/ctlog/actions/service.go | 14 +++ internal/controller/ctlog/ctlog_controller.go | 4 +- .../controller/ctlog/ctlog_controller_test.go | 11 ++ .../controller/fulcio/actions/config_map.go | 79 ++++++++++++ .../controller/fulcio/actions/deployment.go | 118 +++++++++++++++++- internal/controller/fulcio/actions/service.go | 14 +++ .../controller/fulcio/fulcio_controller.go | 2 + .../fulcio/fulcio_controller_test.go | 11 ++ 23 files changed, 1191 insertions(+), 5 deletions(-) create mode 100644 internal/controller/ctlog/actions/config_map.go create mode 100644 internal/controller/fulcio/actions/config_map.go diff --git a/api/v1alpha1/common.go b/api/v1alpha1/common.go index 919cf0b10..bc833784f 100644 --- a/api/v1alpha1/common.go +++ b/api/v1alpha1/common.go @@ -90,3 +90,17 @@ type Pvc struct { //+optional StorageClass string `json:"storageClass,omitempty"` } + +// TLSCert defines fields for TLS certificate +// +kubebuilder:validation:XValidation:rule=(!has(self.certRef) || has(self.privateKeyRef)),message=privateKeyRef cannot be empty +type TLSCert struct { + // Reference to the private key + //+optional + PrivateKeyRef *SecretKeySelector `json:"privateKeyRef,omitempty"` + // Reference to service certificate + //+optional + CertRef *SecretKeySelector `json:"certRef,omitempty"` + // Reference to CA certificate + //+optional + CACertRef *LocalObjectReference `json:"CACertRef,omitempty"` +} diff --git a/api/v1alpha1/ctlog_types.go b/api/v1alpha1/ctlog_types.go index f1af7b356..a4eb05d40 100644 --- a/api/v1alpha1/ctlog_types.go +++ b/api/v1alpha1/ctlog_types.go @@ -38,6 +38,10 @@ type CTlogSpec struct { //Enable Service monitors for ctlog Monitoring MonitoringConfig `json:"monitoring,omitempty"` + + // Reference to TLS server certificate, private key and CA certificate + //+optional + TLSCertificate TLSCert `json:"tls"` } // CTlogStatus defines the observed state of CTlog component @@ -47,6 +51,7 @@ type CTlogStatus struct { PrivateKeyPasswordRef *SecretKeySelector `json:"privateKeyPasswordRef,omitempty"` PublicKeyRef *SecretKeySelector `json:"publicKeyRef,omitempty"` RootCertificates []SecretKeySelector `json:"rootCertificates,omitempty"` + TLSCertificate *TLSCert `json:"tls,omitempty"` // The ID of a Trillian tree that stores the log data. TreeID *int64 `json:"treeID,omitempty"` // +listType=map diff --git a/api/v1alpha1/ctlog_types_test.go b/api/v1alpha1/ctlog_types_test.go index 1fbf20dc8..00a18124f 100644 --- a/api/v1alpha1/ctlog_types_test.go +++ b/api/v1alpha1/ctlog_types_test.go @@ -130,6 +130,16 @@ var _ = Describe("CTlog", func() { }, }, }, + TLSCertificate: TLSCert{ + CertRef: &SecretKeySelector{ + Key: "cert", + LocalObjectReference: LocalObjectReference{Name: "secret"}, + }, + PrivateKeyRef: &SecretKeySelector{ + Key: "key", + LocalObjectReference: LocalObjectReference{Name: "secret"}, + }, + }, }, } diff --git a/api/v1alpha1/fulcio_types.go b/api/v1alpha1/fulcio_types.go index 36c5dbf32..02621845d 100644 --- a/api/v1alpha1/fulcio_types.go +++ b/api/v1alpha1/fulcio_types.go @@ -26,6 +26,9 @@ type FulcioSpec struct { // ConfigMap with additional bundle of trusted CA //+optional TrustedCA *LocalObjectReference `json:"trustedCA,omitempty"` + // Reference to TLS server certificate, private key and CA certificate + //+optional + TLSCertificate TLSCert `json:"tls"` } // FulcioCert defines fields for system-generated certificate @@ -101,6 +104,7 @@ type OIDCIssuer struct { type FulcioStatus struct { ServerConfigRef *LocalObjectReference `json:"serverConfigRef,omitempty"` Certificate *FulcioCert `json:"certificate,omitempty"` + TLSCertificate *TLSCert `json:"tls,omitempty"` Url string `json:"url,omitempty"` // +listType=map // +listMapKey=type diff --git a/api/v1alpha1/fulcio_types_test.go b/api/v1alpha1/fulcio_types_test.go index e04ce3b35..ca21bbfba 100644 --- a/api/v1alpha1/fulcio_types_test.go +++ b/api/v1alpha1/fulcio_types_test.go @@ -209,10 +209,16 @@ var _ = Describe("Fulcio", func() { PrivateKeyRef: &SecretKeySelector{Key: "key", LocalObjectReference: LocalObjectReference{Name: "name"}}, PrivateKeyPasswordRef: &SecretKeySelector{Key: "key", LocalObjectReference: LocalObjectReference{Name: "name"}}, }, + Ctlog: CtlogService{ Address: "ctlog.default.svc", Port: &port, }, + TLSCertificate: TLSCert{ + CertRef: &SecretKeySelector{Key: "key", LocalObjectReference: LocalObjectReference{Name: "name"}}, + PrivateKeyRef: &SecretKeySelector{Key: "key", LocalObjectReference: LocalObjectReference{Name: "name"}}, + CACertRef: &LocalObjectReference{Name: "ca-configmap"}, + }, }, } @@ -260,6 +266,17 @@ func generateFulcioObject(name string) *Fulcio { CommonName: "hostname", OrganizationName: "organization", }, + TLSCertificate: TLSCert{ + CertRef: &SecretKeySelector{ + Key: "cert", + LocalObjectReference: LocalObjectReference{Name: "secret"}, + }, + PrivateKeyRef: &SecretKeySelector{ + Key: "key", + LocalObjectReference: LocalObjectReference{Name: "secret"}, + }, + CACertRef: &LocalObjectReference{Name: "ca-configmap"}, + }, }, } } diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go index b5ac4040e..71fea7e70 100644 --- a/api/v1alpha1/zz_generated.deepcopy.go +++ b/api/v1alpha1/zz_generated.deepcopy.go @@ -133,6 +133,7 @@ func (in *CTlogSpec) DeepCopyInto(out *CTlogSpec) { copy(*out, *in) } out.Monitoring = in.Monitoring + in.TLSCertificate.DeepCopyInto(&out.TLSCertificate) } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CTlogSpec. @@ -173,6 +174,11 @@ func (in *CTlogStatus) DeepCopyInto(out *CTlogStatus) { *out = make([]SecretKeySelector, len(*in)) copy(*out, *in) } + if in.TLSCertificate != nil { + in, out := &in.TLSCertificate, &out.TLSCertificate + *out = new(TLSCert) + (*in).DeepCopyInto(*out) + } if in.TreeID != nil { in, out := &in.TreeID, &out.TreeID *out = new(int64) @@ -359,6 +365,7 @@ func (in *FulcioSpec) DeepCopyInto(out *FulcioSpec) { *out = new(LocalObjectReference) **out = **in } + in.TLSCertificate.DeepCopyInto(&out.TLSCertificate) } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FulcioSpec. @@ -384,6 +391,11 @@ func (in *FulcioStatus) DeepCopyInto(out *FulcioStatus) { *out = new(FulcioCert) (*in).DeepCopyInto(*out) } + if in.TLSCertificate != nil { + in, out := &in.TLSCertificate, &out.TLSCertificate + *out = new(TLSCert) + (*in).DeepCopyInto(*out) + } if in.Conditions != nil { in, out := &in.Conditions, &out.Conditions *out = make([]v1.Condition, len(*in)) @@ -802,6 +814,36 @@ func (in *SecuresignTufStatus) DeepCopy() *SecuresignTufStatus { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TLSCert) DeepCopyInto(out *TLSCert) { + *out = *in + if in.PrivateKeyRef != nil { + in, out := &in.PrivateKeyRef, &out.PrivateKeyRef + *out = new(SecretKeySelector) + **out = **in + } + if in.CertRef != nil { + in, out := &in.CertRef, &out.CertRef + *out = new(SecretKeySelector) + **out = **in + } + if in.CACertRef != nil { + in, out := &in.CACertRef, &out.CACertRef + *out = new(LocalObjectReference) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TLSCert. +func (in *TLSCert) DeepCopy() *TLSCert { + if in == nil { + return nil + } + out := new(TLSCert) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Trillian) DeepCopyInto(out *Trillian) { *out = *in diff --git a/bundle/manifests/rhtas-operator.clusterserviceversion.yaml b/bundle/manifests/rhtas-operator.clusterserviceversion.yaml index 55bf5f54f..27afd534a 100644 --- a/bundle/manifests/rhtas-operator.clusterserviceversion.yaml +++ b/bundle/manifests/rhtas-operator.clusterserviceversion.yaml @@ -192,7 +192,7 @@ metadata: ] capabilities: Seamless Upgrades containerImage: registry.redhat.io/rhtas/rhtas-rhel9-operator@sha256:a21f7128694a64989bf0d84a7a7da4c1ffc89edf62d594dc8bea7bcfe9ac08d3 - createdAt: "2024-07-03T12:24:03Z" + createdAt: "2024-07-04T14:29:12Z" features.operators.openshift.io/cnf: "false" features.operators.openshift.io/cni: "false" features.operators.openshift.io/csi: "false" diff --git a/bundle/manifests/rhtas.redhat.com_ctlogs.yaml b/bundle/manifests/rhtas.redhat.com_ctlogs.yaml index deece3f75..b085b016e 100644 --- a/bundle/manifests/rhtas.redhat.com_ctlogs.yaml +++ b/bundle/manifests/rhtas.redhat.com_ctlogs.yaml @@ -137,6 +137,62 @@ spec: type: object x-kubernetes-map-type: atomic type: array + tls: + description: Reference to TLS server certificate, private key and + CA certificate + properties: + CACertRef: + description: Reference to CA certificate + properties: + name: + description: |- + Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + required: + - name + type: object + x-kubernetes-map-type: atomic + certRef: + description: Reference to service certificate + properties: + key: + description: The key of the secret to select from. Must be + a valid secret key. + pattern: ^[-._a-zA-Z0-9]+$ + type: string + name: + description: |- + Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + required: + - key + - name + type: object + x-kubernetes-map-type: atomic + privateKeyRef: + description: Reference to the private key + properties: + key: + description: The key of the secret to select from. Must be + a valid secret key. + pattern: ^[-._a-zA-Z0-9]+$ + type: string + name: + description: |- + Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + required: + - key + - name + type: object + x-kubernetes-map-type: atomic + type: object + x-kubernetes-validations: + - message: privateKeyRef cannot be empty + rule: (!has(self.certRef) || has(self.privateKeyRef)) treeID: description: |- The ID of a Trillian tree that stores the log data. @@ -312,6 +368,61 @@ spec: - name type: object x-kubernetes-map-type: atomic + tls: + description: TLSCert defines fields for TLS certificate + properties: + CACertRef: + description: Reference to CA certificate + properties: + name: + description: |- + Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + required: + - name + type: object + x-kubernetes-map-type: atomic + certRef: + description: Reference to service certificate + properties: + key: + description: The key of the secret to select from. Must be + a valid secret key. + pattern: ^[-._a-zA-Z0-9]+$ + type: string + name: + description: |- + Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + required: + - key + - name + type: object + x-kubernetes-map-type: atomic + privateKeyRef: + description: Reference to the private key + properties: + key: + description: The key of the secret to select from. Must be + a valid secret key. + pattern: ^[-._a-zA-Z0-9]+$ + type: string + name: + description: |- + Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + required: + - key + - name + type: object + x-kubernetes-map-type: atomic + type: object + x-kubernetes-validations: + - message: privateKeyRef cannot be empty + rule: (!has(self.certRef) || has(self.privateKeyRef)) treeID: description: The ID of a Trillian tree that stores the log data. format: int64 diff --git a/bundle/manifests/rhtas.redhat.com_fulcios.yaml b/bundle/manifests/rhtas.redhat.com_fulcios.yaml index f7d448581..964dd4257 100644 --- a/bundle/manifests/rhtas.redhat.com_fulcios.yaml +++ b/bundle/manifests/rhtas.redhat.com_fulcios.yaml @@ -268,6 +268,62 @@ spec: required: - enabled type: object + tls: + description: Reference to TLS server certificate, private key and + CA certificate + properties: + CACertRef: + description: Reference to CA certificate + properties: + name: + description: |- + Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + required: + - name + type: object + x-kubernetes-map-type: atomic + certRef: + description: Reference to service certificate + properties: + key: + description: The key of the secret to select from. Must be + a valid secret key. + pattern: ^[-._a-zA-Z0-9]+$ + type: string + name: + description: |- + Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + required: + - key + - name + type: object + x-kubernetes-map-type: atomic + privateKeyRef: + description: Reference to the private key + properties: + key: + description: The key of the secret to select from. Must be + a valid secret key. + pattern: ^[-._a-zA-Z0-9]+$ + type: string + name: + description: |- + Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + required: + - key + - name + type: object + x-kubernetes-map-type: atomic + type: object + x-kubernetes-validations: + - message: privateKeyRef cannot be empty + rule: (!has(self.certRef) || has(self.privateKeyRef)) trustedCA: description: ConfigMap with additional bundle of trusted CA properties: @@ -445,6 +501,61 @@ spec: - name type: object x-kubernetes-map-type: atomic + tls: + description: TLSCert defines fields for TLS certificate + properties: + CACertRef: + description: Reference to CA certificate + properties: + name: + description: |- + Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + required: + - name + type: object + x-kubernetes-map-type: atomic + certRef: + description: Reference to service certificate + properties: + key: + description: The key of the secret to select from. Must be + a valid secret key. + pattern: ^[-._a-zA-Z0-9]+$ + type: string + name: + description: |- + Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + required: + - key + - name + type: object + x-kubernetes-map-type: atomic + privateKeyRef: + description: Reference to the private key + properties: + key: + description: The key of the secret to select from. Must be + a valid secret key. + pattern: ^[-._a-zA-Z0-9]+$ + type: string + name: + description: |- + Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + required: + - key + - name + type: object + x-kubernetes-map-type: atomic + type: object + x-kubernetes-validations: + - message: privateKeyRef cannot be empty + rule: (!has(self.certRef) || has(self.privateKeyRef)) url: type: string type: object diff --git a/bundle/manifests/rhtas.redhat.com_securesigns.yaml b/bundle/manifests/rhtas.redhat.com_securesigns.yaml index ed6057ef3..80503d5ae 100644 --- a/bundle/manifests/rhtas.redhat.com_securesigns.yaml +++ b/bundle/manifests/rhtas.redhat.com_securesigns.yaml @@ -153,6 +153,62 @@ spec: type: object x-kubernetes-map-type: atomic type: array + tls: + description: Reference to TLS server certificate, private key + and CA certificate + properties: + CACertRef: + description: Reference to CA certificate + properties: + name: + description: |- + Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + required: + - name + type: object + x-kubernetes-map-type: atomic + certRef: + description: Reference to service certificate + properties: + key: + description: The key of the secret to select from. Must + be a valid secret key. + pattern: ^[-._a-zA-Z0-9]+$ + type: string + name: + description: |- + Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + required: + - key + - name + type: object + x-kubernetes-map-type: atomic + privateKeyRef: + description: Reference to the private key + properties: + key: + description: The key of the secret to select from. Must + be a valid secret key. + pattern: ^[-._a-zA-Z0-9]+$ + type: string + name: + description: |- + Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + required: + - key + - name + type: object + x-kubernetes-map-type: atomic + type: object + x-kubernetes-validations: + - message: privateKeyRef cannot be empty + rule: (!has(self.certRef) || has(self.privateKeyRef)) treeID: description: |- The ID of a Trillian tree that stores the log data. @@ -390,6 +446,62 @@ spec: required: - enabled type: object + tls: + description: Reference to TLS server certificate, private key + and CA certificate + properties: + CACertRef: + description: Reference to CA certificate + properties: + name: + description: |- + Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + required: + - name + type: object + x-kubernetes-map-type: atomic + certRef: + description: Reference to service certificate + properties: + key: + description: The key of the secret to select from. Must + be a valid secret key. + pattern: ^[-._a-zA-Z0-9]+$ + type: string + name: + description: |- + Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + required: + - key + - name + type: object + x-kubernetes-map-type: atomic + privateKeyRef: + description: Reference to the private key + properties: + key: + description: The key of the secret to select from. Must + be a valid secret key. + pattern: ^[-._a-zA-Z0-9]+$ + type: string + name: + description: |- + Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + required: + - key + - name + type: object + x-kubernetes-map-type: atomic + type: object + x-kubernetes-validations: + - message: privateKeyRef cannot be empty + rule: (!has(self.certRef) || has(self.privateKeyRef)) trustedCA: description: ConfigMap with additional bundle of trusted CA properties: diff --git a/config/crd/bases/rhtas.redhat.com_ctlogs.yaml b/config/crd/bases/rhtas.redhat.com_ctlogs.yaml index ca762bb52..db400635c 100644 --- a/config/crd/bases/rhtas.redhat.com_ctlogs.yaml +++ b/config/crd/bases/rhtas.redhat.com_ctlogs.yaml @@ -137,6 +137,62 @@ spec: type: object x-kubernetes-map-type: atomic type: array + tls: + description: Reference to TLS server certificate, private key and + CA certificate + properties: + CACertRef: + description: Reference to CA certificate + properties: + name: + description: |- + Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + required: + - name + type: object + x-kubernetes-map-type: atomic + certRef: + description: Reference to service certificate + properties: + key: + description: The key of the secret to select from. Must be + a valid secret key. + pattern: ^[-._a-zA-Z0-9]+$ + type: string + name: + description: |- + Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + required: + - key + - name + type: object + x-kubernetes-map-type: atomic + privateKeyRef: + description: Reference to the private key + properties: + key: + description: The key of the secret to select from. Must be + a valid secret key. + pattern: ^[-._a-zA-Z0-9]+$ + type: string + name: + description: |- + Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + required: + - key + - name + type: object + x-kubernetes-map-type: atomic + type: object + x-kubernetes-validations: + - message: privateKeyRef cannot be empty + rule: (!has(self.certRef) || has(self.privateKeyRef)) treeID: description: |- The ID of a Trillian tree that stores the log data. @@ -312,6 +368,61 @@ spec: - name type: object x-kubernetes-map-type: atomic + tls: + description: TLSCert defines fields for TLS certificate + properties: + CACertRef: + description: Reference to CA certificate + properties: + name: + description: |- + Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + required: + - name + type: object + x-kubernetes-map-type: atomic + certRef: + description: Reference to service certificate + properties: + key: + description: The key of the secret to select from. Must be + a valid secret key. + pattern: ^[-._a-zA-Z0-9]+$ + type: string + name: + description: |- + Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + required: + - key + - name + type: object + x-kubernetes-map-type: atomic + privateKeyRef: + description: Reference to the private key + properties: + key: + description: The key of the secret to select from. Must be + a valid secret key. + pattern: ^[-._a-zA-Z0-9]+$ + type: string + name: + description: |- + Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + required: + - key + - name + type: object + x-kubernetes-map-type: atomic + type: object + x-kubernetes-validations: + - message: privateKeyRef cannot be empty + rule: (!has(self.certRef) || has(self.privateKeyRef)) treeID: description: The ID of a Trillian tree that stores the log data. format: int64 diff --git a/config/crd/bases/rhtas.redhat.com_fulcios.yaml b/config/crd/bases/rhtas.redhat.com_fulcios.yaml index d31cbbc5f..2d3a5594b 100644 --- a/config/crd/bases/rhtas.redhat.com_fulcios.yaml +++ b/config/crd/bases/rhtas.redhat.com_fulcios.yaml @@ -268,6 +268,62 @@ spec: required: - enabled type: object + tls: + description: Reference to TLS server certificate, private key and + CA certificate + properties: + CACertRef: + description: Reference to CA certificate + properties: + name: + description: |- + Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + required: + - name + type: object + x-kubernetes-map-type: atomic + certRef: + description: Reference to service certificate + properties: + key: + description: The key of the secret to select from. Must be + a valid secret key. + pattern: ^[-._a-zA-Z0-9]+$ + type: string + name: + description: |- + Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + required: + - key + - name + type: object + x-kubernetes-map-type: atomic + privateKeyRef: + description: Reference to the private key + properties: + key: + description: The key of the secret to select from. Must be + a valid secret key. + pattern: ^[-._a-zA-Z0-9]+$ + type: string + name: + description: |- + Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + required: + - key + - name + type: object + x-kubernetes-map-type: atomic + type: object + x-kubernetes-validations: + - message: privateKeyRef cannot be empty + rule: (!has(self.certRef) || has(self.privateKeyRef)) trustedCA: description: ConfigMap with additional bundle of trusted CA properties: @@ -445,6 +501,61 @@ spec: - name type: object x-kubernetes-map-type: atomic + tls: + description: TLSCert defines fields for TLS certificate + properties: + CACertRef: + description: Reference to CA certificate + properties: + name: + description: |- + Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + required: + - name + type: object + x-kubernetes-map-type: atomic + certRef: + description: Reference to service certificate + properties: + key: + description: The key of the secret to select from. Must be + a valid secret key. + pattern: ^[-._a-zA-Z0-9]+$ + type: string + name: + description: |- + Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + required: + - key + - name + type: object + x-kubernetes-map-type: atomic + privateKeyRef: + description: Reference to the private key + properties: + key: + description: The key of the secret to select from. Must be + a valid secret key. + pattern: ^[-._a-zA-Z0-9]+$ + type: string + name: + description: |- + Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + required: + - key + - name + type: object + x-kubernetes-map-type: atomic + type: object + x-kubernetes-validations: + - message: privateKeyRef cannot be empty + rule: (!has(self.certRef) || has(self.privateKeyRef)) url: type: string type: object diff --git a/config/crd/bases/rhtas.redhat.com_securesigns.yaml b/config/crd/bases/rhtas.redhat.com_securesigns.yaml index 658cab268..f1e381c1f 100644 --- a/config/crd/bases/rhtas.redhat.com_securesigns.yaml +++ b/config/crd/bases/rhtas.redhat.com_securesigns.yaml @@ -153,6 +153,62 @@ spec: type: object x-kubernetes-map-type: atomic type: array + tls: + description: Reference to TLS server certificate, private key + and CA certificate + properties: + CACertRef: + description: Reference to CA certificate + properties: + name: + description: |- + Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + required: + - name + type: object + x-kubernetes-map-type: atomic + certRef: + description: Reference to service certificate + properties: + key: + description: The key of the secret to select from. Must + be a valid secret key. + pattern: ^[-._a-zA-Z0-9]+$ + type: string + name: + description: |- + Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + required: + - key + - name + type: object + x-kubernetes-map-type: atomic + privateKeyRef: + description: Reference to the private key + properties: + key: + description: The key of the secret to select from. Must + be a valid secret key. + pattern: ^[-._a-zA-Z0-9]+$ + type: string + name: + description: |- + Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + required: + - key + - name + type: object + x-kubernetes-map-type: atomic + type: object + x-kubernetes-validations: + - message: privateKeyRef cannot be empty + rule: (!has(self.certRef) || has(self.privateKeyRef)) treeID: description: |- The ID of a Trillian tree that stores the log data. @@ -390,6 +446,62 @@ spec: required: - enabled type: object + tls: + description: Reference to TLS server certificate, private key + and CA certificate + properties: + CACertRef: + description: Reference to CA certificate + properties: + name: + description: |- + Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + required: + - name + type: object + x-kubernetes-map-type: atomic + certRef: + description: Reference to service certificate + properties: + key: + description: The key of the secret to select from. Must + be a valid secret key. + pattern: ^[-._a-zA-Z0-9]+$ + type: string + name: + description: |- + Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + required: + - key + - name + type: object + x-kubernetes-map-type: atomic + privateKeyRef: + description: Reference to the private key + properties: + key: + description: The key of the secret to select from. Must + be a valid secret key. + pattern: ^[-._a-zA-Z0-9]+$ + type: string + name: + description: |- + Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + required: + - key + - name + type: object + x-kubernetes-map-type: atomic + type: object + x-kubernetes-validations: + - message: privateKeyRef cannot be empty + rule: (!has(self.certRef) || has(self.privateKeyRef)) trustedCA: description: ConfigMap with additional bundle of trusted CA properties: diff --git a/internal/controller/ctlog/actions/config_map.go b/internal/controller/ctlog/actions/config_map.go new file mode 100644 index 000000000..e6ee19d33 --- /dev/null +++ b/internal/controller/ctlog/actions/config_map.go @@ -0,0 +1,79 @@ +package actions + +import ( + "context" + "fmt" + + rhtasv1alpha1 "github.com/securesign/operator/api/v1alpha1" + "github.com/securesign/operator/internal/controller/common/action" + k8sutils "github.com/securesign/operator/internal/controller/common/utils/kubernetes" + "github.com/securesign/operator/internal/controller/constants" + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/meta" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" +) + +func NewCAConfigMapAction() action.Action[*rhtasv1alpha1.CTlog] { + return &configMapAction{} +} + +type configMapAction struct { + action.BaseAction +} + +func (i configMapAction) Name() string { + return "create CA configMap" +} + +func (i configMapAction) CanHandle(ctx context.Context, instance *rhtasv1alpha1.CTlog) bool { + c := meta.FindStatusCondition(instance.Status.Conditions, constants.Ready) + cm, _ := k8sutils.GetConfigMap(ctx, i.Client, instance.Namespace, "ca-configmap") + return c.Reason == constants.Creating || c.Reason == constants.Ready && cm == nil +} + +func (i configMapAction) Handle(ctx context.Context, instance *rhtasv1alpha1.CTlog) *action.Result { + var ( + err error + updated bool + ) + + labels := constants.LabelsFor(ComponentName, DeploymentName, instance.Name) + + configMap := &corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: "ca-configmap", + Namespace: instance.Namespace, + Labels: labels, + }, + Data: map[string]string{}, + } + + if err = controllerutil.SetControllerReference(instance, configMap, i.Client.Scheme()); err != nil { + return i.Failed(fmt.Errorf("could not set controller reference for configMap: %w", err)) + } + if updated, err = i.Ensure(ctx, configMap); err != nil { + meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{ + Type: constants.Ready, + Status: metav1.ConditionFalse, + Reason: constants.Failure, + Message: err.Error(), + }) + return i.FailedWithStatusUpdate(ctx, fmt.Errorf("could not create configMap: %w", err), instance) + } + + //TLS: Annotate configMap + configMap.Annotations = map[string]string{"service.beta.openshift.io/inject-cabundle": "true"} + err = i.Client.Update(ctx, configMap) + if err != nil { + return i.FailedWithStatusUpdate(ctx, fmt.Errorf("could not annotate configMap: %w", err), instance) + } + + if updated { + meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{Type: constants.Ready, + Status: metav1.ConditionFalse, Reason: constants.Creating, Message: "ConfigMap created"}) + return i.StatusUpdate(ctx, instance) + } else { + return i.Continue() + } +} diff --git a/internal/controller/ctlog/actions/deployment.go b/internal/controller/ctlog/actions/deployment.go index d273a31c9..c56310073 100644 --- a/internal/controller/ctlog/actions/deployment.go +++ b/internal/controller/ctlog/actions/deployment.go @@ -6,8 +6,10 @@ import ( rhtasv1alpha1 "github.com/securesign/operator/api/v1alpha1" "github.com/securesign/operator/internal/controller/common/action" + k8sutils "github.com/securesign/operator/internal/controller/common/utils/kubernetes" "github.com/securesign/operator/internal/controller/constants" "github.com/securesign/operator/internal/controller/ctlog/utils" + corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/meta" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" @@ -51,6 +53,106 @@ func (i deployAction) Handle(ctx context.Context, instance *rhtasv1alpha1.CTlog) } } + // TLS certificate + signingKeySecret, _ := k8sutils.GetSecret(i.Client, "openshift-service-ca", "signing-key") + if instance.Spec.TLSCertificate.CertRef != nil && instance.Spec.TLSCertificate.CACertRef != nil { + dp.Spec.Template.Spec.Volumes = append(dp.Spec.Template.Spec.Volumes, + corev1.Volume{ + Name: "tls-cert", + VolumeSource: corev1.VolumeSource{ + Projected: &corev1.ProjectedVolumeSource{ + Sources: []corev1.VolumeProjection{ + { + Secret: &corev1.SecretProjection{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: instance.Spec.TLSCertificate.CertRef.Name, + }, + Items: []corev1.KeyToPath{ + { + Key: instance.Spec.TLSCertificate.CertRef.Key, + Path: "tls.crt", + }, + }, + }, + }, + { + Secret: &corev1.SecretProjection{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: instance.Spec.TLSCertificate.PrivateKeyRef.Name, + }, + Items: []corev1.KeyToPath{ + { + Key: instance.Spec.TLSCertificate.PrivateKeyRef.Key, + Path: "tls.key", + }, + }, + }, + }, + { + ConfigMap: &corev1.ConfigMapProjection{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: instance.Spec.TLSCertificate.CACertRef.Name, + }, + Items: []corev1.KeyToPath{ + { + Key: "ca.crt", // User should use this key. + Path: "ca.crt", + }, + }, + }, + }, + }, + }, + }, + }) + } else if signingKeySecret != nil { + i.Logger.V(1).Info("TLS: Using secrets/signing-key secret") + dp.Spec.Template.Spec.Volumes = append(dp.Spec.Template.Spec.Volumes, + corev1.Volume{ + Name: "tls-cert", + VolumeSource: corev1.VolumeSource{ + Projected: &corev1.ProjectedVolumeSource{ + Sources: []corev1.VolumeProjection{ + { + Secret: &corev1.SecretProjection{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: instance.Name + "-tls-secret", + }, + }, + }, + { + ConfigMap: &corev1.ConfigMapProjection{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "ca-configmap", + }, + Items: []corev1.KeyToPath{ + { + Key: "service-ca.crt", + Path: "ca.crt", + }, + }, + }, + }, + }, + }, + }, + }) + } else { + i.Logger.V(1).Info("Communication between services is insecure") + } + + if instance.Spec.TLSCertificate.CertRef != nil && instance.Spec.TLSCertificate.CACertRef != nil || signingKeySecret != nil { + dp.Spec.Template.Spec.Containers[0].VolumeMounts = append(dp.Spec.Template.Spec.Containers[0].VolumeMounts, + corev1.VolumeMount{ + Name: "tls-cert", + MountPath: "/etc/ssl/certs", + ReadOnly: true, + }) + dp.Spec.Template.Spec.Containers[0].Args = append(dp.Spec.Template.Spec.Containers[0].Args, "--tls_certificate", "/etc/ssl/certs/tls.crt") + dp.Spec.Template.Spec.Containers[0].Args = append(dp.Spec.Template.Spec.Containers[0].Args, "--tls_key", "/etc/ssl/certs/tls.key") + dp.Spec.Template.Spec.Containers[0].Args = append(dp.Spec.Template.Spec.Containers[0].Args, "--trillian_tls_ca_cert_file", "/etc/ssl/certs/ca.crt") + } + if err = controllerutil.SetControllerReference(instance, dp, i.Client.Scheme()); err != nil { return i.Failed(fmt.Errorf("could not set controller reference for Deployment: %w", err)) } diff --git a/internal/controller/ctlog/actions/service.go b/internal/controller/ctlog/actions/service.go index 90d1c42b6..003ef7936 100644 --- a/internal/controller/ctlog/actions/service.go +++ b/internal/controller/ctlog/actions/service.go @@ -7,6 +7,7 @@ import ( rhtasv1alpha1 "github.com/securesign/operator/api/v1alpha1" "github.com/securesign/operator/internal/controller/common/action" "github.com/securesign/operator/internal/controller/common/utils/kubernetes" + k8sutils "github.com/securesign/operator/internal/controller/common/utils/kubernetes" "github.com/securesign/operator/internal/controller/constants" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/meta" @@ -60,6 +61,19 @@ func (i serviceAction) Handle(ctx context.Context, instance *rhtasv1alpha1.CTlog return i.FailedWithStatusUpdate(ctx, fmt.Errorf("could not create service: %w", err), instance) } + //TLS: Annotate service + signingKeySecret, _ := k8sutils.GetSecret(i.Client, "openshift-service-ca", "signing-key") + if signingKeySecret != nil && instance.Spec.TLSCertificate.CertRef == nil { + if svc.Annotations == nil { + svc.Annotations = make(map[string]string) + } + svc.Annotations["service.beta.openshift.io/serving-cert-secret-name"] = instance.Name + "-tls-secret" + err := i.Client.Update(ctx, svc) + if err != nil { + return i.FailedWithStatusUpdate(ctx, fmt.Errorf("could not annotate service: %w", err), instance) + } + } + if updated { meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{Type: constants.Ready, Status: metav1.ConditionFalse, Reason: constants.Creating, Message: "Service created"}) diff --git a/internal/controller/ctlog/ctlog_controller.go b/internal/controller/ctlog/ctlog_controller.go index 16730ea0a..91d6be8ae 100644 --- a/internal/controller/ctlog/ctlog_controller.go +++ b/internal/controller/ctlog/ctlog_controller.go @@ -89,9 +89,8 @@ func (r *CTlogReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl }), actions.NewPendingAction(), - transitions.NewToCreatePhaseAction[*rhtasv1alpha1.CTlog](), - + actions.NewCAConfigMapAction(), actions.NewHandleFulcioCertAction(), actions.NewHandleKeysAction(), actions.NewCreateTrillianTreeAction(), @@ -154,6 +153,7 @@ func (r *CTlogReconciler) SetupWithManager(mgr ctrl.Manager) error { For(&rhtasv1alpha1.CTlog{}). Owns(&v1.Deployment{}). Owns(&v12.Service{}). + Owns(&v12.ConfigMap{}). WatchesMetadata(partialSecret, handler.EnqueueRequestsFromMapFunc(func(ctx context.Context, object client.Object) []reconcile.Request { val, ok := object.GetLabels()["app.kubernetes.io/instance"] if ok { diff --git a/internal/controller/ctlog/ctlog_controller_test.go b/internal/controller/ctlog/ctlog_controller_test.go index d45967785..bfd4576c4 100644 --- a/internal/controller/ctlog/ctlog_controller_test.go +++ b/internal/controller/ctlog/ctlog_controller_test.go @@ -95,6 +95,17 @@ var _ = Describe("CTlog controller", func() { Spec: v1alpha1.CTlogSpec{ TreeID: &ptr, + TLSCertificate: v1alpha1.TLSCert{ + CertRef: &v1alpha1.SecretKeySelector{ + Key: "cert", + LocalObjectReference: v1alpha1.LocalObjectReference{Name: "secret-crt"}, + }, + PrivateKeyRef: &v1alpha1.SecretKeySelector{ + Key: "key", + LocalObjectReference: v1alpha1.LocalObjectReference{Name: "secret-key"}, + }, + CACertRef: &v1alpha1.LocalObjectReference{Name: "ca-configmap"}, + }, }, } err = k8sClient.Create(ctx, instance) diff --git a/internal/controller/fulcio/actions/config_map.go b/internal/controller/fulcio/actions/config_map.go new file mode 100644 index 000000000..1084ebb5d --- /dev/null +++ b/internal/controller/fulcio/actions/config_map.go @@ -0,0 +1,79 @@ +package actions + +import ( + "context" + "fmt" + + rhtasv1alpha1 "github.com/securesign/operator/api/v1alpha1" + "github.com/securesign/operator/internal/controller/common/action" + k8sutils "github.com/securesign/operator/internal/controller/common/utils/kubernetes" + "github.com/securesign/operator/internal/controller/constants" + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/meta" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" +) + +func NewCAConfigMapAction() action.Action[*rhtasv1alpha1.Fulcio] { + return &configMapAction{} +} + +type configMapAction struct { + action.BaseAction +} + +func (i configMapAction) Name() string { + return "create CA configMap" +} + +func (i configMapAction) CanHandle(ctx context.Context, instance *rhtasv1alpha1.Fulcio) bool { + c := meta.FindStatusCondition(instance.Status.Conditions, constants.Ready) + cm, _ := k8sutils.GetConfigMap(ctx, i.Client, instance.Namespace, "ca-configmap") + return c.Reason == constants.Creating || c.Reason == constants.Ready && cm == nil +} + +func (i configMapAction) Handle(ctx context.Context, instance *rhtasv1alpha1.Fulcio) *action.Result { + var ( + err error + updated bool + ) + + labels := constants.LabelsFor(ComponentName, DeploymentName, instance.Name) + + configMap := &corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: "ca-configmap", + Namespace: instance.Namespace, + Labels: labels, + }, + Data: map[string]string{}, + } + + if err = controllerutil.SetControllerReference(instance, configMap, i.Client.Scheme()); err != nil { + return i.Failed(fmt.Errorf("could not set controller reference for configMap: %w", err)) + } + if updated, err = i.Ensure(ctx, configMap); err != nil { + meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{ + Type: constants.Ready, + Status: metav1.ConditionFalse, + Reason: constants.Failure, + Message: err.Error(), + }) + return i.FailedWithStatusUpdate(ctx, fmt.Errorf("could not create configMap: %w", err), instance) + } + + //TLS: Annotate configMap + configMap.Annotations = map[string]string{"service.beta.openshift.io/inject-cabundle": "true"} + err = i.Client.Update(ctx, configMap) + if err != nil { + return i.FailedWithStatusUpdate(ctx, fmt.Errorf("could not annotate configMap: %w", err), instance) + } + + if updated { + meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{Type: constants.Ready, + Status: metav1.ConditionFalse, Reason: constants.Creating, Message: "ConfigMap created"}) + return i.StatusUpdate(ctx, instance) + } else { + return i.Continue() + } +} diff --git a/internal/controller/fulcio/actions/deployment.go b/internal/controller/fulcio/actions/deployment.go index d0aea6334..ead82a3f3 100644 --- a/internal/controller/fulcio/actions/deployment.go +++ b/internal/controller/fulcio/actions/deployment.go @@ -6,8 +6,10 @@ import ( rhtasv1alpha1 "github.com/securesign/operator/api/v1alpha1" "github.com/securesign/operator/internal/controller/common/action" + k8sutils "github.com/securesign/operator/internal/controller/common/utils/kubernetes" "github.com/securesign/operator/internal/controller/constants" futils "github.com/securesign/operator/internal/controller/fulcio/utils" + corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/meta" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" @@ -38,11 +40,23 @@ func (i deployAction) Handle(ctx context.Context, instance *rhtasv1alpha1.Fulcio labels := constants.LabelsFor(ComponentName, DeploymentName, instance.Name) + signingKeySecret, _ := k8sutils.GetSecret(i.Client, "openshift-service-ca", "signing-key") + tlsEnabled := instance.Spec.TLSCertificate.CertRef != nil && instance.Spec.TLSCertificate.CACertRef != nil || signingKeySecret != nil switch { case instance.Spec.Ctlog.Address == "": - instance.Spec.Ctlog.Address = fmt.Sprintf("http://ctlog.%s.svc", instance.Namespace) + if tlsEnabled { + instance.Spec.Ctlog.Address = fmt.Sprintf("https://ctlog.%s.svc", instance.Namespace) + } else { + instance.Spec.Ctlog.Address = fmt.Sprintf("http://ctlog.%s.svc", instance.Namespace) + } case instance.Spec.Ctlog.Port == nil: - port := int32(80) + var port int32 + if tlsEnabled { + port = int32(443) + + } else { + port = int32(80) + } instance.Spec.Ctlog.Port = &port } dp, err := futils.CreateDeployment(instance, DeploymentName, RBACName, labels) @@ -58,6 +72,106 @@ func (i deployAction) Handle(ctx context.Context, instance *rhtasv1alpha1.Fulcio } } + // TLS certificate + if instance.Spec.TLSCertificate.CertRef != nil && instance.Spec.TLSCertificate.CACertRef != nil { + dp.Spec.Template.Spec.Volumes = append(dp.Spec.Template.Spec.Volumes, + corev1.Volume{ + Name: "tls-cert", + VolumeSource: corev1.VolumeSource{ + Projected: &corev1.ProjectedVolumeSource{ + Sources: []corev1.VolumeProjection{ + { + Secret: &corev1.SecretProjection{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: instance.Spec.TLSCertificate.CertRef.Name, + }, + Items: []corev1.KeyToPath{ + { + Key: instance.Spec.TLSCertificate.CertRef.Key, + Path: "tls.crt", + }, + }, + }, + }, + { + Secret: &corev1.SecretProjection{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: instance.Spec.TLSCertificate.PrivateKeyRef.Name, + }, + Items: []corev1.KeyToPath{ + { + Key: instance.Spec.TLSCertificate.PrivateKeyRef.Key, + Path: "tls.key", + }, + }, + }, + }, + { + ConfigMap: &corev1.ConfigMapProjection{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: instance.Spec.TLSCertificate.CACertRef.Name, + }, + Items: []corev1.KeyToPath{ + { + Key: "ca.crt", // User should use this key. + Path: "ca.crt", + }, + }, + }, + }, + }, + }, + }, + }) + } else if signingKeySecret != nil { + i.Logger.V(1).Info("TLS: Using secrets/signing-key secret") + dp.Spec.Template.Spec.Volumes = append(dp.Spec.Template.Spec.Volumes, + corev1.Volume{ + Name: "tls-cert", + VolumeSource: corev1.VolumeSource{ + Projected: &corev1.ProjectedVolumeSource{ + Sources: []corev1.VolumeProjection{ + { + Secret: &corev1.SecretProjection{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: instance.Name + "-tls-secret", + }, + }, + }, + { + ConfigMap: &corev1.ConfigMapProjection{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "ca-configmap", + }, + Items: []corev1.KeyToPath{ + { + Key: "service-ca.crt", + Path: "ca.crt", + }, + }, + }, + }, + }, + }, + }, + }) + } else { + i.Logger.V(1).Info("Communication between services is insecure") + } + + if tlsEnabled { + dp.Spec.Template.Spec.Containers[0].VolumeMounts = append(dp.Spec.Template.Spec.Containers[0].VolumeMounts, + corev1.VolumeMount{ + Name: "tls-cert", + MountPath: "/etc/ssl/certs", + ReadOnly: true, + }) + + dp.Spec.Template.Spec.Containers[0].Args = append(dp.Spec.Template.Spec.Containers[0].Args, "--grpc-tls-certificate", "/etc/ssl/certs/tls.crt") + dp.Spec.Template.Spec.Containers[0].Args = append(dp.Spec.Template.Spec.Containers[0].Args, "--grpc-tls-key", "/etc/ssl/certs/tls.key") + dp.Spec.Template.Spec.Containers[0].Args = append(dp.Spec.Template.Spec.Containers[0].Args, "--tls-ca-cert", "/etc/ssl/certs/ca.crt") + } + if err = controllerutil.SetControllerReference(instance, dp, i.Client.Scheme()); err != nil { return i.Failed(fmt.Errorf("could not set controller reference for Deployment: %w", err)) } diff --git a/internal/controller/fulcio/actions/service.go b/internal/controller/fulcio/actions/service.go index 35a7ef537..659b8c405 100644 --- a/internal/controller/fulcio/actions/service.go +++ b/internal/controller/fulcio/actions/service.go @@ -7,6 +7,7 @@ import ( rhtasv1alpha1 "github.com/securesign/operator/api/v1alpha1" "github.com/securesign/operator/internal/controller/common/action" "github.com/securesign/operator/internal/controller/common/utils/kubernetes" + k8sutils "github.com/securesign/operator/internal/controller/common/utils/kubernetes" "github.com/securesign/operator/internal/controller/constants" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/meta" @@ -66,6 +67,19 @@ func (i serviceAction) Handle(ctx context.Context, instance *rhtasv1alpha1.Fulci return i.FailedWithStatusUpdate(ctx, fmt.Errorf("could not create service: %w", err), instance) } + //TLS: Annotate service + signingKeySecret, _ := k8sutils.GetSecret(i.Client, "openshift-service-ca", "signing-key") + if signingKeySecret != nil && instance.Spec.TLSCertificate.CertRef == nil { + if svc.Annotations == nil { + svc.Annotations = make(map[string]string) + } + svc.Annotations["service.beta.openshift.io/serving-cert-secret-name"] = instance.Name + "-tls-secret" + err := i.Client.Update(ctx, svc) + if err != nil { + return i.FailedWithStatusUpdate(ctx, fmt.Errorf("could not annotate service: %w", err), instance) + } + } + if updated { meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{Type: constants.Ready, Status: metav1.ConditionFalse, Reason: constants.Creating, Message: "Service created"}) diff --git a/internal/controller/fulcio/fulcio_controller.go b/internal/controller/fulcio/fulcio_controller.go index c450ff84b..81f955216 100644 --- a/internal/controller/fulcio/fulcio_controller.go +++ b/internal/controller/fulcio/fulcio_controller.go @@ -94,6 +94,7 @@ func (r *FulcioReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctr }), actions.NewHandleCertAction(), transitions.NewToCreatePhaseAction[*rhtasv1alpha1.Fulcio](), + actions.NewCAConfigMapAction(), actions.NewRBACAction(), actions.NewServerConfigAction(), actions.NewDeployAction(), @@ -133,6 +134,7 @@ func (r *FulcioReconciler) SetupWithManager(mgr ctrl.Manager) error { For(&rhtasv1alpha1.Fulcio{}). Owns(&v1.Deployment{}). Owns(&v12.Service{}). + Owns(&v12.ConfigMap{}). Owns(&v13.Ingress{}). Complete(r) } diff --git a/internal/controller/fulcio/fulcio_controller_test.go b/internal/controller/fulcio/fulcio_controller_test.go index a04ad62c6..8263e439b 100644 --- a/internal/controller/fulcio/fulcio_controller_test.go +++ b/internal/controller/fulcio/fulcio_controller_test.go @@ -120,6 +120,17 @@ var _ = Describe("Fulcio controller", func() { TrustedCA: &v1alpha1.LocalObjectReference{ Name: "trusted-ca-bundle", }, + TLSCertificate: v1alpha1.TLSCert{ + CertRef: &v1alpha1.SecretKeySelector{ + Key: "cert", + LocalObjectReference: v1alpha1.LocalObjectReference{Name: "secret-crt"}, + }, + PrivateKeyRef: &v1alpha1.SecretKeySelector{ + Key: "key", + LocalObjectReference: v1alpha1.LocalObjectReference{Name: "secret-key"}, + }, + CACertRef: &v1alpha1.LocalObjectReference{Name: "ca-configmap"}, + }, }, } err = k8sClient.Create(ctx, instance)