svcacct: move claim generation out of TokenGenerator

pull/6/head
Mike Danese 2018-02-05 20:07:50 -08:00
parent c02b784b76
commit 7d74c35963
7 changed files with 71 additions and 36 deletions

View File

@ -58,6 +58,7 @@ go_test(
"//pkg/controller:go_default_library",
"//vendor/github.com/davecgh/go-spew/spew:go_default_library",
"//vendor/github.com/golang/glog:go_default_library",
"//vendor/gopkg.in/square/go-jose.v2/jwt:go_default_library",
"//vendor/k8s.io/api/core/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/api/errors:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",

View File

@ -395,7 +395,7 @@ func (e *TokensController) ensureReferencedToken(serviceAccount *v1.ServiceAccou
}
// Generate the token
token, err := e.token.GenerateToken(*serviceAccount, *secret)
token, err := e.token.GenerateToken(serviceaccount.LegacyClaims(*serviceAccount, *secret))
if err != nil {
// retriable error
return true, err
@ -551,7 +551,7 @@ func (e *TokensController) generateTokenIfNeeded(serviceAccount *v1.ServiceAccou
// Generate the token
if needsToken {
token, err := e.token.GenerateToken(*serviceAccount, *liveSecret)
token, err := e.token.GenerateToken(serviceaccount.LegacyClaims(*serviceAccount, *liveSecret))
if err != nil {
return false, err
}

View File

@ -24,6 +24,7 @@ import (
"github.com/davecgh/go-spew/spew"
"github.com/golang/glog"
"gopkg.in/square/go-jose.v2/jwt"
"k8s.io/api/core/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
@ -39,15 +40,11 @@ import (
)
type testGenerator struct {
GeneratedServiceAccounts []v1.ServiceAccount
GeneratedSecrets []v1.Secret
Token string
Err error
Token string
Err error
}
func (t *testGenerator) GenerateToken(serviceAccount v1.ServiceAccount, secret v1.Secret) (string, error) {
t.GeneratedSecrets = append(t.GeneratedSecrets, secret)
t.GeneratedServiceAccounts = append(t.GeneratedServiceAccounts, serviceAccount)
func (t *testGenerator) GenerateToken(sc *jwt.Claims, pc interface{}) (string, error) {
return t.Token, t.Err
}

View File

@ -10,6 +10,7 @@ go_library(
name = "go_default_library",
srcs = [
"jwt.go",
"legacy.go",
"util.go",
],
importpath = "k8s.io/kubernetes/pkg/serviceaccount",

View File

@ -38,15 +38,6 @@ import (
"gopkg.in/square/go-jose.v2/jwt"
)
const LegacyIssuer = "kubernetes/serviceaccount"
type privateClaims struct {
ServiceAccountName string `json:"kubernetes.io/serviceaccount/service-account.name"`
ServiceAccountUID string `json:"kubernetes.io/serviceaccount/service-account.uid"`
SecretName string `json:"kubernetes.io/serviceaccount/secret.name"`
Namespace string `json:"kubernetes.io/serviceaccount/namespace"`
}
// ServiceAccountTokenGetter defines functions to retrieve a named service account and secret
type ServiceAccountTokenGetter interface {
GetServiceAccount(namespace, name string) (*v1.ServiceAccount, error)
@ -54,9 +45,13 @@ type ServiceAccountTokenGetter interface {
}
type TokenGenerator interface {
// GenerateToken generates a token which will identify the given ServiceAccount.
// The returned token will be stored in the given (and yet-unpersisted) Secret.
GenerateToken(serviceAccount v1.ServiceAccount, secret v1.Secret) (string, error)
// GenerateToken generates a token which will identify the given
// ServiceAccount. privateClaims is an interface that will be
// serialized into the JWT payload JSON encoding at the root level of
// the payload object. Public claims take precedent over private
// claims i.e. if both claims and privateClaims have an "exp" field,
// the value in claims will be used.
GenerateToken(claims *jwt.Claims, privateClaims interface{}) (string, error)
}
// JWTTokenGenerator returns a TokenGenerator that generates signed JWT tokens, using the given privateKey.
@ -74,7 +69,7 @@ type jwtTokenGenerator struct {
privateKey interface{}
}
func (j *jwtTokenGenerator) GenerateToken(serviceAccount v1.ServiceAccount, secret v1.Secret) (string, error) {
func (j *jwtTokenGenerator) GenerateToken(claims *jwt.Claims, privateClaims interface{}) (string, error) {
var alg jose.SignatureAlgorithm
switch privateKey := j.privateKey.(type) {
case *rsa.PrivateKey:
@ -105,17 +100,14 @@ func (j *jwtTokenGenerator) GenerateToken(serviceAccount v1.ServiceAccount, secr
return "", err
}
// claims are applied in reverse precedence
return jwt.Signed(signer).
Claims(privateClaims).
Claims(claims).
Claims(&jwt.Claims{
Issuer: j.iss,
Subject: apiserverserviceaccount.MakeUsername(serviceAccount.Namespace, serviceAccount.Name),
Issuer: j.iss,
}).
Claims(&privateClaims{
Namespace: serviceAccount.Namespace,
ServiceAccountName: serviceAccount.Name,
ServiceAccountUID: string(serviceAccount.UID),
SecretName: secret.Name,
}).CompactSerialize()
CompactSerialize()
}
// JWTTokenAuthenticator authenticates tokens as JWT tokens produced by JWTTokenGenerator
@ -139,7 +131,7 @@ type jwtTokenAuthenticator struct {
}
type Validator interface {
Validate(tokenData string, public *jwt.Claims, private *privateClaims) error
Validate(tokenData string, public *jwt.Claims, private *legacyPrivateClaims) error
}
var errMismatchedSigningMethod = errors.New("invalid signing method")
@ -155,7 +147,7 @@ func (j *jwtTokenAuthenticator) AuthenticateToken(tokenData string) (user.Info,
}
public := &jwt.Claims{}
private := &privateClaims{}
private := &legacyPrivateClaims{}
var (
found bool
@ -218,7 +210,7 @@ type legacyValidator struct {
getter ServiceAccountTokenGetter
}
func (v *legacyValidator) Validate(tokenData string, public *jwt.Claims, private *privateClaims) error {
func (v *legacyValidator) Validate(tokenData string, public *jwt.Claims, private *legacyPrivateClaims) error {
// Make sure the claims we need exist
if len(public.Subject) == 0 {

View File

@ -129,7 +129,7 @@ func TestTokenGenerateAndValidate(t *testing.T) {
// Generate the RSA token
rsaGenerator := serviceaccount.JWTTokenGenerator(serviceaccount.LegacyIssuer, getPrivateKey(rsaPrivateKey))
rsaToken, err := rsaGenerator.GenerateToken(*serviceAccount, *rsaSecret)
rsaToken, err := rsaGenerator.GenerateToken(serviceaccount.LegacyClaims(*serviceAccount, *rsaSecret))
if err != nil {
t.Fatalf("error generating token: %v", err)
}
@ -142,7 +142,7 @@ func TestTokenGenerateAndValidate(t *testing.T) {
// Generate the ECDSA token
ecdsaGenerator := serviceaccount.JWTTokenGenerator(serviceaccount.LegacyIssuer, getPrivateKey(ecdsaPrivateKey))
ecdsaToken, err := ecdsaGenerator.GenerateToken(*serviceAccount, *ecdsaSecret)
ecdsaToken, err := ecdsaGenerator.GenerateToken(serviceaccount.LegacyClaims(*serviceAccount, *ecdsaSecret))
if err != nil {
t.Fatalf("error generating token: %v", err)
}
@ -155,7 +155,7 @@ func TestTokenGenerateAndValidate(t *testing.T) {
// Generate signer with same keys as RSA signer but different issuer
badIssuerGenerator := serviceaccount.JWTTokenGenerator("foo", getPrivateKey(rsaPrivateKey))
badIssuerToken, err := badIssuerGenerator.GenerateToken(*serviceAccount, *rsaSecret)
badIssuerToken, err := badIssuerGenerator.GenerateToken(serviceaccount.LegacyClaims(*serviceAccount, *rsaSecret))
if err != nil {
t.Fatalf("error generating token: %v", err)
}

View File

@ -0,0 +1,44 @@
/*
Copyright 2018 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package serviceaccount
import (
"k8s.io/api/core/v1"
apiserverserviceaccount "k8s.io/apiserver/pkg/authentication/serviceaccount"
"gopkg.in/square/go-jose.v2/jwt"
)
func LegacyClaims(serviceAccount v1.ServiceAccount, secret v1.Secret) (*jwt.Claims, interface{}) {
return &jwt.Claims{
Subject: apiserverserviceaccount.MakeUsername(serviceAccount.Namespace, serviceAccount.Name),
}, &legacyPrivateClaims{
Namespace: serviceAccount.Namespace,
ServiceAccountName: serviceAccount.Name,
ServiceAccountUID: string(serviceAccount.UID),
SecretName: secret.Name,
}
}
const LegacyIssuer = "kubernetes/serviceaccount"
type legacyPrivateClaims struct {
ServiceAccountName string `json:"kubernetes.io/serviceaccount/service-account.name"`
ServiceAccountUID string `json:"kubernetes.io/serviceaccount/service-account.uid"`
SecretName string `json:"kubernetes.io/serviceaccount/secret.name"`
Namespace string `json:"kubernetes.io/serviceaccount/namespace"`
}