Skip to content

Commit 856db02

Browse files
authored
Merge pull request #120 from kubescape/creds
acknoledge that imagePullSecrets contain multiple credentials
2 parents 9765191 + 45ab50c commit 856db02

File tree

1 file changed

+135
-28
lines changed

1 file changed

+135
-28
lines changed

cloudsupport/dockerregistrycredsutils.go

+135-28
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,14 @@ package cloudsupport
22

33
import (
44
"context"
5+
"encoding/base64"
6+
"encoding/json"
57
"fmt"
8+
"strings"
69

7-
logger "github.com/kubescape/go-logger"
8-
"github.com/kubescape/go-logger/helpers"
9-
10-
"github.com/armosec/utils-k8s-go/secrethandling"
1110
"github.com/docker/docker/api/types/registry"
11+
"github.com/kubescape/go-logger"
12+
"github.com/kubescape/go-logger/helpers"
1213
"github.com/kubescape/k8s-interface/k8sinterface"
1314
corev1 "k8s.io/api/core/v1"
1415
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -18,15 +19,15 @@ func listPodImagePullSecrets(podSpec *corev1.PodSpec) ([]string, error) {
1819
if podSpec == nil {
1920
return []string{}, fmt.Errorf("in listPodImagePullSecrets podSpec is nil")
2021
}
21-
secrets := []string{}
22+
var secrets []string
2223
for _, i := range podSpec.ImagePullSecrets {
2324
secrets = append(secrets, i.Name)
2425
}
2526
return secrets, nil
2627
}
2728

2829
func listServiceAccountImagePullSecrets(k8sAPI *k8sinterface.KubernetesApi, namespace, serviceAccountName string) ([]string, error) {
29-
secrets := []string{}
30+
var secrets []string
3031
if serviceAccountName == "" {
3132
return secrets, nil
3233
}
@@ -41,49 +42,44 @@ func listServiceAccountImagePullSecrets(k8sAPI *k8sinterface.KubernetesApi, name
4142
return secrets, nil
4243
}
4344

44-
func getImagePullSecret(k8sAPI *k8sinterface.KubernetesApi, secrets []string, namespace string) map[string]registry.AuthConfig {
45+
func getImagePullSecret(k8sAPI *k8sinterface.KubernetesApi, secrets []string, namespace string) map[string][]registry.AuthConfig {
4546

46-
secretsAuthConfig := make(map[string]registry.AuthConfig)
47+
secretsAuthConfig := make(map[string][]registry.AuthConfig)
4748

4849
for i := range secrets {
4950
res, err := k8sAPI.KubernetesClient.CoreV1().Secrets(namespace).Get(context.Background(), secrets[i], metav1.GetOptions{})
5051
if err != nil {
5152
logger.L().Error("unable to get secret", helpers.String("secret name", secrets[i]), helpers.Error(err))
5253
continue
5354
}
54-
sec, err := secrethandling.ParseSecret(res, secrets[i])
55+
sec, err := ParseSecret(res)
5556
if err != nil {
5657
logger.L().Error("failed to pars secret", helpers.String("secret name", secrets[i]), helpers.Error(err))
5758
continue
5859
}
59-
secretsAuthConfig[secrets[i]] = *sec
60+
secretsAuthConfig[secrets[i]] = sec
6061
}
6162

6263
return secretsAuthConfig
6364
}
6465

65-
// DEPRECATED
6666
// GetImageRegistryCredentials returns various credentials for images in the pod
6767
// imageTag empty means returns all of the credentials for all images in pod spec containers
6868
// pod.ObjectMeta.Namespace must be well setted
69-
func GetImageRegistryCredentials(imageTag string, pod *corev1.Pod) (map[string]registry.AuthConfig, error) {
70-
k8sAPI := k8sinterface.NewKubernetesApi()
69+
// DEPRECATED
70+
func GetImageRegistryCredentials(k8sAPI *k8sinterface.KubernetesApi, imageTag string, pod *corev1.Pod) (map[string][]registry.AuthConfig, error) {
7171
listSecret, _ := listPodImagePullSecrets(&pod.Spec)
7272
listServiceSecret, _ := listServiceAccountImagePullSecrets(k8sAPI, pod.GetNamespace(), pod.Spec.ServiceAccountName)
7373
listSecret = append(listSecret, listServiceSecret...)
7474
secrets := getImagePullSecret(k8sAPI, listSecret, pod.ObjectMeta.Namespace)
7575

76-
if len(secrets) == 0 {
77-
secrets = make(map[string]registry.AuthConfig)
78-
}
79-
8076
if imageTag != "" {
8177
cloudVendorSecrets, err := GetCloudVendorRegistryCredentials(imageTag)
8278
if err != nil {
8379
logger.L().Debug("failed to GetCloudVendorRegistryCredentials", helpers.String("imageTag", imageTag), helpers.Error(err))
8480
} else if len(cloudVendorSecrets) > 0 {
8581
for secName := range cloudVendorSecrets {
86-
secrets[secName] = cloudVendorSecrets[secName]
82+
secrets[secName] = []registry.AuthConfig{cloudVendorSecrets[secName]}
8783
}
8884
}
8985
} else {
@@ -95,7 +91,7 @@ func GetImageRegistryCredentials(imageTag string, pod *corev1.Pod) (map[string]r
9591
logger.L().Debug("failed to GetCloudVendorRegistryCredentials", helpers.String("imageTag", imageTag), helpers.Error(err))
9692
} else if len(cloudVendorSecrets) > 0 {
9793
for secName := range cloudVendorSecrets {
98-
secrets[secName] = cloudVendorSecrets[secName]
94+
secrets[secName] = []registry.AuthConfig{cloudVendorSecrets[secName]}
9995
}
10096
}
10197
}
@@ -104,30 +100,25 @@ func GetImageRegistryCredentials(imageTag string, pod *corev1.Pod) (map[string]r
104100
return secrets, nil
105101
}
106102

107-
// GetImageRegistryCredentials returns various credentials for images in the pod
103+
// GetWorkloadImageRegistryCredentials returns various credentials for images in the pod
108104
// imageTag empty means returns all of the credentials for all images in pod spec containers
109105
// pod.ObjectMeta.Namespace must be well setted
110-
func GetWorkloadImageRegistryCredentials(imageTag string, workload k8sinterface.IWorkload) (map[string]registry.AuthConfig, error) {
106+
func GetWorkloadImageRegistryCredentials(k8sAPI *k8sinterface.KubernetesApi, imageTag string, workload k8sinterface.IWorkload) (map[string][]registry.AuthConfig, error) {
111107
podSpec, err := workload.GetPodSpec()
112108
if err != nil {
113109
return nil, err
114110
}
115-
k8sAPI := k8sinterface.NewKubernetesApi()
116111
listSecret, _ := listPodImagePullSecrets(podSpec)
117112
listServiceSecret, _ := listServiceAccountImagePullSecrets(k8sAPI, workload.GetNamespace(), podSpec.ServiceAccountName)
118113
listSecret = append(listSecret, listServiceSecret...)
119114
secrets := getImagePullSecret(k8sAPI, listSecret, workload.GetNamespace())
120115

121-
if len(secrets) == 0 {
122-
secrets = make(map[string]registry.AuthConfig)
123-
}
124-
125116
if imageTag != "" {
126117
if cloudVendorSecrets, err := GetCloudVendorRegistryCredentials(imageTag); err != nil {
127118
return secrets, fmt.Errorf("failed to GetCloudVendorRegistryCredentials, image: %s, message: %v", imageTag, err)
128119
} else if len(cloudVendorSecrets) > 0 {
129120
for secName := range cloudVendorSecrets {
130-
secrets[secName] = cloudVendorSecrets[secName]
121+
secrets[secName] = []registry.AuthConfig{cloudVendorSecrets[secName]}
131122
}
132123
}
133124
} else {
@@ -137,7 +128,7 @@ func GetWorkloadImageRegistryCredentials(imageTag string, workload k8sinterface.
137128
return secrets, fmt.Errorf("failed to GetCloudVendorRegistryCredentials, image: %s, message: %v", imageTag, err)
138129
} else if len(cloudVendorSecrets) > 0 {
139130
for secName := range cloudVendorSecrets {
140-
secrets[secName] = cloudVendorSecrets[secName]
131+
secrets[secName] = []registry.AuthConfig{cloudVendorSecrets[secName]}
141132
}
142133
}
143134
}
@@ -165,3 +156,119 @@ func GetWorkloadsImages(workload k8sinterface.IWorkload) map[string]string {
165156
}
166157
return images
167158
}
159+
160+
type DockerConfigJsonstructure map[string]map[string]registry.AuthConfig
161+
162+
func updateSecret(authConfig *registry.AuthConfig, serverAddress string) {
163+
if authConfig.ServerAddress == "" {
164+
authConfig.ServerAddress = serverAddress
165+
}
166+
if authConfig.Username == "" || authConfig.Password == "" {
167+
auth := authConfig.Auth
168+
decodedAuth, err := base64.StdEncoding.DecodeString(auth)
169+
if err != nil {
170+
return
171+
}
172+
173+
splittedAuth := strings.Split(string(decodedAuth), ":")
174+
if len(splittedAuth) == 2 {
175+
authConfig.Username = splittedAuth[0]
176+
authConfig.Password = splittedAuth[1]
177+
}
178+
}
179+
if authConfig.Auth == "" {
180+
auth := fmt.Sprintf("%s:%s", authConfig.Username, authConfig.Password)
181+
authConfig.Auth = base64.StdEncoding.EncodeToString([]byte(auth))
182+
}
183+
}
184+
185+
func parseEncodedSecret(sec map[string][]byte) (string, string) {
186+
buser := sec[corev1.BasicAuthUsernameKey]
187+
bpsw := sec[corev1.BasicAuthPasswordKey]
188+
duser, _ := base64.StdEncoding.DecodeString(string(buser))
189+
dpsw, _ := base64.StdEncoding.DecodeString(string(bpsw))
190+
return string(duser), string(dpsw)
191+
192+
}
193+
194+
func parseDecodedSecret(sec map[string]string) (string, string) {
195+
user := sec[corev1.BasicAuthUsernameKey]
196+
psw := sec[corev1.BasicAuthPasswordKey]
197+
return user, psw
198+
199+
}
200+
201+
func GetSecretContent(secret *corev1.Secret) (interface{}, error) {
202+
203+
// Secret types- https://github.com/kubernetes/kubernetes/blob/7693a1d5fe2a35b6e2e205f03ae9b3eddcdabc6b/pkg/apis/core/types.go#L4394-L4478
204+
switch secret.Type {
205+
case corev1.SecretTypeDockerConfigJson:
206+
sec := make(DockerConfigJsonstructure)
207+
if err := json.Unmarshal(secret.Data[corev1.DockerConfigJsonKey], &sec); err != nil {
208+
return nil, err
209+
}
210+
return sec, nil
211+
default:
212+
user, psw := "", ""
213+
if len(secret.Data) != 0 {
214+
user, psw = parseEncodedSecret(secret.Data)
215+
} else if len(secret.StringData) != 0 {
216+
userD, pswD := parseDecodedSecret(secret.StringData)
217+
if userD != "" {
218+
user = userD
219+
}
220+
if pswD != "" {
221+
psw = pswD
222+
}
223+
} else {
224+
return nil, fmt.Errorf("data not found in secret")
225+
}
226+
if user == "" || psw == "" {
227+
return nil, fmt.Errorf("username or password not found")
228+
}
229+
230+
return &registry.AuthConfig{Username: user, Password: psw}, nil
231+
}
232+
}
233+
234+
func ParseSecret(res *corev1.Secret) ([]registry.AuthConfig, error) {
235+
236+
// Read secret
237+
secret, err := GetSecretContent(res)
238+
if err != nil {
239+
return nil, err
240+
}
241+
242+
if secret == nil {
243+
return nil, fmt.Errorf("secret not found")
244+
}
245+
sec, err := ReadSecret(secret)
246+
if err != nil {
247+
return sec, err
248+
}
249+
return sec, nil
250+
251+
}
252+
253+
func ReadSecret(secret interface{}) ([]registry.AuthConfig, error) {
254+
// Store secret based on it's structure
255+
var authConfig []registry.AuthConfig
256+
if sec, ok := secret.(*registry.AuthConfig); ok {
257+
return []registry.AuthConfig{*sec}, nil
258+
}
259+
if sec, ok := secret.(map[string]string); ok {
260+
return []registry.AuthConfig{{Username: sec["username"]}}, nil
261+
}
262+
if sec, ok := secret.(DockerConfigJsonstructure); ok {
263+
if _, k := sec["auths"]; !k {
264+
return authConfig, fmt.Errorf("cant find auths")
265+
}
266+
for serverAddress, auth := range sec["auths"] {
267+
updateSecret(&auth, serverAddress)
268+
authConfig = append(authConfig, auth)
269+
}
270+
return authConfig, nil
271+
}
272+
273+
return authConfig, fmt.Errorf("cant find secret")
274+
}

0 commit comments

Comments
 (0)