Skip to content

move authorizer into the construction of the kubeapiserver #20552

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 17 additions & 9 deletions pkg/cmd/openshift-apiserver/server.go
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
package openshift_apiserver

import (
"time"

"github.com/golang/glog"

kerrors "k8s.io/apimachinery/pkg/api/errors"
utilwait "k8s.io/apimachinery/pkg/util/wait"
"k8s.io/apiserver/pkg/authorization/authorizerfactory"
authorizationclient "k8s.io/client-go/kubernetes/typed/authorization/v1beta1"
"k8s.io/client-go/pkg/version"
"k8s.io/client-go/tools/cache"
"k8s.io/kubernetes/pkg/capabilities"
kubelettypes "k8s.io/kubernetes/pkg/kubelet/types"

Expand All @@ -15,7 +18,6 @@ import (
"github.com/openshift/origin/pkg/cmd/server/origin"
"github.com/openshift/origin/pkg/cmd/util"
"github.com/openshift/origin/pkg/cmd/util/variable"
usercache "github.com/openshift/origin/pkg/user/cache"
)

func RunOpenShiftAPIServer(masterConfig *configapi.MasterConfig) error {
Expand All @@ -42,25 +44,31 @@ func RunOpenShiftAPIServer(masterConfig *configapi.MasterConfig) error {

// informers are shared amongst all the various api components we build
// TODO the needs of the apiserver and the controllers are drifting. We should consider two different skins here
clientConfig, err := configapi.GetClientConfig(masterConfig.MasterClients.OpenShiftLoopbackKubeConfig, masterConfig.MasterClients.OpenShiftLoopbackClientConnectionOverrides)
kubeapiserverClientConfig, err := configapi.GetClientConfig(masterConfig.MasterClients.OpenShiftLoopbackKubeConfig, masterConfig.MasterClients.OpenShiftLoopbackClientConnectionOverrides)
if err != nil {
return err
}
informers, err := origin.NewInformers(clientConfig)

openshiftConfig, err := origin.BuildMasterConfig(*masterConfig, kubeapiserverClientConfig)
if err != nil {
return err
}

if err := informers.GetOpenshiftUserInformers().User().V1().Groups().Informer().AddIndexers(cache.Indexers{
usercache.ByUserIndexName: usercache.ByUserIndexKeys,
}); err != nil {
sarClient, err := authorizationclient.NewForConfig(kubeapiserverClientConfig)
if err != nil {
return err
}

openshiftConfig, err := origin.BuildMasterConfig(*masterConfig, informers)
delegatingAuthorizerConfig := authorizerfactory.DelegatingAuthorizerConfig{
SubjectAccessReviewClient: sarClient.SubjectAccessReviews(),
// TODO get this from config
AllowCacheTTL: 10 * time.Second,
DenyCacheTTL: 10 * time.Second,
}
delegatingAuthorizer, err := delegatingAuthorizerConfig.New()
if err != nil {
return err
}
openshiftConfig.KubeAPIServerConfig.GenericConfig.Authorization.Authorizer = delegatingAuthorizer

glog.Infof("Starting master on %s (%s)", masterConfig.ServingInfo.BindAddress, version.Get().String())
glog.Infof("Public master address is %s", masterConfig.MasterPublicURL)
Expand Down
18 changes: 16 additions & 2 deletions pkg/cmd/openshift-controller-manager/controller_manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,21 @@ import (
"time"

"github.com/golang/glog"
"k8s.io/client-go/restmapper"

"k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/util/wait"
cacheddiscovery "k8s.io/client-go/discovery/cached"
"k8s.io/client-go/informers"
kclientsetexternal "k8s.io/client-go/kubernetes"
v1core "k8s.io/client-go/kubernetes/typed/core/v1"
"k8s.io/client-go/rest"
"k8s.io/client-go/restmapper"
"k8s.io/client-go/tools/leaderelection"
"k8s.io/client-go/tools/leaderelection/resourcelock"
"k8s.io/client-go/tools/record"
"k8s.io/kubernetes/pkg/api/legacyscheme"
kclientsetinternal "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
kinternalinformers "k8s.io/kubernetes/pkg/client/informers/informers_generated/internalversion"
"k8s.io/kubernetes/pkg/controller"

origincontrollers "github.com/openshift/origin/pkg/cmd/openshift-controller-manager/controller"
Expand All @@ -31,11 +34,22 @@ import (

func RunOpenShiftControllerManager(config *configapi.OpenshiftControllerConfig, clientConfig *rest.Config) error {
util.InitLogrus()
configapi.ApplyClientConnectionOverrides(config.ClientConnectionOverrides, clientConfig)

kubeExternal, err := kclientsetexternal.NewForConfig(clientConfig)
if err != nil {
return err
}
openshiftControllerInformers, err := origin.NewInformers(clientConfig)
kubeInternalClient, err := kclientsetinternal.NewForConfig(clientConfig)
if err != nil {
return err
}

const defaultInformerResyncPeriod = 10 * time.Minute
internalInformers := kinternalinformers.NewSharedInformerFactory(kubeInternalClient, defaultInformerResyncPeriod)
informers := informers.NewSharedInformerFactory(kubeExternal, defaultInformerResyncPeriod)

openshiftControllerInformers, err := origin.NewInformers(internalInformers, informers, clientConfig)
if err != nil {
return err
}
Expand Down
3 changes: 1 addition & 2 deletions pkg/cmd/openshift-kube-apiserver/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,7 @@ func RunOpenShiftKubeAPIServerServer(masterConfig *configapi.MasterConfig) error
return kerrors.NewInvalid(configapi.Kind("MasterConfig"), "master-config.yaml", validationResults.Errors)
}

informers := origin.InformerAccess(nil) // use real kube-apiserver loopback client with secret token instead of that from masterConfig.MasterClients.OpenShiftLoopbackKubeConfig
openshiftConfig, err := origin.BuildMasterConfig(*masterConfig, informers)
openshiftConfig, err := origin.BuildMasterConfig(*masterConfig, nil)
if err != nil {
return err
}
Expand Down
6 changes: 3 additions & 3 deletions pkg/cmd/server/apis/config/helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -257,7 +257,7 @@ func GetKubeConfigOrInClusterConfig(kubeConfigFile string, overrides *ClientConn
if err != nil {
return nil, err
}
applyClientConnectionOverrides(overrides, clientConfig)
ApplyClientConnectionOverrides(overrides, clientConfig)
clientConfig.WrapTransport = DefaultClientTransport

return clientConfig, nil
Expand All @@ -276,14 +276,14 @@ func GetClientConfig(kubeConfigFile string, overrides *ClientConnectionOverrides
if err != nil {
return nil, err
}
applyClientConnectionOverrides(overrides, clientConfig)
ApplyClientConnectionOverrides(overrides, clientConfig)
clientConfig.WrapTransport = DefaultClientTransport

return clientConfig, nil
}

// applyClientConnectionOverrides updates a kubeConfig with the overrides from the config.
func applyClientConnectionOverrides(overrides *ClientConnectionOverrides, kubeConfig *restclient.Config) {
func ApplyClientConnectionOverrides(overrides *ClientConnectionOverrides, kubeConfig *restclient.Config) {
if overrides == nil {
return
}
Expand Down
55 changes: 55 additions & 0 deletions pkg/cmd/server/kubernetes/master/authorizer.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package master

import (
"k8s.io/apiserver/pkg/authentication/user"
"k8s.io/apiserver/pkg/authorization/authorizer"
"k8s.io/apiserver/pkg/authorization/authorizerfactory"
authorizerunion "k8s.io/apiserver/pkg/authorization/union"
kinformers "k8s.io/client-go/informers"
"k8s.io/kubernetes/pkg/auth/nodeidentifier"
kinternalinformers "k8s.io/kubernetes/pkg/client/informers/informers_generated/internalversion"
"k8s.io/kubernetes/plugin/pkg/auth/authorizer/node"
rbacauthorizer "k8s.io/kubernetes/plugin/pkg/auth/authorizer/rbac"
kbootstrappolicy "k8s.io/kubernetes/plugin/pkg/auth/authorizer/rbac/bootstrappolicy"

openshiftauthorizer "github.com/openshift/origin/pkg/authorization/authorizer"
"github.com/openshift/origin/pkg/authorization/authorizer/browsersafe"
"github.com/openshift/origin/pkg/authorization/authorizer/scope"
)

func NewAuthorizer(internalInformers kinternalinformers.SharedInformerFactory, informers kinformers.SharedInformerFactory, projectRequestDenyMessage string) authorizer.Authorizer {
messageMaker := openshiftauthorizer.NewForbiddenMessageResolver(projectRequestDenyMessage)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@deads2k can we merge #20379 first so this forbidden message maker can die before moving here?

That PR is just waiting on a LGTM from @smarterclayton

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@deads2k can we merge #20379 first so this forbidden message maker can die before moving here?

That PR is just waiting on a LGTM from @smarterclayton

Get my comment fixed and I'll do it.

Continue review here please.

rbacInformers := informers.Rbac().V1()

scopeLimitedAuthorizer := scope.NewAuthorizer(rbacInformers.ClusterRoles().Lister(), messageMaker)

kubeAuthorizer := rbacauthorizer.New(
&rbacauthorizer.RoleGetter{Lister: rbacInformers.Roles().Lister()},
&rbacauthorizer.RoleBindingLister{Lister: rbacInformers.RoleBindings().Lister()},
&rbacauthorizer.ClusterRoleGetter{Lister: rbacInformers.ClusterRoles().Lister()},
&rbacauthorizer.ClusterRoleBindingLister{Lister: rbacInformers.ClusterRoleBindings().Lister()},
)

graph := node.NewGraph()
node.AddGraphEventHandlers(
graph,
internalInformers.Core().InternalVersion().Nodes(),
internalInformers.Core().InternalVersion().Pods(),
internalInformers.Core().InternalVersion().PersistentVolumes(),
informers.Storage().V1beta1().VolumeAttachments(),
)
nodeAuthorizer := node.NewAuthorizer(graph, nodeidentifier.NewDefaultNodeIdentifier(), kbootstrappolicy.NodeRules())

openshiftAuthorizer := authorizerunion.New(
// Wrap with an authorizer that detects unsafe requests and modifies verbs/resources appropriately so policy can address them separately.
// Scopes are first because they will authoritatively deny and can logically be attached to anyone.
browsersafe.NewBrowserSafeAuthorizer(scopeLimitedAuthorizer, user.AllAuthenticated),
// authorizes system:masters to do anything, just like upstream
authorizerfactory.NewPrivilegedGroups(user.SystemPrivilegedGroup),
nodeAuthorizer,
// Wrap with an authorizer that detects unsafe requests and modifies verbs/resources appropriately so policy can address them separately
browsersafe.NewBrowserSafeAuthorizer(openshiftauthorizer.NewAuthorizer(kubeAuthorizer, messageMaker), user.AllAuthenticated),
)

return openshiftAuthorizer
}
48 changes: 37 additions & 11 deletions pkg/cmd/server/kubernetes/master/master_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ import (
"k8s.io/apiserver/pkg/audit"
auditpolicy "k8s.io/apiserver/pkg/audit/policy"
"k8s.io/apiserver/pkg/authentication/authenticator"
"k8s.io/apiserver/pkg/authorization/authorizer"
apiserverendpointsopenapi "k8s.io/apiserver/pkg/endpoints/openapi"
apirequest "k8s.io/apiserver/pkg/endpoints/request"
"k8s.io/apiserver/pkg/registry/generic"
Expand All @@ -48,6 +47,7 @@ import (
auditlog "k8s.io/apiserver/plugin/pkg/audit/log"
auditwebhook "k8s.io/apiserver/plugin/pkg/audit/webhook"
pluginwebhook "k8s.io/apiserver/plugin/pkg/audit/webhook"
kinformers "k8s.io/client-go/informers"
"k8s.io/client-go/rest"
"k8s.io/kube-aggregator/pkg/apis/apiregistration"
apiregistrationv1beta1 "k8s.io/kube-aggregator/pkg/apis/apiregistration/v1beta1"
Expand All @@ -65,6 +65,8 @@ import (
"k8s.io/kubernetes/pkg/apis/policy"
storageapi "k8s.io/kubernetes/pkg/apis/storage"
storageapiv1beta1 "k8s.io/kubernetes/pkg/apis/storage/v1beta1"
kclientsetinternal "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
kinternalinformers "k8s.io/kubernetes/pkg/client/informers/informers_generated/internalversion"
"k8s.io/kubernetes/pkg/kubeapiserver"
"k8s.io/kubernetes/pkg/master"
"k8s.io/kubernetes/pkg/registry/cachesize"
Expand All @@ -89,6 +91,7 @@ import (
// TODO fix this install, it is required for TestPreferredGroupVersions to pass
"github.com/openshift/api/security"
_ "github.com/openshift/origin/pkg/authorization/apis/authorization/install"
"k8s.io/client-go/kubernetes"
)

// request paths that match this regular expression will be treated as long running
Expand Down Expand Up @@ -378,13 +381,16 @@ func buildPublicAddress(masterConfig configapi.MasterConfig) (net.IP, error) {
return publicAddress, nil
}

type incompleteKubeMasterConfig struct {
type IncompleteKubeMasterConfig struct {
options *kapiserveroptions.ServerRunOptions
incompleteConfig *apiserver.Config
IncompleteConfig *apiserver.Config
masterConfig configapi.MasterConfig

InternalKubernetesInformers kinternalinformers.SharedInformerFactory
KubernetesInformers kinformers.SharedInformerFactory
}

func BuildKubernetesMasterConfig(masterConfig configapi.MasterConfig) (*incompleteKubeMasterConfig, error) {
func BuildKubernetesMasterConfig(masterConfig configapi.MasterConfig) (*IncompleteKubeMasterConfig, error) {
apiserverOptions, err := BuildKubeAPIserverOptions(masterConfig)
if err != nil {
return nil, err
Expand All @@ -395,19 +401,40 @@ func BuildKubernetesMasterConfig(masterConfig configapi.MasterConfig) (*incomple
return nil, err
}

return &incompleteKubeMasterConfig{apiserverOptions, genericConfig, masterConfig}, nil
configapi.ApplyClientConnectionOverrides(masterConfig.MasterClients.OpenShiftLoopbackClientConnectionOverrides, genericConfig.LoopbackClientConfig)
kubeInternalClient, err := kclientsetinternal.NewForConfig(genericConfig.LoopbackClientConfig)
if err != nil {
return nil, err
}
kubeClient, err := kubernetes.NewForConfig(genericConfig.LoopbackClientConfig)
if err != nil {
return nil, err
}

const defaultInformerResyncPeriod = 10 * time.Minute
internalInformers := kinternalinformers.NewSharedInformerFactory(kubeInternalClient, defaultInformerResyncPeriod)
informers := kinformers.NewSharedInformerFactory(kubeClient, defaultInformerResyncPeriod)

genericConfig.Authorization.Authorizer = NewAuthorizer(internalInformers, informers, masterConfig.ProjectConfig.ProjectRequestMessage)

return &IncompleteKubeMasterConfig{
options: apiserverOptions,
IncompleteConfig: genericConfig,
masterConfig: masterConfig,
InternalKubernetesInformers: internalInformers,
KubernetesInformers: informers,
}, nil
}

func (rc *incompleteKubeMasterConfig) LoopbackConfig() *rest.Config {
return rc.incompleteConfig.LoopbackClientConfig
func (rc *IncompleteKubeMasterConfig) LoopbackConfig() *rest.Config {
return rc.IncompleteConfig.LoopbackClientConfig
}

func (rc *incompleteKubeMasterConfig) Complete(
func (rc *IncompleteKubeMasterConfig) Complete(
admissionControl admission.Interface,
originAuthenticator authenticator.Request,
kubeAuthorizer authorizer.Authorizer,
) (*master.Config, error) {
genericConfig, apiserverOptions, masterConfig := rc.incompleteConfig, rc.options, rc.masterConfig
genericConfig, apiserverOptions, masterConfig := rc.IncompleteConfig, rc.options, rc.masterConfig

proxyClientCerts, err := buildProxyClientCerts(masterConfig)
if err != nil {
Expand Down Expand Up @@ -439,7 +466,6 @@ func (rc *incompleteKubeMasterConfig) Complete(
genericConfig.Version = &kubeVersion
genericConfig.PublicAddress = publicAddress
genericConfig.Authentication.Authenticator = originAuthenticator // this is used to fulfill the tokenreviews endpoint which is used by node authentication
genericConfig.Authorization.Authorizer = kubeAuthorizer // this is used to fulfill the kube SAR endpoints
genericConfig.DisabledPostStartHooks.Insert(rbacrest.PostStartHookName)
// This disables the ThirdPartyController which removes handlers from our go-restful containers. The remove functionality is broken and destroys the serve mux.
genericConfig.DisabledPostStartHooks.Insert("extensions/third-party-resources")
Expand Down
48 changes: 0 additions & 48 deletions pkg/cmd/server/origin/authorizer.go
Original file line number Diff line number Diff line change
@@ -1,59 +1,11 @@
package origin

import (
"k8s.io/apiserver/pkg/authentication/user"
"k8s.io/apiserver/pkg/authorization/authorizer"
"k8s.io/apiserver/pkg/authorization/authorizerfactory"
authorizerunion "k8s.io/apiserver/pkg/authorization/union"
rbacinformers "k8s.io/client-go/informers/rbac/v1"
"k8s.io/kubernetes/pkg/auth/nodeidentifier"
rbacregistryvalidation "k8s.io/kubernetes/pkg/registry/rbac/validation"
"k8s.io/kubernetes/plugin/pkg/auth/authorizer/node"
rbacauthorizer "k8s.io/kubernetes/plugin/pkg/auth/authorizer/rbac"
kbootstrappolicy "k8s.io/kubernetes/plugin/pkg/auth/authorizer/rbac/bootstrappolicy"

openshiftauthorizer "github.com/openshift/origin/pkg/authorization/authorizer"
"github.com/openshift/origin/pkg/authorization/authorizer/browsersafe"
"github.com/openshift/origin/pkg/authorization/authorizer/scope"
)

func NewAuthorizer(informers InformerAccess, projectRequestDenyMessage string) authorizer.Authorizer {
messageMaker := openshiftauthorizer.NewForbiddenMessageResolver(projectRequestDenyMessage)
rbacInformers := informers.GetKubernetesInformers().Rbac().V1()

scopeLimitedAuthorizer := scope.NewAuthorizer(rbacInformers.ClusterRoles().Lister(), messageMaker)

kubeAuthorizer := rbacauthorizer.New(
&rbacauthorizer.RoleGetter{Lister: rbacInformers.Roles().Lister()},
&rbacauthorizer.RoleBindingLister{Lister: rbacInformers.RoleBindings().Lister()},
&rbacauthorizer.ClusterRoleGetter{Lister: rbacInformers.ClusterRoles().Lister()},
&rbacauthorizer.ClusterRoleBindingLister{Lister: rbacInformers.ClusterRoleBindings().Lister()},
)

graph := node.NewGraph()
node.AddGraphEventHandlers(
graph,
informers.GetInternalKubernetesInformers().Core().InternalVersion().Nodes(),
informers.GetInternalKubernetesInformers().Core().InternalVersion().Pods(),
informers.GetInternalKubernetesInformers().Core().InternalVersion().PersistentVolumes(),
informers.GetKubernetesInformers().Storage().V1beta1().VolumeAttachments(),
)
nodeAuthorizer := node.NewAuthorizer(graph, nodeidentifier.NewDefaultNodeIdentifier(), kbootstrappolicy.NodeRules())

openshiftAuthorizer := authorizerunion.New(
// Wrap with an authorizer that detects unsafe requests and modifies verbs/resources appropriately so policy can address them separately.
// Scopes are first because they will authoritatively deny and can logically be attached to anyone.
browsersafe.NewBrowserSafeAuthorizer(scopeLimitedAuthorizer, user.AllAuthenticated),
// authorizes system:masters to do anything, just like upstream
authorizerfactory.NewPrivilegedGroups(user.SystemPrivilegedGroup),
nodeAuthorizer,
// Wrap with an authorizer that detects unsafe requests and modifies verbs/resources appropriately so policy can address them separately
browsersafe.NewBrowserSafeAuthorizer(openshiftauthorizer.NewAuthorizer(kubeAuthorizer, messageMaker), user.AllAuthenticated),
)

return openshiftAuthorizer
}

func NewRuleResolver(informers rbacinformers.Interface) rbacregistryvalidation.AuthorizationRuleResolver {
return rbacregistryvalidation.NewDefaultRuleResolver(
&rbacauthorizer.RoleGetter{Lister: informers.Roles().Lister()},
Expand Down
Loading