mirror of
https://github.com/pomerium/pomerium.git
synced 2025-05-29 17:07:24 +02:00
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:
parent
376bfe053d
commit
1dee325b72
10 changed files with 36 additions and 34 deletions
|
@ -17,7 +17,6 @@ import (
|
||||||
"github.com/pomerium/pomerium/internal/httputil"
|
"github.com/pomerium/pomerium/internal/httputil"
|
||||||
"github.com/pomerium/pomerium/internal/log"
|
"github.com/pomerium/pomerium/internal/log"
|
||||||
"github.com/pomerium/pomerium/internal/telemetry/trace"
|
"github.com/pomerium/pomerium/internal/telemetry/trace"
|
||||||
"github.com/pomerium/pomerium/internal/urlutil"
|
|
||||||
"github.com/pomerium/pomerium/pkg/contextutil"
|
"github.com/pomerium/pomerium/pkg/contextutil"
|
||||||
"github.com/pomerium/pomerium/pkg/cryptutil"
|
"github.com/pomerium/pomerium/pkg/cryptutil"
|
||||||
"github.com/pomerium/pomerium/pkg/policy/criteria"
|
"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)
|
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(
|
e.store.UpdateGoogleCloudServerlessAuthenticationServiceAccount(
|
||||||
cfg.googleCloudServerlessAuthenticationServiceAccount,
|
cfg.googleCloudServerlessAuthenticationServiceAccount,
|
||||||
)
|
)
|
||||||
|
|
|
@ -33,7 +33,6 @@ func TestEvaluator(t *testing.T) {
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
ctx = storage.WithQuerier(ctx, storage.NewStaticQuerier(data...))
|
ctx = storage.WithQuerier(ctx, storage.NewStaticQuerier(data...))
|
||||||
store := store.New()
|
store := store.New()
|
||||||
store.UpdateIssuer("authenticate.example.com")
|
|
||||||
store.UpdateJWTClaimHeaders(config.NewJWTClaimHeaders("email", "groups", "user", "CUSTOM_KEY"))
|
store.UpdateJWTClaimHeaders(config.NewJWTClaimHeaders("email", "groups", "user", "CUSTOM_KEY"))
|
||||||
store.UpdateSigningKey(privateJWK)
|
store.UpdateSigningKey(privateJWK)
|
||||||
e, err := New(ctx, store, options...)
|
e, err := New(ctx, store, options...)
|
||||||
|
|
|
@ -20,7 +20,7 @@ import (
|
||||||
type HeadersRequest struct {
|
type HeadersRequest struct {
|
||||||
EnableGoogleCloudServerlessAuthentication bool `json:"enable_google_cloud_serverless_authentication"`
|
EnableGoogleCloudServerlessAuthentication bool `json:"enable_google_cloud_serverless_authentication"`
|
||||||
EnableRoutingKey bool `json:"enable_routing_key"`
|
EnableRoutingKey bool `json:"enable_routing_key"`
|
||||||
FromAudience string `json:"from_audience"`
|
Issuer string `json:"issuer"`
|
||||||
KubernetesServiceAccountToken string `json:"kubernetes_service_account_token"`
|
KubernetesServiceAccountToken string `json:"kubernetes_service_account_token"`
|
||||||
ToAudience string `json:"to_audience"`
|
ToAudience string `json:"to_audience"`
|
||||||
Session RequestSession `json:"session"`
|
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 ||
|
input.EnableRoutingKey = policy.EnvoyOpts.GetLbPolicy() == envoy_config_cluster_v3.Cluster_RING_HASH ||
|
||||||
policy.EnvoyOpts.GetLbPolicy() == envoy_config_cluster_v3.Cluster_MAGLEV
|
policy.EnvoyOpts.GetLbPolicy() == envoy_config_cluster_v3.Cluster_MAGLEV
|
||||||
if u, err := urlutil.ParseAndValidateURL(policy.From); err == nil {
|
if u, err := urlutil.ParseAndValidateURL(policy.From); err == nil {
|
||||||
input.FromAudience = u.Hostname()
|
input.Issuer = u.Hostname()
|
||||||
}
|
}
|
||||||
input.KubernetesServiceAccountToken = policy.KubernetesServiceAccountToken
|
input.KubernetesServiceAccountToken = policy.KubernetesServiceAccountToken
|
||||||
for _, wu := range policy.To {
|
for _, wu := range policy.To {
|
||||||
|
|
|
@ -31,8 +31,8 @@ func TestNewHeadersRequestFromPolicy(t *testing.T) {
|
||||||
})
|
})
|
||||||
assert.Equal(t, &HeadersRequest{
|
assert.Equal(t, &HeadersRequest{
|
||||||
EnableGoogleCloudServerlessAuthentication: true,
|
EnableGoogleCloudServerlessAuthentication: true,
|
||||||
FromAudience: "from.example.com",
|
Issuer: "from.example.com",
|
||||||
ToAudience: "https://to.example.com",
|
ToAudience: "https://to.example.com",
|
||||||
}, req)
|
}, req)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -53,7 +53,6 @@ func TestHeadersEvaluator(t *testing.T) {
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
ctx = storage.WithQuerier(ctx, storage.NewStaticQuerier(data...))
|
ctx = storage.WithQuerier(ctx, storage.NewStaticQuerier(data...))
|
||||||
store := store.New()
|
store := store.New()
|
||||||
store.UpdateIssuer("authenticate.example.com")
|
|
||||||
store.UpdateJWTClaimHeaders(config.NewJWTClaimHeaders("email", "groups", "user", "CUSTOM_KEY"))
|
store.UpdateJWTClaimHeaders(config.NewJWTClaimHeaders("email", "groups", "user", "CUSTOM_KEY"))
|
||||||
store.UpdateSigningKey(privateJWK)
|
store.UpdateSigningKey(privateJWK)
|
||||||
e, err := NewHeadersEvaluator(ctx, store)
|
e, err := NewHeadersEvaluator(ctx, store)
|
||||||
|
@ -72,8 +71,8 @@ func TestHeadersEvaluator(t *testing.T) {
|
||||||
}},
|
}},
|
||||||
},
|
},
|
||||||
&HeadersRequest{
|
&HeadersRequest{
|
||||||
FromAudience: "from.example.com",
|
Issuer: "from.example.com",
|
||||||
ToAudience: "to.example.com",
|
ToAudience: "to.example.com",
|
||||||
Session: RequestSession{
|
Session: RequestSession{
|
||||||
ID: "s1",
|
ID: "s1",
|
||||||
},
|
},
|
||||||
|
@ -87,6 +86,8 @@ func TestHeadersEvaluator(t *testing.T) {
|
||||||
err = rawJWT.Claims(publicJWK, &claims)
|
err = rawJWT.Claims(publicJWK, &claims)
|
||||||
require.NoError(t, err)
|
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.Equal(t, claims["exp"], math.Round(claims["exp"].(float64)))
|
||||||
assert.LessOrEqual(t, claims["exp"], float64(time.Now().Add(time.Minute*6).Unix()),
|
assert.LessOrEqual(t, claims["exp"], float64(time.Now().Add(time.Minute*6).Unix()),
|
||||||
"JWT should expire within 5 minutes, but got: %v", claims["exp"])
|
"JWT should expire within 5 minutes, but got: %v", claims["exp"])
|
||||||
|
@ -104,7 +105,7 @@ func TestHeadersEvaluator(t *testing.T) {
|
||||||
}},
|
}},
|
||||||
},
|
},
|
||||||
&HeadersRequest{
|
&HeadersRequest{
|
||||||
FromAudience: "from.example.com",
|
Issuer: "from.example.com",
|
||||||
ToAudience: "to.example.com",
|
ToAudience: "to.example.com",
|
||||||
Session: RequestSession{ID: "s1"},
|
Session: RequestSession{ID: "s1"},
|
||||||
PassAccessToken: true,
|
PassAccessToken: true,
|
||||||
|
@ -122,10 +123,10 @@ func TestHeadersEvaluator(t *testing.T) {
|
||||||
}},
|
}},
|
||||||
},
|
},
|
||||||
&HeadersRequest{
|
&HeadersRequest{
|
||||||
FromAudience: "from.example.com",
|
Issuer: "from.example.com",
|
||||||
ToAudience: "to.example.com",
|
ToAudience: "to.example.com",
|
||||||
Session: RequestSession{ID: "s1"},
|
Session: RequestSession{ID: "s1"},
|
||||||
PassIDToken: true,
|
PassIDToken: true,
|
||||||
})
|
})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@ package pomerium.headers
|
||||||
# input:
|
# input:
|
||||||
# enable_google_cloud_serverless_authentication: boolean
|
# enable_google_cloud_serverless_authentication: boolean
|
||||||
# enable_routing_key: boolean
|
# enable_routing_key: boolean
|
||||||
# from_audience: string
|
# issuer: string
|
||||||
# kubernetes_service_account_token: string
|
# kubernetes_service_account_token: string
|
||||||
# session:
|
# session:
|
||||||
# id: string
|
# id: string
|
||||||
|
@ -12,7 +12,6 @@ package pomerium.headers
|
||||||
# pass_id_token: boolean
|
# pass_id_token: boolean
|
||||||
#
|
#
|
||||||
# data:
|
# data:
|
||||||
# issuer: string
|
|
||||||
# jwt_claim_headers: map[string]string
|
# jwt_claim_headers: map[string]string
|
||||||
# signing_key:
|
# signing_key:
|
||||||
# alg: string
|
# alg: string
|
||||||
|
@ -81,12 +80,16 @@ jwt_headers = {
|
||||||
}
|
}
|
||||||
|
|
||||||
jwt_payload_aud = v {
|
jwt_payload_aud = v {
|
||||||
v := input.from_audience
|
v := input.issuer
|
||||||
} else = "" {
|
} else = "" {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
jwt_payload_iss = data.issuer
|
jwt_payload_iss = v {
|
||||||
|
v := input.issuer
|
||||||
|
} else = "" {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
jwt_payload_jti = v {
|
jwt_payload_jti = v {
|
||||||
v = session.id
|
v = session.id
|
||||||
|
|
|
@ -35,7 +35,6 @@ func TestPolicyEvaluator(t *testing.T) {
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
ctx = storage.WithQuerier(ctx, storage.NewStaticQuerier(data...))
|
ctx = storage.WithQuerier(ctx, storage.NewStaticQuerier(data...))
|
||||||
store := store.New()
|
store := store.New()
|
||||||
store.UpdateIssuer("authenticate.example.com")
|
|
||||||
store.UpdateJWTClaimHeaders(config.NewJWTClaimHeaders("email", "groups", "user", "CUSTOM_KEY"))
|
store.UpdateJWTClaimHeaders(config.NewJWTClaimHeaders("email", "groups", "user", "CUSTOM_KEY"))
|
||||||
store.UpdateSigningKey(privateJWK)
|
store.UpdateSigningKey(privateJWK)
|
||||||
e, err := NewPolicyEvaluator(ctx, store, policy)
|
e, err := NewPolicyEvaluator(ctx, store, policy)
|
||||||
|
|
|
@ -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
|
// UpdateGoogleCloudServerlessAuthenticationServiceAccount updates the google cloud serverless authentication
|
||||||
// service account in the store.
|
// service account in the store.
|
||||||
func (s *Store) UpdateGoogleCloudServerlessAuthenticationServiceAccount(serviceAccount string) {
|
func (s *Store) UpdateGoogleCloudServerlessAuthenticationServiceAccount(serviceAccount string) {
|
||||||
|
|
|
@ -53,8 +53,8 @@ func TestServerHTTP(t *testing.T) {
|
||||||
|
|
||||||
expect := map[string]any{
|
expect := map[string]any{
|
||||||
"authentication_callback_endpoint": "https://authenticate.localhost.pomerium.io/oauth2/callback",
|
"authentication_callback_endpoint": "https://authenticate.localhost.pomerium.io/oauth2/callback",
|
||||||
"frontchannel_logout_uri": "https://authenticate.localhost.pomerium.io/.pomerium/sign_out",
|
"frontchannel_logout_uri": fmt.Sprintf("https://localhost:%s/.pomerium/sign_out", src.GetConfig().HTTPPort),
|
||||||
"jwks_uri": "https://authenticate.localhost.pomerium.io/.well-known/pomerium/jwks.json",
|
"jwks_uri": fmt.Sprintf("https://localhost:%s/.well-known/pomerium/jwks.json", src.GetConfig().HTTPPort),
|
||||||
}
|
}
|
||||||
assert.Equal(t, expect, actual)
|
assert.Equal(t, expect, actual)
|
||||||
})
|
})
|
||||||
|
|
|
@ -8,6 +8,7 @@ import (
|
||||||
|
|
||||||
"github.com/pomerium/csrf"
|
"github.com/pomerium/csrf"
|
||||||
"github.com/pomerium/pomerium/internal/httputil"
|
"github.com/pomerium/pomerium/internal/httputil"
|
||||||
|
"github.com/pomerium/pomerium/internal/urlutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
// WellKnownPomerium returns the /.well-known/pomerium handler.
|
// 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
|
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: "/oauth2/callback"}).String(),
|
||||||
authenticateURL.ResolveReference(&url.URL{Path: "/.well-known/pomerium/jwks.json"}).String(),
|
urlutil.GetAbsoluteURL(r).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: "/.pomerium/sign_out"}).String(),
|
||||||
}
|
}
|
||||||
w.Header().Set("X-CSRF-Token", csrf.Token(r))
|
w.Header().Set("X-CSRF-Token", csrf.Token(r))
|
||||||
httputil.RenderJSON(w, http.StatusOK, wellKnownURLs)
|
httputil.RenderJSON(w, http.StatusOK, wellKnownURLs)
|
||||||
|
|
|
@ -21,4 +21,15 @@ func TestWellKnownPomeriumHandler(t *testing.T) {
|
||||||
WellKnownPomerium(authenticateURL).ServeHTTP(w, r)
|
WellKnownPomerium(authenticateURL).ServeHTTP(w, r)
|
||||||
assert.Equal(t, http.StatusNoContent, w.Result().StatusCode)
|
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())
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue