1
16
17 package copycerts
18
19 import (
20 "context"
21 "encoding/hex"
22 "os"
23 "path/filepath"
24 "regexp"
25 goruntime "runtime"
26 "testing"
27
28 "github.com/lithammer/dedent"
29
30 v1 "k8s.io/api/core/v1"
31 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
32 fakeclient "k8s.io/client-go/kubernetes/fake"
33 "k8s.io/client-go/util/keyutil"
34
35 kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
36 kubeadmconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants"
37 "k8s.io/kubernetes/cmd/kubeadm/app/phases/certs"
38 cryptoutil "k8s.io/kubernetes/cmd/kubeadm/app/util/crypto"
39 testutil "k8s.io/kubernetes/cmd/kubeadm/test"
40 )
41
42 func TestGetDataFromInitConfig(t *testing.T) {
43 certData := []byte("cert-data")
44 tmpdir := testutil.SetupTempDir(t)
45 defer os.RemoveAll(tmpdir)
46 cfg := &kubeadmapi.InitConfiguration{}
47 cfg.CertificatesDir = tmpdir
48
49 key, err := CreateCertificateKey()
50 if err != nil {
51 t.Fatalf(dedent.Dedent("failed to create key.\nfatal error: %v"), err)
52 }
53 decodedKey, err := hex.DecodeString(key)
54 if err != nil {
55 t.Fatalf(dedent.Dedent("failed to decode key.\nfatal error: %v"), err)
56 }
57
58 if err := os.Mkdir(filepath.Join(tmpdir, "etcd"), 0755); err != nil {
59 t.Fatalf(dedent.Dedent("failed to create etcd cert dir.\nfatal error: %v"), err)
60 }
61
62 certs := certsToTransfer(cfg)
63 for name, path := range certs {
64 if err := os.WriteFile(path, certData, 0644); err != nil {
65 t.Fatalf(dedent.Dedent("failed to write cert: %s\nfatal error: %v"), name, err)
66 }
67 }
68
69 secretData, err := getDataFromDisk(cfg, decodedKey)
70 if err != nil {
71 t.Fatalf("failed to get secret data. fatal error: %v", err)
72 }
73
74 re := regexp.MustCompile(`[-.\w]+`)
75 for name, data := range secretData {
76 if !re.MatchString(name) {
77 t.Fatalf(dedent.Dedent("failed to validate secretData\n %s isn't a valid secret key"), name)
78 }
79
80 decryptedData, err := cryptoutil.DecryptBytes(data, decodedKey)
81 if string(certData) != string(decryptedData) {
82 t.Fatalf(dedent.Dedent("can't decrypt cert: %s\nfatal error: %v"), name, err)
83 }
84 }
85 }
86
87 func TestCertsToTransfer(t *testing.T) {
88 localEtcdCfg := &kubeadmapi.InitConfiguration{}
89 externalEtcdCfg := &kubeadmapi.InitConfiguration{}
90 externalEtcdCfg.Etcd = kubeadmapi.Etcd{}
91 externalEtcdCfg.Etcd.External = &kubeadmapi.ExternalEtcd{}
92
93 commonExpectedCerts := []string{
94 kubeadmconstants.CACertName,
95 kubeadmconstants.CAKeyName,
96 kubeadmconstants.FrontProxyCACertName,
97 kubeadmconstants.FrontProxyCAKeyName,
98 kubeadmconstants.ServiceAccountPublicKeyName,
99 kubeadmconstants.ServiceAccountPrivateKeyName,
100 }
101
102 tests := map[string]struct {
103 config *kubeadmapi.InitConfiguration
104 expectedCerts []string
105 }{
106 "local etcd": {
107 config: localEtcdCfg,
108 expectedCerts: append(
109 []string{kubeadmconstants.EtcdCACertName, kubeadmconstants.EtcdCAKeyName},
110 commonExpectedCerts...,
111 ),
112 },
113 "external etcd": {
114 config: externalEtcdCfg,
115 expectedCerts: append(
116 []string{externalEtcdCA, externalEtcdCert, externalEtcdKey},
117 commonExpectedCerts...,
118 ),
119 },
120 }
121
122 for name, test := range tests {
123 t.Run(name, func(t2 *testing.T) {
124 certList := certsToTransfer(test.config)
125 for _, cert := range test.expectedCerts {
126 if _, found := certList[cert]; !found {
127 t2.Fatalf(dedent.Dedent("failed to get list of certs to upload\ncert %s not found"), cert)
128 }
129 }
130 })
131 }
132 }
133
134 func TestCertOrKeyNameToSecretName(t *testing.T) {
135 tests := []struct {
136 keyName string
137 expectedSecretName string
138 }{
139 {
140 keyName: "apiserver-kubelet-client.crt",
141 expectedSecretName: "apiserver-kubelet-client.crt",
142 },
143 {
144 keyName: "etcd/ca.crt",
145 expectedSecretName: "etcd-ca.crt",
146 },
147 {
148 keyName: "etcd/healthcheck-client.crt",
149 expectedSecretName: "etcd-healthcheck-client.crt",
150 },
151 }
152
153 for _, tc := range tests {
154 secretName := certOrKeyNameToSecretName(tc.keyName)
155 if secretName != tc.expectedSecretName {
156 t.Fatalf("secret name %s didn't match expected name %s", secretName, tc.expectedSecretName)
157 }
158 }
159 }
160
161 func TestUploadCerts(t *testing.T) {
162 tmpdir := testutil.SetupTempDir(t)
163 defer os.RemoveAll(tmpdir)
164
165 secretKey, err := CreateCertificateKey()
166 if err != nil {
167 t.Fatalf("could not create certificate key: %v", err)
168 }
169
170 initConfiguration := testutil.GetDefaultInternalConfig(t)
171 initConfiguration.ClusterConfiguration.CertificatesDir = tmpdir
172
173 if err := certs.CreatePKIAssets(initConfiguration); err != nil {
174 t.Fatalf("error creating PKI assets: %v", err)
175 }
176
177 cs := fakeclient.NewSimpleClientset()
178 if err := UploadCerts(cs, initConfiguration, secretKey); err != nil {
179 t.Fatalf("error uploading certs: %v", err)
180 }
181 rawSecretKey, err := hex.DecodeString(secretKey)
182 if err != nil {
183 t.Fatalf("error decoding key: %v", err)
184 }
185 secretMap, err := cs.CoreV1().Secrets(metav1.NamespaceSystem).Get(context.TODO(), kubeadmconstants.KubeadmCertsSecret, metav1.GetOptions{})
186 if err != nil {
187 t.Fatalf("could not fetch secret: %v", err)
188 }
189 for certName, certPath := range certsToTransfer(initConfiguration) {
190 secretCertData, err := cryptoutil.DecryptBytes(secretMap.Data[certOrKeyNameToSecretName(certName)], rawSecretKey)
191 if err != nil {
192 t.Fatalf("error decrypting secret data: %v", err)
193 }
194 diskCertData, err := os.ReadFile(certPath)
195 if err != nil {
196 t.Fatalf("error reading certificate from disk: %v", err)
197 }
198
199
200 if string(secretCertData) != string(diskCertData) {
201 t.Fatalf("cert %s does not have the expected contents. contents: %q; expected contents: %q", certName, string(secretCertData), string(diskCertData))
202 }
203 }
204 }
205
206 func TestDownloadCerts(t *testing.T) {
207 secretKey, err := CreateCertificateKey()
208 if err != nil {
209 t.Fatalf("could not create certificate key: %v", err)
210 }
211
212
213 tmpdir := testutil.SetupTempDir(t)
214 defer os.RemoveAll(tmpdir)
215 initConfiguration := testutil.GetDefaultInternalConfig(t)
216 initConfiguration.ClusterConfiguration.CertificatesDir = tmpdir
217
218
219 targetTmpdir := testutil.SetupTempDir(t)
220 defer os.RemoveAll(targetTmpdir)
221 initForDownloadConfiguration := testutil.GetDefaultInternalConfig(t)
222 initForDownloadConfiguration.ClusterConfiguration.CertificatesDir = targetTmpdir
223
224 if err := certs.CreatePKIAssets(initConfiguration); err != nil {
225 t.Fatalf("error creating PKI assets: %v", err)
226 }
227
228 kubeadmCertsSecret := createKubeadmCertsSecret(t, initConfiguration, secretKey)
229 cs := fakeclient.NewSimpleClientset(kubeadmCertsSecret)
230 if err := DownloadCerts(cs, initForDownloadConfiguration, secretKey); err != nil {
231 t.Fatalf("error downloading certs: %v", err)
232 }
233
234 const keyFileMode = 0600
235 const certFileMode = 0644
236
237 for certName, certPath := range certsToTransfer(initForDownloadConfiguration) {
238 diskCertData, err := os.ReadFile(certPath)
239 if err != nil {
240 t.Errorf("error reading certificate from disk: %v", err)
241 }
242
243
244 if _, err := keyutil.ParsePrivateKeyPEM(diskCertData); err == nil {
245
246 if goruntime.GOOS != "windows" {
247 if stat, err := os.Stat(certPath); err == nil {
248 if stat.Mode() != keyFileMode {
249 t.Errorf("key %q should have mode %#o, has %#o", certName, keyFileMode, stat.Mode())
250 }
251 } else {
252 t.Errorf("could not stat key %q: %v", certName, err)
253 }
254 }
255 } else if _, err := keyutil.ParsePublicKeysPEM(diskCertData); err == nil {
256
257 if goruntime.GOOS != "windows" {
258 if stat, err := os.Stat(certPath); err == nil {
259 if stat.Mode() != certFileMode {
260 t.Errorf("cert %q should have mode %#o, has %#o", certName, certFileMode, stat.Mode())
261 }
262 } else {
263 t.Errorf("could not stat cert %q: %v", certName, err)
264 }
265 }
266 } else {
267 t.Errorf("secret %q was not identified as a cert or as a key", certName)
268 }
269 }
270 }
271
272 func createKubeadmCertsSecret(t *testing.T, cfg *kubeadmapi.InitConfiguration, secretKey string) *v1.Secret {
273 decodedKey, err := hex.DecodeString(secretKey)
274 if err != nil {
275 t.Fatalf("error decoding key: %v", err)
276 }
277 secretData, err := getDataFromDisk(cfg, decodedKey)
278 if err != nil {
279 t.Fatalf("error creating secret data: %v", err)
280 }
281 return &v1.Secret{
282 ObjectMeta: metav1.ObjectMeta{
283 Name: kubeadmconstants.KubeadmCertsSecret,
284 Namespace: metav1.NamespaceSystem,
285 },
286 Data: secretData,
287 }
288 }
289
View as plain text