1 package provision
2
3 import (
4 "crypto"
5 "crypto/x509"
6 "fmt"
7 "path/filepath"
8
9 "github.com/spf13/afero"
10
11 "edge-infra.dev/pkg/lib/crypto/certs/pem"
12 edgex509 "edge-infra.dev/pkg/lib/crypto/certs/x509"
13 "edge-infra.dev/pkg/sds/etcd/operator/internal/resources"
14 "edge-infra.dev/pkg/sds/etcd/operator/internal/tar"
15 "edge-infra.dev/pkg/sds/lib/etcd/client"
16 "edge-infra.dev/pkg/sds/lib/etcd/server"
17 )
18
19
20 func (r *Reconciler) secretContent(handlers *Handlers) ([]byte, error) {
21 ip := handlers.member.EtcdMember.Spec.Address.Host
22 certList := []edgex509.CertInfo{
23 server.CertInfo(handlers.member.EtcdMember.Name, ip),
24 server.PeerCertInfo(handlers.member.EtcdMember.Name, ip),
25 client.HealthcheckCertInfo(),
26 client.APIServerEtcdCertInfo(),
27 }
28
29 requiredFiles, err := r.requiredFiles(r.Fs, certList, handlers)
30 if err != nil {
31 return nil, err
32 }
33
34 writer := tar.NewWriter()
35 defer writer.Close()
36
37 if err := writer.Archive(requiredFiles); err != nil {
38 return nil, fmt.Errorf("failed to write files to tarball: %w", err)
39 }
40 return writer.Bytes(), nil
41 }
42
43
44
45 func (r *Reconciler) requiredFiles(fs afero.Fs, certList []edgex509.CertInfo, handlers *Handlers) ([]tar.File, error) {
46 var requiredFiles []tar.File
47 requiredEtcdCerts, err := r.requiredEtcdCerts(fs, certList)
48 if err != nil {
49 return nil, err
50 }
51 requiredFiles = append(requiredFiles, requiredEtcdCerts...)
52
53 requiredKubeFiles, err := r.requiredKubeFiles(fs, handlers.member.EtcdMember.Spec.RequiredFiles)
54 if err != nil {
55 return nil, err
56 }
57
58 return append(requiredFiles, requiredKubeFiles...), nil
59 }
60
61
62
63 func (r *Reconciler) requiredEtcdCerts(fs afero.Fs, certInfoList []edgex509.CertInfo) ([]tar.File, error) {
64 caCert, err := etcdCaCert(fs)
65 if err != nil {
66 return nil, fmt.Errorf("failed to read cert: %s: %w", caCertPath, err)
67 }
68
69 caKeySigner, err := etcdCaKeySigner(fs)
70 if err != nil {
71 return nil, fmt.Errorf("failed to decode cert: %s: %w", caCertPath, err)
72 }
73
74 var certs []tar.File
75
76 for _, certInfo := range certInfoList {
77 certs, err = appendKeyCertPair(certs, certInfo, caCert, caKeySigner)
78 if err != nil {
79 return nil, err
80 }
81 }
82
83 return certs, nil
84 }
85
86
87 func etcdCaCert(fs afero.Fs) (*x509.Certificate, error) {
88 certBytes, err := afero.ReadFile(fs, caCertPath)
89 if err != nil {
90 return nil, fmt.Errorf("failed to read cert: %s: %w", caCertPath, err)
91 }
92
93 caCert, err := pem.GetCertFromPemFile(certBytes)
94 if err != nil {
95 return nil, fmt.Errorf("failed to decode cert: %s: %w", caCertPath, err)
96 }
97
98 return caCert, nil
99 }
100
101
102 func etcdCaKeySigner(fs afero.Fs) (crypto.Signer, error) {
103 keyBytes, err := afero.ReadFile(fs, caKeyPath)
104 if err != nil {
105 return nil, fmt.Errorf("failed to read key: %s: %w", caKeyPath, err)
106 }
107
108 caKeySigner, err := pem.GetKeySignerFromPemFile(keyBytes)
109 if err != nil {
110 return nil, fmt.Errorf("failed to decode key: %s: %w", caKeyPath, err)
111 }
112 return caKeySigner, nil
113 }
114
115
116 func appendKeyCertPair(certs []tar.File, certInfo edgex509.CertInfo, caCert *x509.Certificate, caKeySigner crypto.Signer) ([]tar.File, error) {
117 encodedKeyPair, err := edgex509.GenerateCertAndKey(certInfo, caCert, caKeySigner)
118 if err != nil {
119 return nil, fmt.Errorf("failed to create x509 public certificate: %w", err)
120 }
121
122 var path string
123 switch certInfo.Name {
124 case "apiserver-etcd-client":
125 path = "/etc/kubernetes/pki/"
126 default:
127 path = "/etc/kubernetes/pki/etcd/"
128 }
129
130 key := tar.File{
131 Name: filepath.Join(path, certInfo.Name+".key"),
132 Bytes: encodedKeyPair.Key,
133 Size: int64(len(encodedKeyPair.Key)),
134 Mode: 0600,
135 }
136 cert := tar.File{
137 Name: filepath.Join(path, certInfo.Name+".crt"),
138 Bytes: encodedKeyPair.Cert,
139 Size: int64(len(encodedKeyPair.Cert)),
140 Mode: 0644,
141 }
142
143 return append(certs, key, cert), nil
144 }
145
146
147
148 func (r *Reconciler) requiredKubeFiles(fs afero.Fs, fileNames []string) ([]tar.File, error) {
149 var requiredFiles []tar.File
150 for _, fileName := range fileNames {
151 file, err := fs.Open(fileName)
152 if err != nil {
153 return nil, fmt.Errorf("failed to open file: %s: %w", fileName, err)
154 }
155
156 bytes, err := afero.ReadFile(fs, fileName)
157 if err != nil {
158 return nil, fmt.Errorf("failed to read file: %s: %w", fileName, err)
159 }
160
161 fileInfo, err := file.Stat()
162 if err != nil {
163 return nil, fmt.Errorf("failed to stat file: %s: %w", fileName, err)
164 }
165
166 requiredFiles = append(requiredFiles, tar.File{
167 Name: fileName,
168 Bytes: bytes,
169 Size: fileInfo.Size(),
170 Mode: fileInfo.Mode(),
171 })
172 }
173 return requiredFiles, nil
174 }
175
176
177 func (r *Reconciler) generateSecret(data []byte, handlers *Handlers) {
178 b := resources.NewSecretHandlerBuilder().
179 WithClient(r.KubeRetryClient).
180 WithKey(handlers.secret.Key).
181 HandlesSecret().
182 Named(handlers.member.EtcdMember.Name).
183 InNamespace(operatorNamespace).
184 WithOwner(handlers.member.EtcdMember).
185 WithData(data)
186 handlers.secret = b.Build()
187 }
188
View as plain text