@@ -58,6 +58,22 @@ const (
5858 // (validDataDirSubPath) — an annotation has no apiserver schema, so the
5959 // controller fails closed against a mount-escaping value.
6060 AnnDataDirSubPath = ReservedAnnotationPrefix + "data-dir-subpath"
61+
62+ // AnnPeerAutoTLS, set to "true" on an EtcdCluster, runs the peer plane
63+ // with etcd's --peer-auto-tls: per-member self-signed certs, NO shared
64+ // CA, so peer traffic is encrypted but NOT authenticated. This is a
65+ // migration-only knob etcd-migrate stamps when adopting a legacy cluster
66+ // that ran the previous operator's unconditional --peer-auto-tls default
67+ // (no CA exists to do real mTLS, so a strict-mTLS replacement could never
68+ // rejoin the still-auto-tls members). Unlike AnnHeadlessServiceName /
69+ // AnnDataDirSubPath it is cluster-level and does NOT self-wipe: the
70+ // controller propagates it to every member it builds so replacement/
71+ // scaled members keep interoperating. Deliberately NOT a typed spec field
72+ // — an unauthenticated peer plane must not be a discoverable, CEL-blessed
73+ // option for new clusters; an undocumented reserved key is the lesser
74+ // footgun. Superseded by an explicit spec.tls.peer.secretRef/certManager
75+ // (real mTLS wins; precedence lives in clusterPeerAutoTLS).
76+ AnnPeerAutoTLS = ReservedAnnotationPrefix + "peer-auto-tls"
6177)
6278
6379// etcdDataDirRoot is the mount path of every member's data volume; --data-dir
@@ -136,14 +152,33 @@ func clusterClientScheme(cluster *lll.EtcdCluster) string {
136152}
137153
138154// clusterPeerScheme returns "https" when the cluster has peer TLS configured,
139- // "http" otherwise.
155+ // "http" otherwise. The legacy-compat --peer-auto-tls mode (carried on the
156+ // AnnPeerAutoTLS annotation, no typed spec.tls.peer) also serves peer over
157+ // https, so it counts too.
140158func clusterPeerScheme (cluster * lll.EtcdCluster ) string {
141159 if cluster != nil && cluster .Spec .TLS != nil && cluster .Spec .TLS .Peer != nil {
142160 return "https"
143161 }
162+ if clusterPeerAutoTLS (cluster ) {
163+ return "https"
164+ }
144165 return "http"
145166}
146167
168+ // clusterPeerAutoTLS reports whether the cluster runs the legacy-compat
169+ // --peer-auto-tls peer mode, carried on the reserved AnnPeerAutoTLS annotation
170+ // (see its doc). An explicit typed peer TLS mode (secretRef/certManager) always
171+ // wins, so the annotation is honoured only when spec.tls.peer is unset.
172+ func clusterPeerAutoTLS (cluster * lll.EtcdCluster ) bool {
173+ if cluster == nil {
174+ return false
175+ }
176+ if cluster .Spec .TLS != nil && cluster .Spec .TLS .Peer != nil {
177+ return false
178+ }
179+ return cluster .Annotations [AnnPeerAutoTLS ] == "true"
180+ }
181+
147182// memberClientScheme is the per-member counterpart to clusterClientScheme,
148183// keyed off the propagated EtcdMemberSpec.TLS.
149184func memberClientScheme (member * lll.EtcdMember ) string {
@@ -155,7 +190,8 @@ func memberClientScheme(member *lll.EtcdMember) string {
155190
156191// memberPeerScheme is the per-member counterpart to clusterPeerScheme.
157192func memberPeerScheme (member * lll.EtcdMember ) string {
158- if member != nil && member .Spec .TLS != nil && member .Spec .TLS .PeerSecretRef != nil {
193+ if member != nil && member .Spec .TLS != nil &&
194+ (member .Spec .TLS .PeerSecretRef != nil || member .Spec .TLS .PeerAutoTLS ) {
159195 return "https"
160196 }
161197 return "http"
@@ -179,19 +215,27 @@ func buildInitialCluster(peerScheme string, names []string, service, namespace s
179215// Secret names regardless of source, so buildPod / ensurePod /
180216// buildOperatorTLSConfig stay source-agnostic.
181217func deriveMemberTLS (cluster * lll.EtcdCluster ) * lll.EtcdMemberTLS {
182- if cluster == nil || cluster .Spec .TLS == nil {
183- return nil
184- }
185- if cluster .Spec .TLS .Client == nil && cluster .Spec .TLS .Peer == nil {
218+ if cluster == nil {
186219 return nil
187220 }
188221 out := & lll.EtcdMemberTLS {}
189- if name := serverSecretName (cluster ); name != "" {
190- out .ClientServerSecretRef = & corev1.LocalObjectReference {Name : name }
191- out .ClientMTLS = operatorClientSecretName (cluster ) != ""
222+ if cluster .Spec .TLS != nil {
223+ if name := serverSecretName (cluster ); name != "" {
224+ out .ClientServerSecretRef = & corev1.LocalObjectReference {Name : name }
225+ out .ClientMTLS = operatorClientSecretName (cluster ) != ""
226+ }
227+ if name := peerSecretName (cluster ); name != "" {
228+ out .PeerSecretRef = & corev1.LocalObjectReference {Name : name }
229+ }
230+ }
231+ // Carry the legacy-compat --peer-auto-tls posture (a cluster-level
232+ // reserved annotation, not typed spec) down to the member. clusterPeerAutoTLS
233+ // already yields false when an explicit peer mode is set, so real mTLS wins.
234+ if out .PeerSecretRef == nil && clusterPeerAutoTLS (cluster ) {
235+ out .PeerAutoTLS = true
192236 }
193- if name := peerSecretName ( cluster ); name != "" {
194- out . PeerSecretRef = & corev1. LocalObjectReference { Name : name }
237+ if out . ClientServerSecretRef == nil && out . PeerSecretRef == nil && ! out . PeerAutoTLS {
238+ return nil
195239 }
196240 return out
197241}
0 commit comments