mirror of https://github.com/k3s-io/k3s
Fix 401/403 apiserver errors do not return 'Status' objects
parent
ee4f635d61
commit
3d6479f721
|
@ -23,6 +23,8 @@ go_test(
|
||||||
deps = [
|
deps = [
|
||||||
"//vendor/k8s.io/api/authentication/v1:go_default_library",
|
"//vendor/k8s.io/api/authentication/v1:go_default_library",
|
||||||
"//vendor/k8s.io/api/batch/v1:go_default_library",
|
"//vendor/k8s.io/api/batch/v1:go_default_library",
|
||||||
|
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
|
||||||
|
"//vendor/k8s.io/apimachinery/pkg/runtime/serializer:go_default_library",
|
||||||
"//vendor/k8s.io/apimachinery/pkg/util/sets:go_default_library",
|
"//vendor/k8s.io/apimachinery/pkg/util/sets:go_default_library",
|
||||||
"//vendor/k8s.io/apimachinery/pkg/util/wait:go_default_library",
|
"//vendor/k8s.io/apimachinery/pkg/util/wait:go_default_library",
|
||||||
"//vendor/k8s.io/apiserver/pkg/apis/audit:go_default_library",
|
"//vendor/k8s.io/apiserver/pkg/apis/audit:go_default_library",
|
||||||
|
@ -54,7 +56,10 @@ go_library(
|
||||||
"//vendor/github.com/prometheus/client_golang/prometheus:go_default_library",
|
"//vendor/github.com/prometheus/client_golang/prometheus:go_default_library",
|
||||||
"//vendor/k8s.io/api/authentication/v1:go_default_library",
|
"//vendor/k8s.io/api/authentication/v1:go_default_library",
|
||||||
"//vendor/k8s.io/api/core/v1:go_default_library",
|
"//vendor/k8s.io/api/core/v1:go_default_library",
|
||||||
|
"//vendor/k8s.io/apimachinery/pkg/api/errors:go_default_library",
|
||||||
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||||
|
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
|
||||||
|
"//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
|
||||||
"//vendor/k8s.io/apimachinery/pkg/util/net:go_default_library",
|
"//vendor/k8s.io/apimachinery/pkg/util/net:go_default_library",
|
||||||
"//vendor/k8s.io/apimachinery/pkg/util/runtime:go_default_library",
|
"//vendor/k8s.io/apimachinery/pkg/util/runtime:go_default_library",
|
||||||
"//vendor/k8s.io/apiserver/pkg/apis/audit:go_default_library",
|
"//vendor/k8s.io/apiserver/pkg/apis/audit:go_default_library",
|
||||||
|
|
|
@ -17,13 +17,19 @@ limitations under the License.
|
||||||
package filters
|
package filters
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/golang/glog"
|
"github.com/golang/glog"
|
||||||
"github.com/prometheus/client_golang/prometheus"
|
"github.com/prometheus/client_golang/prometheus"
|
||||||
|
|
||||||
|
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||||
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||||
"k8s.io/apiserver/pkg/authentication/authenticator"
|
"k8s.io/apiserver/pkg/authentication/authenticator"
|
||||||
|
"k8s.io/apiserver/pkg/endpoints/handlers/responsewriters"
|
||||||
|
"k8s.io/apiserver/pkg/endpoints/request"
|
||||||
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
|
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -76,22 +82,25 @@ func WithAuthentication(handler http.Handler, mapper genericapirequest.RequestCo
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
func Unauthorized(supportsBasicAuth bool) http.HandlerFunc {
|
func Unauthorized(requestContextMapper request.RequestContextMapper, s runtime.NegotiatedSerializer, supportsBasicAuth bool) http.Handler {
|
||||||
if supportsBasicAuth {
|
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
||||||
return unauthorizedBasicAuth
|
if supportsBasicAuth {
|
||||||
}
|
w.Header().Set("WWW-Authenticate", `Basic realm="kubernetes-master"`)
|
||||||
return unauthorized
|
}
|
||||||
}
|
ctx, ok := requestContextMapper.Get(req)
|
||||||
|
if !ok {
|
||||||
|
responsewriters.InternalError(w, req, errors.New("no context found for request"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
requestInfo, found := request.RequestInfoFrom(ctx)
|
||||||
|
if !found {
|
||||||
|
responsewriters.InternalError(w, req, errors.New("no RequestInfo found in the context"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// unauthorizedBasicAuth serves an unauthorized message to clients.
|
gv := schema.GroupVersion{Group: requestInfo.APIGroup, Version: requestInfo.APIVersion}
|
||||||
func unauthorizedBasicAuth(w http.ResponseWriter, req *http.Request) {
|
responsewriters.ErrorNegotiated(ctx, apierrors.NewUnauthorized("Unauthorized"), s, gv, w, req)
|
||||||
w.Header().Set("WWW-Authenticate", `Basic realm="kubernetes-master"`)
|
})
|
||||||
http.Error(w, "Unauthorized", http.StatusUnauthorized)
|
|
||||||
}
|
|
||||||
|
|
||||||
// unauthorized serves an unauthorized message to clients.
|
|
||||||
func unauthorized(w http.ResponseWriter, req *http.Request) {
|
|
||||||
http.Error(w, "Unauthorized", http.StatusUnauthorized)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// compressUsername maps all possible usernames onto a small set of categories
|
// compressUsername maps all possible usernames onto a small set of categories
|
||||||
|
|
|
@ -22,13 +22,14 @@ import (
|
||||||
|
|
||||||
"github.com/golang/glog"
|
"github.com/golang/glog"
|
||||||
|
|
||||||
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
"k8s.io/apiserver/pkg/authorization/authorizer"
|
"k8s.io/apiserver/pkg/authorization/authorizer"
|
||||||
"k8s.io/apiserver/pkg/endpoints/handlers/responsewriters"
|
"k8s.io/apiserver/pkg/endpoints/handlers/responsewriters"
|
||||||
"k8s.io/apiserver/pkg/endpoints/request"
|
"k8s.io/apiserver/pkg/endpoints/request"
|
||||||
)
|
)
|
||||||
|
|
||||||
// WithAuthorizationCheck passes all authorized requests on to handler, and returns a forbidden error otherwise.
|
// WithAuthorizationCheck passes all authorized requests on to handler, and returns a forbidden error otherwise.
|
||||||
func WithAuthorization(handler http.Handler, requestContextMapper request.RequestContextMapper, a authorizer.Authorizer) http.Handler {
|
func WithAuthorization(handler http.Handler, requestContextMapper request.RequestContextMapper, a authorizer.Authorizer, s runtime.NegotiatedSerializer) http.Handler {
|
||||||
if a == nil {
|
if a == nil {
|
||||||
glog.Warningf("Authorization is disabled")
|
glog.Warningf("Authorization is disabled")
|
||||||
return handler
|
return handler
|
||||||
|
@ -56,7 +57,7 @@ func WithAuthorization(handler http.Handler, requestContextMapper request.Reques
|
||||||
}
|
}
|
||||||
|
|
||||||
glog.V(4).Infof("Forbidden: %#v, Reason: %q", req.RequestURI, reason)
|
glog.V(4).Infof("Forbidden: %#v, Reason: %q", req.RequestURI, reason)
|
||||||
responsewriters.Forbidden(attributes, w, req, reason)
|
responsewriters.Forbidden(ctx, attributes, w, req, reason, s)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -26,6 +26,7 @@ import (
|
||||||
|
|
||||||
authenticationv1 "k8s.io/api/authentication/v1"
|
authenticationv1 "k8s.io/api/authentication/v1"
|
||||||
"k8s.io/api/core/v1"
|
"k8s.io/api/core/v1"
|
||||||
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
"k8s.io/apiserver/pkg/authentication/serviceaccount"
|
"k8s.io/apiserver/pkg/authentication/serviceaccount"
|
||||||
"k8s.io/apiserver/pkg/authentication/user"
|
"k8s.io/apiserver/pkg/authentication/user"
|
||||||
"k8s.io/apiserver/pkg/authorization/authorizer"
|
"k8s.io/apiserver/pkg/authorization/authorizer"
|
||||||
|
@ -35,7 +36,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
// WithImpersonation is a filter that will inspect and check requests that attempt to change the user.Info for their requests
|
// WithImpersonation is a filter that will inspect and check requests that attempt to change the user.Info for their requests
|
||||||
func WithImpersonation(handler http.Handler, requestContextMapper request.RequestContextMapper, a authorizer.Authorizer) http.Handler {
|
func WithImpersonation(handler http.Handler, requestContextMapper request.RequestContextMapper, a authorizer.Authorizer, s runtime.NegotiatedSerializer) http.Handler {
|
||||||
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
||||||
impersonationRequests, err := buildImpersonationRequests(req.Header)
|
impersonationRequests, err := buildImpersonationRequests(req.Header)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -104,14 +105,14 @@ func WithImpersonation(handler http.Handler, requestContextMapper request.Reques
|
||||||
|
|
||||||
default:
|
default:
|
||||||
glog.V(4).Infof("unknown impersonation request type: %v", impersonationRequest)
|
glog.V(4).Infof("unknown impersonation request type: %v", impersonationRequest)
|
||||||
responsewriters.Forbidden(actingAsAttributes, w, req, fmt.Sprintf("unknown impersonation request type: %v", impersonationRequest))
|
responsewriters.Forbidden(ctx, actingAsAttributes, w, req, fmt.Sprintf("unknown impersonation request type: %v", impersonationRequest), s)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
allowed, reason, err := a.Authorize(actingAsAttributes)
|
allowed, reason, err := a.Authorize(actingAsAttributes)
|
||||||
if err != nil || !allowed {
|
if err != nil || !allowed {
|
||||||
glog.V(4).Infof("Forbidden: %#v, Reason: %s, Error: %v", req.RequestURI, reason, err)
|
glog.V(4).Infof("Forbidden: %#v, Reason: %s, Error: %v", req.RequestURI, reason, err)
|
||||||
responsewriters.Forbidden(actingAsAttributes, w, req, reason)
|
responsewriters.Forbidden(ctx, actingAsAttributes, w, req, reason, s)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,6 +26,8 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
authenticationapi "k8s.io/api/authentication/v1"
|
authenticationapi "k8s.io/api/authentication/v1"
|
||||||
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
|
serializer "k8s.io/apimachinery/pkg/runtime/serializer"
|
||||||
"k8s.io/apiserver/pkg/authentication/user"
|
"k8s.io/apiserver/pkg/authentication/user"
|
||||||
"k8s.io/apiserver/pkg/authorization/authorizer"
|
"k8s.io/apiserver/pkg/authorization/authorizer"
|
||||||
"k8s.io/apiserver/pkg/endpoints/request"
|
"k8s.io/apiserver/pkg/endpoints/request"
|
||||||
|
@ -356,7 +358,7 @@ func TestImpersonationFilter(t *testing.T) {
|
||||||
|
|
||||||
delegate.ServeHTTP(w, req)
|
delegate.ServeHTTP(w, req)
|
||||||
})
|
})
|
||||||
}(WithImpersonation(doNothingHandler, requestContextMapper, impersonateAuthorizer{}))
|
}(WithImpersonation(doNothingHandler, requestContextMapper, impersonateAuthorizer{}, serializer.NewCodecFactory(runtime.NewScheme())))
|
||||||
handler = request.WithRequestContext(handler, requestContextMapper)
|
handler = request.WithRequestContext(handler, requestContextMapper)
|
||||||
|
|
||||||
server := httptest.NewServer(handler)
|
server := httptest.NewServer(handler)
|
||||||
|
|
|
@ -19,9 +19,12 @@ go_test(
|
||||||
deps = [
|
deps = [
|
||||||
"//vendor/k8s.io/apimachinery/pkg/api/errors:go_default_library",
|
"//vendor/k8s.io/apimachinery/pkg/api/errors:go_default_library",
|
||||||
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||||
|
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
|
||||||
"//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
|
"//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
|
||||||
|
"//vendor/k8s.io/apimachinery/pkg/runtime/serializer:go_default_library",
|
||||||
"//vendor/k8s.io/apiserver/pkg/authentication/user:go_default_library",
|
"//vendor/k8s.io/apiserver/pkg/authentication/user:go_default_library",
|
||||||
"//vendor/k8s.io/apiserver/pkg/authorization/authorizer:go_default_library",
|
"//vendor/k8s.io/apiserver/pkg/authorization/authorizer:go_default_library",
|
||||||
|
"//vendor/k8s.io/apiserver/pkg/endpoints/request:go_default_library",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -35,6 +38,7 @@ go_library(
|
||||||
],
|
],
|
||||||
tags = ["automanaged"],
|
tags = ["automanaged"],
|
||||||
deps = [
|
deps = [
|
||||||
|
"//vendor/k8s.io/apimachinery/pkg/api/errors:go_default_library",
|
||||||
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||||
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
|
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
|
||||||
"//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
|
"//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
|
||||||
|
|
|
@ -21,8 +21,12 @@ import (
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"k8s.io/apimachinery/pkg/util/runtime"
|
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||||
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||||
|
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
|
||||||
"k8s.io/apiserver/pkg/authorization/authorizer"
|
"k8s.io/apiserver/pkg/authorization/authorizer"
|
||||||
|
"k8s.io/apiserver/pkg/endpoints/request"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Avoid emitting errors that look like valid HTML. Quotes are okay.
|
// Avoid emitting errors that look like valid HTML. Quotes are okay.
|
||||||
|
@ -37,17 +41,20 @@ func BadGatewayError(w http.ResponseWriter, req *http.Request) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Forbidden renders a simple forbidden error
|
// Forbidden renders a simple forbidden error
|
||||||
func Forbidden(attributes authorizer.Attributes, w http.ResponseWriter, req *http.Request, reason string) {
|
func Forbidden(ctx request.Context, attributes authorizer.Attributes, w http.ResponseWriter, req *http.Request, reason string, s runtime.NegotiatedSerializer) {
|
||||||
msg := sanitizer.Replace(forbiddenMessage(attributes))
|
msg := sanitizer.Replace(forbiddenMessage(attributes))
|
||||||
w.Header().Set("Content-Type", "text/plain")
|
|
||||||
w.Header().Set("X-Content-Type-Options", "nosniff")
|
w.Header().Set("X-Content-Type-Options", "nosniff")
|
||||||
w.WriteHeader(http.StatusForbidden)
|
w.WriteHeader(http.StatusForbidden)
|
||||||
|
|
||||||
|
var errMsg string
|
||||||
if len(reason) == 0 {
|
if len(reason) == 0 {
|
||||||
fmt.Fprintf(w, "%s", msg)
|
errMsg = fmt.Sprintf("%s", msg)
|
||||||
} else {
|
} else {
|
||||||
fmt.Fprintf(w, "%s: %q", msg, reason)
|
errMsg = fmt.Sprintf("%s: %q", msg, reason)
|
||||||
}
|
}
|
||||||
|
gv := schema.GroupVersion{Group: attributes.GetAPIGroup(), Version: attributes.GetAPIVersion()}
|
||||||
|
gr := schema.GroupResource{Group: attributes.GetAPIGroup(), Resource: attributes.GetResource()}
|
||||||
|
ErrorNegotiated(ctx, apierrors.NewForbidden(gr, attributes.GetName(), fmt.Errorf(errMsg)), s, gv, w, req)
|
||||||
}
|
}
|
||||||
|
|
||||||
func forbiddenMessage(attributes authorizer.Attributes) string {
|
func forbiddenMessage(attributes authorizer.Attributes) string {
|
||||||
|
@ -81,7 +88,7 @@ func InternalError(w http.ResponseWriter, req *http.Request, err error) {
|
||||||
w.Header().Set("X-Content-Type-Options", "nosniff")
|
w.Header().Set("X-Content-Type-Options", "nosniff")
|
||||||
w.WriteHeader(http.StatusInternalServerError)
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
fmt.Fprintf(w, "Internal Server Error: %q: %v", sanitizer.Replace(req.RequestURI), err)
|
fmt.Fprintf(w, "Internal Server Error: %q: %v", sanitizer.Replace(req.RequestURI), err)
|
||||||
runtime.HandleError(err)
|
utilruntime.HandleError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NotFound renders a simple not found error.
|
// NotFound renders a simple not found error.
|
||||||
|
|
|
@ -22,8 +22,11 @@ import (
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
|
"k8s.io/apimachinery/pkg/runtime/serializer"
|
||||||
"k8s.io/apiserver/pkg/authentication/user"
|
"k8s.io/apiserver/pkg/authentication/user"
|
||||||
"k8s.io/apiserver/pkg/authorization/authorizer"
|
"k8s.io/apiserver/pkg/authorization/authorizer"
|
||||||
|
"k8s.io/apiserver/pkg/endpoints/request"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestErrors(t *testing.T) {
|
func TestErrors(t *testing.T) {
|
||||||
|
@ -64,18 +67,20 @@ func TestForbidden(t *testing.T) {
|
||||||
attributes authorizer.Attributes
|
attributes authorizer.Attributes
|
||||||
reason string
|
reason string
|
||||||
}{
|
}{
|
||||||
{`User "NAME" cannot GET path "/whatever".`,
|
{`{"metadata":{},"status":"Failure","message":" \"\" is forbidden: User \"NAME\" cannot GET path \"/whatever\".","reason":"Forbidden","details":{},"code":403}
|
||||||
authorizer.AttributesRecord{User: u, Verb: "GET", Path: "/whatever"}, ""},
|
`, authorizer.AttributesRecord{User: u, Verb: "GET", Path: "/whatever"}, ""},
|
||||||
{`User "NAME" cannot GET path "/<script>".`,
|
{`{"metadata":{},"status":"Failure","message":" \"\" is forbidden: User \"NAME\" cannot GET path \"/\u0026lt;script\u0026gt;\".","reason":"Forbidden","details":{},"code":403}
|
||||||
authorizer.AttributesRecord{User: u, Verb: "GET", Path: "/<script>"}, ""},
|
`, authorizer.AttributesRecord{User: u, Verb: "GET", Path: "/<script>"}, ""},
|
||||||
{`User "NAME" cannot GET pod at the cluster scope.`,
|
{`{"metadata":{},"status":"Failure","message":"pod \"\" is forbidden: User \"NAME\" cannot GET pod at the cluster scope.","reason":"Forbidden","details":{"kind":"pod"},"code":403}
|
||||||
authorizer.AttributesRecord{User: u, Verb: "GET", Resource: "pod", ResourceRequest: true}, ""},
|
`, authorizer.AttributesRecord{User: u, Verb: "GET", Resource: "pod", ResourceRequest: true}, ""},
|
||||||
{`User "NAME" cannot GET pod.v2/quota in the namespace "test".`,
|
{`{"metadata":{},"status":"Failure","message":"pod.v2 \"\" is forbidden: User \"NAME\" cannot GET pod.v2/quota in the namespace \"test\".","reason":"Forbidden","details":{"group":"v2","kind":"pod"},"code":403}
|
||||||
authorizer.AttributesRecord{User: u, Verb: "GET", Namespace: "test", APIGroup: "v2", Resource: "pod", Subresource: "quota", ResourceRequest: true}, ""},
|
`, authorizer.AttributesRecord{User: u, Verb: "GET", Namespace: "test", APIGroup: "v2", Resource: "pod", Subresource: "quota", ResourceRequest: true}, ""},
|
||||||
}
|
}
|
||||||
for _, test := range cases {
|
for _, test := range cases {
|
||||||
observer := httptest.NewRecorder()
|
observer := httptest.NewRecorder()
|
||||||
Forbidden(test.attributes, observer, &http.Request{}, test.reason)
|
scheme := runtime.NewScheme()
|
||||||
|
negotiatedSerializer := serializer.DirectCodecFactory{CodecFactory: serializer.NewCodecFactory(scheme)}
|
||||||
|
Forbidden(request.NewDefaultContext(), test.attributes, observer, &http.Request{}, test.reason, negotiatedSerializer)
|
||||||
result := string(observer.Body.Bytes())
|
result := string(observer.Body.Bytes())
|
||||||
if result != test.expected {
|
if result != test.expected {
|
||||||
t.Errorf("Forbidden(%#v...) != %#v, got %#v", test.attributes, test.expected, result)
|
t.Errorf("Forbidden(%#v...) != %#v, got %#v", test.attributes, test.expected, result)
|
||||||
|
|
|
@ -467,14 +467,14 @@ func (c completedConfig) New(name string, delegationTarget DelegationTarget) (*G
|
||||||
}
|
}
|
||||||
|
|
||||||
func DefaultBuildHandlerChain(apiHandler http.Handler, c *Config) http.Handler {
|
func DefaultBuildHandlerChain(apiHandler http.Handler, c *Config) http.Handler {
|
||||||
handler := genericapifilters.WithAuthorization(apiHandler, c.RequestContextMapper, c.Authorizer)
|
handler := genericapifilters.WithAuthorization(apiHandler, c.RequestContextMapper, c.Authorizer, c.Serializer)
|
||||||
handler = genericapifilters.WithImpersonation(handler, c.RequestContextMapper, c.Authorizer)
|
handler = genericapifilters.WithImpersonation(handler, c.RequestContextMapper, c.Authorizer, c.Serializer)
|
||||||
if utilfeature.DefaultFeatureGate.Enabled(features.AdvancedAuditing) {
|
if utilfeature.DefaultFeatureGate.Enabled(features.AdvancedAuditing) {
|
||||||
handler = genericapifilters.WithAudit(handler, c.RequestContextMapper, c.AuditBackend, c.AuditPolicyChecker, c.LongRunningFunc)
|
handler = genericapifilters.WithAudit(handler, c.RequestContextMapper, c.AuditBackend, c.AuditPolicyChecker, c.LongRunningFunc)
|
||||||
} else {
|
} else {
|
||||||
handler = genericapifilters.WithLegacyAudit(handler, c.RequestContextMapper, c.LegacyAuditWriter)
|
handler = genericapifilters.WithLegacyAudit(handler, c.RequestContextMapper, c.LegacyAuditWriter)
|
||||||
}
|
}
|
||||||
handler = genericapifilters.WithAuthentication(handler, c.RequestContextMapper, c.Authenticator, genericapifilters.Unauthorized(c.SupportsBasicAuth))
|
handler = genericapifilters.WithAuthentication(handler, c.RequestContextMapper, c.Authenticator, genericapifilters.Unauthorized(c.RequestContextMapper, c.Serializer, c.SupportsBasicAuth))
|
||||||
handler = genericfilters.WithCORS(handler, c.CorsAllowedOriginList, nil, nil, nil, "true")
|
handler = genericfilters.WithCORS(handler, c.CorsAllowedOriginList, nil, nil, nil, "true")
|
||||||
handler = genericfilters.WithPanicRecovery(handler)
|
handler = genericfilters.WithPanicRecovery(handler)
|
||||||
handler = genericfilters.WithTimeoutForNonLongRunningRequests(handler, c.RequestContextMapper, c.LongRunningFunc)
|
handler = genericfilters.WithTimeoutForNonLongRunningRequests(handler, c.RequestContextMapper, c.LongRunningFunc)
|
||||||
|
|
|
@ -19,6 +19,7 @@ go_test(
|
||||||
"//pkg/api/testapi:go_default_library",
|
"//pkg/api/testapi:go_default_library",
|
||||||
"//pkg/client/clientset_generated/clientset/typed/core/v1:go_default_library",
|
"//pkg/client/clientset_generated/clientset/typed/core/v1:go_default_library",
|
||||||
"//pkg/client/clientset_generated/internalclientset:go_default_library",
|
"//pkg/client/clientset_generated/internalclientset:go_default_library",
|
||||||
|
"//pkg/master:go_default_library",
|
||||||
"//test/integration:go_default_library",
|
"//test/integration:go_default_library",
|
||||||
"//test/integration/framework:go_default_library",
|
"//test/integration/framework:go_default_library",
|
||||||
"//vendor/github.com/ghodss/yaml:go_default_library",
|
"//vendor/github.com/ghodss/yaml:go_default_library",
|
||||||
|
@ -26,6 +27,12 @@ go_test(
|
||||||
"//vendor/k8s.io/apimachinery/pkg/api/errors:go_default_library",
|
"//vendor/k8s.io/apimachinery/pkg/api/errors:go_default_library",
|
||||||
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||||
"//vendor/k8s.io/apimachinery/pkg/util/wait:go_default_library",
|
"//vendor/k8s.io/apimachinery/pkg/util/wait:go_default_library",
|
||||||
|
"//vendor/k8s.io/apiserver/pkg/authentication/group:go_default_library",
|
||||||
|
"//vendor/k8s.io/apiserver/pkg/authentication/request/bearertoken:go_default_library",
|
||||||
|
"//vendor/k8s.io/apiserver/pkg/authentication/user:go_default_library",
|
||||||
|
"//vendor/k8s.io/apiserver/pkg/authorization/authorizer:go_default_library",
|
||||||
|
"//vendor/k8s.io/apiserver/pkg/authorization/authorizerfactory:go_default_library",
|
||||||
|
"//vendor/k8s.io/apiserver/plugin/pkg/authenticator/token/tokentest:go_default_library",
|
||||||
"//vendor/k8s.io/client-go/rest:go_default_library",
|
"//vendor/k8s.io/client-go/rest:go_default_library",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
|
@ -35,15 +35,36 @@ import (
|
||||||
"k8s.io/apimachinery/pkg/api/errors"
|
"k8s.io/apimachinery/pkg/api/errors"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/apimachinery/pkg/util/wait"
|
"k8s.io/apimachinery/pkg/util/wait"
|
||||||
|
"k8s.io/apiserver/pkg/authentication/group"
|
||||||
|
"k8s.io/apiserver/pkg/authentication/request/bearertoken"
|
||||||
|
"k8s.io/apiserver/pkg/authentication/user"
|
||||||
|
"k8s.io/apiserver/pkg/authorization/authorizer"
|
||||||
|
"k8s.io/apiserver/pkg/authorization/authorizerfactory"
|
||||||
|
"k8s.io/apiserver/plugin/pkg/authenticator/token/tokentest"
|
||||||
restclient "k8s.io/client-go/rest"
|
restclient "k8s.io/client-go/rest"
|
||||||
"k8s.io/kubernetes/pkg/api"
|
"k8s.io/kubernetes/pkg/api"
|
||||||
"k8s.io/kubernetes/pkg/api/testapi"
|
"k8s.io/kubernetes/pkg/api/testapi"
|
||||||
clienttypedv1 "k8s.io/kubernetes/pkg/client/clientset_generated/clientset/typed/core/v1"
|
clienttypedv1 "k8s.io/kubernetes/pkg/client/clientset_generated/clientset/typed/core/v1"
|
||||||
clientset "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
|
clientset "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
|
||||||
|
"k8s.io/kubernetes/pkg/master"
|
||||||
"k8s.io/kubernetes/test/integration"
|
"k8s.io/kubernetes/test/integration"
|
||||||
"k8s.io/kubernetes/test/integration/framework"
|
"k8s.io/kubernetes/test/integration/framework"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
AliceToken string = "abc123" // username: alice. Present in token file.
|
||||||
|
BobToken string = "xyz987" // username: bob. Present in token file.
|
||||||
|
)
|
||||||
|
|
||||||
|
type allowAliceAuthorizer struct{}
|
||||||
|
|
||||||
|
func (allowAliceAuthorizer) Authorize(a authorizer.Attributes) (bool, string, error) {
|
||||||
|
if a.GetUser() != nil && a.GetUser().GetName() == "alice" {
|
||||||
|
return true, "", nil
|
||||||
|
}
|
||||||
|
return false, "I can't allow that. Go ask alice.", nil
|
||||||
|
}
|
||||||
|
|
||||||
func testPrefix(t *testing.T, prefix string) {
|
func testPrefix(t *testing.T, prefix string) {
|
||||||
_, s, closeFn := framework.RunAMaster(nil)
|
_, s, closeFn := framework.RunAMaster(nil)
|
||||||
defer closeFn()
|
defer closeFn()
|
||||||
|
@ -101,38 +122,96 @@ func TestEmptyList(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func initStatusForbiddenMasterCongfig() *master.Config {
|
||||||
|
masterConfig := framework.NewIntegrationTestMasterConfig()
|
||||||
|
masterConfig.GenericConfig.Authorizer = authorizerfactory.NewAlwaysDenyAuthorizer()
|
||||||
|
return masterConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
func initUnauthorizedMasterCongfig() *master.Config {
|
||||||
|
masterConfig := framework.NewIntegrationTestMasterConfig()
|
||||||
|
tokenAuthenticator := tokentest.New()
|
||||||
|
tokenAuthenticator.Tokens[AliceToken] = &user.DefaultInfo{Name: "alice", UID: "1"}
|
||||||
|
tokenAuthenticator.Tokens[BobToken] = &user.DefaultInfo{Name: "bob", UID: "2"}
|
||||||
|
masterConfig.GenericConfig.Authenticator = group.NewGroupAdder(bearertoken.New(tokenAuthenticator), []string{user.AllAuthenticated})
|
||||||
|
masterConfig.GenericConfig.Authorizer = allowAliceAuthorizer{}
|
||||||
|
return masterConfig
|
||||||
|
}
|
||||||
|
|
||||||
func TestStatus(t *testing.T) {
|
func TestStatus(t *testing.T) {
|
||||||
_, s, closeFn := framework.RunAMaster(nil)
|
testCases := []struct {
|
||||||
defer closeFn()
|
name string
|
||||||
|
masterConfig *master.Config
|
||||||
|
statusCode int
|
||||||
|
reqPath string
|
||||||
|
reason string
|
||||||
|
message string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "404",
|
||||||
|
masterConfig: nil,
|
||||||
|
statusCode: http.StatusNotFound,
|
||||||
|
reqPath: "/apis/batch/v1/namespaces/default/jobs/foo",
|
||||||
|
reason: "NotFound",
|
||||||
|
message: `jobs.batch "foo" not found`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "403",
|
||||||
|
masterConfig: initStatusForbiddenMasterCongfig(),
|
||||||
|
statusCode: http.StatusForbidden,
|
||||||
|
reqPath: "/apis",
|
||||||
|
reason: "Forbidden",
|
||||||
|
message: ` "" is forbidden: User "" cannot get path "/apis".: "Everything is forbidden."`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "401",
|
||||||
|
masterConfig: initUnauthorizedMasterCongfig(),
|
||||||
|
statusCode: http.StatusUnauthorized,
|
||||||
|
reqPath: "/apis",
|
||||||
|
reason: "Unauthorized",
|
||||||
|
message: `Unauthorized`,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
u := s.URL + "/apis/batch/v1/namespaces/default/jobs/foo"
|
for _, tc := range testCases {
|
||||||
resp, err := http.Get(u)
|
_, s, closeFn := framework.RunAMaster(tc.masterConfig)
|
||||||
if err != nil {
|
defer closeFn()
|
||||||
t.Fatalf("unexpected error getting %s: %v", u, err)
|
|
||||||
}
|
u := s.URL + tc.reqPath
|
||||||
if resp.StatusCode != http.StatusNotFound {
|
resp, err := http.Get(u)
|
||||||
t.Fatalf("got status %v instead of 404", resp.StatusCode)
|
if err != nil {
|
||||||
}
|
t.Fatalf("unexpected error getting %s: %v", u, err)
|
||||||
defer resp.Body.Close()
|
}
|
||||||
data, _ := ioutil.ReadAll(resp.Body)
|
if resp.StatusCode != tc.statusCode {
|
||||||
decodedData := map[string]interface{}{}
|
t.Fatalf("got status %v instead of %s", resp.StatusCode, tc.name)
|
||||||
if err := json.Unmarshal(data, &decodedData); err != nil {
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
data, _ := ioutil.ReadAll(resp.Body)
|
||||||
|
decodedData := map[string]interface{}{}
|
||||||
|
if err := json.Unmarshal(data, &decodedData); err != nil {
|
||||||
|
t.Logf("body: %s", string(data))
|
||||||
|
t.Fatalf("got error decoding data: %v", err)
|
||||||
|
}
|
||||||
t.Logf("body: %s", string(data))
|
t.Logf("body: %s", string(data))
|
||||||
t.Fatalf("got error decoding data: %v", err)
|
|
||||||
}
|
|
||||||
t.Logf("body: %s", string(data))
|
|
||||||
|
|
||||||
if got, expected := decodedData["apiVersion"], "v1"; got != expected {
|
if got, expected := decodedData["apiVersion"], "v1"; got != expected {
|
||||||
t.Errorf("unexpected apiVersion %q, expected %q", got, expected)
|
t.Errorf("unexpected apiVersion %q, expected %q", got, expected)
|
||||||
}
|
}
|
||||||
if got, expected := decodedData["kind"], "Status"; got != expected {
|
if got, expected := decodedData["kind"], "Status"; got != expected {
|
||||||
t.Errorf("unexpected kind %q, expected %q", got, expected)
|
t.Errorf("unexpected kind %q, expected %q", got, expected)
|
||||||
}
|
}
|
||||||
if got, expected := decodedData["status"], "Failure"; got != expected {
|
if got, expected := decodedData["status"], "Failure"; got != expected {
|
||||||
t.Errorf("unexpected status %q, expected %q", got, expected)
|
t.Errorf("unexpected status %q, expected %q", got, expected)
|
||||||
}
|
}
|
||||||
if got, expected := decodedData["code"], float64(404); got != expected {
|
if got, expected := decodedData["code"], float64(tc.statusCode); got != expected {
|
||||||
t.Errorf("unexpected code %v, expected %v", got, expected)
|
t.Errorf("unexpected code %v, expected %v", got, expected)
|
||||||
|
}
|
||||||
|
if got, expected := decodedData["reason"], tc.reason; got != expected {
|
||||||
|
t.Errorf("unexpected reason %v, expected %v", got, expected)
|
||||||
|
}
|
||||||
|
if got, expected := decodedData["message"], tc.message; got != expected {
|
||||||
|
t.Errorf("unexpected message %v, expected %v", got, expected)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue