add anytoken authenticator

pull/6/head
deads2k 2016-09-23 12:35:31 -04:00
parent d187997c94
commit 5080a575ad
10 changed files with 140 additions and 4 deletions

View File

@ -202,6 +202,7 @@ func Run(s *options.APIServer) error {
apiAuthenticator, err := authenticator.New(authenticator.AuthenticatorConfig{ apiAuthenticator, err := authenticator.New(authenticator.AuthenticatorConfig{
Anonymous: s.AnonymousAuth, Anonymous: s.AnonymousAuth,
AnyToken: s.EnableAnyToken,
BasicAuthFile: s.BasicAuthFile, BasicAuthFile: s.BasicAuthFile,
ClientCAFile: s.ClientCAFile, ClientCAFile: s.ClientCAFile,
TokenAuthFile: s.TokenAuthFile, TokenAuthFile: s.TokenAuthFile,

View File

@ -116,6 +116,7 @@ func Run(s *options.ServerRunOptions) error {
apiAuthenticator, err := authenticator.New(authenticator.AuthenticatorConfig{ apiAuthenticator, err := authenticator.New(authenticator.AuthenticatorConfig{
Anonymous: s.AnonymousAuth, Anonymous: s.AnonymousAuth,
AnyToken: s.EnableAnyToken,
BasicAuthFile: s.BasicAuthFile, BasicAuthFile: s.BasicAuthFile,
ClientCAFile: s.ClientCAFile, ClientCAFile: s.ClientCAFile,
TokenAuthFile: s.TokenAuthFile, TokenAuthFile: s.TokenAuthFile,

View File

@ -280,6 +280,10 @@ function start_apiserver {
CERT_DIR=/var/run/kubernetes CERT_DIR=/var/run/kubernetes
ROOT_CA_FILE=$CERT_DIR/apiserver.crt ROOT_CA_FILE=$CERT_DIR/apiserver.crt
anytoken_arg=""
if [[ -n "${ALLOW_ANY_TOKEN:-}" ]]; then
anytoken_arg="--insecure-allow-any-token "
fi
priv_arg="" priv_arg=""
if [[ -n "${ALLOW_PRIVILEGED}" ]]; then if [[ -n "${ALLOW_PRIVILEGED}" ]]; then
priv_arg="--allow-privileged " priv_arg="--allow-privileged "
@ -297,7 +301,7 @@ function start_apiserver {
fi fi
APISERVER_LOG=/tmp/kube-apiserver.log APISERVER_LOG=/tmp/kube-apiserver.log
sudo -E "${GO_OUT}/hyperkube" apiserver ${priv_arg} ${runtime_config}\ sudo -E "${GO_OUT}/hyperkube" apiserver ${anytoken_arg} ${priv_arg} ${runtime_config}\
${advertise_address} \ ${advertise_address} \
--v=${LOG_LEVEL} \ --v=${LOG_LEVEL} \
--cert-dir="${CERT_DIR}" \ --cert-dir="${CERT_DIR}" \

View File

@ -250,8 +250,9 @@ included-types-overrides
include-extended-apis include-extended-apis
input-base input-base
input-dirs input-dirs
insecure-experimental-approve-all-kubelet-csrs-for-group insecure-allow-any-token
insecure-bind-address insecure-bind-address
insecure-experimental-approve-all-kubelet-csrs-for-group
insecure-port insecure-port
insecure-skip-tls-verify insecure-skip-tls-verify
instance-metadata instance-metadata

View File

@ -32,6 +32,7 @@ import (
"k8s.io/kubernetes/plugin/pkg/auth/authenticator/request/basicauth" "k8s.io/kubernetes/plugin/pkg/auth/authenticator/request/basicauth"
"k8s.io/kubernetes/plugin/pkg/auth/authenticator/request/union" "k8s.io/kubernetes/plugin/pkg/auth/authenticator/request/union"
"k8s.io/kubernetes/plugin/pkg/auth/authenticator/request/x509" "k8s.io/kubernetes/plugin/pkg/auth/authenticator/request/x509"
"k8s.io/kubernetes/plugin/pkg/auth/authenticator/token/anytoken"
"k8s.io/kubernetes/plugin/pkg/auth/authenticator/token/oidc" "k8s.io/kubernetes/plugin/pkg/auth/authenticator/token/oidc"
"k8s.io/kubernetes/plugin/pkg/auth/authenticator/token/tokenfile" "k8s.io/kubernetes/plugin/pkg/auth/authenticator/token/tokenfile"
"k8s.io/kubernetes/plugin/pkg/auth/authenticator/token/webhook" "k8s.io/kubernetes/plugin/pkg/auth/authenticator/token/webhook"
@ -39,6 +40,7 @@ import (
type AuthenticatorConfig struct { type AuthenticatorConfig struct {
Anonymous bool Anonymous bool
AnyToken bool
BasicAuthFile string BasicAuthFile string
ClientCAFile string ClientCAFile string
TokenAuthFile string TokenAuthFile string
@ -121,10 +123,19 @@ func New(config AuthenticatorConfig) (authenticator.Request, error) {
authenticators = append(authenticators, webhookTokenAuth) authenticators = append(authenticators, webhookTokenAuth)
} }
// always add anytoken last, so that every other token authenticator gets to try first
if config.AnyToken {
authenticators = append(authenticators, bearertoken.New(anytoken.AnyTokenAuthenticator{}))
}
if len(authenticators) == 0 { if len(authenticators) == 0 {
if config.Anonymous { if config.Anonymous {
return anonymous.NewAuthenticator(), nil return anonymous.NewAuthenticator(), nil
} }
}
switch len(authenticators) {
case 0:
return nil, nil return nil, nil
} }

View File

@ -9148,7 +9148,7 @@ var OpenAPIDefinitions *common.OpenAPIDefinitions = &common.OpenAPIDefinitions{
"v1.Node": { "v1.Node": {
Schema: spec.Schema{ Schema: spec.Schema{
SchemaProps: spec.SchemaProps{ SchemaProps: spec.SchemaProps{
Description: "Node is a worker node in Kubernetes, formerly known as minion. Each node will have a unique identifier in the cache (i.e. in etcd).", Description: "Node is a worker node in Kubernetes. Each node will have a unique identifier in the cache (i.e. in etcd).",
Properties: map[string]spec.Schema{ Properties: map[string]spec.Schema{
"metadata": { "metadata": {
SchemaProps: spec.SchemaProps{ SchemaProps: spec.SchemaProps{

View File

@ -258,7 +258,7 @@ func (s *GenericAPIServer) Run(options *options.ServerRunOptions) {
options.TLSPrivateKeyFile = path.Join(options.CertDirectory, "apiserver.key") options.TLSPrivateKeyFile = path.Join(options.CertDirectory, "apiserver.key")
// TODO (cjcullen): Is ClusterIP the right address to sign a cert with? // TODO (cjcullen): Is ClusterIP the right address to sign a cert with?
alternateIPs := []net.IP{s.ServiceReadWriteIP} alternateIPs := []net.IP{s.ServiceReadWriteIP}
alternateDNS := []string{"kubernetes.default.svc", "kubernetes.default", "kubernetes"} alternateDNS := []string{"kubernetes.default.svc", "kubernetes.default", "kubernetes", "localhost"}
// It would be nice to set a fqdn subject alt name, but only the kubelets know, the apiserver is clueless // It would be nice to set a fqdn subject alt name, but only the kubelets know, the apiserver is clueless
// alternateDNS = append(alternateDNS, "kubernetes.default.svc.CLUSTER.DNS.NAME") // alternateDNS = append(alternateDNS, "kubernetes.default.svc.CLUSTER.DNS.NAME")
if !certutil.CanReadCertOrKey(options.TLSCertFile, options.TLSPrivateKeyFile) { if !certutil.CanReadCertOrKey(options.TLSCertFile, options.TLSPrivateKeyFile) {

View File

@ -120,6 +120,7 @@ type ServerRunOptions struct {
TLSCertFile string TLSCertFile string
TLSPrivateKeyFile string TLSPrivateKeyFile string
TokenAuthFile string TokenAuthFile string
EnableAnyToken bool
WatchCacheSizes []string WatchCacheSizes []string
} }
@ -473,6 +474,10 @@ func (s *ServerRunOptions) AddUniversalFlags(fs *pflag.FlagSet) {
"If set, the file that will be used to secure the secure port of the API server "+ "If set, the file that will be used to secure the secure port of the API server "+
"via token authentication.") "via token authentication.")
fs.BoolVar(&s.EnableAnyToken, "insecure-allow-any-token", s.EnableAnyToken, ""+
"If set, your server will be INSECURE. Any token will be allowed and user information will be parsed "+
"from the token as `username/group1,group2`")
fs.StringSliceVar(&s.WatchCacheSizes, "watch-cache-sizes", s.WatchCacheSizes, ""+ fs.StringSliceVar(&s.WatchCacheSizes, "watch-cache-sizes", s.WatchCacheSizes, ""+
"List of watch cache sizes for every resource (pods, nodes, etc.), comma separated. "+ "List of watch cache sizes for every resource (pods, nodes, etc.), comma separated. "+
"The individual override format: resource#size, where size is a number. It takes effect "+ "The individual override format: resource#size, where size is a number. It takes effect "+

View File

@ -0,0 +1,42 @@
/*
Copyright 2016 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 anytoken
import (
"strings"
"k8s.io/kubernetes/pkg/auth/user"
)
type AnyTokenAuthenticator struct{}
func (AnyTokenAuthenticator) AuthenticateToken(value string) (user.Info, bool, error) {
lastSlash := strings.LastIndex(value, "/")
if lastSlash == -1 {
return &user.DefaultInfo{Name: value}, true, nil
}
ret := &user.DefaultInfo{Name: value[:lastSlash]}
groupString := value[lastSlash+1:]
if len(groupString) == 0 {
return ret, true, nil
}
ret.Groups = strings.Split(groupString, ",")
return ret, true, nil
}

View File

@ -0,0 +1,71 @@
/*
Copyright 2016 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 anytoken
import (
"reflect"
"testing"
"k8s.io/kubernetes/pkg/auth/user"
)
func TestAnyTokenAuthenticator(t *testing.T) {
tests := []struct {
name string
token string
expectedUser user.Info
}{
{
name: "user only",
token: "joe",
expectedUser: &user.DefaultInfo{Name: "joe"},
},
{
name: "user with slash",
token: "scheme/joe/",
expectedUser: &user.DefaultInfo{Name: "scheme/joe"},
},
{
name: "user with groups",
token: "joe/group1,group2",
expectedUser: &user.DefaultInfo{Name: "joe", Groups: []string{"group1", "group2"}},
},
{
name: "user with slash and groups",
token: "scheme/joe/group1,group2",
expectedUser: &user.DefaultInfo{Name: "scheme/joe", Groups: []string{"group1", "group2"}},
},
}
for _, tc := range tests {
actualUser, _, _ := AnyTokenAuthenticator{}.AuthenticateToken(tc.token)
if len(actualUser.GetExtra()) != 0 {
t.Errorf("%q: got extra: %v", tc.name, actualUser.GetExtra())
}
if len(actualUser.GetUID()) != 0 {
t.Errorf("%q: got extra: %v", tc.name, actualUser.GetUID())
}
if e, a := tc.expectedUser.GetName(), actualUser.GetName(); e != a {
t.Errorf("%q: expected %v, got %v", tc.name, e, a)
}
if e, a := tc.expectedUser.GetGroups(), actualUser.GetGroups(); !reflect.DeepEqual(e, a) {
t.Errorf("%q: expected %v, got %v", tc.name, e, a)
}
}
}