Add ECDSA support

pull/6/head
Jordan Liggitt 2016-09-27 01:44:33 -04:00
parent 987aef1f64
commit 6333d8fd86
No known key found for this signature in database
GPG Key ID: 24E7ADF9A3B42012
6 changed files with 207 additions and 60 deletions

View File

@ -71,7 +71,7 @@ func (s *APIServer) AddFlags(fs *pflag.FlagSet) {
"Amount of time to retain events. Default is 1h.") "Amount of time to retain events. Default is 1h.")
fs.StringVar(&s.ServiceAccountKeyFile, "service-account-key-file", s.ServiceAccountKeyFile, ""+ fs.StringVar(&s.ServiceAccountKeyFile, "service-account-key-file", s.ServiceAccountKeyFile, ""+
"File containing PEM-encoded x509 RSA private or public key, used to verify "+ "File containing PEM-encoded x509 RSA or ECDSA private or public key, used to verify "+
"ServiceAccount tokens. If unspecified, --tls-private-key-file is used.") "ServiceAccount tokens. If unspecified, --tls-private-key-file is used.")
fs.BoolVar(&s.ServiceAccountLookup, "service-account-lookup", s.ServiceAccountLookup, fs.BoolVar(&s.ServiceAccountLookup, "service-account-lookup", s.ServiceAccountLookup,

View File

@ -158,7 +158,7 @@ func (s *CMServer) AddFlags(fs *pflag.FlagSet) {
"Amount of time which we allow starting Node to be unresponsive before marking it unhealthy.") "Amount of time which we allow starting Node to be unresponsive before marking it unhealthy.")
fs.DurationVar(&s.NodeMonitorPeriod.Duration, "node-monitor-period", s.NodeMonitorPeriod.Duration, fs.DurationVar(&s.NodeMonitorPeriod.Duration, "node-monitor-period", s.NodeMonitorPeriod.Duration,
"The period for syncing NodeStatus in NodeController.") "The period for syncing NodeStatus in NodeController.")
fs.StringVar(&s.ServiceAccountKeyFile, "service-account-private-key-file", s.ServiceAccountKeyFile, "Filename containing a PEM-encoded private RSA key used to sign service account tokens.") fs.StringVar(&s.ServiceAccountKeyFile, "service-account-private-key-file", s.ServiceAccountKeyFile, "Filename containing a PEM-encoded private RSA or ECDSA key used to sign service account tokens.")
fs.StringVar(&s.ClusterSigningCertFile, "cluster-signing-cert-file", s.ClusterSigningCertFile, "Filename containing a PEM-encoded X509 CA certificate used to issue cluster-scoped certificates") fs.StringVar(&s.ClusterSigningCertFile, "cluster-signing-cert-file", s.ClusterSigningCertFile, "Filename containing a PEM-encoded X509 CA certificate used to issue cluster-scoped certificates")
fs.StringVar(&s.ClusterSigningKeyFile, "cluster-signing-key-file", s.ClusterSigningKeyFile, "Filename containing a PEM-encoded RSA or ECDSA private key used to sign cluster-scoped certificates") fs.StringVar(&s.ClusterSigningKeyFile, "cluster-signing-key-file", s.ClusterSigningKeyFile, "Filename containing a PEM-encoded RSA or ECDSA private key used to sign cluster-scoped certificates")
fs.StringVar(&s.ApproveAllKubeletCSRsForGroup, "insecure-experimental-approve-all-kubelet-csrs-for-group", s.ApproveAllKubeletCSRsForGroup, "The group for which the controller-manager will auto approve all CSRs for kubelet client certificates.") fs.StringVar(&s.ApproveAllKubeletCSRsForGroup, "insecure-experimental-approve-all-kubelet-csrs-for-group", s.ApproveAllKubeletCSRsForGroup, "The group for which the controller-manager will auto approve all CSRs for kubelet client certificates.")

View File

@ -17,7 +17,6 @@ limitations under the License.
package authenticator package authenticator
import ( import (
"crypto/rsa"
"time" "time"
"k8s.io/kubernetes/pkg/auth/authenticator" "k8s.io/kubernetes/pkg/auth/authenticator"
@ -183,7 +182,7 @@ func newServiceAccountAuthenticator(keyfile string, lookup bool, serviceAccountG
return nil, err return nil, err
} }
tokenAuthenticator := serviceaccount.JWTTokenAuthenticator([]*rsa.PublicKey{publicKey}, lookup, serviceAccountGetter) tokenAuthenticator := serviceaccount.JWTTokenAuthenticator([]interface{}{publicKey}, lookup, serviceAccountGetter)
return bearertoken.New(tokenAuthenticator), nil return bearertoken.New(tokenAuthenticator), nil
} }

View File

@ -18,6 +18,8 @@ package serviceaccount
import ( import (
"bytes" "bytes"
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rsa" "crypto/rsa"
"errors" "errors"
"fmt" "fmt"
@ -54,43 +56,95 @@ type TokenGenerator interface {
GenerateToken(serviceAccount api.ServiceAccount, secret api.Secret) (string, error) GenerateToken(serviceAccount api.ServiceAccount, secret api.Secret) (string, error)
} }
// ReadPrivateKey is a helper function for reading an rsa.PrivateKey from a PEM-encoded file // ReadPrivateKey is a helper function for reading a private key from a PEM-encoded file
func ReadPrivateKey(file string) (*rsa.PrivateKey, error) { func ReadPrivateKey(file string) (interface{}, error) {
data, err := ioutil.ReadFile(file) data, err := ioutil.ReadFile(file)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return jwt.ParseRSAPrivateKeyFromPEM(data) key, err := ReadPrivateKeyFromPEM(data)
if err != nil {
return nil, fmt.Errorf("error reading private key file %s: %v", file, err)
}
return key, nil
} }
// ReadPublicKey is a helper function for reading an rsa.PublicKey from a PEM-encoded file // ReadPrivateKeyFromPEM is a helper function for reading a private key from a PEM-encoded file
// Reads public keys from both public and private key files func ReadPrivateKeyFromPEM(data []byte) (interface{}, error) {
func ReadPublicKey(file string) (*rsa.PublicKey, error) { if key, err := jwt.ParseRSAPrivateKeyFromPEM(data); err == nil {
return key, nil
}
if key, err := jwt.ParseECPrivateKeyFromPEM(data); err == nil {
return key, nil
}
return nil, fmt.Errorf("data does not contain a valid RSA or ECDSA private key")
}
// ReadPublicKey is a helper function for reading an rsa.PublicKey or ecdsa.PublicKey from a PEM-encoded file.
// Reads public keys from both public and private key files.
func ReadPublicKey(file string) (interface{}, error) {
data, err := ioutil.ReadFile(file) data, err := ioutil.ReadFile(file)
if err != nil { if err != nil {
return nil, err return nil, err
} }
key, err := ReadPublicKeyFromPEM(data)
if err != nil {
return nil, fmt.Errorf("error reading public key file %s: %v", file, err)
}
return key, nil
}
// ReadPublicKeyFromPEM is a helper function for reading an rsa.PublicKey or ecdsa.PublicKey from a PEM-encoded byte array.
// Reads public keys from both public and private key files.
func ReadPublicKeyFromPEM(data []byte) (interface{}, error) {
if privateKey, err := jwt.ParseRSAPrivateKeyFromPEM(data); err == nil { if privateKey, err := jwt.ParseRSAPrivateKeyFromPEM(data); err == nil {
return &privateKey.PublicKey, nil return &privateKey.PublicKey, nil
} }
if publicKey, err := jwt.ParseRSAPublicKeyFromPEM(data); err == nil {
return publicKey, nil
}
return jwt.ParseRSAPublicKeyFromPEM(data) if privateKey, err := jwt.ParseECPrivateKeyFromPEM(data); err == nil {
return &privateKey.PublicKey, nil
}
if publicKey, err := jwt.ParseECPublicKeyFromPEM(data); err == nil {
return publicKey, nil
}
return nil, fmt.Errorf("data does not contain a valid RSA or ECDSA key")
} }
// JWTTokenGenerator returns a TokenGenerator that generates signed JWT tokens, using the given privateKey. // JWTTokenGenerator returns a TokenGenerator that generates signed JWT tokens, using the given privateKey.
// privateKey is a PEM-encoded byte array of a private RSA key. // privateKey is a PEM-encoded byte array of a private RSA key.
// JWTTokenAuthenticator() // JWTTokenAuthenticator()
func JWTTokenGenerator(key *rsa.PrivateKey) TokenGenerator { func JWTTokenGenerator(privateKey interface{}) TokenGenerator {
return &jwtTokenGenerator{key} return &jwtTokenGenerator{privateKey}
} }
type jwtTokenGenerator struct { type jwtTokenGenerator struct {
key *rsa.PrivateKey privateKey interface{}
} }
func (j *jwtTokenGenerator) GenerateToken(serviceAccount api.ServiceAccount, secret api.Secret) (string, error) { func (j *jwtTokenGenerator) GenerateToken(serviceAccount api.ServiceAccount, secret api.Secret) (string, error) {
token := jwt.New(jwt.SigningMethodRS256) var method jwt.SigningMethod
switch privateKey := j.privateKey.(type) {
case *rsa.PrivateKey:
method = jwt.SigningMethodRS256
case *ecdsa.PrivateKey:
switch privateKey.Curve {
case elliptic.P256():
method = jwt.SigningMethodES256
case elliptic.P384():
method = jwt.SigningMethodES384
case elliptic.P521():
method = jwt.SigningMethodES512
default:
return "", fmt.Errorf("unknown private key curve, must be 256, 384, or 521")
}
default:
return "", fmt.Errorf("unknown private key type %T, must be *rsa.PrivateKey or *ecdsa.PrivateKey", j.privateKey)
}
token := jwt.New(method)
claims, _ := token.Claims.(jwt.MapClaims) claims, _ := token.Claims.(jwt.MapClaims)
@ -107,32 +161,44 @@ func (j *jwtTokenGenerator) GenerateToken(serviceAccount api.ServiceAccount, sec
claims[SecretNameClaim] = secret.Name claims[SecretNameClaim] = secret.Name
// Sign and get the complete encoded token as a string // Sign and get the complete encoded token as a string
return token.SignedString(j.key) return token.SignedString(j.privateKey)
} }
// JWTTokenAuthenticator authenticates tokens as JWT tokens produced by JWTTokenGenerator // JWTTokenAuthenticator authenticates tokens as JWT tokens produced by JWTTokenGenerator
// Token signatures are verified using each of the given public keys until one works (allowing key rotation) // Token signatures are verified using each of the given public keys until one works (allowing key rotation)
// If lookup is true, the service account and secret referenced as claims inside the token are retrieved and verified with the provided ServiceAccountTokenGetter // If lookup is true, the service account and secret referenced as claims inside the token are retrieved and verified with the provided ServiceAccountTokenGetter
func JWTTokenAuthenticator(keys []*rsa.PublicKey, lookup bool, getter ServiceAccountTokenGetter) authenticator.Token { func JWTTokenAuthenticator(keys []interface{}, lookup bool, getter ServiceAccountTokenGetter) authenticator.Token {
return &jwtTokenAuthenticator{keys, lookup, getter} return &jwtTokenAuthenticator{keys, lookup, getter}
} }
type jwtTokenAuthenticator struct { type jwtTokenAuthenticator struct {
keys []*rsa.PublicKey keys []interface{}
lookup bool lookup bool
getter ServiceAccountTokenGetter getter ServiceAccountTokenGetter
} }
var errMismatchedSigningMethod = errors.New("invalid signing method")
func (j *jwtTokenAuthenticator) AuthenticateToken(token string) (user.Info, bool, error) { func (j *jwtTokenAuthenticator) AuthenticateToken(token string) (user.Info, bool, error) {
var validationError error var validationError error
for i, key := range j.keys { for i, key := range j.keys {
// Attempt to verify with each key until we find one that works // Attempt to verify with each key until we find one that works
parsedToken, err := jwt.Parse(token, func(token *jwt.Token) (interface{}, error) { parsedToken, err := jwt.Parse(token, func(token *jwt.Token) (interface{}, error) {
if _, ok := token.Method.(*jwt.SigningMethodRSA); !ok { switch token.Method.(type) {
case *jwt.SigningMethodRSA:
if _, ok := key.(*rsa.PublicKey); ok {
return key, nil
}
return nil, errMismatchedSigningMethod
case *jwt.SigningMethodECDSA:
if _, ok := key.(*ecdsa.PublicKey); ok {
return key, nil
}
return nil, errMismatchedSigningMethod
default:
return nil, fmt.Errorf("Unexpected signing method: %v", token.Header["alg"]) return nil, fmt.Errorf("Unexpected signing method: %v", token.Header["alg"])
} }
return key, nil
}) })
if err != nil { if err != nil {
@ -150,6 +216,15 @@ func (j *jwtTokenAuthenticator) AuthenticateToken(token string) (user.Info, bool
validationError = err validationError = err
continue continue
} }
// This key doesn't apply to the given signature type
// Perhaps one of the other keys will verify the signature
// If not, we want to return this error
if err.Inner == errMismatchedSigningMethod {
glog.V(4).Infof("Mismatched key type (key %d): %v", i, err)
validationError = err
continue
}
} }
// Other errors should just return as errors // Other errors should just return as errors

View File

@ -17,14 +17,11 @@ limitations under the License.
package serviceaccount_test package serviceaccount_test
import ( import (
"crypto/rsa"
"io/ioutil" "io/ioutil"
"os" "os"
"reflect" "reflect"
"testing" "testing"
"github.com/dgrijalva/jwt-go"
"k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api"
clientset "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset" clientset "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/fake" "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/fake"
@ -42,7 +39,7 @@ gPZm7ZsipmfbZK2Tkhnpsa4QxDg7zHJPMsB5kxRXW0cQipXcC3baDyN9KBApNXa0
PwIDAQAB PwIDAQAB
-----END PUBLIC KEY-----` -----END PUBLIC KEY-----`
const publicKey = `-----BEGIN PUBLIC KEY----- const rsaPublicKey = `-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA249XwEo9k4tM8fMxV7zx MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA249XwEo9k4tM8fMxV7zx
OhcrP+WvXn917koM5Qr2ZXs4vo26e4ytdlrV0bQ9SlcLpQVSYjIxNfhTZdDt+ecI OhcrP+WvXn917koM5Qr2ZXs4vo26e4ytdlrV0bQ9SlcLpQVSYjIxNfhTZdDt+ecI
zshKuv1gKIxbbLQMOuK1eA/4HALyEkFgmS/tleLJrhc65tKPMGD+pKQ/xhmzRuCG zshKuv1gKIxbbLQMOuK1eA/4HALyEkFgmS/tleLJrhc65tKPMGD+pKQ/xhmzRuCG
@ -53,7 +50,7 @@ WwIDAQAB
-----END PUBLIC KEY----- -----END PUBLIC KEY-----
` `
const privateKey = `-----BEGIN RSA PRIVATE KEY----- const rsaPrivateKey = `-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEA249XwEo9k4tM8fMxV7zxOhcrP+WvXn917koM5Qr2ZXs4vo26 MIIEowIBAAKCAQEA249XwEo9k4tM8fMxV7zxOhcrP+WvXn917koM5Qr2ZXs4vo26
e4ytdlrV0bQ9SlcLpQVSYjIxNfhTZdDt+ecIzshKuv1gKIxbbLQMOuK1eA/4HALy e4ytdlrV0bQ9SlcLpQVSYjIxNfhTZdDt+ecIzshKuv1gKIxbbLQMOuK1eA/4HALy
EkFgmS/tleLJrhc65tKPMGD+pKQ/xhmzRuCG51RoiMgbQxaCyYxGfNLpLAZK9L0T EkFgmS/tleLJrhc65tKPMGD+pKQ/xhmzRuCG51RoiMgbQxaCyYxGfNLpLAZK9L0T
@ -82,16 +79,28 @@ X024wzbiw1q07jFCyfQmODzURAx1VNT7QVUMdz/N8vy47/H40AZJ
-----END RSA PRIVATE KEY----- -----END RSA PRIVATE KEY-----
` `
func getPrivateKey(data string) *rsa.PrivateKey { // openssl ecparam -name prime256v1 -genkey -noout -out ecdsa256.pem
key, _ := jwt.ParseRSAPrivateKeyFromPEM([]byte(data)) const ecdsaPrivateKey = `-----BEGIN EC PRIVATE KEY-----
MHcCAQEEIEZmTmUhuanLjPA2CLquXivuwBDHTt5XYwgIr/kA1LtRoAoGCCqGSM49
AwEHoUQDQgAEH6cuzP8XuD5wal6wf9M6xDljTOPLX2i8uIp/C/ASqiIGUeeKQtX0
/IR3qCXyThP/dbCiHrF3v1cuhBOHY8CLVg==
-----END EC PRIVATE KEY-----`
// openssl ec -in ecdsa256.pem -pubout -out ecdsa256pub.pem
const ecdsaPublicKey = `-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEH6cuzP8XuD5wal6wf9M6xDljTOPL
X2i8uIp/C/ASqiIGUeeKQtX0/IR3qCXyThP/dbCiHrF3v1cuhBOHY8CLVg==
-----END PUBLIC KEY-----`
func getPrivateKey(data string) interface{} {
key, _ := serviceaccount.ReadPrivateKeyFromPEM([]byte(data))
return key return key
} }
func getPublicKey(data string) *rsa.PublicKey { func getPublicKey(data string) interface{} {
key, _ := jwt.ParseRSAPublicKeyFromPEM([]byte(data)) key, _ := serviceaccount.ReadPublicKeyFromPEM([]byte(data))
return key return key
} }
func TestReadPrivateKey(t *testing.T) { func TestReadPrivateKey(t *testing.T) {
f, err := ioutil.TempFile("", "") f, err := ioutil.TempFile("", "")
if err != nil { if err != nil {
@ -99,12 +108,18 @@ func TestReadPrivateKey(t *testing.T) {
} }
defer os.Remove(f.Name()) defer os.Remove(f.Name())
if err := ioutil.WriteFile(f.Name(), []byte(privateKey), os.FileMode(0600)); err != nil { if err := ioutil.WriteFile(f.Name(), []byte(rsaPrivateKey), os.FileMode(0600)); err != nil {
t.Fatalf("error writing private key to tmpfile: %v", err) t.Fatalf("error writing private key to tmpfile: %v", err)
} }
if _, err := serviceaccount.ReadPrivateKey(f.Name()); err != nil { if _, err := serviceaccount.ReadPrivateKey(f.Name()); err != nil {
t.Fatalf("error reading private key: %v", err) t.Fatalf("error reading private RSA key: %v", err)
}
if err := ioutil.WriteFile(f.Name(), []byte(ecdsaPrivateKey), os.FileMode(0600)); err != nil {
t.Fatalf("error writing private key to tmpfile: %v", err)
}
if _, err := serviceaccount.ReadPrivateKey(f.Name()); err != nil {
t.Fatalf("error reading private ECDSA key: %v", err)
} }
} }
@ -115,12 +130,18 @@ func TestReadPublicKey(t *testing.T) {
} }
defer os.Remove(f.Name()) defer os.Remove(f.Name())
if err := ioutil.WriteFile(f.Name(), []byte(publicKey), os.FileMode(0600)); err != nil { if err := ioutil.WriteFile(f.Name(), []byte(rsaPublicKey), os.FileMode(0600)); err != nil {
t.Fatalf("error writing public key to tmpfile: %v", err) t.Fatalf("error writing public key to tmpfile: %v", err)
} }
if _, err := serviceaccount.ReadPublicKey(f.Name()); err != nil { if _, err := serviceaccount.ReadPublicKey(f.Name()); err != nil {
t.Fatalf("error reading public key: %v", err) t.Fatalf("error reading RSA public key: %v", err)
}
if err := ioutil.WriteFile(f.Name(), []byte(ecdsaPublicKey), os.FileMode(0600)); err != nil {
t.Fatalf("error writing public key to tmpfile: %v", err)
}
if _, err := serviceaccount.ReadPublicKey(f.Name()); err != nil {
t.Fatalf("error reading ECDSA public key: %v", err)
} }
} }
@ -136,31 +157,49 @@ func TestTokenGenerateAndValidate(t *testing.T) {
Namespace: "test", Namespace: "test",
}, },
} }
secret := &api.Secret{ rsaSecret := &api.Secret{
ObjectMeta: api.ObjectMeta{ ObjectMeta: api.ObjectMeta{
Name: "my-secret", Name: "my-rsa-secret",
Namespace: "test",
},
}
ecdsaSecret := &api.Secret{
ObjectMeta: api.ObjectMeta{
Name: "my-ecdsa-secret",
Namespace: "test", Namespace: "test",
}, },
} }
// Generate the token // Generate the RSA token
generator := serviceaccount.JWTTokenGenerator(getPrivateKey(privateKey)) rsaGenerator := serviceaccount.JWTTokenGenerator(getPrivateKey(rsaPrivateKey))
token, err := generator.GenerateToken(*serviceAccount, *secret) rsaToken, err := rsaGenerator.GenerateToken(*serviceAccount, *rsaSecret)
if err != nil { if err != nil {
t.Fatalf("error generating token: %v", err) t.Fatalf("error generating token: %v", err)
} }
if len(token) == 0 { if len(rsaToken) == 0 {
t.Fatalf("no token generated") t.Fatalf("no token generated")
} }
rsaSecret.Data = map[string][]byte{
"token": []byte(rsaToken),
}
// "Save" the token // Generate the ECDSA token
secret.Data = map[string][]byte{ ecdsaGenerator := serviceaccount.JWTTokenGenerator(getPrivateKey(ecdsaPrivateKey))
"token": []byte(token), ecdsaToken, err := ecdsaGenerator.GenerateToken(*serviceAccount, *ecdsaSecret)
if err != nil {
t.Fatalf("error generating token: %v", err)
}
if len(ecdsaToken) == 0 {
t.Fatalf("no token generated")
}
ecdsaSecret.Data = map[string][]byte{
"token": []byte(ecdsaToken),
} }
testCases := map[string]struct { testCases := map[string]struct {
Client clientset.Interface Client clientset.Interface
Keys []*rsa.PublicKey Keys []interface{}
Token string
ExpectedErr bool ExpectedErr bool
ExpectedOK bool ExpectedOK bool
@ -169,29 +208,60 @@ func TestTokenGenerateAndValidate(t *testing.T) {
ExpectedGroups []string ExpectedGroups []string
}{ }{
"no keys": { "no keys": {
Token: rsaToken,
Client: nil, Client: nil,
Keys: []*rsa.PublicKey{}, Keys: []interface{}{},
ExpectedErr: false, ExpectedErr: false,
ExpectedOK: false, ExpectedOK: false,
}, },
"invalid keys": { "invalid keys (rsa)": {
Token: rsaToken,
Client: nil, Client: nil,
Keys: []*rsa.PublicKey{getPublicKey(otherPublicKey)}, Keys: []interface{}{getPublicKey(otherPublicKey), getPublicKey(ecdsaPublicKey)},
ExpectedErr: true, ExpectedErr: true,
ExpectedOK: false, ExpectedOK: false,
}, },
"valid key": { "invalid keys (ecdsa)": {
Token: ecdsaToken,
Client: nil, Client: nil,
Keys: []*rsa.PublicKey{getPublicKey(publicKey)}, Keys: []interface{}{getPublicKey(otherPublicKey), getPublicKey(rsaPublicKey)},
ExpectedErr: true,
ExpectedOK: false,
},
"valid key (rsa)": {
Token: rsaToken,
Client: nil,
Keys: []interface{}{getPublicKey(rsaPublicKey)},
ExpectedErr: false, ExpectedErr: false,
ExpectedOK: true, ExpectedOK: true,
ExpectedUserName: expectedUserName, ExpectedUserName: expectedUserName,
ExpectedUserUID: expectedUserUID, ExpectedUserUID: expectedUserUID,
ExpectedGroups: []string{"system:serviceaccounts", "system:serviceaccounts:test"}, ExpectedGroups: []string{"system:serviceaccounts", "system:serviceaccounts:test"},
}, },
"rotated keys": { "valid key (ecdsa)": {
Token: ecdsaToken,
Client: nil, Client: nil,
Keys: []*rsa.PublicKey{getPublicKey(otherPublicKey), getPublicKey(publicKey)}, Keys: []interface{}{getPublicKey(ecdsaPublicKey)},
ExpectedErr: false,
ExpectedOK: true,
ExpectedUserName: expectedUserName,
ExpectedUserUID: expectedUserUID,
ExpectedGroups: []string{"system:serviceaccounts", "system:serviceaccounts:test"},
},
"rotated keys (rsa)": {
Token: rsaToken,
Client: nil,
Keys: []interface{}{getPublicKey(otherPublicKey), getPublicKey(ecdsaPublicKey), getPublicKey(rsaPublicKey)},
ExpectedErr: false,
ExpectedOK: true,
ExpectedUserName: expectedUserName,
ExpectedUserUID: expectedUserUID,
ExpectedGroups: []string{"system:serviceaccounts", "system:serviceaccounts:test"},
},
"rotated keys (ecdsa)": {
Token: ecdsaToken,
Client: nil,
Keys: []interface{}{getPublicKey(otherPublicKey), getPublicKey(rsaPublicKey), getPublicKey(ecdsaPublicKey)},
ExpectedErr: false, ExpectedErr: false,
ExpectedOK: true, ExpectedOK: true,
ExpectedUserName: expectedUserName, ExpectedUserName: expectedUserName,
@ -199,8 +269,9 @@ func TestTokenGenerateAndValidate(t *testing.T) {
ExpectedGroups: []string{"system:serviceaccounts", "system:serviceaccounts:test"}, ExpectedGroups: []string{"system:serviceaccounts", "system:serviceaccounts:test"},
}, },
"valid lookup": { "valid lookup": {
Client: fake.NewSimpleClientset(serviceAccount, secret), Token: rsaToken,
Keys: []*rsa.PublicKey{getPublicKey(publicKey)}, Client: fake.NewSimpleClientset(serviceAccount, rsaSecret, ecdsaSecret),
Keys: []interface{}{getPublicKey(rsaPublicKey)},
ExpectedErr: false, ExpectedErr: false,
ExpectedOK: true, ExpectedOK: true,
ExpectedUserName: expectedUserName, ExpectedUserName: expectedUserName,
@ -208,14 +279,16 @@ func TestTokenGenerateAndValidate(t *testing.T) {
ExpectedGroups: []string{"system:serviceaccounts", "system:serviceaccounts:test"}, ExpectedGroups: []string{"system:serviceaccounts", "system:serviceaccounts:test"},
}, },
"invalid secret lookup": { "invalid secret lookup": {
Token: rsaToken,
Client: fake.NewSimpleClientset(serviceAccount), Client: fake.NewSimpleClientset(serviceAccount),
Keys: []*rsa.PublicKey{getPublicKey(publicKey)}, Keys: []interface{}{getPublicKey(rsaPublicKey)},
ExpectedErr: true, ExpectedErr: true,
ExpectedOK: false, ExpectedOK: false,
}, },
"invalid serviceaccount lookup": { "invalid serviceaccount lookup": {
Client: fake.NewSimpleClientset(secret), Token: rsaToken,
Keys: []*rsa.PublicKey{getPublicKey(publicKey)}, Client: fake.NewSimpleClientset(rsaSecret, ecdsaSecret),
Keys: []interface{}{getPublicKey(rsaPublicKey)},
ExpectedErr: true, ExpectedErr: true,
ExpectedOK: false, ExpectedOK: false,
}, },
@ -231,7 +304,7 @@ func TestTokenGenerateAndValidate(t *testing.T) {
continue continue
} }
user, ok, err := authenticator.AuthenticateToken(token) user, ok, err := authenticator.AuthenticateToken(tc.Token)
if (err != nil) != tc.ExpectedErr { if (err != nil) != tc.ExpectedErr {
t.Errorf("%s: Expected error=%v, got %v", k, tc.ExpectedErr, err) t.Errorf("%s: Expected error=%v, got %v", k, tc.ExpectedErr, err)
continue continue

View File

@ -361,7 +361,7 @@ func startServiceAccountTestServer(t *testing.T) (*clientset.Clientset, restclie
}) })
serviceAccountKey, _ := rsa.GenerateKey(rand.Reader, 2048) serviceAccountKey, _ := rsa.GenerateKey(rand.Reader, 2048)
serviceAccountTokenGetter := serviceaccountcontroller.NewGetterFromClient(rootClientset) serviceAccountTokenGetter := serviceaccountcontroller.NewGetterFromClient(rootClientset)
serviceAccountTokenAuth := serviceaccount.JWTTokenAuthenticator([]*rsa.PublicKey{&serviceAccountKey.PublicKey}, true, serviceAccountTokenGetter) serviceAccountTokenAuth := serviceaccount.JWTTokenAuthenticator([]interface{}{&serviceAccountKey.PublicKey}, true, serviceAccountTokenGetter)
authenticator := union.New( authenticator := union.New(
bearertoken.New(rootTokenAuth), bearertoken.New(rootTokenAuth),
bearertoken.New(serviceAccountTokenAuth), bearertoken.New(serviceAccountTokenAuth),