mirror of
				https://github.com/optim-enterprises-bv/kubernetes.git
				synced 2025-11-04 04:08:16 +00:00 
			
		
		
		
	Merge pull request #33478 from sttts/sttts-separate-apiserver-filters
Automatic merge from submit-queue Separate handler filters in pkg/apiserver into pkg/apiserver/filters ... in order to make dependencies explicit towards better composability.
This commit is contained in:
		@@ -27,7 +27,6 @@ import (
 | 
			
		||||
	"path"
 | 
			
		||||
	rt "runtime"
 | 
			
		||||
	"strconv"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"k8s.io/kubernetes/pkg/admission"
 | 
			
		||||
@@ -210,22 +209,6 @@ func logStackOnRecover(s runtime.NegotiatedSerializer, panicReason interface{},
 | 
			
		||||
	errorNegotiated(apierrors.NewGenericServerResponse(http.StatusInternalServerError, "", api.Resource(""), "", "", 0, false), s, unversioned.GroupVersion{}, w, &http.Request{Header: headers})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func InstallServiceErrorHandler(s runtime.NegotiatedSerializer, container *restful.Container) {
 | 
			
		||||
	container.ServiceErrorHandler(func(serviceErr restful.ServiceError, request *restful.Request, response *restful.Response) {
 | 
			
		||||
		serviceErrorHandler(s, serviceErr, request, response)
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func serviceErrorHandler(s runtime.NegotiatedSerializer, serviceErr restful.ServiceError, request *restful.Request, response *restful.Response) {
 | 
			
		||||
	errorNegotiated(
 | 
			
		||||
		apierrors.NewGenericServerResponse(serviceErr.Code, "", api.Resource(""), "", serviceErr.Message, 0, false),
 | 
			
		||||
		s,
 | 
			
		||||
		unversioned.GroupVersion{},
 | 
			
		||||
		response.ResponseWriter,
 | 
			
		||||
		request.Request,
 | 
			
		||||
	)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Adds a service to return the supported api versions at the legacy /api.
 | 
			
		||||
func AddApiWebService(s runtime.NegotiatedSerializer, container *restful.Container, apiPrefix string, getAPIVersionsFunc func(req *restful.Request) *unversioned.APIVersions) {
 | 
			
		||||
	// TODO: InstallREST should register each version automatically
 | 
			
		||||
@@ -501,12 +484,3 @@ func readBody(req *http.Request) ([]byte, error) {
 | 
			
		||||
	defer req.Body.Close()
 | 
			
		||||
	return ioutil.ReadAll(req.Body)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// splitPath returns the segments for a URL path.
 | 
			
		||||
func splitPath(path string) []string {
 | 
			
		||||
	path = strings.Trim(path, "/")
 | 
			
		||||
	if path == "" {
 | 
			
		||||
		return []string{}
 | 
			
		||||
	}
 | 
			
		||||
	return strings.Split(path, "/")
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -44,7 +44,6 @@ import (
 | 
			
		||||
	"k8s.io/kubernetes/pkg/labels"
 | 
			
		||||
	"k8s.io/kubernetes/pkg/runtime"
 | 
			
		||||
	"k8s.io/kubernetes/pkg/util/diff"
 | 
			
		||||
	"k8s.io/kubernetes/pkg/util/sets"
 | 
			
		||||
	"k8s.io/kubernetes/pkg/watch"
 | 
			
		||||
	"k8s.io/kubernetes/pkg/watch/versioned"
 | 
			
		||||
	"k8s.io/kubernetes/plugin/pkg/admission/admit"
 | 
			
		||||
@@ -252,10 +251,6 @@ func handleLinker(storage map[string]rest.Storage, selfLinker runtime.SelfLinker
 | 
			
		||||
	return handleInternal(storage, admissionControl, selfLinker)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func newTestRequestInfoResolver() *RequestInfoResolver {
 | 
			
		||||
	return &RequestInfoResolver{sets.NewString("api", "apis"), sets.NewString("api")}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func handleInternal(storage map[string]rest.Storage, admissionControl admission.Interface, selfLinker runtime.SelfLinker) http.Handler {
 | 
			
		||||
	container := restful.NewContainer()
 | 
			
		||||
	container.Router(restful.CurlyRouter{})
 | 
			
		||||
 
 | 
			
		||||
@@ -76,25 +76,6 @@ func notFound(w http.ResponseWriter, req *http.Request) {
 | 
			
		||||
	fmt.Fprintf(w, "Not Found: %#v", req.RequestURI)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// badGatewayError renders a simple bad gateway error.
 | 
			
		||||
func badGatewayError(w http.ResponseWriter, req *http.Request) {
 | 
			
		||||
	w.WriteHeader(http.StatusBadGateway)
 | 
			
		||||
	fmt.Fprintf(w, "Bad Gateway: %#v", req.RequestURI)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// forbidden renders a simple forbidden error
 | 
			
		||||
func forbidden(w http.ResponseWriter, req *http.Request) {
 | 
			
		||||
	w.WriteHeader(http.StatusForbidden)
 | 
			
		||||
	fmt.Fprintf(w, "Forbidden: %#v", req.RequestURI)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// internalError renders a simple internal error
 | 
			
		||||
func internalError(w http.ResponseWriter, req *http.Request, err error) {
 | 
			
		||||
	w.WriteHeader(http.StatusInternalServerError)
 | 
			
		||||
	fmt.Fprintf(w, "Internal Server Error: %#v", req.RequestURI)
 | 
			
		||||
	runtime.HandleError(err)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// errAPIPrefixNotFound indicates that a RequestInfo resolution failed because the request isn't under
 | 
			
		||||
// any known API prefixes
 | 
			
		||||
type errAPIPrefixNotFound struct {
 | 
			
		||||
 
 | 
			
		||||
@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
 | 
			
		||||
limitations under the License.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
package audit
 | 
			
		||||
package filters
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bufio"
 | 
			
		||||
@@ -27,7 +27,6 @@ import (
 | 
			
		||||
	"github.com/golang/glog"
 | 
			
		||||
	"github.com/pborman/uuid"
 | 
			
		||||
 | 
			
		||||
	"k8s.io/kubernetes/pkg/apiserver"
 | 
			
		||||
	utilnet "k8s.io/kubernetes/pkg/util/net"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
@@ -85,7 +84,7 @@ var _ http.Hijacker = &fancyResponseWriterDelegator{}
 | 
			
		||||
// 2. the response line containing:
 | 
			
		||||
//    - the unique id from 1
 | 
			
		||||
//    - response code
 | 
			
		||||
func WithAudit(handler http.Handler, attributeGetter apiserver.RequestAttributeGetter, out io.Writer) http.Handler {
 | 
			
		||||
func WithAudit(handler http.Handler, attributeGetter RequestAttributeGetter, out io.Writer) http.Handler {
 | 
			
		||||
	if out == nil {
 | 
			
		||||
		return handler
 | 
			
		||||
	}
 | 
			
		||||
@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
 | 
			
		||||
limitations under the License.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
package audit
 | 
			
		||||
package filters
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bufio"
 | 
			
		||||
@@ -85,7 +85,7 @@ func (*fakeRequestContextMapper) Update(req *http.Request, context api.Context)
 | 
			
		||||
 | 
			
		||||
func TestAudit(t *testing.T) {
 | 
			
		||||
	var buf bytes.Buffer
 | 
			
		||||
	attributeGetter := apiserver.NewRequestAttributeGetter(&fakeRequestContextMapper{},
 | 
			
		||||
	attributeGetter := NewRequestAttributeGetter(&fakeRequestContextMapper{},
 | 
			
		||||
		&apiserver.RequestInfoResolver{APIPrefixes: sets.NewString("api", "apis"), GrouplessAPIPrefixes: sets.NewString("api")})
 | 
			
		||||
	handler := WithAudit(&fakeHTTPHandler{}, attributeGetter, &buf)
 | 
			
		||||
	req, _ := http.NewRequest("GET", "/api/v1/namespaces/default/pods", nil)
 | 
			
		||||
							
								
								
									
										91
									
								
								pkg/apiserver/filters/authorization.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										91
									
								
								pkg/apiserver/filters/authorization.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,91 @@
 | 
			
		||||
/*
 | 
			
		||||
Copyright 2016 The Kubernetes Authors.
 | 
			
		||||
 | 
			
		||||
Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
you may not use this file except in compliance with the License.
 | 
			
		||||
You may obtain a copy of the License at
 | 
			
		||||
 | 
			
		||||
    http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
 | 
			
		||||
Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
See the License for the specific language governing permissions and
 | 
			
		||||
limitations under the License.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
package filters
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"net/http"
 | 
			
		||||
 | 
			
		||||
	"github.com/golang/glog"
 | 
			
		||||
 | 
			
		||||
	"k8s.io/kubernetes/pkg/api"
 | 
			
		||||
	"k8s.io/kubernetes/pkg/apiserver"
 | 
			
		||||
	"k8s.io/kubernetes/pkg/auth/authorizer"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// WithAuthorizationCheck passes all authorized requests on to handler, and returns a forbidden error otherwise.
 | 
			
		||||
func WithAuthorization(handler http.Handler, getAttribs RequestAttributeGetter, a authorizer.Authorizer) http.Handler {
 | 
			
		||||
	if a == nil {
 | 
			
		||||
		glog.Warningf("Authorization is disabled")
 | 
			
		||||
		return handler
 | 
			
		||||
	}
 | 
			
		||||
	return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
 | 
			
		||||
		authorized, reason, err := a.Authorize(getAttribs.GetAttribs(req))
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			internalError(w, req, err)
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
		if !authorized {
 | 
			
		||||
			glog.V(4).Infof("Forbidden: %#v, Reason: %s", req.RequestURI, reason)
 | 
			
		||||
			forbidden(w, req)
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
		handler.ServeHTTP(w, req)
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// RequestAttributeGetter is a function that extracts authorizer.Attributes from an http.Request
 | 
			
		||||
type RequestAttributeGetter interface {
 | 
			
		||||
	GetAttribs(req *http.Request) (attribs authorizer.Attributes)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type requestAttributeGetter struct {
 | 
			
		||||
	requestContextMapper api.RequestContextMapper
 | 
			
		||||
	requestInfoResolver  *apiserver.RequestInfoResolver
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewAttributeGetter returns an object which implements the RequestAttributeGetter interface.
 | 
			
		||||
func NewRequestAttributeGetter(requestContextMapper api.RequestContextMapper, requestInfoResolver *apiserver.RequestInfoResolver) RequestAttributeGetter {
 | 
			
		||||
	return &requestAttributeGetter{requestContextMapper, requestInfoResolver}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (r *requestAttributeGetter) GetAttribs(req *http.Request) authorizer.Attributes {
 | 
			
		||||
	attribs := authorizer.AttributesRecord{}
 | 
			
		||||
 | 
			
		||||
	ctx, ok := r.requestContextMapper.Get(req)
 | 
			
		||||
	if ok {
 | 
			
		||||
		user, ok := api.UserFrom(ctx)
 | 
			
		||||
		if ok {
 | 
			
		||||
			attribs.User = user
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	requestInfo, _ := r.requestInfoResolver.GetRequestInfo(req)
 | 
			
		||||
 | 
			
		||||
	// Start with common attributes that apply to resource and non-resource requests
 | 
			
		||||
	attribs.ResourceRequest = requestInfo.IsResourceRequest
 | 
			
		||||
	attribs.Path = requestInfo.Path
 | 
			
		||||
	attribs.Verb = requestInfo.Verb
 | 
			
		||||
 | 
			
		||||
	attribs.APIGroup = requestInfo.APIGroup
 | 
			
		||||
	attribs.APIVersion = requestInfo.APIVersion
 | 
			
		||||
	attribs.Resource = requestInfo.Resource
 | 
			
		||||
	attribs.Subresource = requestInfo.Subresource
 | 
			
		||||
	attribs.Namespace = requestInfo.Namespace
 | 
			
		||||
	attribs.Name = requestInfo.Name
 | 
			
		||||
 | 
			
		||||
	return &attribs
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										111
									
								
								pkg/apiserver/filters/authorization_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										111
									
								
								pkg/apiserver/filters/authorization_test.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,111 @@
 | 
			
		||||
/*
 | 
			
		||||
Copyright 2016 The Kubernetes Authors.
 | 
			
		||||
 | 
			
		||||
Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
you may not use this file except in compliance with the License.
 | 
			
		||||
You may obtain a copy of the License at
 | 
			
		||||
 | 
			
		||||
    http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
 | 
			
		||||
Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
See the License for the specific language governing permissions and
 | 
			
		||||
limitations under the License.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
package filters
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"reflect"
 | 
			
		||||
	"testing"
 | 
			
		||||
 | 
			
		||||
	"k8s.io/kubernetes/pkg/api"
 | 
			
		||||
	"k8s.io/kubernetes/pkg/apis/extensions"
 | 
			
		||||
	"k8s.io/kubernetes/pkg/apiserver"
 | 
			
		||||
	"k8s.io/kubernetes/pkg/auth/authorizer"
 | 
			
		||||
	"k8s.io/kubernetes/pkg/util/sets"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func TestGetAttribs(t *testing.T) {
 | 
			
		||||
	r := &requestAttributeGetter{api.NewRequestContextMapper(), &apiserver.RequestInfoResolver{APIPrefixes: sets.NewString("api", "apis"), GrouplessAPIPrefixes: sets.NewString("api")}}
 | 
			
		||||
 | 
			
		||||
	testcases := map[string]struct {
 | 
			
		||||
		Verb               string
 | 
			
		||||
		Path               string
 | 
			
		||||
		ExpectedAttributes *authorizer.AttributesRecord
 | 
			
		||||
	}{
 | 
			
		||||
		"non-resource root": {
 | 
			
		||||
			Verb: "POST",
 | 
			
		||||
			Path: "/",
 | 
			
		||||
			ExpectedAttributes: &authorizer.AttributesRecord{
 | 
			
		||||
				Verb: "post",
 | 
			
		||||
				Path: "/",
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		"non-resource api prefix": {
 | 
			
		||||
			Verb: "GET",
 | 
			
		||||
			Path: "/api/",
 | 
			
		||||
			ExpectedAttributes: &authorizer.AttributesRecord{
 | 
			
		||||
				Verb: "get",
 | 
			
		||||
				Path: "/api/",
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		"non-resource group api prefix": {
 | 
			
		||||
			Verb: "GET",
 | 
			
		||||
			Path: "/apis/extensions/",
 | 
			
		||||
			ExpectedAttributes: &authorizer.AttributesRecord{
 | 
			
		||||
				Verb: "get",
 | 
			
		||||
				Path: "/apis/extensions/",
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		"resource": {
 | 
			
		||||
			Verb: "POST",
 | 
			
		||||
			Path: "/api/v1/nodes/mynode",
 | 
			
		||||
			ExpectedAttributes: &authorizer.AttributesRecord{
 | 
			
		||||
				Verb:            "create",
 | 
			
		||||
				Path:            "/api/v1/nodes/mynode",
 | 
			
		||||
				ResourceRequest: true,
 | 
			
		||||
				Resource:        "nodes",
 | 
			
		||||
				APIVersion:      "v1",
 | 
			
		||||
				Name:            "mynode",
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		"namespaced resource": {
 | 
			
		||||
			Verb: "PUT",
 | 
			
		||||
			Path: "/api/v1/namespaces/myns/pods/mypod",
 | 
			
		||||
			ExpectedAttributes: &authorizer.AttributesRecord{
 | 
			
		||||
				Verb:            "update",
 | 
			
		||||
				Path:            "/api/v1/namespaces/myns/pods/mypod",
 | 
			
		||||
				ResourceRequest: true,
 | 
			
		||||
				Namespace:       "myns",
 | 
			
		||||
				Resource:        "pods",
 | 
			
		||||
				APIVersion:      "v1",
 | 
			
		||||
				Name:            "mypod",
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		"API group resource": {
 | 
			
		||||
			Verb: "GET",
 | 
			
		||||
			Path: "/apis/extensions/v1beta1/namespaces/myns/jobs",
 | 
			
		||||
			ExpectedAttributes: &authorizer.AttributesRecord{
 | 
			
		||||
				Verb:            "list",
 | 
			
		||||
				Path:            "/apis/extensions/v1beta1/namespaces/myns/jobs",
 | 
			
		||||
				ResourceRequest: true,
 | 
			
		||||
				APIGroup:        extensions.GroupName,
 | 
			
		||||
				APIVersion:      "v1beta1",
 | 
			
		||||
				Namespace:       "myns",
 | 
			
		||||
				Resource:        "jobs",
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for k, tc := range testcases {
 | 
			
		||||
		req, _ := http.NewRequest(tc.Verb, tc.Path, nil)
 | 
			
		||||
		attribs := r.GetAttribs(req)
 | 
			
		||||
		if !reflect.DeepEqual(attribs, tc.ExpectedAttributes) {
 | 
			
		||||
			t.Errorf("%s: expected\n\t%#v\ngot\n\t%#v", k, tc.ExpectedAttributes, attribs)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										19
									
								
								pkg/apiserver/filters/doc.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								pkg/apiserver/filters/doc.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,19 @@
 | 
			
		||||
/*
 | 
			
		||||
Copyright 2016 The Kubernetes Authors.
 | 
			
		||||
 | 
			
		||||
Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
you may not use this file except in compliance with the License.
 | 
			
		||||
You may obtain a copy of the License at
 | 
			
		||||
 | 
			
		||||
    http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
 | 
			
		||||
Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
See the License for the specific language governing permissions and
 | 
			
		||||
limitations under the License.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
// Package filters contains all the http handler chain filters which
 | 
			
		||||
// _are_ api related.
 | 
			
		||||
package filters // import "k8s.io/kubernetes/pkg/apiserver/filters"
 | 
			
		||||
							
								
								
									
										43
									
								
								pkg/apiserver/filters/errors.go
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										43
									
								
								pkg/apiserver/filters/errors.go
									
									
									
									
									
										Executable file
									
								
							@@ -0,0 +1,43 @@
 | 
			
		||||
/*
 | 
			
		||||
Copyright 2014 The Kubernetes Authors.
 | 
			
		||||
 | 
			
		||||
Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
you may not use this file except in compliance with the License.
 | 
			
		||||
You may obtain a copy of the License at
 | 
			
		||||
 | 
			
		||||
    http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
 | 
			
		||||
Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
See the License for the specific language governing permissions and
 | 
			
		||||
limitations under the License.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
package filters
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"net/http"
 | 
			
		||||
 | 
			
		||||
	"k8s.io/kubernetes/pkg/util/runtime"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// badGatewayError renders a simple bad gateway error.
 | 
			
		||||
func badGatewayError(w http.ResponseWriter, req *http.Request) {
 | 
			
		||||
	w.WriteHeader(http.StatusBadGateway)
 | 
			
		||||
	fmt.Fprintf(w, "Bad Gateway: %#v", req.RequestURI)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// forbidden renders a simple forbidden error
 | 
			
		||||
func forbidden(w http.ResponseWriter, req *http.Request) {
 | 
			
		||||
	w.WriteHeader(http.StatusForbidden)
 | 
			
		||||
	fmt.Fprintf(w, "Forbidden: %#v", req.RequestURI)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// internalError renders a simple internal error
 | 
			
		||||
func internalError(w http.ResponseWriter, req *http.Request, err error) {
 | 
			
		||||
	w.WriteHeader(http.StatusInternalServerError)
 | 
			
		||||
	fmt.Fprintf(w, "Internal Server Error: %#v", req.RequestURI)
 | 
			
		||||
	runtime.HandleError(err)
 | 
			
		||||
}
 | 
			
		||||
@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
 | 
			
		||||
limitations under the License.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
package apiserver
 | 
			
		||||
package filters
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
 | 
			
		||||
limitations under the License.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
package apiserver
 | 
			
		||||
package filters
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
@@ -264,7 +264,7 @@ func TestImpersonationFilter(t *testing.T) {
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	requestContextMapper = api.NewRequestContextMapper()
 | 
			
		||||
	requestContextMapper := api.NewRequestContextMapper()
 | 
			
		||||
	var ctx api.Context
 | 
			
		||||
	var actualUser user.Info
 | 
			
		||||
	var lock sync.Mutex
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
Copyright 2014 The Kubernetes Authors.
 | 
			
		||||
Copyright 2015 The Kubernetes Authors.
 | 
			
		||||
 | 
			
		||||
Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
you may not use this file except in compliance with the License.
 | 
			
		||||
@@ -19,156 +19,12 @@ package apiserver
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"runtime/debug"
 | 
			
		||||
	"strings"
 | 
			
		||||
 | 
			
		||||
	"github.com/golang/glog"
 | 
			
		||||
	"k8s.io/kubernetes/pkg/api"
 | 
			
		||||
	"k8s.io/kubernetes/pkg/api/errors"
 | 
			
		||||
	"k8s.io/kubernetes/pkg/auth/authorizer"
 | 
			
		||||
	"k8s.io/kubernetes/pkg/httplog"
 | 
			
		||||
	"k8s.io/kubernetes/pkg/util/runtime"
 | 
			
		||||
	"k8s.io/kubernetes/pkg/util/sets"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// specialVerbs contains just strings which are used in REST paths for special actions that don't fall under the normal
 | 
			
		||||
// CRUDdy GET/POST/PUT/DELETE actions on REST objects.
 | 
			
		||||
// TODO: find a way to keep this up to date automatically.  Maybe dynamically populate list as handlers added to
 | 
			
		||||
// master's Mux.
 | 
			
		||||
var specialVerbs = sets.NewString("proxy", "redirect", "watch")
 | 
			
		||||
 | 
			
		||||
// specialVerbsNoSubresources contains root verbs which do not allow subresources
 | 
			
		||||
var specialVerbsNoSubresources = sets.NewString("proxy", "redirect")
 | 
			
		||||
 | 
			
		||||
// namespaceSubresources contains subresources of namespace
 | 
			
		||||
// this list allows the parser to distinguish between a namespace subresource, and a namespaced resource
 | 
			
		||||
var namespaceSubresources = sets.NewString("status", "finalize")
 | 
			
		||||
 | 
			
		||||
// NamespaceSubResourcesForTest exports namespaceSubresources for testing in pkg/master/master_test.go, so we never drift
 | 
			
		||||
var NamespaceSubResourcesForTest = sets.NewString(namespaceSubresources.List()...)
 | 
			
		||||
 | 
			
		||||
// IsReadOnlyReq() is true for any (or at least many) request which has no observable
 | 
			
		||||
// side effects on state of apiserver (though there may be internal side effects like
 | 
			
		||||
// caching and logging).
 | 
			
		||||
func IsReadOnlyReq(req http.Request) bool {
 | 
			
		||||
	if req.Method == "GET" {
 | 
			
		||||
		// TODO: add OPTIONS and HEAD if we ever support those.
 | 
			
		||||
		return true
 | 
			
		||||
	}
 | 
			
		||||
	return false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ReadOnly passes all GET requests on to handler, and returns an error on all other requests.
 | 
			
		||||
func ReadOnly(handler http.Handler) http.Handler {
 | 
			
		||||
	return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
 | 
			
		||||
		if IsReadOnlyReq(*req) {
 | 
			
		||||
			handler.ServeHTTP(w, req)
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
		w.WriteHeader(http.StatusForbidden)
 | 
			
		||||
		fmt.Fprintf(w, "This is a read-only endpoint.")
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// RecoverPanics wraps an http Handler to recover and log panics.
 | 
			
		||||
func RecoverPanics(handler http.Handler, resolver *RequestInfoResolver) http.Handler {
 | 
			
		||||
	return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
 | 
			
		||||
		defer runtime.HandleCrash(func(err interface{}) {
 | 
			
		||||
			http.Error(w, "This request caused apisever to panic. Look in log for details.", http.StatusInternalServerError)
 | 
			
		||||
			glog.Errorf("APIServer panic'd on %v %v: %v\n%s\n", req.Method, req.RequestURI, err, debug.Stack())
 | 
			
		||||
		})
 | 
			
		||||
 | 
			
		||||
		logger := httplog.NewLogged(req, &w)
 | 
			
		||||
		requestInfo, err := resolver.GetRequestInfo(req)
 | 
			
		||||
		if err != nil || requestInfo.Verb != "proxy" {
 | 
			
		||||
			logger.StacktraceWhen(
 | 
			
		||||
				httplog.StatusIsNot(
 | 
			
		||||
					http.StatusOK,
 | 
			
		||||
					http.StatusCreated,
 | 
			
		||||
					http.StatusAccepted,
 | 
			
		||||
					http.StatusBadRequest,
 | 
			
		||||
					http.StatusMovedPermanently,
 | 
			
		||||
					http.StatusTemporaryRedirect,
 | 
			
		||||
					http.StatusConflict,
 | 
			
		||||
					http.StatusNotFound,
 | 
			
		||||
					http.StatusUnauthorized,
 | 
			
		||||
					http.StatusForbidden,
 | 
			
		||||
					http.StatusNotModified,
 | 
			
		||||
					errors.StatusUnprocessableEntity,
 | 
			
		||||
					http.StatusSwitchingProtocols,
 | 
			
		||||
				),
 | 
			
		||||
			)
 | 
			
		||||
		}
 | 
			
		||||
		defer logger.Log()
 | 
			
		||||
		// Dispatch to the internal handler
 | 
			
		||||
		handler.ServeHTTP(w, req)
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// RequestAttributeGetter is a function that extracts authorizer.Attributes from an http.Request
 | 
			
		||||
type RequestAttributeGetter interface {
 | 
			
		||||
	GetAttribs(req *http.Request) (attribs authorizer.Attributes)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type requestAttributeGetter struct {
 | 
			
		||||
	requestContextMapper api.RequestContextMapper
 | 
			
		||||
	requestInfoResolver  *RequestInfoResolver
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewAttributeGetter returns an object which implements the RequestAttributeGetter interface.
 | 
			
		||||
func NewRequestAttributeGetter(requestContextMapper api.RequestContextMapper, requestInfoResolver *RequestInfoResolver) RequestAttributeGetter {
 | 
			
		||||
	return &requestAttributeGetter{requestContextMapper, requestInfoResolver}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (r *requestAttributeGetter) GetAttribs(req *http.Request) authorizer.Attributes {
 | 
			
		||||
	attribs := authorizer.AttributesRecord{}
 | 
			
		||||
 | 
			
		||||
	ctx, ok := r.requestContextMapper.Get(req)
 | 
			
		||||
	if ok {
 | 
			
		||||
		user, ok := api.UserFrom(ctx)
 | 
			
		||||
		if ok {
 | 
			
		||||
			attribs.User = user
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	requestInfo, _ := r.requestInfoResolver.GetRequestInfo(req)
 | 
			
		||||
 | 
			
		||||
	// Start with common attributes that apply to resource and non-resource requests
 | 
			
		||||
	attribs.ResourceRequest = requestInfo.IsResourceRequest
 | 
			
		||||
	attribs.Path = requestInfo.Path
 | 
			
		||||
	attribs.Verb = requestInfo.Verb
 | 
			
		||||
 | 
			
		||||
	attribs.APIGroup = requestInfo.APIGroup
 | 
			
		||||
	attribs.APIVersion = requestInfo.APIVersion
 | 
			
		||||
	attribs.Resource = requestInfo.Resource
 | 
			
		||||
	attribs.Subresource = requestInfo.Subresource
 | 
			
		||||
	attribs.Namespace = requestInfo.Namespace
 | 
			
		||||
	attribs.Name = requestInfo.Name
 | 
			
		||||
 | 
			
		||||
	return &attribs
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// WithAuthorizationCheck passes all authorized requests on to handler, and returns a forbidden error otherwise.
 | 
			
		||||
func WithAuthorization(handler http.Handler, getAttribs RequestAttributeGetter, a authorizer.Authorizer) http.Handler {
 | 
			
		||||
	if a == nil {
 | 
			
		||||
		glog.Warningf("Authorization is disabled")
 | 
			
		||||
		return handler
 | 
			
		||||
	}
 | 
			
		||||
	return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
 | 
			
		||||
		authorized, reason, err := a.Authorize(getAttribs.GetAttribs(req))
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			internalError(w, req, err)
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
		if !authorized {
 | 
			
		||||
			glog.V(4).Infof("Forbidden: %#v, Reason: %s", req.RequestURI, reason)
 | 
			
		||||
			forbidden(w, req)
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
		handler.ServeHTTP(w, req)
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// RequestInfo holds information parsed from the http.Request
 | 
			
		||||
type RequestInfo struct {
 | 
			
		||||
	// IsResourceRequest indicates whether or not the request is for an API resource or subresource
 | 
			
		||||
@@ -195,6 +51,22 @@ type RequestInfo struct {
 | 
			
		||||
	Parts []string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// specialVerbs contains just strings which are used in REST paths for special actions that don't fall under the normal
 | 
			
		||||
// CRUDdy GET/POST/PUT/DELETE actions on REST objects.
 | 
			
		||||
// TODO: find a way to keep this up to date automatically.  Maybe dynamically populate list as handlers added to
 | 
			
		||||
// master's Mux.
 | 
			
		||||
var specialVerbs = sets.NewString("proxy", "redirect", "watch")
 | 
			
		||||
 | 
			
		||||
// specialVerbsNoSubresources contains root verbs which do not allow subresources
 | 
			
		||||
var specialVerbsNoSubresources = sets.NewString("proxy", "redirect")
 | 
			
		||||
 | 
			
		||||
// namespaceSubresources contains subresources of namespace
 | 
			
		||||
// this list allows the parser to distinguish between a namespace subresource, and a namespaced resource
 | 
			
		||||
var namespaceSubresources = sets.NewString("status", "finalize")
 | 
			
		||||
 | 
			
		||||
// NamespaceSubResourcesForTest exports namespaceSubresources for testing in pkg/master/master_test.go, so we never drift
 | 
			
		||||
var NamespaceSubResourcesForTest = sets.NewString(namespaceSubresources.List()...)
 | 
			
		||||
 | 
			
		||||
type RequestInfoResolver struct {
 | 
			
		||||
	APIPrefixes          sets.String
 | 
			
		||||
	GrouplessAPIPrefixes sets.String
 | 
			
		||||
@@ -334,3 +206,12 @@ func (r *RequestInfoResolver) GetRequestInfo(req *http.Request) (RequestInfo, er
 | 
			
		||||
 | 
			
		||||
	return requestInfo, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// splitPath returns the segments for a URL path.
 | 
			
		||||
func splitPath(path string) []string {
 | 
			
		||||
	path = strings.Trim(path, "/")
 | 
			
		||||
	if path == "" {
 | 
			
		||||
		return []string{}
 | 
			
		||||
	}
 | 
			
		||||
	return strings.Split(path, "/")
 | 
			
		||||
}
 | 
			
		||||
@@ -18,14 +18,11 @@ package apiserver
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"net/http/httptest"
 | 
			
		||||
	"reflect"
 | 
			
		||||
	"testing"
 | 
			
		||||
 | 
			
		||||
	"k8s.io/kubernetes/pkg/api"
 | 
			
		||||
	"k8s.io/kubernetes/pkg/api/testapi"
 | 
			
		||||
	"k8s.io/kubernetes/pkg/apis/extensions"
 | 
			
		||||
	"k8s.io/kubernetes/pkg/auth/authorizer"
 | 
			
		||||
	"k8s.io/kubernetes/pkg/util/sets"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
@@ -43,106 +40,6 @@ func pathWithPrefix(prefix, resource, namespace, name string) string {
 | 
			
		||||
	return testapi.Default.ResourcePathWithPrefix(prefix, resource, namespace, name)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestReadOnly(t *testing.T) {
 | 
			
		||||
	server := httptest.NewServer(ReadOnly(http.HandlerFunc(
 | 
			
		||||
		func(w http.ResponseWriter, req *http.Request) {
 | 
			
		||||
			if req.Method != "GET" {
 | 
			
		||||
				t.Errorf("Unexpected call: %v", req.Method)
 | 
			
		||||
			}
 | 
			
		||||
		},
 | 
			
		||||
	)))
 | 
			
		||||
	defer server.Close()
 | 
			
		||||
	for _, verb := range []string{"GET", "POST", "PUT", "DELETE", "CREATE"} {
 | 
			
		||||
		req, err := http.NewRequest(verb, server.URL, nil)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			t.Fatalf("Couldn't make request: %v", err)
 | 
			
		||||
		}
 | 
			
		||||
		http.DefaultClient.Do(req)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestGetAttribs(t *testing.T) {
 | 
			
		||||
	r := &requestAttributeGetter{api.NewRequestContextMapper(), &RequestInfoResolver{sets.NewString("api", "apis"), sets.NewString("api")}}
 | 
			
		||||
 | 
			
		||||
	testcases := map[string]struct {
 | 
			
		||||
		Verb               string
 | 
			
		||||
		Path               string
 | 
			
		||||
		ExpectedAttributes *authorizer.AttributesRecord
 | 
			
		||||
	}{
 | 
			
		||||
		"non-resource root": {
 | 
			
		||||
			Verb: "POST",
 | 
			
		||||
			Path: "/",
 | 
			
		||||
			ExpectedAttributes: &authorizer.AttributesRecord{
 | 
			
		||||
				Verb: "post",
 | 
			
		||||
				Path: "/",
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		"non-resource api prefix": {
 | 
			
		||||
			Verb: "GET",
 | 
			
		||||
			Path: "/api/",
 | 
			
		||||
			ExpectedAttributes: &authorizer.AttributesRecord{
 | 
			
		||||
				Verb: "get",
 | 
			
		||||
				Path: "/api/",
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		"non-resource group api prefix": {
 | 
			
		||||
			Verb: "GET",
 | 
			
		||||
			Path: "/apis/extensions/",
 | 
			
		||||
			ExpectedAttributes: &authorizer.AttributesRecord{
 | 
			
		||||
				Verb: "get",
 | 
			
		||||
				Path: "/apis/extensions/",
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		"resource": {
 | 
			
		||||
			Verb: "POST",
 | 
			
		||||
			Path: "/api/v1/nodes/mynode",
 | 
			
		||||
			ExpectedAttributes: &authorizer.AttributesRecord{
 | 
			
		||||
				Verb:            "create",
 | 
			
		||||
				Path:            "/api/v1/nodes/mynode",
 | 
			
		||||
				ResourceRequest: true,
 | 
			
		||||
				Resource:        "nodes",
 | 
			
		||||
				APIVersion:      "v1",
 | 
			
		||||
				Name:            "mynode",
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		"namespaced resource": {
 | 
			
		||||
			Verb: "PUT",
 | 
			
		||||
			Path: "/api/v1/namespaces/myns/pods/mypod",
 | 
			
		||||
			ExpectedAttributes: &authorizer.AttributesRecord{
 | 
			
		||||
				Verb:            "update",
 | 
			
		||||
				Path:            "/api/v1/namespaces/myns/pods/mypod",
 | 
			
		||||
				ResourceRequest: true,
 | 
			
		||||
				Namespace:       "myns",
 | 
			
		||||
				Resource:        "pods",
 | 
			
		||||
				APIVersion:      "v1",
 | 
			
		||||
				Name:            "mypod",
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		"API group resource": {
 | 
			
		||||
			Verb: "GET",
 | 
			
		||||
			Path: "/apis/extensions/v1beta1/namespaces/myns/jobs",
 | 
			
		||||
			ExpectedAttributes: &authorizer.AttributesRecord{
 | 
			
		||||
				Verb:            "list",
 | 
			
		||||
				Path:            "/apis/extensions/v1beta1/namespaces/myns/jobs",
 | 
			
		||||
				ResourceRequest: true,
 | 
			
		||||
				APIGroup:        extensions.GroupName,
 | 
			
		||||
				APIVersion:      "v1beta1",
 | 
			
		||||
				Namespace:       "myns",
 | 
			
		||||
				Resource:        "jobs",
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for k, tc := range testcases {
 | 
			
		||||
		req, _ := http.NewRequest(tc.Verb, tc.Path, nil)
 | 
			
		||||
		attribs := r.GetAttribs(req)
 | 
			
		||||
		if !reflect.DeepEqual(attribs, tc.ExpectedAttributes) {
 | 
			
		||||
			t.Errorf("%s: expected\n\t%#v\ngot\n\t%#v", k, tc.ExpectedAttributes, attribs)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestGetAPIRequestInfo(t *testing.T) {
 | 
			
		||||
	successCases := []struct {
 | 
			
		||||
		method              string
 | 
			
		||||
@@ -202,12 +99,12 @@ func TestGetAPIRequestInfo(t *testing.T) {
 | 
			
		||||
		{"POST", "/apis/extensions/v1beta3/namespaces/other/pods", "create", "api", "extensions", "v1beta3", "other", "pods", "", "", []string{"pods"}},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	requestInfoResolver := newTestRequestInfoResolver()
 | 
			
		||||
	resolver := newTestRequestInfoResolver()
 | 
			
		||||
 | 
			
		||||
	for _, successCase := range successCases {
 | 
			
		||||
		req, _ := http.NewRequest(successCase.method, successCase.url, nil)
 | 
			
		||||
 | 
			
		||||
		apiRequestInfo, err := requestInfoResolver.GetRequestInfo(req)
 | 
			
		||||
		apiRequestInfo, err := resolver.GetRequestInfo(req)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			t.Errorf("Unexpected error for url: %s %v", successCase.url, err)
 | 
			
		||||
		}
 | 
			
		||||
@@ -250,7 +147,7 @@ func TestGetAPIRequestInfo(t *testing.T) {
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			t.Errorf("Unexpected error %v", err)
 | 
			
		||||
		}
 | 
			
		||||
		apiRequestInfo, err := requestInfoResolver.GetRequestInfo(req)
 | 
			
		||||
		apiRequestInfo, err := resolver.GetRequestInfo(req)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			t.Errorf("%s: Unexpected error %v", k, err)
 | 
			
		||||
		}
 | 
			
		||||
@@ -281,12 +178,12 @@ func TestGetNonAPIRequestInfo(t *testing.T) {
 | 
			
		||||
		"empty":                        {"", false},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	requestInfoResolver := newTestRequestInfoResolver()
 | 
			
		||||
	resolver := newTestRequestInfoResolver()
 | 
			
		||||
 | 
			
		||||
	for testName, tc := range tests {
 | 
			
		||||
		req, _ := http.NewRequest("GET", tc.url, nil)
 | 
			
		||||
 | 
			
		||||
		apiRequestInfo, err := requestInfoResolver.GetRequestInfo(req)
 | 
			
		||||
		apiRequestInfo, err := resolver.GetRequestInfo(req)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			t.Errorf("%s: Unexpected error %v", testName, err)
 | 
			
		||||
		}
 | 
			
		||||
@@ -295,3 +192,7 @@ func TestGetNonAPIRequestInfo(t *testing.T) {
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func newTestRequestInfoResolver() *RequestInfoResolver {
 | 
			
		||||
	return &RequestInfoResolver{sets.NewString("api", "apis"), sets.NewString("api")}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										42
									
								
								pkg/apiserver/serviceerror.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								pkg/apiserver/serviceerror.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,42 @@
 | 
			
		||||
/*
 | 
			
		||||
Copyright 2014 The Kubernetes Authors.
 | 
			
		||||
 | 
			
		||||
Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
you may not use this file except in compliance with the License.
 | 
			
		||||
You may obtain a copy of the License at
 | 
			
		||||
 | 
			
		||||
    http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
 | 
			
		||||
Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
See the License for the specific language governing permissions and
 | 
			
		||||
limitations under the License.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
package apiserver
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"github.com/emicklei/go-restful"
 | 
			
		||||
 | 
			
		||||
	"k8s.io/kubernetes/pkg/api"
 | 
			
		||||
	apierrors "k8s.io/kubernetes/pkg/api/errors"
 | 
			
		||||
	"k8s.io/kubernetes/pkg/api/unversioned"
 | 
			
		||||
	"k8s.io/kubernetes/pkg/runtime"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func InstallServiceErrorHandler(s runtime.NegotiatedSerializer, container *restful.Container) {
 | 
			
		||||
	container.ServiceErrorHandler(func(serviceErr restful.ServiceError, request *restful.Request, response *restful.Response) {
 | 
			
		||||
		serviceErrorHandler(s, serviceErr, request, response)
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func serviceErrorHandler(s runtime.NegotiatedSerializer, serviceErr restful.ServiceError, request *restful.Request, response *restful.Response) {
 | 
			
		||||
	errorNegotiated(
 | 
			
		||||
		apierrors.NewGenericServerResponse(serviceErr.Code, "", api.Resource(""), "", serviceErr.Message, 0, false),
 | 
			
		||||
		s,
 | 
			
		||||
		unversioned.GroupVersion{},
 | 
			
		||||
		response.ResponseWriter,
 | 
			
		||||
		request.Request,
 | 
			
		||||
	)
 | 
			
		||||
}
 | 
			
		||||
@@ -36,7 +36,7 @@ import (
 | 
			
		||||
	"k8s.io/kubernetes/pkg/api"
 | 
			
		||||
	"k8s.io/kubernetes/pkg/api/unversioned"
 | 
			
		||||
	"k8s.io/kubernetes/pkg/apiserver"
 | 
			
		||||
	"k8s.io/kubernetes/pkg/apiserver/audit"
 | 
			
		||||
	apiserverfilters "k8s.io/kubernetes/pkg/apiserver/filters"
 | 
			
		||||
	"k8s.io/kubernetes/pkg/auth/authenticator"
 | 
			
		||||
	"k8s.io/kubernetes/pkg/auth/authorizer"
 | 
			
		||||
	authhandlers "k8s.io/kubernetes/pkg/auth/handlers"
 | 
			
		||||
@@ -363,19 +363,17 @@ func (s *GenericAPIServer) buildHandlerChains(c *Config, handler http.Handler) (
 | 
			
		||||
 | 
			
		||||
	// insecure filters
 | 
			
		||||
	insecure = handler
 | 
			
		||||
	insecure = api.WithRequestContext(insecure, c.RequestContextMapper)
 | 
			
		||||
	insecure = apiserver.RecoverPanics(insecure, s.NewRequestInfoResolver())
 | 
			
		||||
	insecure = genericfilters.WithPanicRecovery(insecure, s.NewRequestInfoResolver())
 | 
			
		||||
	insecure = genericfilters.WithTimeoutForNonLongRunningRequests(insecure, longRunningFunc)
 | 
			
		||||
 | 
			
		||||
	// secure filters
 | 
			
		||||
	attributeGetter := apiserver.NewRequestAttributeGetter(c.RequestContextMapper, s.NewRequestInfoResolver())
 | 
			
		||||
	attributeGetter := apiserverfilters.NewRequestAttributeGetter(c.RequestContextMapper, s.NewRequestInfoResolver())
 | 
			
		||||
	secure = handler
 | 
			
		||||
	secure = apiserver.WithAuthorization(secure, attributeGetter, c.Authorizer)
 | 
			
		||||
	secure = apiserver.WithImpersonation(secure, c.RequestContextMapper, c.Authorizer)
 | 
			
		||||
	secure = audit.WithAudit(secure, attributeGetter, s.auditWriter) // before impersonation to read original user
 | 
			
		||||
	secure = apiserverfilters.WithAuthorization(secure, attributeGetter, c.Authorizer)
 | 
			
		||||
	secure = apiserverfilters.WithImpersonation(secure, c.RequestContextMapper, c.Authorizer)
 | 
			
		||||
	secure = apiserverfilters.WithAudit(secure, attributeGetter, s.auditWriter) // before impersonation to read original user
 | 
			
		||||
	secure = authhandlers.WithAuthentication(secure, c.RequestContextMapper, c.Authenticator, authhandlers.Unauthorized(c.SupportsBasicAuth))
 | 
			
		||||
	secure = api.WithRequestContext(secure, c.RequestContextMapper)
 | 
			
		||||
	secure = apiserver.RecoverPanics(secure, s.NewRequestInfoResolver())
 | 
			
		||||
	secure = genericfilters.WithPanicRecovery(secure, s.NewRequestInfoResolver())
 | 
			
		||||
	secure = genericfilters.WithTimeoutForNonLongRunningRequests(secure, longRunningFunc)
 | 
			
		||||
	secure = genericfilters.WithMaxInFlightLimit(secure, c.MaxRequestsInFlight, longRunningFunc)
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										64
									
								
								pkg/genericapiserver/filters/panics.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										64
									
								
								pkg/genericapiserver/filters/panics.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,64 @@
 | 
			
		||||
/*
 | 
			
		||||
Copyright 2016 The Kubernetes Authors.
 | 
			
		||||
 | 
			
		||||
Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
you may not use this file except in compliance with the License.
 | 
			
		||||
You may obtain a copy of the License at
 | 
			
		||||
 | 
			
		||||
    http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
 | 
			
		||||
Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
See the License for the specific language governing permissions and
 | 
			
		||||
limitations under the License.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
package filters
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"runtime/debug"
 | 
			
		||||
 | 
			
		||||
	"github.com/golang/glog"
 | 
			
		||||
 | 
			
		||||
	"k8s.io/kubernetes/pkg/api/errors"
 | 
			
		||||
	"k8s.io/kubernetes/pkg/apiserver"
 | 
			
		||||
	"k8s.io/kubernetes/pkg/httplog"
 | 
			
		||||
	"k8s.io/kubernetes/pkg/util/runtime"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// WithPanicRecovery wraps an http Handler to recover and log panics.
 | 
			
		||||
func WithPanicRecovery(handler http.Handler, resolver *apiserver.RequestInfoResolver) http.Handler {
 | 
			
		||||
	return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
 | 
			
		||||
		defer runtime.HandleCrash(func(err interface{}) {
 | 
			
		||||
			http.Error(w, "This request caused apisever to panic. Look in log for details.", http.StatusInternalServerError)
 | 
			
		||||
			glog.Errorf("APIServer panic'd on %v %v: %v\n%s\n", req.Method, req.RequestURI, err, debug.Stack())
 | 
			
		||||
		})
 | 
			
		||||
 | 
			
		||||
		logger := httplog.NewLogged(req, &w)
 | 
			
		||||
		requestInfo, err := resolver.GetRequestInfo(req)
 | 
			
		||||
		if err != nil || requestInfo.Verb != "proxy" {
 | 
			
		||||
			logger.StacktraceWhen(
 | 
			
		||||
				httplog.StatusIsNot(
 | 
			
		||||
					http.StatusOK,
 | 
			
		||||
					http.StatusCreated,
 | 
			
		||||
					http.StatusAccepted,
 | 
			
		||||
					http.StatusBadRequest,
 | 
			
		||||
					http.StatusMovedPermanently,
 | 
			
		||||
					http.StatusTemporaryRedirect,
 | 
			
		||||
					http.StatusConflict,
 | 
			
		||||
					http.StatusNotFound,
 | 
			
		||||
					http.StatusUnauthorized,
 | 
			
		||||
					http.StatusForbidden,
 | 
			
		||||
					http.StatusNotModified,
 | 
			
		||||
					errors.StatusUnprocessableEntity,
 | 
			
		||||
					http.StatusSwitchingProtocols,
 | 
			
		||||
				),
 | 
			
		||||
			)
 | 
			
		||||
		}
 | 
			
		||||
		defer logger.Log()
 | 
			
		||||
		// Dispatch to the internal handler
 | 
			
		||||
		handler.ServeHTTP(w, req)
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user