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

Skip TLS check when using insecure host #1526

Merged
merged 5 commits into from
Feb 4, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
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
10 changes: 6 additions & 4 deletions cmd/fluxd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -96,15 +96,17 @@ func main() {
gitTimeout = fs.Duration("git-timeout", 20*time.Second, "duration after which git operations time out")
// syncing
syncInterval = fs.Duration("sync-interval", 5*time.Minute, "apply config in git to cluster at least this often, even if there are no new commits")

// registry
memcachedHostname = fs.String("memcached-hostname", "memcached", "hostname for memcached service.")
memcachedTimeout = fs.Duration("memcached-timeout", time.Second, "maximum time to wait before giving up on memcached requests.")
memcachedService = fs.String("memcached-service", "memcached", "SRV service used to discover memcache servers.")
memcachedHostname = fs.String("memcached-hostname", "memcached", "hostname for memcached service.")
memcachedTimeout = fs.Duration("memcached-timeout", time.Second, "maximum time to wait before giving up on memcached requests.")
memcachedService = fs.String("memcached-service", "memcached", "SRV service used to discover memcache servers.")

registryPollInterval = fs.Duration("registry-poll-interval", 5*time.Minute, "period at which to check for updated images")
registryRPS = fs.Float64("registry-rps", 50, "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")
registryInsecure = fs.StringSlice("registry-insecure-host", []string{}, "let these registry hosts skip TLS host verification and fall back to using HTTP instead of HTTPS; this allows man-in-the-middle attacks, so use with extreme caution")
registryExcludeImage = fs.StringSlice("registry-exclude-image", []string{"k8s.gcr.io/*"}, "do not scan images that match these glob expressions; the default is to exclude the 'k8s.gcr.io/*' images")

// AWS authentication
Expand Down
88 changes: 60 additions & 28 deletions registry/client_factory.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package registry

import (
"context"
"crypto/tls"
"net/http"
"net/url"
"sync"
Expand All @@ -17,9 +18,12 @@ import (
)

type RemoteClientFactory struct {
Logger log.Logger
Limiters *middleware.RateLimiters
Trace bool
Logger log.Logger
Limiters *middleware.RateLimiters
Trace bool

// hosts with which to tolerate insecure connections (e.g., with
// TLS_INSECURE_SKIP_VERIFY, or as a fallback, using HTTP).
InsecureHosts []string

mu sync.Mutex
Expand All @@ -41,35 +45,17 @@ func (t *logging) RoundTrip(req *http.Request) (*http.Response, error) {
return res, err
}

func (f *RemoteClientFactory) ClientFor(repo image.CanonicalName, creds Credentials) (Client, error) {
tx := f.Limiters.RoundTripper(http.DefaultTransport, repo.Domain)
if f.Trace {
tx = &logging{f.Logger, tx}
}

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

scheme := "https"
for _, h := range f.InsecureHosts {
if repo.Domain == h {
scheme = "http"
}
}

func (f *RemoteClientFactory) doChallenge(manager challenge.Manager, tx http.RoundTripper, domain string, insecureOK bool) (*url.URL, error) {
registryURL := url.URL{
Scheme: scheme,
Host: repo.Domain,
Scheme: "https",
Host: domain,
Path: "/v2/",
}

// Before we know how to authorise, need to establish which
// authorisation challenges the host will send. See if we've been
// here before.
attemptChallenge:
cs, err := manager.GetChallenges(registryURL)
if err != nil {
return nil, err
Expand All @@ -89,6 +75,11 @@ func (f *RemoteClientFactory) ClientFor(repo image.CanonicalName, creds Credenti
Transport: tx,
}).Do(req.WithContext(ctx))
if err != nil {
if insecureOK {
registryURL.Scheme = "http"
insecureOK = false
goto attemptChallenge
}
return nil, err
}
defer res.Body.Close()
Expand All @@ -97,15 +88,56 @@ func (f *RemoteClientFactory) ClientFor(repo image.CanonicalName, creds Credenti
}
registryURL = *res.Request.URL // <- the URL after any redirection
}
return &registryURL, nil
}

func (f *RemoteClientFactory) ClientFor(repo image.CanonicalName, creds Credentials) (Client, error) {
insecure := false
for _, h := range f.InsecureHosts {
if repo.Domain == h {
insecure = true
break
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The size of this function is getting out of hand. I would suggest factoring out the challenge obtention (or part of it at least)


tlsConfig := &tls.Config{
InsecureSkipVerify: insecure,
}
// Since we construct one of these per scan, be fairly ruthless
// about throttling the number, and closing of, idle connections.
baseTx := &http.Transport{
TLSClientConfig: tlsConfig,
MaxIdleConns: 10,
IdleConnTimeout: 10 * time.Second,
Proxy: http.ProxyFromEnvironment,
}
tx := f.Limiters.RoundTripper(baseTx, repo.Domain)
if f.Trace {
tx = &logging{f.Logger, tx}
}

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

registryURL, err := f.doChallenge(manager, tx, repo.Domain, insecure)
if err != nil {
return nil, err
}

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

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

// For the API base we want only the scheme and host.
registryURL.Path = ""
Expand Down