Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Merge pull request #80 from stlaz/sa_fix
Bug 1970828: fix projected service accounts as oauth-client
  • Loading branch information
openshift-merge-robot committed Jun 11, 2021
2 parents fe7c28f + 2681f58 commit 374e2ee
Show file tree
Hide file tree
Showing 253 changed files with 33,460 additions and 8 deletions.
1 change: 1 addition & 0 deletions go.mod
Expand Up @@ -21,6 +21,7 @@ require (
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d
golang.org/x/text v0.3.4
gopkg.in/ldap.v2 v2.5.1
gopkg.in/square/go-jose.v2 v2.2.2
k8s.io/api v0.21.0
k8s.io/apimachinery v0.21.0
k8s.io/apiserver v0.21.0
Expand Down
1 change: 1 addition & 0 deletions go.sum
Expand Up @@ -828,6 +828,7 @@ gopkg.in/ldap.v2 v2.5.1/go.mod h1:oI0cpe/D7HRtBQl8aTg+ZmzFUAvu4lsv3eLXMLGFxWk=
gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8=
gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
gopkg.in/square/go-jose.v2 v2.2.2 h1:orlkJ3myw8CN1nVQHBFfloD+L3egixIa4FvUP6RosSA=
gopkg.in/square/go-jose.v2 v2.2.2/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
Expand Down
4 changes: 3 additions & 1 deletion pkg/oauth/registry/registry_test.go
Expand Up @@ -12,6 +12,7 @@ import (
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apiserver/pkg/authentication/authenticator"
"k8s.io/apiserver/pkg/authentication/user"
kubefake "k8s.io/client-go/kubernetes/fake"

oauthv1 "github.com/openshift/api/oauth/v1"
oauthfake "github.com/openshift/client-go/oauth/clientset/versioned/fake"
Expand Down Expand Up @@ -281,7 +282,8 @@ func TestRegistryAndServer(t *testing.T) {
objs = append(objs, testCase.ClientAuth)
}
fakeOAuthClient := oauthfake.NewSimpleClientset(objs...)
storage := registrystorage.New(fakeOAuthClient.OauthV1().OAuthAccessTokens(), fakeOAuthClient.OauthV1().OAuthAuthorizeTokens(), fakeOAuthClient.OauthV1().OAuthClients(), 0)
fakeTokenReviewClient := kubefake.NewSimpleClientset()
storage := registrystorage.New(fakeOAuthClient.OauthV1().OAuthAccessTokens(), fakeOAuthClient.OauthV1().OAuthAuthorizeTokens(), fakeOAuthClient.OauthV1().OAuthClients(), fakeTokenReviewClient.AuthenticationV1().TokenReviews(), 0)
config := osinserver.NewDefaultServerConfig()

h.AuthorizeHandler = osinserver.AuthorizeHandlers{
Expand Down
2 changes: 1 addition & 1 deletion pkg/oauthserver/auth.go
Expand Up @@ -108,7 +108,7 @@ func (c *OAuthServerConfig) WithOAuth(handler http.Handler) (http.Handler, error
if timeout := c.ExtraOAuthConfig.Options.TokenConfig.AccessTokenInactivityTimeout; timeout != nil {
tokentimeout = int32(timeout.Seconds())
}
storage := registrystorage.New(c.ExtraOAuthConfig.OAuthAccessTokenClient, c.ExtraOAuthConfig.OAuthAuthorizeTokenClient, combinedOAuthClientGetter, tokentimeout)
storage := registrystorage.New(c.ExtraOAuthConfig.OAuthAccessTokenClient, c.ExtraOAuthConfig.OAuthAuthorizeTokenClient, combinedOAuthClientGetter, c.ExtraOAuthConfig.TokenReviewClient, tokentimeout)
config := osinserver.NewDefaultServerConfig()
if authorizationExpiration := c.ExtraOAuthConfig.Options.TokenConfig.AuthorizeTokenMaxAgeSeconds; authorizationExpiration > 0 {
config.AuthorizationExpiration = authorizationExpiration
Expand Down
3 changes: 3 additions & 0 deletions pkg/oauthserver/oauth_apiserver.go
Expand Up @@ -16,6 +16,7 @@ import (
"k8s.io/apimachinery/pkg/util/yaml"
genericapiserver "k8s.io/apiserver/pkg/server"
kclientset "k8s.io/client-go/kubernetes"
authenticationv1client "k8s.io/client-go/kubernetes/typed/authentication/v1"
corev1 "k8s.io/client-go/kubernetes/typed/core/v1"
"k8s.io/client-go/rest"

Expand Down Expand Up @@ -153,6 +154,7 @@ func NewOAuthServerConfig(oauthConfig osinv1.OAuthConfig, userClientConfig *rest
OAuthClientAuthorizationClient: oauthClient.OAuthClientAuthorizations(),
SessionAuth: sessionAuth,
BootstrapUserDataGetter: bootstrapUserDataGetter,
TokenReviewClient: kubeClient.AuthenticationV1().TokenReviews(),
},
}
genericConfig.BuildHandlerChainFunc = ret.buildHandlerChainForOAuth
Expand Down Expand Up @@ -250,6 +252,7 @@ type ExtraOAuthConfig struct {
SessionAuth session.SessionAuthenticator

BootstrapUserDataGetter bootstrap.BootstrapUserDataGetter
TokenReviewClient authenticationv1client.TokenReviewInterface
}

type OAuthServerConfig struct {
Expand Down
51 changes: 45 additions & 6 deletions pkg/osinserver/registrystorage/storage.go
Expand Up @@ -7,10 +7,14 @@ import (
"strings"

"github.com/RangelReale/osin"
"gopkg.in/square/go-jose.v2/jwt"

tokenreviewv1 "k8s.io/api/authentication/v1"
kerrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apiserver/pkg/authentication/authenticator"
kuser "k8s.io/apiserver/pkg/authentication/user"
authenticationv1client "k8s.io/client-go/kubernetes/typed/authentication/v1"
"k8s.io/klog/v2"

oauthapi "github.com/openshift/api/oauth/v1"
Expand All @@ -26,22 +30,25 @@ import (
type storage struct {
accesstoken oauthclient.OAuthAccessTokenInterface
authorizetoken oauthclient.OAuthAuthorizeTokenInterface
tokenReview authenticationv1client.TokenReviewInterface
client api.OAuthClientGetter
tokentimeout int32
}

func New(access oauthclient.OAuthAccessTokenInterface, authorize oauthclient.OAuthAuthorizeTokenInterface, client api.OAuthClientGetter, tokentimeout int32) osin.Storage {
func New(access oauthclient.OAuthAccessTokenInterface, authorize oauthclient.OAuthAuthorizeTokenInterface, client api.OAuthClientGetter, tokenReview authenticationv1client.TokenReviewInterface, tokentimeout int32) osin.Storage {
return &storage{
accesstoken: access,
authorizetoken: authorize,
client: client,
tokentimeout: tokentimeout,
tokenReview: tokenReview,
}
}

type clientWrapper struct {
id string
client *oauthapi.OAuthClient
id string
client *oauthapi.OAuthClient
reviewToken func(ctx context.Context, token string, audiences []string) (*tokenreviewv1.TokenReview, error)
}

// Ensure we implement the secret matcher method that allows us to validate multiple secrets
Expand Down Expand Up @@ -70,6 +77,32 @@ func (w *clientWrapper) ClientSecretMatches(secret string) bool {
}
}

// assume this is an SA token
tok, err := jwt.ParseSigned(secret)
if err != nil {
return false
}

if tok != nil {
public := &jwt.Claims{}
err := tok.UnsafeClaimsWithoutVerification(public)
if err != nil {
klog.V(4).Infof("failed to get unsafe claims: %v", err)
return false
}
tokenAuds := authenticator.Audiences(public.Audience)
tokenReview, err := w.reviewToken(context.TODO(), secret, tokenAuds)
if err != nil {
klog.V(4).Infof("the tokenreview failed: %v", err)
return false
}

if tokenReview.Status.User.Username != w.client.Name {
return false
}
return true
}

return false
}

Expand Down Expand Up @@ -113,7 +146,13 @@ func (s *storage) GetClient(id string) (osin.Client, error) {
}
return nil, err
}
return &clientWrapper{id, c}, nil
return &clientWrapper{id, c, newTokenReviewer(s.tokenReview)}, nil
}

func newTokenReviewer(tokenReviewer authenticationv1client.TokenReviewInterface) func(ctx context.Context, token string, audiences []string) (*tokenreviewv1.TokenReview, error) {
return func(ctx context.Context, token string, audiences []string) (*tokenreviewv1.TokenReview, error) {
return tokenReviewer.Create(ctx, &tokenreviewv1.TokenReview{Spec: tokenreviewv1.TokenReviewSpec{Token: token, Audiences: audiences}}, metav1.CreateOptions{})
}
}

// SaveAuthorize saves authorize data.
Expand Down Expand Up @@ -226,7 +265,7 @@ func (s *storage) convertFromAuthorizeToken(code string, authorize *oauthapi.OAu
Code: code,
CodeChallenge: authorize.CodeChallenge,
CodeChallengeMethod: authorize.CodeChallengeMethod,
Client: &clientWrapper{authorize.ClientName, client},
Client: &clientWrapper{authorize.ClientName, client, newTokenReviewer(s.tokenReview)},
ExpiresIn: int32(authorize.ExpiresIn),
Scope: scopecovers.Join(authorize.Scopes),
RedirectUri: authorize.RedirectURI,
Expand Down Expand Up @@ -284,7 +323,7 @@ func (s *storage) convertFromAccessToken(code string, access *oauthapi.OAuthAcce
return &osin.AccessData{
AccessToken: code,
RefreshToken: access.RefreshToken,
Client: &clientWrapper{access.ClientName, client},
Client: &clientWrapper{access.ClientName, client, newTokenReviewer(s.tokenReview)},
ExpiresIn: int32(access.ExpiresIn),
Scope: scopecovers.Join(access.Scopes),
RedirectUri: access.RedirectURI,
Expand Down

0 comments on commit 374e2ee

Please sign in to comment.