...
1 package breakglass
2
3 import (
4 "context"
5 _ "embed"
6 "os"
7 "strings"
8 "testing"
9
10 "github.com/stretchr/testify/require"
11 "gotest.tools/v3/fs"
12 "sigs.k8s.io/controller-runtime/pkg/client/fake"
13
14 corev1 "k8s.io/api/core/v1"
15
16 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
17
18 kruntime "k8s.io/apimachinery/pkg/runtime"
19 utilruntime "k8s.io/apimachinery/pkg/util/runtime"
20 clientgoscheme "k8s.io/client-go/kubernetes/scheme"
21
22 "edge-infra.dev/pkg/sds"
23 "edge-infra.dev/pkg/sds/clustersecrets/breakglass"
24 cc "edge-infra.dev/pkg/sds/clustersecrets/common"
25 "edge-infra.dev/pkg/sds/ien/k8s/controllers/nodeagent/config"
26
27 v1ien "edge-infra.dev/pkg/sds/ien/k8s/apis/v1"
28 )
29
30 var p = Plugin{}
31
32
33 var contents []byte
34
35 type testCase struct {
36 secret *corev1.Secret
37 }
38
39 var testSuite = []testCase{
40 {
41 toSecret(
42 "724e77776b37516a695951514848707376646670514467512e364c58624f50534a416e777849357438786d7a4264516839432e577553414c6b463578756633564a6347673449666c4b5141646c58754e67784a536a2e",
43 "4f54764c3033645a357557386b79394b",
44 "eH8DAQEGU2VjcmV0Af+AAAEIAQlQcmluY2lwYWwBDAABCkl0ZXJhdGlvbnMBBAABBFNhbHQBDAABBEhhc2gBDAABCEZ1bmN0aW9uAQwAAQhIYXNoVHlwZQEMAAEKR2VuZXJhdGlvbgEEAAEIUm90YXRpb24BAgAAAP/w/4ABCHp5bGV2ZWwwAiA0ZjU0NzY0YzMwMzM2NDVhMzU3NTU3Mzg2Yjc5Mzk0YgH/rDcyNGU3Nzc3NmIzNzUxNmE2OTU5NTE1MTQ4NDg3MDczNzY2NDY2NzA1MTQ0Njc1MTJlMzY0YzU4NjI0ZjUwNTM0YTQxNmU3Nzc4NDkzNTc0Mzg3ODZkN2E0MjY0NTE2ODM5NDMyZTU3NzU1MzQxNGM2YjQ2MzU3ODc1NjYzMzU2NGE2MzQ3NjczNDQ5NjY2YzRiNTE0MTY0NmM1ODc1NGU2Nzc4NGE1MzZhMmUBBnNoYTUxMgEGc2hhNTEyAQIA",
45 ),
46 },
47 }
48
49 var nodeHostname = "node-1"
50
51 func TestMain(_ *testing.M) {
52 os.Setenv("HOSTNAME", nodeHostname)
53 }
54
55 func TestBreakGlassApply(t *testing.T) {
56 cleanup := setupFilesystem(t)
57 defer cleanup()
58
59 for _, test := range testSuite {
60 c := fake.NewClientBuilder().WithScheme(createScheme()).WithObjects(test.secret).Build()
61
62 ctx := context.Background()
63 _, err := p.Reconcile(ctx, &v1ien.IENode{}, config.Data{Client: c})
64 require.NoError(t, err)
65
66 secret, err := breakglass.FromSecret(test.secret)
67 require.NoError(t, err)
68
69 expectedEntry := secret.String()
70
71 actualContents, err := os.ReadFile(shadowFilePath)
72 require.NoError(t, err)
73
74 entryFound := false
75 for _, line := range strings.Split(string(actualContents), "\n") {
76 if strings.Contains(line, breakglass.Username) {
77 entryFound = true
78 require.Equal(t, line, expectedEntry)
79 }
80 }
81 require.True(t, entryFound)
82 }
83 }
84
85 func setupFilesystem(t *testing.T) func() {
86 readOnlyTmpDir := fs.NewDir(t, readOnlyShadowFilePath)
87 shadowFileTmpDir := fs.NewDir(t, shadowFilePath)
88
89 cleanupFunc := func() {
90 shadowFileTmpDir.Remove()
91 readOnlyTmpDir.Remove()
92 }
93
94 shadowFileName := "/shadow"
95 readOnlyShadowFilePath = readOnlyTmpDir.Path() + shadowFileName
96 shadowFilePath = shadowFileTmpDir.Path() + shadowFileName
97
98
99 err := os.WriteFile(readOnlyShadowFilePath, contents, 0600)
100 require.NoError(t, err)
101 return cleanupFunc
102 }
103
104 func toSecret(hash, salt, serialised string) *corev1.Secret {
105 return &corev1.Secret{
106 ObjectMeta: metav1.ObjectMeta{
107 Name: breakglass.HashedSecretName,
108 Namespace: sds.Namespace,
109 },
110 Data: map[string][]byte{
111 breakglass.SecretKey: []byte(serialised),
112 cc.PrincipalKey: []byte(breakglass.Username),
113 cc.HashFunctionKey: []byte("sha512"),
114 cc.HashTypeKey: []byte("sha512"),
115 cc.SaltKey: []byte(salt),
116 cc.HashKey: []byte(hash),
117 cc.SecretVersionKey: []byte("2"),
118 },
119 }
120 }
121
122 func createScheme() *kruntime.Scheme {
123 scheme := kruntime.NewScheme()
124 utilruntime.Must(clientgoscheme.AddToScheme(scheme))
125 utilruntime.Must(v1ien.AddToScheme(scheme))
126 return scheme
127 }
128
View as plain text