Merge pull request #19088 from smarterclayton/separate_service_account

Auto commit by PR queue bot
This commit is contained in:
k8s-merge-robot 2016-01-04 08:38:24 -08:00
commit cd097e3f86
12 changed files with 91 additions and 79 deletions

View File

@ -41,13 +41,16 @@ import (
"k8s.io/kubernetes/pkg/api/validation"
"k8s.io/kubernetes/pkg/apis/extensions"
"k8s.io/kubernetes/pkg/apiserver"
"k8s.io/kubernetes/pkg/apiserver/authenticator"
"k8s.io/kubernetes/pkg/capabilities"
client "k8s.io/kubernetes/pkg/client/unversioned"
"k8s.io/kubernetes/pkg/cloudprovider"
serviceaccountcontroller "k8s.io/kubernetes/pkg/controller/serviceaccount"
"k8s.io/kubernetes/pkg/genericapiserver"
kubeletclient "k8s.io/kubernetes/pkg/kubelet/client"
"k8s.io/kubernetes/pkg/master"
"k8s.io/kubernetes/pkg/master/ports"
"k8s.io/kubernetes/pkg/serviceaccount"
"k8s.io/kubernetes/pkg/storage"
etcdstorage "k8s.io/kubernetes/pkg/storage/etcd"
"k8s.io/kubernetes/pkg/util"
@ -485,13 +488,21 @@ func (s *APIServer) Run(_ []string) error {
// Default to the private server key for service account token signing
if s.ServiceAccountKeyFile == "" && s.TLSPrivateKeyFile != "" {
if apiserver.IsValidServiceAccountKeyFile(s.TLSPrivateKeyFile) {
if authenticator.IsValidServiceAccountKeyFile(s.TLSPrivateKeyFile) {
s.ServiceAccountKeyFile = s.TLSPrivateKeyFile
} else {
glog.Warning("No RSA key provided, service account token authentication disabled")
}
}
authenticator, err := apiserver.NewAuthenticator(apiserver.AuthenticatorConfig{
var serviceAccountGetter serviceaccount.ServiceAccountTokenGetter
if s.ServiceAccountLookup {
// If we need to look up service accounts and tokens,
// go directly to etcd to avoid recursive auth insanity
serviceAccountGetter = serviceaccountcontroller.NewGetterFromStorageInterface(etcdStorage)
}
authenticator, err := authenticator.New(authenticator.AuthenticatorConfig{
BasicAuthFile: s.BasicAuthFile,
ClientCAFile: s.ClientCAFile,
TokenAuthFile: s.TokenAuthFile,
@ -501,7 +512,7 @@ func (s *APIServer) Run(_ []string) error {
OIDCUsernameClaim: s.OIDCUsernameClaim,
ServiceAccountKeyFile: s.ServiceAccountKeyFile,
ServiceAccountLookup: s.ServiceAccountLookup,
Storage: etcdStorage,
ServiceAccountTokenGetter: serviceAccountGetter,
KeystoneURL: s.KeystoneURL,
})

View File

@ -51,9 +51,10 @@ import (
resourcequotacontroller "k8s.io/kubernetes/pkg/controller/resourcequota"
routecontroller "k8s.io/kubernetes/pkg/controller/route"
servicecontroller "k8s.io/kubernetes/pkg/controller/service"
"k8s.io/kubernetes/pkg/controller/serviceaccount"
serviceaccountcontroller "k8s.io/kubernetes/pkg/controller/serviceaccount"
"k8s.io/kubernetes/pkg/healthz"
"k8s.io/kubernetes/pkg/master/ports"
"k8s.io/kubernetes/pkg/serviceaccount"
"k8s.io/kubernetes/pkg/util"
"k8s.io/kubernetes/pkg/util/wait"
@ -433,9 +434,9 @@ func (s *CMServer) Run(_ []string) error {
if err != nil {
glog.Errorf("Error reading key for service account token controller: %v", err)
} else {
serviceaccount.NewTokensController(
serviceaccountcontroller.NewTokensController(
clientForUserAgentOrDie(*kubeconfig, "tokens-controller"),
serviceaccount.TokensControllerOptions{
serviceaccountcontroller.TokensControllerOptions{
TokenGenerator: serviceaccount.JWTTokenGenerator(privateKey),
RootCA: rootCA,
},
@ -443,9 +444,9 @@ func (s *CMServer) Run(_ []string) error {
}
}
serviceaccount.NewServiceAccountsController(
serviceaccountcontroller.NewServiceAccountsController(
clientForUserAgentOrDie(*kubeconfig, "service-account-controller"),
serviceaccount.DefaultServiceAccountsControllerOptions(),
serviceaccountcontroller.DefaultServiceAccountsControllerOptions(),
).Run()
select {}

View File

@ -43,8 +43,9 @@ import (
resourcequotacontroller "k8s.io/kubernetes/pkg/controller/resourcequota"
routecontroller "k8s.io/kubernetes/pkg/controller/route"
servicecontroller "k8s.io/kubernetes/pkg/controller/service"
"k8s.io/kubernetes/pkg/controller/serviceaccount"
serviceaccountcontroller "k8s.io/kubernetes/pkg/controller/serviceaccount"
"k8s.io/kubernetes/pkg/healthz"
"k8s.io/kubernetes/pkg/serviceaccount"
"k8s.io/kubernetes/pkg/util"
"k8s.io/kubernetes/contrib/mesos/pkg/profile"
@ -209,9 +210,9 @@ func (s *CMServer) Run(_ []string) error {
if err != nil {
glog.Errorf("Error reading key for service account token controller: %v", err)
} else {
serviceaccount.NewTokensController(
serviceaccountcontroller.NewTokensController(
kubeClient,
serviceaccount.TokensControllerOptions{
serviceaccountcontroller.TokensControllerOptions{
TokenGenerator: serviceaccount.JWTTokenGenerator(privateKey),
RootCA: rootCA,
},
@ -219,9 +220,9 @@ func (s *CMServer) Run(_ []string) error {
}
}
serviceaccount.NewServiceAccountsController(
serviceaccountcontroller.NewServiceAccountsController(
kubeClient,
serviceaccount.DefaultServiceAccountsControllerOptions(),
serviceaccountcontroller.DefaultServiceAccountsControllerOptions(),
).Run()
select {}

View File

@ -14,15 +14,14 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
package apiserver
package authenticator
import (
"crypto/rsa"
"k8s.io/kubernetes/pkg/auth/authenticator"
"k8s.io/kubernetes/pkg/auth/authenticator/bearertoken"
"k8s.io/kubernetes/pkg/controller/serviceaccount"
"k8s.io/kubernetes/pkg/storage"
"k8s.io/kubernetes/pkg/serviceaccount"
"k8s.io/kubernetes/pkg/util"
"k8s.io/kubernetes/plugin/pkg/auth/authenticator/password/passwordfile"
"k8s.io/kubernetes/plugin/pkg/auth/authenticator/request/basicauth"
@ -43,12 +42,13 @@ type AuthenticatorConfig struct {
OIDCUsernameClaim string
ServiceAccountKeyFile string
ServiceAccountLookup bool
Storage storage.Interface
ServiceAccountTokenGetter serviceaccount.ServiceAccountTokenGetter
KeystoneURL string
}
// NewAuthenticator returns an authenticator.Request or an error
func NewAuthenticator(config AuthenticatorConfig) (authenticator.Request, error) {
// New returns an authenticator.Request or an error that supports the standard
// Kubernetes authentication mechanisms.
func New(config AuthenticatorConfig) (authenticator.Request, error) {
var authenticators []authenticator.Request
if len(config.BasicAuthFile) > 0 {
@ -84,7 +84,7 @@ func NewAuthenticator(config AuthenticatorConfig) (authenticator.Request, error)
}
if len(config.ServiceAccountKeyFile) > 0 {
serviceAccountAuth, err := newServiceAccountAuthenticator(config.ServiceAccountKeyFile, config.ServiceAccountLookup, config.Storage)
serviceAccountAuth, err := newServiceAccountAuthenticator(config.ServiceAccountKeyFile, config.ServiceAccountLookup, config.ServiceAccountTokenGetter)
if err != nil {
return nil, err
}
@ -146,19 +146,12 @@ func newAuthenticatorFromOIDCIssuerURL(issuerURL, clientID, caFile, usernameClai
}
// newServiceAccountAuthenticator returns an authenticator.Request or an error
func newServiceAccountAuthenticator(keyfile string, lookup bool, storage storage.Interface) (authenticator.Request, error) {
func newServiceAccountAuthenticator(keyfile string, lookup bool, serviceAccountGetter serviceaccount.ServiceAccountTokenGetter) (authenticator.Request, error) {
publicKey, err := serviceaccount.ReadPublicKey(keyfile)
if err != nil {
return nil, err
}
var serviceAccountGetter serviceaccount.ServiceAccountTokenGetter
if lookup {
// If we need to look up service accounts and tokens,
// go directly to etcd to avoid recursive auth insanity
serviceAccountGetter = serviceaccount.NewGetterFromStorageInterface(storage)
}
tokenAuthenticator := serviceaccount.JWTTokenAuthenticator([]*rsa.PublicKey{publicKey}, lookup, serviceAccountGetter)
return bearertoken.New(tokenAuthenticator), nil
}

View File

@ -22,17 +22,12 @@ import (
"k8s.io/kubernetes/pkg/registry/generic"
"k8s.io/kubernetes/pkg/registry/secret"
secretetcd "k8s.io/kubernetes/pkg/registry/secret/etcd"
"k8s.io/kubernetes/pkg/registry/serviceaccount"
serviceaccountregistry "k8s.io/kubernetes/pkg/registry/serviceaccount"
serviceaccountetcd "k8s.io/kubernetes/pkg/registry/serviceaccount/etcd"
"k8s.io/kubernetes/pkg/serviceaccount"
"k8s.io/kubernetes/pkg/storage"
)
// ServiceAccountTokenGetter defines functions to retrieve a named service account and secret
type ServiceAccountTokenGetter interface {
GetServiceAccount(namespace, name string) (*api.ServiceAccount, error)
GetSecret(namespace, name string) (*api.Secret, error)
}
// clientGetter implements ServiceAccountTokenGetter using a client.Interface
type clientGetter struct {
client client.Interface
@ -42,7 +37,7 @@ type clientGetter struct {
// uses the specified client to retrieve service accounts and secrets.
// The client should NOT authenticate using a service account token
// the returned getter will be used to retrieve, or recursion will result.
func NewGetterFromClient(c client.Interface) ServiceAccountTokenGetter {
func NewGetterFromClient(c client.Interface) serviceaccount.ServiceAccountTokenGetter {
return clientGetter{c}
}
func (c clientGetter) GetServiceAccount(namespace, name string) (*api.ServiceAccount, error) {
@ -54,13 +49,13 @@ func (c clientGetter) GetSecret(namespace, name string) (*api.Secret, error) {
// registryGetter implements ServiceAccountTokenGetter using a service account and secret registry
type registryGetter struct {
serviceAccounts serviceaccount.Registry
serviceAccounts serviceaccountregistry.Registry
secrets secret.Registry
}
// NewGetterFromRegistries returns a ServiceAccountTokenGetter that
// uses the specified registries to retrieve service accounts and secrets.
func NewGetterFromRegistries(serviceAccounts serviceaccount.Registry, secrets secret.Registry) ServiceAccountTokenGetter {
func NewGetterFromRegistries(serviceAccounts serviceaccountregistry.Registry, secrets secret.Registry) serviceaccount.ServiceAccountTokenGetter {
return &registryGetter{serviceAccounts, secrets}
}
func (r *registryGetter) GetServiceAccount(namespace, name string) (*api.ServiceAccount, error) {
@ -74,9 +69,9 @@ func (r *registryGetter) GetSecret(namespace, name string) (*api.Secret, error)
// NewGetterFromStorageInterface returns a ServiceAccountTokenGetter that
// uses the specified storage to retrieve service accounts and secrets.
func NewGetterFromStorageInterface(s storage.Interface) ServiceAccountTokenGetter {
func NewGetterFromStorageInterface(s storage.Interface) serviceaccount.ServiceAccountTokenGetter {
return NewGetterFromRegistries(
serviceaccount.NewRegistry(serviceaccountetcd.NewREST(s, generic.UndecoratedStorage)),
serviceaccountregistry.NewRegistry(serviceaccountetcd.NewREST(s, generic.UndecoratedStorage)),
secret.NewRegistry(secretetcd.NewREST(s, generic.UndecoratedStorage)),
)
}

View File

@ -30,6 +30,7 @@ import (
"k8s.io/kubernetes/pkg/fields"
"k8s.io/kubernetes/pkg/registry/secret"
"k8s.io/kubernetes/pkg/runtime"
"k8s.io/kubernetes/pkg/serviceaccount"
"k8s.io/kubernetes/pkg/util/sets"
"k8s.io/kubernetes/pkg/util/wait"
"k8s.io/kubernetes/pkg/watch"
@ -40,7 +41,7 @@ const NumServiceAccountRemoveReferenceRetries = 10
// TokensControllerOptions contains options for the TokensController
type TokensControllerOptions struct {
// TokenGenerator is the generator to use to create new tokens
TokenGenerator TokenGenerator
TokenGenerator serviceaccount.TokenGenerator
// ServiceAccountResync is the time.Duration at which to fully re-list service accounts.
// If zero, re-list will be delayed as long as possible
ServiceAccountResync time.Duration
@ -111,7 +112,7 @@ type TokensController struct {
stopChan chan struct{}
client client.Interface
token TokenGenerator
token serviceaccount.TokenGenerator
rootCA []byte
@ -451,7 +452,7 @@ func (e *TokensController) getServiceAccount(secret *api.Secret, fetchOnCacheMis
for _, obj := range namespaceAccounts {
serviceAccount := obj.(*api.ServiceAccount)
if IsServiceAccountToken(secret, serviceAccount) {
if serviceaccount.IsServiceAccountToken(secret, serviceAccount) {
return serviceAccount, nil
}
}
@ -465,7 +466,7 @@ func (e *TokensController) getServiceAccount(secret *api.Secret, fetchOnCacheMis
return nil, err
}
if IsServiceAccountToken(secret, serviceAccount) {
if serviceaccount.IsServiceAccountToken(secret, serviceAccount) {
return serviceAccount, nil
}
}
@ -486,7 +487,7 @@ func (e *TokensController) listTokenSecrets(serviceAccount *api.ServiceAccount)
for _, obj := range namespaceSecrets {
secret := obj.(*api.Secret)
if IsServiceAccountToken(secret, serviceAccount) {
if serviceaccount.IsServiceAccountToken(secret, serviceAccount) {
items = append(items, secret)
}
}

View File

@ -42,6 +42,12 @@ const (
NamespaceClaim = "kubernetes.io/serviceaccount/namespace"
)
// ServiceAccountTokenGetter defines functions to retrieve a named service account and secret
type ServiceAccountTokenGetter interface {
GetServiceAccount(namespace, name string) (*api.ServiceAccount, error)
GetSecret(namespace, name string) (*api.Secret, error)
}
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.

View File

@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
package serviceaccount
package serviceaccount_test
import (
"crypto/rsa"
@ -24,9 +24,12 @@ import (
"testing"
"github.com/dgrijalva/jwt-go"
"k8s.io/kubernetes/pkg/api"
client "k8s.io/kubernetes/pkg/client/unversioned"
"k8s.io/kubernetes/pkg/client/unversioned/testclient"
serviceaccountcontroller "k8s.io/kubernetes/pkg/controller/serviceaccount"
"k8s.io/kubernetes/pkg/serviceaccount"
)
const otherPublicKey = `-----BEGIN PUBLIC KEY-----
@ -100,7 +103,7 @@ func TestReadPrivateKey(t *testing.T) {
t.Fatalf("error creating tmpfile: %v", err)
}
if _, err := ReadPrivateKey(f.Name()); err != nil {
if _, err := serviceaccount.ReadPrivateKey(f.Name()); err != nil {
t.Fatalf("error reading key: %v", err)
}
}
@ -116,7 +119,7 @@ func TestReadPublicKey(t *testing.T) {
t.Fatalf("error creating tmpfile: %v", err)
}
if _, err := ReadPublicKey(f.Name()); err != nil {
if _, err := serviceaccount.ReadPublicKey(f.Name()); err != nil {
t.Fatalf("error reading key: %v", err)
}
}
@ -141,7 +144,7 @@ func TestTokenGenerateAndValidate(t *testing.T) {
}
// Generate the token
generator := JWTTokenGenerator(getPrivateKey(privateKey))
generator := serviceaccount.JWTTokenGenerator(getPrivateKey(privateKey))
token, err := generator.GenerateToken(*serviceAccount, *secret)
if err != nil {
t.Fatalf("error generating token: %v", err)
@ -219,8 +222,8 @@ func TestTokenGenerateAndValidate(t *testing.T) {
}
for k, tc := range testCases {
getter := NewGetterFromClient(tc.Client)
authenticator := JWTTokenAuthenticator(tc.Keys, tc.Client != nil, getter)
getter := serviceaccountcontroller.NewGetterFromClient(tc.Client)
authenticator := serviceaccount.JWTTokenAuthenticator(tc.Keys, tc.Client != nil, getter)
user, ok, err := authenticator.AuthenticateToken(token)
if (err != nil) != tc.ExpectedErr {
@ -253,8 +256,8 @@ func TestTokenGenerateAndValidate(t *testing.T) {
}
func TestMakeSplitUsername(t *testing.T) {
username := MakeUsername("ns", "name")
ns, name, err := SplitUsername(username)
username := serviceaccount.MakeUsername("ns", "name")
ns, name, err := serviceaccount.SplitUsername(username)
if err != nil {
t.Errorf("Unexpected error %v", err)
}
@ -264,7 +267,7 @@ func TestMakeSplitUsername(t *testing.T) {
invalid := []string{"test", "system:serviceaccount", "system:serviceaccount:", "system:serviceaccount:ns", "system:serviceaccount:ns:name:extra"}
for _, n := range invalid {
_, _, err := SplitUsername("test")
_, _, err := serviceaccount.SplitUsername("test")
if err == nil {
t.Errorf("Expected error for %s", n)
}

View File

@ -28,10 +28,10 @@ import (
"k8s.io/kubernetes/pkg/api/errors"
"k8s.io/kubernetes/pkg/client/cache"
client "k8s.io/kubernetes/pkg/client/unversioned"
"k8s.io/kubernetes/pkg/controller/serviceaccount"
"k8s.io/kubernetes/pkg/fields"
kubelet "k8s.io/kubernetes/pkg/kubelet/types"
"k8s.io/kubernetes/pkg/runtime"
"k8s.io/kubernetes/pkg/serviceaccount"
"k8s.io/kubernetes/pkg/util/sets"
"k8s.io/kubernetes/pkg/watch"
)

View File

@ -39,8 +39,9 @@ import (
"k8s.io/kubernetes/pkg/auth/authorizer"
"k8s.io/kubernetes/pkg/auth/user"
client "k8s.io/kubernetes/pkg/client/unversioned"
"k8s.io/kubernetes/pkg/controller/serviceaccount"
serviceaccountcontroller "k8s.io/kubernetes/pkg/controller/serviceaccount"
"k8s.io/kubernetes/pkg/master"
"k8s.io/kubernetes/pkg/serviceaccount"
"k8s.io/kubernetes/pkg/util/sets"
"k8s.io/kubernetes/pkg/util/wait"
serviceaccountadmission "k8s.io/kubernetes/plugin/pkg/admission/serviceaccount"
@ -358,7 +359,7 @@ func startServiceAccountTestServer(t *testing.T) (*client.Client, client.Config,
return nil, false, nil
})
serviceAccountKey, _ := rsa.GenerateKey(rand.Reader, 2048)
serviceAccountTokenGetter := serviceaccount.NewGetterFromClient(rootClient)
serviceAccountTokenGetter := serviceaccountcontroller.NewGetterFromClient(rootClient)
serviceAccountTokenAuth := serviceaccount.JWTTokenAuthenticator([]*rsa.PublicKey{&serviceAccountKey.PublicKey}, true, serviceAccountTokenGetter)
authenticator := union.New(
bearertoken.New(rootTokenAuth),
@ -410,9 +411,9 @@ func startServiceAccountTestServer(t *testing.T) (*client.Client, client.Config,
m = master.New(masterConfig)
// Start the service account and service account token controllers
tokenController := serviceaccount.NewTokensController(rootClient, serviceaccount.TokensControllerOptions{TokenGenerator: serviceaccount.JWTTokenGenerator(serviceAccountKey)})
tokenController := serviceaccountcontroller.NewTokensController(rootClient, serviceaccountcontroller.TokensControllerOptions{TokenGenerator: serviceaccount.JWTTokenGenerator(serviceAccountKey)})
tokenController.Run()
serviceAccountController := serviceaccount.NewServiceAccountsController(rootClient, serviceaccount.DefaultServiceAccountsControllerOptions())
serviceAccountController := serviceaccountcontroller.NewServiceAccountsController(rootClient, serviceaccountcontroller.DefaultServiceAccountsControllerOptions())
serviceAccountController.Run()
// Start the admission plugin reflectors
serviceAccountAdmission.Run()