Skip to content
This repository has been archived by the owner on Nov 1, 2022. It is now read-only.

Support basic authentication for registries #918

Merged
merged 2 commits into from
Jan 29, 2018
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions cmd/fluxd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ func main() {
registryRPS = fs.Int("registry-rps", 200, "maximum registry requests per second per host")
registryBurst = fs.Int("registry-burst", defaultRemoteConnections, "maximum number of warmer connections to remote and memcache")
registryTrace = fs.Bool("registry-trace", false, "output trace of image registry requests to log")
registryInsecure = fs.StringSlice("registry-insecure-host", []string{}, "use HTTP for this image registry domain (e.g., registry.cluster.local), instead of HTTPS")

// k8s-secret backed ssh keyring configuration
k8sSecretName = fs.String("k8s-secret-name", "flux-git-deploy", "Name of the k8s secret used to store the private SSH key")
Expand Down Expand Up @@ -257,9 +258,10 @@ func main() {
Burst: *registryBurst,
}
remoteFactory := &registry.RemoteClientFactory{
Logger: registryLogger,
Limiters: registryLimits,
Trace: *registryTrace,
Logger: registryLogger,
Limiters: registryLimits,
Trace: *registryTrace,
InsecureHosts: *registryInsecure,
}

// Warmer
Expand Down
13 changes: 7 additions & 6 deletions registry/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,24 +37,25 @@ type ClientFactory interface {
type Remote struct {
transport http.RoundTripper
repo image.CanonicalName
base string
}

// Adapt to docker distribution `reference.Named`.
type named struct {
image.CanonicalName
}

// Name returns the name of the repository. These values are used to
// build API URLs, and (it turns out) are _not_ expected to include a
// domain (e.g., quay.io). Hence, the implementation here just returns
// the path.
// Name returns the name of the repository. These values are used by
// the docker distribution client package to build API URLs, and (it
// turns out) are _not_ expected to include a domain (e.g.,
// quay.io). Hence, the implementation here just returns the path.
func (n named) Name() string {
return n.Image
}

// Return the tags for this repository.
func (a *Remote) Tags(ctx context.Context) ([]string, error) {
repository, err := client.NewRepository(named{a.repo}, "https://"+a.repo.Domain, a.transport)
repository, err := client.NewRepository(named{a.repo}, a.base, a.transport)
if err != nil {
return nil, err
}
Expand All @@ -64,7 +65,7 @@ func (a *Remote) Tags(ctx context.Context) ([]string, error) {
// Manifest fetches the metadata for an image reference; currently
// assumed to be in the same repo as that provided to `NewRemote(...)`
func (a *Remote) Manifest(ctx context.Context, ref string) (image.Info, error) {
repository, err := client.NewRepository(named{a.repo}, "https://"+a.repo.Domain, a.transport)
repository, err := client.NewRepository(named{a.repo}, a.base, a.transport)
if err != nil {
return image.Info{}, err
}
Expand Down
77 changes: 49 additions & 28 deletions registry/client_factory.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,13 @@ import (
)

type RemoteClientFactory struct {
Logger log.Logger
Limiters *middleware.RateLimiters
Trace bool
Logger log.Logger
Limiters *middleware.RateLimiters
Trace bool
InsecureHosts []string

mu sync.Mutex

This comment was marked as abuse.

challengeManager challenge.Manager
mx sync.Mutex
}

type logging struct {
Expand All @@ -43,47 +45,66 @@ func (f *RemoteClientFactory) ClientFor(repo image.CanonicalName, creds Credenti
tx = &logging{f.Logger, tx}
}

f.mx.Lock()
f.mu.Lock()
if f.challengeManager == nil {
f.challengeManager = challenge.NewSimpleManager()
}
f.mx.Unlock()
manager := f.challengeManager
f.mu.Unlock()

scheme := "https"
for _, h := range f.InsecureHosts {

This comment was marked as abuse.

This comment was marked as abuse.

if repo.Domain == h {
scheme = "http"
}
}

pingURL := url.URL{
Scheme: "https",
registryURL := url.URL{
Scheme: scheme,
Host: repo.Domain,
Path: "/v2/",
}

// Before we know how to authorise, need to establish which
// authorisation challenges the host will send.
if cs, err := manager.GetChallenges(pingURL); err == nil {
if len(cs) == 0 {
req, err := http.NewRequest("GET", pingURL.String(), nil)
if err != nil {
return nil, err
}
res, err := (&http.Client{
Transport: tx,
}).Do(req)
if err != nil {
return nil, err
}
if err = manager.AddResponse(res); err != nil {
return nil, err
}
// authorisation challenges the host will send. See if we've been
// here before.
cs, err := manager.GetChallenges(registryURL)
if err != nil {
return nil, err
}
if len(cs) == 0 {
// No prior challenge; try pinging the registry endpoint to
// get a challenge. `http.Client` will follow redirects, so
// even if we thought it was an insecure (HTTP) host, we may
// end up requesting HTTPS.
req, err := http.NewRequest("GET", registryURL.String(), nil)
if err != nil {
return nil, err
}
res, err := (&http.Client{
Transport: tx,
}).Do(req)
if err != nil {
return nil, err
}
if err = manager.AddResponse(res); err != nil {
return nil, err
}
registryURL = *res.Request.URL // <- the URL after any redirection
}

cred := creds.credsFor(repo.Domain)
if f.Trace {
f.Logger.Log("repo", repo.String(), "auth", cred.String())
f.Logger.Log("repo", repo.String(), "auth", cred.String(), "api", registryURL.String())
}

handler := auth.NewTokenHandler(tx, &store{cred}, repo.Image, "pull")
tx = transport.NewTransport(tx, auth.NewAuthorizer(manager, handler))
tokenHandler := auth.NewTokenHandler(tx, &store{cred}, repo.Image, "pull")
basicauthHandler := auth.NewBasicHandler(&store{cred})
tx = transport.NewTransport(tx, auth.NewAuthorizer(manager, tokenHandler, basicauthHandler))

client := &Remote{transport: tx, repo: repo}
// For the API base we want only the scheme and host.
registryURL.Path = ""
client := &Remote{transport: tx, repo: repo, base: registryURL.String()}
return NewInstrumentedClient(client), nil
}

Expand Down