Skip to content

Commit 0daedd1

Browse files
xrlclaude
andcommitted
Merge remote-tracking branch 'fork/pr/T6-tls-e2e' into downstream/integration
Brings the etcd-io#376 TLS reshape (clientEndpointForOrdinalIndex, etcdutils.ClusterHealth, per-surface TLS args) plus T5 and T6 e2e coverage. Cert-block conflict (T0 vs T5) resolved to log + Recorder.Eventf(reasonClientCertificateError) + requeue. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Signed-off-by: Xavier Lange <xrlange@gmail.com>
2 parents 12b2cea + af739f7 commit 0daedd1

19 files changed

Lines changed: 2963 additions & 296 deletions

api/v1alpha1/etcdcluster_types.go

Lines changed: 76 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,17 @@ type EtcdClusterSpec struct {
4343
Version string `json:"version"`
4444
// StorageSpec is the name of the StorageSpec to use for the etcd cluster. If not provided, then each POD just uses the temporary storage inside the container.
4545
StorageSpec *StorageSpec `json:"storageSpec,omitempty"`
46-
// TLS is the TLS certificate configuration to use for the etcd cluster and etcd operator.
47-
TLS *TLSCertificate `json:"tls,omitempty"`
46+
// TLS configures etcd's two independent TLS surfaces (peer and client/server).
47+
// Each surface is optional and configured fully independently; a nil surface
48+
// means that surface is served/dialed in cleartext. When TLS itself is nil, the
49+
// entire cluster (peer + client + operator client) is cleartext, byte-identical
50+
// to a TLS-free deployment.
51+
//
52+
// TLS is effectively create-time: it flows into the pod template and cert mounts.
53+
// Toggling it on (or off) on a running cluster rolls the StatefulSet into a mixed
54+
// http/https membership whose peers cannot connect, dropping quorum. The supported
55+
// path is a NEW TLS cluster plus data migration, not an in-place flip.
56+
TLS *EtcdClusterTLS `json:"tls,omitempty"`
4857
// etcd configuration options are passed as command line arguments to the etcd container, refer to etcd documentation for configuration options applicable for the version of etcd being used.
4958
EtcdOptions []string `json:"etcdOptions,omitempty"`
5059
// PodTemplate is the pod template to use for the etcd cluster.
@@ -68,9 +77,64 @@ type PodMetadata struct {
6877
Labels map[string]string `json:"labels,omitempty"`
6978
}
7079

71-
type TLSCertificate struct {
72-
Provider string `json:"provider,omitempty"` // Defaults to Auto provider if not present
80+
// EtcdClusterTLS configures etcd's two independent TLS surfaces. Each surface is
81+
// optional; a nil surface means that surface is served/dialed in cleartext (http).
82+
// The two surfaces are configured fully independently -- different providers,
83+
// issuers, and client-cert-auth policy are allowed and expected. Both surfaces nil
84+
// is legal and means fully-cleartext (today's default); it is intentional, not an
85+
// error, so there is no "at least one surface" validation.
86+
type EtcdClusterTLS struct {
87+
// Peer configures etcd<->etcd (peer) TLS. When nil, peer traffic is cleartext.
88+
// A configured peer surface REQUIRES a CA-capable issuer shared by all members
89+
// so members can mutually verify; a self-signed *leaf* issuer cannot form a
90+
// multi-member cluster. (CA-capability lives on the cert-manager Issuer object,
91+
// not on this spec, so that check is enforced at reconcile time, not via CEL.)
92+
// +optional
93+
Peer *TLSSurface `json:"peer,omitempty"`
94+
95+
// Client configures client->etcd (server) TLS AND, transitively, the operator's
96+
// own etcd client identity (the operator authenticates to etcd as a client).
97+
// When nil, client traffic is cleartext and the operator dials cleartext.
98+
// +optional
99+
Client *TLSSurface `json:"client,omitempty"`
100+
}
101+
102+
// TLSSurface is the full, independent TLS configuration for ONE surface (peer or
103+
// client). It carries its own provider, provider config (issuer), and mutual
104+
// client-cert-auth policy.
105+
//
106+
// The two XValidation rules below are the apply-time anti-misconfiguration
107+
// guardrails (plan Decision 2.1/2.2): they reject incoherent provider/config
108+
// combinations and mTLS-without-a-resolvable-CA at the API server, so a user
109+
// "cannot misconfigure" these from the spec alone. Rules that require reading
110+
// cluster objects (issuer existence, peer CA-capability, client/server CA match)
111+
// cannot be expressed in CEL and are enforced at reconcile time instead (plan
112+
// Decision 2.5-2.7, Decision 3) -- see validateTLSSurface and the cert-manager
113+
// provider's validateCertificateConfig.
114+
//
115+
// +kubebuilder:validation:XValidation:rule="self.provider != 'cert-manager' || has(self.providerCfg.certManagerCfg)",message="provider 'cert-manager' requires providerCfg.certManagerCfg"
116+
// +kubebuilder:validation:XValidation:rule="self.provider == 'cert-manager' || !has(self.providerCfg.certManagerCfg)",message="providerCfg.certManagerCfg may only be set when provider is 'cert-manager'"
117+
// +kubebuilder:validation:XValidation:rule="!self.clientCertAuth || self.provider != 'cert-manager' || (has(self.providerCfg.certManagerCfg) && size(self.providerCfg.certManagerCfg.issuerName) > 0)",message="clientCertAuth requires a trusted CA: set providerCfg.certManagerCfg.issuerName"
118+
type TLSSurface struct {
119+
// Provider selects the certificate provider for THIS surface.
120+
// Defaults to "auto" when empty.
121+
// +kubebuilder:validation:Enum=auto;cert-manager
122+
// +optional
123+
Provider string `json:"provider,omitempty"`
124+
125+
// ProviderCfg is the provider-specific config for THIS surface.
126+
// +optional
73127
ProviderCfg ProviderConfig `json:"providerCfg,omitempty"`
128+
129+
// ClientCertAuth toggles mutual cert auth for THIS surface (etcd's
130+
// --client-cert-auth for the client surface, --peer-client-cert-auth for the
131+
// peer surface). Defaults to true (mTLS). Set false to serve server-only TLS
132+
// where clients authenticate by other means (password/token). When true with
133+
// the cert-manager provider a trusted CA (issuerName) is REQUIRED, enforced by
134+
// the XValidation rule above.
135+
// +kubebuilder:default=true
136+
// +optional
137+
ClientCertAuth *bool `json:"clientCertAuth,omitempty"`
74138
}
75139

76140
type ProviderConfig struct {
@@ -109,13 +173,6 @@ type CommonConfig struct {
109173
// and 365d for auto as per: https://github.com/etcd-io/etcd/blob/b87bc1c3a275d7d4904f4d201b963a2de2264f0d/client/pkg/transport/listener.go#L275
110174
// +optional
111175
ValidityDuration string `json:"validityDuration,omitempty"`
112-
113-
// CABundleSecret is the expected secret name with CABundle present. It's used
114-
// by each etcd POD to verify TLS communications with its peers or clients. If it isn't
115-
// provided, the CA included in the secret generated by certificate provider will be
116-
// used instead if present; otherwise, there is no way to verify TLS communications.
117-
// +optional
118-
CABundleSecret string `json:"caBundleSecret,omitempty"`
119176
}
120177

121178
type ProviderAutoConfig struct {
@@ -127,11 +184,18 @@ type ProviderCertManagerConfig struct {
127184
// CommonConfig is the struct of common fields required to create a certificate
128185
CommonConfig `json:",inline"`
129186

130-
// IssuerKind is the expected kind of Issuer, either "ClusterIssuer" or "Issuer"
187+
// IssuerKind is the expected kind of Issuer, either "ClusterIssuer" or "Issuer".
188+
// +kubebuilder:validation:Enum=Issuer;ClusterIssuer
131189
IssuerKind string `json:"issuerKind"`
132190

133191
// IssuerName is the expected name of Issuer required to issue a certificate
134192
IssuerName string `json:"issuerName"`
193+
194+
// IssuerGroup is the API group of the issuer referenced by IssuerKind/IssuerName.
195+
// Empty defaults to "cert-manager.io". Set this to target issuers served by an
196+
// external/intermediate issuer group (e.g. an out-of-tree CA controller).
197+
// +optional
198+
IssuerGroup string `json:"issuerGroup,omitempty"`
135199
}
136200

137201
// EtcdClusterStatus defines the observed state of EtcdCluster.

api/v1alpha1/zz_generated.deepcopy.go

Lines changed: 36 additions & 6 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)