mirror of
https://github.com/pomerium/pomerium.git
synced 2025-07-04 18:38:12 +02:00
authorize: allow access to /.pomerium/webauthn when policy denies access
This commit is contained in:
parent
88915a79c1
commit
2d194e728b
6 changed files with 55 additions and 11 deletions
|
@ -226,14 +226,20 @@ func (a *Authorize) requireWebAuthnResponse(
|
||||||
opts := a.currentOptions.Load()
|
opts := a.currentOptions.Load()
|
||||||
state := a.state.Load()
|
state := a.state.Load()
|
||||||
|
|
||||||
if !a.shouldRedirect(in) {
|
|
||||||
return a.deniedResponse(ctx, in, http.StatusUnauthorized, http.StatusText(http.StatusUnauthorized), nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
// always assume https scheme
|
// always assume https scheme
|
||||||
checkRequestURL := getCheckRequestURL(in)
|
checkRequestURL := getCheckRequestURL(in)
|
||||||
checkRequestURL.Scheme = "https"
|
checkRequestURL.Scheme = "https"
|
||||||
|
|
||||||
|
// If we're already on a webauthn route, return OK.
|
||||||
|
// https://github.com/pomerium/pomerium-console/issues/3210
|
||||||
|
if checkRequestURL.Path == urlutil.WebAuthnURLPath || checkRequestURL.Path == urlutil.DeviceEnrolledPath {
|
||||||
|
return a.okResponse(result.Headers), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if !a.shouldRedirect(in) {
|
||||||
|
return a.deniedResponse(ctx, in, http.StatusUnauthorized, http.StatusText(http.StatusUnauthorized), nil)
|
||||||
|
}
|
||||||
|
|
||||||
q := url.Values{}
|
q := url.Values{}
|
||||||
if deviceType, ok := result.Allow.AdditionalData["device_type"].(string); ok {
|
if deviceType, ok := result.Allow.AdditionalData["device_type"].(string); ok {
|
||||||
q.Set(urlutil.QueryDeviceType, deviceType)
|
q.Set(urlutil.QueryDeviceType, deviceType)
|
||||||
|
|
|
@ -62,6 +62,36 @@ func TestAuthorize_handleResult(t *testing.T) {
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, 302, int(res.GetDeniedResponse().GetStatus().GetCode()))
|
assert.Equal(t, 302, int(res.GetDeniedResponse().GetStatus().GetCode()))
|
||||||
})
|
})
|
||||||
|
t.Run("device-unauthenticated", func(t *testing.T) {
|
||||||
|
res, err := a.handleResult(context.Background(),
|
||||||
|
&envoy_service_auth_v3.CheckRequest{},
|
||||||
|
&evaluator.Request{},
|
||||||
|
&evaluator.Result{
|
||||||
|
Allow: evaluator.NewRuleResult(false, criteria.ReasonDeviceUnauthenticated),
|
||||||
|
})
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, 302, int(res.GetDeniedResponse().GetStatus().GetCode()))
|
||||||
|
|
||||||
|
t.Run("webauthn path", func(t *testing.T) {
|
||||||
|
res, err := a.handleResult(context.Background(),
|
||||||
|
&envoy_service_auth_v3.CheckRequest{
|
||||||
|
Attributes: &envoy_service_auth_v3.AttributeContext{
|
||||||
|
Request: &envoy_service_auth_v3.AttributeContext_Request{
|
||||||
|
Http: &envoy_service_auth_v3.AttributeContext_HttpRequest{
|
||||||
|
Path: "/.pomerium/webauthn",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
&evaluator.Request{},
|
||||||
|
&evaluator.Result{
|
||||||
|
Allow: evaluator.NewRuleResult(true, criteria.ReasonPomeriumRoute),
|
||||||
|
Deny: evaluator.NewRuleResult(false, criteria.ReasonDeviceUnauthenticated),
|
||||||
|
})
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.NotNil(t, res.GetOkResponse())
|
||||||
|
})
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAuthorize_okResponse(t *testing.T) {
|
func TestAuthorize_okResponse(t *testing.T) {
|
||||||
|
|
|
@ -60,7 +60,7 @@ func (b *Builder) buildPomeriumHTTPRoutes(options *config.Options, host string)
|
||||||
routes = append(routes,
|
routes = append(routes,
|
||||||
// enable ext_authz
|
// enable ext_authz
|
||||||
b.buildControlPlanePathRoute("/.pomerium/jwt", true),
|
b.buildControlPlanePathRoute("/.pomerium/jwt", true),
|
||||||
b.buildControlPlanePathRoute("/.pomerium/webauthn", true),
|
b.buildControlPlanePathRoute(urlutil.WebAuthnURLPath, true),
|
||||||
// disable ext_authz and passthrough to proxy handlers
|
// disable ext_authz and passthrough to proxy handlers
|
||||||
b.buildControlPlanePathRoute("/ping", false),
|
b.buildControlPlanePathRoute("/ping", false),
|
||||||
b.buildControlPlanePathRoute("/healthz", false),
|
b.buildControlPlanePathRoute("/healthz", false),
|
||||||
|
|
|
@ -16,6 +16,7 @@ import (
|
||||||
"github.com/pomerium/pomerium/config"
|
"github.com/pomerium/pomerium/config"
|
||||||
"github.com/pomerium/pomerium/config/envoyconfig/filemgr"
|
"github.com/pomerium/pomerium/config/envoyconfig/filemgr"
|
||||||
"github.com/pomerium/pomerium/internal/testutil"
|
"github.com/pomerium/pomerium/internal/testutil"
|
||||||
|
"github.com/pomerium/pomerium/internal/urlutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
func policyNameFunc() func(*config.Policy) string {
|
func policyNameFunc() func(*config.Policy) string {
|
||||||
|
@ -88,7 +89,7 @@ func Test_buildPomeriumHTTPRoutes(t *testing.T) {
|
||||||
|
|
||||||
testutil.AssertProtoJSONEqual(t, `[
|
testutil.AssertProtoJSONEqual(t, `[
|
||||||
`+routeString("path", "/.pomerium/jwt", true)+`,
|
`+routeString("path", "/.pomerium/jwt", true)+`,
|
||||||
`+routeString("path", "/.pomerium/webauthn", true)+`,
|
`+routeString("path", urlutil.WebAuthnURLPath, true)+`,
|
||||||
`+routeString("path", "/ping", false)+`,
|
`+routeString("path", "/ping", false)+`,
|
||||||
`+routeString("path", "/healthz", false)+`,
|
`+routeString("path", "/healthz", false)+`,
|
||||||
`+routeString("path", "/.pomerium", false)+`,
|
`+routeString("path", "/.pomerium", false)+`,
|
||||||
|
@ -127,7 +128,7 @@ func Test_buildPomeriumHTTPRoutes(t *testing.T) {
|
||||||
|
|
||||||
testutil.AssertProtoJSONEqual(t, `[
|
testutil.AssertProtoJSONEqual(t, `[
|
||||||
`+routeString("path", "/.pomerium/jwt", true)+`,
|
`+routeString("path", "/.pomerium/jwt", true)+`,
|
||||||
`+routeString("path", "/.pomerium/webauthn", true)+`,
|
`+routeString("path", urlutil.WebAuthnURLPath, true)+`,
|
||||||
`+routeString("path", "/ping", false)+`,
|
`+routeString("path", "/ping", false)+`,
|
||||||
`+routeString("path", "/healthz", false)+`,
|
`+routeString("path", "/healthz", false)+`,
|
||||||
`+routeString("path", "/.pomerium", false)+`,
|
`+routeString("path", "/.pomerium", false)+`,
|
||||||
|
@ -155,7 +156,7 @@ func Test_buildPomeriumHTTPRoutes(t *testing.T) {
|
||||||
|
|
||||||
testutil.AssertProtoJSONEqual(t, `[
|
testutil.AssertProtoJSONEqual(t, `[
|
||||||
`+routeString("path", "/.pomerium/jwt", true)+`,
|
`+routeString("path", "/.pomerium/jwt", true)+`,
|
||||||
`+routeString("path", "/.pomerium/webauthn", true)+`,
|
`+routeString("path", urlutil.WebAuthnURLPath, true)+`,
|
||||||
`+routeString("path", "/ping", false)+`,
|
`+routeString("path", "/ping", false)+`,
|
||||||
`+routeString("path", "/healthz", false)+`,
|
`+routeString("path", "/healthz", false)+`,
|
||||||
`+routeString("path", "/.pomerium", false)+`,
|
`+routeString("path", "/.pomerium", false)+`,
|
||||||
|
|
|
@ -34,15 +34,21 @@ func SignOutURL(r *http.Request, authenticateURL *url.URL, key []byte) string {
|
||||||
return NewSignedURL(key, u).Sign().String()
|
return NewSignedURL(key, u).Sign().String()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Device paths
|
||||||
|
const (
|
||||||
|
WebAuthnURLPath = "/.pomerium/webauthn"
|
||||||
|
DeviceEnrolledPath = "/.pomerium/device-enrolled"
|
||||||
|
)
|
||||||
|
|
||||||
// WebAuthnURL returns the /.pomerium/webauthn URL.
|
// WebAuthnURL returns the /.pomerium/webauthn URL.
|
||||||
func WebAuthnURL(r *http.Request, authenticateURL *url.URL, key []byte, values url.Values) string {
|
func WebAuthnURL(r *http.Request, authenticateURL *url.URL, key []byte, values url.Values) string {
|
||||||
u := authenticateURL.ResolveReference(&url.URL{
|
u := authenticateURL.ResolveReference(&url.URL{
|
||||||
Path: "/.pomerium/webauthn",
|
Path: WebAuthnURLPath,
|
||||||
RawQuery: buildURLValues(values, url.Values{
|
RawQuery: buildURLValues(values, url.Values{
|
||||||
QueryDeviceType: {DefaultDeviceType},
|
QueryDeviceType: {DefaultDeviceType},
|
||||||
QueryEnrollmentToken: nil,
|
QueryEnrollmentToken: nil,
|
||||||
QueryRedirectURI: {authenticateURL.ResolveReference(&url.URL{
|
QueryRedirectURI: {authenticateURL.ResolveReference(&url.URL{
|
||||||
Path: "/.pomerium/device-enrolled",
|
Path: DeviceEnrolledPath,
|
||||||
}).String()},
|
}).String()},
|
||||||
}).Encode(),
|
}).Encode(),
|
||||||
})
|
})
|
||||||
|
|
|
@ -3,6 +3,7 @@ package criteria
|
||||||
import (
|
import (
|
||||||
"github.com/open-policy-agent/opa/ast"
|
"github.com/open-policy-agent/opa/ast"
|
||||||
|
|
||||||
|
"github.com/pomerium/pomerium/internal/urlutil"
|
||||||
"github.com/pomerium/pomerium/pkg/policy/generator"
|
"github.com/pomerium/pomerium/pkg/policy/generator"
|
||||||
"github.com/pomerium/pomerium/pkg/policy/parser"
|
"github.com/pomerium/pomerium/pkg/policy/parser"
|
||||||
"github.com/pomerium/pomerium/pkg/policy/rules"
|
"github.com/pomerium/pomerium/pkg/policy/rules"
|
||||||
|
@ -34,7 +35,7 @@ func (c pomeriumRoutesCriterion) GenerateRule(_ string, _ parser.Value) (*ast.Ru
|
||||||
r2.Body = ast.Body{
|
r2.Body = ast.Body{
|
||||||
ast.MustParseExpr(`contains(input.http.url, "/.pomerium/")`),
|
ast.MustParseExpr(`contains(input.http.url, "/.pomerium/")`),
|
||||||
ast.MustParseExpr(`not contains(input.http.url, "/.pomerium/jwt")`),
|
ast.MustParseExpr(`not contains(input.http.url, "/.pomerium/jwt")`),
|
||||||
ast.MustParseExpr(`not contains(input.http.url, "/.pomerium/webauthn")`),
|
ast.MustParseExpr(`not contains(input.http.url, "` + urlutil.WebAuthnURLPath + `")`),
|
||||||
}
|
}
|
||||||
r1.Else = r2
|
r1.Else = r2
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue