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
This commit is contained in:
Caleb Doxsey 2023-03-08 12:40:15 -07:00 committed by GitHub
parent 376bfe053d
commit 1dee325b72
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 36 additions and 34 deletions

View file

@ -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,
)

View file

@ -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...)

View file

@ -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 {

View file

@ -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)

View file

@ -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

View file

@ -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)

View file

@ -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) {

View file

@ -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)
})

View file

@ -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)

View file

@ -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())
})
}