commit
203a3d0cec
@ -23,6 +23,7 @@ import (
|
|||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/apiserver"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/apiserver"
|
||||||
@ -63,6 +64,7 @@ var (
|
|||||||
healthCheckMinions = flag.Bool("health_check_minions", true, "If true, health check minions and filter unhealthy ones. Default true.")
|
healthCheckMinions = flag.Bool("health_check_minions", true, "If true, health check minions and filter unhealthy ones. Default true.")
|
||||||
eventTTL = flag.Duration("event_ttl", 48*time.Hour, "Amount of time to retain events. Default 2 days.")
|
eventTTL = flag.Duration("event_ttl", 48*time.Hour, "Amount of time to retain events. Default 2 days.")
|
||||||
tokenAuthFile = flag.String("token_auth_file", "", "If set, the file that will be used to secure the API server via token authentication.")
|
tokenAuthFile = flag.String("token_auth_file", "", "If set, the file that will be used to secure the API server via token authentication.")
|
||||||
|
authorizationMode = flag.String("authorization_mode", "AlwaysAllow", "Selects how to do authorization. One of: "+strings.Join(apiserver.AuthorizationModeChoices, ","))
|
||||||
etcdServerList util.StringList
|
etcdServerList util.StringList
|
||||||
etcdConfigFile = flag.String("etcd_config", "", "The config file for the etcd client. Mutually exclusive with -etcd_servers.")
|
etcdConfigFile = flag.String("etcd_config", "", "The config file for the etcd client. Mutually exclusive with -etcd_servers.")
|
||||||
corsAllowedOriginList util.StringList
|
corsAllowedOriginList util.StringList
|
||||||
@ -159,6 +161,7 @@ func main() {
|
|||||||
ReadOnlyPort: *readOnlyPort,
|
ReadOnlyPort: *readOnlyPort,
|
||||||
ReadWritePort: *port,
|
ReadWritePort: *port,
|
||||||
PublicAddress: *publicAddressOverride,
|
PublicAddress: *publicAddressOverride,
|
||||||
|
AuthorizationMode: *authorizationMode,
|
||||||
}
|
}
|
||||||
m := master.New(config)
|
m := master.New(config)
|
||||||
|
|
||||||
|
@ -146,6 +146,7 @@ func startComponents(manifestURL string) (apiServerURL string) {
|
|||||||
KubeletClient: fakeKubeletClient{},
|
KubeletClient: fakeKubeletClient{},
|
||||||
EnableLogsSupport: false,
|
EnableLogsSupport: false,
|
||||||
APIPrefix: "/api",
|
APIPrefix: "/api",
|
||||||
|
AuthorizationMode: "AlwaysAllow",
|
||||||
|
|
||||||
ReadWritePort: portNumber,
|
ReadWritePort: portNumber,
|
||||||
ReadOnlyPort: portNumber,
|
ReadOnlyPort: portNumber,
|
||||||
|
@ -41,7 +41,7 @@ start_etcd
|
|||||||
echo ""
|
echo ""
|
||||||
echo "Integration test cases..."
|
echo "Integration test cases..."
|
||||||
echo ""
|
echo ""
|
||||||
GOFLAGS="-tags 'integration no-docker'" \
|
GOFLAGS="-tags 'integration no-docker' -test.v" \
|
||||||
"${KUBE_ROOT}/hack/test-go.sh" test/integration
|
"${KUBE_ROOT}/hack/test-go.sh" test/integration
|
||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
|
68
pkg/apiserver/authz.go
Normal file
68
pkg/apiserver/authz.go
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2014 Google Inc. All rights reserved.
|
||||||
|
|
||||||
|
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 (
|
||||||
|
"errors"
|
||||||
|
|
||||||
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/auth/authorizer"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Attributes implements authorizer.Attributes interface.
|
||||||
|
type Attributes struct {
|
||||||
|
// TODO: add fields and methods when authorizer.Attributes is completed.
|
||||||
|
}
|
||||||
|
|
||||||
|
// alwaysAllowAuthorizer is an implementation of authorizer.Attributes
|
||||||
|
// which always says yes to an authorization request.
|
||||||
|
// It is useful in tests and when using kubernetes in an open manner.
|
||||||
|
type alwaysAllowAuthorizer struct{}
|
||||||
|
|
||||||
|
func (alwaysAllowAuthorizer) Authorize(a authorizer.Attributes) (err error) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// alwaysDenyAuthorizer is an implementation of authorizer.Attributes
|
||||||
|
// which always says no to an authorization request.
|
||||||
|
// It is useful in unit tests to force an operation to be forbidden.
|
||||||
|
type alwaysDenyAuthorizer struct{}
|
||||||
|
|
||||||
|
func (alwaysDenyAuthorizer) Authorize(a authorizer.Attributes) (err error) {
|
||||||
|
return errors.New("Everything is forbidden.")
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
ModeAlwaysAllow string = "AlwaysAllow"
|
||||||
|
ModeAlwaysDeny string = "AlwaysDeny"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Keep this list in sync with constant list above.
|
||||||
|
var AuthorizationModeChoices = []string{ModeAlwaysAllow, ModeAlwaysDeny}
|
||||||
|
|
||||||
|
// NewAuthorizerFromAuthorizationConfig returns the right sort of authorizer.Authorizer
|
||||||
|
// based on the authorizationMode xor an error. authorizationMode should be one of AuthorizationModeChoices.
|
||||||
|
func NewAuthorizerFromAuthorizationConfig(authorizationMode string) (authorizer.Authorizer, error) {
|
||||||
|
// Keep cases in sync with constant list above.
|
||||||
|
switch authorizationMode {
|
||||||
|
case ModeAlwaysAllow:
|
||||||
|
return new(alwaysAllowAuthorizer), nil
|
||||||
|
case ModeAlwaysDeny:
|
||||||
|
return new(alwaysDenyAuthorizer), nil
|
||||||
|
default:
|
||||||
|
return nil, errors.New("Unknown authorization mode")
|
||||||
|
}
|
||||||
|
}
|
@ -23,6 +23,7 @@ import (
|
|||||||
"runtime/debug"
|
"runtime/debug"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/auth/authorizer"
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/httplog"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/httplog"
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
|
||||||
"github.com/golang/glog"
|
"github.com/golang/glog"
|
||||||
@ -118,3 +119,24 @@ func CORS(handler http.Handler, allowedOriginPatterns []*regexp.Regexp, allowedM
|
|||||||
handler.ServeHTTP(w, req)
|
handler.ServeHTTP(w, req)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RequestAttributeGetter is a function that extracts authorizer.Attributes from an http.Request
|
||||||
|
type RequestAttributeGetter func(req *http.Request) (attribs authorizer.Attributes)
|
||||||
|
|
||||||
|
// BasicAttributeGetter gets authorizer.Attributes from an http.Request.
|
||||||
|
func BasicAttributeGetter(req *http.Request) (attribs authorizer.Attributes) {
|
||||||
|
// TODO: fill in attributes once attributes are defined.
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithAuthorizationCheck passes all authorized requests on to handler, and returns a forbidden error otherwise.
|
||||||
|
func WithAuthorizationCheck(handler http.Handler, getAttribs RequestAttributeGetter, a authorizer.Authorizer) http.Handler {
|
||||||
|
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
||||||
|
err := a.Authorize(getAttribs(req))
|
||||||
|
if err == nil {
|
||||||
|
handler.ServeHTTP(w, req)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
forbidden(w, req)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
30
pkg/auth/authorizer/interfaces.go
Normal file
30
pkg/auth/authorizer/interfaces.go
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2014 Google Inc. All rights reserved.
|
||||||
|
|
||||||
|
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 authorizer
|
||||||
|
|
||||||
|
// Attributes is an interface used by an Authorizer to get information about a request
|
||||||
|
// that is used to make an authorization decision.
|
||||||
|
type Attributes interface {
|
||||||
|
// TODO: add attribute getter functions, e.g. GetUserName(), per #1430.
|
||||||
|
}
|
||||||
|
|
||||||
|
// Authorizer makes an authorization decision based on information gained by making
|
||||||
|
// zero or more calls to methods of the Attributes interface. It returns nil when an action is
|
||||||
|
// authorized, otherwise it returns an error.
|
||||||
|
type Authorizer interface {
|
||||||
|
Authorize(a Attributes) (err error)
|
||||||
|
}
|
@ -66,6 +66,7 @@ type Config struct {
|
|||||||
APIPrefix string
|
APIPrefix string
|
||||||
CorsAllowedOriginList util.StringList
|
CorsAllowedOriginList util.StringList
|
||||||
TokenAuthFile string
|
TokenAuthFile string
|
||||||
|
AuthorizationMode string
|
||||||
|
|
||||||
// Number of masters running; all masters must be started with the
|
// Number of masters running; all masters must be started with the
|
||||||
// same value for this field. (Numbers > 1 currently untested.)
|
// same value for this field. (Numbers > 1 currently untested.)
|
||||||
@ -101,6 +102,7 @@ type Master struct {
|
|||||||
apiPrefix string
|
apiPrefix string
|
||||||
corsAllowedOriginList util.StringList
|
corsAllowedOriginList util.StringList
|
||||||
tokenAuthFile string
|
tokenAuthFile string
|
||||||
|
authorizationzMode string
|
||||||
masterCount int
|
masterCount int
|
||||||
|
|
||||||
// "Outputs"
|
// "Outputs"
|
||||||
@ -220,9 +222,11 @@ func New(c *Config) *Master {
|
|||||||
apiPrefix: c.APIPrefix,
|
apiPrefix: c.APIPrefix,
|
||||||
corsAllowedOriginList: c.CorsAllowedOriginList,
|
corsAllowedOriginList: c.CorsAllowedOriginList,
|
||||||
tokenAuthFile: c.TokenAuthFile,
|
tokenAuthFile: c.TokenAuthFile,
|
||||||
masterCount: c.MasterCount,
|
authorizationzMode: c.AuthorizationMode,
|
||||||
readOnlyServer: net.JoinHostPort(c.PublicAddress, strconv.Itoa(int(c.ReadOnlyPort))),
|
|
||||||
readWriteServer: net.JoinHostPort(c.PublicAddress, strconv.Itoa(int(c.ReadWritePort))),
|
masterCount: c.MasterCount,
|
||||||
|
readOnlyServer: net.JoinHostPort(c.PublicAddress, strconv.Itoa(int(c.ReadOnlyPort))),
|
||||||
|
readWriteServer: net.JoinHostPort(c.PublicAddress, strconv.Itoa(int(c.ReadWritePort))),
|
||||||
}
|
}
|
||||||
m.masterServices = util.NewRunner(m.serviceWriterLoop, m.roServiceWriterLoop)
|
m.masterServices = util.NewRunner(m.serviceWriterLoop, m.roServiceWriterLoop)
|
||||||
m.init(c)
|
m.init(c)
|
||||||
@ -310,6 +314,14 @@ func (m *Master) init(c *Config) {
|
|||||||
handler = apiserver.CORS(handler, allowedOriginRegexps, nil, nil, "true")
|
handler = apiserver.CORS(handler, allowedOriginRegexps, nil, nil, "true")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Install Authorizer
|
||||||
|
authorizer, err := apiserver.NewAuthorizerFromAuthorizationConfig(m.authorizationzMode)
|
||||||
|
if err != nil {
|
||||||
|
glog.Fatal(err)
|
||||||
|
}
|
||||||
|
handler = apiserver.WithAuthorizationCheck(handler, apiserver.BasicAttributeGetter, authorizer)
|
||||||
|
|
||||||
|
// Install Authenticator
|
||||||
if authenticator != nil {
|
if authenticator != nil {
|
||||||
handler = handlers.NewRequestAuthenticator(userContexts, authenticator, handlers.Unauthorized, handler)
|
handler = handlers.NewRequestAuthenticator(userContexts, authenticator, handlers.Unauthorized, handler)
|
||||||
}
|
}
|
||||||
|
@ -23,6 +23,7 @@ package integration
|
|||||||
// to work for any client of the HTTP interface.
|
// to work for any client of the HTTP interface.
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net/http"
|
"net/http"
|
||||||
@ -70,6 +71,7 @@ xyz987,bob,2
|
|||||||
EnableUISupport: false,
|
EnableUISupport: false,
|
||||||
APIPrefix: "/api",
|
APIPrefix: "/api",
|
||||||
TokenAuthFile: f.Name(),
|
TokenAuthFile: f.Name(),
|
||||||
|
AuthorizationMode: "AlwaysAllow",
|
||||||
})
|
})
|
||||||
|
|
||||||
s := httptest.NewServer(m.Handler)
|
s := httptest.NewServer(m.Handler)
|
||||||
@ -118,3 +120,293 @@ xyz987,bob,2
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Bodies for requests used in subsequent tests.
|
||||||
|
var aPod string = `
|
||||||
|
{
|
||||||
|
"kind": "Pod",
|
||||||
|
"apiVersion": "v1beta1",
|
||||||
|
"id": "a",
|
||||||
|
"desiredState": {
|
||||||
|
"manifest": {
|
||||||
|
"version": "v1beta1",
|
||||||
|
"id": "a",
|
||||||
|
"containers": [{ "name": "foo", "image": "bar/foo", }]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
`
|
||||||
|
var aRC string = `
|
||||||
|
{
|
||||||
|
"kind": "ReplicationController",
|
||||||
|
"apiVersion": "v1beta1",
|
||||||
|
"id": "a",
|
||||||
|
"desiredState": {
|
||||||
|
"replicas": 2,
|
||||||
|
"replicaSelector": {"name": "a"},
|
||||||
|
"podTemplate": {
|
||||||
|
"desiredState": {
|
||||||
|
"manifest": {
|
||||||
|
"version": "v1beta1",
|
||||||
|
"id": "a",
|
||||||
|
"containers": [{
|
||||||
|
"name": "foo",
|
||||||
|
"image": "bar/foo",
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"labels": {"name": "a"}
|
||||||
|
}},
|
||||||
|
"labels": {"name": "a"}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
var aService string = `
|
||||||
|
{
|
||||||
|
"kind": "Service",
|
||||||
|
"apiVersion": "v1beta1",
|
||||||
|
"id": "a",
|
||||||
|
"port": 8000,
|
||||||
|
"labels": { "name": "a" },
|
||||||
|
"selector": { "name": "a" }
|
||||||
|
}
|
||||||
|
`
|
||||||
|
var aMinion string = `
|
||||||
|
{
|
||||||
|
"kind": "Minion",
|
||||||
|
"apiVersion": "v1beta1",
|
||||||
|
"id": "a",
|
||||||
|
"hostIP": "10.10.10.10",
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
var aEvent string = `
|
||||||
|
{
|
||||||
|
"kind": "Binding",
|
||||||
|
"apiVersion": "v1beta1",
|
||||||
|
"id": "a",
|
||||||
|
"involvedObject": {
|
||||||
|
{
|
||||||
|
"kind": "Minion",
|
||||||
|
"name": "a"
|
||||||
|
"apiVersion": "v1beta1",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
var aBinding string = `
|
||||||
|
{
|
||||||
|
"kind": "Binding",
|
||||||
|
"apiVersion": "v1beta1",
|
||||||
|
"id": "a",
|
||||||
|
"host": "10.10.10.10",
|
||||||
|
"podID": "a"
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
var aEndpoints string = `
|
||||||
|
{
|
||||||
|
"kind": "Endpoints",
|
||||||
|
"apiVersion": "v1beta1",
|
||||||
|
"id": "a",
|
||||||
|
"endpoints": ["10.10.1.1:1909"],
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
// Requests to try. Each one should be forbidden or not forbidden
|
||||||
|
// depending on the authentication and authorization setup of the master.
|
||||||
|
|
||||||
|
func getTestRequests() []struct {
|
||||||
|
verb string
|
||||||
|
URL string
|
||||||
|
body string
|
||||||
|
} {
|
||||||
|
requests := []struct {
|
||||||
|
verb string
|
||||||
|
URL string
|
||||||
|
body string
|
||||||
|
}{
|
||||||
|
// Normal methods on pods
|
||||||
|
{"GET", "/api/v1beta1/pods", ""},
|
||||||
|
{"GET", "/api/v1beta1/pods/a", ""},
|
||||||
|
{"POST", "/api/v1beta1/pods", aPod},
|
||||||
|
{"PUT", "/api/v1beta1/pods", aPod},
|
||||||
|
{"GET", "/api/v1beta1/pods", ""},
|
||||||
|
{"GET", "/api/v1beta1/pods/a", ""},
|
||||||
|
{"DELETE", "/api/v1beta1/pods", ""},
|
||||||
|
|
||||||
|
// Non-standard methods (not expected to work,
|
||||||
|
// but expected to pass/fail authorization prior to
|
||||||
|
// failing validation.
|
||||||
|
{"PATCH", "/api/v1beta1/pods/a", ""},
|
||||||
|
{"OPTIONS", "/api/v1beta1/pods", ""},
|
||||||
|
{"OPTIONS", "/api/v1beta1/pods/a", ""},
|
||||||
|
{"HEAD", "/api/v1beta1/pods", ""},
|
||||||
|
{"HEAD", "/api/v1beta1/pods/a", ""},
|
||||||
|
{"TRACE", "/api/v1beta1/pods", ""},
|
||||||
|
{"TRACE", "/api/v1beta1/pods/a", ""},
|
||||||
|
{"NOSUCHVERB", "/api/v1beta1/pods", ""},
|
||||||
|
|
||||||
|
// Normal methods on services
|
||||||
|
{"GET", "/api/v1beta1/services", ""},
|
||||||
|
{"GET", "/api/v1beta1/services/a", ""},
|
||||||
|
{"POST", "/api/v1beta1/services", aService},
|
||||||
|
{"PUT", "/api/v1beta1/services", aService},
|
||||||
|
{"GET", "/api/v1beta1/services", ""},
|
||||||
|
{"GET", "/api/v1beta1/services/a", ""},
|
||||||
|
{"DELETE", "/api/v1beta1/services", ""},
|
||||||
|
|
||||||
|
// Normal methods on replicationControllers
|
||||||
|
{"GET", "/api/v1beta1/replicationControllers", ""},
|
||||||
|
{"GET", "/api/v1beta1/replicationControllers/a", ""},
|
||||||
|
{"POST", "/api/v1beta1/replicationControllers", aRC},
|
||||||
|
{"PUT", "/api/v1beta1/replicationControllers", aRC},
|
||||||
|
{"GET", "/api/v1beta1/replicationControllers", ""},
|
||||||
|
{"GET", "/api/v1beta1/replicationControllers/a", ""},
|
||||||
|
{"DELETE", "/api/v1beta1/replicationControllers", ""},
|
||||||
|
|
||||||
|
// Normal methods on endpoints
|
||||||
|
{"GET", "/api/v1beta1/endpoints", ""},
|
||||||
|
{"GET", "/api/v1beta1/endpoints/a", ""},
|
||||||
|
{"POST", "/api/v1beta1/endpoints", aEndpoints},
|
||||||
|
{"PUT", "/api/v1beta1/endpoints", aEndpoints},
|
||||||
|
{"GET", "/api/v1beta1/endpoints", ""},
|
||||||
|
{"GET", "/api/v1beta1/endpoints/a", ""},
|
||||||
|
{"DELETE", "/api/v1beta1/endpoints", ""},
|
||||||
|
|
||||||
|
// Normal methods on minions
|
||||||
|
{"GET", "/api/v1beta1/minions", ""},
|
||||||
|
{"GET", "/api/v1beta1/minions/a", ""},
|
||||||
|
{"POST", "/api/v1beta1/minions", aMinion},
|
||||||
|
{"PUT", "/api/v1beta1/minions", aMinion},
|
||||||
|
{"GET", "/api/v1beta1/minions", ""},
|
||||||
|
{"GET", "/api/v1beta1/minions/a", ""},
|
||||||
|
{"DELETE", "/api/v1beta1/minions", ""},
|
||||||
|
|
||||||
|
// Normal methods on events
|
||||||
|
{"GET", "/api/v1beta1/events", ""},
|
||||||
|
{"GET", "/api/v1beta1/events/a", ""},
|
||||||
|
{"POST", "/api/v1beta1/events", aEvent},
|
||||||
|
{"PUT", "/api/v1beta1/events", aEvent},
|
||||||
|
{"GET", "/api/v1beta1/events", ""},
|
||||||
|
{"GET", "/api/v1beta1/events/a", ""},
|
||||||
|
{"DELETE", "/api/v1beta1/events", ""},
|
||||||
|
|
||||||
|
// Normal methods on bindings
|
||||||
|
{"GET", "/api/v1beta1/events", ""},
|
||||||
|
{"GET", "/api/v1beta1/events/a", ""},
|
||||||
|
{"POST", "/api/v1beta1/events", aBinding},
|
||||||
|
{"PUT", "/api/v1beta1/events", aBinding},
|
||||||
|
{"GET", "/api/v1beta1/events", ""},
|
||||||
|
{"GET", "/api/v1beta1/events/a", ""},
|
||||||
|
{"DELETE", "/api/v1beta1/events", ""},
|
||||||
|
|
||||||
|
// Non-existent object type.
|
||||||
|
{"GET", "/api/v1beta1/foo", ""},
|
||||||
|
{"GET", "/api/v1beta1/foo/a", ""},
|
||||||
|
{"POST", "/api/v1beta1/foo", `{"foo": "foo"}`},
|
||||||
|
{"PUT", "/api/v1beta1/foo", `{"foo": "foo"}`},
|
||||||
|
{"GET", "/api/v1beta1/foo", ""},
|
||||||
|
{"GET", "/api/v1beta1/foo/a", ""},
|
||||||
|
{"DELETE", "/api/v1beta1/foo", ""},
|
||||||
|
|
||||||
|
// Operations
|
||||||
|
{"GET", "/api/v1beta1/operations", ""},
|
||||||
|
{"GET", "/api/v1beta1/operations/1234567890", ""},
|
||||||
|
|
||||||
|
// Special verbs on pods
|
||||||
|
{"GET", "/api/v1beta1/proxy/pods/a", ""},
|
||||||
|
{"GET", "/api/v1beta1/redirect/pods/a", ""},
|
||||||
|
// TODO: test .../watch/..., which doesn't end before the test timeout.
|
||||||
|
|
||||||
|
// Non-object endpoints
|
||||||
|
{"GET", "/", ""},
|
||||||
|
{"GET", "/healthz", ""},
|
||||||
|
{"GET", "/versions", ""},
|
||||||
|
}
|
||||||
|
return requests
|
||||||
|
}
|
||||||
|
|
||||||
|
// The TestAuthMode* tests tests a large number of URLs and checks that they
|
||||||
|
// are FORBIDDEN or not, depending on the mode. They do not attempt to do
|
||||||
|
// detailed verification of behaviour beyond authorization. They are not
|
||||||
|
// fuzz tests.
|
||||||
|
//
|
||||||
|
// TODO(etune): write a fuzz test of the REST API.
|
||||||
|
func TestAuthModeAlwaysAllow(t *testing.T) {
|
||||||
|
deleteAllEtcdKeys()
|
||||||
|
|
||||||
|
// Set up a master
|
||||||
|
|
||||||
|
helper, err := master.NewEtcdHelper(newEtcdClient(), "v1beta1")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
m := master.New(&master.Config{
|
||||||
|
EtcdHelper: helper,
|
||||||
|
EnableLogsSupport: false,
|
||||||
|
EnableUISupport: false,
|
||||||
|
APIPrefix: "/api",
|
||||||
|
AuthorizationMode: "AlwaysAllow",
|
||||||
|
})
|
||||||
|
|
||||||
|
s := httptest.NewServer(m.Handler)
|
||||||
|
defer s.Close()
|
||||||
|
transport := http.DefaultTransport
|
||||||
|
|
||||||
|
for _, r := range getTestRequests() {
|
||||||
|
t.Logf("case %v", r)
|
||||||
|
bodyBytes := bytes.NewReader([]byte(r.body))
|
||||||
|
req, err := http.NewRequest(r.verb, s.URL+r.URL, bodyBytes)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
resp, err := transport.RoundTrip(req)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
if resp.StatusCode == http.StatusForbidden {
|
||||||
|
t.Errorf("Expected status other than Forbidden")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAuthModeAlwaysDeny(t *testing.T) {
|
||||||
|
deleteAllEtcdKeys()
|
||||||
|
|
||||||
|
// Set up a master
|
||||||
|
|
||||||
|
helper, err := master.NewEtcdHelper(newEtcdClient(), "v1beta1")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
m := master.New(&master.Config{
|
||||||
|
EtcdHelper: helper,
|
||||||
|
EnableLogsSupport: false,
|
||||||
|
EnableUISupport: false,
|
||||||
|
APIPrefix: "/api",
|
||||||
|
AuthorizationMode: "AlwaysDeny",
|
||||||
|
})
|
||||||
|
|
||||||
|
s := httptest.NewServer(m.Handler)
|
||||||
|
defer s.Close()
|
||||||
|
transport := http.DefaultTransport
|
||||||
|
|
||||||
|
for _, r := range getTestRequests() {
|
||||||
|
t.Logf("case %v", r)
|
||||||
|
bodyBytes := bytes.NewReader([]byte(r.body))
|
||||||
|
req, err := http.NewRequest(r.verb, s.URL+r.URL, bodyBytes)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := transport.RoundTrip(req)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
if resp.StatusCode != http.StatusForbidden {
|
||||||
|
t.Errorf("Expected status Forbidden but got status %v", resp.Status)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -44,6 +44,7 @@ func TestClient(t *testing.T) {
|
|||||||
EnableLogsSupport: false,
|
EnableLogsSupport: false,
|
||||||
EnableUISupport: false,
|
EnableUISupport: false,
|
||||||
APIPrefix: "/api",
|
APIPrefix: "/api",
|
||||||
|
AuthorizationMode: "AlwaysAllow",
|
||||||
})
|
})
|
||||||
|
|
||||||
s := httptest.NewServer(m.Handler)
|
s := httptest.NewServer(m.Handler)
|
||||||
|
Loading…
Reference in New Issue
Block a user