Commit f9b382e9 authored by Darren Shepherd's avatar Darren Shepherd

Delete webhook authorizer

parent c88d08b2
...@@ -83,7 +83,6 @@ type KubeControllerManagerOptions struct { ...@@ -83,7 +83,6 @@ type KubeControllerManagerOptions struct {
// TODO: remove insecure serving mode // TODO: remove insecure serving mode
InsecureServing *apiserveroptions.DeprecatedInsecureServingOptionsWithLoopback InsecureServing *apiserveroptions.DeprecatedInsecureServingOptionsWithLoopback
Authentication *apiserveroptions.DelegatingAuthenticationOptions Authentication *apiserveroptions.DelegatingAuthenticationOptions
Authorization *apiserveroptions.DelegatingAuthorizationOptions
Master string Master string
Kubeconfig string Kubeconfig string
...@@ -183,12 +182,9 @@ func NewKubeControllerManagerOptions() (*KubeControllerManagerOptions, error) { ...@@ -183,12 +182,9 @@ func NewKubeControllerManagerOptions() (*KubeControllerManagerOptions, error) {
BindNetwork: "tcp", BindNetwork: "tcp",
}).WithLoopback(), }).WithLoopback(),
Authentication: apiserveroptions.NewDelegatingAuthenticationOptions(), Authentication: apiserveroptions.NewDelegatingAuthenticationOptions(),
Authorization: apiserveroptions.NewDelegatingAuthorizationOptions(),
} }
s.Authentication.RemoteKubeConfigFileOptional = true s.Authentication.RemoteKubeConfigFileOptional = true
s.Authorization.RemoteKubeConfigFileOptional = true
s.Authorization.AlwaysAllowPaths = []string{"/healthz"}
s.SecureServing.ServerCert.CertDirectory = "/var/run/kubernetes" s.SecureServing.ServerCert.CertDirectory = "/var/run/kubernetes"
s.SecureServing.ServerCert.PairName = "kube-controller-manager" s.SecureServing.ServerCert.PairName = "kube-controller-manager"
...@@ -235,7 +231,6 @@ func (s *KubeControllerManagerOptions) Flags(allControllers []string, disabledBy ...@@ -235,7 +231,6 @@ func (s *KubeControllerManagerOptions) Flags(allControllers []string, disabledBy
s.SecureServing.AddFlags(fss.FlagSet("secure serving")) s.SecureServing.AddFlags(fss.FlagSet("secure serving"))
s.InsecureServing.AddUnqualifiedFlags(fss.FlagSet("insecure serving")) s.InsecureServing.AddUnqualifiedFlags(fss.FlagSet("insecure serving"))
s.Authentication.AddFlags(fss.FlagSet("authentication")) s.Authentication.AddFlags(fss.FlagSet("authentication"))
s.Authorization.AddFlags(fss.FlagSet("authorization"))
s.AttachDetachController.AddFlags(fss.FlagSet("attachdetach controller")) s.AttachDetachController.AddFlags(fss.FlagSet("attachdetach controller"))
s.CSRSigningController.AddFlags(fss.FlagSet("csrsigning controller")) s.CSRSigningController.AddFlags(fss.FlagSet("csrsigning controller"))
...@@ -346,9 +341,6 @@ func (s *KubeControllerManagerOptions) ApplyTo(c *kubecontrollerconfig.Config) e ...@@ -346,9 +341,6 @@ func (s *KubeControllerManagerOptions) ApplyTo(c *kubecontrollerconfig.Config) e
if err := s.Authentication.ApplyTo(&c.Authentication, c.SecureServing); err != nil { if err := s.Authentication.ApplyTo(&c.Authentication, c.SecureServing); err != nil {
return err return err
} }
if err := s.Authorization.ApplyTo(&c.Authorization); err != nil {
return err
}
} }
// sync back to component config // sync back to component config
...@@ -388,7 +380,6 @@ func (s *KubeControllerManagerOptions) Validate(allControllers []string, disable ...@@ -388,7 +380,6 @@ func (s *KubeControllerManagerOptions) Validate(allControllers []string, disable
errs = append(errs, s.SecureServing.Validate()...) errs = append(errs, s.SecureServing.Validate()...)
errs = append(errs, s.InsecureServing.Validate()...) errs = append(errs, s.InsecureServing.Validate()...)
errs = append(errs, s.Authentication.Validate()...) errs = append(errs, s.Authentication.Validate()...)
errs = append(errs, s.Authorization.Validate()...)
// TODO: validate component config, master and kubeconfig // TODO: validate component config, master and kubeconfig
......
...@@ -59,7 +59,6 @@ type Options struct { ...@@ -59,7 +59,6 @@ type Options struct {
SecureServing *apiserveroptions.SecureServingOptions SecureServing *apiserveroptions.SecureServingOptions
CombinedInsecureServing *CombinedInsecureServingOptions CombinedInsecureServing *CombinedInsecureServingOptions
Authentication *apiserveroptions.DelegatingAuthenticationOptions Authentication *apiserveroptions.DelegatingAuthenticationOptions
Authorization *apiserveroptions.DelegatingAuthorizationOptions
Deprecated *DeprecatedOptions Deprecated *DeprecatedOptions
// ConfigFile is the location of the scheduler server's configuration file. // ConfigFile is the location of the scheduler server's configuration file.
...@@ -97,7 +96,6 @@ func NewOptions() (*Options, error) { ...@@ -97,7 +96,6 @@ func NewOptions() (*Options, error) {
BindAddress: hhost, BindAddress: hhost,
}, },
Authentication: nil, // TODO: enable with apiserveroptions.NewDelegatingAuthenticationOptions() Authentication: nil, // TODO: enable with apiserveroptions.NewDelegatingAuthenticationOptions()
Authorization: nil, // TODO: enable with apiserveroptions.NewDelegatingAuthorizationOptions()
Deprecated: &DeprecatedOptions{ Deprecated: &DeprecatedOptions{
UseLegacyPolicyConfig: false, UseLegacyPolicyConfig: false,
PolicyConfigMapNamespace: metav1.NamespaceSystem, PolicyConfigMapNamespace: metav1.NamespaceSystem,
...@@ -138,7 +136,6 @@ func (o *Options) AddFlags(fs *pflag.FlagSet) { ...@@ -138,7 +136,6 @@ func (o *Options) AddFlags(fs *pflag.FlagSet) {
o.SecureServing.AddFlags(fs) o.SecureServing.AddFlags(fs)
o.CombinedInsecureServing.AddFlags(fs) o.CombinedInsecureServing.AddFlags(fs)
o.Authentication.AddFlags(fs) o.Authentication.AddFlags(fs)
o.Authorization.AddFlags(fs)
o.Deprecated.AddFlags(fs, &o.ComponentConfig) o.Deprecated.AddFlags(fs, &o.ComponentConfig)
leaderelectionconfig.BindFlags(&o.ComponentConfig.LeaderElection.LeaderElectionConfiguration, fs) leaderelectionconfig.BindFlags(&o.ComponentConfig.LeaderElection.LeaderElectionConfiguration, fs)
...@@ -179,7 +176,7 @@ func (o *Options) ApplyTo(c *schedulerappconfig.Config) error { ...@@ -179,7 +176,7 @@ func (o *Options) ApplyTo(c *schedulerappconfig.Config) error {
if err := o.Authentication.ApplyTo(&c.Authentication, c.SecureServing); err != nil { if err := o.Authentication.ApplyTo(&c.Authentication, c.SecureServing); err != nil {
return err return err
} }
return o.Authorization.ApplyTo(&c.Authorization) return nil
} }
// Validate validates all the required options. // Validate validates all the required options.
...@@ -192,7 +189,6 @@ func (o *Options) Validate() []error { ...@@ -192,7 +189,6 @@ func (o *Options) Validate() []error {
errs = append(errs, o.SecureServing.Validate()...) errs = append(errs, o.SecureServing.Validate()...)
errs = append(errs, o.CombinedInsecureServing.Validate()...) errs = append(errs, o.CombinedInsecureServing.Validate()...)
errs = append(errs, o.Authentication.Validate()...) errs = append(errs, o.Authentication.Validate()...)
errs = append(errs, o.Authorization.Validate()...)
errs = append(errs, o.Deprecated.Validate()...) errs = append(errs, o.Deprecated.Validate()...)
return errs return errs
......
...@@ -85,17 +85,6 @@ func BuildAuthz(client authorizationclient.SubjectAccessReviewInterface, authz k ...@@ -85,17 +85,6 @@ func BuildAuthz(client authorizationclient.SubjectAccessReviewInterface, authz k
case kubeletconfig.KubeletAuthorizationModeAlwaysAllow: case kubeletconfig.KubeletAuthorizationModeAlwaysAllow:
return authorizerfactory.NewAlwaysAllowAuthorizer(), nil return authorizerfactory.NewAlwaysAllowAuthorizer(), nil
case kubeletconfig.KubeletAuthorizationModeWebhook:
if client == nil {
return nil, errors.New("no client provided, cannot use webhook authorization")
}
authorizerConfig := authorizerfactory.DelegatingAuthorizerConfig{
SubjectAccessReviewClient: client,
AllowCacheTTL: authz.Webhook.CacheAuthorizedTTL.Duration,
DenyCacheTTL: authz.Webhook.CacheUnauthorizedTTL.Duration,
}
return authorizerConfig.New()
case "": case "":
return nil, fmt.Errorf("No authorization mode specified") return nil, fmt.Errorf("No authorization mode specified")
......
...@@ -18,12 +18,10 @@ package authorizer ...@@ -18,12 +18,10 @@ package authorizer
import ( import (
"fmt" "fmt"
"time"
"k8s.io/apiserver/pkg/authorization/authorizer" "k8s.io/apiserver/pkg/authorization/authorizer"
"k8s.io/apiserver/pkg/authorization/authorizerfactory" "k8s.io/apiserver/pkg/authorization/authorizerfactory"
"k8s.io/apiserver/pkg/authorization/union" "k8s.io/apiserver/pkg/authorization/union"
"k8s.io/apiserver/plugin/pkg/authorizer/webhook"
versionedinformers "k8s.io/client-go/informers" versionedinformers "k8s.io/client-go/informers"
"k8s.io/kubernetes/pkg/auth/nodeidentifier" "k8s.io/kubernetes/pkg/auth/nodeidentifier"
"k8s.io/kubernetes/pkg/kubeapiserver/authorizer/modes" "k8s.io/kubernetes/pkg/kubeapiserver/authorizer/modes"
...@@ -39,13 +37,6 @@ type AuthorizationConfig struct { ...@@ -39,13 +37,6 @@ type AuthorizationConfig struct {
// Options for ModeWebhook // Options for ModeWebhook
// Kubeconfig file for Webhook authorization plugin.
WebhookConfigFile string
// TTL for caching of authorized responses from the webhook server.
WebhookCacheAuthorizedTTL time.Duration
// TTL for caching of unauthorized responses from the webhook server.
WebhookCacheUnauthorizedTTL time.Duration
VersionedInformerFactory versionedinformers.SharedInformerFactory VersionedInformerFactory versionedinformers.SharedInformerFactory
} }
...@@ -84,15 +75,6 @@ func (config AuthorizationConfig) New() (authorizer.Authorizer, authorizer.RuleR ...@@ -84,15 +75,6 @@ func (config AuthorizationConfig) New() (authorizer.Authorizer, authorizer.RuleR
alwaysDenyAuthorizer := authorizerfactory.NewAlwaysDenyAuthorizer() alwaysDenyAuthorizer := authorizerfactory.NewAlwaysDenyAuthorizer()
authorizers = append(authorizers, alwaysDenyAuthorizer) authorizers = append(authorizers, alwaysDenyAuthorizer)
ruleResolvers = append(ruleResolvers, alwaysDenyAuthorizer) ruleResolvers = append(ruleResolvers, alwaysDenyAuthorizer)
case modes.ModeWebhook:
webhookAuthorizer, err := webhook.New(config.WebhookConfigFile,
config.WebhookCacheAuthorizedTTL,
config.WebhookCacheUnauthorizedTTL)
if err != nil {
return nil, nil, err
}
authorizers = append(authorizers, webhookAuthorizer)
ruleResolvers = append(ruleResolvers, webhookAuthorizer)
case modes.ModeRBAC: case modes.ModeRBAC:
rbacAuthorizer := rbac.New( rbacAuthorizer := rbac.New(
&rbac.RoleGetter{Lister: config.VersionedInformerFactory.Rbac().V1().Roles().Lister()}, &rbac.RoleGetter{Lister: config.VersionedInformerFactory.Rbac().V1().Roles().Lister()},
......
...@@ -21,12 +21,11 @@ import "k8s.io/apimachinery/pkg/util/sets" ...@@ -21,12 +21,11 @@ import "k8s.io/apimachinery/pkg/util/sets"
const ( const (
ModeAlwaysAllow string = "AlwaysAllow" ModeAlwaysAllow string = "AlwaysAllow"
ModeAlwaysDeny string = "AlwaysDeny" ModeAlwaysDeny string = "AlwaysDeny"
ModeWebhook string = "Webhook"
ModeRBAC string = "RBAC" ModeRBAC string = "RBAC"
ModeNode string = "Node" ModeNode string = "Node"
) )
var AuthorizationModeChoices = []string{ModeAlwaysAllow, ModeAlwaysDeny, ModeWebhook, ModeRBAC, ModeNode} var AuthorizationModeChoices = []string{ModeAlwaysAllow, ModeAlwaysDeny, ModeRBAC, ModeNode}
// IsValidAuthorizationMode returns true if the given authorization mode is a valid one for the apiserver // IsValidAuthorizationMode returns true if the given authorization mode is a valid one for the apiserver
func IsValidAuthorizationMode(authzMode string) bool { func IsValidAuthorizationMode(authzMode string) bool {
......
...@@ -18,10 +18,8 @@ package options ...@@ -18,10 +18,8 @@ package options
import ( import (
"fmt" "fmt"
"strings"
"time"
"github.com/spf13/pflag" "github.com/spf13/pflag"
"strings"
"k8s.io/apimachinery/pkg/util/sets" "k8s.io/apimachinery/pkg/util/sets"
versionedinformers "k8s.io/client-go/informers" versionedinformers "k8s.io/client-go/informers"
...@@ -31,16 +29,11 @@ import ( ...@@ -31,16 +29,11 @@ import (
type BuiltInAuthorizationOptions struct { type BuiltInAuthorizationOptions struct {
Modes []string Modes []string
WebhookConfigFile string
WebhookCacheAuthorizedTTL time.Duration
WebhookCacheUnauthorizedTTL time.Duration
} }
func NewBuiltInAuthorizationOptions() *BuiltInAuthorizationOptions { func NewBuiltInAuthorizationOptions() *BuiltInAuthorizationOptions {
return &BuiltInAuthorizationOptions{ return &BuiltInAuthorizationOptions{
Modes: []string{authzmodes.ModeAlwaysAllow}, Modes: []string{authzmodes.ModeAlwaysAllow},
WebhookCacheAuthorizedTTL: 5 * time.Minute,
WebhookCacheUnauthorizedTTL: 30 * time.Second,
} }
} }
...@@ -60,15 +53,6 @@ func (s *BuiltInAuthorizationOptions) Validate() []error { ...@@ -60,15 +53,6 @@ func (s *BuiltInAuthorizationOptions) Validate() []error {
if !allowedModes.Has(mode) { if !allowedModes.Has(mode) {
allErrors = append(allErrors, fmt.Errorf("authorization-mode %q is not a valid mode", mode)) allErrors = append(allErrors, fmt.Errorf("authorization-mode %q is not a valid mode", mode))
} }
if mode == authzmodes.ModeWebhook {
if s.WebhookConfigFile == "" {
allErrors = append(allErrors, fmt.Errorf("authorization-mode Webhook's authorization config file not passed"))
}
}
}
if s.WebhookConfigFile != "" && !modes.Has(authzmodes.ModeWebhook) {
allErrors = append(allErrors, fmt.Errorf("cannot specify --authorization-webhook-config-file without mode Webhook"))
} }
if len(s.Modes) != len(modes.List()) { if len(s.Modes) != len(modes.List()) {
...@@ -82,26 +66,11 @@ func (s *BuiltInAuthorizationOptions) AddFlags(fs *pflag.FlagSet) { ...@@ -82,26 +66,11 @@ func (s *BuiltInAuthorizationOptions) AddFlags(fs *pflag.FlagSet) {
fs.StringSliceVar(&s.Modes, "authorization-mode", s.Modes, ""+ fs.StringSliceVar(&s.Modes, "authorization-mode", s.Modes, ""+
"Ordered list of plug-ins to do authorization on secure port. Comma-delimited list of: "+ "Ordered list of plug-ins to do authorization on secure port. Comma-delimited list of: "+
strings.Join(authzmodes.AuthorizationModeChoices, ",")+".") strings.Join(authzmodes.AuthorizationModeChoices, ",")+".")
fs.StringVar(&s.WebhookConfigFile, "authorization-webhook-config-file", s.WebhookConfigFile, ""+
"File with webhook configuration in kubeconfig format, used with --authorization-mode=Webhook. "+
"The API server will query the remote service to determine access on the API server's secure port.")
fs.DurationVar(&s.WebhookCacheAuthorizedTTL, "authorization-webhook-cache-authorized-ttl",
s.WebhookCacheAuthorizedTTL,
"The duration to cache 'authorized' responses from the webhook authorizer.")
fs.DurationVar(&s.WebhookCacheUnauthorizedTTL,
"authorization-webhook-cache-unauthorized-ttl", s.WebhookCacheUnauthorizedTTL,
"The duration to cache 'unauthorized' responses from the webhook authorizer.")
} }
func (s *BuiltInAuthorizationOptions) ToAuthorizationConfig(versionedInformerFactory versionedinformers.SharedInformerFactory) authorizer.AuthorizationConfig { func (s *BuiltInAuthorizationOptions) ToAuthorizationConfig(versionedInformerFactory versionedinformers.SharedInformerFactory) authorizer.AuthorizationConfig {
return authorizer.AuthorizationConfig{ return authorizer.AuthorizationConfig{
AuthorizationModes: s.Modes, AuthorizationModes: s.Modes,
WebhookConfigFile: s.WebhookConfigFile,
WebhookCacheAuthorizedTTL: s.WebhookCacheAuthorizedTTL,
WebhookCacheUnauthorizedTTL: s.WebhookCacheUnauthorizedTTL,
VersionedInformerFactory: versionedInformerFactory, VersionedInformerFactory: versionedInformerFactory,
} }
} }
...@@ -312,8 +312,6 @@ type KubeletAuthorizationMode string ...@@ -312,8 +312,6 @@ type KubeletAuthorizationMode string
const ( const (
// KubeletAuthorizationModeAlwaysAllow authorizes all authenticated requests // KubeletAuthorizationModeAlwaysAllow authorizes all authenticated requests
KubeletAuthorizationModeAlwaysAllow KubeletAuthorizationMode = "AlwaysAllow" KubeletAuthorizationModeAlwaysAllow KubeletAuthorizationMode = "AlwaysAllow"
// KubeletAuthorizationModeWebhook uses the SubjectAccessReview API to determine authorization
KubeletAuthorizationModeWebhook KubeletAuthorizationMode = "Webhook"
) )
type KubeletAuthorization struct { type KubeletAuthorization struct {
......
/*
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 authorizerfactory
import (
"k8s.io/client-go/kubernetes/typed/authorization/v1"
"time"
"k8s.io/apiserver/pkg/authorization/authorizer"
"k8s.io/apiserver/plugin/pkg/authorizer/webhook"
)
// DelegatingAuthorizerConfig is the minimal configuration needed to create an authenticator
// built to delegate authorization to a kube API server
type DelegatingAuthorizerConfig struct {
SubjectAccessReviewClient v1.SubjectAccessReviewInterface
// AllowCacheTTL is the length of time that a successful authorization response will be cached
AllowCacheTTL time.Duration
// DenyCacheTTL is the length of time that an unsuccessful authorization response will be cached.
// You generally want more responsive, "deny, try again" flows.
DenyCacheTTL time.Duration
}
func (c DelegatingAuthorizerConfig) New() (authorizer.Authorizer, error) {
return webhook.NewFromInterface(
c.SubjectAccessReviewClient,
c.AllowCacheTTL,
c.DenyCacheTTL,
)
}
/*
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 options
import (
"fmt"
"k8s.io/client-go/kubernetes"
"time"
"github.com/golang/glog"
"github.com/spf13/pflag"
"k8s.io/apiserver/pkg/authorization/authorizer"
"k8s.io/apiserver/pkg/authorization/authorizerfactory"
"k8s.io/apiserver/pkg/authorization/path"
"k8s.io/apiserver/pkg/authorization/union"
"k8s.io/apiserver/pkg/server"
"k8s.io/client-go/rest"
"k8s.io/client-go/tools/clientcmd"
)
// DelegatingAuthorizationOptions provides an easy way for composing API servers to delegate their authorization to
// the root kube API server.
// WARNING: never assume that every authenticated incoming request already does authorization.
// The aggregator in the kube API server does this today, but this behaviour is not
// guaranteed in the future.
type DelegatingAuthorizationOptions struct {
// RemoteKubeConfigFile is the file to use to connect to a "normal" kube API server which hosts the
// SubjectAccessReview.authorization.k8s.io endpoint for checking tokens.
RemoteKubeConfigFile string
// RemoteKubeConfigFileOptional is specifying whether not specifying the kubeconfig or
// a missing in-cluster config will be fatal.
RemoteKubeConfigFileOptional bool
// AllowCacheTTL is the length of time that a successful authorization response will be cached
AllowCacheTTL time.Duration
// DenyCacheTTL is the length of time that an unsuccessful authorization response will be cached.
// You generally want more responsive, "deny, try again" flows.
DenyCacheTTL time.Duration
// AlwaysAllowPaths are HTTP paths which are excluded from authorization. They can be plain
// paths or end in * in which case prefix-match is applied. A leading / is optional.
AlwaysAllowPaths []string
}
func NewDelegatingAuthorizationOptions() *DelegatingAuthorizationOptions {
return &DelegatingAuthorizationOptions{
// very low for responsiveness, but high enough to handle storms
AllowCacheTTL: 10 * time.Second,
DenyCacheTTL: 10 * time.Second,
}
}
func (s *DelegatingAuthorizationOptions) Validate() []error {
allErrors := []error{}
return allErrors
}
func (s *DelegatingAuthorizationOptions) AddFlags(fs *pflag.FlagSet) {
if s == nil {
return
}
var optionalKubeConfigSentence string
if s.RemoteKubeConfigFileOptional {
optionalKubeConfigSentence = " This is optional. If empty, all requests not skipped by authorization are forbidden."
}
fs.StringVar(&s.RemoteKubeConfigFile, "authorization-kubeconfig", s.RemoteKubeConfigFile,
"kubeconfig file pointing at the 'core' kubernetes server with enough rights to create "+
"subjectaccessreviews.authorization.k8s.io."+optionalKubeConfigSentence)
fs.DurationVar(&s.AllowCacheTTL, "authorization-webhook-cache-authorized-ttl",
s.AllowCacheTTL,
"The duration to cache 'authorized' responses from the webhook authorizer.")
fs.DurationVar(&s.DenyCacheTTL,
"authorization-webhook-cache-unauthorized-ttl", s.DenyCacheTTL,
"The duration to cache 'unauthorized' responses from the webhook authorizer.")
fs.StringSliceVar(&s.AlwaysAllowPaths, "authorization-always-allow-paths", s.AlwaysAllowPaths,
"A list of HTTP paths to skip during authorization, i.e. these are authorized without "+
"contacting the 'core' kubernetes server.")
}
func (s *DelegatingAuthorizationOptions) ApplyTo(c *server.AuthorizationInfo) error {
if s == nil {
c.Authorizer = authorizerfactory.NewAlwaysAllowAuthorizer()
return nil
}
client, err := s.getClient()
if err != nil {
return err
}
c.Authorizer, err = s.toAuthorizer(client)
return err
}
func (s *DelegatingAuthorizationOptions) toAuthorizer(client kubernetes.Interface) (authorizer.Authorizer, error) {
var authorizers []authorizer.Authorizer
if len(s.AlwaysAllowPaths) > 0 {
a, err := path.NewAuthorizer(s.AlwaysAllowPaths)
if err != nil {
return nil, err
}
authorizers = append(authorizers, a)
}
if client == nil {
glog.Warningf("No authorization-kubeconfig provided, so SubjectAccessReview of authorization tokens won't work.")
} else {
cfg := authorizerfactory.DelegatingAuthorizerConfig{
SubjectAccessReviewClient: client.AuthorizationV1().SubjectAccessReviews(),
AllowCacheTTL: s.AllowCacheTTL,
DenyCacheTTL: s.DenyCacheTTL,
}
delegatedAuthorizer, err := cfg.New()
if err != nil {
return nil, err
}
authorizers = append(authorizers, delegatedAuthorizer)
}
return union.New(authorizers...), nil
}
func (s *DelegatingAuthorizationOptions) getClient() (kubernetes.Interface, error) {
var clientConfig *rest.Config
var err error
if len(s.RemoteKubeConfigFile) > 0 {
loadingRules := &clientcmd.ClientConfigLoadingRules{ExplicitPath: s.RemoteKubeConfigFile}
loader := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(loadingRules, &clientcmd.ConfigOverrides{})
clientConfig, err = loader.ClientConfig()
} else {
// without the remote kubeconfig file, try to use the in-cluster config. Most addon API servers will
// use this path. If it is optional, ignore errors.
clientConfig, err = rest.InClusterConfig()
if err != nil && s.RemoteKubeConfigFileOptional {
if err != rest.ErrNotInCluster {
glog.Warningf("failed to read in-cluster kubeconfig for delegated authorization: %v", err)
}
return nil, nil
}
}
if err != nil {
return nil, fmt.Errorf("failed to get delegated authorization kubeconfig: %v", err)
}
// set high qps/burst limits since this will effectively limit API server responsiveness
clientConfig.QPS = 200
clientConfig.Burst = 400
return kubernetes.NewForConfig(clientConfig)
}
...@@ -32,7 +32,6 @@ type RecommendedOptions struct { ...@@ -32,7 +32,6 @@ type RecommendedOptions struct {
Etcd *EtcdOptions Etcd *EtcdOptions
SecureServing *SecureServingOptionsWithLoopback SecureServing *SecureServingOptionsWithLoopback
Authentication *DelegatingAuthenticationOptions Authentication *DelegatingAuthenticationOptions
Authorization *DelegatingAuthorizationOptions
Audit *AuditOptions Audit *AuditOptions
Features *FeatureOptions Features *FeatureOptions
CoreAPI *CoreAPIOptions CoreAPI *CoreAPIOptions
...@@ -56,7 +55,6 @@ func NewRecommendedOptions(prefix string, codec runtime.Codec) *RecommendedOptio ...@@ -56,7 +55,6 @@ func NewRecommendedOptions(prefix string, codec runtime.Codec) *RecommendedOptio
Etcd: NewEtcdOptions(storagebackend.NewDefaultConfig(prefix, codec)), Etcd: NewEtcdOptions(storagebackend.NewDefaultConfig(prefix, codec)),
SecureServing: sso.WithLoopback(), SecureServing: sso.WithLoopback(),
Authentication: NewDelegatingAuthenticationOptions(), Authentication: NewDelegatingAuthenticationOptions(),
Authorization: NewDelegatingAuthorizationOptions(),
Audit: NewAuditOptions(), Audit: NewAuditOptions(),
Features: NewFeatureOptions(), Features: NewFeatureOptions(),
CoreAPI: NewCoreAPIOptions(), CoreAPI: NewCoreAPIOptions(),
...@@ -69,7 +67,6 @@ func (o *RecommendedOptions) AddFlags(fs *pflag.FlagSet) { ...@@ -69,7 +67,6 @@ func (o *RecommendedOptions) AddFlags(fs *pflag.FlagSet) {
o.Etcd.AddFlags(fs) o.Etcd.AddFlags(fs)
o.SecureServing.AddFlags(fs) o.SecureServing.AddFlags(fs)
o.Authentication.AddFlags(fs) o.Authentication.AddFlags(fs)
o.Authorization.AddFlags(fs)
o.Audit.AddFlags(fs) o.Audit.AddFlags(fs)
o.Features.AddFlags(fs) o.Features.AddFlags(fs)
o.CoreAPI.AddFlags(fs) o.CoreAPI.AddFlags(fs)
...@@ -89,9 +86,6 @@ func (o *RecommendedOptions) ApplyTo(config *server.RecommendedConfig, scheme *r ...@@ -89,9 +86,6 @@ func (o *RecommendedOptions) ApplyTo(config *server.RecommendedConfig, scheme *r
if err := o.Authentication.ApplyTo(&config.Config.Authentication, config.SecureServing); err != nil { if err := o.Authentication.ApplyTo(&config.Config.Authentication, config.SecureServing); err != nil {
return err return err
} }
if err := o.Authorization.ApplyTo(&config.Config.Authorization); err != nil {
return err
}
if err := o.Audit.ApplyTo(&config.Config); err != nil { if err := o.Audit.ApplyTo(&config.Config); err != nil {
return err return err
} }
...@@ -115,7 +109,6 @@ func (o *RecommendedOptions) Validate() []error { ...@@ -115,7 +109,6 @@ func (o *RecommendedOptions) Validate() []error {
errors = append(errors, o.Etcd.Validate()...) errors = append(errors, o.Etcd.Validate()...)
errors = append(errors, o.SecureServing.Validate()...) errors = append(errors, o.SecureServing.Validate()...)
errors = append(errors, o.Authentication.Validate()...) errors = append(errors, o.Authentication.Validate()...)
errors = append(errors, o.Authorization.Validate()...)
errors = append(errors, o.Audit.Validate()...) errors = append(errors, o.Audit.Validate()...)
errors = append(errors, o.Features.Validate()...) errors = append(errors, o.Features.Validate()...)
errors = append(errors, o.CoreAPI.Validate()...) errors = append(errors, o.CoreAPI.Validate()...)
......
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
"go_test",
)
go_test(
name = "go_default_test",
srcs = [
"certs_test.go",
"webhook_test.go",
],
embed = [":go_default_library"],
deps = [
"//staging/src/k8s.io/api/authorization/v1beta1:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/util/diff:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/authentication/user:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/authorization/authorizer:go_default_library",
"//staging/src/k8s.io/client-go/tools/clientcmd/api/v1:go_default_library",
],
)
go_library(
name = "go_default_library",
srcs = ["webhook.go"],
importmap = "k8s.io/kubernetes/vendor/k8s.io/apiserver/plugin/pkg/authorizer/webhook",
importpath = "k8s.io/apiserver/plugin/pkg/authorizer/webhook",
deps = [
"//staging/src/k8s.io/api/authorization/v1beta1:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/util/cache:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/authentication/user:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/authorization/authorizer:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/util/webhook:go_default_library",
"//staging/src/k8s.io/client-go/kubernetes/scheme:go_default_library",
"//staging/src/k8s.io/client-go/kubernetes/typed/authorization/v1beta1:go_default_library",
"//vendor/github.com/golang/glog:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
)
#!/usr/bin/env bash
# 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.
set -e
# gencerts.sh generates the certificates for the webhook authz plugin tests.
#
# It is not expected to be run often (there is no go generate rule), and mainly
# exists for documentation purposes.
cat > server.conf << EOF
[req]
req_extensions = v3_req
distinguished_name = req_distinguished_name
[req_distinguished_name]
[ v3_req ]
basicConstraints = CA:FALSE
keyUsage = nonRepudiation, digitalSignature, keyEncipherment
extendedKeyUsage = serverAuth
subjectAltName = @alt_names
[alt_names]
IP.1 = 127.0.0.1
EOF
cat > client.conf << EOF
[req]
req_extensions = v3_req
distinguished_name = req_distinguished_name
[req_distinguished_name]
[ v3_req ]
basicConstraints = CA:FALSE
keyUsage = nonRepudiation, digitalSignature, keyEncipherment
extendedKeyUsage = clientAuth
EOF
# Create a certificate authority
openssl genrsa -out caKey.pem 2048
openssl req -x509 -new -nodes -key caKey.pem -days 100000 -out caCert.pem -subj "/CN=webhook_authz_ca"
# Create a second certificate authority
openssl genrsa -out badCAKey.pem 2048
openssl req -x509 -new -nodes -key badCAKey.pem -days 100000 -out badCACert.pem -subj "/CN=webhook_authz_ca"
# Create a server certiticate
openssl genrsa -out serverKey.pem 2048
openssl req -new -key serverKey.pem -out server.csr -subj "/CN=webhook_authz_server" -config server.conf
openssl x509 -req -in server.csr -CA caCert.pem -CAkey caKey.pem -CAcreateserial -out serverCert.pem -days 100000 -extensions v3_req -extfile server.conf
# Create a client certiticate
openssl genrsa -out clientKey.pem 2048
openssl req -new -key clientKey.pem -out client.csr -subj "/CN=webhook_authz_client" -config client.conf
openssl x509 -req -in client.csr -CA caCert.pem -CAkey caKey.pem -CAcreateserial -out clientCert.pem -days 100000 -extensions v3_req -extfile client.conf
outfile=certs_test.go
cat > $outfile << EOF
/*
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.
*/
EOF
echo "// This file was generated using openssl by the gencerts.sh script" >> $outfile
echo "// and holds raw certificates for the webhook tests." >> $outfile
echo "" >> $outfile
echo "package webhook" >> $outfile
for file in caKey caCert badCAKey badCACert serverKey serverCert clientKey clientCert; do
data=$(cat ${file}.pem)
echo "" >> $outfile
echo "var $file = []byte(\`$data\`)" >> $outfile
done
# Clean up after we're done.
rm *.pem
rm *.csr
rm *.srl
rm *.conf
/*
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 webhook implements the authorizer.Authorizer interface using HTTP webhooks.
package webhook
import (
"encoding/json"
"fmt"
"time"
"github.com/golang/glog"
authorization "k8s.io/api/authorization/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/util/cache"
"k8s.io/apiserver/pkg/authentication/user"
"k8s.io/apiserver/pkg/authorization/authorizer"
"k8s.io/apiserver/pkg/util/webhook"
"k8s.io/client-go/kubernetes/scheme"
authorizationclient "k8s.io/client-go/kubernetes/typed/authorization/v1"
)
var (
groupVersions = []schema.GroupVersion{authorization.SchemeGroupVersion}
)
const retryBackoff = 500 * time.Millisecond
// Ensure Webhook implements the authorizer.Authorizer interface.
var _ authorizer.Authorizer = (*WebhookAuthorizer)(nil)
type WebhookAuthorizer struct {
subjectAccessReview authorizationclient.SubjectAccessReviewInterface
responseCache *cache.LRUExpireCache
authorizedTTL time.Duration
unauthorizedTTL time.Duration
initialBackoff time.Duration
decisionOnError authorizer.Decision
}
// NewFromInterface creates a WebhookAuthorizer using the given subjectAccessReview client
func NewFromInterface(subjectAccessReview authorizationclient.SubjectAccessReviewInterface, authorizedTTL, unauthorizedTTL time.Duration) (*WebhookAuthorizer, error) {
return newWithBackoff(subjectAccessReview, authorizedTTL, unauthorizedTTL, retryBackoff)
}
// New creates a new WebhookAuthorizer from the provided kubeconfig file.
//
// The config's cluster field is used to refer to the remote service, user refers to the returned authorizer.
//
// # clusters refers to the remote service.
// clusters:
// - name: name-of-remote-authz-service
// cluster:
// certificate-authority: /path/to/ca.pem # CA for verifying the remote service.
// server: https://authz.example.com/authorize # URL of remote service to query. Must use 'https'.
//
// # users refers to the API server's webhook configuration.
// users:
// - name: name-of-api-server
// user:
// client-certificate: /path/to/cert.pem # cert for the webhook plugin to use
// client-key: /path/to/key.pem # key matching the cert
//
// For additional HTTP configuration, refer to the kubeconfig documentation
// https://kubernetes.io/docs/user-guide/kubeconfig-file/.
func New(kubeConfigFile string, authorizedTTL, unauthorizedTTL time.Duration) (*WebhookAuthorizer, error) {
subjectAccessReview, err := subjectAccessReviewInterfaceFromKubeconfig(kubeConfigFile)
if err != nil {
return nil, err
}
return newWithBackoff(subjectAccessReview, authorizedTTL, unauthorizedTTL, retryBackoff)
}
// newWithBackoff allows tests to skip the sleep.
func newWithBackoff(subjectAccessReview authorizationclient.SubjectAccessReviewInterface, authorizedTTL, unauthorizedTTL, initialBackoff time.Duration) (*WebhookAuthorizer, error) {
return &WebhookAuthorizer{
subjectAccessReview: subjectAccessReview,
responseCache: cache.NewLRUExpireCache(1024),
authorizedTTL: authorizedTTL,
unauthorizedTTL: unauthorizedTTL,
initialBackoff: initialBackoff,
decisionOnError: authorizer.DecisionNoOpinion,
}, nil
}
// Authorize makes a REST request to the remote service describing the attempted action as a JSON
// serialized api.authorization.v1beta1.SubjectAccessReview object. An example request body is
// provided below.
//
// {
// "apiVersion": "authorization.k8s.io/v1beta1",
// "kind": "SubjectAccessReview",
// "spec": {
// "resourceAttributes": {
// "namespace": "kittensandponies",
// "verb": "GET",
// "group": "group3",
// "resource": "pods"
// },
// "user": "jane",
// "group": [
// "group1",
// "group2"
// ]
// }
// }
//
// The remote service is expected to fill the SubjectAccessReviewStatus field to either allow or
// disallow access. A permissive response would return:
//
// {
// "apiVersion": "authorization.k8s.io/v1beta1",
// "kind": "SubjectAccessReview",
// "status": {
// "allowed": true
// }
// }
//
// To disallow access, the remote service would return:
//
// {
// "apiVersion": "authorization.k8s.io/v1beta1",
// "kind": "SubjectAccessReview",
// "status": {
// "allowed": false,
// "reason": "user does not have read access to the namespace"
// }
// }
//
// TODO(mikedanese): We should eventually support failing closed when we
// encounter an error. We are failing open now to preserve backwards compatible
// behavior.
func (w *WebhookAuthorizer) Authorize(attr authorizer.Attributes) (decision authorizer.Decision, reason string, err error) {
r := &authorization.SubjectAccessReview{}
if user := attr.GetUser(); user != nil {
r.Spec = authorization.SubjectAccessReviewSpec{
User: user.GetName(),
UID: user.GetUID(),
Groups: user.GetGroups(),
Extra: convertToSARExtra(user.GetExtra()),
}
}
if attr.IsResourceRequest() {
r.Spec.ResourceAttributes = &authorization.ResourceAttributes{
Namespace: attr.GetNamespace(),
Verb: attr.GetVerb(),
Group: attr.GetAPIGroup(),
Version: attr.GetAPIVersion(),
Resource: attr.GetResource(),
Subresource: attr.GetSubresource(),
Name: attr.GetName(),
}
} else {
r.Spec.NonResourceAttributes = &authorization.NonResourceAttributes{
Path: attr.GetPath(),
Verb: attr.GetVerb(),
}
}
key, err := json.Marshal(r.Spec)
if err != nil {
return w.decisionOnError, "", err
}
if entry, ok := w.responseCache.Get(string(key)); ok {
r.Status = entry.(authorization.SubjectAccessReviewStatus)
} else {
var (
result *authorization.SubjectAccessReview
err error
)
webhook.WithExponentialBackoff(w.initialBackoff, func() error {
result, err = w.subjectAccessReview.Create(r)
return err
})
if err != nil {
// An error here indicates bad configuration or an outage. Log for debugging.
glog.Errorf("Failed to make webhook authorizer request: %v", err)
return w.decisionOnError, "", err
}
r.Status = result.Status
if r.Status.Allowed {
w.responseCache.Add(string(key), r.Status, w.authorizedTTL)
} else {
w.responseCache.Add(string(key), r.Status, w.unauthorizedTTL)
}
}
switch {
case r.Status.Denied && r.Status.Allowed:
return authorizer.DecisionDeny, r.Status.Reason, fmt.Errorf("webhook subject access review returned both allow and deny response")
case r.Status.Denied:
return authorizer.DecisionDeny, r.Status.Reason, nil
case r.Status.Allowed:
return authorizer.DecisionAllow, r.Status.Reason, nil
default:
return authorizer.DecisionNoOpinion, r.Status.Reason, nil
}
}
//TODO: need to finish the method to get the rules when using webhook mode
func (w *WebhookAuthorizer) RulesFor(user user.Info, namespace string) ([]authorizer.ResourceRuleInfo, []authorizer.NonResourceRuleInfo, bool, error) {
var (
resourceRules []authorizer.ResourceRuleInfo
nonResourceRules []authorizer.NonResourceRuleInfo
)
incomplete := true
return resourceRules, nonResourceRules, incomplete, fmt.Errorf("webhook authorizer does not support user rule resolution")
}
func convertToSARExtra(extra map[string][]string) map[string]authorization.ExtraValue {
if extra == nil {
return nil
}
ret := map[string]authorization.ExtraValue{}
for k, v := range extra {
ret[k] = authorization.ExtraValue(v)
}
return ret
}
// subjectAccessReviewInterfaceFromKubeconfig builds a client from the specified kubeconfig file,
// and returns a SubjectAccessReviewInterface that uses that client. Note that the client submits SubjectAccessReview
// requests to the exact path specified in the kubeconfig file, so arbitrary non-API servers can be targeted.
func subjectAccessReviewInterfaceFromKubeconfig(kubeConfigFile string) (authorizationclient.SubjectAccessReviewInterface, error) {
localScheme := runtime.NewScheme()
if err := scheme.AddToScheme(localScheme); err != nil {
return nil, err
}
if err := localScheme.SetVersionPriority(groupVersions...); err != nil {
return nil, err
}
gw, err := webhook.NewGenericWebhook(localScheme, scheme.Codecs, kubeConfigFile, groupVersions, 0)
if err != nil {
return nil, err
}
return &subjectAccessReviewClient{gw}, nil
}
type subjectAccessReviewClient struct {
w *webhook.GenericWebhook
}
func (t *subjectAccessReviewClient) Create(subjectAccessReview *authorization.SubjectAccessReview) (*authorization.SubjectAccessReview, error) {
result := &authorization.SubjectAccessReview{}
err := t.w.RestClient.Post().Body(subjectAccessReview).Do().Into(result)
return result, err
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment