1 package entrypoint
2
3 import (
4 "context"
5 "crypto/x509"
6 "encoding/json"
7 "encoding/pem"
8 "fmt"
9 "os"
10 "strconv"
11 "strings"
12
13 v1 "k8s.io/api/core/v1"
14 "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
15
16 "github.com/datawire/dlib/derror"
17 "github.com/datawire/dlib/dlog"
18 amb "github.com/emissary-ingress/emissary/v3/pkg/api/getambassador.io/v3alpha1"
19 "github.com/emissary-ingress/emissary/v3/pkg/kates"
20 "github.com/emissary-ingress/emissary/v3/pkg/snapshot/v1"
21 snapshotTypes "github.com/emissary-ingress/emissary/v3/pkg/snapshot/v1"
22 )
23
24
25
26 func checkSecret(
27 ctx context.Context,
28 sh *SnapshotHolder,
29 what string,
30 ref snapshotTypes.SecretRef,
31 secret *v1.Secret,
32 ) {
33 forceSecretValidation, _ := strconv.ParseBool(os.Getenv("AMBASSADOR_FORCE_SECRET_VALIDATION"))
34
35 secretName := fmt.Sprintf("%s secret %s.%s", what, ref.Name, ref.Namespace)
36
37 if secret == nil {
38
39 dlog.Debugf(ctx, "%s not found", secretName)
40 return
41 }
42
43
44 isValid := true
45
46
47 var errs derror.MultiError
48
49
50 privKeyPEMBytes, ok := secret.Data[v1.TLSPrivateKeyKey]
51
52 if ok && len(privKeyPEMBytes) > 0 {
53
54 caKeyBlock, _ := pem.Decode(privKeyPEMBytes)
55
56 if caKeyBlock != nil {
57 dlog.Debugf(ctx, "%s has private key, block type %s", secretName, caKeyBlock.Type)
58
59
60 _, err := x509.ParsePKCS1PrivateKey(caKeyBlock.Bytes)
61
62 if err != nil {
63
64
65 _, err = x509.ParsePKCS8PrivateKey(caKeyBlock.Bytes)
66 }
67
68 if err != nil {
69
70
71 _, err = x509.ParseECPrivateKey(caKeyBlock.Bytes)
72 }
73
74
75 if err != nil {
76 errs = append(errs,
77 fmt.Errorf("%s %s cannot be parsed as PKCS1, PKCS8, or EC: %s", secretName, v1.TLSPrivateKeyKey, err.Error()))
78 isValid = false
79 }
80 } else {
81 errs = append(errs,
82 fmt.Errorf("%s %s is not a PEM-encoded key", secretName, v1.TLSPrivateKeyKey))
83 isValid = false
84 }
85 }
86
87
88 caCertPEMBytes, ok := secret.Data[v1.TLSCertKey]
89
90 if ok && len(caCertPEMBytes) > 0 {
91 caCertBlock, _ := pem.Decode(caCertPEMBytes)
92
93 if caCertBlock != nil {
94 dlog.Debugf(ctx, "%s has public key, block type %s", secretName, caCertBlock.Type)
95
96 _, err := x509.ParseCertificate(caCertBlock.Bytes)
97
98 if err != nil {
99 errs = append(errs,
100 fmt.Errorf("%s %s cannot be parsed as x.509: %s", secretName, v1.TLSCertKey, err.Error()))
101 isValid = false
102 }
103 } else {
104 errs = append(errs,
105 fmt.Errorf("%s %s is not a PEM-encoded certificate", secretName, v1.TLSCertKey))
106 isValid = false
107 }
108 }
109
110 if isValid || !forceSecretValidation {
111 dlog.Debugf(ctx, "taking %s", secretName)
112 sh.k8sSnapshot.Secrets = append(sh.k8sSnapshot.Secrets, secret)
113 }
114 if !isValid {
115
116
117 dlog.Debugf(ctx, "%s is not valid, skipping: %s", secretName, errs.Error())
118
119
120
121 secretBytes, err := json.Marshal(secret)
122
123 if err != nil {
124
125 dlog.Errorf(ctx, "unable to marshal invalid %s: %s", secretName, err)
126 return
127 }
128
129 var unstructuredSecret kates.Unstructured
130 err = json.Unmarshal(secretBytes, &unstructuredSecret)
131
132 if err != nil {
133
134 dlog.Errorf(ctx, "unable to unmarshal invalid %s: %s", secretName, err)
135 return
136 }
137
138
139 redactedData := map[string]interface{}{}
140
141 for key := range secret.Data {
142 redactedData[key] = "-redacted-"
143 }
144
145 unstructuredSecret.Object["data"] = redactedData
146
147
148
149
150 metadata, ok := unstructuredSecret.Object["metadata"].(map[string]interface{})
151
152 if ok {
153 delete(metadata, "managedFields")
154
155 annotations, ok := metadata["annotations"].(map[string]interface{})
156
157 if ok {
158 delete(annotations, "kubectl.kubernetes.io/last-applied-configuration")
159
160 if len(annotations) == 0 {
161 delete(metadata, "annotations")
162 }
163 }
164
165 if len(metadata) == 0 {
166 delete(unstructuredSecret.Object, "metadata")
167 }
168 }
169
170
171 sh.validator.addInvalid(ctx, &unstructuredSecret, errs.Error())
172 }
173 }
174
175
176
177
178 func ReconcileSecrets(ctx context.Context, sh *SnapshotHolder) error {
179 envAmbID := GetAmbassadorID()
180
181
182
183
184 var resources []kates.Object
185
186
187
188
189
190
191 for _, list := range sh.k8sSnapshot.Annotations {
192 for _, a := range list {
193 if _, isInvalid := a.(*kates.Unstructured); isInvalid {
194 continue
195 }
196 if GetAmbID(ctx, a).Matches(envAmbID) {
197 resources = append(resources, a)
198 }
199 }
200 }
201
202
203
204 for _, h := range sh.k8sSnapshot.Hosts {
205 var id amb.AmbassadorID
206 if len(h.Spec.AmbassadorID) > 0 {
207 id = h.Spec.AmbassadorID
208 }
209 if id.Matches(envAmbID) {
210 resources = append(resources, h)
211 }
212 }
213
214
215 for _, t := range sh.k8sSnapshot.TLSContexts {
216 if t.Spec.AmbassadorID.Matches(envAmbID) {
217 resources = append(resources, t)
218 }
219 }
220 for _, m := range sh.k8sSnapshot.Modules {
221 if m.Spec.AmbassadorID.Matches(envAmbID) {
222 resources = append(resources, m)
223 }
224 }
225 for _, i := range sh.k8sSnapshot.Ingresses {
226 resources = append(resources, i)
227 }
228
229
230
231
232
233
234
235
236
237 secretNamespacing := true
238 for _, resource := range resources {
239 mod, ok := resource.(*amb.Module)
240
241
242
243
244 if ok && mod.GetName() == "ambassador" {
245
246
247 secs := ModuleSecrets{}
248 err := convert(mod.Spec.Config, &secs)
249 if err != nil {
250 dlog.Errorf(ctx, "error parsing module: %v", err)
251 continue
252 }
253 secretNamespacing = secs.Defaults.TLSSecretNamespacing
254 break
255 }
256 }
257
258
259
260 refs := map[snapshotTypes.SecretRef]bool{}
261
262
263
264
265 action := func(ref snapshotTypes.SecretRef) {
266 refs[ref] = true
267 }
268
269
270 for _, resource := range resources {
271
272 findSecretRefs(ctx, resource, secretNamespacing, action)
273 }
274
275
276 secretRef(GetCloudConnectTokenResourceNamespace(), GetCloudConnectTokenResourceName(), false, action)
277
278
279 secretRef(GetAmbassadorNamespace(), "fallback-self-signed-cert", false, action)
280
281 isEdgeStack, err := IsEdgeStack()
282 if err != nil {
283 return err
284 }
285 if isEdgeStack {
286
287
288 secretRef(GetLicenseSecretNamespace(), GetLicenseSecretName(), false, action)
289
290
291 for _, f := range sh.k8sSnapshot.Filters {
292 err := findFilterSecret(f, action)
293 if err != nil {
294 dlog.Errorf(ctx, "Error gathering secret reference from Filter: %v", err)
295 }
296 }
297 }
298
299
300
301
302
303
304
305 sh.k8sSnapshot.Secrets = make([]*kates.Secret, 0, len(refs))
306
307 for ref, secret := range sh.k8sSnapshot.FSSecrets {
308 if refs[ref] {
309 checkSecret(ctx, sh, "FSSecret", ref, secret)
310 }
311 }
312
313 for _, secret := range sh.k8sSnapshot.K8sSecrets {
314 ref := snapshotTypes.SecretRef{Namespace: secret.GetNamespace(), Name: secret.GetName()}
315
316 _, found := sh.k8sSnapshot.FSSecrets[ref]
317 if found {
318 dlog.Debugf(ctx, "Conflict! skipping K8sSecret %#v", ref)
319 continue
320 }
321
322 if refs[ref] {
323 checkSecret(ctx, sh, "K8sSecret", ref, secret)
324 }
325 }
326 return nil
327 }
328
329
330
331 func findFilterSecret(filter *unstructured.Unstructured, action func(snapshotTypes.SecretRef)) error {
332
333 if filter.GetKind() != "Filter" {
334 return fmt.Errorf("non-Filter object in Snapshot.Filters: %s", filter.GetKind())
335 }
336
337
338
339 filterContents := filter.UnstructuredContent()
340 filterSpec := filterContents["spec"]
341 if filterSpec != nil {
342 mapFilters, ok := filterSpec.(map[string]interface{})
343
344
345 if !ok {
346
347
348 return nil
349 }
350
351 findOAuthFilterSecret(mapFilters, filter.GetNamespace(), action)
352 findAPIKeyFilterSecret(mapFilters, filter.GetNamespace(), action)
353 }
354 return nil
355 }
356
357 func findOAuthFilterSecret(
358 mapFilters map[string]interface{},
359 filterNamespace string,
360 action func(snapshotTypes.SecretRef),
361 ) {
362 oAuthFilter := mapFilters["OAuth2"]
363 if oAuthFilter == nil {
364 return
365 }
366
367 secretName, secretNamespace := "", ""
368
369 mapOAuth, ok := oAuthFilter.(map[string]interface{})
370 if !ok {
371 return
372 }
373 sName := mapOAuth["secretName"]
374 if sName == nil {
375 return
376 }
377 secretName, ok = sName.(string)
378
379 if !ok || secretName == "" {
380
381 return
382 }
383 sNamespace := mapOAuth["secretNamespace"]
384 if sNamespace == nil {
385 secretNamespace = filterNamespace
386 } else {
387 secretNamespace, ok = sNamespace.(string)
388 if !ok {
389 return
390 } else if secretNamespace == "" {
391 secretNamespace = filterNamespace
392 }
393 }
394 secretRef(secretNamespace, secretName, false, action)
395
396 }
397
398 func findAPIKeyFilterSecret(
399 mapFilters map[string]interface{},
400 filterNamespace string,
401 action func(snapshotTypes.SecretRef),
402 ) {
403 apiKeyFilter := mapFilters["APIKey"]
404 if apiKeyFilter != nil {
405 mapKeyFilter, ok := apiKeyFilter.(map[string]interface{})
406
407 if !ok {
408 return
409 }
410
411 apiKeys := mapKeyFilter["keys"].([]interface{})
412
413 for i := range apiKeys {
414 secretName := ""
415 mapKey, ok := apiKeys[i].(map[string]interface{})
416
417 if !ok {
418 continue
419 }
420
421 sName := mapKey["secretName"]
422 if sName == nil {
423 continue
424 }
425
426 secretName, ok = sName.(string)
427
428 if !ok || secretName == "" {
429
430 continue
431 }
432
433 secretRef(filterNamespace, secretName, false, action)
434 }
435 }
436 }
437
438
439 func findSecretRefs(ctx context.Context, resource kates.Object, secretNamespacing bool, action func(snapshotTypes.SecretRef)) {
440 switch r := resource.(type) {
441 case *amb.Host:
442
443
444 if r.Spec == nil {
445 return
446 }
447
448 if r.Spec.TLS != nil {
449
450 secretRef(r.GetNamespace(), r.Spec.TLS.CASecret, secretNamespacing, action)
451
452 if r.Spec.TLS.CRLSecret != "" {
453 secretRef(r.GetNamespace(), r.Spec.TLS.CRLSecret, secretNamespacing, action)
454 }
455 }
456
457
458
459
460
461
462 if r.Spec.TLSSecret != nil && r.Spec.TLSSecret.Name != "" {
463 if r.Spec.TLSSecret.Namespace != "" {
464 secretRef(r.Spec.TLSSecret.Namespace, r.Spec.TLSSecret.Name, false, action)
465 } else {
466 secretRef(r.GetNamespace(), r.Spec.TLSSecret.Name, false, action)
467 }
468 }
469
470 if r.Spec.AcmeProvider != nil && r.Spec.AcmeProvider.PrivateKeySecret != nil &&
471 r.Spec.AcmeProvider.PrivateKeySecret.Name != "" {
472 secretRef(r.GetNamespace(), r.Spec.AcmeProvider.PrivateKeySecret.Name, false, action)
473 }
474
475 case *amb.TLSContext:
476
477
478 if r.Spec.Secret != "" {
479 if r.Spec.SecretNamespacing != nil {
480 secretNamespacing = *r.Spec.SecretNamespacing
481 }
482 secretRef(r.GetNamespace(), r.Spec.Secret, secretNamespacing, action)
483 }
484
485 if r.Spec.CASecret != "" {
486 if r.Spec.SecretNamespacing != nil {
487 secretNamespacing = *r.Spec.SecretNamespacing
488 }
489 secretRef(r.GetNamespace(), r.Spec.CASecret, secretNamespacing, action)
490 }
491
492 if r.Spec.CRLSecret != "" {
493 if r.Spec.SecretNamespacing != nil {
494 secretNamespacing = *r.Spec.SecretNamespacing
495 }
496 secretRef(r.GetNamespace(), r.Spec.CRLSecret, secretNamespacing, action)
497 }
498
499 case *amb.Module:
500
501
502
503
504
505 secs := ModuleSecrets{}
506 err := convert(r.Spec.Config, &secs)
507 if err != nil {
508
509 dlog.Errorf(ctx, "error extracting secrets from module: %v", err)
510 return
511 }
512
513
514
515 if secs.Upstream.Secret != "" {
516 secretRef(r.GetNamespace(), secs.Upstream.Secret, secretNamespacing, action)
517 }
518 if secs.Server.Secret != "" {
519 secretRef(r.GetNamespace(), secs.Server.Secret, secretNamespacing, action)
520 }
521 if secs.Client.Secret != "" {
522 secretRef(r.GetNamespace(), secs.Client.Secret, secretNamespacing, action)
523 }
524
525 case *snapshot.Ingress:
526
527 for _, itls := range r.Spec.TLS {
528 if itls.SecretName != "" {
529 secretRef(r.GetNamespace(), itls.SecretName, secretNamespacing, action)
530 }
531 }
532 }
533 }
534
535
536 func secretRef(namespace, name string, secretNamespacing bool, action func(snapshotTypes.SecretRef)) {
537 if secretNamespacing {
538 parts := strings.Split(name, ".")
539 if len(parts) > 1 {
540 namespace = parts[len(parts)-1]
541 name = strings.Join(parts[:len(parts)-1], ".")
542 }
543 }
544
545 action(snapshotTypes.SecretRef{Namespace: namespace, Name: name})
546 }
547
548
549
550
551
552
553
554
555
556
557 type ModuleSecrets struct {
558 Defaults struct {
559 TLSSecretNamespacing bool `json:"tls_secret_namespacing"`
560 } `json:"defaults"`
561 Upstream struct {
562 Secret string `json:"secret"`
563 } `json:"upstream"`
564 Server struct {
565 Secret string `json:"secret"`
566 } `json:"server"`
567 Client struct {
568 Secret string `json:"secret"`
569 } `json:"client"`
570 }
571
View as plain text