Browse Source

Merge pull request #918 from weaveworks/issue/915-basicauth

Support basic authentication for registries
Michael Bridgen 2 years ago
parent
commit
b5a285b884
No account linked to committer's email address
5 changed files with 70 additions and 37 deletions
  1. 5
    3
      cmd/fluxd/main.go
  2. 7
    6
      registry/client.go
  3. 49
    28
      registry/client_factory.go
  4. 1
    0
      site/daemon.md
  5. 8
    0
      site/requirements.md

+ 5
- 3
cmd/fluxd/main.go View File

@@ -94,6 +94,7 @@ func main() {
94 94
 		registryRPS          = fs.Int("registry-rps", 200, "maximum registry requests per second per host")
95 95
 		registryBurst        = fs.Int("registry-burst", defaultRemoteConnections, "maximum number of warmer connections to remote and memcache")
96 96
 		registryTrace        = fs.Bool("registry-trace", false, "output trace of image registry requests to log")
97
+		registryInsecure     = fs.StringSlice("registry-insecure-host", []string{}, "use HTTP for this image registry domain (e.g., registry.cluster.local), instead of HTTPS")
97 98
 
98 99
 		// k8s-secret backed ssh keyring configuration
99 100
 		k8sSecretName            = fs.String("k8s-secret-name", "flux-git-deploy", "Name of the k8s secret used to store the private SSH key")
@@ -257,9 +258,10 @@ func main() {
257 258
 			Burst: *registryBurst,
258 259
 		}
259 260
 		remoteFactory := &registry.RemoteClientFactory{
260
-			Logger:   registryLogger,
261
-			Limiters: registryLimits,
262
-			Trace:    *registryTrace,
261
+			Logger:        registryLogger,
262
+			Limiters:      registryLimits,
263
+			Trace:         *registryTrace,
264
+			InsecureHosts: *registryInsecure,
263 265
 		}
264 266
 
265 267
 		// Warmer

+ 7
- 6
registry/client.go View File

@@ -37,6 +37,7 @@ type ClientFactory interface {
37 37
 type Remote struct {
38 38
 	transport http.RoundTripper
39 39
 	repo      image.CanonicalName
40
+	base      string
40 41
 }
41 42
 
42 43
 // Adapt to docker distribution `reference.Named`.
@@ -44,17 +45,17 @@ type named struct {
44 45
 	image.CanonicalName
45 46
 }
46 47
 
47
-// Name returns the name of the repository. These values are used to
48
-// build API URLs, and (it turns out) are _not_ expected to include a
49
-// domain (e.g., quay.io). Hence, the implementation here just returns
50
-// the path.
48
+// Name returns the name of the repository. These values are used by
49
+// the docker distribution client package to build API URLs, and (it
50
+// turns out) are _not_ expected to include a domain (e.g.,
51
+// quay.io). Hence, the implementation here just returns the path.
51 52
 func (n named) Name() string {
52 53
 	return n.Image
53 54
 }
54 55
 
55 56
 // Return the tags for this repository.
56 57
 func (a *Remote) Tags(ctx context.Context) ([]string, error) {
57
-	repository, err := client.NewRepository(named{a.repo}, "https://"+a.repo.Domain, a.transport)
58
+	repository, err := client.NewRepository(named{a.repo}, a.base, a.transport)
58 59
 	if err != nil {
59 60
 		return nil, err
60 61
 	}
@@ -64,7 +65,7 @@ func (a *Remote) Tags(ctx context.Context) ([]string, error) {
64 65
 // Manifest fetches the metadata for an image reference; currently
65 66
 // assumed to be in the same repo as that provided to `NewRemote(...)`
66 67
 func (a *Remote) Manifest(ctx context.Context, ref string) (image.Info, error) {
67
-	repository, err := client.NewRepository(named{a.repo}, "https://"+a.repo.Domain, a.transport)
68
+	repository, err := client.NewRepository(named{a.repo}, a.base, a.transport)
68 69
 	if err != nil {
69 70
 		return image.Info{}, err
70 71
 	}

+ 49
- 28
registry/client_factory.go View File

@@ -15,11 +15,13 @@ import (
15 15
 )
16 16
 
17 17
 type RemoteClientFactory struct {
18
-	Logger           log.Logger
19
-	Limiters         *middleware.RateLimiters
20
-	Trace            bool
18
+	Logger        log.Logger
19
+	Limiters      *middleware.RateLimiters
20
+	Trace         bool
21
+	InsecureHosts []string
22
+
23
+	mu               sync.Mutex
21 24
 	challengeManager challenge.Manager
22
-	mx               sync.Mutex
23 25
 }
24 26
 
25 27
 type logging struct {
@@ -43,47 +45,66 @@ func (f *RemoteClientFactory) ClientFor(repo image.CanonicalName, creds Credenti
43 45
 		tx = &logging{f.Logger, tx}
44 46
 	}
45 47
 
46
-	f.mx.Lock()
48
+	f.mu.Lock()
47 49
 	if f.challengeManager == nil {
48 50
 		f.challengeManager = challenge.NewSimpleManager()
49 51
 	}
50
-	f.mx.Unlock()
51 52
 	manager := f.challengeManager
53
+	f.mu.Unlock()
54
+
55
+	scheme := "https"
56
+	for _, h := range f.InsecureHosts {
57
+		if repo.Domain == h {
58
+			scheme = "http"
59
+		}
60
+	}
52 61
 
53
-	pingURL := url.URL{
54
-		Scheme: "https",
62
+	registryURL := url.URL{
63
+		Scheme: scheme,
55 64
 		Host:   repo.Domain,
56 65
 		Path:   "/v2/",
57 66
 	}
67
+
58 68
 	// Before we know how to authorise, need to establish which
59
-	// authorisation challenges the host will send.
60
-	if cs, err := manager.GetChallenges(pingURL); err == nil {
61
-		if len(cs) == 0 {
62
-			req, err := http.NewRequest("GET", pingURL.String(), nil)
63
-			if err != nil {
64
-				return nil, err
65
-			}
66
-			res, err := (&http.Client{
67
-				Transport: tx,
68
-			}).Do(req)
69
-			if err != nil {
70
-				return nil, err
71
-			}
72
-			if err = manager.AddResponse(res); err != nil {
73
-				return nil, err
74
-			}
69
+	// authorisation challenges the host will send. See if we've been
70
+	// here before.
71
+	cs, err := manager.GetChallenges(registryURL)
72
+	if err != nil {
73
+		return nil, err
74
+	}
75
+	if len(cs) == 0 {
76
+		// No prior challenge; try pinging the registry endpoint to
77
+		// get a challenge. `http.Client` will follow redirects, so
78
+		// even if we thought it was an insecure (HTTP) host, we may
79
+		// end up requesting HTTPS.
80
+		req, err := http.NewRequest("GET", registryURL.String(), nil)
81
+		if err != nil {
82
+			return nil, err
83
+		}
84
+		res, err := (&http.Client{
85
+			Transport: tx,
86
+		}).Do(req)
87
+		if err != nil {
88
+			return nil, err
89
+		}
90
+		if err = manager.AddResponse(res); err != nil {
91
+			return nil, err
75 92
 		}
93
+		registryURL = *res.Request.URL // <- the URL after any redirection
76 94
 	}
77 95
 
78 96
 	cred := creds.credsFor(repo.Domain)
79 97
 	if f.Trace {
80
-		f.Logger.Log("repo", repo.String(), "auth", cred.String())
98
+		f.Logger.Log("repo", repo.String(), "auth", cred.String(), "api", registryURL.String())
81 99
 	}
82 100
 
83
-	handler := auth.NewTokenHandler(tx, &store{cred}, repo.Image, "pull")
84
-	tx = transport.NewTransport(tx, auth.NewAuthorizer(manager, handler))
101
+	tokenHandler := auth.NewTokenHandler(tx, &store{cred}, repo.Image, "pull")
102
+	basicauthHandler := auth.NewBasicHandler(&store{cred})
103
+	tx = transport.NewTransport(tx, auth.NewAuthorizer(manager, tokenHandler, basicauthHandler))
85 104
 
86
-	client := &Remote{transport: tx, repo: repo}
105
+	// For the API base we want only the scheme and host.
106
+	registryURL.Path = ""
107
+	client := &Remote{transport: tx, repo: repo, base: registryURL.String()}
87 108
 	return NewInstrumentedClient(client), nil
88 109
 }
89 110
 

+ 1
- 0
site/daemon.md View File

@@ -61,6 +61,7 @@ fluxd requires setup and offers customization though a multitude of flags.
61 61
 |--registry-poll-interval| `5 minutes`                   | period at which to poll registry for new images|
62 62
 |--registry-rps          | `200`                           | maximum registry requests per second per host|
63 63
 |--registry-burst        | `125`      | maximum number of warmer connections to remote and memcache|
64
+|--registry-insecure-host| []         | registry hosts to use HTTP for (instead of HTTPS) |
64 65
 |**k8s-secret backed ssh keyring configuration**      |  | |
65 66
 |--k8s-secret-name       | `flux-git-deploy`               | name of the k8s secret used to store the private SSH key|
66 67
 |--k8s-secret-volume-mount-path | `/etc/fluxd/ssh`         | mount location of the k8s secret storing the private SSH key|

+ 8
- 0
site/requirements.md View File

@@ -29,3 +29,11 @@ It is _not_ a requirement that the files are arranged in any
29 29
 particular way into directories. Flux will look in subdirectories for
30 30
 YAML files recursively, but does not infer any meaning from the
31 31
 directory structure.
32
+
33
+Flux uses the Docker Registry API to collect metadata about the images
34
+running in the cluster. This comes with at least one limitation:
35
+
36
+ * Since Flux runs in a container in your cluster, it may not be able
37
+   to resolve all hostnames that you or Kubernetes can resolve. In
38
+   particular, it won't be able to get image metadata for images in a
39
+   private image registry that's made available at `localhost`.

Loading…
Cancel
Save