Path: blob/main/components/image-builder-bob/pkg/proxy/auth.go
2506 views
// Copyright (c) 2021 Gitpod GmbH. All rights reserved.1// Licensed under the GNU Affero General Public License (AGPL).2// See License.AGPL.txt in the project root for license information.34package proxy56import (7"encoding/base64"8"encoding/json"9"regexp"10"strings"1112"github.com/gitpod-io/gitpod/common-go/log"13"github.com/sirupsen/logrus"14)1516var ecrRegistryRegexp = regexp.MustCompile(`\d{12}.dkr.ecr.\w+-\w+-\w+.amazonaws.com`)1718const DummyECRRegistryDomain = "000000000000.dkr.ecr.dummy-host-zone.amazonaws.com"1920// isECRRegistry returns true if the registry domain is an ECR registry21func isECRRegistry(domain string) bool {22return ecrRegistryRegexp.MatchString(domain)23}2425// isDockerHubRegistry returns true if the registry domain is an docker hub26func isDockerHubRegistry(domain string) bool {27switch domain {28case "registry-1.docker.io":29fallthrough30case "docker.io":31return true32default:33return false34}35}3637// authConfig configures authentication for a single host38type authConfig struct {39Username string `json:"username"`40Password string `json:"password"`41Auth string `json:"auth"`42}4344type MapAuthorizer map[string]authConfig4546func (a MapAuthorizer) Authorize(hostHeader string) (user, pass string, err error) {47defer func() {48log.WithFields(logrus.Fields{49"host": hostHeader,50"user": user,51}).Info("authorizing registry access")52}()5354parseHostHeader := func(hostHeader string) (string, string) {55hostHeaderSlice := strings.Split(hostHeader, ":")56hostname := strings.TrimSpace(hostHeaderSlice[0])57var port string58if len(hostHeaderSlice) > 1 {59port = strings.TrimSpace(hostHeaderSlice[1])60}61return hostname, port62}63hostname, port := parseHostHeader(hostHeader)64// gpl: Could be port 80 as well, but we don't know if we are servinc http or https, we assume https65if port == "" {66port = "443"67}68host := hostname + ":" + port6970explicitHostMatcher := func() (authConfig, bool) {71// 1. precise host match72res, ok := a[host]73if ok {74return res, ok75}7677// 2. make sure we not have a hostname match78res, ok = a[hostname]79return res, ok80}81ecrHostMatcher := func() (authConfig, bool) {82if isECRRegistry(hostname) {83res, ok := a[DummyECRRegistryDomain]84return res, ok85}86return authConfig{}, false87}88dockerHubHostMatcher := func() (authConfig, bool) {89if isDockerHubRegistry(hostname) {90res, ok := a["docker.io"]91return res, ok92}93return authConfig{}, false94}9596matchers := []func() (authConfig, bool){explicitHostMatcher, ecrHostMatcher, dockerHubHostMatcher}97res, ok := authConfig{}, false98for _, matcher := range matchers {99res, ok = matcher()100if ok {101break102}103}104105if !ok {106return107}108109user, pass = res.Username, res.Password110if res.Auth != "" {111var authBytes []byte112authBytes, err = base64.StdEncoding.DecodeString(res.Auth)113if err != nil {114return115}116auth := strings.TrimSpace(string(authBytes))117segs := strings.Split(auth, ":")118if len(segs) < 2 {119return120}121122user = segs[0]123pass = strings.Join(segs[1:], ":")124}125126return127}128129func (a MapAuthorizer) AddIfNotExists(other MapAuthorizer) MapAuthorizer {130res := make(map[string]authConfig)131for k, v := range a {132res[k] = v133}134for k, v := range other {135if _, ok := a[k]; ok {136log.Infof("Skip adding key: %s to MapAuthorizer as it already exists", k)137continue138}139res[k] = v140}141return MapAuthorizer(res)142}143144type Authorizer interface {145Authorize(host string) (user, pass string, err error)146}147148func NewAuthorizerFromDockerEnvVar(content string) (auth MapAuthorizer, err error) {149var res struct {150Auths map[string]authConfig `json:"auths"`151}152err = json.Unmarshal([]byte(content), &res)153if err != nil {154return155}156return MapAuthorizer(res.Auths), nil157}158159func NewAuthorizerFromEnvVar(content string) (auth MapAuthorizer, err error) {160if content == "" {161return nil, nil162}163164var res map[string]authConfig165err = json.Unmarshal([]byte(content), &res)166if err != nil {167return nil, err168}169return MapAuthorizer(res), nil170}171172173