pass loopback config to posthooks

pull/6/head
deads2k 2016-09-29 15:10:04 -04:00
parent 448ceb3881
commit 4c8959df59
12 changed files with 128 additions and 36 deletions

View File

@ -265,6 +265,10 @@ func Run(s *options.APIServer) error {
admissionControlPluginNames := strings.Split(s.AdmissionControl, ",")
privilegedLoopbackToken := uuid.NewRandom().String()
selfClientConfig, err := s.NewSelfClientConfig(privilegedLoopbackToken)
if err != nil {
glog.Fatalf("Failed to create clientset: %v", err)
}
client, err := s.NewSelfClient(privilegedLoopbackToken)
if err != nil {
glog.Errorf("Failed to create clientset: %v", err)
@ -297,6 +301,7 @@ func Run(s *options.APIServer) error {
genericConfig := genericapiserver.NewConfig(s.ServerRunOptions)
// TODO: Move the following to generic api server as well.
genericConfig.LoopbackClientConfig = selfClientConfig
genericConfig.Authenticator = apiAuthenticator
genericConfig.SupportsBasicAuth = len(s.BasicAuthFile) > 0
genericConfig.Authorizer = apiAuthorizer

View File

@ -173,6 +173,10 @@ func Run(s *options.ServerRunOptions) error {
admissionControlPluginNames := strings.Split(s.AdmissionControl, ",")
privilegedLoopbackToken := uuid.NewRandom().String()
selfClientConfig, err := s.NewSelfClientConfig(privilegedLoopbackToken)
if err != nil {
glog.Fatalf("Failed to create clientset: %v", err)
}
client, err := s.NewSelfClient(privilegedLoopbackToken)
if err != nil {
glog.Errorf("Failed to create clientset: %v", err)
@ -204,6 +208,7 @@ func Run(s *options.ServerRunOptions) error {
}
genericConfig := genericapiserver.NewConfig(s.ServerRunOptions)
// TODO: Move the following to generic api server as well.
genericConfig.LoopbackClientConfig = selfClientConfig
genericConfig.Authenticator = apiAuthenticator
genericConfig.SupportsBasicAuth = len(s.BasicAuthFile) > 0
genericConfig.Authorizer = apiAuthorizer

View File

@ -42,6 +42,7 @@ import (
"k8s.io/kubernetes/pkg/auth/authenticator"
"k8s.io/kubernetes/pkg/auth/authorizer"
authhandlers "k8s.io/kubernetes/pkg/auth/handlers"
"k8s.io/kubernetes/pkg/client/restclient"
"k8s.io/kubernetes/pkg/cloudprovider"
genericfilters "k8s.io/kubernetes/pkg/genericapiserver/filters"
"k8s.io/kubernetes/pkg/genericapiserver/openapi/common"
@ -83,6 +84,9 @@ type Config struct {
// TODO(ericchiang): Determine if policy escalation checks should be an admission controller.
AuthorizerRBACSuperUser string
// LoopbackClientConfig is a config for a privileged loopback connection to the API server
LoopbackClientConfig *restclient.Config
// Map requests to contexts. Exported so downstream consumers can provider their own mappers
RequestContextMapper api.RequestContextMapper
@ -309,6 +313,7 @@ func (c completedConfig) New() (*GenericAPIServer, error) {
s := &GenericAPIServer{
ServiceClusterIPRange: c.ServiceClusterIPRange,
ServiceNodePortRange: c.ServiceNodePortRange,
LoopbackClientConfig: c.LoopbackClientConfig,
legacyAPIPrefix: c.APIPrefix,
apiPrefix: c.APIGroupPrefix,
admissionControl: c.AdmissionControl,

View File

@ -42,6 +42,7 @@ import (
"k8s.io/kubernetes/pkg/apimachinery"
"k8s.io/kubernetes/pkg/apimachinery/registered"
"k8s.io/kubernetes/pkg/apiserver"
"k8s.io/kubernetes/pkg/client/restclient"
"k8s.io/kubernetes/pkg/genericapiserver/openapi"
"k8s.io/kubernetes/pkg/genericapiserver/openapi/common"
"k8s.io/kubernetes/pkg/genericapiserver/options"
@ -94,6 +95,9 @@ type GenericAPIServer struct {
// TODO refactor this closer to the point of use.
ServiceNodePortRange utilnet.PortRange
// LoopbackClientConfig is a config for a privileged loopback connection to the API server
LoopbackClientConfig *restclient.Config
// minRequestTimeout is how short the request timeout can be. This is used to build the RESTHandler
minRequestTimeout time.Duration
@ -315,7 +319,7 @@ func (s *GenericAPIServer) Run(options *options.ServerRunOptions) {
<-secureStartedCh
<-insecureStartedCh
s.RunPostStartHooks(PostStartHookContext{})
s.RunPostStartHooks()
// err == systemd.SdNotifyNoSocket when not running on a systemd system
if err := systemd.SdNotify("READY=1\n"); err != nil && err != systemd.SdNotifyNoSocket {

View File

@ -21,6 +21,7 @@ import (
"github.com/golang/glog"
"k8s.io/kubernetes/pkg/client/restclient"
utilruntime "k8s.io/kubernetes/pkg/util/runtime"
)
@ -37,8 +38,8 @@ type PostStartHookFunc func(context PostStartHookContext) error
// PostStartHookContext provides information about this API server to a PostStartHookFunc
type PostStartHookContext struct {
// TODO this should probably contain a cluster-admin powered client config which can be used to loopback
// to this API server. That client config doesn't exist yet.
// LoopbackClientConfig is a config for a privileged loopback connection to the API server
LoopbackClientConfig *restclient.Config
}
// PostStartHookProvider is an interface in addition to provide a post start hook for the api server
@ -74,11 +75,13 @@ func (s *GenericAPIServer) AddPostStartHook(name string, hook PostStartHookFunc)
}
// RunPostStartHooks runs the PostStartHooks for the server
func (s *GenericAPIServer) RunPostStartHooks(context PostStartHookContext) {
func (s *GenericAPIServer) RunPostStartHooks() {
s.postStartHookLock.Lock()
defer s.postStartHookLock.Unlock()
s.postStartHooksCalled = true
context := PostStartHookContext{LoopbackClientConfig: s.LoopbackClientConfig}
for hookName, hook := range s.postStartHooks {
go runPostStartHook(hookName, hook, context)
}

View File

@ -211,6 +211,15 @@ func mergeGroupVersionIntoMap(gvList string, dest map[string]unversioned.GroupVe
// Returns a clientset which can be used to talk to this apiserver.
func (s *ServerRunOptions) NewSelfClient(token string) (clientset.Interface, error) {
clientConfig, err := s.NewSelfClientConfig(token)
if err != nil {
return nil, err
}
return clientset.NewForConfig(clientConfig)
}
// Returns a clientconfig which can be used to talk to this apiserver.
func (s *ServerRunOptions) NewSelfClientConfig(token string) (*restclient.Config, error) {
clientConfig := &restclient.Config{
// Increase QPS limits. The client is currently passed to all admission plugins,
// and those can be throttled in case of higher load on apiserver - see #22340 and #22422
@ -228,7 +237,7 @@ func (s *ServerRunOptions) NewSelfClient(token string) (clientset.Interface, err
return nil, errors.New("Unable to set url for apiserver local client")
}
return clientset.NewForConfig(clientConfig)
return clientConfig, nil
}
// AddFlags adds flags for a specific APIServer to the specified FlagSet

View File

@ -46,6 +46,14 @@ func (s *Storage) Create(ctx api.Context, obj runtime.Object) (runtime.Object, e
if s.superUser != "" && user.GetName() == s.superUser {
return s.StandardStorage.Create(ctx, obj)
}
// system:masters is special because the API server uses it for privileged loopback connections
// therefore we know that a member of system:masters can always do anything
for _, group := range user.GetGroups() {
if group == "system:masters" {
return s.StandardStorage.Create(ctx, obj)
}
}
}
clusterRole := obj.(*rbac.ClusterRole)

View File

@ -46,6 +46,14 @@ func (s *Storage) Create(ctx api.Context, obj runtime.Object) (runtime.Object, e
if s.superUser != "" && user.GetName() == s.superUser {
return s.StandardStorage.Create(ctx, obj)
}
// system:masters is special because the API server uses it for privileged loopback connections
// therefore we know that a member of system:masters can always do anything
for _, group := range user.GetGroups() {
if group == "system:masters" {
return s.StandardStorage.Create(ctx, obj)
}
}
}
clusterRoleBinding := obj.(*rbac.ClusterRoleBinding)

View File

@ -27,6 +27,7 @@ import (
"k8s.io/kubernetes/pkg/apis/rbac"
rbacapiv1alpha1 "k8s.io/kubernetes/pkg/apis/rbac/v1alpha1"
rbacvalidation "k8s.io/kubernetes/pkg/apis/rbac/validation"
rbacclient "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/rbac/unversioned"
"k8s.io/kubernetes/pkg/genericapiserver"
"k8s.io/kubernetes/pkg/registry/rbac/clusterrole"
clusterroleetcd "k8s.io/kubernetes/pkg/registry/rbac/clusterrole/etcd"
@ -46,8 +47,6 @@ import (
type RESTStorageProvider struct {
AuthorizerRBACSuperUser string
postStartHook genericapiserver.PostStartHookFunc
}
var _ genericapiserver.RESTStorageProvider = &RESTStorageProvider{}
@ -93,8 +92,6 @@ func (p *RESTStorageProvider) v1alpha1Storage(apiResourceConfigSource genericapi
if apiResourceConfigSource.ResourceEnabled(version.WithResource("clusterroles")) {
clusterRolesStorage := clusterroleetcd.NewREST(restOptionsGetter(rbac.Resource("clusterroles")))
storage["clusterroles"] = clusterrolepolicybased.NewStorage(clusterRolesStorage, newRuleValidator(), p.AuthorizerRBACSuperUser)
p.postStartHook = newPostStartHook(clusterRolesStorage)
}
if apiResourceConfigSource.ResourceEnabled(version.WithResource("clusterrolebindings")) {
clusterRoleBindingsStorage := clusterrolebindingetcd.NewREST(restOptionsGetter(rbac.Resource("clusterrolebindings")))
@ -104,33 +101,35 @@ func (p *RESTStorageProvider) v1alpha1Storage(apiResourceConfigSource genericapi
}
func (p *RESTStorageProvider) PostStartHook() (string, genericapiserver.PostStartHookFunc, error) {
return "rbac/bootstrap-roles", p.postStartHook, nil
return "rbac/bootstrap-roles", PostStartHook, nil
}
func newPostStartHook(directClusterRoleAccess *clusterroleetcd.REST) genericapiserver.PostStartHookFunc {
return func(genericapiserver.PostStartHookContext) error {
ctx := api.NewContext()
existingClusterRoles, err := directClusterRoleAccess.List(ctx, &api.ListOptions{})
if err != nil {
utilruntime.HandleError(fmt.Errorf("unable to initialize clusterroles: %v", err))
return nil
}
// if clusterroles already exist, then assume we don't have work to do because we've already
// initialized or another API server has started this task
if len(existingClusterRoles.(*rbac.ClusterRoleList).Items) > 0 {
return nil
}
for _, clusterRole := range append(bootstrappolicy.ClusterRoles(), bootstrappolicy.ControllerRoles()...) {
if _, err := directClusterRoleAccess.Create(ctx, &clusterRole); err != nil {
// don't fail on failures, try to create as many as you can
utilruntime.HandleError(fmt.Errorf("unable to initialize clusterroles: %v", err))
continue
}
glog.Infof("Created clusterrole.%s/%s", rbac.GroupName, clusterRole.Name)
}
func PostStartHook(hookContext genericapiserver.PostStartHookContext) error {
clientset, err := rbacclient.NewForConfig(hookContext.LoopbackClientConfig)
if err != nil {
utilruntime.HandleError(fmt.Errorf("unable to initialize clusterroles: %v", err))
return nil
}
existingClusterRoles, err := clientset.ClusterRoles().List(api.ListOptions{})
if err != nil {
utilruntime.HandleError(fmt.Errorf("unable to initialize clusterroles: %v", err))
return nil
}
// if clusterroles already exist, then assume we don't have work to do because we've already
// initialized or another API server has started this task
if len(existingClusterRoles.Items) > 0 {
return nil
}
for _, clusterRole := range append(bootstrappolicy.ClusterRoles(), bootstrappolicy.ControllerRoles()...) {
if _, err := clientset.ClusterRoles().Create(&clusterRole); err != nil {
// don't fail on failures, try to create as many as you can
utilruntime.HandleError(fmt.Errorf("unable to initialize clusterroles: %v", err))
continue
}
glog.Infof("Created clusterrole.%s/%s", rbac.GroupName, clusterRole.Name)
}
return nil
}

View File

@ -46,6 +46,14 @@ func (s *Storage) Create(ctx api.Context, obj runtime.Object) (runtime.Object, e
if s.superUser != "" && user.GetName() == s.superUser {
return s.StandardStorage.Create(ctx, obj)
}
// system:masters is special because the API server uses it for privileged loopback connections
// therefore we know that a member of system:masters can always do anything
for _, group := range user.GetGroups() {
if group == "system:masters" {
return s.StandardStorage.Create(ctx, obj)
}
}
}
role := obj.(*rbac.Role)

View File

@ -46,6 +46,14 @@ func (s *Storage) Create(ctx api.Context, obj runtime.Object) (runtime.Object, e
if s.superUser != "" && user.GetName() == s.superUser {
return s.StandardStorage.Create(ctx, obj)
}
// system:masters is special because the API server uses it for privileged loopback connections
// therefore we know that a member of system:masters can always do anything
for _, group := range user.GetGroups() {
if group == "system:masters" {
return s.StandardStorage.Create(ctx, obj)
}
}
}
roleBinding := obj.(*rbac.RoleBinding)

View File

@ -38,12 +38,16 @@ import (
"k8s.io/kubernetes/pkg/apis/policy"
"k8s.io/kubernetes/pkg/apis/rbac"
"k8s.io/kubernetes/pkg/apis/storage"
"k8s.io/kubernetes/pkg/apiserver/authenticator"
authorizerunion "k8s.io/kubernetes/pkg/auth/authorizer/union"
"k8s.io/kubernetes/pkg/auth/user"
clientset "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
"k8s.io/kubernetes/pkg/client/record"
"k8s.io/kubernetes/pkg/client/restclient"
client "k8s.io/kubernetes/pkg/client/unversioned"
"k8s.io/kubernetes/pkg/controller"
replicationcontroller "k8s.io/kubernetes/pkg/controller/replication"
"k8s.io/kubernetes/pkg/generated/openapi"
"k8s.io/kubernetes/pkg/genericapiserver"
"k8s.io/kubernetes/pkg/genericapiserver/authorizer"
"k8s.io/kubernetes/pkg/kubectl"
@ -53,10 +57,10 @@ import (
"k8s.io/kubernetes/pkg/storage/storagebackend"
utilnet "k8s.io/kubernetes/pkg/util/net"
"k8s.io/kubernetes/plugin/pkg/admission/admit"
authenticatorunion "k8s.io/kubernetes/plugin/pkg/auth/authenticator/request/union"
"github.com/go-openapi/spec"
"github.com/pborman/uuid"
"k8s.io/kubernetes/pkg/generated/openapi"
)
const (
@ -149,6 +153,32 @@ func startMasterOrDie(masterConfig *master.Config) (*master.Master, *httptest.Se
},
}
}
// set the loopback client config
if masterConfig.GenericConfig.LoopbackClientConfig == nil {
masterConfig.GenericConfig.LoopbackClientConfig = &restclient.Config{QPS: 50, Burst: 100}
}
masterConfig.GenericConfig.LoopbackClientConfig.Host = s.URL
privilegedLoopbackToken := uuid.NewRandom().String()
// wrap any available authorizer
if masterConfig.GenericConfig.Authenticator != nil {
tokens := make(map[string]*user.DefaultInfo)
tokens[privilegedLoopbackToken] = &user.DefaultInfo{
Name: "system:apiserver",
UID: uuid.NewRandom().String(),
Groups: []string{"system:masters"},
}
tokenAuthenticator := authenticator.NewAuthenticatorFromTokens(tokens)
masterConfig.GenericConfig.Authenticator = authenticatorunion.New(tokenAuthenticator, masterConfig.GenericConfig.Authenticator)
tokenAuthorizer := authorizer.NewPrivilegedGroups("system:masters")
masterConfig.GenericConfig.Authorizer = authorizerunion.New(tokenAuthorizer, masterConfig.GenericConfig.Authorizer)
masterConfig.GenericConfig.LoopbackClientConfig.BearerToken = privilegedLoopbackToken
}
m, err := masterConfig.Complete().New()
if err != nil {
glog.Fatalf("error in bringing up the master: %v", err)
@ -157,7 +187,7 @@ func startMasterOrDie(masterConfig *master.Config) (*master.Master, *httptest.Se
// TODO have this start method actually use the normal start sequence for the API server
// this method never actually calls the `Run` method for the API server
// fire the post hooks ourselves
m.GenericAPIServer.RunPostStartHooks(genericapiserver.PostStartHookContext{})
m.GenericAPIServer.RunPostStartHooks()
return m, s
}