Skip to content

Commit b7800b2

Browse files
committed
UPSTREAM: <carry>: Add configuration to RestrictSubjectBindings admission plugin
to allow external components to specify that the oauth stack will/will not be present on the cluster. When the oauth stack is not present, this admission plugin is not effective and will be disabled to prevent configuration and startup of informers for APIs served by the oauth-apiserver Signed-off-by: Bryce Palmer <[email protected]>
1 parent 8ac36bf commit b7800b2

File tree

8 files changed

+255
-24
lines changed

8 files changed

+255
-24
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
// +k8s:deepcopy-gen=package,register
2+
3+
// Package v1alpha is the v1alpha1 version of the API.
4+
package v1alpha1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package v1alpha1
2+
3+
import (
4+
"k8s.io/apimachinery/pkg/runtime"
5+
"k8s.io/apimachinery/pkg/runtime/schema"
6+
)
7+
8+
var (
9+
GroupVersion = schema.GroupVersion{Group: "authorization.openshift.io", Version: "v1alpha1"}
10+
internalGroupVersion = schema.GroupVersion{Group: "authorization.openshift.io", Version: runtime.APIVersionInternal}
11+
)
12+
13+
var (
14+
localSchemeBuilder = runtime.NewSchemeBuilder(
15+
addKnownTypes,
16+
)
17+
Install = localSchemeBuilder.AddToScheme
18+
)
19+
20+
func addKnownTypes(scheme *runtime.Scheme) error {
21+
scheme.AddKnownTypes(GroupVersion,
22+
&RestrictSubjectBindingsAdmissionConfig{},
23+
)
24+
25+
scheme.AddKnownTypes(internalGroupVersion,
26+
&RestrictSubjectBindingsAdmissionConfig{},
27+
)
28+
return nil
29+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package v1alpha1
2+
3+
import (
4+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
5+
)
6+
7+
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
8+
9+
// RestrictSubjectBindingsAdmissionConfig is the type
10+
// used for configuring the authorization.openshift.io/RestrictSubjectBindings
11+
// admission plugin.
12+
type RestrictSubjectBindingsAdmissionConfig struct {
13+
metav1.TypeMeta `json:",inline"`
14+
15+
// openshiftOAuthDesiredState specifies the desired state
16+
// of the OpenShift oauth-apiserver based on observed configuration.
17+
//
18+
// Allowed values are Desired and NotDesired.
19+
// Defaults to Desired.
20+
//
21+
// When set to Desired, the authorization.openshift.io/RestrictSubjectBindings
22+
// admission plugin will be configured with the expectation that the OpenShift
23+
// oauth-apiserver will eventually be running and serving it's APIs.
24+
//
25+
// When set to NotDesired, the authorization.openshift.io/RestrictSubjectBindings
26+
// admission plugin will be configured with the expectation that the OpenShift
27+
// oauth-apiserver will not be running.
28+
OpenShiftOAuthDesiredState OpenShiftOAuthState `json:"openshiftOAuthDesiredState"`
29+
}
30+
31+
type OpenShiftOAuthState string
32+
33+
const (
34+
OpenShiftOAuthStateDesired = "Desired"
35+
OpenShiftOAuthStateNotDesired = "NotDesired"
36+
)

openshift-kube-apiserver/admission/authorization/apis/restrictusers/v1alpha1/zz_generated.deepcopy.go

+51
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

openshift-kube-apiserver/admission/authorization/restrictusers/restrictusers.go

+56-15
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import (
1313
"k8s.io/apiserver/pkg/admission/initializer"
1414
"k8s.io/client-go/kubernetes"
1515
"k8s.io/client-go/rest"
16+
"k8s.io/client-go/tools/cache"
1617
"k8s.io/klog/v2"
1718
"k8s.io/kubernetes/pkg/apis/rbac"
1819

@@ -21,14 +22,53 @@ import (
2122
userclient "github.com/openshift/client-go/user/clientset/versioned"
2223
userinformer "github.com/openshift/client-go/user/informers/externalversions"
2324
"github.com/openshift/library-go/pkg/apiserver/admission/admissionrestconfig"
25+
"github.com/openshift/library-go/pkg/config/helpers"
26+
restrictusersv1alpha1 "k8s.io/kubernetes/openshift-kube-apiserver/admission/authorization/apis/restrictusers/v1alpha1"
2427
"k8s.io/kubernetes/openshift-kube-apiserver/admission/authorization/restrictusers/usercache"
2528
)
2629

30+
const RestrictSubjectBindingsPluginName = "authorization.openshift.io/RestrictSubjectBindings"
31+
2732
func Register(plugins *admission.Plugins) {
28-
plugins.Register("authorization.openshift.io/RestrictSubjectBindings",
29-
func(config io.Reader) (admission.Interface, error) {
30-
return NewRestrictUsersAdmission()
31-
})
33+
plugins.Register(RestrictSubjectBindingsPluginName, pluginForConfig)
34+
}
35+
36+
func pluginForConfig(config io.Reader) (admission.Interface, error) {
37+
cfg, err := readConfig(config)
38+
if err != nil {
39+
return nil, err
40+
}
41+
42+
if cfg != nil && cfg.OpenShiftOAuthDesiredState == restrictusersv1alpha1.OpenShiftOAuthStateNotDesired {
43+
klog.Infof("Admission plugin %q configured to expect the OpenShift oauth-apiserver as not being available. This is effectively the same as disabling the plugin, so it will be disabled.", RestrictSubjectBindingsPluginName)
44+
return nil, nil
45+
}
46+
47+
return NewRestrictUsersAdmission()
48+
}
49+
50+
func readConfig(reader io.Reader) (*restrictusersv1alpha1.RestrictSubjectBindingsAdmissionConfig, error) {
51+
obj, err := helpers.ReadYAMLToInternal(reader, restrictusersv1alpha1.Install)
52+
if err != nil {
53+
return nil, err
54+
}
55+
if obj == nil {
56+
return nil, nil
57+
}
58+
config, ok := obj.(*restrictusersv1alpha1.RestrictSubjectBindingsAdmissionConfig)
59+
if !ok {
60+
return nil, fmt.Errorf("unexpected config object: %#v", obj)
61+
}
62+
63+
// validate config
64+
switch config.OpenShiftOAuthDesiredState {
65+
case restrictusersv1alpha1.OpenShiftOAuthStateDesired, restrictusersv1alpha1.OpenShiftOAuthStateNotDesired:
66+
// valid, do nothing
67+
default:
68+
return nil, fmt.Errorf("config is invalid, openshiftOAuthDesiredState must be one of Desired,NotDesired but was %s", config.OpenShiftOAuthDesiredState)
69+
}
70+
71+
return config, nil
3272
}
3373

3474
type GroupCache interface {
@@ -48,19 +88,16 @@ type restrictUsersAdmission struct {
4888
groupCache GroupCache
4989
}
5090

51-
var _ = admissionrestconfig.WantsRESTClientConfig(&restrictUsersAdmission{})
52-
var _ = WantsUserInformer(&restrictUsersAdmission{})
53-
var _ = initializer.WantsExternalKubeClientSet(&restrictUsersAdmission{})
54-
var _ = admission.ValidationInterface(&restrictUsersAdmission{})
91+
var (
92+
_ = admissionrestconfig.WantsRESTClientConfig(&restrictUsersAdmission{})
93+
_ = WantsUserInformer(&restrictUsersAdmission{})
94+
_ = initializer.WantsExternalKubeClientSet(&restrictUsersAdmission{})
95+
_ = admission.ValidationInterface(&restrictUsersAdmission{})
96+
)
5597

5698
// NewRestrictUsersAdmission configures an admission plugin that enforces
5799
// restrictions on adding role bindings in a project.
58-
func NewRestrictUsersAdmission() (admission.Interface, error) {
59-
return &restrictUsersAdmission{
60-
Handler: admission.NewHandler(admission.Create, admission.Update),
61-
}, nil
62-
}
63-
100+
func NewRestrictUsersAdmission() (admission.Interface, error)
64101
func (q *restrictUsersAdmission) SetExternalKubeClientSet(c kubernetes.Interface) {
65102
q.kubeClient = c
66103
}
@@ -87,6 +124,11 @@ func (q *restrictUsersAdmission) SetRESTClientConfig(restClientConfig rest.Confi
87124
}
88125

89126
func (q *restrictUsersAdmission) SetUserInformer(userInformers userinformer.SharedInformerFactory) {
127+
if err := userInformers.User().V1().Groups().Informer().AddIndexers(cache.Indexers{
128+
usercache.ByUserIndexName: usercache.ByUserIndexKeys,
129+
}); err != nil {
130+
return
131+
}
90132
q.groupCache = usercache.NewGroupCache(userInformers.User().V1().Groups())
91133
}
92134

@@ -116,7 +158,6 @@ func subjectsDelta(elementsToIgnore, elements []rbac.Subject) []rbac.Subject {
116158
// each subject in the binding must be matched by some rolebinding restriction
117159
// in the namespace.
118160
func (q *restrictUsersAdmission) Validate(ctx context.Context, a admission.Attributes, _ admission.ObjectInterfaces) (err error) {
119-
120161
// We only care about rolebindings
121162
if a.GetResource().GroupResource() != rbac.Resource("rolebindings") {
122163
return nil

openshift-kube-apiserver/admission/authorization/restrictusers/restrictusers_test.go

+73
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,12 @@ package restrictusers
33
import (
44
"context"
55
"fmt"
6+
"io"
67
"strings"
78
"testing"
89

910
corev1 "k8s.io/api/core/v1"
11+
"k8s.io/apimachinery/pkg/api/equality"
1012
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
1113
"k8s.io/apimachinery/pkg/runtime"
1214
"k8s.io/apimachinery/pkg/runtime/schema"
@@ -402,3 +404,74 @@ func TestAdmission(t *testing.T) {
402404
}
403405
}
404406
}
407+
408+
func TestPluginForConfig(t *testing.T) {
409+
testcases := []struct {
410+
name string
411+
config string
412+
expectedErr string
413+
expectedPlugin admission.Interface
414+
}{
415+
{
416+
name: "no config, no err, expect plugin",
417+
expectedPlugin: func() admission.Interface {
418+
plugin, _ := NewRestrictUsersAdmission()
419+
return plugin
420+
}(),
421+
},
422+
{
423+
name: "config sets openshiftOAuthDesiredState to NotDesired, no err, nil plugin",
424+
config: `apiVersion: authorization.openshift.io/v1alpha1
425+
kind: RestrictSubjectBindingsAdmissionConfig
426+
openshiftOAuthDesiredState: NotDesired
427+
`,
428+
expectedPlugin: nil,
429+
},
430+
{
431+
name: "config sets openshiftOAuthDesiredState to Desired, no err, expect plugin",
432+
config: `apiVersion: authorization.openshift.io/v1alpha1
433+
kind: RestrictSubjectBindingsAdmissionConfig
434+
openshiftOAuthDesiredState: Desired
435+
`,
436+
expectedPlugin: func() admission.Interface {
437+
plugin, _ := NewRestrictUsersAdmission()
438+
return plugin
439+
}(),
440+
},
441+
{
442+
name: "config sets openshiftOAuthDesiredState to invalid value, err, nil plugin",
443+
config: `apiVersion: authorization.openshift.io/v1alpha1
444+
kind: RestrictSubjectBindingsAdmissionConfig
445+
openshiftOAuthDesiredState: FooBar
446+
`,
447+
expectedPlugin: nil,
448+
expectedErr: "config is invalid, openshiftOAuthDesiredState must be one of Desired,NotDesired",
449+
},
450+
}
451+
452+
for _, tc := range testcases {
453+
t.Run(tc.name, func(t *testing.T) {
454+
var reader io.Reader
455+
if len(tc.config) > 0 {
456+
reader = strings.NewReader(tc.config)
457+
}
458+
459+
plugin, err := pluginForConfig(reader)
460+
switch {
461+
case len(tc.expectedErr) == 0 && err == nil:
462+
case len(tc.expectedErr) == 0 && err != nil:
463+
t.Errorf("%s: unexpected error: %v", tc.name, err)
464+
case len(tc.expectedErr) != 0 && err == nil:
465+
t.Errorf("%s: missing error: %v", tc.name, tc.expectedErr)
466+
case len(tc.expectedErr) != 0 && err != nil &&
467+
!strings.Contains(err.Error(), tc.expectedErr):
468+
t.Errorf("%s: missing error: expected %v, got %v",
469+
tc.name, tc.expectedErr, err)
470+
}
471+
472+
if !equality.Semantic.DeepEqual(tc.expectedPlugin, plugin) {
473+
t.Errorf("plugin does not match. expected %v, got %v", tc.expectedPlugin, plugin)
474+
}
475+
})
476+
}
477+
}

openshift-kube-apiserver/openshiftkubeapiserver/patch.go

+5-8
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,7 @@ import (
2727
clientgoinformers "k8s.io/client-go/informers"
2828
corev1informers "k8s.io/client-go/informers/core/v1"
2929
"k8s.io/client-go/rest"
30-
"k8s.io/client-go/tools/cache"
3130
"k8s.io/kubernetes/openshift-kube-apiserver/admission/authorization/restrictusers"
32-
"k8s.io/kubernetes/openshift-kube-apiserver/admission/authorization/restrictusers/usercache"
3331
"k8s.io/kubernetes/openshift-kube-apiserver/admission/autoscaling/managednode"
3432
"k8s.io/kubernetes/openshift-kube-apiserver/admission/autoscaling/managementcpusoverride"
3533
"k8s.io/kubernetes/openshift-kube-apiserver/admission/scheduler/nodeenv"
@@ -109,7 +107,8 @@ func OpenShiftKubeAPIServerConfigPatch(genericConfig *genericapiserver.Config, k
109107
// END HANDLER CHAIN
110108

111109
openshiftAPIServiceReachabilityCheck := newOpenshiftAPIServiceReachabilityCheck(genericConfig.PublicAddress)
112-
oauthAPIServiceReachabilityCheck := newOAuthPIServiceReachabilityCheck(genericConfig.PublicAddress)
110+
oauthAPIServiceReachabilityCheck := newOAuthAPIServiceReachabilityCheck(genericConfig.PublicAddress)
111+
113112
genericConfig.ReadyzChecks = append(genericConfig.ReadyzChecks, openshiftAPIServiceReachabilityCheck, oauthAPIServiceReachabilityCheck)
114113

115114
genericConfig.AddPostStartHookOrDie("openshift.io-startkubeinformers", func(context genericapiserver.PostStartHookContext) error {
@@ -176,11 +175,6 @@ func newInformers(loopbackClientConfig *rest.Config) (*kubeAPIServerInformers, e
176175
OpenshiftUserInformers: userinformer.NewSharedInformerFactory(userClient, defaultInformerResyncPeriod),
177176
OpenshiftConfigInformers: configv1informer.NewSharedInformerFactory(configClient, defaultInformerResyncPeriod),
178177
}
179-
if err := ret.OpenshiftUserInformers.User().V1().Groups().Informer().AddIndexers(cache.Indexers{
180-
usercache.ByUserIndexName: usercache.ByUserIndexKeys,
181-
}); err != nil {
182-
return nil, err
183-
}
184178

185179
return ret, nil
186180
}
@@ -195,12 +189,15 @@ type kubeAPIServerInformers struct {
195189
func (i *kubeAPIServerInformers) getOpenshiftQuotaInformers() quotainformer.SharedInformerFactory {
196190
return i.OpenshiftQuotaInformers
197191
}
192+
198193
func (i *kubeAPIServerInformers) getOpenshiftSecurityInformers() securityv1informer.SharedInformerFactory {
199194
return i.OpenshiftSecurityInformers
200195
}
196+
201197
func (i *kubeAPIServerInformers) getOpenshiftUserInformers() userinformer.SharedInformerFactory {
202198
return i.OpenshiftUserInformers
203199
}
200+
204201
func (i *kubeAPIServerInformers) getOpenshiftInfraInformers() configv1informer.SharedInformerFactory {
205202
return i.OpenshiftConfigInformers
206203
}

openshift-kube-apiserver/openshiftkubeapiserver/sdn_readyz_wait.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ func newOpenshiftAPIServiceReachabilityCheck(ipForKubernetesDefaultService net.I
2222
return newAggregatedAPIServiceReachabilityCheck(ipForKubernetesDefaultService, "openshift-apiserver", "api")
2323
}
2424

25-
func newOAuthPIServiceReachabilityCheck(ipForKubernetesDefaultService net.IP) *aggregatedAPIServiceAvailabilityCheck {
25+
func newOAuthAPIServiceReachabilityCheck(ipForKubernetesDefaultService net.IP) *aggregatedAPIServiceAvailabilityCheck {
2626
return newAggregatedAPIServiceReachabilityCheck(ipForKubernetesDefaultService, "openshift-oauth-apiserver", "api")
2727
}
2828

0 commit comments

Comments
 (0)