Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
gitpod-io
GitHub Repository: gitpod-io/gitpod
Path: blob/main/components/image-builder-bob/pkg/proxy/auth.go
2506 views
1
// Copyright (c) 2021 Gitpod GmbH. All rights reserved.
2
// Licensed under the GNU Affero General Public License (AGPL).
3
// See License.AGPL.txt in the project root for license information.
4
5
package proxy
6
7
import (
8
"encoding/base64"
9
"encoding/json"
10
"regexp"
11
"strings"
12
13
"github.com/gitpod-io/gitpod/common-go/log"
14
"github.com/sirupsen/logrus"
15
)
16
17
var ecrRegistryRegexp = regexp.MustCompile(`\d{12}.dkr.ecr.\w+-\w+-\w+.amazonaws.com`)
18
19
const DummyECRRegistryDomain = "000000000000.dkr.ecr.dummy-host-zone.amazonaws.com"
20
21
// isECRRegistry returns true if the registry domain is an ECR registry
22
func isECRRegistry(domain string) bool {
23
return ecrRegistryRegexp.MatchString(domain)
24
}
25
26
// isDockerHubRegistry returns true if the registry domain is an docker hub
27
func isDockerHubRegistry(domain string) bool {
28
switch domain {
29
case "registry-1.docker.io":
30
fallthrough
31
case "docker.io":
32
return true
33
default:
34
return false
35
}
36
}
37
38
// authConfig configures authentication for a single host
39
type authConfig struct {
40
Username string `json:"username"`
41
Password string `json:"password"`
42
Auth string `json:"auth"`
43
}
44
45
type MapAuthorizer map[string]authConfig
46
47
func (a MapAuthorizer) Authorize(hostHeader string) (user, pass string, err error) {
48
defer func() {
49
log.WithFields(logrus.Fields{
50
"host": hostHeader,
51
"user": user,
52
}).Info("authorizing registry access")
53
}()
54
55
parseHostHeader := func(hostHeader string) (string, string) {
56
hostHeaderSlice := strings.Split(hostHeader, ":")
57
hostname := strings.TrimSpace(hostHeaderSlice[0])
58
var port string
59
if len(hostHeaderSlice) > 1 {
60
port = strings.TrimSpace(hostHeaderSlice[1])
61
}
62
return hostname, port
63
}
64
hostname, port := parseHostHeader(hostHeader)
65
// gpl: Could be port 80 as well, but we don't know if we are servinc http or https, we assume https
66
if port == "" {
67
port = "443"
68
}
69
host := hostname + ":" + port
70
71
explicitHostMatcher := func() (authConfig, bool) {
72
// 1. precise host match
73
res, ok := a[host]
74
if ok {
75
return res, ok
76
}
77
78
// 2. make sure we not have a hostname match
79
res, ok = a[hostname]
80
return res, ok
81
}
82
ecrHostMatcher := func() (authConfig, bool) {
83
if isECRRegistry(hostname) {
84
res, ok := a[DummyECRRegistryDomain]
85
return res, ok
86
}
87
return authConfig{}, false
88
}
89
dockerHubHostMatcher := func() (authConfig, bool) {
90
if isDockerHubRegistry(hostname) {
91
res, ok := a["docker.io"]
92
return res, ok
93
}
94
return authConfig{}, false
95
}
96
97
matchers := []func() (authConfig, bool){explicitHostMatcher, ecrHostMatcher, dockerHubHostMatcher}
98
res, ok := authConfig{}, false
99
for _, matcher := range matchers {
100
res, ok = matcher()
101
if ok {
102
break
103
}
104
}
105
106
if !ok {
107
return
108
}
109
110
user, pass = res.Username, res.Password
111
if res.Auth != "" {
112
var authBytes []byte
113
authBytes, err = base64.StdEncoding.DecodeString(res.Auth)
114
if err != nil {
115
return
116
}
117
auth := strings.TrimSpace(string(authBytes))
118
segs := strings.Split(auth, ":")
119
if len(segs) < 2 {
120
return
121
}
122
123
user = segs[0]
124
pass = strings.Join(segs[1:], ":")
125
}
126
127
return
128
}
129
130
func (a MapAuthorizer) AddIfNotExists(other MapAuthorizer) MapAuthorizer {
131
res := make(map[string]authConfig)
132
for k, v := range a {
133
res[k] = v
134
}
135
for k, v := range other {
136
if _, ok := a[k]; ok {
137
log.Infof("Skip adding key: %s to MapAuthorizer as it already exists", k)
138
continue
139
}
140
res[k] = v
141
}
142
return MapAuthorizer(res)
143
}
144
145
type Authorizer interface {
146
Authorize(host string) (user, pass string, err error)
147
}
148
149
func NewAuthorizerFromDockerEnvVar(content string) (auth MapAuthorizer, err error) {
150
var res struct {
151
Auths map[string]authConfig `json:"auths"`
152
}
153
err = json.Unmarshal([]byte(content), &res)
154
if err != nil {
155
return
156
}
157
return MapAuthorizer(res.Auths), nil
158
}
159
160
func NewAuthorizerFromEnvVar(content string) (auth MapAuthorizer, err error) {
161
if content == "" {
162
return nil, nil
163
}
164
165
var res map[string]authConfig
166
err = json.Unmarshal([]byte(content), &res)
167
if err != nil {
168
return nil, err
169
}
170
return MapAuthorizer(res), nil
171
}
172
173