From a4e04105cb15173ee3b06090de7573540969a89c Mon Sep 17 00:00:00 2001 From: Hank Donnay Date: Thu, 1 Oct 2020 16:45:22 -0500 Subject: [PATCH] config: allow HTTP client to specify claims Before this commit, the client unconditionally created JWTs with a fixed issuer claim. With this commit, the caller is expected to fill in the claims they want for every outgoing request. Signed-off-by: Hank Donnay --- cmd/clairctl/export.go | 3 ++- cmd/clairctl/import.go | 3 ++- config/httpclient.go | 19 ++++++++++--------- httptransport/auth_test.go | 26 +++++++++++++++++++++++++- initialize/services.go | 10 +++++++--- 5 files changed, 46 insertions(+), 15 deletions(-) diff --git a/cmd/clairctl/export.go b/cmd/clairctl/export.go index de4e6fe5d6..9439cbeb0f 100644 --- a/cmd/clairctl/export.go +++ b/cmd/clairctl/export.go @@ -11,6 +11,7 @@ import ( "github.com/quay/claircore/updater" _ "github.com/quay/claircore/updater/defaults" "github.com/urfave/cli/v2" + "gopkg.in/square/go-jose.v2/jwt" ) // ExportCmd is the "export-updaters" subcommand. @@ -70,7 +71,7 @@ func exportAction(c *cli.Context) error { cfgs[name] = node.Decode } - cl, _, err := cfg.Client(nil) + cl, _, err := cfg.Client(nil, jwt.Claims{}) if err != nil { return err } diff --git a/cmd/clairctl/import.go b/cmd/clairctl/import.go index 0e4ac7af41..69dc2b7c57 100644 --- a/cmd/clairctl/import.go +++ b/cmd/clairctl/import.go @@ -11,6 +11,7 @@ import ( "github.com/jackc/pgx/v4/pgxpool" "github.com/quay/claircore/libvuln" "github.com/urfave/cli/v2" + "gopkg.in/square/go-jose.v2/jwt" ) // ImportCmd is the "import-updaters" subcommand. @@ -39,7 +40,7 @@ func importAction(c *cli.Context) error { return err } - cl, _, err := cfg.Client(nil) + cl, _, err := cfg.Client(nil, jwt.Claims{}) if err != nil { return err } diff --git a/config/httpclient.go b/config/httpclient.go index 6d13b6d0fd..550bc57d79 100644 --- a/config/httpclient.go +++ b/config/httpclient.go @@ -13,7 +13,7 @@ import ( // // It returns an *http.Client and a boolean indicating whether the client is // configured for authentication, or an error that occurred during construction. -func (cfg *Config) Client(next *http.Transport) (c *http.Client, authed bool, err error) { +func (cfg *Config) Client(next *http.Transport, cl jwt.Claims) (c *http.Client, authed bool, err error) { if next == nil { next = http.DefaultTransport.(*http.Transport).Clone() } @@ -29,7 +29,10 @@ func (cfg *Config) Client(next *http.Transport) (c *http.Client, authed bool, er sk.Key = cfg.Auth.PSK.Key default: } - rt := &transport{next: next} + rt := &transport{ + next: next, + base: cl, + } c = &http.Client{Transport: rt} // Both of the JWT-based methods set the signing key. @@ -50,11 +53,11 @@ var _ http.RoundTripper = (*transport)(nil) type transport struct { jose.Signer next http.RoundTripper + base jwt.Claims } func (cs *transport) RoundTrip(r *http.Request) (*http.Response, error) { const ( - issuer = `clair-intraservice` userAgent = `clair/v4` ) r.Header.Set("user-agent", userAgent) @@ -62,12 +65,10 @@ func (cs *transport) RoundTrip(r *http.Request) (*http.Response, error) { // TODO(hank) Make this mint longer-lived tokens and re-use them, only // refreshing when needed. Like a resettable sync.Once. now := time.Now() - cl := jwt.Claims{ - IssuedAt: jwt.NewNumericDate(now), - NotBefore: jwt.NewNumericDate(now.Add(-jwt.DefaultLeeway)), - Expiry: jwt.NewNumericDate(now.Add(jwt.DefaultLeeway)), - Issuer: issuer, - } + cl := cs.base + cl.IssuedAt = jwt.NewNumericDate(now) + cl.NotBefore = jwt.NewNumericDate(now.Add(-jwt.DefaultLeeway)) + cl.Expiry = jwt.NewNumericDate(now.Add(jwt.DefaultLeeway)) h, err := jwt.Signed(cs).Claims(&cl).CompactSerialize() if err != nil { return nil, err diff --git a/httptransport/auth_test.go b/httptransport/auth_test.go index 53b1860b96..8a07fb088a 100644 --- a/httptransport/auth_test.go +++ b/httptransport/auth_test.go @@ -10,6 +10,8 @@ import ( "net/http/httptest" "testing" + "gopkg.in/square/go-jose.v2/jwt" + "github.com/quay/clair/v4/config" ) @@ -18,6 +20,11 @@ type authTestcase struct { Config config.Config ShouldFail bool ConfigMod func(*testing.T, *config.Config) + Claims *jwt.Claims +} + +var defaultClaims = jwt.Claims{ + Issuer: IntraserviceIssuer, } func (tc *authTestcase) Run(t *testing.T) { @@ -51,8 +58,13 @@ func (tc *authTestcase) Run(t *testing.T) { f(t, &tc.Config) } + // Use a default intraservice claim if not set. + if tc.Claims == nil { + tc.Claims = &defaultClaims + } + // Create a client that has auth according to the config. - c, authed, err := tc.Config.Client(nil) + c, authed, err := tc.Config.Client(nil, *tc.Claims) if err != nil { t.Error(err) } @@ -101,6 +113,18 @@ func TestAuth(t *testing.T) { }, }, }, + { + Name: "PSKMultipleIssuer", + Config: config.Config{ + Auth: config.Auth{ + PSK: &config.AuthPSK{ + Issuer: []string{`sweet-bro`, `hella-jeff`, `geromy`}, + Key: fakeKey, + }, + }, + }, + Claims: &jwt.Claims{Issuer: `geromy`}, + }, { Name: "FakeKeyserver", Config: config.Config{ diff --git a/initialize/services.go b/initialize/services.go index 1da76fc2fb..3f0a712adc 100644 --- a/initialize/services.go +++ b/initialize/services.go @@ -4,15 +4,17 @@ import ( "fmt" "time" - notifier "github.com/quay/clair/v4/notifier/service" "github.com/quay/claircore/libindex" "github.com/quay/claircore/libvuln" "github.com/quay/claircore/libvuln/driver" "github.com/rs/zerolog" + "gopkg.in/square/go-jose.v2/jwt" clairerror "github.com/quay/clair/v4/clair-error" "github.com/quay/clair/v4/config" + "github.com/quay/clair/v4/httptransport" "github.com/quay/clair/v4/httptransport/client" + notifier "github.com/quay/clair/v4/notifier/service" ) const ( @@ -170,7 +172,7 @@ func (i *Init) Services() error { return fmt.Errorf("failed to initialize libvuln: %v", err) } // matcher mode needs a remote indexer client - c, auth, err := i.conf.Client(nil) + c, auth, err := i.conf.Client(nil, intraservice) switch { case err != nil: return err @@ -190,7 +192,7 @@ func (i *Init) Services() error { i.Matcher = libV case config.NotifierMode: // notifier uses a remote indexer and matcher - c, auth, err := i.conf.Client(nil) + c, auth, err := i.conf.Client(nil, intraservice) switch { case err != nil: return err @@ -254,3 +256,5 @@ func (i *Init) Services() error { return nil } + +var intraservice = jwt.Claims{Issuer: httptransport.IntraserviceIssuer}