diff --git a/api/operator/v1beta1/vmalertmanager_types.go b/api/operator/v1beta1/vmalertmanager_types.go index b36a5ef1..fa83dc6b 100644 --- a/api/operator/v1beta1/vmalertmanager_types.go +++ b/api/operator/v1beta1/vmalertmanager_types.go @@ -302,6 +302,9 @@ type VMAlertmanagerSpec struct { // WebConfig defines configuration for webserver // https://github.com/prometheus/alertmanager/blob/main/docs/https.md WebConfig *AlertmanagerWebConfig `json:"webConfig,omitempty"` + + // GossipConfig defines gossip TLS configuration for Alertmanager cluster + GossipConfig *AlertmanagerGossipConfig `json:"gossipConfig,omitempty"` } // UnmarshalJSON implements json.Unmarshaler interface @@ -560,10 +563,18 @@ func (cr *VMAlertmanager) SetUpdateStatusTo(ctx context.Context, r client.Client return nil } +// AlertmanagerGossipConfig defines Gossip TLS configuration for alertmanager +type AlertmanagerGossipConfig struct { + // TLSServerConfig defines server TLS configuration for alertmanager + TLSServerConfig *TLSServerConfig `json:"tls_server_config,omitempty"` + // TLSClientConfig defines client TLS configuration for alertmanager + TLSClientConfig *TLSClientConfig `json:"tls_client_config,omitempty"` +} + // AlertmanagerWebConfig defines web server configuration for alertmanager type AlertmanagerWebConfig struct { - // TLSServerConfig defines tls configuration for alertmanager web server - TLSServerConfig *WebserverTLSConfig `json:"tls_server_config,omitempty"` + // TLSServerConfig defines server TLS configuration for alertmanager + TLSServerConfig *TLSServerConfig `json:"tls_server_config,omitempty"` // HTTPServerConfig defines http server configuration for alertmanager web server HTTPServerConfig *AlertmanagerHTTPConfig `json:"http_server_config,omitempty"` // BasicAuthUsers Usernames and hashed passwords that have full access to the web server diff --git a/api/operator/v1beta1/vmalertmanager_webhook.go b/api/operator/v1beta1/vmalertmanager_webhook.go index b2b7fc0e..a9c3a0f5 100644 --- a/api/operator/v1beta1/vmalertmanager_webhook.go +++ b/api/operator/v1beta1/vmalertmanager_webhook.go @@ -52,10 +52,10 @@ func (r *VMAlertmanager) sanityCheck() error { } if r.Spec.WebConfig.TLSServerConfig != nil { tc := r.Spec.WebConfig.TLSServerConfig - if tc.CertFile == "" && tc.CertSecretRef == nil { + if tc.Certs.CertFile == "" && tc.Certs.CertSecretRef == nil { return fmt.Errorf("either cert_secret_ref or cert_file must be set for tls_server_config") } - if tc.KeyFile == "" && tc.KeySecretRef == nil { + if tc.Certs.KeyFile == "" && tc.Certs.KeySecretRef == nil { return fmt.Errorf("either key_secret_ref or key_file must be set for tls_server_config") } if tc.ClientAuthType == "RequireAndVerifyClientCert" { @@ -65,6 +65,31 @@ func (r *VMAlertmanager) sanityCheck() error { } } } + if r.Spec.GossipConfig != nil { + if r.Spec.GossipConfig.TLSServerConfig != nil { + tc := r.Spec.GossipConfig.TLSServerConfig + if tc.Certs.CertFile == "" && tc.Certs.CertSecretRef == nil { + return fmt.Errorf("either cert_secret_ref or cert_file must be set for tls_server_config") + } + if tc.Certs.KeyFile == "" && tc.Certs.KeySecretRef == nil { + return fmt.Errorf("either key_secret_ref or key_file must be set for tls_server_config") + } + if tc.ClientAuthType == "RequireAndVerifyClientCert" { + if tc.ClientCAFile == "" && tc.ClientCASecretRef == nil { + return fmt.Errorf("either client_ca_secret_ref or client_ca_file must be set for tls_server_config with enabled RequireAndVerifyClientCert") + } + } + } + if r.Spec.GossipConfig.TLSClientConfig != nil { + tc := r.Spec.GossipConfig.TLSClientConfig + if tc.Certs.CertFile == "" && tc.Certs.CertSecretRef == nil { + return fmt.Errorf("either cert_secret_ref or cert_file must be set for tls_client_config") + } + if tc.Certs.KeyFile == "" && tc.Certs.KeySecretRef == nil { + return fmt.Errorf("either key_secret_ref or key_file must be set for tls_client_config") + } + } + } return nil } diff --git a/api/operator/v1beta1/vmextra_types.go b/api/operator/v1beta1/vmextra_types.go index 0582c609..d655b1bc 100644 --- a/api/operator/v1beta1/vmextra_types.go +++ b/api/operator/v1beta1/vmextra_types.go @@ -899,22 +899,8 @@ func (c *TLSConfig) BuildAssetPath(prefix, name, key string) string { return fmt.Sprintf("%s_%s_%s", prefix, name, key) } -// WebserverTLSConfig defines TLS configuration for the applications webserver -type WebserverTLSConfig struct { - // ClientAuthType defines server policy for client authentication - // If you want to enable client authentication (aka mTLS), you need to use RequireAndVerifyClientCert - // Note, mTLS is supported only at enterprise version of VictoriaMetrics components - // +kubebuilder:validation:Enum=NoClientCert;RequireAndVerifyClientCert - ClientAuthType string `json:"client_auth_type,omitempty"` - - // ClientCA defines reference for secret with CA content under given key - // mutually exclusive with ClientCAFile - ClientCASecretRef *v1.SecretKeySelector `json:"client_ca_secret_ref,omitempty"` - // ClientCAFile defines path to the pre-mounted file with CA - // mutually exclusive with ClientCASecretRef - ClientCAFile string `json:"client_ca_file,omitempty"` - // Cert defines reference for secret with CA content under given key - // mutually exclusive with CertFile +// Certs defines TLS certs configuration +type Certs struct { CertSecretRef *v1.SecretKeySelector `json:"cert_secret_ref,omitempty"` // CertFile defines path to the pre-mounted file with certificate // mutually exclusive with CertSecretRef @@ -925,6 +911,23 @@ type WebserverTLSConfig struct { // KeyFile defines path to the pre-mounted file with certificate key // mutually exclusive with KeySecretRef KeyFile string `json:"key_file,omitempty"` +} + +// TLSServerConfig defines TLS configuration for the application's server +type TLSServerConfig struct { + // ClientCASecretRef defines reference for secret with CA content under given key + // mutually exclusive with ClientCAFile + ClientCASecretRef *v1.SecretKeySelector `json:"client_ca_secret_ref,omitempty"` + // ClientCAFile defines path to the pre-mounted file with CA + // mutually exclusive with ClientCASecretRef + ClientCAFile string `json:"client_ca_file,omitempty"` + // Cert defines reference for secret with CA content under given key + // mutually exclusive with CertFile + // ClientAuthType defines server policy for client authentication + // If you want to enable client authentication (aka mTLS), you need to use RequireAndVerifyClientCert + // Note, mTLS is supported only at enterprise version of VictoriaMetrics components + // +kubebuilder:validation:Enum=NoClientCert;RequireAndVerifyClientCert + ClientAuthType string `json:"client_auth_type,omitempty"` // MinVersion minimum TLS version that is acceptable. // +kubebuilder:validation:Enum=TLS10;TLS11;TLS12;TLS13 MinVersion string `json:"min_version,omitempty"` @@ -940,4 +943,23 @@ type WebserverTLSConfig struct { // PreferServerCipherSuites controls whether the server selects the // client's most preferred ciphersuite PreferServerCipherSuites bool `json:"prefer_server_cipher_suites,omitempty"` + // Certs defines cert, CA and key for TLS auth + Certs `json:",inline"` +} + +// TLSClientConfig defines TLS configuration for the application's client +type TLSClientConfig struct { + // CA defines reference for secret with CA content under given key + // mutually exclusive with CAFile + CASecretRef *v1.SecretKeySelector `json:"ca_secret_ref,omitempty"` + // CAFile defines path to the pre-mounted file with CA + // mutually exclusive with CASecretRef + CAFile string `json:"ca_file,omitempty"` + // Cert defines reference for secret with CA content under given key + // mutually exclusive with CertFile + InsecureSkipVerify bool `json:"insecure_skip_verify,omitempty"` + // ServerName indicates a name of a server + ServerName string `json:"server_name,omitempty"` + // Certs defines cert, CA and key for TLS auth + Certs `json:",inline"` } diff --git a/api/operator/v1beta1/zz_generated.deepcopy.go b/api/operator/v1beta1/zz_generated.deepcopy.go index 9febdc73..42899600 100644 --- a/api/operator/v1beta1/zz_generated.deepcopy.go +++ b/api/operator/v1beta1/zz_generated.deepcopy.go @@ -79,6 +79,31 @@ func (in *AdditionalServiceSpec) DeepCopy() *AdditionalServiceSpec { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *AlertmanagerGossipConfig) DeepCopyInto(out *AlertmanagerGossipConfig) { + *out = *in + if in.TLSServerConfig != nil { + in, out := &in.TLSServerConfig, &out.TLSServerConfig + *out = new(TLSServerConfig) + (*in).DeepCopyInto(*out) + } + if in.TLSClientConfig != nil { + in, out := &in.TLSClientConfig, &out.TLSClientConfig + *out = new(TLSClientConfig) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AlertmanagerGossipConfig. +func (in *AlertmanagerGossipConfig) DeepCopy() *AlertmanagerGossipConfig { + if in == nil { + return nil + } + out := new(AlertmanagerGossipConfig) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *AlertmanagerHTTPConfig) DeepCopyInto(out *AlertmanagerHTTPConfig) { *out = *in @@ -106,7 +131,7 @@ func (in *AlertmanagerWebConfig) DeepCopyInto(out *AlertmanagerWebConfig) { *out = *in if in.TLSServerConfig != nil { in, out := &in.TLSServerConfig, &out.TLSServerConfig - *out = new(WebserverTLSConfig) + *out = new(TLSServerConfig) (*in).DeepCopyInto(*out) } if in.HTTPServerConfig != nil { @@ -290,6 +315,31 @@ func (in *CRDRef) DeepCopy() *CRDRef { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Certs) DeepCopyInto(out *Certs) { + *out = *in + if in.CertSecretRef != nil { + in, out := &in.CertSecretRef, &out.CertSecretRef + *out = new(v1.SecretKeySelector) + (*in).DeepCopyInto(*out) + } + if in.KeySecretRef != nil { + in, out := &in.KeySecretRef, &out.KeySecretRef + *out = new(v1.SecretKeySelector) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Certs. +func (in *Certs) DeepCopy() *Certs { + if in == nil { + return nil + } + out := new(Certs) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ConfigMapKeyReference) DeepCopyInto(out *ConfigMapKeyReference) { *out = *in @@ -2602,6 +2652,27 @@ func (in *SubRoute) DeepCopy() *SubRoute { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TLSClientConfig) DeepCopyInto(out *TLSClientConfig) { + *out = *in + if in.CASecretRef != nil { + in, out := &in.CASecretRef, &out.CASecretRef + *out = new(v1.SecretKeySelector) + (*in).DeepCopyInto(*out) + } + in.Certs.DeepCopyInto(&out.Certs) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TLSClientConfig. +func (in *TLSClientConfig) DeepCopy() *TLSClientConfig { + if in == nil { + return nil + } + out := new(TLSClientConfig) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *TLSConfig) DeepCopyInto(out *TLSConfig) { *out = *in @@ -2639,6 +2710,37 @@ func (in *TLSConfigValidationError) DeepCopy() *TLSConfigValidationError { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TLSServerConfig) DeepCopyInto(out *TLSServerConfig) { + *out = *in + if in.ClientCASecretRef != nil { + in, out := &in.ClientCASecretRef, &out.ClientCASecretRef + *out = new(v1.SecretKeySelector) + (*in).DeepCopyInto(*out) + } + if in.CipherSuites != nil { + in, out := &in.CipherSuites, &out.CipherSuites + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.CurvePreferences != nil { + in, out := &in.CurvePreferences, &out.CurvePreferences + *out = make([]string, len(*in)) + copy(*out, *in) + } + in.Certs.DeepCopyInto(&out.Certs) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TLSServerConfig. +func (in *TLSServerConfig) DeepCopy() *TLSServerConfig { + if in == nil { + return nil + } + out := new(TLSServerConfig) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *TargetEndpoint) DeepCopyInto(out *TargetEndpoint) { *out = *in @@ -4350,6 +4452,11 @@ func (in *VMAlertmanagerSpec) DeepCopyInto(out *VMAlertmanagerSpec) { *out = new(AlertmanagerWebConfig) (*in).DeepCopyInto(*out) } + if in.GossipConfig != nil { + in, out := &in.GossipConfig, &out.GossipConfig + *out = new(AlertmanagerGossipConfig) + (*in).DeepCopyInto(*out) + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VMAlertmanagerSpec. @@ -6908,43 +7015,3 @@ func (in *WebhookConfig) DeepCopy() *WebhookConfig { in.DeepCopyInto(out) return out } - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *WebserverTLSConfig) DeepCopyInto(out *WebserverTLSConfig) { - *out = *in - if in.ClientCASecretRef != nil { - in, out := &in.ClientCASecretRef, &out.ClientCASecretRef - *out = new(v1.SecretKeySelector) - (*in).DeepCopyInto(*out) - } - if in.CertSecretRef != nil { - in, out := &in.CertSecretRef, &out.CertSecretRef - *out = new(v1.SecretKeySelector) - (*in).DeepCopyInto(*out) - } - if in.KeySecretRef != nil { - in, out := &in.KeySecretRef, &out.KeySecretRef - *out = new(v1.SecretKeySelector) - (*in).DeepCopyInto(*out) - } - if in.CipherSuites != nil { - in, out := &in.CipherSuites, &out.CipherSuites - *out = make([]string, len(*in)) - copy(*out, *in) - } - if in.CurvePreferences != nil { - in, out := &in.CurvePreferences, &out.CurvePreferences - *out = make([]string, len(*in)) - copy(*out, *in) - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WebserverTLSConfig. -func (in *WebserverTLSConfig) DeepCopy() *WebserverTLSConfig { - if in == nil { - return nil - } - out := new(WebserverTLSConfig) - in.DeepCopyInto(out) - return out -} diff --git a/config/crd/overlay/crd.yaml b/config/crd/overlay/crd.yaml index e6dff301..dd2dfa4c 100644 --- a/config/crd/overlay/crd.yaml +++ b/config/crd/overlay/crd.yaml @@ -8123,6 +8123,269 @@ spec: type: object x-kubernetes-preserve-unknown-fields: true type: array + gossipConfig: + description: GossipConfig defines gossip TLS configuration for Alertmanager + cluster + properties: + tls_client_config: + description: TLSClientConfig defines client TLS configuration + for alertmanager + properties: + ca_file: + description: |- + CAFile defines path to the pre-mounted file with CA + mutually exclusive with CASecretRef + type: string + ca_secret_ref: + description: |- + CA defines reference for secret with CA content under given key + mutually exclusive with CAFile + properties: + key: + description: The key of the secret to select from. Must + be a valid secret key. + type: string + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + TODO: Add other useful fields. apiVersion, kind, uid? + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Drop `kubebuilder:default` when controller-gen doesn't need it https://github.com/kubernetes-sigs/kubebuilder/issues/3896. + type: string + optional: + description: Specify whether the Secret or its key must + be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + cert_file: + description: |- + CertFile defines path to the pre-mounted file with certificate + mutually exclusive with CertSecretRef + type: string + cert_secret_ref: + description: SecretKeySelector selects a key of a Secret. + properties: + key: + description: The key of the secret to select from. Must + be a valid secret key. + type: string + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + TODO: Add other useful fields. apiVersion, kind, uid? + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Drop `kubebuilder:default` when controller-gen doesn't need it https://github.com/kubernetes-sigs/kubebuilder/issues/3896. + type: string + optional: + description: Specify whether the Secret or its key must + be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + insecure_skip_verify: + description: |- + Cert defines reference for secret with CA content under given key + mutually exclusive with CertFile + type: boolean + key_file: + description: |- + KeyFile defines path to the pre-mounted file with certificate key + mutually exclusive with KeySecretRef + type: string + key_secret_ref: + description: |- + Key defines reference for secret with certificate key content under given key + mutually exclusive with KeyFile + properties: + key: + description: The key of the secret to select from. Must + be a valid secret key. + type: string + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + TODO: Add other useful fields. apiVersion, kind, uid? + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Drop `kubebuilder:default` when controller-gen doesn't need it https://github.com/kubernetes-sigs/kubebuilder/issues/3896. + type: string + optional: + description: Specify whether the Secret or its key must + be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + server_name: + description: ServerName indicates a name of a server + type: string + type: object + tls_server_config: + description: TLSServerConfig defines server TLS configuration + for alertmanager + properties: + cert_file: + description: |- + CertFile defines path to the pre-mounted file with certificate + mutually exclusive with CertSecretRef + type: string + cert_secret_ref: + description: SecretKeySelector selects a key of a Secret. + properties: + key: + description: The key of the secret to select from. Must + be a valid secret key. + type: string + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + TODO: Add other useful fields. apiVersion, kind, uid? + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Drop `kubebuilder:default` when controller-gen doesn't need it https://github.com/kubernetes-sigs/kubebuilder/issues/3896. + type: string + optional: + description: Specify whether the Secret or its key must + be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + cipher_suites: + description: |- + CipherSuites defines list of supported cipher suites for TLS versions up to TLS 1.2 + https://golang.org/pkg/crypto/tls/#pkg-constants + items: + type: string + type: array + client_auth_type: + description: |- + Cert defines reference for secret with CA content under given key + mutually exclusive with CertFile + ClientAuthType defines server policy for client authentication + If you want to enable client authentication (aka mTLS), you need to use RequireAndVerifyClientCert + Note, mTLS is supported only at enterprise version of VictoriaMetrics components + enum: + - NoClientCert + - RequireAndVerifyClientCert + type: string + client_ca_file: + description: |- + ClientCAFile defines path to the pre-mounted file with CA + mutually exclusive with ClientCASecretRef + type: string + client_ca_secret_ref: + description: |- + ClientCASecretRef defines reference for secret with CA content under given key + mutually exclusive with ClientCAFile + properties: + key: + description: The key of the secret to select from. Must + be a valid secret key. + type: string + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + TODO: Add other useful fields. apiVersion, kind, uid? + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Drop `kubebuilder:default` when controller-gen doesn't need it https://github.com/kubernetes-sigs/kubebuilder/issues/3896. + type: string + optional: + description: Specify whether the Secret or its key must + be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + curve_preferences: + description: |- + CurvePreferences defines elliptic curves that will be used in an ECDHE handshake, in preference order. + https://golang.org/pkg/crypto/tls/#CurveID + items: + type: string + type: array + key_file: + description: |- + KeyFile defines path to the pre-mounted file with certificate key + mutually exclusive with KeySecretRef + type: string + key_secret_ref: + description: |- + Key defines reference for secret with certificate key content under given key + mutually exclusive with KeyFile + properties: + key: + description: The key of the secret to select from. Must + be a valid secret key. + type: string + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + TODO: Add other useful fields. apiVersion, kind, uid? + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Drop `kubebuilder:default` when controller-gen doesn't need it https://github.com/kubernetes-sigs/kubebuilder/issues/3896. + type: string + optional: + description: Specify whether the Secret or its key must + be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + max_version: + description: MaxVersion maximum TLS version that is acceptable. + enum: + - TLS10 + - TLS11 + - TLS12 + - TLS13 + type: string + min_version: + description: MinVersion minimum TLS version that is acceptable. + enum: + - TLS10 + - TLS11 + - TLS12 + - TLS13 + type: string + prefer_server_cipher_suites: + description: |- + PreferServerCipherSuites controls whether the server selects the + client's most preferred ciphersuite + type: boolean + type: object + type: object hostNetwork: description: HostNetwork controls whether the pod may use the node network namespace @@ -9164,8 +9427,8 @@ spec: type: boolean type: object tls_server_config: - description: TLSServerConfig defines tls configuration for alertmanager - web server + description: TLSServerConfig defines server TLS configuration + for alertmanager properties: cert_file: description: |- @@ -9173,9 +9436,7 @@ spec: mutually exclusive with CertSecretRef type: string cert_secret_ref: - description: |- - Cert defines reference for secret with CA content under given key - mutually exclusive with CertFile + description: SecretKeySelector selects a key of a Secret. properties: key: description: The key of the secret to select from. Must @@ -9209,6 +9470,8 @@ spec: type: array client_auth_type: description: |- + Cert defines reference for secret with CA content under given key + mutually exclusive with CertFile ClientAuthType defines server policy for client authentication If you want to enable client authentication (aka mTLS), you need to use RequireAndVerifyClientCert Note, mTLS is supported only at enterprise version of VictoriaMetrics components @@ -9223,7 +9486,7 @@ spec: type: string client_ca_secret_ref: description: |- - ClientCA defines reference for secret with CA content under given key + ClientCASecretRef defines reference for secret with CA content under given key mutually exclusive with ClientCAFile properties: key: diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index a9d43c03..29c8964d 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -17,6 +17,7 @@ aliases: - [operator](./README.md): properly release `PodDisruptionBudget` object finalizer. Previously it could be kept due to typo. See this [issue](https://github.com/VictoriaMetrics/operator/issues/1036) for details. - [operator](./README.md): refactors finalizers usage. Simplifies finalizer manipulation with helper functions - [vmalertmanager](./api.md#vmalertmanager): adds `webConfig` that simplifies tls configuration for alertmanager and allows to properly build probes and access urls for alertmanager. See this [issue](https://github.com/VictoriaMetrics/operator/issues/994) for details. +- [vmalertmanager](./api.md#vmalertmanager): adds `gossipConfig` to setup client and server TLS configuration for alertmanager. ## [v0.46.4](https://github.com/VictoriaMetrics/operator/releases/tag/v0.46.4) - 9 Jul 2024 diff --git a/docs/api.md b/docs/api.md index 6a46a981..9905abfb 100644 --- a/docs/api.md +++ b/docs/api.md @@ -89,6 +89,23 @@ _Appears in:_ | `useAsDefault` | UseAsDefault applies changes from given service definition to the main object Service
Changing from headless service to clusterIP or loadbalancer may break cross-component communication | _boolean_ | false | +#### AlertmanagerGossipConfig + + + +AlertmanagerGossipConfig defines Gossip TLS configuration for alertmanager + + + +_Appears in:_ +- [VMAlertmanagerSpec](#vmalertmanagerspec) + +| Field | Description | Scheme | Required | +| --- | --- | --- | --- | +| `tls_client_config` | TLSClientConfig defines client TLS configuration for alertmanager | _[TLSClientConfig](#tlsclientconfig)_ | true | +| `tls_server_config` | TLSServerConfig defines server TLS configuration for alertmanager | _[TLSServerConfig](#tlsserverconfig)_ | true | + + #### AlertmanagerHTTPConfig @@ -121,7 +138,7 @@ _Appears in:_ | --- | --- | --- | --- | | `basic_auth_users` | BasicAuthUsers Usernames and hashed passwords that have full access to the web server
Passwords must be hashed with bcrypt | _object (keys:string, values:string)_ | true | | `http_server_config` | HTTPServerConfig defines http server configuration for alertmanager web server | _[AlertmanagerHTTPConfig](#alertmanagerhttpconfig)_ | true | -| `tls_server_config` | TLSServerConfig defines tls configuration for alertmanager web server | _[WebserverTLSConfig](#webservertlsconfig)_ | true | +| `tls_server_config` | TLSServerConfig defines server TLS configuration for alertmanager | _[TLSServerConfig](#tlsserverconfig)_ | true | #### ArbitraryFSAccessThroughSMsConfig @@ -298,6 +315,26 @@ _Appears in:_ | `namespace` | Namespace target CRD object namespace. | _string_ | true | +#### Certs + + + +Certs defines TLS certs configuration + + + +_Appears in:_ +- [TLSClientConfig](#tlsclientconfig) +- [TLSServerConfig](#tlsserverconfig) + +| Field | Description | Scheme | Required | +| --- | --- | --- | --- | +| `cert_file` | CertFile defines path to the pre-mounted file with certificate
mutually exclusive with CertSecretRef | _string_ | true | +| `cert_secret_ref` | | _[SecretKeySelector](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#secretkeyselector-v1-core)_ | true | +| `key_file` | KeyFile defines path to the pre-mounted file with certificate key
mutually exclusive with KeySecretRef | _string_ | true | +| `key_secret_ref` | Key defines reference for secret with certificate key content under given key
mutually exclusive with KeyFile | _[SecretKeySelector](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#secretkeyselector-v1-core)_ | true | + + #### ConfigMapKeyReference @@ -1873,6 +1910,29 @@ _Appears in:_ +#### TLSClientConfig + + + +TLSClientConfig defines TLS configuration for the application's client + + + +_Appears in:_ +- [AlertmanagerGossipConfig](#alertmanagergossipconfig) + +| Field | Description | Scheme | Required | +| --- | --- | --- | --- | +| `ca_file` | CAFile defines path to the pre-mounted file with CA
mutually exclusive with CASecretRef | _string_ | true | +| `ca_secret_ref` | CA defines reference for secret with CA content under given key
mutually exclusive with CAFile | _[SecretKeySelector](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#secretkeyselector-v1-core)_ | true | +| `cert_file` | CertFile defines path to the pre-mounted file with certificate
mutually exclusive with CertSecretRef | _string_ | true | +| `cert_secret_ref` | | _[SecretKeySelector](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#secretkeyselector-v1-core)_ | true | +| `insecure_skip_verify` | Cert defines reference for secret with CA content under given key
mutually exclusive with CertFile | _boolean_ | true | +| `key_file` | KeyFile defines path to the pre-mounted file with certificate key
mutually exclusive with KeySecretRef | _string_ | true | +| `key_secret_ref` | Key defines reference for secret with certificate key content under given key
mutually exclusive with KeyFile | _[SecretKeySelector](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#secretkeyselector-v1-core)_ | true | +| `server_name` | ServerName indicates a name of a server | _string_ | true | + + #### TLSConfig @@ -1922,6 +1982,34 @@ _Appears in:_ +#### TLSServerConfig + + + +TLSServerConfig defines TLS configuration for the application's server + + + +_Appears in:_ +- [AlertmanagerGossipConfig](#alertmanagergossipconfig) +- [AlertmanagerWebConfig](#alertmanagerwebconfig) + +| Field | Description | Scheme | Required | +| --- | --- | --- | --- | +| `cert_file` | CertFile defines path to the pre-mounted file with certificate
mutually exclusive with CertSecretRef | _string_ | true | +| `cert_secret_ref` | | _[SecretKeySelector](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#secretkeyselector-v1-core)_ | true | +| `cipher_suites` | CipherSuites defines list of supported cipher suites for TLS versions up to TLS 1.2
https://golang.org/pkg/crypto/tls/#pkg-constants | _string array_ | true | +| `client_auth_type` | Cert defines reference for secret with CA content under given key
mutually exclusive with CertFile
ClientAuthType defines server policy for client authentication
If you want to enable client authentication (aka mTLS), you need to use RequireAndVerifyClientCert
Note, mTLS is supported only at enterprise version of VictoriaMetrics components | _string_ | true | +| `client_ca_file` | ClientCAFile defines path to the pre-mounted file with CA
mutually exclusive with ClientCASecretRef | _string_ | true | +| `client_ca_secret_ref` | ClientCASecretRef defines reference for secret with CA content under given key
mutually exclusive with ClientCAFile | _[SecretKeySelector](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#secretkeyselector-v1-core)_ | true | +| `curve_preferences` | CurvePreferences defines elliptic curves that will be used in an ECDHE handshake, in preference order.
https://golang.org/pkg/crypto/tls/#CurveID | _string array_ | true | +| `key_file` | KeyFile defines path to the pre-mounted file with certificate key
mutually exclusive with KeySecretRef | _string_ | true | +| `key_secret_ref` | Key defines reference for secret with certificate key content under given key
mutually exclusive with KeyFile | _[SecretKeySelector](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#secretkeyselector-v1-core)_ | true | +| `max_version` | MaxVersion maximum TLS version that is acceptable. | _string_ | true | +| `min_version` | MinVersion minimum TLS version that is acceptable. | _string_ | true | +| `prefer_server_cipher_suites` | PreferServerCipherSuites controls whether the server selects the
client's most preferred ciphersuite | _boolean_ | true | + + #### TargetEndpoint @@ -2606,6 +2694,7 @@ _Appears in:_ | `externalURL` | ExternalURL the VMAlertmanager instances will be available under. This is
necessary to generate correct URLs. This is necessary if VMAlertmanager is not
served from root of a DNS name. | _string_ | false | | `extraArgs` | ExtraArgs that will be passed to VMAlertmanager pod
for example log.level: debug | _object (keys:string, values:string)_ | false | | `extraEnvs` | ExtraEnvs that will be added to VMAlertmanager pod | _[EnvVar](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#envvar-v1-core) array_ | false | +| `gossipConfig` | GossipConfig defines gossip TLS configuration for Alertmanager cluster | _[AlertmanagerGossipConfig](#alertmanagergossipconfig)_ | true | | `hostNetwork` | HostNetwork controls whether the pod may use the node network namespace | _boolean_ | false | | `image` | Image - docker image settings for VMAlertmanager
if no specified operator uses default config version | _[Image](#image)_ | false | | `imagePullSecrets` | ImagePullSecrets An optional list of references to secrets in the same namespace
to use for pulling images from registries
see https://kubernetes.io/docs/concepts/containers/images/#referring-to-an-imagepullsecrets-on-a-pod | _[LocalObjectReference](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#localobjectreference-v1-core) array_ | false | @@ -3721,30 +3810,3 @@ _Appears in:_ | `url_secret` | URLSecret defines secret name and key at the CRD namespace.
It must contain the webhook URL.
one of `urlSecret` and `url` must be defined. | _[SecretKeySelector](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#secretkeyselector-v1-core)_ | false | -#### WebserverTLSConfig - - - -WebserverTLSConfig defines TLS configuration for the applications webserver - - - -_Appears in:_ -- [AlertmanagerWebConfig](#alertmanagerwebconfig) - -| Field | Description | Scheme | Required | -| --- | --- | --- | --- | -| `cert_file` | CertFile defines path to the pre-mounted file with certificate
mutually exclusive with CertSecretRef | _string_ | true | -| `cert_secret_ref` | Cert defines reference for secret with CA content under given key
mutually exclusive with CertFile | _[SecretKeySelector](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#secretkeyselector-v1-core)_ | true | -| `cipher_suites` | CipherSuites defines list of supported cipher suites for TLS versions up to TLS 1.2
https://golang.org/pkg/crypto/tls/#pkg-constants | _string array_ | true | -| `client_auth_type` | ClientAuthType defines server policy for client authentication
If you want to enable client authentication (aka mTLS), you need to use RequireAndVerifyClientCert
Note, mTLS is supported only at enterprise version of VictoriaMetrics components | _string_ | true | -| `client_ca_file` | ClientCAFile defines path to the pre-mounted file with CA
mutually exclusive with ClientCASecretRef | _string_ | true | -| `client_ca_secret_ref` | ClientCA defines reference for secret with CA content under given key
mutually exclusive with ClientCAFile | _[SecretKeySelector](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#secretkeyselector-v1-core)_ | true | -| `curve_preferences` | CurvePreferences defines elliptic curves that will be used in an ECDHE handshake, in preference order.
https://golang.org/pkg/crypto/tls/#CurveID | _string array_ | true | -| `key_file` | KeyFile defines path to the pre-mounted file with certificate key
mutually exclusive with KeySecretRef | _string_ | true | -| `key_secret_ref` | Key defines reference for secret with certificate key content under given key
mutually exclusive with KeyFile | _[SecretKeySelector](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#secretkeyselector-v1-core)_ | true | -| `max_version` | MaxVersion maximum TLS version that is acceptable. | _string_ | true | -| `min_version` | MinVersion minimum TLS version that is acceptable. | _string_ | true | -| `prefer_server_cipher_suites` | PreferServerCipherSuites controls whether the server selects the
client's most preferred ciphersuite | _boolean_ | true | - - diff --git a/internal/controller/operator/factory/alertmanager/config.go b/internal/controller/operator/factory/alertmanager/config.go index 0dff7e51..22518b69 100644 --- a/internal/controller/operator/factory/alertmanager/config.go +++ b/internal/controller/operator/factory/alertmanager/config.go @@ -1316,7 +1316,138 @@ func secretSelectorToAssetKey(selector *v1.SecretKeySelector) string { return fmt.Sprintf("%s_%s", selector.Name, selector.Key) } -// builds configuration according to https://github.com/prometheus/alertmanager/blob/main/docs/https.md +// builds configuration according to https://prometheus.io/docs/alerting/latest/https/#gossip-traffic +func buildGossipConfigYAML(ctx context.Context, rclient client.Client, vmaCR *vmv1beta1.VMAlertmanager, tlsAssets map[string]string) ([]byte, error) { + if vmaCR.Spec.GossipConfig == nil { + return nil, nil + } + var cfg yaml.MapSlice + gossipCfg := vmaCR.Spec.GossipConfig + if gossipCfg.TLSServerConfig != nil { + var tlsCfg yaml.MapSlice + secretMap := make(map[string]*v1.Secret) + tlsAssetsServerDir := tlsAssetsDir + "/gossip/server/" + if gossipCfg.TLSServerConfig.ClientCASecretRef != nil { + data, err := fetchSecretValue(ctx, rclient, vmaCR.Namespace, gossipCfg.TLSServerConfig.ClientCASecretRef, secretMap) + if err != nil { + return nil, fmt.Errorf("cannot fetch secret CA value: %w", err) + } + assetKey := secretSelectorToAssetKey(gossipCfg.TLSServerConfig.ClientCASecretRef) + tlsAssets[assetKey] = string(data) + gossipCfg.TLSServerConfig.ClientCAFile = tlsAssetsServerDir + assetKey + } + if gossipCfg.TLSServerConfig.Certs.CertSecretRef != nil { + data, err := fetchSecretValue(ctx, rclient, vmaCR.Namespace, gossipCfg.TLSServerConfig.Certs.CertSecretRef, secretMap) + if err != nil { + return nil, fmt.Errorf("cannot fetch secret CA value: %w", err) + } + assetKey := secretSelectorToAssetKey(gossipCfg.TLSServerConfig.Certs.CertSecretRef) + tlsAssets[assetKey] = string(data) + gossipCfg.TLSServerConfig.Certs.CertFile = tlsAssetsServerDir + assetKey + + } + + if gossipCfg.TLSServerConfig.Certs.KeySecretRef != nil { + data, err := fetchSecretValue(ctx, rclient, vmaCR.Namespace, gossipCfg.TLSServerConfig.Certs.KeySecretRef, secretMap) + if err != nil { + return nil, fmt.Errorf("cannot fetch secret clientCA value: %w", err) + } + assetKey := secretSelectorToAssetKey(gossipCfg.TLSServerConfig.Certs.KeySecretRef) + tlsAssets[assetKey] = string(data) + gossipCfg.TLSServerConfig.Certs.KeyFile = tlsAssetsServerDir + assetKey + } + + if len(gossipCfg.TLSServerConfig.ClientCAFile) > 0 { + tlsCfg = append(tlsCfg, yaml.MapItem{Key: "client_ca_file", Value: gossipCfg.TLSServerConfig.ClientCAFile}) + } + if len(gossipCfg.TLSServerConfig.Certs.CertFile) > 0 { + tlsCfg = append(tlsCfg, yaml.MapItem{Key: "cert_file", Value: gossipCfg.TLSServerConfig.Certs.CertFile}) + } + if len(gossipCfg.TLSServerConfig.Certs.KeyFile) > 0 { + tlsCfg = append(tlsCfg, yaml.MapItem{Key: "key_file", Value: gossipCfg.TLSServerConfig.Certs.KeyFile}) + } + if len(gossipCfg.TLSServerConfig.CipherSuites) > 0 { + tlsCfg = append(tlsCfg, yaml.MapItem{Key: "cipher_suites", Value: gossipCfg.TLSServerConfig.CipherSuites}) + } + if len(gossipCfg.TLSServerConfig.CurvePreferences) > 0 { + tlsCfg = append(tlsCfg, yaml.MapItem{Key: "curve_preferences", Value: gossipCfg.TLSServerConfig.CurvePreferences}) + } + if len(gossipCfg.TLSServerConfig.ClientAuthType) > 0 { + tlsCfg = append(tlsCfg, yaml.MapItem{Key: "client_auth_type", Value: gossipCfg.TLSServerConfig.ClientAuthType}) + } + if gossipCfg.TLSServerConfig.PreferServerCipherSuites { + tlsCfg = append(tlsCfg, yaml.MapItem{Key: "prefer_server_cipher_suites", Value: gossipCfg.TLSServerConfig.PreferServerCipherSuites}) + } + if len(gossipCfg.TLSServerConfig.MaxVersion) > 0 { + tlsCfg = append(tlsCfg, yaml.MapItem{Key: "max_version", Value: gossipCfg.TLSServerConfig.MaxVersion}) + } + if len(gossipCfg.TLSServerConfig.MinVersion) > 0 { + tlsCfg = append(tlsCfg, yaml.MapItem{Key: "min_version", Value: gossipCfg.TLSServerConfig.MinVersion}) + } + + cfg = append(cfg, yaml.MapItem{Key: "tls_server_config", Value: tlsCfg}) + } + + if gossipCfg.TLSClientConfig != nil { + var tlsCfg yaml.MapSlice + secretMap := make(map[string]*v1.Secret) + tlsAssetsClientDir := tlsAssetsDir + "/gossip/client/" + if gossipCfg.TLSClientConfig.CASecretRef != nil { + data, err := fetchSecretValue(ctx, rclient, vmaCR.Namespace, gossipCfg.TLSClientConfig.CASecretRef, secretMap) + if err != nil { + return nil, fmt.Errorf("cannot fetch secret clientCA value: %w", err) + } + assetKey := secretSelectorToAssetKey(gossipCfg.TLSClientConfig.CASecretRef) + tlsAssets[assetKey] = string(data) + gossipCfg.TLSClientConfig.CAFile = tlsAssetsClientDir + assetKey + } + if gossipCfg.TLSClientConfig.Certs.CertSecretRef != nil { + data, err := fetchSecretValue(ctx, rclient, vmaCR.Namespace, gossipCfg.TLSClientConfig.Certs.CertSecretRef, secretMap) + if err != nil { + return nil, fmt.Errorf("cannot fetch secret clientCA value: %w", err) + } + assetKey := secretSelectorToAssetKey(gossipCfg.TLSClientConfig.Certs.CertSecretRef) + tlsAssets[assetKey] = string(data) + gossipCfg.TLSClientConfig.Certs.CertFile = tlsAssetsClientDir + assetKey + + } + + if gossipCfg.TLSClientConfig.Certs.KeySecretRef != nil { + data, err := fetchSecretValue(ctx, rclient, vmaCR.Namespace, gossipCfg.TLSClientConfig.Certs.KeySecretRef, secretMap) + if err != nil { + return nil, fmt.Errorf("cannot fetch secret clientCA value: %w", err) + } + assetKey := secretSelectorToAssetKey(gossipCfg.TLSClientConfig.Certs.KeySecretRef) + tlsAssets[assetKey] = string(data) + gossipCfg.TLSClientConfig.Certs.KeyFile = tlsAssetsClientDir + assetKey + } + + if len(gossipCfg.TLSClientConfig.CAFile) > 0 { + tlsCfg = append(tlsCfg, yaml.MapItem{Key: "ca_file", Value: gossipCfg.TLSClientConfig.CAFile}) + } + if len(gossipCfg.TLSClientConfig.Certs.CertFile) > 0 { + tlsCfg = append(tlsCfg, yaml.MapItem{Key: "cert_file", Value: gossipCfg.TLSClientConfig.Certs.CertFile}) + } + if len(gossipCfg.TLSClientConfig.Certs.KeyFile) > 0 { + tlsCfg = append(tlsCfg, yaml.MapItem{Key: "key_file", Value: gossipCfg.TLSClientConfig.Certs.KeyFile}) + } + if gossipCfg.TLSClientConfig.InsecureSkipVerify { + tlsCfg = append(tlsCfg, yaml.MapItem{Key: "insecure_skip_verify", Value: gossipCfg.TLSClientConfig.InsecureSkipVerify}) + } + if len(gossipCfg.TLSClientConfig.ServerName) > 0 { + tlsCfg = append(tlsCfg, yaml.MapItem{Key: "server_name", Value: gossipCfg.TLSClientConfig.ServerName}) + } + + cfg = append(cfg, yaml.MapItem{Key: "tls_client_config", Value: tlsCfg}) + } + data, err := yaml.Marshal(cfg) + if err != nil { + return nil, fmt.Errorf("cannot serialize alertmanager gossip config as yaml: %w", err) + } + return data, nil +} + +// builds configuration according to https://prometheus.io/docs/alerting/latest/https/#http-traffic func buildWebServerConfigYAML(ctx context.Context, rclient client.Client, vmaCR *vmv1beta1.VMAlertmanager, tlsAssets map[string]string) ([]byte, error) { if vmaCR.Spec.WebConfig == nil { return nil, nil @@ -1339,44 +1470,45 @@ func buildWebServerConfigYAML(ctx context.Context, rclient client.Client, vmaCR if webCfg.TLSServerConfig != nil { var tlsCfg yaml.MapSlice secretMap := make(map[string]*v1.Secret) + tlsAssetsServerDir := tlsAssetsDir + "/web/server/" if webCfg.TLSServerConfig.ClientCASecretRef != nil { data, err := fetchSecretValue(ctx, rclient, vmaCR.Namespace, webCfg.TLSServerConfig.ClientCASecretRef, secretMap) if err != nil { - return nil, fmt.Errorf("cannot fetch secret clientCA value: %w", err) + return nil, fmt.Errorf("cannot fetch secret CA value: %w", err) } assetKey := secretSelectorToAssetKey(webCfg.TLSServerConfig.ClientCASecretRef) tlsAssets[assetKey] = string(data) - webCfg.TLSServerConfig.ClientCAFile = tlsAssetsDir + "/" + assetKey + webCfg.TLSServerConfig.ClientCAFile = tlsAssetsServerDir + assetKey } - if webCfg.TLSServerConfig.CertSecretRef != nil { - data, err := fetchSecretValue(ctx, rclient, vmaCR.Namespace, webCfg.TLSServerConfig.CertSecretRef, secretMap) + if webCfg.TLSServerConfig.Certs.CertSecretRef != nil { + data, err := fetchSecretValue(ctx, rclient, vmaCR.Namespace, webCfg.TLSServerConfig.Certs.CertSecretRef, secretMap) if err != nil { - return nil, fmt.Errorf("cannot fetch secret clientCA value: %w", err) + return nil, fmt.Errorf("cannot fetch secret CA value: %w", err) } - assetKey := secretSelectorToAssetKey(webCfg.TLSServerConfig.CertSecretRef) + assetKey := secretSelectorToAssetKey(webCfg.TLSServerConfig.Certs.CertSecretRef) tlsAssets[assetKey] = string(data) - webCfg.TLSServerConfig.CertFile = tlsAssetsDir + "/" + assetKey + webCfg.TLSServerConfig.Certs.CertFile = tlsAssetsServerDir + assetKey } - if webCfg.TLSServerConfig.KeySecretRef != nil { - data, err := fetchSecretValue(ctx, rclient, vmaCR.Namespace, webCfg.TLSServerConfig.KeySecretRef, secretMap) + if webCfg.TLSServerConfig.Certs.KeySecretRef != nil { + data, err := fetchSecretValue(ctx, rclient, vmaCR.Namespace, webCfg.TLSServerConfig.Certs.KeySecretRef, secretMap) if err != nil { return nil, fmt.Errorf("cannot fetch secret clientCA value: %w", err) } - assetKey := secretSelectorToAssetKey(webCfg.TLSServerConfig.KeySecretRef) + assetKey := secretSelectorToAssetKey(webCfg.TLSServerConfig.Certs.KeySecretRef) tlsAssets[assetKey] = string(data) - webCfg.TLSServerConfig.KeyFile = tlsAssetsDir + "/" + assetKey + webCfg.TLSServerConfig.Certs.KeyFile = tlsAssetsServerDir + assetKey } if len(webCfg.TLSServerConfig.ClientCAFile) > 0 { tlsCfg = append(tlsCfg, yaml.MapItem{Key: "client_ca_file", Value: webCfg.TLSServerConfig.ClientCAFile}) } - if len(webCfg.TLSServerConfig.CertFile) > 0 { - tlsCfg = append(tlsCfg, yaml.MapItem{Key: "cert_file", Value: webCfg.TLSServerConfig.CertFile}) + if len(webCfg.TLSServerConfig.Certs.CertFile) > 0 { + tlsCfg = append(tlsCfg, yaml.MapItem{Key: "cert_file", Value: webCfg.TLSServerConfig.Certs.CertFile}) } - if len(webCfg.TLSServerConfig.KeyFile) > 0 { - tlsCfg = append(tlsCfg, yaml.MapItem{Key: "key_file", Value: webCfg.TLSServerConfig.KeyFile}) + if len(webCfg.TLSServerConfig.Certs.KeyFile) > 0 { + tlsCfg = append(tlsCfg, yaml.MapItem{Key: "key_file", Value: webCfg.TLSServerConfig.Certs.KeyFile}) } if len(webCfg.TLSServerConfig.CipherSuites) > 0 { tlsCfg = append(tlsCfg, yaml.MapItem{Key: "cipher_suites", Value: webCfg.TLSServerConfig.CipherSuites}) @@ -1399,6 +1531,7 @@ func buildWebServerConfigYAML(ctx context.Context, rclient client.Client, vmaCR cfg = append(cfg, yaml.MapItem{Key: "tls_server_config", Value: tlsCfg}) } + if len(webCfg.BasicAuthUsers) > 0 { cfg = append(cfg, yaml.MapItem{Key: "basic_auth_users", Value: orderedYAMLMAp(webCfg.BasicAuthUsers)}) } diff --git a/internal/controller/operator/factory/alertmanager/config_test.go b/internal/controller/operator/factory/alertmanager/config_test.go index a363df3a..dcbf077f 100644 --- a/internal/controller/operator/factory/alertmanager/config_test.go +++ b/internal/controller/operator/factory/alertmanager/config_test.go @@ -1506,11 +1506,29 @@ func TestBuildWebConfig(t *testing.T) { Name: "web-cfg", }, Spec: vmv1beta1.VMAlertmanagerSpec{ + GossipConfig: &vmv1beta1.AlertmanagerGossipConfig{ + TLSClientConfig: &vmv1beta1.TLSClientConfig{ + CAFile: "/etc/client/client_ca", + Certs: vmv1beta1.Certs{ + CertFile: "/etc/client/cert.pem", + KeyFile: "/etc/client/cert.key", + }, + }, + TLSServerConfig: &vmv1beta1.TLSServerConfig{ + ClientCAFile: "/etc/server/client_ca", + Certs: vmv1beta1.Certs{ + CertFile: "/etc/server/cert.pem", + KeyFile: "/etc/server/cert.key", + }, + }, + }, WebConfig: &vmv1beta1.AlertmanagerWebConfig{ - TLSServerConfig: &vmv1beta1.WebserverTLSConfig{ - ClientCAFile: "/etc/client_ca", - CertFile: "/etc/cert.pem", - KeyFile: "/etc/cert.key", + TLSServerConfig: &vmv1beta1.TLSServerConfig{ + ClientCAFile: "/etc/server/client_ca", + Certs: vmv1beta1.Certs{ + CertFile: "/etc/server/cert.pem", + KeyFile: "/etc/server/cert.key", + }, }, HTTPServerConfig: &vmv1beta1.AlertmanagerHTTPConfig{ HTTP2: true, @@ -1526,9 +1544,9 @@ func TestBuildWebConfig(t *testing.T) { h-1: v-1 h-2: v-2 tls_server_config: - client_ca_file: /etc/client_ca - cert_file: /etc/cert.pem - key_file: /etc/cert.key + client_ca_file: /etc/server/client_ca + cert_file: /etc/server/cert.pem + key_file: /etc/server/cert.key `, }, { @@ -1542,18 +1560,20 @@ tls_server_config: }, Spec: vmv1beta1.VMAlertmanagerSpec{ WebConfig: &vmv1beta1.AlertmanagerWebConfig{ - TLSServerConfig: &vmv1beta1.WebserverTLSConfig{ + TLSServerConfig: &vmv1beta1.TLSServerConfig{ ClientCASecretRef: &v1.SecretKeySelector{ Key: "client_ca", LocalObjectReference: v1.LocalObjectReference{Name: "tls-secret"}, }, - CertSecretRef: &v1.SecretKeySelector{ - Key: "cert", - LocalObjectReference: v1.LocalObjectReference{Name: "tls-secret"}, - }, - KeySecretRef: &v1.SecretKeySelector{ - Key: "key", - LocalObjectReference: v1.LocalObjectReference{Name: "tls-secret-key"}, + Certs: vmv1beta1.Certs{ + CertSecretRef: &v1.SecretKeySelector{ + Key: "cert", + LocalObjectReference: v1.LocalObjectReference{Name: "tls-secret"}, + }, + KeySecretRef: &v1.SecretKeySelector{ + Key: "key", + LocalObjectReference: v1.LocalObjectReference{Name: "tls-secret-key"}, + }, }, }, HTTPServerConfig: &vmv1beta1.AlertmanagerHTTPConfig{ @@ -1591,9 +1611,9 @@ tls_server_config: h-1: v-1 h-2: v-2 tls_server_config: - client_ca_file: /etc/alertmanager/tls_assets/tls-secret_client_ca - cert_file: /etc/alertmanager/tls_assets/tls-secret_cert - key_file: /etc/alertmanager/tls_assets/tls-secret-key_key + client_ca_file: /etc/alertmanager/tls_assets/web/server/tls-secret_client_ca + cert_file: /etc/alertmanager/tls_assets/web/server/tls-secret_cert + key_file: /etc/alertmanager/tls_assets/web/server/tls-secret-key_key `, }, } @@ -1609,3 +1629,68 @@ tls_server_config: }) } } + +func TestBuildGossipConfig(t *testing.T) { + type args struct { + ctx context.Context + vmaCR vmv1beta1.VMAlertmanager + } + tests := []struct { + name string + args args + predefinedObjects []runtime.Object + want string + wantErr bool + }{ + { + name: "tls secrets", + args: args{ + ctx: context.Background(), + vmaCR: vmv1beta1.VMAlertmanager{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "test", + Name: "web-cfg", + }, + Spec: vmv1beta1.VMAlertmanagerSpec{ + GossipConfig: &vmv1beta1.AlertmanagerGossipConfig{ + TLSClientConfig: &vmv1beta1.TLSClientConfig{ + CAFile: "/etc/client/client_ca", + Certs: vmv1beta1.Certs{ + CertFile: "/etc/client/cert.pem", + KeyFile: "/etc/client/cert.key", + }, + }, + TLSServerConfig: &vmv1beta1.TLSServerConfig{ + ClientCAFile: "/etc/server/client_ca", + Certs: vmv1beta1.Certs{ + CertFile: "/etc/server/cert.pem", + KeyFile: "/etc/server/cert.key", + }, + }, + }, + }, + }, + }, + want: `tls_server_config: + client_ca_file: /etc/server/client_ca + cert_file: /etc/server/cert.pem + key_file: /etc/server/cert.key +tls_client_config: + ca_file: /etc/client/client_ca + cert_file: /etc/client/cert.pem + key_file: /etc/client/cert.key +`, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + fclient := k8stools.GetTestClientWithObjects(tt.predefinedObjects) + tlsAssets := make(map[string]string) + cfg, err := buildGossipConfigYAML(tt.args.ctx, fclient, &tt.args.vmaCR, tlsAssets) + if (err != nil) != tt.wantErr { + t.Fatalf("unexpected error: %q", err) + } + assert.Equal(t, tt.want, string(cfg)) + }) + } +} diff --git a/internal/controller/operator/factory/alertmanager/statefulset.go b/internal/controller/operator/factory/alertmanager/statefulset.go index 49152063..d8ae70c5 100644 --- a/internal/controller/operator/factory/alertmanager/statefulset.go +++ b/internal/controller/operator/factory/alertmanager/statefulset.go @@ -32,6 +32,7 @@ const ( defaultRetention = "120h" alertmanagerSecretConfigKey = "alertmanager.yaml" webserverConfigKey = "webserver_config.yaml" + gossipConfigKey = "gossip_config.yaml" alertmanagerConfDir = "/etc/alertmanager/config" alertmanagerConfFile = alertmanagerConfDir + "/alertmanager.yaml" tlsAssetsDir = "/etc/alertmanager/tls_assets" @@ -161,6 +162,9 @@ func makeStatefulSetSpec(cr *vmv1beta1.VMAlertmanager, c *config.BaseOperatorCon if cr.Spec.WebConfig != nil { amArgs = append(amArgs, fmt.Sprintf("--web.config.file=%s/%s", tlsAssetsDir, webserverConfigKey)) } + if cr.Spec.GossipConfig != nil { + amArgs = append(amArgs, fmt.Sprintf("--cluster.tls-config=%s/%s", tlsAssetsDir, gossipConfigKey)) + } if *cr.Spec.ReplicaCount == 1 { amArgs = append(amArgs, "--cluster.listen-address=") @@ -503,6 +507,11 @@ func createDefaultAMConfig(ctx context.Context, cr *vmv1beta1.VMAlertmanager, rc return fmt.Errorf("cannot build webserver config: %w", err) } + gossipCfg, err := buildGossipConfigYAML(ctx, rclient, cr, tlsAssets) + if err != nil { + return fmt.Errorf("cannot build gossip config: %w", err) + } + // apply default config to be able just start alertmanager if len(alertmananagerConfig) == 0 { alertmananagerConfig = []byte(defaultAMConfig) @@ -535,6 +544,9 @@ func createDefaultAMConfig(ctx context.Context, cr *vmv1beta1.VMAlertmanager, rc if cr.Spec.WebConfig != nil { newAMSecretConfig.Data[webserverConfigKey] = webCfg } + if cr.Spec.GossipConfig != nil { + newAMSecretConfig.Data[gossipConfigKey] = gossipCfg + } for assetKey, assetValue := range tlsAssets { newAMSecretConfig.Data[assetKey] = []byte(assetValue)