Skip to content

Commit 3b1cb3d

Browse files
committed
add trimmed down version of the built-in k8s authorizer options to the kcp options
On-behalf-of: @SAP [email protected]
1 parent 1d1f3e4 commit 3b1cb3d

File tree

5 files changed

+82
-10
lines changed

5 files changed

+82
-10
lines changed

cmd/kcp/kcp.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,9 @@ func main() {
117117
logger := klog.FromContext(cmd.Context())
118118
logger.Info("running with selected batteries", "batteries", strings.Join(completed.Server.Extra.BatteriesIncluded, ","))
119119

120-
config, err := server.NewConfig(completed.Server)
120+
ctx := genericapiserver.SetupSignalContext()
121+
122+
serverConfig, err := server.NewConfig(ctx, completed.Server)
121123
if err != nil {
122124
return err
123125
}
@@ -127,8 +129,6 @@ func main() {
127129
return err
128130
}
129131

130-
ctx := genericapiserver.SetupSignalContext()
131-
132132
// the etcd server must be up before NewServer because storage decorators access it right away
133133
if completedConfig.EmbeddedEtcd.Config != nil {
134134
if err := embeddedetcd.NewServer(completedConfig.EmbeddedEtcd).Run(ctx); err != nil {

pkg/server/config.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,7 @@ func (c *Config) Complete() (CompletedConfig, error) {
175175

176176
const KcpBootstrapperUserName = "system:kcp:bootstrapper"
177177

178-
func NewConfig(opts kcpserveroptions.CompletedOptions) (*Config, error) {
178+
func NewConfig(ctx context.Context, opts kcpserveroptions.CompletedOptions) (*Config, error) {
179179
c := &Config{
180180
Options: opts,
181181
}
@@ -324,7 +324,7 @@ func NewConfig(opts kcpserveroptions.CompletedOptions) (*Config, error) {
324324
return nil, err
325325
}
326326

327-
if err := opts.Authorization.ApplyTo(c.GenericConfig, c.KubeSharedInformerFactory, c.CacheKubeSharedInformerFactory, c.KcpSharedInformerFactory, c.CacheKcpSharedInformerFactory); err != nil {
327+
if err := opts.Authorization.ApplyTo(ctx, c.GenericConfig, c.KubeSharedInformerFactory, c.CacheKubeSharedInformerFactory, c.KcpSharedInformerFactory, c.CacheKcpSharedInformerFactory); err != nil {
328328
return nil, err
329329
}
330330
var userToken string

pkg/server/options/authorization.go

Lines changed: 71 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ limitations under the License.
1717
package options
1818

1919
import (
20+
"context"
21+
2022
kcpkubernetesinformers "github.com/kcp-dev/client-go/informers"
2123
"github.com/spf13/pflag"
2224

@@ -25,7 +27,11 @@ import (
2527
"k8s.io/apiserver/pkg/authorization/authorizerfactory"
2628
"k8s.io/apiserver/pkg/authorization/path"
2729
"k8s.io/apiserver/pkg/authorization/union"
30+
"k8s.io/apiserver/pkg/informerfactoryhack"
2831
genericapiserver "k8s.io/apiserver/pkg/server"
32+
"k8s.io/apiserver/pkg/server/egressselector"
33+
authzmodes "k8s.io/kubernetes/pkg/kubeapiserver/authorizer/modes"
34+
kubeoptions "k8s.io/kubernetes/pkg/kubeapiserver/options"
2935

3036
authz "github.com/kcp-dev/kcp/pkg/authorization"
3137
kcpinformers "github.com/kcp-dev/kcp/sdk/client/informers/externalversions"
@@ -38,6 +44,10 @@ type Authorization struct {
3844

3945
// AlwaysAllowGroups are groups which are allowed to take any actions. In kube, this is privileged system group.
4046
AlwaysAllowGroups []string
47+
48+
// Webhook contains flags to enable an external HTTPS webhook to perform
49+
// authorization against. Note that not all built-in options are supported by kcp.
50+
Webhook *kubeoptions.BuiltInAuthorizationOptions
4151
}
4252

4353
func NewAuthorization() *Authorization {
@@ -46,6 +56,7 @@ func NewAuthorization() *Authorization {
4656
// This field can be cleared by callers if they don't want this behavior.
4757
AlwaysAllowPaths: []string{"/healthz", "/readyz", "/livez"},
4858
AlwaysAllowGroups: []string{user.SystemPrivilegedGroup},
59+
Webhook: kubeoptions.NewBuiltInAuthorizationOptions(),
4960
}
5061
}
5162

@@ -61,13 +72,35 @@ func (s *Authorization) WithAlwaysAllowPaths(paths ...string) *Authorization {
6172
return s
6273
}
6374

75+
func (s *Authorization) Complete() error {
76+
if s == nil {
77+
return nil
78+
}
79+
80+
// kcp only supports optionally specifying an external authorization webhook
81+
// in addition to the built-in authorization logic.
82+
if s.Webhook.WebhookConfigFile != "" {
83+
s.Webhook.Modes = []string{authzmodes.ModeWebhook}
84+
} else {
85+
s.Webhook = nil
86+
}
87+
88+
return nil
89+
}
90+
6491
func (s *Authorization) Validate() []error {
6592
if s == nil {
6693
return nil
6794
}
6895

6996
allErrors := []error{}
7097

98+
if s.Webhook != nil {
99+
if errs := s.Webhook.Validate(); len(errs) > 0 {
100+
allErrors = append(allErrors, errs...)
101+
}
102+
}
103+
71104
return allErrors
72105
}
73106

@@ -79,11 +112,48 @@ func (s *Authorization) AddFlags(fs *pflag.FlagSet) {
79112
fs.StringSliceVar(&s.AlwaysAllowPaths, "authorization-always-allow-paths", s.AlwaysAllowPaths,
80113
"A list of HTTP paths to skip during authorization, i.e. these are authorized without "+
81114
"contacting the 'core' kubernetes server.")
115+
116+
// Only surface selected, webhook-related CLI flags
117+
118+
fs.StringVar(&s.Webhook.WebhookConfigFile, "authorization-webhook-config-file", s.Webhook.WebhookConfigFile,
119+
"File with optional webhook configuration in kubeconfig format. The API server will query the remote service to determine access on the API server's secure port.")
120+
121+
fs.StringVar(&s.Webhook.WebhookVersion, "authorization-webhook-version", s.Webhook.WebhookVersion,
122+
"The API version of the authorization.k8s.io SubjectAccessReview to send to and expect from the webhook.")
123+
124+
fs.DurationVar(&s.Webhook.WebhookCacheAuthorizedTTL, "authorization-webhook-cache-authorized-ttl", s.Webhook.WebhookCacheAuthorizedTTL,
125+
"The duration to cache 'authorized' responses from the webhook authorizer.")
126+
127+
fs.DurationVar(&s.Webhook.WebhookCacheUnauthorizedTTL, "authorization-webhook-cache-unauthorized-ttl", s.Webhook.WebhookCacheUnauthorizedTTL,
128+
"The duration to cache 'unauthorized' responses from the webhook authorizer.")
82129
}
83130

84-
func (s *Authorization) ApplyTo(config *genericapiserver.Config, kubeInformers, globalKubeInformers kcpkubernetesinformers.SharedInformerFactory, kcpInformers, globalKcpInformers kcpinformers.SharedInformerFactory) error {
131+
func (s *Authorization) ApplyTo(ctx context.Context, config *genericapiserver.Config, kubeInformers, globalKubeInformers kcpkubernetesinformers.SharedInformerFactory, kcpInformers, globalKcpInformers kcpinformers.SharedInformerFactory) error {
85132
var authorizers []authorizer.Authorizer
86133

134+
// re-use the authorizer from the generic control plane (this is only set for webhooks)
135+
if webhook := s.Webhook; webhook != nil && webhook.WebhookConfigFile != "" {
136+
authorizationConfig, err := webhook.ToAuthorizationConfig(informerfactoryhack.Wrap(kubeInformers))
137+
if err != nil {
138+
return err
139+
}
140+
141+
if config.EgressSelector != nil {
142+
egressDialer, err := config.EgressSelector.Lookup(egressselector.ControlPlane.AsNetworkContext())
143+
if err != nil {
144+
return err
145+
}
146+
authorizationConfig.CustomDial = egressDialer
147+
}
148+
149+
authorizer, _, err := authorizationConfig.New(ctx, config.APIServerID)
150+
if err != nil {
151+
return err
152+
}
153+
154+
authorizers = append(authorizers, authorizer)
155+
}
156+
87157
localLogicalClusterLister := kcpInformers.Core().V1alpha1().LogicalClusters().Lister()
88158
globalLogicalClusterLister := globalKcpInformers.Core().V1alpha1().LogicalClusters().Lister()
89159

pkg/server/options/options.go

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ import (
2929
genericapiserveroptions "k8s.io/apiserver/pkg/server/options"
3030
cliflag "k8s.io/component-base/cli/flag"
3131
controlplaneapiserver "k8s.io/kubernetes/pkg/controlplane/apiserver/options"
32-
kubeoptions "k8s.io/kubernetes/pkg/kubeapiserver/options"
32+
authzmodes "k8s.io/kubernetes/pkg/kubeapiserver/authorizer/modes"
3333

3434
kcpadmission "github.com/kcp-dev/kcp/pkg/admission"
3535
etcdoptions "github.com/kcp-dev/kcp/pkg/embeddedetcd/options"
@@ -251,6 +251,10 @@ func (o *Options) Complete(rootDir string) (*CompletedOptions, error) {
251251
o.EmbeddedEtcd.Enabled = true
252252
}
253253

254+
if err := o.Authorization.Complete(); err != nil {
255+
return nil, err
256+
}
257+
254258
var err error
255259
if !filepath.IsAbs(o.EmbeddedEtcd.Directory) {
256260
o.EmbeddedEtcd.Directory, err = filepath.Abs(o.EmbeddedEtcd.Directory)
@@ -311,8 +315,6 @@ func (o *Options) Complete(rootDir string) (*CompletedOptions, error) {
311315
}
312316
if o.GenericControlPlane.ServiceAccountSigningKeyFile == "" {
313317
o.GenericControlPlane.ServiceAccountSigningKeyFile = o.Controllers.SAController.ServiceAccountKeyFile
314-
}
315-
316318
completedGenericServerRunOptions, err := o.GenericControlPlane.Complete(nil, nil)
317319
if err != nil {
318320
return nil, err

test/e2e/framework/kcp.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -655,7 +655,7 @@ func (c *kcpServer) Run(opts ...RunOption) error {
655655
return apierrors.NewAggregate(errs)
656656
}
657657

658-
config, err := server.NewConfig(completed.Server)
658+
config, err := server.NewConfig(ctx, completed.Server)
659659
if err != nil {
660660
cleanup()
661661
return err

0 commit comments

Comments
 (0)