Skip to content

Commit

Permalink
Merge pull request #218 from ivanilves/auth-headers
Browse files Browse the repository at this point in the history
feat(auth): Set BASIC auth with no respect to remote server
  • Loading branch information
vonrabbe authored Mar 7, 2020
2 parents d901dcd + 26ee146 commit 6b3aeb6
Show file tree
Hide file tree
Showing 6 changed files with 175 additions and 13 deletions.
83 changes: 83 additions & 0 deletions api/v1/registry/client/auth/basic/store/store.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
package store

import (
"fmt"
"net/url"
"strings"
)

// Store stores BASIC authentication credentials
type Store struct {
logins map[string]*Login
}

// Login stores username and password for BASIC authentication
type Login struct {
Username string
Password string
}

// LoadAll parses and loads a list of BASIC authentication strings
func (st *Store) LoadAll(aa []string) error {
logins := make(map[string]*Login, 0)

for _, a := range aa {
registry, login, err := loadOne(strings.TrimSpace(a))

if err != nil {
return err
}

logins[registry] = login
}

st.logins = logins

return nil
}

// GetByHostname gets a BASIC auth login for a registry hostname passed
func (st *Store) GetByHostname(registryHostname string) *Login {
login, defined := st.logins[registryHostname]
if !defined {
return nil
}

return login
}

// GetByURL gets a BASIC auth login for a registry URL passed
func (st *Store) GetByURL(registryURL string) *Login {
u, _ := url.Parse(registryURL)

return st.GetByHostname(u.Host)
}

func loadOne(a string) (string, *Login, error) {
const format = "REGISTRY[:PORT] username:password"

var formatErr = fmt.Errorf(
"invalid format for BASIC auth (should be: %s)",
format,
)

ss := strings.SplitN(a, " ", 2)
if len(ss) != 2 {
return "", nil, formatErr
}

up := strings.SplitN(ss[1], ":", 2)
if len(up) != 2 {
return "", nil, formatErr
}

registry := ss[0]
username := up[0]
password := up[1]

if password == "" {
return "", nil, formatErr
}

return registry, &Login{Username: username, Password: password}, nil
}
55 changes: 55 additions & 0 deletions api/v1/registry/client/auth/basic/store/store_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package store

import (
"testing"

"github.com/stretchr/testify/assert"
)

var examples = []string{"localhost:5000 foo:bar", "quay.io quser:qpass"}

func TestLoadAllValid(t *testing.T) {
var store Store

err := store.LoadAll(examples)

assert.NoError(t, err)
}

func TestLoadAllInvalid(t *testing.T) {
var store Store

assert.Error(t, store.LoadAll([]string{""}))
assert.Error(t, store.LoadAll([]string{"us.gcr.io"}))
assert.Error(t, store.LoadAll([]string{"us.gcr.io forgotsomething"}))
assert.Error(t, store.LoadAll([]string{"quay.io quser:"}))
assert.Error(t, store.LoadAll([]string{" foo:bar"}))
}

func TestGet(t *testing.T) {
var store Store

store.LoadAll(examples)

assert.NotNil(t, store.GetByHostname("localhost:5000"))
assert.NotNil(t, store.GetByHostname("quay.io"))
assert.Nil(t, store.GetByHostname("eu.gcr.io"))

assert.NotNil(t, store.GetByURL("http://localhost:5000"))
assert.NotNil(t, store.GetByURL("https://quay.io"))
assert.Nil(t, store.GetByURL("https://eu.gcr.io"))
}

func TestGetValues(t *testing.T) {
var store Store

store.LoadAll(examples)

login1 := store.GetByHostname("localhost:5000")
login2 := store.GetByHostname("quay.io")

assert.Equal(t, login1.Username, "foo")
assert.Equal(t, login1.Password, "bar")
assert.Equal(t, login2.Username, "quser")
assert.Equal(t, login2.Password, "qpass")
}
36 changes: 26 additions & 10 deletions api/v1/registry/client/auth/token.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,14 @@ import (
log "github.com/sirupsen/logrus"

"github.com/ivanilves/lstags/api/v1/registry/client/auth/basic"
basicstore "github.com/ivanilves/lstags/api/v1/registry/client/auth/basic/store"
"github.com/ivanilves/lstags/api/v1/registry/client/auth/bearer"
"github.com/ivanilves/lstags/api/v1/registry/client/auth/none"
)

// BasicStore stores explicitly set BASIC authorization headers
var BasicStore basicstore.Store

// Token is an abstraction for aggregated token-related information we get from authentication services
type Token interface {
Method() string
Expand Down Expand Up @@ -58,18 +62,30 @@ func getAuthParams(h authHeader) map[string]string {
// * detects authentication type ("Bearer", "Basic" or "None")
// * delegates actual authentication to the type-specific implementation
func NewToken(url, username, password, scope string) (Token, error) {
resp, err := http.Get(url)
if err != nil {
return nil, err
}
var method = ""
var params = make(map[string]string)

authHeader, err := extractAuthHeader(resp.Header["Www-Authenticate"])
if err != nil {
return nil, err
}
storedBasicAuth := BasicStore.GetByURL(url)

if storedBasicAuth == nil {
resp, err := http.Get(url)
if err != nil {
return nil, err
}

method := strings.ToLower(getAuthMethod(authHeader))
params := getAuthParams(authHeader)
authHeader, err := extractAuthHeader(resp.Header["Www-Authenticate"])
if err != nil {
return nil, err
}

method = strings.ToLower(getAuthMethod(authHeader))
params = getAuthParams(authHeader)
} else {
method = "basic"

username = storedBasicAuth.Username
password = storedBasicAuth.Password
}

switch method {
case "none":
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ require (
github.com/sirupsen/logrus v1.4.2
github.com/stevvooe/resumable v0.0.0-20180830230917-22b14a53ba50 // indirect
github.com/stretchr/testify v1.4.0
golang.org/x/crypto v0.0.0-20191002192127-34f69633bfdc // indirect
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550 // indirect
golang.org/x/net v0.0.0-20191007182048-72f939374954
gopkg.in/yaml.v2 v2.2.4
)
6 changes: 4 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8=
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
github.com/stevvooe/resumable v0.0.0-20180830230917-22b14a53ba50 h1:4bT0pPowCpQImewr+BjzfUKcuFW+KVyB8d1OF3b6oTI=
github.com/stevvooe/resumable v0.0.0-20180830230917-22b14a53ba50/go.mod h1:1pdIZTAHUz+HDKDVZ++5xg/duPlhKAIzw9qy42CWYp4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
Expand All @@ -52,8 +53,8 @@ github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJy
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191002192127-34f69633bfdc h1:c0o/qxkaO2LF5t6fQrT4b5hzyggAkLLlCUjqfRxd8Q4=
golang.org/x/crypto v0.0.0-20191002192127-34f69633bfdc/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550 h1:ObdrDkeb4kJdCP557AjRjq69pTHfNouLtWZG7j9rPN8=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20191007182048-72f939374954 h1:JGZucVF/L/TotR719NbujzadOZ2AgnYlqphQGHDCKaU=
golang.org/x/net v0.0.0-20191007182048-72f939374954/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
Expand All @@ -64,6 +65,7 @@ golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b h1:ag/x1USPSsqHud38I9BAC88qdNLDHHtQ4mlgQIZPPNA=
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I=
Expand Down
6 changes: 6 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
log "github.com/sirupsen/logrus"

v1 "github.com/ivanilves/lstags/api/v1"
"github.com/ivanilves/lstags/api/v1/registry/client/auth"
"github.com/ivanilves/lstags/config"
)

Expand All @@ -34,6 +35,7 @@ type Options struct {
RetryRequests int `short:"y" long:"retry-requests" default:"2" description:"Number of retries for failed Docker registry requests" env:"RETRY_REQUESTS"`
RetryDelay time.Duration `short:"D" long:"retry-delay" default:"2s" description:"Delay between retries of failed registry requests" env:"RETRY_DELAY"`
InsecureRegistryEx string `short:"I" long:"insecure-registry-ex" description:"Expression to match insecure registry hostnames" env:"INSECURE_REGISTRY_EX"`
BasicAuth []string `short:"B" long:"basic-auth" description:"Set per-registry BASIC auth username:password pair" env:"BASIC_AUTH"`
TraceRequests bool `short:"T" long:"trace-requests" description:"Trace Docker registry HTTP requests" env:"TRACE_REQUESTS"`
DoNotFail bool `short:"N" long:"do-not-fail" description:"Do not fail on non-critical errors (could be dangerous!)" env:"DO_NOT_FAIL"`
DaemonMode bool `short:"d" long:"daemon-mode" description:"Run as daemon instead of just execute and exit" env:"DAEMON_MODE"`
Expand Down Expand Up @@ -105,6 +107,10 @@ func main() {
suicide(err, true)
}

if err := auth.BasicStore.LoadAll(o.BasicAuth); err != nil {
suicide(err, true)
}

apiConfig := v1.Config{
DockerJSONConfigFile: o.DockerJSON,
ConcurrentRequests: o.ConcurrentRequests,
Expand Down

0 comments on commit 6b3aeb6

Please sign in to comment.