@@ -2,7 +2,11 @@ package factory
22
33import (
44 "context"
5+ "crypto/tls"
6+ "crypto/x509"
7+ "encoding/pem"
58 "fmt"
9+ "time"
610
711 "github.com/aenix-io/etcd-operator/api/v1alpha1"
812 clientv3 "go.etcd.io/etcd/client/v3"
@@ -57,8 +61,72 @@ func configFromCluster(ctx context.Context, cluster *v1alpha1.EtcdCluster, cli c
5761 }
5862 }
5963 for name := range names {
60- urls = append (urls , fmt .Sprintf ("%s.%s.%s:%s" , name , ep .Name , ep .Namespace , "2379" ))
64+ urls = append (urls , fmt .Sprintf ("%s.%s.%s.svc :%s" , name , ep .Name , ep .Namespace , "2379" ))
6165 }
66+ etcdClient := clientv3.Config {Endpoints : urls , DialTimeout : 5 * time .Second }
6267
63- return clientv3.Config {Endpoints : urls }, nil
68+ if s := cluster .Spec .Security ; s != nil {
69+ if s .TLS .ClientSecret == "" {
70+ return clientv3.Config {}, fmt .Errorf ("cannot configure client: security enabled, but client certificates not set" )
71+ }
72+ clientSecret := & v1.Secret {}
73+ err := cli .Get (ctx , types.NamespacedName {Namespace : cluster .Namespace , Name : s .TLS .ClientSecret }, clientSecret )
74+ if err != nil {
75+ return clientv3.Config {}, fmt .Errorf ("cannot configure client: failed to get client certificate: %w" , err )
76+ }
77+ cert , err := parseTLSSecret (clientSecret )
78+ if err != nil {
79+ return clientv3.Config {}, fmt .Errorf ("cannot configure client: failed to extract keypair from client secret: %w" , err )
80+ }
81+ etcdClient .TLS = & tls.Config {}
82+ etcdClient .TLS .Certificates = []tls.Certificate {cert }
83+ etcdClient .TLS .GetClientCertificate = func (cri * tls.CertificateRequestInfo ) (* tls.Certificate , error ) {
84+ return & etcdClient .TLS .Certificates [0 ], nil
85+ }
86+ caSecret := & v1.Secret {}
87+ err = cli .Get (ctx , types.NamespacedName {Namespace : cluster .Namespace , Name : s .TLS .ServerSecret }, caSecret )
88+ if err != nil {
89+ return clientv3.Config {}, fmt .Errorf ("cannot configure client: failed to get server CA secret: %w" , err )
90+ }
91+ ca , err := parseTLSSecretCA (caSecret )
92+ if err != nil {
93+ return clientv3.Config {}, fmt .Errorf ("cannot configure client: failed to extract Root CA from server secret: %w" , err )
94+ }
95+ pool := x509 .NewCertPool ()
96+ pool .AddCert (ca )
97+ etcdClient .TLS .RootCAs = pool
98+ }
99+
100+ return etcdClient , nil
101+ }
102+
103+ func parseTLSSecret (secret * v1.Secret ) (cert tls.Certificate , err error ) {
104+ certPem , ok := secret .Data ["tls.crt" ]
105+ if ! ok {
106+ return tls.Certificate {}, fmt .Errorf ("tls.crt not found in secret" )
107+ }
108+ keyPem , ok := secret .Data ["tls.key" ]
109+ if ! ok {
110+ return tls.Certificate {}, fmt .Errorf ("tls.key not found in secret" )
111+ }
112+ cert , err = tls .X509KeyPair (certPem , keyPem )
113+ if err != nil {
114+ err = fmt .Errorf ("failed to load x509 key pair: %w" , err )
115+ }
116+ return
117+ }
118+
119+ func parseTLSSecretCA (secret * v1.Secret ) (ca * x509.Certificate , err error ) {
120+ caPemBytes , ok := secret .Data ["ca.crt" ]
121+ if ! ok {
122+ err = fmt .Errorf ("secret does not contain ca.crt" )
123+ return
124+ }
125+ block , _ := pem .Decode (caPemBytes )
126+ if block == nil {
127+ err = fmt .Errorf ("failed to decode PEM bytes" )
128+ return
129+ }
130+ ca , err = x509 .ParseCertificate (block .Bytes )
131+ return
64132}
0 commit comments