Skip to content

Commit f6b0681

Browse files
Changing route and ingress host is allowed if the user has permission
Add "update" to the permissions that a user may have for "routes/custom-host", and allow the spec.host field to be updated if this value is set. This allows administrators to change routes, but by default no one can. An administrator can delegate this permission as necessary or fix the route.
1 parent 9086c5d commit f6b0681

File tree

6 files changed

+109
-10
lines changed

6 files changed

+109
-10
lines changed

pkg/ingress/admission/ingress_admission.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,24 @@ func (r *ingressAdmission) Admit(a admission.Attributes) error {
119119
return nil
120120
}
121121
if !haveHostnamesChanged(oldIngress, newIngress) {
122+
attr := authorizer.AttributesRecord{
123+
User: a.GetUserInfo(),
124+
Verb: "update",
125+
Namespace: a.GetNamespace(),
126+
Name: a.GetName(),
127+
Resource: "routes",
128+
Subresource: "custom-host",
129+
APIGroup: "route.openshift.io",
130+
ResourceRequest: true,
131+
}
132+
kind := schema.GroupKind{Group: a.GetResource().Group, Kind: a.GetResource().Resource}
133+
allow, _, err := r.authorizer.Authorize(attr)
134+
if err != nil {
135+
return errors.NewInvalid(kind, newIngress.Name, field.ErrorList{field.InternalError(field.NewPath("spec", "rules"), err)})
136+
}
137+
if allow {
138+
return nil
139+
}
122140
return fmt.Errorf("cannot change hostname")
123141
}
124142
}

pkg/ingress/admission/ingress_admission_test.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,15 @@ func TestAdmission(t *testing.T) {
6161
oldHost: "bar.com",
6262
testName: "changing hostname should fail",
6363
},
64+
{
65+
admit: true,
66+
allow: true,
67+
config: emptyConfig(),
68+
op: admission.Update,
69+
newHost: "foo.com",
70+
oldHost: "bar.com",
71+
testName: "changing hostname should succeed if the user has permission",
72+
},
6473
{
6574
admit: false,
6675
config: nil,

pkg/route/api/validation/validation.go

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ import (
1111
kvalidation "k8s.io/apimachinery/pkg/util/validation"
1212
"k8s.io/apimachinery/pkg/util/validation/field"
1313
"k8s.io/kubernetes/pkg/api/validation"
14-
kval "k8s.io/kubernetes/pkg/api/validation"
1514

1615
oapi "github.com/openshift/origin/pkg/api"
1716
cmdutil "github.com/openshift/origin/pkg/cmd/util"
@@ -21,7 +20,7 @@ import (
2120
// ValidateRoute tests if required fields in the route are set.
2221
func ValidateRoute(route *routeapi.Route) field.ErrorList {
2322
//ensure meta is set properly
24-
result := kval.ValidateObjectMeta(&route.ObjectMeta, true, oapi.GetNameValidationFunc(kval.ValidatePodName), field.NewPath("metadata"))
23+
result := validation.ValidateObjectMeta(&route.ObjectMeta, true, oapi.GetNameValidationFunc(validation.ValidatePodName), field.NewPath("metadata"))
2524

2625
specPath := field.NewPath("spec")
2726

@@ -94,7 +93,6 @@ func ValidateRoute(route *routeapi.Route) field.ErrorList {
9493

9594
func ValidateRouteUpdate(route *routeapi.Route, older *routeapi.Route) field.ErrorList {
9695
allErrs := validation.ValidateObjectMetaUpdate(&route.ObjectMeta, &older.ObjectMeta, field.NewPath("metadata"))
97-
allErrs = append(allErrs, validation.ValidateImmutableField(route.Spec.Host, older.Spec.Host, field.NewPath("spec", "host"))...)
9896
allErrs = append(allErrs, validation.ValidateImmutableField(route.Spec.WildcardPolicy, older.Spec.WildcardPolicy, field.NewPath("spec", "wildcardPolicy"))...)
9997
allErrs = append(allErrs, ValidateRoute(route)...)
10098
return allErrs

pkg/route/api/validation/validation_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1113,7 +1113,7 @@ func TestValidateRouteUpdate(t *testing.T) {
11131113
},
11141114
},
11151115
change: func(route *api.Route) { route.Spec.Host = "" },
1116-
expectedErrors: 1,
1116+
expectedErrors: 0, // now controlled by rbac
11171117
},
11181118
{
11191119
route: &api.Route{
@@ -1131,7 +1131,7 @@ func TestValidateRouteUpdate(t *testing.T) {
11311131
},
11321132
},
11331133
change: func(route *api.Route) { route.Spec.Host = "other" },
1134-
expectedErrors: 1,
1134+
expectedErrors: 0, // now controlled by rbac
11351135
},
11361136
{
11371137
route: &api.Route{

pkg/route/registry/route/strategy.go

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111
"k8s.io/apiserver/pkg/storage"
1212
"k8s.io/apiserver/pkg/storage/names"
1313
kapi "k8s.io/kubernetes/pkg/api"
14+
kvalidation "k8s.io/kubernetes/pkg/api/validation"
1415

1516
authorizationapi "github.com/openshift/origin/pkg/authorization/api"
1617
"github.com/openshift/origin/pkg/route"
@@ -128,10 +129,40 @@ func (routeStrategy) AllowCreateOnUpdate() bool {
128129
func (routeStrategy) Canonicalize(obj runtime.Object) {
129130
}
130131

131-
func (routeStrategy) ValidateUpdate(ctx apirequest.Context, obj, old runtime.Object) field.ErrorList {
132+
func (s routeStrategy) ValidateUpdate(ctx apirequest.Context, obj, old runtime.Object) field.ErrorList {
132133
oldRoute := old.(*api.Route)
133134
objRoute := obj.(*api.Route)
134-
return validation.ValidateRouteUpdate(objRoute, oldRoute)
135+
errs := s.validateHostUpdate(ctx, objRoute, oldRoute)
136+
errs = append(errs, validation.ValidateRouteUpdate(objRoute, oldRoute)...)
137+
return errs
138+
}
139+
140+
func (s routeStrategy) validateHostUpdate(ctx apirequest.Context, route, older *api.Route) field.ErrorList {
141+
if route.Spec.Host == older.Spec.Host {
142+
return nil
143+
}
144+
user, ok := apirequest.UserFrom(ctx)
145+
if !ok {
146+
return field.ErrorList{field.InternalError(field.NewPath("spec", "host"), fmt.Errorf("unable to verify host field can be changed"))}
147+
}
148+
res, err := s.sarClient.CreateSubjectAccessReview(
149+
ctx,
150+
&authorizationapi.SubjectAccessReview{
151+
User: user.GetName(),
152+
Action: authorizationapi.Action{
153+
Verb: "update",
154+
Group: "route.openshift.io",
155+
Resource: "routes/custom-host",
156+
},
157+
},
158+
)
159+
if err != nil {
160+
return field.ErrorList{field.InternalError(field.NewPath("spec", "host"), err)}
161+
}
162+
if !res.Allowed {
163+
return kvalidation.ValidateImmutableField(route.Spec.Host, older.Spec.Host, field.NewPath("spec", "host"))
164+
}
165+
return nil
135166
}
136167

137168
func (routeStrategy) AllowUnconditionalUpdate() bool {

pkg/route/registry/route/strategy_test.go

Lines changed: 46 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55

66
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
77
"k8s.io/apimachinery/pkg/types"
8+
"k8s.io/apimachinery/pkg/util/validation/field"
89
"k8s.io/apiserver/pkg/authentication/user"
910
apirequest "k8s.io/apiserver/pkg/endpoints/request"
1011
kapi "k8s.io/kubernetes/pkg/api"
@@ -71,6 +72,7 @@ func TestHostWithWildcardPolicies(t *testing.T) {
7172
tests := []struct {
7273
name string
7374
host string
75+
oldHost string
7476
wildcardPolicy api.WildcardPolicyType
7577
expected string
7678
errs int
@@ -128,9 +130,30 @@ func TestHostWithWildcardPolicies(t *testing.T) {
128130
wildcardPolicy: api.WildcardPolicyNone,
129131
allow: false,
130132
},
133+
{
134+
name: "update-changed-host-denied",
135+
host: "new.host",
136+
expected: "new.host",
137+
oldHost: "original.host",
138+
wildcardPolicy: api.WildcardPolicyNone,
139+
allow: false,
140+
errs: 1,
141+
},
142+
{
143+
name: "update-changed-host-allowed",
144+
host: "new.host",
145+
expected: "new.host",
146+
oldHost: "original.host",
147+
wildcardPolicy: api.WildcardPolicyNone,
148+
allow: true,
149+
errs: 0,
150+
},
131151
}
132152

133153
for _, tc := range tests {
154+
sar := &testSAR{allow: tc.allow}
155+
strategy := NewStrategy(testAllocator{}, sar)
156+
134157
route := &api.Route{
135158
ObjectMeta: metav1.ObjectMeta{
136159
Namespace: "wildcard",
@@ -147,10 +170,30 @@ func TestHostWithWildcardPolicies(t *testing.T) {
147170
},
148171
},
149172
}
150-
sar := &testSAR{allow: tc.allow}
151-
strategy := NewStrategy(testAllocator{}, sar)
152173

153-
errs := strategy.Validate(ctx, route)
174+
var errs field.ErrorList
175+
if len(tc.oldHost) > 0 {
176+
oldRoute := &api.Route{
177+
ObjectMeta: metav1.ObjectMeta{
178+
Namespace: "wildcard",
179+
Name: tc.name,
180+
UID: types.UID("wild"),
181+
ResourceVersion: "1",
182+
},
183+
Spec: api.RouteSpec{
184+
Host: tc.oldHost,
185+
WildcardPolicy: tc.wildcardPolicy,
186+
To: api.RouteTargetReference{
187+
Name: "test",
188+
Kind: "Service",
189+
},
190+
},
191+
}
192+
errs = strategy.ValidateUpdate(ctx, route, oldRoute)
193+
} else {
194+
errs = strategy.Validate(ctx, route)
195+
}
196+
154197
if route.Spec.Host != tc.expected {
155198
t.Fatalf("test case %s expected host %s, got %s", tc.name, tc.expected, route.Spec.Host)
156199
}

0 commit comments

Comments
 (0)