mirror of
https://github.com/pomerium/pomerium.git
synced 2025-05-13 17:17:43 +02:00
kubernetes apiserver integration (#1063)
* sessions: support bearer tokens in authorization * wip * remove dead code * refactor signed jwt code * use function * update per comments * fix test
This commit is contained in:
parent
5f6a67e6eb
commit
a70254ab76
10 changed files with 140 additions and 57 deletions
|
@ -9,6 +9,7 @@ import (
|
||||||
envoy_api_v2_core "github.com/envoyproxy/go-control-plane/envoy/api/v2/core"
|
envoy_api_v2_core "github.com/envoyproxy/go-control-plane/envoy/api/v2/core"
|
||||||
envoy_service_auth_v2 "github.com/envoyproxy/go-control-plane/envoy/service/auth/v2"
|
envoy_service_auth_v2 "github.com/envoyproxy/go-control-plane/envoy/service/auth/v2"
|
||||||
envoy_type "github.com/envoyproxy/go-control-plane/envoy/type"
|
envoy_type "github.com/envoyproxy/go-control-plane/envoy/type"
|
||||||
|
"github.com/golang/protobuf/ptypes/wrappers"
|
||||||
"google.golang.org/genproto/googleapis/rpc/status"
|
"google.golang.org/genproto/googleapis/rpc/status"
|
||||||
"google.golang.org/grpc/codes"
|
"google.golang.org/grpc/codes"
|
||||||
|
|
||||||
|
@ -25,7 +26,9 @@ func (a *Authorize) okResponse(reply *evaluator.Result) *envoy_service_auth_v2.C
|
||||||
}
|
}
|
||||||
|
|
||||||
requestHeaders = append(requestHeaders,
|
requestHeaders = append(requestHeaders,
|
||||||
mkHeader(httputil.HeaderPomeriumJWTAssertion, reply.SignedJWT))
|
mkHeader(httputil.HeaderPomeriumJWTAssertion, reply.SignedJWT, false))
|
||||||
|
|
||||||
|
requestHeaders = append(requestHeaders, getKubernetesHeaders(reply)...)
|
||||||
|
|
||||||
return &envoy_service_auth_v2.CheckResponse{
|
return &envoy_service_auth_v2.CheckResponse{
|
||||||
Status: &status.Status{Code: int32(codes.OK), Message: "OK"},
|
Status: &status.Status{Code: int32(codes.OK), Message: "OK"},
|
||||||
|
@ -82,10 +85,10 @@ func (a *Authorize) htmlDeniedResponse(code int32, reason string, headers map[st
|
||||||
}
|
}
|
||||||
|
|
||||||
envoyHeaders := []*envoy_api_v2_core.HeaderValueOption{
|
envoyHeaders := []*envoy_api_v2_core.HeaderValueOption{
|
||||||
mkHeader("Content-Type", "text/html"),
|
mkHeader("Content-Type", "text/html", false),
|
||||||
}
|
}
|
||||||
for k, v := range headers {
|
for k, v := range headers {
|
||||||
envoyHeaders = append(envoyHeaders, mkHeader(k, v))
|
envoyHeaders = append(envoyHeaders, mkHeader(k, v, false))
|
||||||
}
|
}
|
||||||
|
|
||||||
return &envoy_service_auth_v2.CheckResponse{
|
return &envoy_service_auth_v2.CheckResponse{
|
||||||
|
@ -104,10 +107,10 @@ func (a *Authorize) htmlDeniedResponse(code int32, reason string, headers map[st
|
||||||
|
|
||||||
func (a *Authorize) plainTextDeniedResponse(code int32, reason string, headers map[string]string) *envoy_service_auth_v2.CheckResponse {
|
func (a *Authorize) plainTextDeniedResponse(code int32, reason string, headers map[string]string) *envoy_service_auth_v2.CheckResponse {
|
||||||
envoyHeaders := []*envoy_api_v2_core.HeaderValueOption{
|
envoyHeaders := []*envoy_api_v2_core.HeaderValueOption{
|
||||||
mkHeader("Content-Type", "text/plain"),
|
mkHeader("Content-Type", "text/plain", false),
|
||||||
}
|
}
|
||||||
for k, v := range headers {
|
for k, v := range headers {
|
||||||
envoyHeaders = append(envoyHeaders, mkHeader(k, v))
|
envoyHeaders = append(envoyHeaders, mkHeader(k, v, false))
|
||||||
}
|
}
|
||||||
|
|
||||||
return &envoy_service_auth_v2.CheckResponse{
|
return &envoy_service_auth_v2.CheckResponse{
|
||||||
|
@ -138,11 +141,30 @@ func (a *Authorize) redirectResponse(in *envoy_service_auth_v2.CheckRequest) *en
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func mkHeader(k, v string) *envoy_api_v2_core.HeaderValueOption {
|
func getKubernetesHeaders(reply *evaluator.Result) []*envoy_api_v2_core.HeaderValueOption {
|
||||||
|
var requestHeaders []*envoy_api_v2_core.HeaderValueOption
|
||||||
|
if reply.MatchingPolicy != nil && reply.MatchingPolicy.KubernetesServiceAccountToken != "" {
|
||||||
|
requestHeaders = append(requestHeaders,
|
||||||
|
mkHeader("Authorization", "Bearer "+reply.MatchingPolicy.KubernetesServiceAccountToken, false))
|
||||||
|
|
||||||
|
if reply.UserEmail != "" {
|
||||||
|
requestHeaders = append(requestHeaders, mkHeader("Impersonate-User", reply.UserEmail, false))
|
||||||
|
}
|
||||||
|
for _, group := range reply.UserGroups {
|
||||||
|
requestHeaders = append(requestHeaders, mkHeader("Impersonate-Group", group, true))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return requestHeaders
|
||||||
|
}
|
||||||
|
|
||||||
|
func mkHeader(k, v string, shouldAppend bool) *envoy_api_v2_core.HeaderValueOption {
|
||||||
return &envoy_api_v2_core.HeaderValueOption{
|
return &envoy_api_v2_core.HeaderValueOption{
|
||||||
Header: &envoy_api_v2_core.HeaderValue{
|
Header: &envoy_api_v2_core.HeaderValue{
|
||||||
Key: k,
|
Key: k,
|
||||||
Value: v,
|
Value: v,
|
||||||
},
|
},
|
||||||
|
Append: &wrappers.BoolValue{
|
||||||
|
Value: shouldAppend,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,6 +40,7 @@ const (
|
||||||
type Evaluator struct {
|
type Evaluator struct {
|
||||||
rego *rego.Rego
|
rego *rego.Rego
|
||||||
query rego.PreparedEvalQuery
|
query rego.PreparedEvalQuery
|
||||||
|
policies []config.Policy
|
||||||
|
|
||||||
clientCA string
|
clientCA string
|
||||||
authenticateHost string
|
authenticateHost string
|
||||||
|
@ -51,6 +52,7 @@ type Evaluator struct {
|
||||||
func New(options *config.Options) (*Evaluator, error) {
|
func New(options *config.Options) (*Evaluator, error) {
|
||||||
e := &Evaluator{
|
e := &Evaluator{
|
||||||
authenticateHost: options.AuthenticateURL.Host,
|
authenticateHost: options.AuthenticateURL.Host,
|
||||||
|
policies: options.Policies,
|
||||||
}
|
}
|
||||||
if options.ClientCA != "" {
|
if options.ClientCA != "" {
|
||||||
e.clientCA = options.ClientCA
|
e.clientCA = options.ClientCA
|
||||||
|
@ -129,33 +131,40 @@ func (e *Evaluator) Evaluate(ctx context.Context, req *Request) (*Result, error)
|
||||||
return &deny[0], nil
|
return &deny[0], nil
|
||||||
}
|
}
|
||||||
|
|
||||||
signedJWT, err := e.SignedJWT(req)
|
payload := e.JWTPayload(req)
|
||||||
|
|
||||||
|
signedJWT, err := e.SignedJWT(payload)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("error signing JWT: %w", err)
|
return nil, fmt.Errorf("error signing JWT: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
evalResult := &Result{
|
||||||
|
MatchingPolicy: getMatchingPolicy(res[0].Bindings.WithoutWildcards(), e.policies),
|
||||||
|
SignedJWT: signedJWT,
|
||||||
|
}
|
||||||
|
if e, ok := payload["email"].(string); ok {
|
||||||
|
evalResult.UserEmail = e
|
||||||
|
}
|
||||||
|
if gs, ok := payload["groups"].([]string); ok {
|
||||||
|
evalResult.UserGroups = gs
|
||||||
|
}
|
||||||
|
|
||||||
allow := allowed(res[0].Bindings.WithoutWildcards())
|
allow := allowed(res[0].Bindings.WithoutWildcards())
|
||||||
if allow {
|
if allow {
|
||||||
return &Result{
|
evalResult.Status = http.StatusOK
|
||||||
Status: http.StatusOK,
|
evalResult.Message = "OK"
|
||||||
Message: "OK",
|
return evalResult, nil
|
||||||
SignedJWT: signedJWT,
|
|
||||||
}, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if req.Session.ID == "" {
|
if req.Session.ID == "" {
|
||||||
return &Result{
|
evalResult.Status = http.StatusUnauthorized
|
||||||
Status: http.StatusUnauthorized,
|
evalResult.Message = "login required"
|
||||||
Message: "login required",
|
return evalResult, nil
|
||||||
SignedJWT: signedJWT,
|
|
||||||
}, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return &Result{
|
evalResult.Status = http.StatusForbidden
|
||||||
Status: http.StatusForbidden,
|
evalResult.Message = "forbidden"
|
||||||
Message: "forbidden",
|
return evalResult, nil
|
||||||
SignedJWT: signedJWT,
|
|
||||||
}, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ParseSignedJWT parses the input signature and return its payload.
|
// ParseSignedJWT parses the input signature and return its payload.
|
||||||
|
@ -167,17 +176,8 @@ func (e *Evaluator) ParseSignedJWT(signature string) ([]byte, error) {
|
||||||
return object.Verify(&(e.jwk.(*ecdsa.PrivateKey).PublicKey))
|
return object.Verify(&(e.jwk.(*ecdsa.PrivateKey).PublicKey))
|
||||||
}
|
}
|
||||||
|
|
||||||
// SignedJWT returns the signature of given request.
|
// JWTPayload returns the JWT payload for a request.
|
||||||
func (e *Evaluator) SignedJWT(req *Request) (string, error) {
|
func (e *Evaluator) JWTPayload(req *Request) map[string]interface{} {
|
||||||
signerOpt := &jose.SignerOptions{}
|
|
||||||
signer, err := jose.NewSigner(jose.SigningKey{
|
|
||||||
Algorithm: jose.ES256,
|
|
||||||
Key: e.jwk,
|
|
||||||
}, signerOpt.WithHeader("kid", e.kid))
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
payload := map[string]interface{}{
|
payload := map[string]interface{}{
|
||||||
"iss": e.authenticateHost,
|
"iss": e.authenticateHost,
|
||||||
}
|
}
|
||||||
|
@ -200,6 +200,19 @@ func (e *Evaluator) SignedJWT(req *Request) (string, error) {
|
||||||
payload["groups"] = du.GetGroups()
|
payload["groups"] = du.GetGroups()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return payload
|
||||||
|
}
|
||||||
|
|
||||||
|
// SignedJWT returns the signature of given request.
|
||||||
|
func (e *Evaluator) SignedJWT(payload map[string]interface{}) (string, error) {
|
||||||
|
signerOpt := &jose.SignerOptions{}
|
||||||
|
signer, err := jose.NewSigner(jose.SigningKey{
|
||||||
|
Algorithm: jose.ES256,
|
||||||
|
Key: e.jwk,
|
||||||
|
}, signerOpt.WithHeader("kid", e.kid))
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
bs, err := json.Marshal(payload)
|
bs, err := json.Marshal(payload)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -269,6 +282,28 @@ type Result struct {
|
||||||
Status int
|
Status int
|
||||||
Message string
|
Message string
|
||||||
SignedJWT string
|
SignedJWT string
|
||||||
|
MatchingPolicy *config.Policy
|
||||||
|
|
||||||
|
UserEmail string
|
||||||
|
UserGroups []string
|
||||||
|
}
|
||||||
|
|
||||||
|
func getMatchingPolicy(vars rego.Vars, policies []config.Policy) *config.Policy {
|
||||||
|
result, ok := vars["result"].(map[string]interface{})
|
||||||
|
if !ok {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
idx, err := strconv.Atoi(fmt.Sprint(result["route_policy_idx"]))
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if idx >= len(policies) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return &policies[idx]
|
||||||
}
|
}
|
||||||
|
|
||||||
func allowed(vars rego.Vars) bool {
|
func allowed(vars rego.Vars) bool {
|
||||||
|
|
|
@ -80,7 +80,7 @@ func TestEvaluator_SignedJWT(t *testing.T) {
|
||||||
URL: "https://example.com",
|
URL: "https://example.com",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
signedJWT, err := e.SignedJWT(req)
|
signedJWT, err := e.SignedJWT(e.JWTPayload(req))
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.NotEmpty(t, signedJWT)
|
assert.NotEmpty(t, signedJWT)
|
||||||
|
|
||||||
|
@ -101,7 +101,7 @@ func TestEvaluator_JWTWithKID(t *testing.T) {
|
||||||
URL: "https://example.com",
|
URL: "https://example.com",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
signedJWT, err := e.SignedJWT(req)
|
signedJWT, err := e.SignedJWT(e.JWTPayload(req))
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.NotEmpty(t, signedJWT)
|
assert.NotEmpty(t, signedJWT)
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,8 @@ package pomerium.authz
|
||||||
default allow = false
|
default allow = false
|
||||||
|
|
||||||
|
|
||||||
route_policy := first_allowed_route_policy(input.http.url)
|
route_policy_idx := first_allowed_route_policy_idx(input.http.url)
|
||||||
|
route_policy := data.route_policies[route_policy_idx]
|
||||||
session := input.databroker_data.session
|
session := input.databroker_data.session
|
||||||
user := input.databroker_data.user
|
user := input.databroker_data.user
|
||||||
directory_user := input.databroker_data.directory_user
|
directory_user := input.databroker_data.directory_user
|
||||||
|
@ -84,8 +85,8 @@ deny[reason] {
|
||||||
}
|
}
|
||||||
|
|
||||||
# returns the first matching route
|
# returns the first matching route
|
||||||
first_allowed_route_policy(input_url) = first_policy {
|
first_allowed_route_policy_idx(input_url) = first_policy_idx {
|
||||||
first_policy := [policy | some i, policy; policy = data.route_policies[i]; allowed_route(input.http.url, policy)][0]
|
first_policy_idx := [idx | some idx, policy; policy = data.route_policies[idx]; allowed_route(input.http.url, policy)][0]
|
||||||
}
|
}
|
||||||
|
|
||||||
allowed_route(input_url, policy){
|
allowed_route(input_url, policy){
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -173,7 +173,7 @@ func (a *Authorize) getEnvoyRequestHeaders(signedJWT string) ([]*envoy_api_v2_co
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
for k, v := range hdrs {
|
for k, v := range hdrs {
|
||||||
hvos = append(hvos, mkHeader(k, v))
|
hvos = append(hvos, mkHeader(k, v, false))
|
||||||
}
|
}
|
||||||
|
|
||||||
return hvos, nil
|
return hvos, nil
|
||||||
|
|
|
@ -100,6 +100,9 @@ type Policy struct {
|
||||||
// - X-Pomerium-Claim-*
|
// - X-Pomerium-Claim-*
|
||||||
//
|
//
|
||||||
PassIdentityHeaders bool `mapstructure:"pass_identity_headers" yaml:"pass_identity_headers,omitempty"`
|
PassIdentityHeaders bool `mapstructure:"pass_identity_headers" yaml:"pass_identity_headers,omitempty"`
|
||||||
|
|
||||||
|
// KubernetesServiceAccountToken is the kubernetes token to use for upstream requests.
|
||||||
|
KubernetesServiceAccountToken string `mapstructure:"kubernetes_service_account_token" yaml:"kubernetes_service_account_token,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate checks the validity of a policy.
|
// Validate checks the validity of a policy.
|
||||||
|
|
9
go.sum
9
go.sum
|
@ -103,7 +103,6 @@ github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5P
|
||||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||||
github.com/cloudflare/cloudflare-go v0.10.2/go.mod h1:qhVI5MKwBGhdNU89ZRz2plgYutcJ5PCekLxXn56w6SY=
|
github.com/cloudflare/cloudflare-go v0.10.2/go.mod h1:qhVI5MKwBGhdNU89ZRz2plgYutcJ5PCekLxXn56w6SY=
|
||||||
github.com/cncf/udpa v0.0.0-20200629203442-efcf912fb354 h1:JBAT2dkeyeqzQOaAA8tB21Zfyv/nHfaqjZvWIllABnw=
|
|
||||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||||
github.com/cncf/udpa/go v0.0.0-20200313221541-5f7e5dd04533 h1:8wZizuKuZVu5COB7EsBYxBQz8nRcXXn5d4Gt91eJLvU=
|
github.com/cncf/udpa/go v0.0.0-20200313221541-5f7e5dd04533 h1:8wZizuKuZVu5COB7EsBYxBQz8nRcXXn5d4Gt91eJLvU=
|
||||||
github.com/cncf/udpa/go v0.0.0-20200313221541-5f7e5dd04533/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
github.com/cncf/udpa/go v0.0.0-20200313221541-5f7e5dd04533/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
||||||
|
@ -135,8 +134,6 @@ github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4s
|
||||||
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||||
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
||||||
github.com/envoyproxy/go-control-plane v0.9.5 h1:lRJIqDD8yjV1YyPRqecMdytjDLs2fTXq363aCib5xPU=
|
|
||||||
github.com/envoyproxy/go-control-plane v0.9.5/go.mod h1:OXl5to++W0ctG+EHWTFUjiypVxC/Y4VLc/KFU+al13s=
|
|
||||||
github.com/envoyproxy/go-control-plane v0.9.6 h1:GgblEiDzxf5ajlAZY4aC8xp7DwkrGfauFNMGdB2bBv0=
|
github.com/envoyproxy/go-control-plane v0.9.6 h1:GgblEiDzxf5ajlAZY4aC8xp7DwkrGfauFNMGdB2bBv0=
|
||||||
github.com/envoyproxy/go-control-plane v0.9.6/go.mod h1:GFqM7v0B62MraO4PWRedIbhThr/Rf7ev6aHOOPXeaDA=
|
github.com/envoyproxy/go-control-plane v0.9.6/go.mod h1:GFqM7v0B62MraO4PWRedIbhThr/Rf7ev6aHOOPXeaDA=
|
||||||
github.com/envoyproxy/protoc-gen-validate v0.1.0 h1:EQciDnbrYxy13PgWoY8AqoxGiPrpgBZ1R8UNe3ddc+A=
|
github.com/envoyproxy/protoc-gen-validate v0.1.0 h1:EQciDnbrYxy13PgWoY8AqoxGiPrpgBZ1R8UNe3ddc+A=
|
||||||
|
@ -381,8 +378,6 @@ github.com/onsi/gocleanup v0.0.0-20140331211545-c1a5478700b5/go.mod h1:tHaogb+iP
|
||||||
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||||
github.com/onsi/gomega v1.8.1 h1:C5Dqfs/LeauYDX0jJXIe2SWmwCbGzx9yF8C8xy3Lh34=
|
github.com/onsi/gomega v1.8.1 h1:C5Dqfs/LeauYDX0jJXIe2SWmwCbGzx9yF8C8xy3Lh34=
|
||||||
github.com/onsi/gomega v1.8.1/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA=
|
github.com/onsi/gomega v1.8.1/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA=
|
||||||
github.com/open-policy-agent/opa v0.21.0 h1:0CVq4EEUP+fJEzjwd9yNLSTWZk4W5rM+QbjdLcT1nY0=
|
|
||||||
github.com/open-policy-agent/opa v0.21.0/go.mod h1:cZaTfhxsj7QdIiUI0U9aBtOLLTqVNe+XE60+9kZKLHw=
|
|
||||||
github.com/open-policy-agent/opa v0.21.1 h1:c4lUnB0mO2KssiUnyh6Y9IGhggvXI3EgObkmhVTvEqQ=
|
github.com/open-policy-agent/opa v0.21.1 h1:c4lUnB0mO2KssiUnyh6Y9IGhggvXI3EgObkmhVTvEqQ=
|
||||||
github.com/open-policy-agent/opa v0.21.1/go.mod h1:cZaTfhxsj7QdIiUI0U9aBtOLLTqVNe+XE60+9kZKLHw=
|
github.com/open-policy-agent/opa v0.21.1/go.mod h1:cZaTfhxsj7QdIiUI0U9aBtOLLTqVNe+XE60+9kZKLHw=
|
||||||
github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw=
|
github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw=
|
||||||
|
@ -616,8 +611,6 @@ golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLL
|
||||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||||
golang.org/x/net v0.0.0-20200625001655-4c5254603344 h1:vGXIOMxbNfDTk/aXCmfdLgkrSV+Z2tcbze+pEc3v5W4=
|
|
||||||
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
|
||||||
golang.org/x/net v0.0.0-20200707034311-ab3426394381 h1:VXak5I6aEWmAXeQjA+QSZzlgNrpq9mjcfDemuexIKsU=
|
golang.org/x/net v0.0.0-20200707034311-ab3426394381 h1:VXak5I6aEWmAXeQjA+QSZzlgNrpq9mjcfDemuexIKsU=
|
||||||
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||||
|
@ -772,8 +765,6 @@ google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfG
|
||||||
google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||||
google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||||
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
|
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
|
||||||
google.golang.org/genproto v0.0.0-20200707001353-8e8330bf89df h1:HWF6nM8ruGdu1K8IXFR+i2oT3YP+iBfZzCbC9zUfcWo=
|
|
||||||
google.golang.org/genproto v0.0.0-20200707001353-8e8330bf89df/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
|
||||||
google.golang.org/genproto v0.0.0-20200711021454-869866162049 h1:YFTFpQhgvrLrmxtiIncJxFXeCyq84ixuKWVCaCAi9Oc=
|
google.golang.org/genproto v0.0.0-20200711021454-869866162049 h1:YFTFpQhgvrLrmxtiIncJxFXeCyq84ixuKWVCaCAi9Oc=
|
||||||
google.golang.org/genproto v0.0.0-20200711021454-869866162049/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
google.golang.org/genproto v0.0.0-20200711021454-869866162049/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||||
google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
|
google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
|
||||||
|
|
|
@ -54,9 +54,17 @@ func (as *Store) LoadSession(r *http.Request) (string, error) {
|
||||||
// request, header key, and authentication type.
|
// request, header key, and authentication type.
|
||||||
func TokenFromHeader(r *http.Request, authHeader, authType string) string {
|
func TokenFromHeader(r *http.Request, authHeader, authType string) string {
|
||||||
bearer := r.Header.Get(authHeader)
|
bearer := r.Header.Get(authHeader)
|
||||||
atSize := len(authType)
|
// Authorization: Pomerium <JWT>
|
||||||
if len(bearer) > atSize && strings.EqualFold(bearer[0:atSize], authType) {
|
prefix := authType + " "
|
||||||
return bearer[atSize+1:]
|
if strings.HasPrefix(bearer, prefix) {
|
||||||
|
return bearer[len(prefix):]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Authorization: Bearer Pomerium-<JWT>
|
||||||
|
prefix = "Bearer " + authType + "-"
|
||||||
|
if strings.HasPrefix(bearer, prefix) {
|
||||||
|
return bearer[len(prefix):]
|
||||||
|
}
|
||||||
|
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
23
internal/sessions/header/header_store_test.go
Normal file
23
internal/sessions/header/header_store_test.go
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
package header
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestTokenFromHeader(t *testing.T) {
|
||||||
|
t.Run("pomerium type", func(t *testing.T) {
|
||||||
|
r, _ := http.NewRequest("GET", "http://localhost/some/url", nil)
|
||||||
|
r.Header.Set("Authorization", "Pomerium JWT")
|
||||||
|
v := TokenFromHeader(r, "Authorization", "Pomerium")
|
||||||
|
assert.Equal(t, "JWT", v)
|
||||||
|
})
|
||||||
|
t.Run("bearer type", func(t *testing.T) {
|
||||||
|
r, _ := http.NewRequest("GET", "http://localhost/some/url", nil)
|
||||||
|
r.Header.Set("Authorization", "Bearer Pomerium-JWT")
|
||||||
|
v := TokenFromHeader(r, "Authorization", "Pomerium")
|
||||||
|
assert.Equal(t, "JWT", v)
|
||||||
|
})
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue