Merge remote-tracking branch 'upstream/master' into helm-example

# Conflicts:
#	CHANGELOG.md
This commit is contained in:
Evgeni Gordeev 2020-06-25 15:24:00 -05:00
commit 054979978f
7 changed files with 120 additions and 73 deletions

View File

@ -56,6 +56,7 @@
## Changes since v5.1.1 ## Changes since v5.1.1
- [#615](https://github.com/oauth2-proxy/oauth2-proxy/pull/615) Kubernetes example based on Kind cluster and Nginx ingress (@EvgeniGordeev) - [#615](https://github.com/oauth2-proxy/oauth2-proxy/pull/615) Kubernetes example based on Kind cluster and Nginx ingress (@EvgeniGordeev)
- [#596](https://github.com/oauth2-proxy/oauth2-proxy/pull/596) Validate Bearer IDTokens in headers with correct provider/extra JWT Verifier (@NickMeves)
- [#620](https://github.com/oauth2-proxy/oauth2-proxy/pull/620) Add HealthCheck middleware (@JoelSpeed) - [#620](https://github.com/oauth2-proxy/oauth2-proxy/pull/620) Add HealthCheck middleware (@JoelSpeed)
- [#597](https://github.com/oauth2-proxy/oauth2-proxy/pull/597) Don't log invalid redirect if redirect is empty (@JoelSpeed) - [#597](https://github.com/oauth2-proxy/oauth2-proxy/pull/597) Don't log invalid redirect if redirect is empty (@JoelSpeed)
- [#604](https://github.com/oauth2-proxy/oauth2-proxy/pull/604) Add Keycloak local testing environment (@EvgeniGordeev) - [#604](https://github.com/oauth2-proxy/oauth2-proxy/pull/604) Add Keycloak local testing environment (@EvgeniGordeev)

View File

@ -111,7 +111,8 @@ type OAuthProxy struct {
skipAuthRegex []string skipAuthRegex []string
skipAuthPreflight bool skipAuthPreflight bool
skipJwtBearerTokens bool skipJwtBearerTokens bool
jwtBearerVerifiers []*oidc.IDTokenVerifier mainJwtBearerVerifier *oidc.IDTokenVerifier
extraJwtBearerVerifiers []*oidc.IDTokenVerifier
compiledRegex []*regexp.Regexp compiledRegex []*regexp.Regexp
templates *template.Template templates *template.Template
realClientIPParser ipapi.RealClientIPParser realClientIPParser ipapi.RealClientIPParser
@ -327,7 +328,8 @@ func NewOAuthProxy(opts *options.Options, validator func(string) bool) *OAuthPro
skipAuthRegex: opts.SkipAuthRegex, skipAuthRegex: opts.SkipAuthRegex,
skipAuthPreflight: opts.SkipAuthPreflight, skipAuthPreflight: opts.SkipAuthPreflight,
skipJwtBearerTokens: opts.SkipJwtBearerTokens, skipJwtBearerTokens: opts.SkipJwtBearerTokens,
jwtBearerVerifiers: opts.GetJWTBearerVerifiers(), mainJwtBearerVerifier: opts.GetOIDCVerifier(),
extraJwtBearerVerifiers: opts.GetJWTBearerVerifiers(),
compiledRegex: opts.GetCompiledRegex(), compiledRegex: opts.GetCompiledRegex(),
realClientIPParser: opts.GetRealClientIPParser(), realClientIPParser: opts.GetRealClientIPParser(),
SetXAuthRequest: opts.SetXAuthRequest, SetXAuthRequest: opts.SetXAuthRequest,
@ -1139,15 +1141,24 @@ func (p *OAuthProxy) GetJwtSession(req *http.Request) (*sessionsapi.SessionState
return nil, err return nil, err
} }
for _, verifier := range p.jwtBearerVerifiers { // If we are using an oidc provider, go ahead and try that provider first with its Verifier
bearerToken, err := verifier.Verify(req.Context(), rawBearerToken) // and Bearer Token -> Session converter
if p.mainJwtBearerVerifier != nil {
bearerToken, err := p.mainJwtBearerVerifier.Verify(req.Context(), rawBearerToken)
if err == nil {
return p.provider.CreateSessionStateFromBearerToken(req.Context(), rawBearerToken, bearerToken)
}
}
// Otherwise, attempt to verify against the extra JWT issuers and use a more generic
// Bearer Token -> Session converter
for _, verifier := range p.extraJwtBearerVerifiers {
bearerToken, err := verifier.Verify(req.Context(), rawBearerToken)
if err != nil { if err != nil {
logger.Printf("failed to verify bearer token: %v", err)
continue continue
} }
return p.provider.CreateSessionStateFromBearerToken(req.Context(), rawBearerToken, bearerToken) return (*providers.ProviderData)(nil).CreateSessionStateFromBearerToken(req.Context(), rawBearerToken, bearerToken)
} }
return nil, fmt.Errorf("unable to verify jwt token %s", req.Header.Get("Authorization")) return nil, fmt.Errorf("unable to verify jwt token %s", req.Header.Get("Authorization"))
} }

View File

@ -1578,7 +1578,7 @@ func TestGetJwtSession(t *testing.T) {
// Bearer // Bearer
expires := time.Unix(1912151821, 0) expires := time.Unix(1912151821, 0)
session, _ := test.proxy.GetJwtSession(test.req) session, _ := test.proxy.GetJwtSession(test.req)
assert.Equal(t, session.User, "john@example.com") assert.Equal(t, session.User, "1234567890")
assert.Equal(t, session.Email, "john@example.com") assert.Equal(t, session.Email, "john@example.com")
assert.Equal(t, session.ExpiresOn, &expires) assert.Equal(t, session.ExpiresOn, &expires)
assert.Equal(t, session.IDToken, goodJwt) assert.Equal(t, session.IDToken, goodJwt)
@ -1590,12 +1590,12 @@ func TestGetJwtSession(t *testing.T) {
// Check PassAuthorization, should overwrite Basic header // Check PassAuthorization, should overwrite Basic header
assert.Equal(t, test.req.Header.Get("Authorization"), authHeader) assert.Equal(t, test.req.Header.Get("Authorization"), authHeader)
assert.Equal(t, test.req.Header.Get("X-Forwarded-User"), "john@example.com") assert.Equal(t, test.req.Header.Get("X-Forwarded-User"), "1234567890")
assert.Equal(t, test.req.Header.Get("X-Forwarded-Email"), "john@example.com") assert.Equal(t, test.req.Header.Get("X-Forwarded-Email"), "john@example.com")
// SetAuthorization and SetXAuthRequest // SetAuthorization and SetXAuthRequest
assert.Equal(t, test.rw.Header().Get("Authorization"), authHeader) assert.Equal(t, test.rw.Header().Get("Authorization"), authHeader)
assert.Equal(t, test.rw.Header().Get("X-Auth-Request-User"), "john@example.com") assert.Equal(t, test.rw.Header().Get("X-Auth-Request-User"), "1234567890")
assert.Equal(t, test.rw.Header().Get("X-Auth-Request-Email"), "john@example.com") assert.Equal(t, test.rw.Header().Get("X-Auth-Request-Email"), "john@example.com")
} }

View File

@ -178,10 +178,6 @@ func Validate(o *options.Options) error {
} }
if o.SkipJwtBearerTokens { if o.SkipJwtBearerTokens {
// If we are using an oidc provider, go ahead and add that provider to the list
if o.GetOIDCVerifier() != nil {
o.SetJWTBearerVerifiers(append(o.GetJWTBearerVerifiers(), o.GetOIDCVerifier()))
}
// Configure extra issuers // Configure extra issuers
if len(o.ExtraJwtIssuers) > 0 { if len(o.ExtraJwtIssuers) > 0 {
var jwtIssuers []jwtIssuer var jwtIssuers []jwtIssuer

View File

@ -124,7 +124,6 @@ func newOIDCServer(body []byte) (*url.URL, *httptest.Server) {
} }
func newSignedTestIDToken(tokenClaims idTokenClaims) (string, error) { func newSignedTestIDToken(tokenClaims idTokenClaims) (string, error) {
key, _ := rsa.GenerateKey(rand.Reader, 2048) key, _ := rsa.GenerateKey(rand.Reader, 2048)
standardClaims := jwt.NewWithClaims(jwt.SigningMethodRS256, tokenClaims) standardClaims := jwt.NewWithClaims(jwt.SigningMethodRS256, tokenClaims)
return standardClaims.SignedString(key) return standardClaims.SignedString(key)

View File

@ -164,14 +164,13 @@ func (p *ProviderData) CreateSessionStateFromBearerToken(ctx context.Context, ra
newSession := &sessions.SessionState{ newSession := &sessions.SessionState{
Email: claims.Email, Email: claims.Email,
User: claims.Email, User: claims.Subject,
PreferredUsername: claims.PreferredUsername, PreferredUsername: claims.PreferredUsername,
AccessToken: rawIDToken,
IDToken: rawIDToken,
RefreshToken: "",
ExpiresOn: &idToken.Expiry,
} }
newSession.AccessToken = rawIDToken
newSession.IDToken = rawIDToken
newSession.RefreshToken = ""
newSession.ExpiresOn = &idToken.Expiry
return newSession, nil return newSession, nil
} }

View File

@ -2,10 +2,15 @@ package providers
import ( import (
"context" "context"
"crypto/rand"
"crypto/rsa"
"net/url" "net/url"
"testing" "testing"
"time" "time"
"github.com/coreos/go-oidc"
"github.com/dgrijalva/jwt-go"
"github.com/oauth2-proxy/oauth2-proxy/pkg/apis/sessions" "github.com/oauth2-proxy/oauth2-proxy/pkg/apis/sessions"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
@ -47,3 +52,39 @@ func TestAcrValuesConfigured(t *testing.T) {
result := p.GetLoginURL("https://my.test.app/oauth", "") result := p.GetLoginURL("https://my.test.app/oauth", "")
assert.Contains(t, result, "acr_values=testValue") assert.Contains(t, result, "acr_values=testValue")
} }
func TestCreateSessionStateFromBearerToken(t *testing.T) {
minimalIDToken := jwt.StandardClaims{
Audience: "asdf1234",
ExpiresAt: time.Now().Add(time.Duration(5) * time.Minute).Unix(),
Id: "id-some-id",
IssuedAt: time.Now().Unix(),
Issuer: "https://issuer.example.com",
NotBefore: 0,
Subject: "123456789",
}
// From oidc_test.go
verifier := oidc.NewVerifier(
"https://issuer.example.com",
fakeKeySetStub{},
&oidc.Config{ClientID: "asdf1234"},
)
key, err := rsa.GenerateKey(rand.Reader, 2048)
assert.NoError(t, err)
rawIDToken, err := jwt.NewWithClaims(jwt.SigningMethodRS256, minimalIDToken).SignedString(key)
assert.NoError(t, err)
// Pass to a dummy Verifier to get an oidc.IDToken from the rawIDToken for our actual test below
idToken, err := verifier.Verify(context.Background(), rawIDToken)
assert.NoError(t, err)
session, err := (*ProviderData)(nil).CreateSessionStateFromBearerToken(context.Background(), rawIDToken, idToken)
assert.NoError(t, err)
assert.Equal(t, rawIDToken, session.AccessToken)
assert.Equal(t, rawIDToken, session.IDToken)
assert.Equal(t, "123456789", session.Email)
assert.Equal(t, "123456789", session.User)
assert.Empty(t, session.RefreshToken)
assert.Empty(t, session.PreferredUsername)
}