From 801158dc5b7226228ab9d880cdabd216d5dc7576 Mon Sep 17 00:00:00 2001 From: Firas Ghanmi Date: Fri, 5 Jul 2024 16:08:17 +0200 Subject: [PATCH] Add TLS to Rekor and Trillian services --- api/v1alpha1/common.go | 14 +++ api/v1alpha1/ctlog_types.go | 4 + api/v1alpha1/ctlog_types_test.go | 9 ++ api/v1alpha1/fulcio_types.go | 4 + api/v1alpha1/fulcio_types_test.go | 17 +++ api/v1alpha1/zz_generated.deepcopy.go | 42 +++++++ 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 | 117 +++++++++++++++++- internal/controller/fulcio/actions/service.go | 14 +++ .../controller/fulcio/fulcio_controller.go | 2 + .../fulcio/fulcio_controller_test.go | 11 ++ 22 files changed, 1187 insertions(+), 4 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 9a37dd3e7..f7aee835d 100644 --- a/api/v1alpha1/ctlog_types.go +++ b/api/v1alpha1/ctlog_types.go @@ -42,6 +42,9 @@ type CTlogSpec struct { // Trillian service configuration //+kubebuilder:default:={port: 8091} Trillian TrillianService `json:"trillian,omitempty"` + // Reference to TLS server certificate, private key and CA certificate + //+optional + TLSCertificate TLSCert `json:"tls"` } // CTlogStatus defines the observed state of CTlog component @@ -51,6 +54,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 3026af7d8..542904fe7 100644 --- a/api/v1alpha1/ctlog_types_test.go +++ b/api/v1alpha1/ctlog_types_test.go @@ -135,6 +135,15 @@ var _ = Describe("CTlog", func() { Trillian: TrillianService{ Address: "trillian-system.default.svc", Port: &port, + 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 b6d7062df..76666ec5b 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 40c0cd300..6cf901fe1 100644 --- a/api/v1alpha1/zz_generated.deepcopy.go +++ b/api/v1alpha1/zz_generated.deepcopy.go @@ -134,6 +134,7 @@ func (in *CTlogSpec) DeepCopyInto(out *CTlogSpec) { } out.Monitoring = in.Monitoring in.Trillian.DeepCopyInto(&out.Trillian) + in.TLSCertificate.DeepCopyInto(&out.TLSCertificate) } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CTlogSpec. @@ -174,6 +175,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) @@ -360,6 +366,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. @@ -385,6 +392,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)) @@ -828,6 +840,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.redhat.com_ctlogs.yaml b/bundle/manifests/rhtas.redhat.com_ctlogs.yaml index 1638b1241..298c2c508 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. @@ -328,6 +384,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 bd2e0bc2d..19234dea8 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. @@ -406,6 +462,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 d3263662a..dc0131468 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. @@ -328,6 +384,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 14c9471a6..b804a6f3a 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. @@ -406,6 +462,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 ad6462398..fdd798501 100644 --- a/internal/controller/ctlog/actions/deployment.go +++ b/internal/controller/ctlog/actions/deployment.go @@ -8,9 +8,11 @@ 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" trillian "github.com/securesign/operator/internal/controller/trillian/actions" + 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" @@ -61,6 +63,106 @@ func (i deployAction) Handle(ctx context.Context, instance *rhtasv1alpha1.CTlog) return i.Failed(err) } + // 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 b1f35e895..9ac903bb4 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" @@ -62,6 +63,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 4e4fa80ec..8b4cf2aff 100644 --- a/internal/controller/ctlog/ctlog_controller.go +++ b/internal/controller/ctlog/ctlog_controller.go @@ -93,9 +93,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.NewResolveTreeAction(), @@ -158,6 +157,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 6ba599d75..b63953246 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..9b461d689 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,22 @@ 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") switch { case instance.Spec.Ctlog.Address == "": - instance.Spec.Ctlog.Address = fmt.Sprintf("http://ctlog.%s.svc", instance.Namespace) + if instance.Spec.TLSCertificate.CACertRef != nil || signingKeySecret != nil { + 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 instance.Spec.TLSCertificate.CACertRef != nil || signingKeySecret != nil { + port = int32(443) + + } else { + port = int32(80) + } instance.Spec.Ctlog.Port = &port } dp, err := futils.CreateDeployment(instance, DeploymentName, RBACName, labels) @@ -58,6 +71,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 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, "--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 b12a37af4..30f8e8daf 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" @@ -70,6 +71,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 e7a44da40..82e2ac71f 100644 --- a/internal/controller/fulcio/fulcio_controller.go +++ b/internal/controller/fulcio/fulcio_controller.go @@ -96,6 +96,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(), @@ -135,6 +136,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 25425f57b..25b07b38a 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)