From 1dee325b72257bc8ccc2845d56db081b65db5001 Mon Sep 17 00:00:00 2001 From: Caleb Doxsey Date: Wed, 8 Mar 2023 12:40:15 -0700 Subject: [PATCH] authorize: move sign out and jwks urls to route, update issuer for JWT (#4046) * authorize: move sign out and jwks urls to route, update issuer for JWT * fix test --- authorize/evaluator/evaluator.go | 7 ------- authorize/evaluator/evaluator_test.go | 1 - authorize/evaluator/headers_evaluator.go | 4 ++-- authorize/evaluator/headers_evaluator_test.go | 21 ++++++++++--------- authorize/evaluator/opa/policy/headers.rego | 11 ++++++---- authorize/evaluator/policy_evaluator_test.go | 1 - authorize/internal/store/store.go | 5 ----- internal/controlplane/server_test.go | 4 ++-- internal/handlers/well_known_pomerium.go | 5 +++-- internal/handlers/well_known_pomerium_test.go | 11 ++++++++++ 10 files changed, 36 insertions(+), 34 deletions(-) diff --git a/authorize/evaluator/evaluator.go b/authorize/evaluator/evaluator.go index 36dd8d453..9cda1e241 100644 --- a/authorize/evaluator/evaluator.go +++ b/authorize/evaluator/evaluator.go @@ -17,7 +17,6 @@ import ( "github.com/pomerium/pomerium/internal/httputil" "github.com/pomerium/pomerium/internal/log" "github.com/pomerium/pomerium/internal/telemetry/trace" - "github.com/pomerium/pomerium/internal/urlutil" "github.com/pomerium/pomerium/pkg/contextutil" "github.com/pomerium/pomerium/pkg/cryptutil" "github.com/pomerium/pomerium/pkg/policy/criteria" @@ -204,12 +203,6 @@ func (e *Evaluator) updateStore(cfg *evaluatorConfig) error { return fmt.Errorf("authorize: couldn't create signer: %w", err) } - authenticateURL, err := urlutil.ParseAndValidateURL(cfg.authenticateURL) - if err != nil { - return fmt.Errorf("authorize: invalid authenticate URL: %w", err) - } - - e.store.UpdateIssuer(authenticateURL.Host) e.store.UpdateGoogleCloudServerlessAuthenticationServiceAccount( cfg.googleCloudServerlessAuthenticationServiceAccount, ) diff --git a/authorize/evaluator/evaluator_test.go b/authorize/evaluator/evaluator_test.go index baa73dfb5..baa1a1871 100644 --- a/authorize/evaluator/evaluator_test.go +++ b/authorize/evaluator/evaluator_test.go @@ -33,7 +33,6 @@ func TestEvaluator(t *testing.T) { ctx := context.Background() ctx = storage.WithQuerier(ctx, storage.NewStaticQuerier(data...)) store := store.New() - store.UpdateIssuer("authenticate.example.com") store.UpdateJWTClaimHeaders(config.NewJWTClaimHeaders("email", "groups", "user", "CUSTOM_KEY")) store.UpdateSigningKey(privateJWK) e, err := New(ctx, store, options...) diff --git a/authorize/evaluator/headers_evaluator.go b/authorize/evaluator/headers_evaluator.go index 42d5e5029..70d3d759f 100644 --- a/authorize/evaluator/headers_evaluator.go +++ b/authorize/evaluator/headers_evaluator.go @@ -20,7 +20,7 @@ import ( type HeadersRequest struct { EnableGoogleCloudServerlessAuthentication bool `json:"enable_google_cloud_serverless_authentication"` EnableRoutingKey bool `json:"enable_routing_key"` - FromAudience string `json:"from_audience"` + Issuer string `json:"issuer"` KubernetesServiceAccountToken string `json:"kubernetes_service_account_token"` ToAudience string `json:"to_audience"` Session RequestSession `json:"session"` @@ -35,7 +35,7 @@ func NewHeadersRequestFromPolicy(policy *config.Policy) *HeadersRequest { input.EnableRoutingKey = policy.EnvoyOpts.GetLbPolicy() == envoy_config_cluster_v3.Cluster_RING_HASH || policy.EnvoyOpts.GetLbPolicy() == envoy_config_cluster_v3.Cluster_MAGLEV if u, err := urlutil.ParseAndValidateURL(policy.From); err == nil { - input.FromAudience = u.Hostname() + input.Issuer = u.Hostname() } input.KubernetesServiceAccountToken = policy.KubernetesServiceAccountToken for _, wu := range policy.To { diff --git a/authorize/evaluator/headers_evaluator_test.go b/authorize/evaluator/headers_evaluator_test.go index e701d6f84..0892569ac 100644 --- a/authorize/evaluator/headers_evaluator_test.go +++ b/authorize/evaluator/headers_evaluator_test.go @@ -31,8 +31,8 @@ func TestNewHeadersRequestFromPolicy(t *testing.T) { }) assert.Equal(t, &HeadersRequest{ EnableGoogleCloudServerlessAuthentication: true, - FromAudience: "from.example.com", - ToAudience: "https://to.example.com", + Issuer: "from.example.com", + ToAudience: "https://to.example.com", }, req) } @@ -53,7 +53,6 @@ func TestHeadersEvaluator(t *testing.T) { ctx := context.Background() ctx = storage.WithQuerier(ctx, storage.NewStaticQuerier(data...)) store := store.New() - store.UpdateIssuer("authenticate.example.com") store.UpdateJWTClaimHeaders(config.NewJWTClaimHeaders("email", "groups", "user", "CUSTOM_KEY")) store.UpdateSigningKey(privateJWK) e, err := NewHeadersEvaluator(ctx, store) @@ -72,8 +71,8 @@ func TestHeadersEvaluator(t *testing.T) { }}, }, &HeadersRequest{ - FromAudience: "from.example.com", - ToAudience: "to.example.com", + Issuer: "from.example.com", + ToAudience: "to.example.com", Session: RequestSession{ ID: "s1", }, @@ -87,6 +86,8 @@ func TestHeadersEvaluator(t *testing.T) { err = rawJWT.Claims(publicJWK, &claims) require.NoError(t, err) + assert.Equal(t, claims["iss"], "from.example.com") + assert.Equal(t, claims["aud"], "from.example.com") assert.Equal(t, claims["exp"], math.Round(claims["exp"].(float64))) assert.LessOrEqual(t, claims["exp"], float64(time.Now().Add(time.Minute*6).Unix()), "JWT should expire within 5 minutes, but got: %v", claims["exp"]) @@ -104,7 +105,7 @@ func TestHeadersEvaluator(t *testing.T) { }}, }, &HeadersRequest{ - FromAudience: "from.example.com", + Issuer: "from.example.com", ToAudience: "to.example.com", Session: RequestSession{ID: "s1"}, PassAccessToken: true, @@ -122,10 +123,10 @@ func TestHeadersEvaluator(t *testing.T) { }}, }, &HeadersRequest{ - FromAudience: "from.example.com", - ToAudience: "to.example.com", - Session: RequestSession{ID: "s1"}, - PassIDToken: true, + Issuer: "from.example.com", + ToAudience: "to.example.com", + Session: RequestSession{ID: "s1"}, + PassIDToken: true, }) require.NoError(t, err) diff --git a/authorize/evaluator/opa/policy/headers.rego b/authorize/evaluator/opa/policy/headers.rego index 0c8b9da05..4b0590ab4 100644 --- a/authorize/evaluator/opa/policy/headers.rego +++ b/authorize/evaluator/opa/policy/headers.rego @@ -3,7 +3,7 @@ package pomerium.headers # input: # enable_google_cloud_serverless_authentication: boolean # enable_routing_key: boolean -# from_audience: string +# issuer: string # kubernetes_service_account_token: string # session: # id: string @@ -12,7 +12,6 @@ package pomerium.headers # pass_id_token: boolean # # data: -# issuer: string # jwt_claim_headers: map[string]string # signing_key: # alg: string @@ -81,12 +80,16 @@ jwt_headers = { } jwt_payload_aud = v { - v := input.from_audience + v := input.issuer } else = "" { true } -jwt_payload_iss = data.issuer +jwt_payload_iss = v { + v := input.issuer +} else = "" { + true +} jwt_payload_jti = v { v = session.id diff --git a/authorize/evaluator/policy_evaluator_test.go b/authorize/evaluator/policy_evaluator_test.go index f0d7e2c69..32edc41ea 100644 --- a/authorize/evaluator/policy_evaluator_test.go +++ b/authorize/evaluator/policy_evaluator_test.go @@ -35,7 +35,6 @@ func TestPolicyEvaluator(t *testing.T) { ctx := context.Background() ctx = storage.WithQuerier(ctx, storage.NewStaticQuerier(data...)) store := store.New() - store.UpdateIssuer("authenticate.example.com") store.UpdateJWTClaimHeaders(config.NewJWTClaimHeaders("email", "groups", "user", "CUSTOM_KEY")) store.UpdateSigningKey(privateJWK) e, err := NewPolicyEvaluator(ctx, store, policy) diff --git a/authorize/internal/store/store.go b/authorize/internal/store/store.go index 6f4f929d8..f9f9e08d3 100644 --- a/authorize/internal/store/store.go +++ b/authorize/internal/store/store.go @@ -36,11 +36,6 @@ func New() *Store { } } -// UpdateIssuer updates the issuer in the store. The issuer is used as part of JWT construction. -func (s *Store) UpdateIssuer(issuer string) { - s.write("/issuer", issuer) -} - // UpdateGoogleCloudServerlessAuthenticationServiceAccount updates the google cloud serverless authentication // service account in the store. func (s *Store) UpdateGoogleCloudServerlessAuthenticationServiceAccount(serviceAccount string) { diff --git a/internal/controlplane/server_test.go b/internal/controlplane/server_test.go index 4f953a608..0b1bd0fff 100644 --- a/internal/controlplane/server_test.go +++ b/internal/controlplane/server_test.go @@ -53,8 +53,8 @@ func TestServerHTTP(t *testing.T) { expect := map[string]any{ "authentication_callback_endpoint": "https://authenticate.localhost.pomerium.io/oauth2/callback", - "frontchannel_logout_uri": "https://authenticate.localhost.pomerium.io/.pomerium/sign_out", - "jwks_uri": "https://authenticate.localhost.pomerium.io/.well-known/pomerium/jwks.json", + "frontchannel_logout_uri": fmt.Sprintf("https://localhost:%s/.pomerium/sign_out", src.GetConfig().HTTPPort), + "jwks_uri": fmt.Sprintf("https://localhost:%s/.well-known/pomerium/jwks.json", src.GetConfig().HTTPPort), } assert.Equal(t, expect, actual) }) diff --git a/internal/handlers/well_known_pomerium.go b/internal/handlers/well_known_pomerium.go index c9525deca..30dab5a05 100644 --- a/internal/handlers/well_known_pomerium.go +++ b/internal/handlers/well_known_pomerium.go @@ -8,6 +8,7 @@ import ( "github.com/pomerium/csrf" "github.com/pomerium/pomerium/internal/httputil" + "github.com/pomerium/pomerium/internal/urlutil" ) // WellKnownPomerium returns the /.well-known/pomerium handler. @@ -19,8 +20,8 @@ func WellKnownPomerium(authenticateURL *url.URL) http.Handler { FrontchannelLogoutURI string `json:"frontchannel_logout_uri"` // https://openid.net/specs/openid-connect-frontchannel-1_0.html }{ authenticateURL.ResolveReference(&url.URL{Path: "/oauth2/callback"}).String(), - authenticateURL.ResolveReference(&url.URL{Path: "/.well-known/pomerium/jwks.json"}).String(), - authenticateURL.ResolveReference(&url.URL{Path: "/.pomerium/sign_out"}).String(), + urlutil.GetAbsoluteURL(r).ResolveReference(&url.URL{Path: "/.well-known/pomerium/jwks.json"}).String(), + urlutil.GetAbsoluteURL(r).ResolveReference(&url.URL{Path: "/.pomerium/sign_out"}).String(), } w.Header().Set("X-CSRF-Token", csrf.Token(r)) httputil.RenderJSON(w, http.StatusOK, wellKnownURLs) diff --git a/internal/handlers/well_known_pomerium_test.go b/internal/handlers/well_known_pomerium_test.go index fc690e882..598fcb4ba 100644 --- a/internal/handlers/well_known_pomerium_test.go +++ b/internal/handlers/well_known_pomerium_test.go @@ -21,4 +21,15 @@ func TestWellKnownPomeriumHandler(t *testing.T) { WellKnownPomerium(authenticateURL).ServeHTTP(w, r) assert.Equal(t, http.StatusNoContent, w.Result().StatusCode) }) + t.Run("links", func(t *testing.T) { + authenticateURL, _ := url.Parse("https://authenticate.example.com") + w := httptest.NewRecorder() + r := httptest.NewRequest(http.MethodGet, "https://route.example.com", nil) + WellKnownPomerium(authenticateURL).ServeHTTP(w, r) + assert.JSONEq(t, `{ + "authentication_callback_endpoint": "https://authenticate.example.com/oauth2/callback", + "frontchannel_logout_uri": "https://route.example.com/.pomerium/sign_out", + "jwks_uri": "https://route.example.com/.well-known/pomerium/jwks.json" + }`, w.Body.String()) + }) }