Skip to content

Commit caab6c5

Browse files
authored
(bug) Fix service account token secret reference (#2862)
Problem: The filterSecretsBySAName function attempts to identify all service account token secrets related to a serviceAccount. To do so, the filterSecretsBySAName function uses a range-for loop to iterate over entries in the secrets argument. If a valid service account token secret is found, a pointer to the range-for loop's value variable is added to a map of results. Unfortunately, if a valid entry is found in the middle of the list of secrets, the value returned by the range-for loop is updated, causes the entry in the map to change. Solution: Add a pointer to the actual secret instead of the range-for loop's value variable. Signed-off-by: Alexander Greene <[email protected]> Signed-off-by: Alexander Greene <[email protected]>
1 parent 71f128a commit caab6c5

File tree

2 files changed

+123
-0
lines changed

2 files changed

+123
-0
lines changed

pkg/lib/scoped/token_retriever.go

+6
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,12 @@ func getAPISecret(logger logrus.FieldLogger, kubeclient operatorclient.ClientInt
8282
func filterSecretsBySAName(saName string, secrets *corev1.SecretList) map[string]*corev1.Secret {
8383
secretMap := make(map[string]*corev1.Secret)
8484
for _, ref := range secrets.Items {
85+
// Avoid referencing the "ref" created by the range-for loop as
86+
// the secret stored in the map will change if there are more
87+
// entries in the list of secrets that the range-for loop is
88+
// iterating over.
89+
ref := ref
90+
8591
annotations := ref.GetAnnotations()
8692
value := annotations[corev1.ServiceAccountNameKey]
8793
if value == saName {
+117
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
package scoped
2+
3+
import (
4+
"testing"
5+
6+
"github.com/stretchr/testify/require"
7+
corev1 "k8s.io/api/core/v1"
8+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
9+
)
10+
11+
const serviceAccountName = "foo"
12+
13+
func TestFilterSecretsBySAName(t *testing.T) {
14+
tests := []struct {
15+
name string
16+
secrets *corev1.SecretList
17+
wantedSecretNames []string
18+
}{
19+
{
20+
name: "NoSecretFound",
21+
secrets: &corev1.SecretList{
22+
Items: []corev1.Secret{
23+
*newSecret("aSecret"),
24+
*newSecret("someSecret"),
25+
*newSecret("zSecret"),
26+
},
27+
},
28+
wantedSecretNames: []string{},
29+
},
30+
{
31+
name: "FirstSecretFound",
32+
secrets: &corev1.SecretList{
33+
Items: []corev1.Secret{
34+
*newSecret("aSecret", withAnnotations(map[string]string{corev1.ServiceAccountNameKey: serviceAccountName})),
35+
*newSecret("someSecret"),
36+
*newSecret("zSecret"),
37+
},
38+
},
39+
wantedSecretNames: []string{"aSecret"},
40+
},
41+
{
42+
name: "SecondSecretFound",
43+
secrets: &corev1.SecretList{
44+
Items: []corev1.Secret{
45+
*newSecret("aSecret"),
46+
*newSecret("someSecret", withAnnotations(map[string]string{corev1.ServiceAccountNameKey: serviceAccountName})),
47+
*newSecret("zSecret"),
48+
},
49+
},
50+
wantedSecretNames: []string{"someSecret"},
51+
},
52+
{
53+
name: "ThirdSecretFound",
54+
secrets: &corev1.SecretList{
55+
Items: []corev1.Secret{
56+
*newSecret("aSecret"),
57+
*newSecret("someSecret"),
58+
*newSecret("zSecret", withAnnotations(map[string]string{corev1.ServiceAccountNameKey: serviceAccountName})),
59+
},
60+
},
61+
wantedSecretNames: []string{"zSecret"},
62+
},
63+
{
64+
name: "TwoSecretsFound",
65+
secrets: &corev1.SecretList{
66+
Items: []corev1.Secret{
67+
*newSecret("aSecret"),
68+
*newSecret("someSecret", withAnnotations(map[string]string{corev1.ServiceAccountNameKey: serviceAccountName})),
69+
*newSecret("zSecret", withAnnotations(map[string]string{corev1.ServiceAccountNameKey: serviceAccountName})),
70+
},
71+
},
72+
wantedSecretNames: []string{"someSecret", "zSecret"},
73+
},
74+
{
75+
name: "AllSecretsFound",
76+
secrets: &corev1.SecretList{
77+
Items: []corev1.Secret{
78+
*newSecret("aSecret", withAnnotations(map[string]string{corev1.ServiceAccountNameKey: serviceAccountName})),
79+
*newSecret("someSecret", withAnnotations(map[string]string{corev1.ServiceAccountNameKey: serviceAccountName})),
80+
*newSecret("zSecret", withAnnotations(map[string]string{corev1.ServiceAccountNameKey: serviceAccountName})),
81+
},
82+
},
83+
wantedSecretNames: []string{"aSecret", "someSecret", "zSecret"},
84+
},
85+
}
86+
87+
for _, tt := range tests {
88+
t.Run(tt.name, func(t *testing.T) {
89+
got := filterSecretsBySAName(serviceAccountName, tt.secrets)
90+
require.Equal(t, len(tt.wantedSecretNames), len(got))
91+
for _, wantedSecretName := range tt.wantedSecretNames {
92+
require.NotNil(t, got[wantedSecretName])
93+
require.Equal(t, wantedSecretName, got[wantedSecretName].GetName())
94+
}
95+
})
96+
}
97+
}
98+
99+
type secretOption func(*corev1.Secret)
100+
101+
func withAnnotations(annotations map[string]string) secretOption {
102+
return func(s *corev1.Secret) {
103+
s.SetAnnotations(annotations)
104+
}
105+
}
106+
107+
func newSecret(name string, opts ...secretOption) *corev1.Secret {
108+
s := &corev1.Secret{
109+
ObjectMeta: metav1.ObjectMeta{
110+
Name: name,
111+
},
112+
}
113+
for _, opt := range opts {
114+
opt(s)
115+
}
116+
return s
117+
}

0 commit comments

Comments
 (0)