1 package breakglass
2
3 import (
4 "context"
5 "fmt"
6 "os"
7
8 corev1 "k8s.io/api/core/v1"
9
10 "edge-infra.dev/pkg/edge/api/graph/model"
11 "edge-infra.dev/pkg/k8s/runtime/controller/reconcile"
12 "edge-infra.dev/pkg/sds/clustersecrets"
13 "edge-infra.dev/pkg/sds/clustersecrets/audit"
14 "edge-infra.dev/pkg/sds/clustersecrets/breakglass"
15 v1ien "edge-infra.dev/pkg/sds/ien/k8s/apis/v1"
16 "edge-infra.dev/pkg/sds/ien/k8s/controllers/nodeagent/config"
17 "edge-infra.dev/pkg/sds/ien/node"
18 )
19
20 type Plugin struct{}
21
22 var (
23 readOnlyShadowFilePath = "/host-rofs/etc/shadow"
24 shadowFilePath = "/host-etc/shadow"
25 )
26
27 func (plugin Plugin) Reconcile(ctx context.Context, ienode *v1ien.IENode, cfg config.Config) (reconcile.Result, error) {
28 secret, err := breakglass.FromClient(ctx, cfg.GetClient())
29 if err != nil {
30 return reconcile.ResultRequeue, err
31 }
32 if err := apply(secret, ienode, cfg); err != nil {
33 return reconcile.ResultRequeue, err
34 }
35 return reconcile.ResultSuccess, nil
36 }
37
38 func apply(breakglassSecret *corev1.Secret, ienode *v1ien.IENode, cfg config.Config) error {
39 secret, err := breakglass.FromSecret(breakglassSecret)
40 if err != nil {
41 return fmt.Errorf("error reading breakglass cluster secret: %w", err)
42 }
43 shadowFileEntry := secret.String()
44 changed, err := write(shadowFileEntry, secret.Version())
45 if err != nil {
46 return fmt.Errorf("error applying breakglass secret: %w", err)
47 }
48 if !changed {
49 return nil
50 }
51
52 terminalID := ienode.ObjectMeta.Labels[node.TerminalIDLabel]
53
54 if ienode.Status.ClusterSecrets == nil {
55 ienode.Status.ClusterSecrets = &v1ien.ClusterSecretInventory{}
56 }
57 ienode.Status.ClusterSecrets.AddClusterSecret(breakglass.HashedSecretName, secret.Version(), terminalID)
58
59 eventRecorder := cfg.GetEventRecorder()
60 clustersecrets.RecordClusterSecretEvent(eventRecorder, breakglassSecret, secret, terminalID)
61 return nil
62 }
63
64
65
66
67 func write(shadowFileEntry, version string) (bool, error) {
68 baseFile, err := os.ReadFile(readOnlyShadowFilePath)
69 if err != nil {
70 return false, err
71 }
72
73 currentContents, err := os.ReadFile(shadowFilePath)
74 if err != nil {
75 return false, err
76 }
77
78 hasChanged, contents := breakglass.UpsertEntry(currentContents, baseFile, shadowFileEntry)
79 if !hasChanged {
80 return false, nil
81 }
82
83
84 auditLog := audit.New("nodeagent")
85 auditLog.Log(model.ClusterSecretTypeBreakglass, audit.SecretApplied, "action_by", "nodeagent", "version", version)
86
87 shadowFileInfo, err := os.Stat(shadowFilePath)
88 if err != nil {
89 return false, fmt.Errorf("error stat on file: %v", err)
90 }
91
92 file, err := os.OpenFile(shadowFilePath, os.O_WRONLY|os.O_TRUNC, shadowFileInfo.Mode())
93 if err != nil {
94 return false, fmt.Errorf("error opening file: %v", err)
95 }
96 defer file.Close()
97 _, err = file.Write(contents)
98 return err == nil, err
99 }
100
View as plain text