-
Notifications
You must be signed in to change notification settings - Fork 41
/
auth.go
144 lines (117 loc) · 4.46 KB
/
auth.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
/*
Copyright 2022.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package internal
import (
"context"
"encoding/base64"
"encoding/json"
"fmt"
"strings"
v1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/types"
"sigs.k8s.io/controller-runtime/pkg/client"
)
// ContainerConfigJSON represents ~/.docker/config.json file info
// See https://github.com/docker/docker/pull/12009
// Structure from https://github.com/kubernetes/kubernetes/blob/master/pkg/credentialprovider/config.go#L39
type ContainerConfigJSON struct {
Auths ContainerConfig `json:"auths"`
// +optional
HTTPHeaders map[string]string `json:"HttpHeaders,omitempty"`
}
// DockerConfig represents the config file used by the docker CLI.
// This config that represents the credentials that should be used
// when pulling images from specific image repositories.
type ContainerConfig map[string]ContainerConfigEntry
// ContainerConfigEntry wraps a container config as a entry
type ContainerConfigEntry struct {
Username string
Password string
Email string
}
// dockerConfigEntryWithAuth is used solely for deserializing the Auth field
// into a dockerConfigEntry during JSON deserialization.
type ContainerConfigEntryWithAuth struct {
// +optional
Username string `json:"username,omitempty"`
// +optional
Password string `json:"password,omitempty"`
// +optional
Email string `json:"email,omitempty"`
// +optional
Auth string `json:"auth,omitempty"`
}
// UnmarshalJSON implements the json.Unmarshaler interface.
func (ident *ContainerConfigEntry) UnmarshalJSON(data []byte) error {
var tmp ContainerConfigEntryWithAuth
err := json.Unmarshal(data, &tmp)
if err != nil {
return err
}
if len(tmp.Auth) == 0 {
return nil
}
ident.Username, ident.Password, err = decodeContainerConfigFieldAuth(tmp.Auth)
return err
}
// decodeContainerConfigFieldAuth deserializes the "auth" field from containercfg into a
// username and a password. The format of the auth field is base64(<username>:<password>).
func decodeContainerConfigFieldAuth(field string) (username, password string, err error) {
var decoded []byte
// StdEncoding can only decode padded string
// RawStdEncoding can only decode unpadded string
if strings.HasSuffix(strings.TrimSpace(field), "=") {
// decode padded data
decoded, err = base64.StdEncoding.DecodeString(field)
} else {
// decode unpadded data
decoded, err = base64.RawStdEncoding.DecodeString(field)
}
if err != nil {
return
}
parts := strings.SplitN(string(decoded), ":", 2)
if len(parts) != 2 {
err = fmt.Errorf("unable to parse auth field, must be formatted as base64(username:password)")
return
}
username = parts[0]
password = parts[1]
return
}
// Mimicking exactly what Kubernetes does to pull out auths from secrets:
// https://github.com/kubernetes/kubernetes/blob/master/pkg/credentialprovider/secrets/secrets.go#L29
func ParseAuth(c client.Client, secretName, secretNamespace string) (*ContainerConfig, error) {
var creds ContainerConfig
// Lookup the k8s Secret for repository authentication
ctx := context.TODO()
imageSecret := &v1.Secret{}
if err := c.Get(ctx, types.NamespacedName{Namespace: secretNamespace, Name: secretName}, imageSecret); err != nil {
return nil, fmt.Errorf("failed image auth secret %s: %v",
secretName, err)
}
if containerConfigJSONBytes, containerConfigJSONExists := imageSecret.Data[v1.DockerConfigJsonKey]; (imageSecret.Type == v1.SecretTypeDockerConfigJson) && containerConfigJSONExists && (len(containerConfigJSONBytes) > 0) {
containerConfigJSON := ContainerConfigJSON{}
if err := json.Unmarshal(containerConfigJSONBytes, &containerConfigJSON); err != nil {
return nil, err
}
creds = containerConfigJSON.Auths
} else if dockercfgBytes, dockercfgExists := imageSecret.Data[v1.DockerConfigKey]; (imageSecret.Type == v1.SecretTypeDockercfg) && dockercfgExists && (len(dockercfgBytes) > 0) {
dockercfg := ContainerConfig{}
if err := json.Unmarshal(dockercfgBytes, &dockercfg); err != nil {
return nil, err
}
creds = dockercfg
}
return &creds, nil
}