Skip to content
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

Bug 1729242: OCP 4 AWS privilege escalation vulnerability by running pods on masters #524

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
10 changes: 9 additions & 1 deletion pkg/operator/configobservation/scheduler/observe_scheduler.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package scheduler

import (
configv1 "github.com/openshift/api/config/v1"
"github.com/openshift/cluster-kube-apiserver-operator/pkg/operator/configobservation"
"github.com/openshift/library-go/pkg/operator/configobserver"
"github.com/openshift/library-go/pkg/operator/events"
Expand All @@ -10,6 +11,8 @@ import (
"k8s.io/klog"
)

const workerNodeSelector = "node-role.kubernetes.io/worker=\"\""
Copy link
Contributor

@smarterclayton smarterclayton Jul 14, 2019

Choose a reason for hiding this comment

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

This is not guaranteed to exist in all clusters.

Node roles are not an api and no business logic should based on node roles.

Copy link
Contributor Author

@ravisantoshgudimetla ravisantoshgudimetla Jul 15, 2019

Choose a reason for hiding this comment

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

IIUC, there are 2 ways to distinguish master nodes from worker nodes:

  • role label
  • Master taints

So, are you suggesting, we cannot rely on role label which the defaultNodeSelector is based on? IIRC, we used to have something like type=Compute as defaultNodeSelector in 3.11.

This is not to say that we cannot have a validation against pods have master tolerations

Choose a reason for hiding this comment

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

@smarterclayton Can you explain what you mean by "Node roles are not an api and no business logic should based on node roles."? It has been common practice with OpenShift in the field to add role labels to nodes and separate workload based on these labels by using node selectors and the openshift.io/node-selector annotation. This has been a very effective design pattern, what is inappropriate about this? Should we encourage use of a different label?


// ObserveDefaultNodeSelector reads the defaultNodeSelector from the scheduler configuration instance cluster
func ObserveDefaultNodeSelector(genericListers configobserver.Listers, recorder events.Recorder, existingConfig map[string]interface{}) (map[string]interface{}, []error) {
listers := genericListers.(configobservation.Listers)
Expand All @@ -36,7 +39,12 @@ func ObserveDefaultNodeSelector(genericListers configobserver.Listers, recorder
if err != nil {
return prevObservedConfig, errs
}

if schedulerConfig.Spec == (configv1.SchedulerSpec{}) {
Copy link
Contributor

Choose a reason for hiding this comment

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

I prefer the defaulting pattern used in:

authConfig := defaultAuthConfig(authConfigNoDefaults)

if err := unstructured.SetNestedField(observedConfig, workerNodeSelector, defaultNodeSelectorPath...); err != nil {
errs = append(errs, err)
}
klog.V(4).Infof("Found an empty scheduler spec, so setting the defaultNodeSelector to worker nodes")
}
defaultNodeSelector := schedulerConfig.Spec.DefaultNodeSelector
if len(defaultNodeSelector) > 0 {
if err := unstructured.SetNestedField(observedConfig, defaultNodeSelector, defaultNodeSelectorPath...); err != nil {
Expand Down
58 changes: 38 additions & 20 deletions pkg/operator/configobservation/scheduler/observer_scheduler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,27 +15,45 @@ import (

func TestObserveSchedulerConfig(t *testing.T) {
nodeSelector := "type=user-node,region=east"
indexer := cache.NewIndexer(cache.MetaNamespaceKeyFunc, cache.Indexers{})
if err := indexer.Add(&configv1.Scheduler{
ObjectMeta: metav1.ObjectMeta{Name: "cluster"},
Spec: configv1.SchedulerSpec{
DefaultNodeSelector: nodeSelector,
tests := []struct {
description string
nodeSelectorExpected string
SchedulerSpec configv1.SchedulerSpec
}{
{
description: "Empty scheduler spec",
nodeSelectorExpected: workerNodeSelector,
SchedulerSpec: configv1.SchedulerSpec{},
},
{
description: "Non-empty scheduler spec",
nodeSelectorExpected: nodeSelector,
SchedulerSpec: configv1.SchedulerSpec{
DefaultNodeSelector: nodeSelector,
},
},
}); err != nil {
t.Fatal(err.Error())
}
listers := configobservation.Listers{
SchedulerLister: configlistersv1.NewSchedulerLister(indexer),
}
result, errors := ObserveDefaultNodeSelector(listers, events.NewInMemoryRecorder("scheduler"), map[string]interface{}{})
if len(errors) > 0 {
t.Fatalf("expected len(errors) == 0")
}
observedSelector, _, err := unstructured.NestedString(result, "projectConfig", "defaultNodeSelector")
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if observedSelector != nodeSelector {
t.Fatalf("expected nodeselector to be %v but got %v", nodeSelector, observedSelector)
for _, test := range tests {
indexer := cache.NewIndexer(cache.MetaNamespaceKeyFunc, cache.Indexers{})
if err := indexer.Add(&configv1.Scheduler{
ObjectMeta: metav1.ObjectMeta{Name: "cluster"},
Spec: test.SchedulerSpec,
}); err != nil {
t.Fatal(err.Error())
}
listers := configobservation.Listers{
SchedulerLister: configlistersv1.NewSchedulerLister(indexer),
}
result, errors := ObserveDefaultNodeSelector(listers, events.NewInMemoryRecorder("scheduler"), map[string]interface{}{})
if len(errors) > 0 {
t.Fatalf("expected len(errors) == 0")
}
observedSelector, _, err := unstructured.NestedString(result, "projectConfig", "defaultNodeSelector")
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if observedSelector != test.nodeSelectorExpected {
t.Fatalf("expected nodeselector to be %v but got %v in %v", test.nodeSelectorExpected, observedSelector, test.description)
}
}
}