Merge pull request #25634 from ericchiang/rbac_api_group

Add RBAC authorization API group and authorizer
This commit is contained in:
Alex Mohr
2016-05-26 12:36:53 -07:00
92 changed files with 8630 additions and 67 deletions

View File

@@ -0,0 +1,110 @@
{
"swaggerVersion": "1.2",
"apiVersion": "",
"basePath": "https://10.10.10.10:443",
"resourcePath": "/apis/rbac.authorization.k8s.io",
"apis": [
{
"path": "/apis/rbac.authorization.k8s.io",
"description": "get information of a group",
"operations": [
{
"type": "unversioned.APIGroup",
"method": "GET",
"summary": "get information of a group",
"nickname": "getAPIGroup",
"parameters": [],
"produces": [
"application/json",
"application/yaml",
"application/vnd.kubernetes.protobuf"
],
"consumes": [
"application/json",
"application/yaml",
"application/vnd.kubernetes.protobuf"
]
}
]
}
],
"models": {
"unversioned.APIGroup": {
"id": "unversioned.APIGroup",
"description": "APIGroup contains the name, the supported versions, and the preferred version of a group.",
"required": [
"name",
"versions",
"serverAddressByClientCIDRs"
],
"properties": {
"kind": {
"type": "string",
"description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#types-kinds"
},
"apiVersion": {
"type": "string",
"description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#resources"
},
"name": {
"type": "string",
"description": "name is the name of the group."
},
"versions": {
"type": "array",
"items": {
"$ref": "unversioned.GroupVersionForDiscovery"
},
"description": "versions are the versions supported in this group."
},
"preferredVersion": {
"$ref": "unversioned.GroupVersionForDiscovery",
"description": "preferredVersion is the version preferred by the API server, which probably is the storage version."
},
"serverAddressByClientCIDRs": {
"type": "array",
"items": {
"$ref": "unversioned.ServerAddressByClientCIDR"
},
"description": "a map of client CIDR to server address that is serving this group. This is to help clients reach servers in the most network-efficient way possible. Clients can use the appropriate server address as per the CIDR that they match. In case of multiple matches, clients should use the longest matching CIDR. The server returns only those CIDRs that it thinks that the client can match. For example: the master will return an internal IP CIDR only, if the client reaches the server using an internal IP. Server looks at X-Forwarded-For header or X-Real-Ip header or request.RemoteAddr (in that order) to get the client IP."
}
}
},
"unversioned.GroupVersionForDiscovery": {
"id": "unversioned.GroupVersionForDiscovery",
"description": "GroupVersion contains the \"group/version\" and \"version\" string of a version. It is made a struct to keep extensiblity.",
"required": [
"groupVersion",
"version"
],
"properties": {
"groupVersion": {
"type": "string",
"description": "groupVersion specifies the API group and version in the form \"group/version\""
},
"version": {
"type": "string",
"description": "version specifies the version in the form of \"version\". This is to save the clients the trouble of splitting the GroupVersion."
}
}
},
"unversioned.ServerAddressByClientCIDR": {
"id": "unversioned.ServerAddressByClientCIDR",
"description": "ServerAddressByClientCIDR helps the client to determine the server address that they should use, depending on the clientCIDR that they match.",
"required": [
"clientCIDR",
"serverAddress"
],
"properties": {
"clientCIDR": {
"type": "string",
"description": "The CIDR with which clients can match their IP to figure out the server address that they should use."
},
"serverAddress": {
"type": "string",
"description": "Address of this server, suitable for a client that matches the above CIDR. This can be a hostname, hostname:port, IP or IP:port."
}
}
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -60,6 +60,14 @@
{ {
"path": "/apis/apps", "path": "/apis/apps",
"description": "get information of a group" "description": "get information of a group"
},
{
"path": "/apis/rbac.authorization.k8s.io/v1alpha1",
"description": "API at /apis/rbac.authorization.k8s.io/v1alpha1"
},
{
"path": "/apis/rbac.authorization.k8s.io",
"description": "get information of a group"
} }
], ],
"apiVersion": "", "apiVersion": "",

View File

@@ -46,6 +46,15 @@ import (
kubeletclient "k8s.io/kubernetes/pkg/kubelet/client" kubeletclient "k8s.io/kubernetes/pkg/kubelet/client"
"k8s.io/kubernetes/pkg/master" "k8s.io/kubernetes/pkg/master"
"k8s.io/kubernetes/pkg/registry/cachesize" "k8s.io/kubernetes/pkg/registry/cachesize"
"k8s.io/kubernetes/pkg/registry/clusterrole"
clusterroleetcd "k8s.io/kubernetes/pkg/registry/clusterrole/etcd"
"k8s.io/kubernetes/pkg/registry/clusterrolebinding"
clusterrolebindingetcd "k8s.io/kubernetes/pkg/registry/clusterrolebinding/etcd"
"k8s.io/kubernetes/pkg/registry/generic"
"k8s.io/kubernetes/pkg/registry/role"
roleetcd "k8s.io/kubernetes/pkg/registry/role/etcd"
"k8s.io/kubernetes/pkg/registry/rolebinding"
rolebindingetcd "k8s.io/kubernetes/pkg/registry/rolebinding/etcd"
"k8s.io/kubernetes/pkg/serviceaccount" "k8s.io/kubernetes/pkg/serviceaccount"
) )
@@ -198,6 +207,32 @@ func Run(s *options.APIServer) error {
} }
authorizationModeNames := strings.Split(s.AuthorizationMode, ",") authorizationModeNames := strings.Split(s.AuthorizationMode, ",")
modeEnabled := func(mode string) bool {
for _, m := range authorizationModeNames {
if m == mode {
return true
}
}
return false
}
if modeEnabled(apiserver.ModeRBAC) {
mustGetRESTOptions := func(resource string) generic.RESTOptions {
s, err := storageFactory.New(api.Resource(resource))
if err != nil {
glog.Fatalf("Unable to get %s storage: %v", resource, err)
}
return generic.RESTOptions{Storage: s, Decorator: generic.UndecoratedStorage}
}
// For initial bootstrapping go directly to etcd to avoid privillege escalation check.
s.AuthorizationConfig.RBACRoleRegistry = role.NewRegistry(roleetcd.NewREST(mustGetRESTOptions("roles")))
s.AuthorizationConfig.RBACRoleBindingRegistry = rolebinding.NewRegistry(rolebindingetcd.NewREST(mustGetRESTOptions("rolebindings")))
s.AuthorizationConfig.RBACClusterRoleRegistry = clusterrole.NewRegistry(clusterroleetcd.NewREST(mustGetRESTOptions("clusterroles")))
s.AuthorizationConfig.RBACClusterRoleBindingRegistry = clusterrolebinding.NewRegistry(clusterrolebindingetcd.NewREST(mustGetRESTOptions("clusterrolebindings")))
}
authorizer, err := apiserver.NewAuthorizerFromAuthorizationConfig(authorizationModeNames, s.AuthorizationConfig) authorizer, err := apiserver.NewAuthorizerFromAuthorizationConfig(authorizationModeNames, s.AuthorizationConfig)
if err != nil { if err != nil {
glog.Fatalf("Invalid Authorization Config: %v", err) glog.Fatalf("Invalid Authorization Config: %v", err)
@@ -216,6 +251,7 @@ func Run(s *options.APIServer) error {
genericConfig.Authenticator = authenticator genericConfig.Authenticator = authenticator
genericConfig.SupportsBasicAuth = len(s.BasicAuthFile) > 0 genericConfig.SupportsBasicAuth = len(s.BasicAuthFile) > 0
genericConfig.Authorizer = authorizer genericConfig.Authorizer = authorizer
genericConfig.AuthorizerRBACSuperUser = s.AuthorizationConfig.RBACSuperUser
genericConfig.AdmissionControl = admissionController genericConfig.AdmissionControl = admissionController
genericConfig.APIResourceConfigSource = storageFactory.APIResourceConfigSource genericConfig.APIResourceConfigSource = storageFactory.APIResourceConfigSource
genericConfig.MasterServiceNamespace = s.MasterServiceNamespace genericConfig.MasterServiceNamespace = s.MasterServiceNamespace

View File

@@ -63,7 +63,7 @@ func generatedBy(customArgs clientgenargs.Args) string {
return fmt.Sprintf("\n// This package is generated by client-gen with the default arguments.\n\n") return fmt.Sprintf("\n// This package is generated by client-gen with the default arguments.\n\n")
} }
func packageForGroup(gv unversioned.GroupVersion, typeList []*types.Type, packageBasePath string, srcTreePath string, boilerplate []byte, generatedBy string) generator.Package { func packageForGroup(gv unversioned.GroupVersion, typeList []*types.Type, packageBasePath string, srcTreePath string, inputPath string, boilerplate []byte, generatedBy string) generator.Package {
outputPackagePath := filepath.Join(packageBasePath, gv.Group, gv.Version) outputPackagePath := filepath.Join(packageBasePath, gv.Group, gv.Version)
return &generator.DefaultPackage{ return &generator.DefaultPackage{
PackageName: gv.Version, PackageName: gv.Version,
@@ -99,6 +99,7 @@ func packageForGroup(gv unversioned.GroupVersion, typeList []*types.Type, packag
OptionalName: normalization.BeforeFirstDot(gv.Group) + "_client", OptionalName: normalization.BeforeFirstDot(gv.Group) + "_client",
}, },
outputPackage: outputPackagePath, outputPackage: outputPackagePath,
inputPacakge: inputPath,
group: gv.Group, group: gv.Group,
version: gv.Version, version: gv.Version,
types: typeList, types: typeList,
@@ -216,9 +217,10 @@ func Packages(context *generator.Context, arguments *args.GeneratorArgs) generat
orderer := namer.Orderer{Namer: namer.NewPrivateNamer(0)} orderer := namer.Orderer{Namer: namer.NewPrivateNamer(0)}
for _, gv := range customArgs.GroupVersions { for _, gv := range customArgs.GroupVersions {
types := gvToTypes[gv] types := gvToTypes[gv]
packageList = append(packageList, packageForGroup(normalization.GroupVersion(gv), orderer.OrderTypes(types), typedClientBasePath, arguments.OutputBase, boilerplate, generatedBy)) inputPath := customArgs.GroupVersionToInputPath[gv]
packageList = append(packageList, packageForGroup(normalization.GroupVersion(gv), orderer.OrderTypes(types), typedClientBasePath, arguments.OutputBase, inputPath, boilerplate, generatedBy))
if customArgs.FakeClient { if customArgs.FakeClient {
packageList = append(packageList, fake.PackageForGroup(normalization.GroupVersion(gv), orderer.OrderTypes(types), typedClientBasePath, arguments.OutputBase, boilerplate, generatedBy)) packageList = append(packageList, fake.PackageForGroup(normalization.GroupVersion(gv), orderer.OrderTypes(types), typedClientBasePath, arguments.OutputBase, inputPath, boilerplate, generatedBy))
} }
} }

View File

@@ -27,7 +27,7 @@ import (
"k8s.io/kubernetes/pkg/api/unversioned" "k8s.io/kubernetes/pkg/api/unversioned"
) )
func PackageForGroup(gv unversioned.GroupVersion, typeList []*types.Type, packageBasePath string, srcTreePath string, boilerplate []byte, generatedBy string) generator.Package { func PackageForGroup(gv unversioned.GroupVersion, typeList []*types.Type, packageBasePath string, srcTreePath string, inputPath string, boilerplate []byte, generatedBy string) generator.Package {
outputPackagePath := filepath.Join(packageBasePath, gv.Group, gv.Version, "fake") outputPackagePath := filepath.Join(packageBasePath, gv.Group, gv.Version, "fake")
// TODO: should make this a function, called by here and in client-generator.go // TODO: should make this a function, called by here and in client-generator.go
realClientPath := filepath.Join(packageBasePath, gv.Group, gv.Version) realClientPath := filepath.Join(packageBasePath, gv.Group, gv.Version)
@@ -55,6 +55,7 @@ func PackageForGroup(gv unversioned.GroupVersion, typeList []*types.Type, packag
}, },
outputPackage: outputPackagePath, outputPackage: outputPackagePath,
group: normalization.BeforeFirstDot(gv.Group), group: normalization.BeforeFirstDot(gv.Group),
inputPackage: inputPath,
version: gv.Version, version: gv.Version,
typeToMatch: t, typeToMatch: t,
imports: generator.NewImportTracker(), imports: generator.NewImportTracker(),

View File

@@ -31,6 +31,7 @@ type genFakeForType struct {
generator.DefaultGen generator.DefaultGen
outputPackage string outputPackage string
group string group string
inputPackage string
version string version string
typeToMatch *types.Type typeToMatch *types.Type
imports namer.ImportTracker imports namer.ImportTracker
@@ -87,6 +88,20 @@ func (g *genFakeForType) GenerateType(c *generator.Context, t *types.Type, w io.
if canonicalVersion == "unversioned" { if canonicalVersion == "unversioned" {
canonicalVersion = "" canonicalVersion = ""
} }
groupName := g.group
if g.group == "core" {
groupName = ""
}
// allow user to define a group name that's different from the one parsed from the directory.
for _, comment := range c.Universe.Package(g.inputPackage).DocComments {
comment = strings.TrimLeft(comment, "//")
if override, ok := types.ExtractCommentTags("+", comment)["groupName"]; ok {
groupName = override
}
}
m := map[string]interface{}{ m := map[string]interface{}{
"type": t, "type": t,
"package": pkg, "package": pkg,
@@ -94,6 +109,7 @@ func (g *genFakeForType) GenerateType(c *generator.Context, t *types.Type, w io.
"namespaced": namespaced, "namespaced": namespaced,
"Group": namer.IC(g.group), "Group": namer.IC(g.group),
"group": canonicalGroup, "group": canonicalGroup,
"groupName": groupName,
"version": canonicalVersion, "version": canonicalVersion,
"watchInterface": c.Universe.Type(types.Name{Package: "k8s.io/kubernetes/pkg/watch", Name: "Interface"}), "watchInterface": c.Universe.Type(types.Name{Package: "k8s.io/kubernetes/pkg/watch", Name: "Interface"}),
"apiDeleteOptions": c.Universe.Type(types.Name{Package: "k8s.io/kubernetes/pkg/api", Name: "DeleteOptions"}), "apiDeleteOptions": c.Universe.Type(types.Name{Package: "k8s.io/kubernetes/pkg/api", Name: "DeleteOptions"}),
@@ -168,7 +184,7 @@ type Fake$.type|publicPlural$ struct {
` `
var resource = ` var resource = `
var $.type|allLowercasePlural$Resource = $.GroupVersionResource|raw${Group: "$.group$", Version: "$.version$", Resource: "$.type|allLowercasePlural$"} var $.type|allLowercasePlural$Resource = $.GroupVersionResource|raw${Group: "$.groupName$", Version: "$.version$", Resource: "$.type|allLowercasePlural$"}
` `
var listTemplate = ` var listTemplate = `

View File

@@ -18,6 +18,7 @@ package generators
import ( import (
"io" "io"
"strings"
"k8s.io/kubernetes/cmd/libs/go2idl/client-gen/generators/normalization" "k8s.io/kubernetes/cmd/libs/go2idl/client-gen/generators/normalization"
"k8s.io/kubernetes/cmd/libs/go2idl/generator" "k8s.io/kubernetes/cmd/libs/go2idl/generator"
@@ -32,8 +33,9 @@ type genGroup struct {
group string group string
version string version string
// types in this group // types in this group
types []*types.Type types []*types.Type
imports namer.ImportTracker imports namer.ImportTracker
inputPacakge string
} }
var _ generator.Generator = &genGroup{} var _ generator.Generator = &genGroup{}
@@ -66,17 +68,22 @@ func (g *genGroup) GenerateType(c *generator.Context, t *types.Type, w io.Writer
return `"/apis"` return `"/apis"`
} }
canonize := func(group string) string { groupName := g.group
if group == "core" { if g.group == "core" {
return "" groupName = ""
}
// allow user to define a group name that's different from the one parsed from the directory.
for _, comment := range c.Universe.Package(g.inputPacakge).DocComments {
comment = strings.TrimLeft(comment, "//")
if override, ok := types.ExtractCommentTags("+", comment)["groupName"]; ok && override != "" {
groupName = override
} }
return group
} }
m := map[string]interface{}{ m := map[string]interface{}{
"group": normalization.BeforeFirstDot(g.group), "group": normalization.BeforeFirstDot(g.group),
"Group": namer.IC(normalization.BeforeFirstDot(g.group)), "Group": namer.IC(normalization.BeforeFirstDot(g.group)),
"canonicalGroup": canonize(g.group), "groupName": groupName,
"types": g.types, "types": g.types,
"Config": c.Universe.Type(types.Name{Package: pkgRESTClient, Name: "Config"}), "Config": c.Universe.Type(types.Name{Package: pkgRESTClient, Name: "Config"}),
"DefaultKubernetesUserAgent": c.Universe.Function(types.Name{Package: pkgRESTClient, Name: "DefaultKubernetesUserAgent"}), "DefaultKubernetesUserAgent": c.Universe.Function(types.Name{Package: pkgRESTClient, Name: "DefaultKubernetesUserAgent"}),
@@ -190,7 +197,7 @@ func New(c *$.RESTClient|raw$) *$.Group$Client {
var setInternalVersionClientDefaultsTemplate = ` var setInternalVersionClientDefaultsTemplate = `
func setConfigDefaults(config *$.Config|raw$) error { func setConfigDefaults(config *$.Config|raw$) error {
// if $.group$ group is not registered, return an error // if $.group$ group is not registered, return an error
g, err := $.latestGroup|raw$("$.canonicalGroup$") g, err := $.latestGroup|raw$("$.groupName$")
if err != nil { if err != nil {
return err return err
} }
@@ -219,7 +226,7 @@ func setConfigDefaults(config *$.Config|raw$) error {
var setClientDefaultsTemplate = ` var setClientDefaultsTemplate = `
func setConfigDefaults(config *$.Config|raw$) error { func setConfigDefaults(config *$.Config|raw$) error {
// if $.group$ group is not registered, return an error // if $.group$ group is not registered, return an error
g, err := $.latestGroup|raw$("$.canonicalGroup$") g, err := $.latestGroup|raw$("$.groupName$")
if err != nil { if err != nil {
return err return err
} }

View File

@@ -34,7 +34,7 @@ import (
var ( var (
test = flag.BoolP("test", "t", false, "set this flag to generate the client code for the testdata") test = flag.BoolP("test", "t", false, "set this flag to generate the client code for the testdata")
inputVersions = flag.StringSlice("input", []string{"api/", "extensions/", "autoscaling/", "batch/"}, "group/versions that client-gen will generate clients for. At most one version per group is allowed. Specified in the format \"group1/version1,group2/version2...\". Default to \"api/,extensions/,autoscaling/,batch/\"") inputVersions = flag.StringSlice("input", []string{"api/", "extensions/", "autoscaling/", "batch/", "rbac/"}, "group/versions that client-gen will generate clients for. At most one version per group is allowed. Specified in the format \"group1/version1,group2/version2...\". Default to \"api/,extensions/,autoscaling/,batch/,rbac/\"")
includedTypesOverrides = flag.StringSlice("included-types-overrides", []string{}, "list of group/version/type for which client should be generated. By default, client is generated for all types which have genclient=true in types.go. This overrides that. For each groupVersion in this list, only the types mentioned here will be included. The default check of genclient=true will be used for other group versions.") includedTypesOverrides = flag.StringSlice("included-types-overrides", []string{}, "list of group/version/type for which client should be generated. By default, client is generated for all types which have genclient=true in types.go. This overrides that. For each groupVersion in this list, only the types mentioned here will be included. The default check of genclient=true will be used for other group versions.")
basePath = flag.String("input-base", "k8s.io/kubernetes/pkg/apis", "base path to look for the api group. Default to \"k8s.io/kubernetes/pkg/apis\"") basePath = flag.String("input-base", "k8s.io/kubernetes/pkg/apis", "base path to look for the api group. Default to \"k8s.io/kubernetes/pkg/apis\"")
clientsetName = flag.StringP("clientset-name", "n", "internalclientset", "the name of the generated clientset package.") clientsetName = flag.StringP("clientset-name", "n", "internalclientset", "the name of the generated clientset package.")

View File

@@ -0,0 +1,18 @@
/*
Copyright 2015 The Kubernetes Authors 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.
*/
// +groupName=testgroup.k8s.io
package testgroup

View File

@@ -31,7 +31,7 @@ type FakeTestTypes struct {
ns string ns string
} }
var testtypesResource = unversioned.GroupVersionResource{Group: "testgroup", Version: "", Resource: "testtypes"} var testtypesResource = unversioned.GroupVersionResource{Group: "testgroup.k8s.io", Version: "", Resource: "testtypes"}
func (c *FakeTestTypes) Create(testType *testgroup_k8s_io.TestType) (result *testgroup_k8s_io.TestType, err error) { func (c *FakeTestTypes) Create(testType *testgroup_k8s_io.TestType) (result *testgroup_k8s_io.TestType, err error) {
obj, err := c.Fake. obj, err := c.Fake.

View File

@@ -58,8 +58,9 @@ kube-apiserver
--apiserver-count=1: The number of apiservers running in the cluster --apiserver-count=1: The number of apiservers running in the cluster
--authentication-token-webhook-cache-ttl=2m0s: The duration to cache responses from the webhook token authenticator. Default is 2m --authentication-token-webhook-cache-ttl=2m0s: The duration to cache responses from the webhook token authenticator. Default is 2m
--authentication-token-webhook-config-file="": File with webhook configuration for token authentication in kubeconfig format. The API server will query the remote service to determine authentication for bearer tokens. --authentication-token-webhook-config-file="": File with webhook configuration for token authentication in kubeconfig format. The API server will query the remote service to determine authentication for bearer tokens.
--authorization-mode="AlwaysAllow": Ordered list of plug-ins to do authorization on secure port. Comma-delimited list of: AlwaysAllow,AlwaysDeny,ABAC,Webhook --authorization-mode="AlwaysAllow": Ordered list of plug-ins to do authorization on secure port. Comma-delimited list of: AlwaysAllow,AlwaysDeny,ABAC,Webhook,RBAC
--authorization-policy-file="": File with authorization policy in csv format, used with --authorization-mode=ABAC, on the secure port. --authorization-policy-file="": File with authorization policy in csv format, used with --authorization-mode=ABAC, on the secure port.
--authorization-rbac-super-user="": If specified, a username which avoids RBAC authorization checks and role binding privilege escalation checks, to be used with --authorization-mode=RBAC.
--authorization-webhook-cache-authorized-ttl=5m0s: The duration to cache 'authorized' responses from the webhook authorizer. Default is 5m. --authorization-webhook-cache-authorized-ttl=5m0s: The duration to cache 'authorized' responses from the webhook authorizer. Default is 5m.
--authorization-webhook-cache-unauthorized-ttl=30s: The duration to cache 'unauthorized' responses from the webhook authorizer. Default is 30s. --authorization-webhook-cache-unauthorized-ttl=30s: The duration to cache 'unauthorized' responses from the webhook authorizer. Default is 30s.
--authorization-webhook-config-file="": File with webhook configuration in kubeconfig format, used with --authorization-mode=Webhook. The API server will query the remote service to determine access on the API server's secure port. --authorization-webhook-config-file="": File with webhook configuration in kubeconfig format, used with --authorization-mode=Webhook. The API server will query the remote service to determine access on the API server's secure port.
@@ -115,7 +116,7 @@ kube-apiserver
--ssh-user="": If non-empty, use secure SSH proxy to the nodes, using this user name --ssh-user="": If non-empty, use secure SSH proxy to the nodes, using this user name
--storage-backend="": The storage backend for persistence. Options: 'etcd2' (default), 'etcd3'. --storage-backend="": The storage backend for persistence. Options: 'etcd2' (default), 'etcd3'.
--storage-media-type="application/json": The media type to use to store objects in storage. Defaults to application/json. Some resources may only support a specific media type and will ignore this setting. --storage-media-type="application/json": The media type to use to store objects in storage. Defaults to application/json. Some resources may only support a specific media type and will ignore this setting.
--storage-versions="apps/v1alpha1,authentication.k8s.io/v1beta1,authorization.k8s.io/v1beta1,autoscaling/v1,batch/v1,componentconfig/v1alpha1,extensions/v1beta1,metrics/v1alpha1,policy/v1alpha1,v1": The per-group version to store resources in. Specified in the format "group1/version1,group2/version2,...". In the case where objects are moved from one group to the other, you may specify the format "group1=group2/v1beta1,group3/v1beta1,...". You only need to pass the groups you wish to change from the defaults. It defaults to a list of preferred versions of all registered groups, which is derived from the KUBE_API_VERSIONS environment variable. --storage-versions="apps/v1alpha1,authentication.k8s.io/v1beta1,authorization.k8s.io/v1beta1,autoscaling/v1,batch/v1,componentconfig/v1alpha1,extensions/v1beta1,metrics/v1alpha1,policy/v1alpha1,rbac.authorization.k8s.io/v1alpha1,v1": The per-group version to store resources in. Specified in the format "group1/version1,group2/version2,...". In the case where objects are moved from one group to the other, you may specify the format "group1=group2/v1beta1,group3/v1beta1,...". You only need to pass the groups you wish to change from the defaults. It defaults to a list of preferred versions of all registered groups, which is derived from the KUBE_API_VERSIONS environment variable.
--tls-cert-file="": File containing x509 Certificate for HTTPS. (CA cert, if any, concatenated after server cert). If HTTPS serving is enabled, and --tls-cert-file and --tls-private-key-file are not provided, a self-signed certificate and key are generated for the public address and saved to /var/run/kubernetes. --tls-cert-file="": File containing x509 Certificate for HTTPS. (CA cert, if any, concatenated after server cert). If HTTPS serving is enabled, and --tls-cert-file and --tls-private-key-file are not provided, a self-signed certificate and key are generated for the public address and saved to /var/run/kubernetes.
--tls-private-key-file="": File containing x509 private key matching --tls-cert-file. --tls-private-key-file="": File containing x509 private key matching --tls-cert-file.
--token-auth-file="": If set, the file that will be used to secure the secure port of the API server via token authentication. --token-auth-file="": If set, the file that will be used to secure the secure port of the API server via token authentication.
@@ -123,7 +124,7 @@ kube-apiserver
--watch-cache-sizes=[]: List of watch cache sizes for every resource (pods, nodes, etc.), comma separated. The individual override format: resource#size, where size is a number. It takes effect when watch-cache is enabled. --watch-cache-sizes=[]: List of watch cache sizes for every resource (pods, nodes, etc.), comma separated. The individual override format: resource#size, where size is a number. It takes effect when watch-cache is enabled.
``` ```
###### Auto generated by spf13/cobra on 18-May-2016 ###### Auto generated by spf13/cobra on 23-May-2016
<!-- BEGIN MUNGE: GENERATED_ANALYTICS --> <!-- BEGIN MUNGE: GENERATED_ANALYTICS -->

View File

@@ -58,7 +58,7 @@ KUBE_GOVERALLS_BIN=${KUBE_GOVERALLS_BIN:-}
# Lists of API Versions of each groups that should be tested, groups are # Lists of API Versions of each groups that should be tested, groups are
# separated by comma, lists are separated by semicolon. e.g., # separated by comma, lists are separated by semicolon. e.g.,
# "v1,compute/v1alpha1,experimental/v1alpha2;v1,compute/v2,experimental/v1alpha3" # "v1,compute/v1alpha1,experimental/v1alpha2;v1,compute/v2,experimental/v1alpha3"
KUBE_TEST_API_VERSIONS=${KUBE_TEST_API_VERSIONS:-"v1,extensions/v1beta1,metrics/v1alpha1,federation/v1alpha1;v1,autoscaling/v1,batch/v1,batch/v2alpha1,extensions/v1beta1,apps/v1alpha1,metrics/v1alpha1,federation/v1alpha1,policy/v1alpha1"} KUBE_TEST_API_VERSIONS=${KUBE_TEST_API_VERSIONS:-"v1,extensions/v1beta1,metrics/v1alpha1,federation/v1alpha1;v1,autoscaling/v1,batch/v1,batch/v2alpha1,extensions/v1beta1,apps/v1alpha1,metrics/v1alpha1,federation/v1alpha1,policy/v1alpha1,rbac.authorization.k8s.io/v1alpha1"}
# once we have multiple group supports # once we have multiple group supports
# Run tests with the standard (registry) and a custom etcd prefix # Run tests with the standard (registry) and a custom etcd prefix
# (kubernetes.io/registry). # (kubernetes.io/registry).

View File

@@ -29,7 +29,7 @@ source "${KUBE_ROOT}/hack/lib/init.sh"
# "v1,compute/v1alpha1,experimental/v1alpha2;v1,compute/v2,experimental/v1alpha3" # "v1,compute/v1alpha1,experimental/v1alpha2;v1,compute/v2,experimental/v1alpha3"
# TODO: It's going to be: # TODO: It's going to be:
# KUBE_TEST_API_VERSIONS=${KUBE_TEST_API_VERSIONS:-"v1,extensions/v1beta1"} # KUBE_TEST_API_VERSIONS=${KUBE_TEST_API_VERSIONS:-"v1,extensions/v1beta1"}
KUBE_TEST_API_VERSIONS=${KUBE_TEST_API_VERSIONS:-"v1,extensions/v1beta1;v1,autoscaling/v1,batch/v1,apps/v1alpha1,policy/v1alpha1,extensions/v1beta1"} KUBE_TEST_API_VERSIONS=${KUBE_TEST_API_VERSIONS:-"v1,extensions/v1beta1;v1,autoscaling/v1,batch/v1,apps/v1alpha1,policy/v1alpha1,extensions/v1beta1,rbac.authorization.k8s.io/v1alpha1"}
# Give integration tests longer to run # Give integration tests longer to run
# TODO: allow a larger value to be passed in # TODO: allow a larger value to be passed in

View File

@@ -74,7 +74,7 @@ APISERVER_PID=$!
kube::util::wait_for_url "http://127.0.0.1:${API_PORT}/healthz" "apiserver: " kube::util::wait_for_url "http://127.0.0.1:${API_PORT}/healthz" "apiserver: "
SWAGGER_API_PATH="http://127.0.0.1:${API_PORT}/swaggerapi/" SWAGGER_API_PATH="http://127.0.0.1:${API_PORT}/swaggerapi/"
DEFAULT_GROUP_VERSIONS="v1 autoscaling/v1 batch/v1 batch/v2alpha1 extensions/v1beta1 apps/v1alpha1 policy/v1alpha1" DEFAULT_GROUP_VERSIONS="v1 autoscaling/v1 batch/v1 batch/v2alpha1 extensions/v1beta1 apps/v1alpha1 policy/v1alpha1 rbac.authorization.k8s.io/v1alpha1"
VERSIONS=${VERSIONS:-$DEFAULT_GROUP_VERSIONS} VERSIONS=${VERSIONS:-$DEFAULT_GROUP_VERSIONS}
kube::log::status "Updating " ${SWAGGER_ROOT_DIR} kube::log::status "Updating " ${SWAGGER_ROOT_DIR}

View File

@@ -22,6 +22,7 @@ authentication-token-webhook-cache-ttl
authentication-token-webhook-config-file authentication-token-webhook-config-file
authorization-mode authorization-mode
authorization-policy-file authorization-policy-file
authorization-rbac-super-user
authorization-webhook-config-file authorization-webhook-config-file
authorization-webhook-cache-authorized-ttl authorization-webhook-cache-authorized-ttl
authorization-webhook-cache-unauthorized-ttl authorization-webhook-cache-unauthorized-ttl

View File

@@ -34,6 +34,7 @@ import (
"k8s.io/kubernetes/pkg/apis/batch" "k8s.io/kubernetes/pkg/apis/batch"
"k8s.io/kubernetes/pkg/apis/extensions" "k8s.io/kubernetes/pkg/apis/extensions"
"k8s.io/kubernetes/pkg/apis/policy" "k8s.io/kubernetes/pkg/apis/policy"
"k8s.io/kubernetes/pkg/apis/rbac"
"k8s.io/kubernetes/pkg/runtime" "k8s.io/kubernetes/pkg/runtime"
"k8s.io/kubernetes/pkg/runtime/serializer/recognizer" "k8s.io/kubernetes/pkg/runtime/serializer/recognizer"
@@ -46,6 +47,7 @@ import (
_ "k8s.io/kubernetes/pkg/apis/extensions/install" _ "k8s.io/kubernetes/pkg/apis/extensions/install"
_ "k8s.io/kubernetes/pkg/apis/metrics/install" _ "k8s.io/kubernetes/pkg/apis/metrics/install"
_ "k8s.io/kubernetes/pkg/apis/policy/install" _ "k8s.io/kubernetes/pkg/apis/policy/install"
_ "k8s.io/kubernetes/pkg/apis/rbac/install"
) )
var ( var (
@@ -57,6 +59,7 @@ var (
Apps TestGroup Apps TestGroup
Policy TestGroup Policy TestGroup
Federation TestGroup Federation TestGroup
Rbac TestGroup
serializer runtime.SerializerInfo serializer runtime.SerializerInfo
storageSerializer runtime.SerializerInfo storageSerializer runtime.SerializerInfo
@@ -191,6 +194,13 @@ func init() {
internalTypes: api.Scheme.KnownTypes(federation.SchemeGroupVersion), internalTypes: api.Scheme.KnownTypes(federation.SchemeGroupVersion),
} }
} }
if _, ok := Groups[rbac.GroupName]; !ok {
Groups[rbac.GroupName] = TestGroup{
externalGroupVersions: []unversioned.GroupVersion{{Group: rbac.GroupName, Version: registered.GroupOrDie(rbac.GroupName).GroupVersion.Version}},
internalGroupVersion: rbac.SchemeGroupVersion,
internalTypes: api.Scheme.KnownTypes(rbac.SchemeGroupVersion),
}
}
Default = Groups[api.GroupName] Default = Groups[api.GroupName]
Autoscaling = Groups[autoscaling.GroupName] Autoscaling = Groups[autoscaling.GroupName]
@@ -199,6 +209,7 @@ func init() {
Policy = Groups[policy.GroupName] Policy = Groups[policy.GroupName]
Extensions = Groups[extensions.GroupName] Extensions = Groups[extensions.GroupName]
Federation = Groups[federation.GroupName] Federation = Groups[federation.GroupName]
Rbac = Groups[rbac.GroupName]
} }
func (g TestGroup) ContentConfig() (string, *unversioned.GroupVersion, runtime.Codec) { func (g TestGroup) ContentConfig() (string, *unversioned.GroupVersion, runtime.Codec) {

18
pkg/apis/rbac/doc.go Normal file
View File

@@ -0,0 +1,18 @@
/*
Copyright 2016 The Kubernetes Authors 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.
*/
// +groupName=rbac.authorization.k8s.io
package rbac

View File

@@ -0,0 +1,114 @@
/*
Copyright 2016 The Kubernetes Authors 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 install
import (
"encoding/json"
"testing"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/unversioned"
"k8s.io/kubernetes/pkg/apimachinery/registered"
"k8s.io/kubernetes/pkg/apis/rbac"
"k8s.io/kubernetes/pkg/apis/rbac/v1alpha1"
"k8s.io/kubernetes/pkg/runtime"
)
func TestResourceVersioner(t *testing.T) {
roleBinding := rbac.RoleBinding{ObjectMeta: api.ObjectMeta{ResourceVersion: "10"}}
version, err := accessor.ResourceVersion(&roleBinding)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if version != "10" {
t.Errorf("unexpected version %v", version)
}
roleBindingList := rbac.RoleBindingList{ListMeta: unversioned.ListMeta{ResourceVersion: "10"}}
version, err = accessor.ResourceVersion(&roleBindingList)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if version != "10" {
t.Errorf("unexpected version %v", version)
}
}
func TestCodec(t *testing.T) {
roleBinding := rbac.RoleBinding{}
// We do want to use package registered rather than testapi here, because we
// want to test if the package install and package registered work as expected.
data, err := runtime.Encode(api.Codecs.LegacyCodec(registered.GroupOrDie(rbac.GroupName).GroupVersion), &roleBinding)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
other := rbac.RoleBinding{}
if err := json.Unmarshal(data, &other); err != nil {
t.Fatalf("unexpected error: %v", err)
}
if other.APIVersion != registered.GroupOrDie(rbac.GroupName).GroupVersion.String() || other.Kind != "RoleBinding" {
t.Errorf("unexpected unmarshalled object %#v", other)
}
}
func TestInterfacesFor(t *testing.T) {
if _, err := registered.GroupOrDie(rbac.GroupName).InterfacesFor(rbac.SchemeGroupVersion); err == nil {
t.Fatalf("unexpected non-error: %v", err)
}
for i, version := range registered.GroupOrDie(rbac.GroupName).GroupVersions {
if vi, err := registered.GroupOrDie(rbac.GroupName).InterfacesFor(version); err != nil || vi == nil {
t.Fatalf("%d: unexpected result: %v", i, err)
}
}
}
func TestRESTMapper(t *testing.T) {
gv := v1alpha1.SchemeGroupVersion
roleBindingGVK := gv.WithKind("RoleBinding")
if gvk, err := registered.GroupOrDie(rbac.GroupName).RESTMapper.KindFor(gv.WithResource("rolebindings")); err != nil || gvk != roleBindingGVK {
t.Errorf("unexpected version mapping: %v %v", gvk, err)
}
for _, version := range registered.GroupOrDie(rbac.GroupName).GroupVersions {
mapping, err := registered.GroupOrDie(rbac.GroupName).RESTMapper.RESTMapping(roleBindingGVK.GroupKind(), version.Version)
if err != nil {
t.Errorf("unexpected error: %v", err)
}
if mapping.Resource != "rolebindings" {
t.Errorf("incorrect resource name: %#v", mapping)
}
if mapping.GroupVersionKind.GroupVersion() != version {
t.Errorf("incorrect groupVersion: %v", mapping)
}
interfaces, _ := registered.GroupOrDie(rbac.GroupName).InterfacesFor(version)
if mapping.ObjectConvertor != interfaces.ObjectConvertor {
t.Errorf("unexpected: %#v, expected: %#v", mapping, interfaces)
}
roleBinding := &rbac.RoleBinding{ObjectMeta: api.ObjectMeta{Name: "foo"}}
name, err := mapping.MetadataAccessor.Name(roleBinding)
if err != nil {
t.Errorf("unexpected error: %v", err)
}
if name != "foo" {
t.Errorf("unable to retrieve object meta with: %v", mapping.MetadataAccessor)
}
}
}

View File

@@ -36,6 +36,8 @@ const (
GroupKind = "Group" GroupKind = "Group"
ServiceAccountKind = "ServiceAccount" ServiceAccountKind = "ServiceAccount"
UserKind = "User" UserKind = "User"
UserAll = "*"
) )
// PolicyRule holds information that describes a policy rule, but does not contain information // PolicyRule holds information that describes a policy rule, but does not contain information
@@ -66,7 +68,7 @@ type Subject struct {
// If the Authorizer does not recognized the kind value, the Authorizer should report an error. // If the Authorizer does not recognized the kind value, the Authorizer should report an error.
Kind string Kind string
// APIVersion holds the API group and version of the referenced object. For non-object references such as "Group" and "User" this is // APIVersion holds the API group and version of the referenced object. For non-object references such as "Group" and "User" this is
// expected to be API version of this API group. For example "rbac.authorization.k8s.io/v1alpha1". // expected to be API version of this API group. For example "rbac/v1alpha1".
APIVersion string APIVersion string
// Name of the object being referenced. // Name of the object being referenced.
Name string Name string
@@ -75,6 +77,8 @@ type Subject struct {
Namespace string Namespace string
} }
// +genclient=true
// Role is a namespaced, logical grouping of PolicyRules that can be referenced as a unit by a RoleBinding. // Role is a namespaced, logical grouping of PolicyRules that can be referenced as a unit by a RoleBinding.
type Role struct { type Role struct {
unversioned.TypeMeta unversioned.TypeMeta
@@ -85,6 +89,8 @@ type Role struct {
Rules []PolicyRule Rules []PolicyRule
} }
// +genclient=true
// RoleBinding references a role, but does not contain it. It can reference a Role in the same namespace or a ClusterRole in the global namespace. // RoleBinding references a role, but does not contain it. It can reference a Role in the same namespace or a ClusterRole in the global namespace.
// It adds who information via Subjects and namespace information by which namespace it exists in. RoleBindings in a given // It adds who information via Subjects and namespace information by which namespace it exists in. RoleBindings in a given
// namespace only have effect in that namespace. // namespace only have effect in that namespace.
@@ -120,6 +126,8 @@ type RoleList struct {
Items []Role Items []Role
} }
// +genclient=true,nonNamespaced=true
// ClusterRole is a cluster level, logical grouping of PolicyRules that can be referenced as a unit by a RoleBinding or ClusterRoleBinding. // ClusterRole is a cluster level, logical grouping of PolicyRules that can be referenced as a unit by a RoleBinding or ClusterRoleBinding.
type ClusterRole struct { type ClusterRole struct {
unversioned.TypeMeta unversioned.TypeMeta
@@ -130,6 +138,8 @@ type ClusterRole struct {
Rules []PolicyRule Rules []PolicyRule
} }
// +genclient=true,nonNamespaced=true
// ClusterRoleBinding references a ClusterRole, but not contain it. It can reference a ClusterRole in the global namespace, // ClusterRoleBinding references a ClusterRole, but not contain it. It can reference a ClusterRole in the global namespace,
// and adds who information via Subject. // and adds who information via Subject.
type ClusterRoleBinding struct { type ClusterRoleBinding struct {

View File

@@ -14,5 +14,6 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
// +groupName=rbac.authorization.k8s.io
// +genconversion=true // +genconversion=true
package v1alpha1 package v1alpha1

View File

@@ -147,7 +147,7 @@ message Subject {
optional string kind = 1; optional string kind = 1;
// APIVersion holds the API group and version of the referenced object. For non-object references such as "Group" and "User" this is // APIVersion holds the API group and version of the referenced object. For non-object references such as "Group" and "User" this is
// expected to be API version of this API group. For example "rbac.authorization.k8s.io/v1alpha1". // expected to be API version of this API group. For example "rbac/v1alpha1".
optional string apiVersion = 2; optional string apiVersion = 2;
// Name of the object being referenced. // Name of the object being referenced.

View File

@@ -24,7 +24,7 @@ import (
) )
// SchemeGroupVersion is group version used to register these objects // SchemeGroupVersion is group version used to register these objects
var SchemeGroupVersion = unversioned.GroupVersion{Group: rbac.GroupName, Version: "v1alpha"} var SchemeGroupVersion = unversioned.GroupVersion{Group: rbac.GroupName, Version: "v1alpha1"}
func AddToScheme(scheme *runtime.Scheme) { func AddToScheme(scheme *runtime.Scheme) {
addKnownTypes(scheme) addKnownTypes(scheme)

View File

@@ -55,7 +55,7 @@ type Subject struct {
// If the Authorizer does not recognized the kind value, the Authorizer should report an error. // If the Authorizer does not recognized the kind value, the Authorizer should report an error.
Kind string `json:"kind" protobuf:"bytes,1,opt,name=kind"` Kind string `json:"kind" protobuf:"bytes,1,opt,name=kind"`
// APIVersion holds the API group and version of the referenced object. For non-object references such as "Group" and "User" this is // APIVersion holds the API group and version of the referenced object. For non-object references such as "Group" and "User" this is
// expected to be API version of this API group. For example "rbac.authorization.k8s.io/v1alpha1". // expected to be API version of this API group. For example "rbac/v1alpha1".
APIVersion string `json:"apiVersion" protobuf:"bytes,2,opt.name=apiVersion"` APIVersion string `json:"apiVersion" protobuf:"bytes,2,opt.name=apiVersion"`
// Name of the object being referenced. // Name of the object being referenced.
Name string `json:"name" protobuf:"bytes,3,opt,name=name"` Name string `json:"name" protobuf:"bytes,3,opt,name=name"`
@@ -64,6 +64,8 @@ type Subject struct {
Namespace string `json:"namespace,omitempty" protobuf:"bytes,4,opt,name=namespace"` Namespace string `json:"namespace,omitempty" protobuf:"bytes,4,opt,name=namespace"`
} }
// +genclient=true
// Role is a namespaced, logical grouping of PolicyRules that can be referenced as a unit by a RoleBinding. // Role is a namespaced, logical grouping of PolicyRules that can be referenced as a unit by a RoleBinding.
type Role struct { type Role struct {
unversioned.TypeMeta `json:",inline"` unversioned.TypeMeta `json:",inline"`
@@ -74,6 +76,8 @@ type Role struct {
Rules []PolicyRule `json:"rules" protobuf:"bytes,2,rep,name=rules"` Rules []PolicyRule `json:"rules" protobuf:"bytes,2,rep,name=rules"`
} }
// +genclient=true
// RoleBinding references a role, but does not contain it. It can reference a Role in the same namespace or a ClusterRole in the global namespace. // RoleBinding references a role, but does not contain it. It can reference a Role in the same namespace or a ClusterRole in the global namespace.
// It adds who information via Subjects and namespace information by which namespace it exists in. RoleBindings in a given // It adds who information via Subjects and namespace information by which namespace it exists in. RoleBindings in a given
// namespace only have effect in that namespace. // namespace only have effect in that namespace.
@@ -110,6 +114,8 @@ type RoleList struct {
Items []Role `json:"items" protobuf:"bytes,2,rep,name=items"` Items []Role `json:"items" protobuf:"bytes,2,rep,name=items"`
} }
// +genclient=true,nonNamespaced=true
// ClusterRole is a cluster level, logical grouping of PolicyRules that can be referenced as a unit by a RoleBinding or ClusterRoleBinding. // ClusterRole is a cluster level, logical grouping of PolicyRules that can be referenced as a unit by a RoleBinding or ClusterRoleBinding.
type ClusterRole struct { type ClusterRole struct {
unversioned.TypeMeta `json:",inline"` unversioned.TypeMeta `json:",inline"`
@@ -120,6 +126,8 @@ type ClusterRole struct {
Rules []PolicyRule `json:"rules" protobuf:"bytes,2,rep,name=rules"` Rules []PolicyRule `json:"rules" protobuf:"bytes,2,rep,name=rules"`
} }
// +genclient=true,nonNamespaced=true
// ClusterRoleBinding references a ClusterRole, but not contain it. It can reference a ClusterRole in the global namespace, // ClusterRoleBinding references a ClusterRole, but not contain it. It can reference a ClusterRole in the global namespace,
// and adds who information via Subject. // and adds who information via Subject.
type ClusterRoleBinding struct { type ClusterRoleBinding struct {

View File

@@ -126,7 +126,7 @@ func (RoleList) SwaggerDoc() map[string]string {
var map_Subject = map[string]string{ var map_Subject = map[string]string{
"": "Subject contains a reference to the object or user identities a role binding applies to. This can either hold a direct API object reference, or a value for non-objects such as user and group names.", "": "Subject contains a reference to the object or user identities a role binding applies to. This can either hold a direct API object reference, or a value for non-objects such as user and group names.",
"kind": "Kind of object being referenced. Values defined by this API group are \"User\", \"Group\", and \"ServiceAccount\". If the Authorizer does not recognized the kind value, the Authorizer should report an error.", "kind": "Kind of object being referenced. Values defined by this API group are \"User\", \"Group\", and \"ServiceAccount\". If the Authorizer does not recognized the kind value, the Authorizer should report an error.",
"apiVersion": "APIVersion holds the API group and version of the referenced object. For non-object references such as \"Group\" and \"User\" this is expected to be API version of this API group. For example \"rbac.authorization.k8s.io/v1alpha1\".", "apiVersion": "APIVersion holds the API group and version of the referenced object. For non-object references such as \"Group\" and \"User\" this is expected to be API version of this API group. For example \"rbac/v1alpha1\".",
"name": "Name of the object being referenced.", "name": "Name of the object being referenced.",
"namespace": "Namespace of the referenced object. If the object kind is non-namespace, such as \"User\" or \"Group\", and this value is not empty the Authorizer should report an error.", "namespace": "Namespace of the referenced object. If the object kind is non-namespace, such as \"User\" or \"Group\", and this value is not empty the Authorizer should report an error.",
} }

View File

@@ -0,0 +1,120 @@
/*
Copyright 2016 The Kubernetes Authors 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 validation
import "k8s.io/kubernetes/pkg/apis/rbac"
// Covers determines whether or not the ownerRules cover the servantRules in terms of allowed actions.
// It returns whether or not the ownerRules cover and a list of the rules that the ownerRules do not cover.
func Covers(ownerRules, servantRules []rbac.PolicyRule) (bool, []rbac.PolicyRule) {
// 1. Break every servantRule into individual rule tuples: group, verb, resource, resourceName
// 2. Compare the mini-rules against each owner rule. Because the breakdown is down to the most atomic level, we're guaranteed that each mini-servant rule will be either fully covered or not covered by a single owner rule
// 3. Any left over mini-rules means that we are not covered and we have a nice list of them.
// TODO: it might be nice to collapse the list down into something more human readable
subrules := []rbac.PolicyRule{}
for _, servantRule := range servantRules {
subrules = append(subrules, breakdownRule(servantRule)...)
}
uncoveredRules := []rbac.PolicyRule{}
for _, subrule := range subrules {
covered := false
for _, ownerRule := range ownerRules {
if ruleCovers(ownerRule, subrule) {
covered = true
break
}
}
if !covered {
uncoveredRules = append(uncoveredRules, subrule)
}
}
return (len(uncoveredRules) == 0), uncoveredRules
}
// breadownRule takes a rule and builds an equivalent list of rules that each have at most one verb, one
// resource, and one resource name
func breakdownRule(rule rbac.PolicyRule) []rbac.PolicyRule {
subrules := []rbac.PolicyRule{}
for _, group := range rule.APIGroups {
for _, resource := range rule.Resources {
for _, verb := range rule.Verbs {
if len(rule.ResourceNames) > 0 {
for _, resourceName := range rule.ResourceNames {
subrules = append(subrules, rbac.PolicyRule{APIGroups: []string{group}, Resources: []string{resource}, Verbs: []string{verb}, ResourceNames: []string{resourceName}})
}
} else {
subrules = append(subrules, rbac.PolicyRule{APIGroups: []string{group}, Resources: []string{resource}, Verbs: []string{verb}})
}
}
}
}
// Non-resource URLs are unique because they don't combine with other policy rule fields.
for _, nonResourceURL := range rule.NonResourceURLs {
subrules = append(subrules, rbac.PolicyRule{NonResourceURLs: []string{nonResourceURL}})
}
return subrules
}
func has(set []string, ele string) bool {
for _, s := range set {
if s == ele {
return true
}
}
return false
}
func hasAll(set, contains []string) bool {
owning := make(map[string]struct{}, len(set))
for _, ele := range set {
owning[ele] = struct{}{}
}
for _, ele := range contains {
if _, ok := owning[ele]; !ok {
return false
}
}
return true
}
// ruleCovers determines whether the ownerRule (which may have multiple verbs, resources, and resourceNames) covers
// the subrule (which may only contain at most one verb, resource, and resourceName)
func ruleCovers(ownerRule, subRule rbac.PolicyRule) bool {
verbMatches := has(ownerRule.Verbs, rbac.VerbAll) || hasAll(ownerRule.Verbs, subRule.Verbs)
groupMatches := has(ownerRule.APIGroups, rbac.APIGroupAll) || hasAll(ownerRule.APIGroups, subRule.APIGroups)
resourceMatches := has(ownerRule.Resources, rbac.ResourceAll) || hasAll(ownerRule.Resources, subRule.Resources)
nonResourceURLMatches := has(ownerRule.NonResourceURLs, rbac.NonResourceAll) || hasAll(ownerRule.NonResourceURLs, subRule.NonResourceURLs)
resourceNameMatches := false
if len(subRule.ResourceNames) == 0 {
resourceNameMatches = (len(ownerRule.ResourceNames) == 0)
} else {
resourceNameMatches = (len(ownerRule.ResourceNames) == 0) || hasAll(ownerRule.ResourceNames, subRule.ResourceNames)
}
return verbMatches && groupMatches && resourceMatches && resourceNameMatches && nonResourceURLMatches
}

View File

@@ -0,0 +1,371 @@
/*
Copyright 2016 The Kubernetes Authors 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 validation
import (
"reflect"
"testing"
"k8s.io/kubernetes/pkg/apis/rbac"
)
type escalationTest struct {
ownerRules []rbac.PolicyRule
servantRules []rbac.PolicyRule
expectedCovered bool
expectedUncoveredRules []rbac.PolicyRule
}
func TestCoversExactMatch(t *testing.T) {
escalationTest{
ownerRules: []rbac.PolicyRule{
{APIGroups: []string{"v1"}, Verbs: []string{"get"}, Resources: []string{"builds"}},
},
servantRules: []rbac.PolicyRule{
{APIGroups: []string{"v1"}, Verbs: []string{"get"}, Resources: []string{"builds"}},
},
expectedCovered: true,
expectedUncoveredRules: []rbac.PolicyRule{},
}.test(t)
}
func TestCoversMultipleRulesCoveringSingleRule(t *testing.T) {
escalationTest{
ownerRules: []rbac.PolicyRule{
{APIGroups: []string{"v1"}, Verbs: []string{"delete"}, Resources: []string{"deployments"}},
{APIGroups: []string{"v1"}, Verbs: []string{"delete"}, Resources: []string{"builds"}},
{APIGroups: []string{"v1"}, Verbs: []string{"update"}, Resources: []string{"builds", "deployments"}},
},
servantRules: []rbac.PolicyRule{
{APIGroups: []string{"v1"}, Verbs: []string{"delete", "update"}, Resources: []string{"builds", "deployments"}},
},
expectedCovered: true,
expectedUncoveredRules: []rbac.PolicyRule{},
}.test(t)
}
func TestCoversMultipleAPIGroupsCoveringSingleRule(t *testing.T) {
escalationTest{
ownerRules: []rbac.PolicyRule{
{APIGroups: []string{"group1"}, Verbs: []string{"delete"}, Resources: []string{"deployments"}},
{APIGroups: []string{"group1"}, Verbs: []string{"delete"}, Resources: []string{"builds"}},
{APIGroups: []string{"group1"}, Verbs: []string{"update"}, Resources: []string{"builds", "deployments"}},
{APIGroups: []string{"group2"}, Verbs: []string{"delete"}, Resources: []string{"deployments"}},
{APIGroups: []string{"group2"}, Verbs: []string{"delete"}, Resources: []string{"builds"}},
{APIGroups: []string{"group2"}, Verbs: []string{"update"}, Resources: []string{"builds", "deployments"}},
},
servantRules: []rbac.PolicyRule{
{APIGroups: []string{"group1", "group2"}, Verbs: []string{"delete", "update"}, Resources: []string{"builds", "deployments"}},
},
expectedCovered: true,
expectedUncoveredRules: []rbac.PolicyRule{},
}.test(t)
}
func TestCoversSingleAPIGroupsCoveringMultiple(t *testing.T) {
escalationTest{
ownerRules: []rbac.PolicyRule{
{APIGroups: []string{"group1", "group2"}, Verbs: []string{"delete", "update"}, Resources: []string{"builds", "deployments"}},
},
servantRules: []rbac.PolicyRule{
{APIGroups: []string{"group1"}, Verbs: []string{"delete"}, Resources: []string{"deployments"}},
{APIGroups: []string{"group1"}, Verbs: []string{"delete"}, Resources: []string{"builds"}},
{APIGroups: []string{"group1"}, Verbs: []string{"update"}, Resources: []string{"builds", "deployments"}},
{APIGroups: []string{"group2"}, Verbs: []string{"delete"}, Resources: []string{"deployments"}},
{APIGroups: []string{"group2"}, Verbs: []string{"delete"}, Resources: []string{"builds"}},
{APIGroups: []string{"group2"}, Verbs: []string{"update"}, Resources: []string{"builds", "deployments"}},
},
expectedCovered: true,
expectedUncoveredRules: []rbac.PolicyRule{},
}.test(t)
}
func TestCoversMultipleRulesMissingSingleVerbResourceCombination(t *testing.T) {
escalationTest{
ownerRules: []rbac.PolicyRule{
{APIGroups: []string{"v1"}, Verbs: []string{"delete", "update"}, Resources: []string{"builds", "deployments"}},
{APIGroups: []string{"v1"}, Verbs: []string{"delete"}, Resources: []string{"pods"}},
},
servantRules: []rbac.PolicyRule{
{APIGroups: []string{"v1"}, Verbs: []string{"delete", "update"}, Resources: []string{"builds", "deployments", "pods"}},
},
expectedCovered: false,
expectedUncoveredRules: []rbac.PolicyRule{
{APIGroups: []string{"v1"}, Verbs: []string{"update"}, Resources: []string{"pods"}},
},
}.test(t)
}
func TestCoversAPIGroupStarCoveringMultiple(t *testing.T) {
escalationTest{
ownerRules: []rbac.PolicyRule{
{APIGroups: []string{"*"}, Verbs: []string{"get"}, Resources: []string{"roles"}},
},
servantRules: []rbac.PolicyRule{
{APIGroups: []string{"group1", "group2"}, Verbs: []string{"get"}, Resources: []string{"roles"}},
},
expectedCovered: true,
expectedUncoveredRules: []rbac.PolicyRule{},
}.test(t)
}
func TestCoversEnumerationNotCoveringAPIGroupStar(t *testing.T) {
escalationTest{
ownerRules: []rbac.PolicyRule{
{APIGroups: []string{"dummy-group"}, Verbs: []string{"get"}, Resources: []string{"roles"}},
},
servantRules: []rbac.PolicyRule{
{APIGroups: []string{"*"}, Verbs: []string{"get"}, Resources: []string{"roles"}},
},
expectedCovered: false,
expectedUncoveredRules: []rbac.PolicyRule{
{APIGroups: []string{"*"}, Verbs: []string{"get"}, Resources: []string{"roles"}},
},
}.test(t)
}
func TestCoversAPIGroupStarCoveringStar(t *testing.T) {
escalationTest{
ownerRules: []rbac.PolicyRule{
{APIGroups: []string{"*"}, Verbs: []string{"get"}, Resources: []string{"roles"}},
},
servantRules: []rbac.PolicyRule{
{APIGroups: []string{"*"}, Verbs: []string{"get"}, Resources: []string{"roles"}},
},
expectedCovered: true,
expectedUncoveredRules: []rbac.PolicyRule{},
}.test(t)
}
func TestCoversVerbStarCoveringMultiple(t *testing.T) {
escalationTest{
ownerRules: []rbac.PolicyRule{
{APIGroups: []string{"v1"}, Verbs: []string{"*"}, Resources: []string{"roles"}},
},
servantRules: []rbac.PolicyRule{
{APIGroups: []string{"v1"}, Verbs: []string{"watch", "list"}, Resources: []string{"roles"}},
},
expectedCovered: true,
expectedUncoveredRules: []rbac.PolicyRule{},
}.test(t)
}
func TestCoversEnumerationNotCoveringVerbStar(t *testing.T) {
escalationTest{
ownerRules: []rbac.PolicyRule{
{APIGroups: []string{"v1"}, Verbs: []string{"get", "list", "watch", "create", "update", "delete", "exec"}, Resources: []string{"roles"}},
},
servantRules: []rbac.PolicyRule{
{APIGroups: []string{"v1"}, Verbs: []string{"*"}, Resources: []string{"roles"}},
},
expectedCovered: false,
expectedUncoveredRules: []rbac.PolicyRule{
{APIGroups: []string{"v1"}, Verbs: []string{"*"}, Resources: []string{"roles"}},
},
}.test(t)
}
func TestCoversVerbStarCoveringStar(t *testing.T) {
escalationTest{
ownerRules: []rbac.PolicyRule{
{APIGroups: []string{"v1"}, Verbs: []string{"*"}, Resources: []string{"roles"}},
},
servantRules: []rbac.PolicyRule{
{APIGroups: []string{"v1"}, Verbs: []string{"*"}, Resources: []string{"roles"}},
},
expectedCovered: true,
expectedUncoveredRules: []rbac.PolicyRule{},
}.test(t)
}
func TestCoversResourceStarCoveringMultiple(t *testing.T) {
escalationTest{
ownerRules: []rbac.PolicyRule{
{APIGroups: []string{"v1"}, Verbs: []string{"get"}, Resources: []string{"*"}},
},
servantRules: []rbac.PolicyRule{
{APIGroups: []string{"v1"}, Verbs: []string{"get"}, Resources: []string{"resourcegroup:deployments"}},
},
expectedCovered: true,
expectedUncoveredRules: []rbac.PolicyRule{},
}.test(t)
}
func TestCoversEnumerationNotCoveringResourceStar(t *testing.T) {
escalationTest{
ownerRules: []rbac.PolicyRule{
{APIGroups: []string{"v1"}, Verbs: []string{"get"}, Resources: []string{"roles", "resourcegroup:deployments"}},
},
servantRules: []rbac.PolicyRule{
{APIGroups: []string{"v1"}, Verbs: []string{"get"}, Resources: []string{"*"}},
},
expectedCovered: false,
expectedUncoveredRules: []rbac.PolicyRule{
{APIGroups: []string{"v1"}, Verbs: []string{"get"}, Resources: []string{"*"}},
},
}.test(t)
}
func TestCoversResourceStarCoveringStar(t *testing.T) {
escalationTest{
ownerRules: []rbac.PolicyRule{
{APIGroups: []string{"v1"}, Verbs: []string{"get"}, Resources: []string{"*"}},
},
servantRules: []rbac.PolicyRule{
{APIGroups: []string{"v1"}, Verbs: []string{"get"}, Resources: []string{"*"}},
},
expectedCovered: true,
expectedUncoveredRules: []rbac.PolicyRule{},
}.test(t)
}
func TestCoversResourceNameEmptyCoveringMultiple(t *testing.T) {
escalationTest{
ownerRules: []rbac.PolicyRule{
{APIGroups: []string{"v1"}, Verbs: []string{"get"}, Resources: []string{"pods"}, ResourceNames: []string{}},
},
servantRules: []rbac.PolicyRule{
{APIGroups: []string{"v1"}, Verbs: []string{"get"}, Resources: []string{"pods"}, ResourceNames: []string{"foo", "bar"}},
},
expectedCovered: true,
expectedUncoveredRules: []rbac.PolicyRule{},
}.test(t)
}
func TestCoversEnumerationNotCoveringResourceNameEmpty(t *testing.T) {
escalationTest{
ownerRules: []rbac.PolicyRule{
{APIGroups: []string{"v1"}, Verbs: []string{"get"}, Resources: []string{"pods"}, ResourceNames: []string{"foo", "bar"}},
},
servantRules: []rbac.PolicyRule{
{APIGroups: []string{"v1"}, Verbs: []string{"get"}, Resources: []string{"pods"}, ResourceNames: []string{}},
},
expectedCovered: false,
expectedUncoveredRules: []rbac.PolicyRule{
{APIGroups: []string{"v1"}, Verbs: []string{"get"}, Resources: []string{"pods"}},
},
}.test(t)
}
func TestCoversNonResourceURLs(t *testing.T) {
escalationTest{
ownerRules: []rbac.PolicyRule{
{NonResourceURLs: []string{"/apis"}},
},
servantRules: []rbac.PolicyRule{
{NonResourceURLs: []string{"/apis"}},
},
expectedCovered: true,
expectedUncoveredRules: []rbac.PolicyRule{},
}.test(t)
}
func TestCoversNonResourceURLsStar(t *testing.T) {
escalationTest{
ownerRules: []rbac.PolicyRule{
{NonResourceURLs: []string{"*"}},
},
servantRules: []rbac.PolicyRule{
{NonResourceURLs: []string{"/apis", "/apis/v1", "/"}},
},
expectedCovered: true,
expectedUncoveredRules: []rbac.PolicyRule{},
}.test(t)
}
func TestCoversNonResourceURLsWithOtherFields(t *testing.T) {
escalationTest{
ownerRules: []rbac.PolicyRule{
{APIGroups: []string{"v1"}, Verbs: []string{"get"}, Resources: []string{"builds"}, NonResourceURLs: []string{"/apis"}},
},
servantRules: []rbac.PolicyRule{
{APIGroups: []string{"v1"}, Verbs: []string{"get"}, Resources: []string{"builds"}, NonResourceURLs: []string{"/apis"}},
},
expectedCovered: true,
expectedUncoveredRules: []rbac.PolicyRule{},
}.test(t)
}
func TestCoversNonResourceURLsWithOtherFieldsFailure(t *testing.T) {
escalationTest{
ownerRules: []rbac.PolicyRule{
{APIGroups: []string{"v1"}, Verbs: []string{"get"}, Resources: []string{"builds"}},
},
servantRules: []rbac.PolicyRule{
{APIGroups: []string{"v1"}, Verbs: []string{"get"}, Resources: []string{"builds"}, NonResourceURLs: []string{"/apis"}},
},
expectedCovered: false,
expectedUncoveredRules: []rbac.PolicyRule{{NonResourceURLs: []string{"/apis"}}},
}.test(t)
}
func (test escalationTest) test(t *testing.T) {
actualCovered, actualUncoveredRules := Covers(test.ownerRules, test.servantRules)
if actualCovered != test.expectedCovered {
t.Errorf("expected %v, but got %v", test.expectedCovered, actualCovered)
}
if !rulesMatch(test.expectedUncoveredRules, actualUncoveredRules) {
t.Errorf("expected %v, but got %v", test.expectedUncoveredRules, actualUncoveredRules)
}
}
func rulesMatch(expectedRules, actualRules []rbac.PolicyRule) bool {
if len(expectedRules) != len(actualRules) {
return false
}
for _, expectedRule := range expectedRules {
found := false
for _, actualRule := range actualRules {
if reflect.DeepEqual(expectedRule, actualRule) {
found = true
}
}
if !found {
return false
}
}
return true
}

View File

@@ -0,0 +1,208 @@
/*
Copyright 2016 The Kubernetes Authors 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 validation
import (
"fmt"
"github.com/golang/glog"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/errors"
"k8s.io/kubernetes/pkg/apis/rbac"
"k8s.io/kubernetes/pkg/auth/user"
utilerrors "k8s.io/kubernetes/pkg/util/errors"
)
type AuthorizationRuleResolver interface {
// GetRoleReferenceRules attempts to resolve the role reference of a RoleBinding or ClusterRoleBinding. The passed namespace should be the namepsace
// of the role binding, the empty string if a cluster role binding.
GetRoleReferenceRules(ctx api.Context, roleRef api.ObjectReference, namespace string) ([]rbac.PolicyRule, error)
// GetEffectivePolicyRules returns the list of rules that apply to a given user in a given namespace and error. If an error is returned, the slice of
// PolicyRules may not be complete, but it contains all retrievable rules. This is done because policy rules are purely additive and policy determinations
// can be made on the basis of those rules that are found.
GetEffectivePolicyRules(ctx api.Context) ([]rbac.PolicyRule, error)
}
// ConfirmNoEscalation determines if the roles for a given user in a given namespace encompass the provided role.
func ConfirmNoEscalation(ctx api.Context, ruleResolver AuthorizationRuleResolver, rules []rbac.PolicyRule) error {
ruleResolutionErrors := []error{}
ownerLocalRules, err := ruleResolver.GetEffectivePolicyRules(ctx)
if err != nil {
// As per AuthorizationRuleResolver contract, this may return a non fatal error with an incomplete list of policies. Log the error and continue.
user, _ := api.UserFrom(ctx)
glog.V(1).Infof("non-fatal error getting local rules for %v: %v", user, err)
ruleResolutionErrors = append(ruleResolutionErrors, err)
}
masterContext := api.WithNamespace(ctx, "")
ownerGlobalRules, err := ruleResolver.GetEffectivePolicyRules(masterContext)
if err != nil {
// Same case as above. Log error, don't fail.
user, _ := api.UserFrom(ctx)
glog.V(1).Infof("non-fatal error getting global rules for %v: %v", user, err)
ruleResolutionErrors = append(ruleResolutionErrors, err)
}
ownerRules := make([]rbac.PolicyRule, 0, len(ownerGlobalRules)+len(ownerLocalRules))
ownerRules = append(ownerRules, ownerLocalRules...)
ownerRules = append(ownerRules, ownerGlobalRules...)
ownerRightsCover, missingRights := Covers(ownerRules, rules)
if !ownerRightsCover {
user, _ := api.UserFrom(ctx)
return errors.NewUnauthorized(fmt.Sprintf("attempt to grant extra privileges: %v user=%v ownerrules=%v ruleResolutionErrors=%v", missingRights, user, ownerRules, ruleResolutionErrors))
}
return nil
}
type DefaultRuleResolver struct {
roleGetter RoleGetter
roleBindingLister RoleBindingLister
clusterRoleGetter ClusterRoleGetter
clusterRoleBindingLister ClusterRoleBindingLister
}
func NewDefaultRuleResolver(roleGetter RoleGetter, roleBindingLister RoleBindingLister, clusterRoleGetter ClusterRoleGetter, clusterRoleBindingLister ClusterRoleBindingLister) *DefaultRuleResolver {
return &DefaultRuleResolver{roleGetter, roleBindingLister, clusterRoleGetter, clusterRoleBindingLister}
}
type RoleGetter interface {
GetRole(ctx api.Context, id string) (*rbac.Role, error)
}
type RoleBindingLister interface {
ListRoleBindings(ctx api.Context, options *api.ListOptions) (*rbac.RoleBindingList, error)
}
type ClusterRoleGetter interface {
GetClusterRole(ctx api.Context, id string) (*rbac.ClusterRole, error)
}
type ClusterRoleBindingLister interface {
ListClusterRoleBindings(ctx api.Context, options *api.ListOptions) (*rbac.ClusterRoleBindingList, error)
}
// GetRoleReferenceRules attempts resolve the RoleBinding or ClusterRoleBinding.
func (r *DefaultRuleResolver) GetRoleReferenceRules(ctx api.Context, roleRef api.ObjectReference, bindingNamespace string) ([]rbac.PolicyRule, error) {
switch roleRef.Kind {
case "Role":
// Roles can only be referenced by RoleBindings within the same namespace.
if len(bindingNamespace) == 0 {
return nil, fmt.Errorf("cluster role binding references role %q in namespace %q", roleRef.Name, roleRef.Namespace)
} else {
if bindingNamespace != roleRef.Namespace {
return nil, fmt.Errorf("role binding in namespace %q references role %q in namespace %q", bindingNamespace, roleRef.Name, roleRef.Namespace)
}
}
role, err := r.roleGetter.GetRole(api.WithNamespace(ctx, roleRef.Namespace), roleRef.Name)
if err != nil {
return nil, err
}
return role.Rules, nil
case "ClusterRole":
clusterRole, err := r.clusterRoleGetter.GetClusterRole(api.WithNamespace(ctx, ""), roleRef.Name)
if err != nil {
return nil, err
}
return clusterRole.Rules, nil
default:
return nil, fmt.Errorf("unsupported role reference kind: %q", roleRef.Kind)
}
}
func (r *DefaultRuleResolver) GetEffectivePolicyRules(ctx api.Context) ([]rbac.PolicyRule, error) {
policyRules := []rbac.PolicyRule{}
errorlist := []error{}
if namespace := api.NamespaceValue(ctx); len(namespace) == 0 {
clusterRoleBindings, err := r.clusterRoleBindingLister.ListClusterRoleBindings(ctx, &api.ListOptions{})
if err != nil {
return nil, err
}
for _, clusterRoleBinding := range clusterRoleBindings.Items {
if ok, err := appliesTo(ctx, clusterRoleBinding.Subjects); err != nil {
errorlist = append(errorlist, err)
} else if !ok {
continue
}
rules, err := r.GetRoleReferenceRules(ctx, clusterRoleBinding.RoleRef, namespace)
if err != nil {
errorlist = append(errorlist, err)
continue
}
policyRules = append(policyRules, rules...)
}
} else {
roleBindings, err := r.roleBindingLister.ListRoleBindings(ctx, &api.ListOptions{})
if err != nil {
return nil, err
}
for _, roleBinding := range roleBindings.Items {
if ok, err := appliesTo(ctx, roleBinding.Subjects); err != nil {
errorlist = append(errorlist, err)
} else if !ok {
continue
}
rules, err := r.GetRoleReferenceRules(ctx, roleBinding.RoleRef, namespace)
if err != nil {
errorlist = append(errorlist, err)
continue
}
policyRules = append(policyRules, rules...)
}
}
if len(errorlist) != 0 {
return policyRules, utilerrors.NewAggregate(errorlist)
}
return policyRules, nil
}
func appliesTo(ctx api.Context, subjects []rbac.Subject) (bool, error) {
user, ok := api.UserFrom(ctx)
if !ok {
return false, fmt.Errorf("no user data associated with context")
}
for _, subject := range subjects {
if ok, err := appliesToUser(user, subject); err != nil || ok {
return ok, err
}
}
return false, nil
}
func appliesToUser(user user.Info, subject rbac.Subject) (bool, error) {
switch subject.Kind {
case rbac.UserKind:
return subject.Name == rbac.UserAll || user.GetName() == subject.Name, nil
case rbac.GroupKind:
return has(user.GetGroups(), subject.Name), nil
case rbac.ServiceAccountKind:
if subject.Namespace == "" {
return false, fmt.Errorf("subject of kind service account without specified namespace")
}
// TODO(ericchiang): Is there a better way of matching a service account name?
return "system:serviceaccount:"+subject.Name+":"+subject.Namespace == user.GetName(), nil
default:
return false, fmt.Errorf("unknown subject kind: %s", subject.Kind)
}
}

View File

@@ -0,0 +1,335 @@
/*
Copyright 2016 The Kubernetes Authors 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 validation
import (
"errors"
"hash/fnv"
"io"
"reflect"
"sort"
"testing"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/apis/rbac"
"k8s.io/kubernetes/pkg/auth/user"
"k8s.io/kubernetes/pkg/util/diff"
)
func newMockRuleResolver(r *staticRoles) AuthorizationRuleResolver {
return NewDefaultRuleResolver(r, r, r, r)
}
type staticRoles struct {
roles []rbac.Role
roleBindings []rbac.RoleBinding
clusterRoles []rbac.ClusterRole
clusterRoleBindings []rbac.ClusterRoleBinding
}
func (r *staticRoles) GetRole(ctx api.Context, id string) (*rbac.Role, error) {
namespace, ok := api.NamespaceFrom(ctx)
if !ok || namespace == "" {
return nil, errors.New("must provide namespace when getting role")
}
for _, role := range r.roles {
if role.Namespace == namespace && role.Name == id {
return &role, nil
}
}
return nil, errors.New("role not found")
}
func (r *staticRoles) GetClusterRole(ctx api.Context, id string) (*rbac.ClusterRole, error) {
namespace, ok := api.NamespaceFrom(ctx)
if ok && namespace != "" {
return nil, errors.New("cannot provide namespace when getting cluster role")
}
for _, clusterRole := range r.clusterRoles {
if clusterRole.Namespace == namespace && clusterRole.Name == id {
return &clusterRole, nil
}
}
return nil, errors.New("role not found")
}
func (r *staticRoles) ListRoleBindings(ctx api.Context, options *api.ListOptions) (*rbac.RoleBindingList, error) {
namespace, ok := api.NamespaceFrom(ctx)
if !ok || namespace == "" {
return nil, errors.New("must provide namespace when listing role bindings")
}
roleBindingList := new(rbac.RoleBindingList)
for _, roleBinding := range r.roleBindings {
if roleBinding.Namespace != namespace {
continue
}
// TODO(ericchiang): need to implement label selectors?
roleBindingList.Items = append(roleBindingList.Items, roleBinding)
}
return roleBindingList, nil
}
func (r *staticRoles) ListClusterRoleBindings(ctx api.Context, options *api.ListOptions) (*rbac.ClusterRoleBindingList, error) {
namespace, ok := api.NamespaceFrom(ctx)
if ok && namespace != "" {
return nil, errors.New("cannot list cluster role bindings from within a namespace")
}
clusterRoleBindings := new(rbac.ClusterRoleBindingList)
clusterRoleBindings.Items = make([]rbac.ClusterRoleBinding, len(r.clusterRoleBindings))
copy(clusterRoleBindings.Items, r.clusterRoleBindings)
return clusterRoleBindings, nil
}
// compute a hash of a policy rule so we can sort in a deterministic order
func hashOf(p rbac.PolicyRule) string {
hash := fnv.New32()
writeStrings := func(slis ...[]string) {
for _, sli := range slis {
for _, s := range sli {
io.WriteString(hash, s)
}
}
}
writeStrings(p.Verbs, p.APIGroups, p.Resources, p.ResourceNames, p.NonResourceURLs)
return string(hash.Sum(nil))
}
// byHash sorts a set of policy rules by a hash of its fields
type byHash []rbac.PolicyRule
func (b byHash) Len() int { return len(b) }
func (b byHash) Less(i, j int) bool { return hashOf(b[i]) < hashOf(b[j]) }
func (b byHash) Swap(i, j int) { b[i], b[j] = b[j], b[i] }
func TestDefaultRuleResolver(t *testing.T) {
ruleReadPods := rbac.PolicyRule{
Verbs: []string{"GET", "WATCH"},
APIGroups: []string{"v1"},
Resources: []string{"pods"},
}
ruleReadServices := rbac.PolicyRule{
Verbs: []string{"GET", "WATCH"},
APIGroups: []string{"v1"},
Resources: []string{"services"},
}
ruleWriteNodes := rbac.PolicyRule{
Verbs: []string{"PUT", "CREATE", "UPDATE"},
APIGroups: []string{"v1"},
Resources: []string{"nodes"},
}
ruleAdmin := rbac.PolicyRule{
Verbs: []string{"*"},
APIGroups: []string{"*"},
Resources: []string{"*"},
}
staticRoles1 := staticRoles{
roles: []rbac.Role{
{
ObjectMeta: api.ObjectMeta{Namespace: "namespace1", Name: "readthings"},
Rules: []rbac.PolicyRule{ruleReadPods, ruleReadServices},
},
},
clusterRoles: []rbac.ClusterRole{
{
ObjectMeta: api.ObjectMeta{Name: "cluster-admin"},
Rules: []rbac.PolicyRule{ruleAdmin},
},
{
ObjectMeta: api.ObjectMeta{Name: "write-nodes"},
Rules: []rbac.PolicyRule{ruleWriteNodes},
},
},
roleBindings: []rbac.RoleBinding{
{
ObjectMeta: api.ObjectMeta{Namespace: "namespace1"},
Subjects: []rbac.Subject{
{Kind: rbac.UserKind, Name: "foobar"},
{Kind: rbac.GroupKind, Name: "group1"},
},
RoleRef: api.ObjectReference{Kind: "Role", Namespace: "namespace1", Name: "readthings"},
},
},
clusterRoleBindings: []rbac.ClusterRoleBinding{
{
Subjects: []rbac.Subject{
{Kind: rbac.UserKind, Name: "admin"},
{Kind: rbac.GroupKind, Name: "admin"},
},
RoleRef: api.ObjectReference{Kind: "ClusterRole", Name: "cluster-admin"},
},
},
}
tests := []struct {
staticRoles
// For a given context, what are the rules that apply?
ctx api.Context
effectiveRules []rbac.PolicyRule
}{
{
staticRoles: staticRoles1,
ctx: api.WithNamespace(
api.WithUser(api.NewContext(), &user.DefaultInfo{Name: "foobar"}), "namespace1",
),
effectiveRules: []rbac.PolicyRule{ruleReadPods, ruleReadServices},
},
{
staticRoles: staticRoles1,
ctx: api.WithNamespace(
// Same as above but diffrerent namespace. Should return no rules.
api.WithUser(api.NewContext(), &user.DefaultInfo{Name: "foobar"}), "namespace2",
),
effectiveRules: []rbac.PolicyRule{},
},
{
staticRoles: staticRoles1,
// GetEffectivePolicyRules only returns the policies for the namespace, not the master namespace.
ctx: api.WithNamespace(
api.WithUser(api.NewContext(), &user.DefaultInfo{
Name: "foobar", Groups: []string{"admin"},
}), "namespace1",
),
effectiveRules: []rbac.PolicyRule{ruleReadPods, ruleReadServices},
},
{
staticRoles: staticRoles1,
// Same as above but without a namespace. Only cluster rules should apply.
ctx: api.WithUser(api.NewContext(), &user.DefaultInfo{
Name: "foobar", Groups: []string{"admin"},
}),
effectiveRules: []rbac.PolicyRule{ruleAdmin},
},
{
staticRoles: staticRoles1,
ctx: api.WithUser(api.NewContext(), &user.DefaultInfo{}),
effectiveRules: []rbac.PolicyRule{},
},
}
for i, tc := range tests {
ruleResolver := newMockRuleResolver(&tc.staticRoles)
rules, err := ruleResolver.GetEffectivePolicyRules(tc.ctx)
if err != nil {
t.Errorf("case %d: GetEffectivePolicyRules(context)=%v", i, err)
continue
}
// Sort for deep equals
sort.Sort(byHash(rules))
sort.Sort(byHash(tc.effectiveRules))
if !reflect.DeepEqual(rules, tc.effectiveRules) {
ruleDiff := diff.ObjectDiff(rules, tc.effectiveRules)
t.Errorf("case %d: %s", i, ruleDiff)
}
}
}
func TestAppliesTo(t *testing.T) {
tests := []struct {
subjects []rbac.Subject
ctx api.Context
appliesTo bool
testCase string
}{
{
subjects: []rbac.Subject{
{Kind: rbac.UserKind, Name: "foobar"},
},
ctx: api.WithUser(api.NewContext(), &user.DefaultInfo{Name: "foobar"}),
appliesTo: true,
testCase: "single subject that matches username",
},
{
subjects: []rbac.Subject{
{Kind: rbac.UserKind, Name: "barfoo"},
{Kind: rbac.UserKind, Name: "foobar"},
},
ctx: api.WithUser(api.NewContext(), &user.DefaultInfo{Name: "foobar"}),
appliesTo: true,
testCase: "multiple subjects, one that matches username",
},
{
subjects: []rbac.Subject{
{Kind: rbac.UserKind, Name: "barfoo"},
{Kind: rbac.UserKind, Name: "foobar"},
},
ctx: api.WithUser(api.NewContext(), &user.DefaultInfo{Name: "zimzam"}),
appliesTo: false,
testCase: "multiple subjects, none that match username",
},
{
subjects: []rbac.Subject{
{Kind: rbac.UserKind, Name: "barfoo"},
{Kind: rbac.GroupKind, Name: "foobar"},
},
ctx: api.WithUser(api.NewContext(), &user.DefaultInfo{Name: "zimzam", Groups: []string{"foobar"}}),
appliesTo: true,
testCase: "multiple subjects, one that match group",
},
{
subjects: []rbac.Subject{
{Kind: rbac.UserKind, Name: "barfoo"},
{Kind: rbac.GroupKind, Name: "foobar"},
},
ctx: api.WithNamespace(
api.WithUser(api.NewContext(), &user.DefaultInfo{Name: "zimzam", Groups: []string{"foobar"}}),
"namespace1",
),
appliesTo: true,
testCase: "multiple subjects, one that match group, should ignore namespace",
},
{
subjects: []rbac.Subject{
{Kind: rbac.UserKind, Name: "barfoo"},
{Kind: rbac.GroupKind, Name: "foobar"},
{Kind: rbac.ServiceAccountKind, Name: "kube-system", Namespace: "default"},
},
ctx: api.WithNamespace(
api.WithUser(api.NewContext(), &user.DefaultInfo{Name: "system:serviceaccount:kube-system:default"}),
"default",
),
appliesTo: true,
testCase: "multiple subjects with a service account that matches",
},
{
subjects: []rbac.Subject{
{Kind: rbac.UserKind, Name: "*"},
},
ctx: api.WithNamespace(
api.WithUser(api.NewContext(), &user.DefaultInfo{Name: "foobar"}),
"default",
),
appliesTo: true,
testCase: "multiple subjects with a service account that matches",
},
}
for _, tc := range tests {
got, err := appliesTo(tc.ctx, tc.subjects)
if err != nil {
t.Errorf("case %q %v", tc.testCase, err)
continue
}
if got != tc.appliesTo {
t.Errorf("case %q want appliesTo=%t, got appliesTo=%t", tc.testCase, tc.appliesTo, got)
}
}
}

View File

@@ -29,73 +29,65 @@ func minimalNameRequirements(name string, prefix bool) []string {
return validation.IsValidPathSegmentName(name) return validation.IsValidPathSegmentName(name)
} }
func ValidateLocalRole(policy *rbac.Role) field.ErrorList { func ValidateRole(policy *rbac.Role) field.ErrorList {
return ValidateRole(policy, true) return validateRole(policy, true)
} }
func ValidateLocalRoleUpdate(policy *rbac.Role, oldRole *rbac.Role) field.ErrorList { func ValidateRoleUpdate(policy *rbac.Role, oldRole *rbac.Role) field.ErrorList {
return ValidateRoleUpdate(policy, oldRole, true) return validateRoleUpdate(policy, oldRole, true)
} }
func ValidateClusterRole(policy *rbac.ClusterRole) field.ErrorList { func ValidateClusterRole(policy *rbac.ClusterRole) field.ErrorList {
return ValidateRole(toRole(policy), false) return validateRole(toRole(policy), false)
} }
func ValidateClusterRoleUpdate(policy *rbac.ClusterRole, oldRole *rbac.ClusterRole) field.ErrorList { func ValidateClusterRoleUpdate(policy *rbac.ClusterRole, oldRole *rbac.ClusterRole) field.ErrorList {
return ValidateRoleUpdate(toRole(policy), toRole(oldRole), false) return validateRoleUpdate(toRole(policy), toRole(oldRole), false)
} }
func ValidateRole(role *rbac.Role, isNamespaced bool) field.ErrorList { func validateRole(role *rbac.Role, isNamespaced bool) field.ErrorList {
return validateRole(role, isNamespaced, nil) return validation.ValidateObjectMeta(&role.ObjectMeta, isNamespaced, minimalNameRequirements, field.NewPath("metadata"))
} }
func validateRole(role *rbac.Role, isNamespaced bool, fldPath *field.Path) field.ErrorList { func validateRoleUpdate(role *rbac.Role, oldRole *rbac.Role, isNamespaced bool) field.ErrorList {
return validation.ValidateObjectMeta(&role.ObjectMeta, isNamespaced, minimalNameRequirements, fldPath.Child("metadata")) allErrs := validateRole(role, isNamespaced)
}
func ValidateRoleUpdate(role *rbac.Role, oldRole *rbac.Role, isNamespaced bool) field.ErrorList {
allErrs := ValidateRole(role, isNamespaced)
allErrs = append(allErrs, validation.ValidateObjectMetaUpdate(&role.ObjectMeta, &oldRole.ObjectMeta, field.NewPath("metadata"))...) allErrs = append(allErrs, validation.ValidateObjectMetaUpdate(&role.ObjectMeta, &oldRole.ObjectMeta, field.NewPath("metadata"))...)
return allErrs return allErrs
} }
func ValidateLocalRoleBinding(policy *rbac.RoleBinding) field.ErrorList { func ValidateRoleBinding(policy *rbac.RoleBinding) field.ErrorList {
return ValidateRoleBinding(policy, true) return validateRoleBinding(policy, true)
} }
func ValidateLocalRoleBindingUpdate(policy *rbac.RoleBinding, oldRoleBinding *rbac.RoleBinding) field.ErrorList { func ValidateRoleBindingUpdate(policy *rbac.RoleBinding, oldRoleBinding *rbac.RoleBinding) field.ErrorList {
return ValidateRoleBindingUpdate(policy, oldRoleBinding, true) return validateRoleBindingUpdate(policy, oldRoleBinding, true)
} }
func ValidateClusterRoleBinding(policy *rbac.ClusterRoleBinding) field.ErrorList { func ValidateClusterRoleBinding(policy *rbac.ClusterRoleBinding) field.ErrorList {
return ValidateRoleBinding(toRoleBinding(policy), false) return validateRoleBinding(toRoleBinding(policy), false)
} }
func ValidateClusterRoleBindingUpdate(policy *rbac.ClusterRoleBinding, oldRoleBinding *rbac.ClusterRoleBinding) field.ErrorList { func ValidateClusterRoleBindingUpdate(policy *rbac.ClusterRoleBinding, oldRoleBinding *rbac.ClusterRoleBinding) field.ErrorList {
return ValidateRoleBindingUpdate(toRoleBinding(policy), toRoleBinding(oldRoleBinding), false) return validateRoleBindingUpdate(toRoleBinding(policy), toRoleBinding(oldRoleBinding), false)
} }
func ValidateRoleBinding(roleBinding *rbac.RoleBinding, isNamespaced bool) field.ErrorList { func validateRoleBinding(roleBinding *rbac.RoleBinding, isNamespaced bool) field.ErrorList {
return validateRoleBinding(roleBinding, isNamespaced, nil)
}
func validateRoleBinding(roleBinding *rbac.RoleBinding, isNamespaced bool, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{} allErrs := field.ErrorList{}
allErrs = append(allErrs, validation.ValidateObjectMeta(&roleBinding.ObjectMeta, isNamespaced, minimalNameRequirements, fldPath.Child("metadata"))...) allErrs = append(allErrs, validation.ValidateObjectMeta(&roleBinding.ObjectMeta, isNamespaced, minimalNameRequirements, field.NewPath("metadata"))...)
// roleRef namespace is empty when referring to global policy. // roleRef namespace is empty when referring to global policy.
if len(roleBinding.RoleRef.Namespace) > 0 { if len(roleBinding.RoleRef.Namespace) > 0 {
for _, msg := range validation.ValidateNamespaceName(roleBinding.RoleRef.Namespace, false) { for _, msg := range validation.ValidateNamespaceName(roleBinding.RoleRef.Namespace, false) {
allErrs = append(allErrs, field.Invalid(fldPath.Child("roleRef", "namespace"), roleBinding.RoleRef.Namespace, msg)) allErrs = append(allErrs, field.Invalid(field.NewPath("roleRef", "namespace"), roleBinding.RoleRef.Namespace, msg))
} }
} }
if len(roleBinding.RoleRef.Name) == 0 { if len(roleBinding.RoleRef.Name) == 0 {
allErrs = append(allErrs, field.Required(fldPath.Child("roleRef", "name"), "")) allErrs = append(allErrs, field.Required(field.NewPath("roleRef", "name"), ""))
} else { } else {
for _, msg := range minimalNameRequirements(roleBinding.RoleRef.Name, false) { for _, msg := range minimalNameRequirements(roleBinding.RoleRef.Name, false) {
allErrs = append(allErrs, field.Invalid(fldPath.Child("roleRef", "name"), roleBinding.RoleRef.Name, msg)) allErrs = append(allErrs, field.Invalid(field.NewPath("roleRef", "name"), roleBinding.RoleRef.Name, msg))
} }
} }
@@ -147,8 +139,8 @@ func validateRoleBindingSubject(subject rbac.Subject, isNamespaced bool, fldPath
return allErrs return allErrs
} }
func ValidateRoleBindingUpdate(roleBinding *rbac.RoleBinding, oldRoleBinding *rbac.RoleBinding, isNamespaced bool) field.ErrorList { func validateRoleBindingUpdate(roleBinding *rbac.RoleBinding, oldRoleBinding *rbac.RoleBinding, isNamespaced bool) field.ErrorList {
allErrs := ValidateRoleBinding(roleBinding, isNamespaced) allErrs := validateRoleBinding(roleBinding, isNamespaced)
allErrs = append(allErrs, validation.ValidateObjectMetaUpdate(&roleBinding.ObjectMeta, &oldRoleBinding.ObjectMeta, field.NewPath("metadata"))...) allErrs = append(allErrs, validation.ValidateObjectMetaUpdate(&roleBinding.ObjectMeta, &oldRoleBinding.ObjectMeta, field.NewPath("metadata"))...)
if oldRoleBinding.RoleRef != roleBinding.RoleRef { if oldRoleBinding.RoleRef != roleBinding.RoleRef {

View File

@@ -25,7 +25,7 @@ import (
) )
func TestValidateRoleBinding(t *testing.T) { func TestValidateRoleBinding(t *testing.T) {
errs := ValidateRoleBinding( errs := validateRoleBinding(
&rbac.RoleBinding{ &rbac.RoleBinding{
ObjectMeta: api.ObjectMeta{Namespace: api.NamespaceDefault, Name: "master"}, ObjectMeta: api.ObjectMeta{Namespace: api.NamespaceDefault, Name: "master"},
RoleRef: api.ObjectReference{Namespace: "master", Name: "valid"}, RoleRef: api.ObjectReference{Namespace: "master", Name: "valid"},
@@ -116,7 +116,7 @@ func TestValidateRoleBinding(t *testing.T) {
}, },
} }
for k, v := range errorCases { for k, v := range errorCases {
errs := ValidateRoleBinding(&v.A, true) errs := validateRoleBinding(&v.A, true)
if len(errs) == 0 { if len(errs) == 0 {
t.Errorf("expected failure %s for %v", k, v.A) t.Errorf("expected failure %s for %v", k, v.A)
continue continue
@@ -138,7 +138,7 @@ func TestValidateRoleBindingUpdate(t *testing.T) {
RoleRef: api.ObjectReference{Namespace: "master", Name: "valid"}, RoleRef: api.ObjectReference{Namespace: "master", Name: "valid"},
} }
errs := ValidateRoleBindingUpdate( errs := validateRoleBindingUpdate(
&rbac.RoleBinding{ &rbac.RoleBinding{
ObjectMeta: api.ObjectMeta{Namespace: api.NamespaceDefault, Name: "master", ResourceVersion: "1"}, ObjectMeta: api.ObjectMeta{Namespace: api.NamespaceDefault, Name: "master", ResourceVersion: "1"},
RoleRef: api.ObjectReference{Namespace: "master", Name: "valid"}, RoleRef: api.ObjectReference{Namespace: "master", Name: "valid"},
@@ -165,7 +165,7 @@ func TestValidateRoleBindingUpdate(t *testing.T) {
}, },
} }
for k, v := range errorCases { for k, v := range errorCases {
errs := ValidateRoleBindingUpdate(&v.A, old, true) errs := validateRoleBindingUpdate(&v.A, old, true)
if len(errs) == 0 { if len(errs) == 0 {
t.Errorf("expected failure %s for %v", k, v.A) t.Errorf("expected failure %s for %v", k, v.A)
continue continue
@@ -182,7 +182,7 @@ func TestValidateRoleBindingUpdate(t *testing.T) {
} }
func TestValidateRole(t *testing.T) { func TestValidateRole(t *testing.T) {
errs := ValidateRole( errs := validateRole(
&rbac.Role{ &rbac.Role{
ObjectMeta: api.ObjectMeta{Namespace: api.NamespaceDefault, Name: "master"}, ObjectMeta: api.ObjectMeta{Namespace: api.NamespaceDefault, Name: "master"},
}, },
@@ -213,7 +213,7 @@ func TestValidateRole(t *testing.T) {
}, },
} }
for k, v := range errorCases { for k, v := range errorCases {
errs := ValidateRole(&v.A, true) errs := validateRole(&v.A, true)
if len(errs) == 0 { if len(errs) == 0 {
t.Errorf("expected failure %s for %v", k, v.A) t.Errorf("expected failure %s for %v", k, v.A)
continue continue

View File

@@ -24,6 +24,11 @@ import (
"k8s.io/kubernetes/pkg/auth/authorizer" "k8s.io/kubernetes/pkg/auth/authorizer"
"k8s.io/kubernetes/pkg/auth/authorizer/abac" "k8s.io/kubernetes/pkg/auth/authorizer/abac"
"k8s.io/kubernetes/pkg/auth/authorizer/union" "k8s.io/kubernetes/pkg/auth/authorizer/union"
"k8s.io/kubernetes/pkg/registry/clusterrole"
"k8s.io/kubernetes/pkg/registry/clusterrolebinding"
"k8s.io/kubernetes/pkg/registry/role"
"k8s.io/kubernetes/pkg/registry/rolebinding"
"k8s.io/kubernetes/plugin/pkg/auth/authorizer/rbac"
"k8s.io/kubernetes/plugin/pkg/auth/authorizer/webhook" "k8s.io/kubernetes/plugin/pkg/auth/authorizer/webhook"
) )
@@ -63,10 +68,11 @@ const (
ModeAlwaysDeny string = "AlwaysDeny" ModeAlwaysDeny string = "AlwaysDeny"
ModeABAC string = "ABAC" ModeABAC string = "ABAC"
ModeWebhook string = "Webhook" ModeWebhook string = "Webhook"
ModeRBAC string = "RBAC"
) )
// Keep this list in sync with constant list above. // Keep this list in sync with constant list above.
var AuthorizationModeChoices = []string{ModeAlwaysAllow, ModeAlwaysDeny, ModeABAC, ModeWebhook} var AuthorizationModeChoices = []string{ModeAlwaysAllow, ModeAlwaysDeny, ModeABAC, ModeWebhook, ModeRBAC}
type AuthorizationConfig struct { type AuthorizationConfig struct {
// Options for ModeABAC // Options for ModeABAC
@@ -82,6 +88,16 @@ type AuthorizationConfig struct {
WebhookCacheAuthorizedTTL time.Duration WebhookCacheAuthorizedTTL time.Duration
// TTL for caching of unauthorized responses from the webhook server. // TTL for caching of unauthorized responses from the webhook server.
WebhookCacheUnauthorizedTTL time.Duration WebhookCacheUnauthorizedTTL time.Duration
// Options for RBAC
// User which can bootstrap role policies
RBACSuperUser string
RBACClusterRoleRegistry clusterrole.Registry
RBACClusterRoleBindingRegistry clusterrolebinding.Registry
RBACRoleRegistry role.Registry
RBACRoleBindingRegistry rolebinding.Registry
} }
// NewAuthorizerFromAuthorizationConfig returns the right sort of union of multiple authorizer.Authorizer objects // NewAuthorizerFromAuthorizationConfig returns the right sort of union of multiple authorizer.Authorizer objects
@@ -126,6 +142,18 @@ func NewAuthorizerFromAuthorizationConfig(authorizationModes []string, config Au
return nil, err return nil, err
} }
authorizers = append(authorizers, webhookAuthorizer) authorizers = append(authorizers, webhookAuthorizer)
case ModeRBAC:
rbacAuthorizer, err := rbac.New(
config.RBACRoleRegistry,
config.RBACRoleBindingRegistry,
config.RBACClusterRoleRegistry,
config.RBACClusterRoleBindingRegistry,
config.RBACSuperUser,
)
if err != nil {
return nil, err
}
authorizers = append(authorizers, rbacAuthorizer)
default: default:
return nil, fmt.Errorf("Unknown authorization mode %s specified", authorizationMode) return nil, fmt.Errorf("Unknown authorization mode %s specified", authorizationMode)
} }
@@ -138,6 +166,9 @@ func NewAuthorizerFromAuthorizationConfig(authorizationModes []string, config Au
if !authorizerMap[ModeWebhook] && config.WebhookConfigFile != "" { if !authorizerMap[ModeWebhook] && config.WebhookConfigFile != "" {
return nil, errors.New("Cannot specify --authorization-webhook-config-file without mode Webhook") return nil, errors.New("Cannot specify --authorization-webhook-config-file without mode Webhook")
} }
if !authorizerMap[ModeRBAC] && config.RBACSuperUser != "" {
return nil, errors.New("Cannot specify --authorization-rbac-super-user without mode RBAC")
}
return union.New(authorizers...), nil return union.New(authorizers...), nil
} }

View File

@@ -22,6 +22,7 @@ import (
unversionedbatch "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/batch/unversioned" unversionedbatch "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/batch/unversioned"
unversionedcore "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/core/unversioned" unversionedcore "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/core/unversioned"
unversionedextensions "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/extensions/unversioned" unversionedextensions "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/extensions/unversioned"
unversionedrbac "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/rbac/unversioned"
restclient "k8s.io/kubernetes/pkg/client/restclient" restclient "k8s.io/kubernetes/pkg/client/restclient"
discovery "k8s.io/kubernetes/pkg/client/typed/discovery" discovery "k8s.io/kubernetes/pkg/client/typed/discovery"
"k8s.io/kubernetes/pkg/util/flowcontrol" "k8s.io/kubernetes/pkg/util/flowcontrol"
@@ -33,6 +34,7 @@ type Interface interface {
Extensions() unversionedextensions.ExtensionsInterface Extensions() unversionedextensions.ExtensionsInterface
Autoscaling() unversionedautoscaling.AutoscalingInterface Autoscaling() unversionedautoscaling.AutoscalingInterface
Batch() unversionedbatch.BatchInterface Batch() unversionedbatch.BatchInterface
Rbac() unversionedrbac.RbacInterface
} }
// Clientset contains the clients for groups. Each group has exactly one // Clientset contains the clients for groups. Each group has exactly one
@@ -43,6 +45,7 @@ type Clientset struct {
*unversionedextensions.ExtensionsClient *unversionedextensions.ExtensionsClient
*unversionedautoscaling.AutoscalingClient *unversionedautoscaling.AutoscalingClient
*unversionedbatch.BatchClient *unversionedbatch.BatchClient
*unversionedrbac.RbacClient
} }
// Core retrieves the CoreClient // Core retrieves the CoreClient
@@ -77,6 +80,14 @@ func (c *Clientset) Batch() unversionedbatch.BatchInterface {
return c.BatchClient return c.BatchClient
} }
// Rbac retrieves the RbacClient
func (c *Clientset) Rbac() unversionedrbac.RbacInterface {
if c == nil {
return nil
}
return c.RbacClient
}
// Discovery retrieves the DiscoveryClient // Discovery retrieves the DiscoveryClient
func (c *Clientset) Discovery() discovery.DiscoveryInterface { func (c *Clientset) Discovery() discovery.DiscoveryInterface {
return c.DiscoveryClient return c.DiscoveryClient
@@ -106,6 +117,10 @@ func NewForConfig(c *restclient.Config) (*Clientset, error) {
if err != nil { if err != nil {
return &clientset, err return &clientset, err
} }
clientset.RbacClient, err = unversionedrbac.NewForConfig(&configShallowCopy)
if err != nil {
return &clientset, err
}
clientset.DiscoveryClient, err = discovery.NewDiscoveryClientForConfig(&configShallowCopy) clientset.DiscoveryClient, err = discovery.NewDiscoveryClientForConfig(&configShallowCopy)
if err != nil { if err != nil {
@@ -122,6 +137,7 @@ func NewForConfigOrDie(c *restclient.Config) *Clientset {
clientset.ExtensionsClient = unversionedextensions.NewForConfigOrDie(c) clientset.ExtensionsClient = unversionedextensions.NewForConfigOrDie(c)
clientset.AutoscalingClient = unversionedautoscaling.NewForConfigOrDie(c) clientset.AutoscalingClient = unversionedautoscaling.NewForConfigOrDie(c)
clientset.BatchClient = unversionedbatch.NewForConfigOrDie(c) clientset.BatchClient = unversionedbatch.NewForConfigOrDie(c)
clientset.RbacClient = unversionedrbac.NewForConfigOrDie(c)
clientset.DiscoveryClient = discovery.NewDiscoveryClientForConfigOrDie(c) clientset.DiscoveryClient = discovery.NewDiscoveryClientForConfigOrDie(c)
return &clientset return &clientset
@@ -134,6 +150,7 @@ func New(c *restclient.RESTClient) *Clientset {
clientset.ExtensionsClient = unversionedextensions.New(c) clientset.ExtensionsClient = unversionedextensions.New(c)
clientset.AutoscalingClient = unversionedautoscaling.New(c) clientset.AutoscalingClient = unversionedautoscaling.New(c)
clientset.BatchClient = unversionedbatch.New(c) clientset.BatchClient = unversionedbatch.New(c)
clientset.RbacClient = unversionedrbac.New(c)
clientset.DiscoveryClient = discovery.NewDiscoveryClient(c) clientset.DiscoveryClient = discovery.NewDiscoveryClient(c)
return &clientset return &clientset

View File

@@ -28,6 +28,8 @@ import (
fakeunversionedcore "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/core/unversioned/fake" fakeunversionedcore "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/core/unversioned/fake"
unversionedextensions "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/extensions/unversioned" unversionedextensions "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/extensions/unversioned"
fakeunversionedextensions "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/extensions/unversioned/fake" fakeunversionedextensions "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/extensions/unversioned/fake"
unversionedrbac "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/rbac/unversioned"
fakeunversionedrbac "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/rbac/unversioned/fake"
"k8s.io/kubernetes/pkg/client/testing/core" "k8s.io/kubernetes/pkg/client/testing/core"
"k8s.io/kubernetes/pkg/client/typed/discovery" "k8s.io/kubernetes/pkg/client/typed/discovery"
fakediscovery "k8s.io/kubernetes/pkg/client/typed/discovery/fake" fakediscovery "k8s.io/kubernetes/pkg/client/typed/discovery/fake"
@@ -84,3 +86,8 @@ func (c *Clientset) Autoscaling() unversionedautoscaling.AutoscalingInterface {
func (c *Clientset) Batch() unversionedbatch.BatchInterface { func (c *Clientset) Batch() unversionedbatch.BatchInterface {
return &fakeunversionedbatch.FakeBatch{Fake: &c.Fake} return &fakeunversionedbatch.FakeBatch{Fake: &c.Fake}
} }
// Rbac retrieves the RbacClient
func (c *Clientset) Rbac() unversionedrbac.RbacInterface {
return &fakeunversionedrbac.FakeRbac{Fake: &c.Fake}
}

View File

@@ -30,6 +30,7 @@ import (
_ "k8s.io/kubernetes/pkg/apis/extensions/install" _ "k8s.io/kubernetes/pkg/apis/extensions/install"
_ "k8s.io/kubernetes/pkg/apis/metrics/install" _ "k8s.io/kubernetes/pkg/apis/metrics/install"
_ "k8s.io/kubernetes/pkg/apis/policy/install" _ "k8s.io/kubernetes/pkg/apis/policy/install"
_ "k8s.io/kubernetes/pkg/apis/rbac/install"
) )
func init() { func init() {

View File

@@ -0,0 +1,127 @@
/*
Copyright 2016 The Kubernetes Authors 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 unversioned
import (
api "k8s.io/kubernetes/pkg/api"
rbac "k8s.io/kubernetes/pkg/apis/rbac"
watch "k8s.io/kubernetes/pkg/watch"
)
// ClusterRolesGetter has a method to return a ClusterRoleInterface.
// A group's client should implement this interface.
type ClusterRolesGetter interface {
ClusterRoles() ClusterRoleInterface
}
// ClusterRoleInterface has methods to work with ClusterRole resources.
type ClusterRoleInterface interface {
Create(*rbac.ClusterRole) (*rbac.ClusterRole, error)
Update(*rbac.ClusterRole) (*rbac.ClusterRole, error)
Delete(name string, options *api.DeleteOptions) error
DeleteCollection(options *api.DeleteOptions, listOptions api.ListOptions) error
Get(name string) (*rbac.ClusterRole, error)
List(opts api.ListOptions) (*rbac.ClusterRoleList, error)
Watch(opts api.ListOptions) (watch.Interface, error)
ClusterRoleExpansion
}
// clusterRoles implements ClusterRoleInterface
type clusterRoles struct {
client *RbacClient
}
// newClusterRoles returns a ClusterRoles
func newClusterRoles(c *RbacClient) *clusterRoles {
return &clusterRoles{
client: c,
}
}
// Create takes the representation of a clusterRole and creates it. Returns the server's representation of the clusterRole, and an error, if there is any.
func (c *clusterRoles) Create(clusterRole *rbac.ClusterRole) (result *rbac.ClusterRole, err error) {
result = &rbac.ClusterRole{}
err = c.client.Post().
Resource("clusterroles").
Body(clusterRole).
Do().
Into(result)
return
}
// Update takes the representation of a clusterRole and updates it. Returns the server's representation of the clusterRole, and an error, if there is any.
func (c *clusterRoles) Update(clusterRole *rbac.ClusterRole) (result *rbac.ClusterRole, err error) {
result = &rbac.ClusterRole{}
err = c.client.Put().
Resource("clusterroles").
Name(clusterRole.Name).
Body(clusterRole).
Do().
Into(result)
return
}
// Delete takes name of the clusterRole and deletes it. Returns an error if one occurs.
func (c *clusterRoles) Delete(name string, options *api.DeleteOptions) error {
return c.client.Delete().
Resource("clusterroles").
Name(name).
Body(options).
Do().
Error()
}
// DeleteCollection deletes a collection of objects.
func (c *clusterRoles) DeleteCollection(options *api.DeleteOptions, listOptions api.ListOptions) error {
return c.client.Delete().
Resource("clusterroles").
VersionedParams(&listOptions, api.ParameterCodec).
Body(options).
Do().
Error()
}
// Get takes name of the clusterRole, and returns the corresponding clusterRole object, and an error if there is any.
func (c *clusterRoles) Get(name string) (result *rbac.ClusterRole, err error) {
result = &rbac.ClusterRole{}
err = c.client.Get().
Resource("clusterroles").
Name(name).
Do().
Into(result)
return
}
// List takes label and field selectors, and returns the list of ClusterRoles that match those selectors.
func (c *clusterRoles) List(opts api.ListOptions) (result *rbac.ClusterRoleList, err error) {
result = &rbac.ClusterRoleList{}
err = c.client.Get().
Resource("clusterroles").
VersionedParams(&opts, api.ParameterCodec).
Do().
Into(result)
return
}
// Watch returns a watch.Interface that watches the requested clusterRoles.
func (c *clusterRoles) Watch(opts api.ListOptions) (watch.Interface, error) {
return c.client.Get().
Prefix("watch").
Resource("clusterroles").
VersionedParams(&opts, api.ParameterCodec).
Watch()
}

View File

@@ -0,0 +1,127 @@
/*
Copyright 2016 The Kubernetes Authors 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 unversioned
import (
api "k8s.io/kubernetes/pkg/api"
rbac "k8s.io/kubernetes/pkg/apis/rbac"
watch "k8s.io/kubernetes/pkg/watch"
)
// ClusterRoleBindingsGetter has a method to return a ClusterRoleBindingInterface.
// A group's client should implement this interface.
type ClusterRoleBindingsGetter interface {
ClusterRoleBindings() ClusterRoleBindingInterface
}
// ClusterRoleBindingInterface has methods to work with ClusterRoleBinding resources.
type ClusterRoleBindingInterface interface {
Create(*rbac.ClusterRoleBinding) (*rbac.ClusterRoleBinding, error)
Update(*rbac.ClusterRoleBinding) (*rbac.ClusterRoleBinding, error)
Delete(name string, options *api.DeleteOptions) error
DeleteCollection(options *api.DeleteOptions, listOptions api.ListOptions) error
Get(name string) (*rbac.ClusterRoleBinding, error)
List(opts api.ListOptions) (*rbac.ClusterRoleBindingList, error)
Watch(opts api.ListOptions) (watch.Interface, error)
ClusterRoleBindingExpansion
}
// clusterRoleBindings implements ClusterRoleBindingInterface
type clusterRoleBindings struct {
client *RbacClient
}
// newClusterRoleBindings returns a ClusterRoleBindings
func newClusterRoleBindings(c *RbacClient) *clusterRoleBindings {
return &clusterRoleBindings{
client: c,
}
}
// Create takes the representation of a clusterRoleBinding and creates it. Returns the server's representation of the clusterRoleBinding, and an error, if there is any.
func (c *clusterRoleBindings) Create(clusterRoleBinding *rbac.ClusterRoleBinding) (result *rbac.ClusterRoleBinding, err error) {
result = &rbac.ClusterRoleBinding{}
err = c.client.Post().
Resource("clusterrolebindings").
Body(clusterRoleBinding).
Do().
Into(result)
return
}
// Update takes the representation of a clusterRoleBinding and updates it. Returns the server's representation of the clusterRoleBinding, and an error, if there is any.
func (c *clusterRoleBindings) Update(clusterRoleBinding *rbac.ClusterRoleBinding) (result *rbac.ClusterRoleBinding, err error) {
result = &rbac.ClusterRoleBinding{}
err = c.client.Put().
Resource("clusterrolebindings").
Name(clusterRoleBinding.Name).
Body(clusterRoleBinding).
Do().
Into(result)
return
}
// Delete takes name of the clusterRoleBinding and deletes it. Returns an error if one occurs.
func (c *clusterRoleBindings) Delete(name string, options *api.DeleteOptions) error {
return c.client.Delete().
Resource("clusterrolebindings").
Name(name).
Body(options).
Do().
Error()
}
// DeleteCollection deletes a collection of objects.
func (c *clusterRoleBindings) DeleteCollection(options *api.DeleteOptions, listOptions api.ListOptions) error {
return c.client.Delete().
Resource("clusterrolebindings").
VersionedParams(&listOptions, api.ParameterCodec).
Body(options).
Do().
Error()
}
// Get takes name of the clusterRoleBinding, and returns the corresponding clusterRoleBinding object, and an error if there is any.
func (c *clusterRoleBindings) Get(name string) (result *rbac.ClusterRoleBinding, err error) {
result = &rbac.ClusterRoleBinding{}
err = c.client.Get().
Resource("clusterrolebindings").
Name(name).
Do().
Into(result)
return
}
// List takes label and field selectors, and returns the list of ClusterRoleBindings that match those selectors.
func (c *clusterRoleBindings) List(opts api.ListOptions) (result *rbac.ClusterRoleBindingList, err error) {
result = &rbac.ClusterRoleBindingList{}
err = c.client.Get().
Resource("clusterrolebindings").
VersionedParams(&opts, api.ParameterCodec).
Do().
Into(result)
return
}
// Watch returns a watch.Interface that watches the requested clusterRoleBindings.
func (c *clusterRoleBindings) Watch(opts api.ListOptions) (watch.Interface, error) {
return c.client.Get().
Prefix("watch").
Resource("clusterrolebindings").
VersionedParams(&opts, api.ParameterCodec).
Watch()
}

View File

@@ -0,0 +1,20 @@
/*
Copyright 2016 The Kubernetes Authors 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.
*/
// This package is generated by client-gen with the default arguments.
// This package has the automatically generated typed clients.
package unversioned

View File

@@ -0,0 +1,20 @@
/*
Copyright 2016 The Kubernetes Authors 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.
*/
// This package is generated by client-gen with the default arguments.
// Package fake has the automatically generated clients.
package fake

View File

@@ -0,0 +1,99 @@
/*
Copyright 2016 The Kubernetes Authors 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 fake
import (
api "k8s.io/kubernetes/pkg/api"
unversioned "k8s.io/kubernetes/pkg/api/unversioned"
rbac "k8s.io/kubernetes/pkg/apis/rbac"
core "k8s.io/kubernetes/pkg/client/testing/core"
labels "k8s.io/kubernetes/pkg/labels"
watch "k8s.io/kubernetes/pkg/watch"
)
// FakeClusterRoles implements ClusterRoleInterface
type FakeClusterRoles struct {
Fake *FakeRbac
}
var clusterrolesResource = unversioned.GroupVersionResource{Group: "rbac.authorization.k8s.io", Version: "", Resource: "clusterroles"}
func (c *FakeClusterRoles) Create(clusterRole *rbac.ClusterRole) (result *rbac.ClusterRole, err error) {
obj, err := c.Fake.
Invokes(core.NewRootCreateAction(clusterrolesResource, clusterRole), &rbac.ClusterRole{})
if obj == nil {
return nil, err
}
return obj.(*rbac.ClusterRole), err
}
func (c *FakeClusterRoles) Update(clusterRole *rbac.ClusterRole) (result *rbac.ClusterRole, err error) {
obj, err := c.Fake.
Invokes(core.NewRootUpdateAction(clusterrolesResource, clusterRole), &rbac.ClusterRole{})
if obj == nil {
return nil, err
}
return obj.(*rbac.ClusterRole), err
}
func (c *FakeClusterRoles) Delete(name string, options *api.DeleteOptions) error {
_, err := c.Fake.
Invokes(core.NewRootDeleteAction(clusterrolesResource, name), &rbac.ClusterRole{})
return err
}
func (c *FakeClusterRoles) DeleteCollection(options *api.DeleteOptions, listOptions api.ListOptions) error {
action := core.NewRootDeleteCollectionAction(clusterrolesResource, listOptions)
_, err := c.Fake.Invokes(action, &rbac.ClusterRoleList{})
return err
}
func (c *FakeClusterRoles) Get(name string) (result *rbac.ClusterRole, err error) {
obj, err := c.Fake.
Invokes(core.NewRootGetAction(clusterrolesResource, name), &rbac.ClusterRole{})
if obj == nil {
return nil, err
}
return obj.(*rbac.ClusterRole), err
}
func (c *FakeClusterRoles) List(opts api.ListOptions) (result *rbac.ClusterRoleList, err error) {
obj, err := c.Fake.
Invokes(core.NewRootListAction(clusterrolesResource, opts), &rbac.ClusterRoleList{})
if obj == nil {
return nil, err
}
label := opts.LabelSelector
if label == nil {
label = labels.Everything()
}
list := &rbac.ClusterRoleList{}
for _, item := range obj.(*rbac.ClusterRoleList).Items {
if label.Matches(labels.Set(item.Labels)) {
list.Items = append(list.Items, item)
}
}
return list, err
}
// Watch returns a watch.Interface that watches the requested clusterRoles.
func (c *FakeClusterRoles) Watch(opts api.ListOptions) (watch.Interface, error) {
return c.Fake.
InvokesWatch(core.NewRootWatchAction(clusterrolesResource, opts))
}

View File

@@ -0,0 +1,99 @@
/*
Copyright 2016 The Kubernetes Authors 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 fake
import (
api "k8s.io/kubernetes/pkg/api"
unversioned "k8s.io/kubernetes/pkg/api/unversioned"
rbac "k8s.io/kubernetes/pkg/apis/rbac"
core "k8s.io/kubernetes/pkg/client/testing/core"
labels "k8s.io/kubernetes/pkg/labels"
watch "k8s.io/kubernetes/pkg/watch"
)
// FakeClusterRoleBindings implements ClusterRoleBindingInterface
type FakeClusterRoleBindings struct {
Fake *FakeRbac
}
var clusterrolebindingsResource = unversioned.GroupVersionResource{Group: "rbac.authorization.k8s.io", Version: "", Resource: "clusterrolebindings"}
func (c *FakeClusterRoleBindings) Create(clusterRoleBinding *rbac.ClusterRoleBinding) (result *rbac.ClusterRoleBinding, err error) {
obj, err := c.Fake.
Invokes(core.NewRootCreateAction(clusterrolebindingsResource, clusterRoleBinding), &rbac.ClusterRoleBinding{})
if obj == nil {
return nil, err
}
return obj.(*rbac.ClusterRoleBinding), err
}
func (c *FakeClusterRoleBindings) Update(clusterRoleBinding *rbac.ClusterRoleBinding) (result *rbac.ClusterRoleBinding, err error) {
obj, err := c.Fake.
Invokes(core.NewRootUpdateAction(clusterrolebindingsResource, clusterRoleBinding), &rbac.ClusterRoleBinding{})
if obj == nil {
return nil, err
}
return obj.(*rbac.ClusterRoleBinding), err
}
func (c *FakeClusterRoleBindings) Delete(name string, options *api.DeleteOptions) error {
_, err := c.Fake.
Invokes(core.NewRootDeleteAction(clusterrolebindingsResource, name), &rbac.ClusterRoleBinding{})
return err
}
func (c *FakeClusterRoleBindings) DeleteCollection(options *api.DeleteOptions, listOptions api.ListOptions) error {
action := core.NewRootDeleteCollectionAction(clusterrolebindingsResource, listOptions)
_, err := c.Fake.Invokes(action, &rbac.ClusterRoleBindingList{})
return err
}
func (c *FakeClusterRoleBindings) Get(name string) (result *rbac.ClusterRoleBinding, err error) {
obj, err := c.Fake.
Invokes(core.NewRootGetAction(clusterrolebindingsResource, name), &rbac.ClusterRoleBinding{})
if obj == nil {
return nil, err
}
return obj.(*rbac.ClusterRoleBinding), err
}
func (c *FakeClusterRoleBindings) List(opts api.ListOptions) (result *rbac.ClusterRoleBindingList, err error) {
obj, err := c.Fake.
Invokes(core.NewRootListAction(clusterrolebindingsResource, opts), &rbac.ClusterRoleBindingList{})
if obj == nil {
return nil, err
}
label := opts.LabelSelector
if label == nil {
label = labels.Everything()
}
list := &rbac.ClusterRoleBindingList{}
for _, item := range obj.(*rbac.ClusterRoleBindingList).Items {
if label.Matches(labels.Set(item.Labels)) {
list.Items = append(list.Items, item)
}
}
return list, err
}
// Watch returns a watch.Interface that watches the requested clusterRoleBindings.
func (c *FakeClusterRoleBindings) Watch(opts api.ListOptions) (watch.Interface, error) {
return c.Fake.
InvokesWatch(core.NewRootWatchAction(clusterrolebindingsResource, opts))
}

View File

@@ -0,0 +1,49 @@
/*
Copyright 2016 The Kubernetes Authors 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 fake
import (
unversioned "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/rbac/unversioned"
restclient "k8s.io/kubernetes/pkg/client/restclient"
core "k8s.io/kubernetes/pkg/client/testing/core"
)
type FakeRbac struct {
*core.Fake
}
func (c *FakeRbac) ClusterRoles() unversioned.ClusterRoleInterface {
return &FakeClusterRoles{c}
}
func (c *FakeRbac) ClusterRoleBindings() unversioned.ClusterRoleBindingInterface {
return &FakeClusterRoleBindings{c}
}
func (c *FakeRbac) Roles(namespace string) unversioned.RoleInterface {
return &FakeRoles{c, namespace}
}
func (c *FakeRbac) RoleBindings(namespace string) unversioned.RoleBindingInterface {
return &FakeRoleBindings{c, namespace}
}
// GetRESTClient returns a RESTClient that is used to communicate
// with API server by this client implementation.
func (c *FakeRbac) GetRESTClient() *restclient.RESTClient {
return nil
}

View File

@@ -0,0 +1,106 @@
/*
Copyright 2016 The Kubernetes Authors 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 fake
import (
api "k8s.io/kubernetes/pkg/api"
unversioned "k8s.io/kubernetes/pkg/api/unversioned"
rbac "k8s.io/kubernetes/pkg/apis/rbac"
core "k8s.io/kubernetes/pkg/client/testing/core"
labels "k8s.io/kubernetes/pkg/labels"
watch "k8s.io/kubernetes/pkg/watch"
)
// FakeRoles implements RoleInterface
type FakeRoles struct {
Fake *FakeRbac
ns string
}
var rolesResource = unversioned.GroupVersionResource{Group: "rbac.authorization.k8s.io", Version: "", Resource: "roles"}
func (c *FakeRoles) Create(role *rbac.Role) (result *rbac.Role, err error) {
obj, err := c.Fake.
Invokes(core.NewCreateAction(rolesResource, c.ns, role), &rbac.Role{})
if obj == nil {
return nil, err
}
return obj.(*rbac.Role), err
}
func (c *FakeRoles) Update(role *rbac.Role) (result *rbac.Role, err error) {
obj, err := c.Fake.
Invokes(core.NewUpdateAction(rolesResource, c.ns, role), &rbac.Role{})
if obj == nil {
return nil, err
}
return obj.(*rbac.Role), err
}
func (c *FakeRoles) Delete(name string, options *api.DeleteOptions) error {
_, err := c.Fake.
Invokes(core.NewDeleteAction(rolesResource, c.ns, name), &rbac.Role{})
return err
}
func (c *FakeRoles) DeleteCollection(options *api.DeleteOptions, listOptions api.ListOptions) error {
action := core.NewDeleteCollectionAction(rolesResource, c.ns, listOptions)
_, err := c.Fake.Invokes(action, &rbac.RoleList{})
return err
}
func (c *FakeRoles) Get(name string) (result *rbac.Role, err error) {
obj, err := c.Fake.
Invokes(core.NewGetAction(rolesResource, c.ns, name), &rbac.Role{})
if obj == nil {
return nil, err
}
return obj.(*rbac.Role), err
}
func (c *FakeRoles) List(opts api.ListOptions) (result *rbac.RoleList, err error) {
obj, err := c.Fake.
Invokes(core.NewListAction(rolesResource, c.ns, opts), &rbac.RoleList{})
if obj == nil {
return nil, err
}
label := opts.LabelSelector
if label == nil {
label = labels.Everything()
}
list := &rbac.RoleList{}
for _, item := range obj.(*rbac.RoleList).Items {
if label.Matches(labels.Set(item.Labels)) {
list.Items = append(list.Items, item)
}
}
return list, err
}
// Watch returns a watch.Interface that watches the requested roles.
func (c *FakeRoles) Watch(opts api.ListOptions) (watch.Interface, error) {
return c.Fake.
InvokesWatch(core.NewWatchAction(rolesResource, c.ns, opts))
}

View File

@@ -0,0 +1,106 @@
/*
Copyright 2016 The Kubernetes Authors 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 fake
import (
api "k8s.io/kubernetes/pkg/api"
unversioned "k8s.io/kubernetes/pkg/api/unversioned"
rbac "k8s.io/kubernetes/pkg/apis/rbac"
core "k8s.io/kubernetes/pkg/client/testing/core"
labels "k8s.io/kubernetes/pkg/labels"
watch "k8s.io/kubernetes/pkg/watch"
)
// FakeRoleBindings implements RoleBindingInterface
type FakeRoleBindings struct {
Fake *FakeRbac
ns string
}
var rolebindingsResource = unversioned.GroupVersionResource{Group: "rbac.authorization.k8s.io", Version: "", Resource: "rolebindings"}
func (c *FakeRoleBindings) Create(roleBinding *rbac.RoleBinding) (result *rbac.RoleBinding, err error) {
obj, err := c.Fake.
Invokes(core.NewCreateAction(rolebindingsResource, c.ns, roleBinding), &rbac.RoleBinding{})
if obj == nil {
return nil, err
}
return obj.(*rbac.RoleBinding), err
}
func (c *FakeRoleBindings) Update(roleBinding *rbac.RoleBinding) (result *rbac.RoleBinding, err error) {
obj, err := c.Fake.
Invokes(core.NewUpdateAction(rolebindingsResource, c.ns, roleBinding), &rbac.RoleBinding{})
if obj == nil {
return nil, err
}
return obj.(*rbac.RoleBinding), err
}
func (c *FakeRoleBindings) Delete(name string, options *api.DeleteOptions) error {
_, err := c.Fake.
Invokes(core.NewDeleteAction(rolebindingsResource, c.ns, name), &rbac.RoleBinding{})
return err
}
func (c *FakeRoleBindings) DeleteCollection(options *api.DeleteOptions, listOptions api.ListOptions) error {
action := core.NewDeleteCollectionAction(rolebindingsResource, c.ns, listOptions)
_, err := c.Fake.Invokes(action, &rbac.RoleBindingList{})
return err
}
func (c *FakeRoleBindings) Get(name string) (result *rbac.RoleBinding, err error) {
obj, err := c.Fake.
Invokes(core.NewGetAction(rolebindingsResource, c.ns, name), &rbac.RoleBinding{})
if obj == nil {
return nil, err
}
return obj.(*rbac.RoleBinding), err
}
func (c *FakeRoleBindings) List(opts api.ListOptions) (result *rbac.RoleBindingList, err error) {
obj, err := c.Fake.
Invokes(core.NewListAction(rolebindingsResource, c.ns, opts), &rbac.RoleBindingList{})
if obj == nil {
return nil, err
}
label := opts.LabelSelector
if label == nil {
label = labels.Everything()
}
list := &rbac.RoleBindingList{}
for _, item := range obj.(*rbac.RoleBindingList).Items {
if label.Matches(labels.Set(item.Labels)) {
list.Items = append(list.Items, item)
}
}
return list, err
}
// Watch returns a watch.Interface that watches the requested roleBindings.
func (c *FakeRoleBindings) Watch(opts api.ListOptions) (watch.Interface, error) {
return c.Fake.
InvokesWatch(core.NewWatchAction(rolebindingsResource, c.ns, opts))
}

View File

@@ -0,0 +1,25 @@
/*
Copyright 2016 The Kubernetes Authors 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 unversioned
type ClusterRoleExpansion interface{}
type ClusterRoleBindingExpansion interface{}
type RoleExpansion interface{}
type RoleBindingExpansion interface{}

View File

@@ -0,0 +1,116 @@
/*
Copyright 2016 The Kubernetes Authors 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 unversioned
import (
api "k8s.io/kubernetes/pkg/api"
registered "k8s.io/kubernetes/pkg/apimachinery/registered"
restclient "k8s.io/kubernetes/pkg/client/restclient"
)
type RbacInterface interface {
GetRESTClient() *restclient.RESTClient
ClusterRolesGetter
ClusterRoleBindingsGetter
RolesGetter
RoleBindingsGetter
}
// RbacClient is used to interact with features provided by the Rbac group.
type RbacClient struct {
*restclient.RESTClient
}
func (c *RbacClient) ClusterRoles() ClusterRoleInterface {
return newClusterRoles(c)
}
func (c *RbacClient) ClusterRoleBindings() ClusterRoleBindingInterface {
return newClusterRoleBindings(c)
}
func (c *RbacClient) Roles(namespace string) RoleInterface {
return newRoles(c, namespace)
}
func (c *RbacClient) RoleBindings(namespace string) RoleBindingInterface {
return newRoleBindings(c, namespace)
}
// NewForConfig creates a new RbacClient for the given config.
func NewForConfig(c *restclient.Config) (*RbacClient, error) {
config := *c
if err := setConfigDefaults(&config); err != nil {
return nil, err
}
client, err := restclient.RESTClientFor(&config)
if err != nil {
return nil, err
}
return &RbacClient{client}, nil
}
// NewForConfigOrDie creates a new RbacClient for the given config and
// panics if there is an error in the config.
func NewForConfigOrDie(c *restclient.Config) *RbacClient {
client, err := NewForConfig(c)
if err != nil {
panic(err)
}
return client
}
// New creates a new RbacClient for the given RESTClient.
func New(c *restclient.RESTClient) *RbacClient {
return &RbacClient{c}
}
func setConfigDefaults(config *restclient.Config) error {
// if rbac group is not registered, return an error
g, err := registered.Group("rbac.authorization.k8s.io")
if err != nil {
return err
}
config.APIPath = "/apis"
if config.UserAgent == "" {
config.UserAgent = restclient.DefaultKubernetesUserAgent()
}
// TODO: Unconditionally set the config.Version, until we fix the config.
//if config.Version == "" {
copyGroupVersion := g.GroupVersion
config.GroupVersion = &copyGroupVersion
//}
config.NegotiatedSerializer = api.Codecs
if config.QPS == 0 {
config.QPS = 5
}
if config.Burst == 0 {
config.Burst = 10
}
return nil
}
// GetRESTClient returns a RESTClient that is used to communicate
// with API server by this client implementation.
func (c *RbacClient) GetRESTClient() *restclient.RESTClient {
if c == nil {
return nil
}
return c.RESTClient
}

View File

@@ -0,0 +1,136 @@
/*
Copyright 2016 The Kubernetes Authors 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 unversioned
import (
api "k8s.io/kubernetes/pkg/api"
rbac "k8s.io/kubernetes/pkg/apis/rbac"
watch "k8s.io/kubernetes/pkg/watch"
)
// RolesGetter has a method to return a RoleInterface.
// A group's client should implement this interface.
type RolesGetter interface {
Roles(namespace string) RoleInterface
}
// RoleInterface has methods to work with Role resources.
type RoleInterface interface {
Create(*rbac.Role) (*rbac.Role, error)
Update(*rbac.Role) (*rbac.Role, error)
Delete(name string, options *api.DeleteOptions) error
DeleteCollection(options *api.DeleteOptions, listOptions api.ListOptions) error
Get(name string) (*rbac.Role, error)
List(opts api.ListOptions) (*rbac.RoleList, error)
Watch(opts api.ListOptions) (watch.Interface, error)
RoleExpansion
}
// roles implements RoleInterface
type roles struct {
client *RbacClient
ns string
}
// newRoles returns a Roles
func newRoles(c *RbacClient, namespace string) *roles {
return &roles{
client: c,
ns: namespace,
}
}
// Create takes the representation of a role and creates it. Returns the server's representation of the role, and an error, if there is any.
func (c *roles) Create(role *rbac.Role) (result *rbac.Role, err error) {
result = &rbac.Role{}
err = c.client.Post().
Namespace(c.ns).
Resource("roles").
Body(role).
Do().
Into(result)
return
}
// Update takes the representation of a role and updates it. Returns the server's representation of the role, and an error, if there is any.
func (c *roles) Update(role *rbac.Role) (result *rbac.Role, err error) {
result = &rbac.Role{}
err = c.client.Put().
Namespace(c.ns).
Resource("roles").
Name(role.Name).
Body(role).
Do().
Into(result)
return
}
// Delete takes name of the role and deletes it. Returns an error if one occurs.
func (c *roles) Delete(name string, options *api.DeleteOptions) error {
return c.client.Delete().
Namespace(c.ns).
Resource("roles").
Name(name).
Body(options).
Do().
Error()
}
// DeleteCollection deletes a collection of objects.
func (c *roles) DeleteCollection(options *api.DeleteOptions, listOptions api.ListOptions) error {
return c.client.Delete().
Namespace(c.ns).
Resource("roles").
VersionedParams(&listOptions, api.ParameterCodec).
Body(options).
Do().
Error()
}
// Get takes name of the role, and returns the corresponding role object, and an error if there is any.
func (c *roles) Get(name string) (result *rbac.Role, err error) {
result = &rbac.Role{}
err = c.client.Get().
Namespace(c.ns).
Resource("roles").
Name(name).
Do().
Into(result)
return
}
// List takes label and field selectors, and returns the list of Roles that match those selectors.
func (c *roles) List(opts api.ListOptions) (result *rbac.RoleList, err error) {
result = &rbac.RoleList{}
err = c.client.Get().
Namespace(c.ns).
Resource("roles").
VersionedParams(&opts, api.ParameterCodec).
Do().
Into(result)
return
}
// Watch returns a watch.Interface that watches the requested roles.
func (c *roles) Watch(opts api.ListOptions) (watch.Interface, error) {
return c.client.Get().
Prefix("watch").
Namespace(c.ns).
Resource("roles").
VersionedParams(&opts, api.ParameterCodec).
Watch()
}

View File

@@ -0,0 +1,136 @@
/*
Copyright 2016 The Kubernetes Authors 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 unversioned
import (
api "k8s.io/kubernetes/pkg/api"
rbac "k8s.io/kubernetes/pkg/apis/rbac"
watch "k8s.io/kubernetes/pkg/watch"
)
// RoleBindingsGetter has a method to return a RoleBindingInterface.
// A group's client should implement this interface.
type RoleBindingsGetter interface {
RoleBindings(namespace string) RoleBindingInterface
}
// RoleBindingInterface has methods to work with RoleBinding resources.
type RoleBindingInterface interface {
Create(*rbac.RoleBinding) (*rbac.RoleBinding, error)
Update(*rbac.RoleBinding) (*rbac.RoleBinding, error)
Delete(name string, options *api.DeleteOptions) error
DeleteCollection(options *api.DeleteOptions, listOptions api.ListOptions) error
Get(name string) (*rbac.RoleBinding, error)
List(opts api.ListOptions) (*rbac.RoleBindingList, error)
Watch(opts api.ListOptions) (watch.Interface, error)
RoleBindingExpansion
}
// roleBindings implements RoleBindingInterface
type roleBindings struct {
client *RbacClient
ns string
}
// newRoleBindings returns a RoleBindings
func newRoleBindings(c *RbacClient, namespace string) *roleBindings {
return &roleBindings{
client: c,
ns: namespace,
}
}
// Create takes the representation of a roleBinding and creates it. Returns the server's representation of the roleBinding, and an error, if there is any.
func (c *roleBindings) Create(roleBinding *rbac.RoleBinding) (result *rbac.RoleBinding, err error) {
result = &rbac.RoleBinding{}
err = c.client.Post().
Namespace(c.ns).
Resource("rolebindings").
Body(roleBinding).
Do().
Into(result)
return
}
// Update takes the representation of a roleBinding and updates it. Returns the server's representation of the roleBinding, and an error, if there is any.
func (c *roleBindings) Update(roleBinding *rbac.RoleBinding) (result *rbac.RoleBinding, err error) {
result = &rbac.RoleBinding{}
err = c.client.Put().
Namespace(c.ns).
Resource("rolebindings").
Name(roleBinding.Name).
Body(roleBinding).
Do().
Into(result)
return
}
// Delete takes name of the roleBinding and deletes it. Returns an error if one occurs.
func (c *roleBindings) Delete(name string, options *api.DeleteOptions) error {
return c.client.Delete().
Namespace(c.ns).
Resource("rolebindings").
Name(name).
Body(options).
Do().
Error()
}
// DeleteCollection deletes a collection of objects.
func (c *roleBindings) DeleteCollection(options *api.DeleteOptions, listOptions api.ListOptions) error {
return c.client.Delete().
Namespace(c.ns).
Resource("rolebindings").
VersionedParams(&listOptions, api.ParameterCodec).
Body(options).
Do().
Error()
}
// Get takes name of the roleBinding, and returns the corresponding roleBinding object, and an error if there is any.
func (c *roleBindings) Get(name string) (result *rbac.RoleBinding, err error) {
result = &rbac.RoleBinding{}
err = c.client.Get().
Namespace(c.ns).
Resource("rolebindings").
Name(name).
Do().
Into(result)
return
}
// List takes label and field selectors, and returns the list of RoleBindings that match those selectors.
func (c *roleBindings) List(opts api.ListOptions) (result *rbac.RoleBindingList, err error) {
result = &rbac.RoleBindingList{}
err = c.client.Get().
Namespace(c.ns).
Resource("rolebindings").
VersionedParams(&opts, api.ParameterCodec).
Do().
Into(result)
return
}
// Watch returns a watch.Interface that watches the requested roleBindings.
func (c *roleBindings) Watch(opts api.ListOptions) (watch.Interface, error) {
return c.client.Get().
Prefix("watch").
Namespace(c.ns).
Resource("rolebindings").
VersionedParams(&opts, api.ParameterCodec).
Watch()
}

View File

@@ -47,6 +47,7 @@ type Interface interface {
Autoscaling() AutoscalingInterface Autoscaling() AutoscalingInterface
Batch() BatchInterface Batch() BatchInterface
Extensions() ExtensionsInterface Extensions() ExtensionsInterface
Rbac() RbacInterface
Discovery() discovery.DiscoveryInterface Discovery() discovery.DiscoveryInterface
} }
@@ -121,6 +122,7 @@ type Client struct {
*ExtensionsClient *ExtensionsClient
*AppsClient *AppsClient
*PolicyClient *PolicyClient
*RbacClient
*discovery.DiscoveryClient *discovery.DiscoveryClient
} }
@@ -162,6 +164,10 @@ func (c *Client) Apps() AppsInterface {
return c.AppsClient return c.AppsClient
} }
func (c *Client) Rbac() RbacInterface {
return c.RbacClient
}
func (c *Client) Discovery() discovery.DiscoveryInterface { func (c *Client) Discovery() discovery.DiscoveryInterface {
return c.DiscoveryClient return c.DiscoveryClient
} }

View File

@@ -0,0 +1,92 @@
/*
Copyright 2016 The Kubernetes Authors 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 unversioned
import (
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/apis/rbac"
"k8s.io/kubernetes/pkg/watch"
)
// ClusterRoleBindings has methods to work with ClusterRoleBinding resources in a namespace
type ClusterRoleBindings interface {
ClusterRoleBindings() ClusterRoleBindingInterface
}
// ClusterRoleBindingInterface has methods to work with ClusterRoleBinding resources.
type ClusterRoleBindingInterface interface {
List(opts api.ListOptions) (*rbac.ClusterRoleBindingList, error)
Get(name string) (*rbac.ClusterRoleBinding, error)
Delete(name string, options *api.DeleteOptions) error
Create(clusterRoleBinding *rbac.ClusterRoleBinding) (*rbac.ClusterRoleBinding, error)
Update(clusterRoleBinding *rbac.ClusterRoleBinding) (*rbac.ClusterRoleBinding, error)
Watch(opts api.ListOptions) (watch.Interface, error)
}
// clusterRoleBindings implements ClusterRoleBindingsNamespacer interface
type clusterRoleBindings struct {
client *RbacClient
}
// newClusterRoleBindings returns a clusterRoleBindings
func newClusterRoleBindings(c *RbacClient) *clusterRoleBindings {
return &clusterRoleBindings{
client: c,
}
}
// List takes label and field selectors, and returns the list of clusterRoleBindings that match those selectors.
func (c *clusterRoleBindings) List(opts api.ListOptions) (result *rbac.ClusterRoleBindingList, err error) {
result = &rbac.ClusterRoleBindingList{}
err = c.client.Get().Resource("clusterrolebindings").VersionedParams(&opts, api.ParameterCodec).Do().Into(result)
return
}
// Get takes the name of the clusterRoleBinding, and returns the corresponding ClusterRoleBinding object, and an error if it occurs
func (c *clusterRoleBindings) Get(name string) (result *rbac.ClusterRoleBinding, err error) {
result = &rbac.ClusterRoleBinding{}
err = c.client.Get().Resource("clusterrolebindings").Name(name).Do().Into(result)
return
}
// Delete takes the name of the clusterRoleBinding and deletes it. Returns an error if one occurs.
func (c *clusterRoleBindings) Delete(name string, options *api.DeleteOptions) error {
return c.client.Delete().Resource("clusterrolebindings").Name(name).Body(options).Do().Error()
}
// Create takes the representation of a clusterRoleBinding and creates it. Returns the server's representation of the clusterRoleBinding, and an error, if it occurs.
func (c *clusterRoleBindings) Create(clusterRoleBinding *rbac.ClusterRoleBinding) (result *rbac.ClusterRoleBinding, err error) {
result = &rbac.ClusterRoleBinding{}
err = c.client.Post().Resource("clusterrolebindings").Body(clusterRoleBinding).Do().Into(result)
return
}
// Update takes the representation of a clusterRoleBinding and updates it. Returns the server's representation of the clusterRoleBinding, and an error, if it occurs.
func (c *clusterRoleBindings) Update(clusterRoleBinding *rbac.ClusterRoleBinding) (result *rbac.ClusterRoleBinding, err error) {
result = &rbac.ClusterRoleBinding{}
err = c.client.Put().Resource("clusterrolebindings").Name(clusterRoleBinding.Name).Body(clusterRoleBinding).Do().Into(result)
return
}
// Watch returns a watch.Interface that watches the requested clusterRoleBindings.
func (c *clusterRoleBindings) Watch(opts api.ListOptions) (watch.Interface, error) {
return c.client.Get().
Prefix("watch").
Resource("clusterrolebindings").
VersionedParams(&opts, api.ParameterCodec).
Watch()
}

View File

@@ -0,0 +1,92 @@
/*
Copyright 2016 The Kubernetes Authors 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 unversioned
import (
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/apis/rbac"
"k8s.io/kubernetes/pkg/watch"
)
// ClusterRoles has methods to work with ClusterRole resources in a namespace
type ClusterRoles interface {
ClusterRoles() ClusterRoleInterface
}
// ClusterRoleInterface has methods to work with ClusterRole resources.
type ClusterRoleInterface interface {
List(opts api.ListOptions) (*rbac.ClusterRoleList, error)
Get(name string) (*rbac.ClusterRole, error)
Delete(name string, options *api.DeleteOptions) error
Create(clusterRole *rbac.ClusterRole) (*rbac.ClusterRole, error)
Update(clusterRole *rbac.ClusterRole) (*rbac.ClusterRole, error)
Watch(opts api.ListOptions) (watch.Interface, error)
}
// clusterRoles implements ClusterRolesNamespacer interface
type clusterRoles struct {
client *RbacClient
}
// newClusterRoles returns a clusterRoles
func newClusterRoles(c *RbacClient) *clusterRoles {
return &clusterRoles{
client: c,
}
}
// List takes label and field selectors, and returns the list of clusterRoles that match those selectors.
func (c *clusterRoles) List(opts api.ListOptions) (result *rbac.ClusterRoleList, err error) {
result = &rbac.ClusterRoleList{}
err = c.client.Get().Resource("clusterroles").VersionedParams(&opts, api.ParameterCodec).Do().Into(result)
return
}
// Get takes the name of the clusterRole, and returns the corresponding ClusterRole object, and an error if it occurs
func (c *clusterRoles) Get(name string) (result *rbac.ClusterRole, err error) {
result = &rbac.ClusterRole{}
err = c.client.Get().Resource("clusterroles").Name(name).Do().Into(result)
return
}
// Delete takes the name of the clusterRole and deletes it. Returns an error if one occurs.
func (c *clusterRoles) Delete(name string, options *api.DeleteOptions) error {
return c.client.Delete().Resource("clusterroles").Name(name).Body(options).Do().Error()
}
// Create takes the representation of a clusterRole and creates it. Returns the server's representation of the clusterRole, and an error, if it occurs.
func (c *clusterRoles) Create(clusterRole *rbac.ClusterRole) (result *rbac.ClusterRole, err error) {
result = &rbac.ClusterRole{}
err = c.client.Post().Resource("clusterroles").Body(clusterRole).Do().Into(result)
return
}
// Update takes the representation of a clusterRole and updates it. Returns the server's representation of the clusterRole, and an error, if it occurs.
func (c *clusterRoles) Update(clusterRole *rbac.ClusterRole) (result *rbac.ClusterRole, err error) {
result = &rbac.ClusterRole{}
err = c.client.Put().Resource("clusterroles").Name(clusterRole.Name).Body(clusterRole).Do().Into(result)
return
}
// Watch returns a watch.Interface that watches the requested clusterRoles.
func (c *clusterRoles) Watch(opts api.ListOptions) (watch.Interface, error) {
return c.client.Get().
Prefix("watch").
Resource("clusterroles").
VersionedParams(&opts, api.ParameterCodec).
Watch()
}

View File

@@ -27,6 +27,7 @@ import (
"k8s.io/kubernetes/pkg/apis/batch" "k8s.io/kubernetes/pkg/apis/batch"
"k8s.io/kubernetes/pkg/apis/extensions" "k8s.io/kubernetes/pkg/apis/extensions"
"k8s.io/kubernetes/pkg/apis/policy" "k8s.io/kubernetes/pkg/apis/policy"
"k8s.io/kubernetes/pkg/apis/rbac"
"k8s.io/kubernetes/pkg/client/restclient" "k8s.io/kubernetes/pkg/client/restclient"
"k8s.io/kubernetes/pkg/client/typed/discovery" "k8s.io/kubernetes/pkg/client/typed/discovery"
"k8s.io/kubernetes/pkg/util/sets" "k8s.io/kubernetes/pkg/util/sets"
@@ -104,7 +105,16 @@ func New(c *restclient.Config) (*Client, error) {
} }
} }
return &Client{RESTClient: client, AutoscalingClient: autoscalingClient, BatchClient: batchClient, ExtensionsClient: extensionsClient, DiscoveryClient: discoveryClient, AppsClient: appsClient, PolicyClient: policyClient}, nil var rbacClient *RbacClient
if registered.IsRegistered(rbac.GroupName) {
rbacConfig := *c
rbacClient, err = NewRbac(&rbacConfig)
if err != nil {
return nil, err
}
}
return &Client{RESTClient: client, AutoscalingClient: autoscalingClient, BatchClient: batchClient, ExtensionsClient: extensionsClient, DiscoveryClient: discoveryClient, AppsClient: appsClient, PolicyClient: policyClient, RbacClient: rbacClient}, nil
} }
// MatchesServerVersion queries the server to compares the build version // MatchesServerVersion queries the server to compares the build version

View File

@@ -31,6 +31,7 @@ import (
_ "k8s.io/kubernetes/pkg/apis/extensions/install" _ "k8s.io/kubernetes/pkg/apis/extensions/install"
_ "k8s.io/kubernetes/pkg/apis/metrics/install" _ "k8s.io/kubernetes/pkg/apis/metrics/install"
_ "k8s.io/kubernetes/pkg/apis/policy/install" _ "k8s.io/kubernetes/pkg/apis/policy/install"
_ "k8s.io/kubernetes/pkg/apis/rbac/install"
) )
func init() { func init() {

View File

@@ -0,0 +1,103 @@
/*
Copyright 2016 The Kubernetes Authors 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 unversioned
import (
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/apimachinery/registered"
"k8s.io/kubernetes/pkg/apis/rbac"
"k8s.io/kubernetes/pkg/client/restclient"
)
// Interface holds the methods for clients of Kubernetes to allow mock testing.
type RbacInterface interface {
RoleBindingsNamespacer
RolesNamespacer
ClusterRoleBindings
ClusterRoles
}
type RbacClient struct {
*restclient.RESTClient
}
func (c *RbacClient) RoleBindings(namespace string) RoleBindingInterface {
return newRoleBindings(c, namespace)
}
func (c *RbacClient) Roles(namespace string) RoleInterface {
return newRoles(c, namespace)
}
func (c *RbacClient) ClusterRoleBindings() ClusterRoleBindingInterface {
return newClusterRoleBindings(c)
}
func (c *RbacClient) ClusterRoles() ClusterRoleInterface {
return newClusterRoles(c)
}
// NewRbac creates a new RbacClient for the given config.
func NewRbac(c *restclient.Config) (*RbacClient, error) {
config := *c
if err := setRbacDefaults(&config); err != nil {
return nil, err
}
client, err := restclient.RESTClientFor(&config)
if err != nil {
return nil, err
}
return &RbacClient{client}, nil
}
// NewRbacOrDie creates a new RbacClient for the given config and
// panics if there is an error in the config.
func NewRbacOrDie(c *restclient.Config) *RbacClient {
client, err := NewRbac(c)
if err != nil {
panic(err)
}
return client
}
func setRbacDefaults(config *restclient.Config) error {
// if rbac group is not registered, return an error
g, err := registered.Group(rbac.GroupName)
if err != nil {
return err
}
config.APIPath = defaultAPIPath
if config.UserAgent == "" {
config.UserAgent = restclient.DefaultKubernetesUserAgent()
}
// TODO: Unconditionally set the config.Version, until we fix the config.
//if config.Version == "" {
copyGroupVersion := g.GroupVersion
config.GroupVersion = &copyGroupVersion
//}
config.Codec = api.Codecs.LegacyCodec(*config.GroupVersion)
config.NegotiatedSerializer = api.Codecs
if config.QPS == 0 {
config.QPS = 5
}
if config.Burst == 0 {
config.Burst = 10
}
return nil
}

View File

@@ -0,0 +1,95 @@
/*
Copyright 2016 The Kubernetes Authors 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 unversioned
import (
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/apis/rbac"
"k8s.io/kubernetes/pkg/watch"
)
// RoleBindingsNamespacer has methods to work with RoleBinding resources in a namespace
type RoleBindingsNamespacer interface {
RoleBindings(namespace string) RoleBindingInterface
}
// RoleBindingInterface has methods to work with RoleBinding resources.
type RoleBindingInterface interface {
List(opts api.ListOptions) (*rbac.RoleBindingList, error)
Get(name string) (*rbac.RoleBinding, error)
Delete(name string, options *api.DeleteOptions) error
Create(roleBinding *rbac.RoleBinding) (*rbac.RoleBinding, error)
Update(roleBinding *rbac.RoleBinding) (*rbac.RoleBinding, error)
Watch(opts api.ListOptions) (watch.Interface, error)
}
// roleBindings implements RoleBindingsNamespacer interface
type roleBindings struct {
client *RbacClient
ns string
}
// newRoleBindings returns a roleBindings
func newRoleBindings(c *RbacClient, namespace string) *roleBindings {
return &roleBindings{
client: c,
ns: namespace,
}
}
// List takes label and field selectors, and returns the list of roleBindings that match those selectors.
func (c *roleBindings) List(opts api.ListOptions) (result *rbac.RoleBindingList, err error) {
result = &rbac.RoleBindingList{}
err = c.client.Get().Namespace(c.ns).Resource("rolebindings").VersionedParams(&opts, api.ParameterCodec).Do().Into(result)
return
}
// Get takes the name of the roleBinding, and returns the corresponding RoleBinding object, and an error if it occurs
func (c *roleBindings) Get(name string) (result *rbac.RoleBinding, err error) {
result = &rbac.RoleBinding{}
err = c.client.Get().Namespace(c.ns).Resource("rolebindings").Name(name).Do().Into(result)
return
}
// Delete takes the name of the roleBinding and deletes it. Returns an error if one occurs.
func (c *roleBindings) Delete(name string, options *api.DeleteOptions) error {
return c.client.Delete().Namespace(c.ns).Resource("rolebindings").Name(name).Body(options).Do().Error()
}
// Create takes the representation of a roleBinding and creates it. Returns the server's representation of the roleBinding, and an error, if it occurs.
func (c *roleBindings) Create(roleBinding *rbac.RoleBinding) (result *rbac.RoleBinding, err error) {
result = &rbac.RoleBinding{}
err = c.client.Post().Namespace(c.ns).Resource("rolebindings").Body(roleBinding).Do().Into(result)
return
}
// Update takes the representation of a roleBinding and updates it. Returns the server's representation of the roleBinding, and an error, if it occurs.
func (c *roleBindings) Update(roleBinding *rbac.RoleBinding) (result *rbac.RoleBinding, err error) {
result = &rbac.RoleBinding{}
err = c.client.Put().Namespace(c.ns).Resource("rolebindings").Name(roleBinding.Name).Body(roleBinding).Do().Into(result)
return
}
// Watch returns a watch.Interface that watches the requested roleBindings.
func (c *roleBindings) Watch(opts api.ListOptions) (watch.Interface, error) {
return c.client.Get().
Prefix("watch").
Namespace(c.ns).
Resource("rolebindings").
VersionedParams(&opts, api.ParameterCodec).
Watch()
}

View File

@@ -0,0 +1,95 @@
/*
Copyright 2016 The Kubernetes Authors 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 unversioned
import (
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/apis/rbac"
"k8s.io/kubernetes/pkg/watch"
)
// RolesNamespacer has methods to work with Role resources in a namespace
type RolesNamespacer interface {
Roles(namespace string) RoleInterface
}
// RoleInterface has methods to work with Role resources.
type RoleInterface interface {
List(opts api.ListOptions) (*rbac.RoleList, error)
Get(name string) (*rbac.Role, error)
Delete(name string, options *api.DeleteOptions) error
Create(role *rbac.Role) (*rbac.Role, error)
Update(role *rbac.Role) (*rbac.Role, error)
Watch(opts api.ListOptions) (watch.Interface, error)
}
// roles implements RolesNamespacer interface
type roles struct {
client *RbacClient
ns string
}
// newRoles returns a roles
func newRoles(c *RbacClient, namespace string) *roles {
return &roles{
client: c,
ns: namespace,
}
}
// List takes label and field selectors, and returns the list of roles that match those selectors.
func (c *roles) List(opts api.ListOptions) (result *rbac.RoleList, err error) {
result = &rbac.RoleList{}
err = c.client.Get().Namespace(c.ns).Resource("roles").VersionedParams(&opts, api.ParameterCodec).Do().Into(result)
return
}
// Get takes the name of the role, and returns the corresponding Role object, and an error if it occurs
func (c *roles) Get(name string) (result *rbac.Role, err error) {
result = &rbac.Role{}
err = c.client.Get().Namespace(c.ns).Resource("roles").Name(name).Do().Into(result)
return
}
// Delete takes the name of the role and deletes it. Returns an error if one occurs.
func (c *roles) Delete(name string, options *api.DeleteOptions) error {
return c.client.Delete().Namespace(c.ns).Resource("roles").Name(name).Body(options).Do().Error()
}
// Create takes the representation of a role and creates it. Returns the server's representation of the role, and an error, if it occurs.
func (c *roles) Create(role *rbac.Role) (result *rbac.Role, err error) {
result = &rbac.Role{}
err = c.client.Post().Namespace(c.ns).Resource("roles").Body(role).Do().Into(result)
return
}
// Update takes the representation of a role and updates it. Returns the server's representation of the role, and an error, if it occurs.
func (c *roles) Update(role *rbac.Role) (result *rbac.Role, err error) {
result = &rbac.Role{}
err = c.client.Put().Namespace(c.ns).Resource("roles").Name(role.Name).Body(role).Do().Into(result)
return
}
// Watch returns a watch.Interface that watches the requested roles.
func (c *roles) Watch(opts api.ListOptions) (watch.Interface, error) {
return c.client.Get().
Prefix("watch").
Namespace(c.ns).
Resource("roles").
VersionedParams(&opts, api.ParameterCodec).
Watch()
}

View File

@@ -0,0 +1,73 @@
/*
Copyright 2016 The Kubernetes Authors 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 testclient
import (
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/apis/rbac"
"k8s.io/kubernetes/pkg/watch"
)
// FakeClusterRoleBindings implements ClusterRoleBindingInterface
type FakeClusterRoleBindings struct {
Fake *FakeRbac
}
func (c *FakeClusterRoleBindings) Get(name string) (*rbac.ClusterRoleBinding, error) {
obj, err := c.Fake.Invokes(NewRootGetAction("clusterrolebindings", name), &rbac.ClusterRoleBinding{})
if obj == nil {
return nil, err
}
return obj.(*rbac.ClusterRoleBinding), err
}
func (c *FakeClusterRoleBindings) List(opts api.ListOptions) (*rbac.ClusterRoleBindingList, error) {
obj, err := c.Fake.Invokes(NewRootListAction("clusterrolebindings", opts), &rbac.ClusterRoleBindingList{})
if obj == nil {
return nil, err
}
return obj.(*rbac.ClusterRoleBindingList), err
}
func (c *FakeClusterRoleBindings) Create(csr *rbac.ClusterRoleBinding) (*rbac.ClusterRoleBinding, error) {
obj, err := c.Fake.Invokes(NewRootCreateAction("clusterrolebindings", csr), csr)
if obj == nil {
return nil, err
}
return obj.(*rbac.ClusterRoleBinding), err
}
func (c *FakeClusterRoleBindings) Update(csr *rbac.ClusterRoleBinding) (*rbac.ClusterRoleBinding, error) {
obj, err := c.Fake.Invokes(NewRootUpdateAction("clusterrolebindings", csr), csr)
if obj == nil {
return nil, err
}
return obj.(*rbac.ClusterRoleBinding), err
}
func (c *FakeClusterRoleBindings) Delete(name string, opts *api.DeleteOptions) error {
_, err := c.Fake.Invokes(NewRootDeleteAction("clusterrolebindings", name), &rbac.ClusterRoleBinding{})
return err
}
func (c *FakeClusterRoleBindings) Watch(opts api.ListOptions) (watch.Interface, error) {
return c.Fake.InvokesWatch(NewRootWatchAction("clusterrolebindings", opts))
}

View File

@@ -0,0 +1,73 @@
/*
Copyright 2016 The Kubernetes Authors 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 testclient
import (
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/apis/rbac"
"k8s.io/kubernetes/pkg/watch"
)
// FakeClusterRoles implements ClusterRoleInterface
type FakeClusterRoles struct {
Fake *FakeRbac
}
func (c *FakeClusterRoles) Get(name string) (*rbac.ClusterRole, error) {
obj, err := c.Fake.Invokes(NewRootGetAction("clusterroles", name), &rbac.ClusterRole{})
if obj == nil {
return nil, err
}
return obj.(*rbac.ClusterRole), err
}
func (c *FakeClusterRoles) List(opts api.ListOptions) (*rbac.ClusterRoleList, error) {
obj, err := c.Fake.Invokes(NewRootListAction("clusterroles", opts), &rbac.ClusterRoleList{})
if obj == nil {
return nil, err
}
return obj.(*rbac.ClusterRoleList), err
}
func (c *FakeClusterRoles) Create(csr *rbac.ClusterRole) (*rbac.ClusterRole, error) {
obj, err := c.Fake.Invokes(NewRootCreateAction("clusterroles", csr), csr)
if obj == nil {
return nil, err
}
return obj.(*rbac.ClusterRole), err
}
func (c *FakeClusterRoles) Update(csr *rbac.ClusterRole) (*rbac.ClusterRole, error) {
obj, err := c.Fake.Invokes(NewRootUpdateAction("clusterroles", csr), csr)
if obj == nil {
return nil, err
}
return obj.(*rbac.ClusterRole), err
}
func (c *FakeClusterRoles) Delete(name string, opts *api.DeleteOptions) error {
_, err := c.Fake.Invokes(NewRootDeleteAction("clusterroles", name), &rbac.ClusterRole{})
return err
}
func (c *FakeClusterRoles) Watch(opts api.ListOptions) (watch.Interface, error) {
return c.Fake.InvokesWatch(NewRootWatchAction("clusterroles", opts))
}

View File

@@ -0,0 +1,74 @@
/*
Copyright 2016 The Kubernetes Authors 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 testclient
import (
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/apis/rbac"
"k8s.io/kubernetes/pkg/watch"
)
// FakeRoleBindings implements RoleBindingInterface
type FakeRoleBindings struct {
Fake *FakeRbac
Namespace string
}
func (c *FakeRoleBindings) Get(name string) (*rbac.RoleBinding, error) {
obj, err := c.Fake.Invokes(NewGetAction("rolebindings", c.Namespace, name), &rbac.RoleBinding{})
if obj == nil {
return nil, err
}
return obj.(*rbac.RoleBinding), err
}
func (c *FakeRoleBindings) List(opts api.ListOptions) (*rbac.RoleBindingList, error) {
obj, err := c.Fake.Invokes(NewListAction("rolebindings", c.Namespace, opts), &rbac.RoleBindingList{})
if obj == nil {
return nil, err
}
return obj.(*rbac.RoleBindingList), err
}
func (c *FakeRoleBindings) Create(csr *rbac.RoleBinding) (*rbac.RoleBinding, error) {
obj, err := c.Fake.Invokes(NewCreateAction("rolebindings", c.Namespace, csr), csr)
if obj == nil {
return nil, err
}
return obj.(*rbac.RoleBinding), err
}
func (c *FakeRoleBindings) Update(csr *rbac.RoleBinding) (*rbac.RoleBinding, error) {
obj, err := c.Fake.Invokes(NewUpdateAction("rolebindings", c.Namespace, csr), csr)
if obj == nil {
return nil, err
}
return obj.(*rbac.RoleBinding), err
}
func (c *FakeRoleBindings) Delete(name string, opts *api.DeleteOptions) error {
_, err := c.Fake.Invokes(NewDeleteAction("rolebindings", c.Namespace, name), &rbac.RoleBinding{})
return err
}
func (c *FakeRoleBindings) Watch(opts api.ListOptions) (watch.Interface, error) {
return c.Fake.InvokesWatch(NewWatchAction("rolebindings", c.Namespace, opts))
}

View File

@@ -0,0 +1,74 @@
/*
Copyright 2016 The Kubernetes Authors 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 testclient
import (
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/apis/rbac"
"k8s.io/kubernetes/pkg/watch"
)
// FakeRoles implements RoleInterface
type FakeRoles struct {
Fake *FakeRbac
Namespace string
}
func (c *FakeRoles) Get(name string) (*rbac.Role, error) {
obj, err := c.Fake.Invokes(NewGetAction("roles", c.Namespace, name), &rbac.Role{})
if obj == nil {
return nil, err
}
return obj.(*rbac.Role), err
}
func (c *FakeRoles) List(opts api.ListOptions) (*rbac.RoleList, error) {
obj, err := c.Fake.Invokes(NewListAction("roles", c.Namespace, opts), &rbac.RoleList{})
if obj == nil {
return nil, err
}
return obj.(*rbac.RoleList), err
}
func (c *FakeRoles) Create(csr *rbac.Role) (*rbac.Role, error) {
obj, err := c.Fake.Invokes(NewCreateAction("roles", c.Namespace, csr), csr)
if obj == nil {
return nil, err
}
return obj.(*rbac.Role), err
}
func (c *FakeRoles) Update(csr *rbac.Role) (*rbac.Role, error) {
obj, err := c.Fake.Invokes(NewUpdateAction("roles", c.Namespace, csr), csr)
if obj == nil {
return nil, err
}
return obj.(*rbac.Role), err
}
func (c *FakeRoles) Delete(name string, opts *api.DeleteOptions) error {
_, err := c.Fake.Invokes(NewDeleteAction("roles", c.Namespace, name), &rbac.Role{})
return err
}
func (c *FakeRoles) Watch(opts api.ListOptions) (watch.Interface, error) {
return c.Fake.InvokesWatch(NewWatchAction("roles", c.Namespace, opts))
}

View File

@@ -101,6 +101,10 @@ func (c *Client) Setup(t *testing.T) *Client {
Host: c.server.URL, Host: c.server.URL,
ContentConfig: restclient.ContentConfig{GroupVersion: testapi.Extensions.GroupVersion()}, ContentConfig: restclient.ContentConfig{GroupVersion: testapi.Extensions.GroupVersion()},
}) })
c.RbacClient = client.NewRbacOrDie(&restclient.Config{
Host: c.server.URL,
ContentConfig: restclient.ContentConfig{GroupVersion: testapi.Rbac.GroupVersion()},
})
c.Clientset = clientset.NewForConfigOrDie(&restclient.Config{Host: c.server.URL}) c.Clientset = clientset.NewForConfigOrDie(&restclient.Config{Host: c.server.URL})
} }

View File

@@ -301,6 +301,10 @@ func (c *Fake) ConfigMaps(namespace string) client.ConfigMapsInterface {
return &FakeConfigMaps{Fake: c, Namespace: namespace} return &FakeConfigMaps{Fake: c, Namespace: namespace}
} }
func (c *Fake) Rbac() client.RbacInterface {
return &FakeRbac{Fake: c}
}
// SwaggerSchema returns an empty swagger.ApiDeclaration for testing // SwaggerSchema returns an empty swagger.ApiDeclaration for testing
func (c *Fake) SwaggerSchema(version unversioned.GroupVersion) (*swagger.ApiDeclaration, error) { func (c *Fake) SwaggerSchema(version unversioned.GroupVersion) (*swagger.ApiDeclaration, error) {
action := ActionImpl{} action := ActionImpl{}
@@ -386,6 +390,30 @@ func (c *FakeExperimental) NetworkPolicies(namespace string) client.NetworkPolic
return &FakeNetworkPolicies{Fake: c, Namespace: namespace} return &FakeNetworkPolicies{Fake: c, Namespace: namespace}
} }
func NewSimpleFakeRbac(objects ...runtime.Object) *FakeRbac {
return &FakeRbac{Fake: NewSimpleFake(objects...)}
}
type FakeRbac struct {
*Fake
}
func (c *FakeRbac) Roles(namespace string) client.RoleInterface {
return &FakeRoles{Fake: c, Namespace: namespace}
}
func (c *FakeRbac) RoleBindings(namespace string) client.RoleBindingInterface {
return &FakeRoleBindings{Fake: c, Namespace: namespace}
}
func (c *FakeRbac) ClusterRoles() client.ClusterRoleInterface {
return &FakeClusterRoles{Fake: c}
}
func (c *FakeRbac) ClusterRoleBindings() client.ClusterRoleBindingInterface {
return &FakeClusterRoleBindings{Fake: c}
}
type FakeDiscovery struct { type FakeDiscovery struct {
*Fake *Fake
} }

View File

@@ -118,6 +118,8 @@ type Config struct {
Authorizer authorizer.Authorizer Authorizer authorizer.Authorizer
AdmissionControl admission.Interface AdmissionControl admission.Interface
MasterServiceNamespace string MasterServiceNamespace string
// TODO(ericchiang): Determine if policy escalation checks should be an admission controller.
AuthorizerRBACSuperUser string
// Map requests to contexts. Exported so downstream consumers can provider their own mappers // Map requests to contexts. Exported so downstream consumers can provider their own mappers
RequestContextMapper api.RequestContextMapper RequestContextMapper api.RequestContextMapper

View File

@@ -227,6 +227,7 @@ func (s *ServerRunOptions) AddFlags(fs *pflag.FlagSet) {
fs.StringVar(&s.AuthorizationConfig.WebhookConfigFile, "authorization-webhook-config-file", s.AuthorizationConfig.WebhookConfigFile, "File with webhook configuration in kubeconfig format, used with --authorization-mode=Webhook. The API server will query the remote service to determine access on the API server's secure port.") fs.StringVar(&s.AuthorizationConfig.WebhookConfigFile, "authorization-webhook-config-file", s.AuthorizationConfig.WebhookConfigFile, "File with webhook configuration in kubeconfig format, used with --authorization-mode=Webhook. The API server will query the remote service to determine access on the API server's secure port.")
fs.DurationVar(&s.AuthorizationConfig.WebhookCacheAuthorizedTTL, "authorization-webhook-cache-authorized-ttl", s.AuthorizationConfig.WebhookCacheAuthorizedTTL, "The duration to cache 'authorized' responses from the webhook authorizer. Default is 5m.") fs.DurationVar(&s.AuthorizationConfig.WebhookCacheAuthorizedTTL, "authorization-webhook-cache-authorized-ttl", s.AuthorizationConfig.WebhookCacheAuthorizedTTL, "The duration to cache 'authorized' responses from the webhook authorizer. Default is 5m.")
fs.DurationVar(&s.AuthorizationConfig.WebhookCacheUnauthorizedTTL, "authorization-webhook-cache-unauthorized-ttl", s.AuthorizationConfig.WebhookCacheUnauthorizedTTL, "The duration to cache 'unauthorized' responses from the webhook authorizer. Default is 30s.") fs.DurationVar(&s.AuthorizationConfig.WebhookCacheUnauthorizedTTL, "authorization-webhook-cache-unauthorized-ttl", s.AuthorizationConfig.WebhookCacheUnauthorizedTTL, "The duration to cache 'unauthorized' responses from the webhook authorizer. Default is 30s.")
fs.StringVar(&s.AuthorizationConfig.RBACSuperUser, "authorization-rbac-super-user", s.AuthorizationConfig.RBACSuperUser, "If specified, a username which avoids RBAC authorization checks and role binding privilege escalation checks, to be used with --authorization-mode=RBAC.")
fs.StringVar(&s.BasicAuthFile, "basic-auth-file", s.BasicAuthFile, "If set, the file that will be used to admit requests to the secure port of the API server via http basic authentication.") fs.StringVar(&s.BasicAuthFile, "basic-auth-file", s.BasicAuthFile, "If set, the file that will be used to admit requests to the secure port of the API server via http basic authentication.")

View File

@@ -49,6 +49,7 @@ import (
"k8s.io/kubernetes/pkg/apis/extensions" "k8s.io/kubernetes/pkg/apis/extensions"
"k8s.io/kubernetes/pkg/apis/metrics" "k8s.io/kubernetes/pkg/apis/metrics"
"k8s.io/kubernetes/pkg/apis/policy" "k8s.io/kubernetes/pkg/apis/policy"
"k8s.io/kubernetes/pkg/apis/rbac"
"k8s.io/kubernetes/pkg/client/restclient" "k8s.io/kubernetes/pkg/client/restclient"
client "k8s.io/kubernetes/pkg/client/unversioned" client "k8s.io/kubernetes/pkg/client/unversioned"
clientset "k8s.io/kubernetes/pkg/client/unversioned/adapters/internalclientset" clientset "k8s.io/kubernetes/pkg/client/unversioned/adapters/internalclientset"
@@ -347,6 +348,8 @@ func NewFactory(optionalClientConfig clientcmd.ClientConfig) *Factory {
return c.ExtensionsClient.RESTClient, nil return c.ExtensionsClient.RESTClient, nil
case federation.GroupName: case federation.GroupName:
return clients.FederationClientForVersion(&mappingVersion) return clients.FederationClientForVersion(&mappingVersion)
case rbac.GroupName:
return c.RbacClient.RESTClient, nil
default: default:
if !registered.IsThirdPartyAPIGroupVersion(gvk.GroupVersion()) { if !registered.IsThirdPartyAPIGroupVersion(gvk.GroupVersion()) {
return nil, fmt.Errorf("unknown api group/version: %s", gvk.String()) return nil, fmt.Errorf("unknown api group/version: %s", gvk.String())
@@ -1017,6 +1020,12 @@ func (c *clientSwaggerSchema) ValidateBytes(data []byte) error {
} }
return getSchemaAndValidate(c.c.BatchClient.RESTClient, data, "apis/", gvk.GroupVersion().String(), c.cacheDir, c) return getSchemaAndValidate(c.c.BatchClient.RESTClient, data, "apis/", gvk.GroupVersion().String(), c.cacheDir, c)
} }
if gvk.Group == rbac.GroupName {
if c.c.RbacClient == nil {
return errors.New("unable to validate: no rbac client")
}
return getSchemaAndValidate(c.c.RbacClient.RESTClient, data, "apis/", gvk.GroupVersion().String(), c.cacheDir, c)
}
if registered.IsThirdPartyAPIGroupVersion(gvk.GroupVersion()) { if registered.IsThirdPartyAPIGroupVersion(gvk.GroupVersion()) {
// Don't attempt to validate third party objects // Don't attempt to validate third party objects
return nil return nil

View File

@@ -29,6 +29,7 @@ import (
_ "k8s.io/kubernetes/pkg/apis/componentconfig/install" _ "k8s.io/kubernetes/pkg/apis/componentconfig/install"
_ "k8s.io/kubernetes/pkg/apis/extensions/install" _ "k8s.io/kubernetes/pkg/apis/extensions/install"
_ "k8s.io/kubernetes/pkg/apis/policy/install" _ "k8s.io/kubernetes/pkg/apis/policy/install"
_ "k8s.io/kubernetes/pkg/apis/rbac/install"
) )
func init() { func init() {

View File

@@ -43,12 +43,21 @@ import (
extensionsapiv1beta1 "k8s.io/kubernetes/pkg/apis/extensions/v1beta1" extensionsapiv1beta1 "k8s.io/kubernetes/pkg/apis/extensions/v1beta1"
"k8s.io/kubernetes/pkg/apis/policy" "k8s.io/kubernetes/pkg/apis/policy"
policyapiv1alpha1 "k8s.io/kubernetes/pkg/apis/policy/v1alpha1" policyapiv1alpha1 "k8s.io/kubernetes/pkg/apis/policy/v1alpha1"
"k8s.io/kubernetes/pkg/apis/rbac"
rbacapi "k8s.io/kubernetes/pkg/apis/rbac/v1alpha1"
rbacvalidation "k8s.io/kubernetes/pkg/apis/rbac/validation"
"k8s.io/kubernetes/pkg/apiserver" "k8s.io/kubernetes/pkg/apiserver"
apiservermetrics "k8s.io/kubernetes/pkg/apiserver/metrics" apiservermetrics "k8s.io/kubernetes/pkg/apiserver/metrics"
"k8s.io/kubernetes/pkg/genericapiserver" "k8s.io/kubernetes/pkg/genericapiserver"
"k8s.io/kubernetes/pkg/healthz" "k8s.io/kubernetes/pkg/healthz"
kubeletclient "k8s.io/kubernetes/pkg/kubelet/client" kubeletclient "k8s.io/kubernetes/pkg/kubelet/client"
"k8s.io/kubernetes/pkg/master/ports" "k8s.io/kubernetes/pkg/master/ports"
"k8s.io/kubernetes/pkg/registry/clusterrole"
clusterroleetcd "k8s.io/kubernetes/pkg/registry/clusterrole/etcd"
clusterrolepolicybased "k8s.io/kubernetes/pkg/registry/clusterrole/policybased"
"k8s.io/kubernetes/pkg/registry/clusterrolebinding"
clusterrolebindingetcd "k8s.io/kubernetes/pkg/registry/clusterrolebinding/etcd"
clusterrolebindingpolicybased "k8s.io/kubernetes/pkg/registry/clusterrolebinding/policybased"
"k8s.io/kubernetes/pkg/registry/componentstatus" "k8s.io/kubernetes/pkg/registry/componentstatus"
configmapetcd "k8s.io/kubernetes/pkg/registry/configmap/etcd" configmapetcd "k8s.io/kubernetes/pkg/registry/configmap/etcd"
controlleretcd "k8s.io/kubernetes/pkg/registry/controller/etcd" controlleretcd "k8s.io/kubernetes/pkg/registry/controller/etcd"
@@ -75,6 +84,12 @@ import (
podtemplateetcd "k8s.io/kubernetes/pkg/registry/podtemplate/etcd" podtemplateetcd "k8s.io/kubernetes/pkg/registry/podtemplate/etcd"
replicasetetcd "k8s.io/kubernetes/pkg/registry/replicaset/etcd" replicasetetcd "k8s.io/kubernetes/pkg/registry/replicaset/etcd"
resourcequotaetcd "k8s.io/kubernetes/pkg/registry/resourcequota/etcd" resourcequotaetcd "k8s.io/kubernetes/pkg/registry/resourcequota/etcd"
"k8s.io/kubernetes/pkg/registry/role"
roleetcd "k8s.io/kubernetes/pkg/registry/role/etcd"
rolepolicybased "k8s.io/kubernetes/pkg/registry/role/policybased"
"k8s.io/kubernetes/pkg/registry/rolebinding"
rolebindingetcd "k8s.io/kubernetes/pkg/registry/rolebinding/etcd"
rolebindingpolicybased "k8s.io/kubernetes/pkg/registry/rolebinding/policybased"
secretetcd "k8s.io/kubernetes/pkg/registry/secret/etcd" secretetcd "k8s.io/kubernetes/pkg/registry/secret/etcd"
"k8s.io/kubernetes/pkg/registry/service" "k8s.io/kubernetes/pkg/registry/service"
etcdallocator "k8s.io/kubernetes/pkg/registry/service/allocator/etcd" etcdallocator "k8s.io/kubernetes/pkg/registry/service/allocator/etcd"
@@ -411,6 +426,39 @@ func (m *Master) InstallAPIs(c *Config) {
allGroups = append(allGroups, group) allGroups = append(allGroups, group)
} }
if c.APIResourceConfigSource.AnyResourcesForVersionEnabled(rbacapi.SchemeGroupVersion) {
rbacResources := m.getRBACResources(c)
rbacGroupMeta := registered.GroupOrDie(rbac.GroupName)
// Hard code preferred group version to rbac/v1alpha1
rbacGroupMeta.GroupVersion = rbacapi.SchemeGroupVersion
apiGroupInfo := genericapiserver.APIGroupInfo{
GroupMeta: *rbacGroupMeta,
VersionedResourcesStorageMap: map[string]map[string]rest.Storage{
"v1alpha1": rbacResources,
},
OptionsExternalVersion: &registered.GroupOrDie(api.GroupName).GroupVersion,
Scheme: api.Scheme,
ParameterCodec: api.ParameterCodec,
NegotiatedSerializer: api.Codecs,
}
apiGroupsInfo = append(apiGroupsInfo, apiGroupInfo)
rbacGVForDiscovery := unversioned.GroupVersionForDiscovery{
GroupVersion: rbacGroupMeta.GroupVersion.String(),
Version: rbacGroupMeta.GroupVersion.Version,
}
group := unversioned.APIGroup{
Name: rbacGroupMeta.GroupVersion.Group,
Versions: []unversioned.GroupVersionForDiscovery{rbacGVForDiscovery},
PreferredVersion: rbacGVForDiscovery,
}
allGroups = append(allGroups, group)
}
if err := m.InstallAPIGroups(apiGroupsInfo); err != nil { if err := m.InstallAPIGroups(apiGroupsInfo); err != nil {
glog.Fatalf("Error in registering group versions: %v", err) glog.Fatalf("Error in registering group versions: %v", err)
} }
@@ -908,6 +956,43 @@ func (m *Master) getAppsResources(c *Config) map[string]rest.Storage {
return storage return storage
} }
func (m *Master) getRBACResources(c *Config) map[string]rest.Storage {
version := rbacapi.SchemeGroupVersion
once := new(sync.Once)
var authorizationRuleResolver rbacvalidation.AuthorizationRuleResolver
newRuleValidator := func() rbacvalidation.AuthorizationRuleResolver {
once.Do(func() {
authorizationRuleResolver = rbacvalidation.NewDefaultRuleResolver(
role.NewRegistry(roleetcd.NewREST(m.GetRESTOptionsOrDie(c, rbac.Resource("roles")))),
rolebinding.NewRegistry(rolebindingetcd.NewREST(m.GetRESTOptionsOrDie(c, rbac.Resource("rolebindings")))),
clusterrole.NewRegistry(clusterroleetcd.NewREST(m.GetRESTOptionsOrDie(c, rbac.Resource("clusterroles")))),
clusterrolebinding.NewRegistry(clusterrolebindingetcd.NewREST(m.GetRESTOptionsOrDie(c, rbac.Resource("clusterrolebindings")))),
)
})
return authorizationRuleResolver
}
storage := map[string]rest.Storage{}
if c.APIResourceConfigSource.ResourceEnabled(version.WithResource("roles")) {
rolesStorage := roleetcd.NewREST(m.GetRESTOptionsOrDie(c, rbac.Resource("roles")))
storage["roles"] = rolepolicybased.NewStorage(rolesStorage, newRuleValidator(), c.AuthorizerRBACSuperUser)
}
if c.APIResourceConfigSource.ResourceEnabled(version.WithResource("rolebindings")) {
roleBindingsStorage := rolebindingetcd.NewREST(m.GetRESTOptionsOrDie(c, rbac.Resource("rolebindings")))
storage["rolebindings"] = rolebindingpolicybased.NewStorage(roleBindingsStorage, newRuleValidator(), c.AuthorizerRBACSuperUser)
}
if c.APIResourceConfigSource.ResourceEnabled(version.WithResource("clusterroles")) {
clusterRolesStorage := clusterroleetcd.NewREST(m.GetRESTOptionsOrDie(c, rbac.Resource("clusterroles")))
storage["clusterroles"] = clusterrolepolicybased.NewStorage(clusterRolesStorage, newRuleValidator(), c.AuthorizerRBACSuperUser)
}
if c.APIResourceConfigSource.ResourceEnabled(version.WithResource("clusterrolebindings")) {
clusterRoleBindingsStorage := clusterrolebindingetcd.NewREST(m.GetRESTOptionsOrDie(c, rbac.Resource("clusterrolebindings")))
storage["clusterrolebindings"] = clusterrolebindingpolicybased.NewStorage(clusterRoleBindingsStorage, newRuleValidator(), c.AuthorizerRBACSuperUser)
}
return storage
}
// findExternalAddress returns ExternalIP of provided node with fallback to LegacyHostIP. // findExternalAddress returns ExternalIP of provided node with fallback to LegacyHostIP.
func findExternalAddress(node *api.Node) (string, error) { func findExternalAddress(node *api.Node) (string, error) {
var fallback string var fallback string
@@ -960,7 +1045,7 @@ func (m *Master) IsTunnelSyncHealthy(req *http.Request) error {
func DefaultAPIResourceConfigSource() *genericapiserver.ResourceConfig { func DefaultAPIResourceConfigSource() *genericapiserver.ResourceConfig {
ret := genericapiserver.NewResourceConfig() ret := genericapiserver.NewResourceConfig()
ret.EnableVersions(apiv1.SchemeGroupVersion, extensionsapiv1beta1.SchemeGroupVersion, batchapiv1.SchemeGroupVersion, autoscalingapiv1.SchemeGroupVersion, appsapi.SchemeGroupVersion, policyapiv1alpha1.SchemeGroupVersion) ret.EnableVersions(apiv1.SchemeGroupVersion, extensionsapiv1beta1.SchemeGroupVersion, batchapiv1.SchemeGroupVersion, autoscalingapiv1.SchemeGroupVersion, appsapi.SchemeGroupVersion, policyapiv1alpha1.SchemeGroupVersion, rbacapi.SchemeGroupVersion)
// all extensions resources except these are disabled by default // all extensions resources except these are disabled by default
ret.EnableResources( ret.EnableResources(

View File

@@ -43,6 +43,7 @@ import (
batchapiv2alpha1 "k8s.io/kubernetes/pkg/apis/batch/v2alpha1" batchapiv2alpha1 "k8s.io/kubernetes/pkg/apis/batch/v2alpha1"
"k8s.io/kubernetes/pkg/apis/extensions" "k8s.io/kubernetes/pkg/apis/extensions"
extensionsapiv1beta1 "k8s.io/kubernetes/pkg/apis/extensions/v1beta1" extensionsapiv1beta1 "k8s.io/kubernetes/pkg/apis/extensions/v1beta1"
"k8s.io/kubernetes/pkg/apis/rbac"
"k8s.io/kubernetes/pkg/apiserver" "k8s.io/kubernetes/pkg/apiserver"
"k8s.io/kubernetes/pkg/genericapiserver" "k8s.io/kubernetes/pkg/genericapiserver"
"k8s.io/kubernetes/pkg/kubelet/client" "k8s.io/kubernetes/pkg/kubelet/client"
@@ -89,6 +90,7 @@ func setUp(t *testing.T) (*Master, *etcdtesting.EtcdTestServer, Config, *assert.
resourceEncoding.SetVersionEncoding(batch.GroupName, *testapi.Batch.GroupVersion(), unversioned.GroupVersion{Group: batch.GroupName, Version: runtime.APIVersionInternal}) resourceEncoding.SetVersionEncoding(batch.GroupName, *testapi.Batch.GroupVersion(), unversioned.GroupVersion{Group: batch.GroupName, Version: runtime.APIVersionInternal})
resourceEncoding.SetVersionEncoding(apps.GroupName, *testapi.Apps.GroupVersion(), unversioned.GroupVersion{Group: apps.GroupName, Version: runtime.APIVersionInternal}) resourceEncoding.SetVersionEncoding(apps.GroupName, *testapi.Apps.GroupVersion(), unversioned.GroupVersion{Group: apps.GroupName, Version: runtime.APIVersionInternal})
resourceEncoding.SetVersionEncoding(extensions.GroupName, *testapi.Extensions.GroupVersion(), unversioned.GroupVersion{Group: extensions.GroupName, Version: runtime.APIVersionInternal}) resourceEncoding.SetVersionEncoding(extensions.GroupName, *testapi.Extensions.GroupVersion(), unversioned.GroupVersion{Group: extensions.GroupName, Version: runtime.APIVersionInternal})
resourceEncoding.SetVersionEncoding(rbac.GroupName, *testapi.Rbac.GroupVersion(), unversioned.GroupVersion{Group: rbac.GroupName, Version: runtime.APIVersionInternal})
storageFactory := genericapiserver.NewDefaultStorageFactory(storageConfig, testapi.StorageMediaType(), api.Codecs, resourceEncoding, DefaultAPIResourceConfigSource()) storageFactory := genericapiserver.NewDefaultStorageFactory(storageConfig, testapi.StorageMediaType(), api.Codecs, resourceEncoding, DefaultAPIResourceConfigSource())
config.StorageFactory = storageFactory config.StorageFactory = storageFactory

View File

@@ -28,6 +28,8 @@ import (
type Resource string type Resource string
const ( const (
ClusterRoles Resource = "clusterroles"
ClusterRoleBindings Resource = "clusterrolebindings"
Controllers Resource = "controllers" Controllers Resource = "controllers"
Daemonsets Resource = "daemonsets" Daemonsets Resource = "daemonsets"
Deployments Resource = "deployments" Deployments Resource = "deployments"
@@ -48,6 +50,8 @@ const (
Replicasets Resource = "replicasets" Replicasets Resource = "replicasets"
ResourceQuotas Resource = "resourcequotas" ResourceQuotas Resource = "resourcequotas"
ScheduledJobs Resource = "scheduledjobs" ScheduledJobs Resource = "scheduledjobs"
Roles Resource = "roles"
RoleBindings Resource = "rolebindings"
Secrets Resource = "secrets" Secrets Resource = "secrets"
ServiceAccounts Resource = "serviceaccounts" ServiceAccounts Resource = "serviceaccounts"
Services Resource = "services" Services Resource = "services"
@@ -57,6 +61,8 @@ var watchCacheSizes map[Resource]int
func init() { func init() {
watchCacheSizes = make(map[Resource]int) watchCacheSizes = make(map[Resource]int)
watchCacheSizes[ClusterRoles] = 100
watchCacheSizes[ClusterRoleBindings] = 100
watchCacheSizes[Controllers] = 100 watchCacheSizes[Controllers] = 100
watchCacheSizes[Daemonsets] = 100 watchCacheSizes[Daemonsets] = 100
watchCacheSizes[Deployments] = 100 watchCacheSizes[Deployments] = 100
@@ -77,6 +83,8 @@ func init() {
watchCacheSizes[Replicasets] = 100 watchCacheSizes[Replicasets] = 100
watchCacheSizes[ResourceQuotas] = 100 watchCacheSizes[ResourceQuotas] = 100
watchCacheSizes[ScheduledJobs] = 100 watchCacheSizes[ScheduledJobs] = 100
watchCacheSizes[Roles] = 100
watchCacheSizes[RoleBindings] = 100
watchCacheSizes[Secrets] = 100 watchCacheSizes[Secrets] = 100
watchCacheSizes[ServiceAccounts] = 100 watchCacheSizes[ServiceAccounts] = 100
watchCacheSizes[Services] = 100 watchCacheSizes[Services] = 100

View File

@@ -0,0 +1,19 @@
/*
Copyright 2016 The Kubernetes Authors 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 certificates provides Registry interface and its RESTStorage
// implementation for storing ClusterRole objects.
package clusterrole

View File

@@ -0,0 +1,76 @@
/*
Copyright 2016 The Kubernetes Authors 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 etcd
import (
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/apis/rbac"
"k8s.io/kubernetes/pkg/fields"
"k8s.io/kubernetes/pkg/labels"
"k8s.io/kubernetes/pkg/registry/cachesize"
"k8s.io/kubernetes/pkg/registry/clusterrole"
"k8s.io/kubernetes/pkg/registry/generic"
"k8s.io/kubernetes/pkg/registry/generic/registry"
"k8s.io/kubernetes/pkg/runtime"
)
// REST implements a RESTStorage for ClusterRole against etcd
type REST struct {
*registry.Store
}
// NewREST returns a RESTStorage object that will work against ClusterRole objects.
func NewREST(opts generic.RESTOptions) *REST {
prefix := "/clusterroles"
newListFunc := func() runtime.Object { return &rbac.ClusterRoleList{} }
storageInterface := opts.Decorator(
opts.Storage,
cachesize.GetWatchCacheSizeByResource(cachesize.ClusterRoles),
&rbac.ClusterRole{},
prefix,
clusterrole.Strategy,
newListFunc,
)
store := &registry.Store{
NewFunc: func() runtime.Object { return &rbac.ClusterRole{} },
NewListFunc: newListFunc,
KeyRootFunc: func(ctx api.Context) string {
return registry.NamespaceKeyRootFunc(ctx, prefix)
},
KeyFunc: func(ctx api.Context, id string) (string, error) {
return registry.NoNamespaceKeyFunc(ctx, prefix, id)
},
ObjectNameFunc: func(obj runtime.Object) (string, error) {
return obj.(*rbac.ClusterRole).Name, nil
},
PredicateFunc: func(label labels.Selector, field fields.Selector) generic.Matcher {
return clusterrole.Matcher(label, field)
},
QualifiedResource: rbac.Resource("clusterroles"),
DeleteCollectionWorkers: opts.DeleteCollectionWorkers,
CreateStrategy: clusterrole.Strategy,
UpdateStrategy: clusterrole.Strategy,
DeleteStrategy: clusterrole.Strategy,
Storage: storageInterface,
}
return &REST{store}
}

View File

@@ -0,0 +1,100 @@
/*
Copyright 2016 The Kubernetes Authors 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 policybased implements a standard storage for ClusterRole that prevents privilege escalation.
package policybased
import (
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/errors"
"k8s.io/kubernetes/pkg/api/rest"
"k8s.io/kubernetes/pkg/apis/rbac"
"k8s.io/kubernetes/pkg/apis/rbac/validation"
"k8s.io/kubernetes/pkg/runtime"
)
var groupResource = rbac.Resource("clusterroles")
type Storage struct {
rest.StandardStorage
ruleResolver validation.AuthorizationRuleResolver
// user which skips privilege escalation checks
superUser string
}
func NewStorage(s rest.StandardStorage, ruleResolver validation.AuthorizationRuleResolver, superUser string) *Storage {
return &Storage{s, ruleResolver, superUser}
}
func (s *Storage) Create(ctx api.Context, obj runtime.Object) (runtime.Object, error) {
if user, ok := api.UserFrom(ctx); ok {
if s.superUser != "" && user.GetName() == s.superUser {
return s.StandardStorage.Create(ctx, obj)
}
}
clusterRole := obj.(*rbac.ClusterRole)
rules := clusterRole.Rules
if err := validation.ConfirmNoEscalation(ctx, s.ruleResolver, rules); err != nil {
return nil, errors.NewForbidden(groupResource, clusterRole.Name, err)
}
return s.StandardStorage.Create(ctx, obj)
}
func (s *Storage) Update(ctx api.Context, name string, obj rest.UpdatedObjectInfo) (runtime.Object, bool, error) {
if user, ok := api.UserFrom(ctx); ok {
if s.superUser != "" && user.GetName() == s.superUser {
return s.StandardStorage.Update(ctx, name, obj)
}
}
nonEscalatingInfo := wrapUpdatedObjectInfo(obj, func(ctx api.Context, obj runtime.Object, oldObj runtime.Object) (runtime.Object, error) {
clusterRole := obj.(*rbac.ClusterRole)
rules := clusterRole.Rules
if err := validation.ConfirmNoEscalation(ctx, s.ruleResolver, rules); err != nil {
return nil, errors.NewForbidden(groupResource, clusterRole.Name, err)
}
return obj, nil
})
return s.StandardStorage.Update(ctx, name, nonEscalatingInfo)
}
// TODO(ericchiang): This logic is copied from #26240. Replace with once that PR is merged into master.
type wrappedUpdatedObjectInfo struct {
objInfo rest.UpdatedObjectInfo
transformFunc rest.TransformFunc
}
func wrapUpdatedObjectInfo(objInfo rest.UpdatedObjectInfo, transformFunc rest.TransformFunc) rest.UpdatedObjectInfo {
return &wrappedUpdatedObjectInfo{objInfo, transformFunc}
}
func (i *wrappedUpdatedObjectInfo) Preconditions() *api.Preconditions {
return i.objInfo.Preconditions()
}
func (i *wrappedUpdatedObjectInfo) UpdatedObject(ctx api.Context, oldObj runtime.Object) (runtime.Object, error) {
obj, err := i.objInfo.UpdatedObject(ctx, oldObj)
if err != nil {
return obj, err
}
return i.transformFunc(ctx, obj, oldObj)
}

View File

@@ -0,0 +1,81 @@
/*
Copyright 2016 The Kubernetes Authors 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 clusterrole
import (
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/rest"
"k8s.io/kubernetes/pkg/apis/rbac"
"k8s.io/kubernetes/pkg/watch"
)
// Registry is an interface for things that know how to store ClusterRoles.
type Registry interface {
ListClusterRoles(ctx api.Context, options *api.ListOptions) (*rbac.ClusterRoleList, error)
CreateClusterRole(ctx api.Context, clusterRole *rbac.ClusterRole) error
UpdateClusterRole(ctx api.Context, clusterRole *rbac.ClusterRole) error
GetClusterRole(ctx api.Context, name string) (*rbac.ClusterRole, error)
DeleteClusterRole(ctx api.Context, name string) error
WatchClusterRoles(ctx api.Context, options *api.ListOptions) (watch.Interface, error)
}
// storage puts strong typing around storage calls
type storage struct {
rest.StandardStorage
}
// NewRegistry returns a new Registry interface for the given Storage. Any mismatched
// types will panic.
func NewRegistry(s rest.StandardStorage) Registry {
return &storage{s}
}
func (s *storage) ListClusterRoles(ctx api.Context, options *api.ListOptions) (*rbac.ClusterRoleList, error) {
obj, err := s.List(ctx, options)
if err != nil {
return nil, err
}
return obj.(*rbac.ClusterRoleList), nil
}
func (s *storage) CreateClusterRole(ctx api.Context, clusterRole *rbac.ClusterRole) error {
_, err := s.Create(ctx, clusterRole)
return err
}
func (s *storage) UpdateClusterRole(ctx api.Context, clusterRole *rbac.ClusterRole) error {
_, _, err := s.Update(ctx, clusterRole.Name, rest.DefaultUpdatedObjectInfo(clusterRole, api.Scheme))
return err
}
func (s *storage) WatchClusterRoles(ctx api.Context, options *api.ListOptions) (watch.Interface, error) {
return s.Watch(ctx, options)
}
func (s *storage) GetClusterRole(ctx api.Context, name string) (*rbac.ClusterRole, error) {
obj, err := s.Get(ctx, name)
if err != nil {
return nil, err
}
return obj.(*rbac.ClusterRole), nil
}
func (s *storage) DeleteClusterRole(ctx api.Context, name string) error {
_, err := s.Delete(ctx, name, nil)
return err
}

View File

@@ -0,0 +1,119 @@
/*
Copyright 2016 The Kubernetes Authors 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 clusterrole
import (
"fmt"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/rest"
"k8s.io/kubernetes/pkg/apis/rbac"
"k8s.io/kubernetes/pkg/apis/rbac/validation"
"k8s.io/kubernetes/pkg/fields"
"k8s.io/kubernetes/pkg/labels"
"k8s.io/kubernetes/pkg/registry/generic"
"k8s.io/kubernetes/pkg/runtime"
"k8s.io/kubernetes/pkg/util/validation/field"
)
// strategy implements behavior for ClusterRoles
type strategy struct {
runtime.ObjectTyper
api.NameGenerator
}
// strategy is the default logic that applies when creating and updating
// ClusterRole objects.
var Strategy = strategy{api.Scheme, api.SimpleNameGenerator}
// Strategy should implement rest.RESTCreateStrategy
var _ rest.RESTCreateStrategy = Strategy
// Strategy should implement rest.RESTUpdateStrategy
var _ rest.RESTUpdateStrategy = Strategy
// NamespaceScoped is true for ClusterRoles.
func (strategy) NamespaceScoped() bool {
return false
}
// AllowCreateOnUpdate is true for ClusterRoles.
func (strategy) AllowCreateOnUpdate() bool {
return true
}
// PrepareForCreate clears fields that are not allowed to be set by end users
// on creation.
func (strategy) PrepareForCreate(obj runtime.Object) {
_ = obj.(*rbac.ClusterRole)
}
// PrepareForUpdate clears fields that are not allowed to be set by end users on update.
func (strategy) PrepareForUpdate(obj, old runtime.Object) {
newClusterRole := obj.(*rbac.ClusterRole)
oldClusterRole := old.(*rbac.ClusterRole)
_, _ = newClusterRole, oldClusterRole
}
// Validate validates a new ClusterRole. Validation must check for a correct signature.
func (strategy) Validate(ctx api.Context, obj runtime.Object) field.ErrorList {
clusterRole := obj.(*rbac.ClusterRole)
return validation.ValidateClusterRole(clusterRole)
}
// Canonicalize normalizes the object after validation.
func (strategy) Canonicalize(obj runtime.Object) {
_ = obj.(*rbac.ClusterRole)
}
// ValidateUpdate is the default update validation for an end user.
func (strategy) ValidateUpdate(ctx api.Context, obj, old runtime.Object) field.ErrorList {
newObj := obj.(*rbac.ClusterRole)
errorList := validation.ValidateClusterRole(newObj)
return append(errorList, validation.ValidateClusterRoleUpdate(newObj, old.(*rbac.ClusterRole))...)
}
// If AllowUnconditionalUpdate() is true and the object specified by
// the user does not have a resource version, then generic Update()
// populates it with the latest version. Else, it checks that the
// version specified by the user matches the version of latest etcd
// object.
func (strategy) AllowUnconditionalUpdate() bool {
return true
}
func (s strategy) Export(obj runtime.Object, exact bool) error {
return nil
}
// Matcher returns a generic matcher for a given label and field selector.
func Matcher(label labels.Selector, field fields.Selector) generic.Matcher {
return generic.MatcherFunc(func(obj runtime.Object) (bool, error) {
sa, ok := obj.(*rbac.ClusterRole)
if !ok {
return false, fmt.Errorf("not a ClusterRole")
}
fields := SelectableFields(sa)
return label.Matches(labels.Set(sa.Labels)) && field.Matches(fields), nil
})
}
// SelectableFields returns a label set that can be used for filter selection
func SelectableFields(obj *rbac.ClusterRole) labels.Set {
return labels.Set{}
}

View File

@@ -0,0 +1,19 @@
/*
Copyright 2016 The Kubernetes Authors 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 certificates provides Registry interface and its RESTStorage
// implementation for storing ClusterRoleBinding objects.
package clusterrolebinding

View File

@@ -0,0 +1,76 @@
/*
Copyright 2016 The Kubernetes Authors 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 etcd
import (
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/apis/rbac"
"k8s.io/kubernetes/pkg/fields"
"k8s.io/kubernetes/pkg/labels"
"k8s.io/kubernetes/pkg/registry/cachesize"
"k8s.io/kubernetes/pkg/registry/clusterrolebinding"
"k8s.io/kubernetes/pkg/registry/generic"
"k8s.io/kubernetes/pkg/registry/generic/registry"
"k8s.io/kubernetes/pkg/runtime"
)
// REST implements a RESTStorage for ClusterRoleBinding against etcd
type REST struct {
*registry.Store
}
// NewREST returns a RESTStorage object that will work against ClusterRoleBinding objects.
func NewREST(opts generic.RESTOptions) *REST {
prefix := "/clusterrolebindings"
newListFunc := func() runtime.Object { return &rbac.ClusterRoleBindingList{} }
storageInterface := opts.Decorator(
opts.Storage,
cachesize.GetWatchCacheSizeByResource(cachesize.ClusterRoleBindings),
&rbac.ClusterRoleBinding{},
prefix,
clusterrolebinding.Strategy,
newListFunc,
)
store := &registry.Store{
NewFunc: func() runtime.Object { return &rbac.ClusterRoleBinding{} },
NewListFunc: newListFunc,
KeyRootFunc: func(ctx api.Context) string {
return registry.NamespaceKeyRootFunc(ctx, prefix)
},
KeyFunc: func(ctx api.Context, id string) (string, error) {
return registry.NoNamespaceKeyFunc(ctx, prefix, id)
},
ObjectNameFunc: func(obj runtime.Object) (string, error) {
return obj.(*rbac.ClusterRoleBinding).Name, nil
},
PredicateFunc: func(label labels.Selector, field fields.Selector) generic.Matcher {
return clusterrolebinding.Matcher(label, field)
},
QualifiedResource: rbac.Resource("clusterrolebindings"),
DeleteCollectionWorkers: opts.DeleteCollectionWorkers,
CreateStrategy: clusterrolebinding.Strategy,
UpdateStrategy: clusterrolebinding.Strategy,
DeleteStrategy: clusterrolebinding.Strategy,
Storage: storageInterface,
}
return &REST{store}
}

View File

@@ -0,0 +1,106 @@
/*
Copyright 2016 The Kubernetes Authors 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 policybased implements a standard storage for ClusterRoleBinding that prevents privilege escalation.
package policybased
import (
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/errors"
"k8s.io/kubernetes/pkg/api/rest"
"k8s.io/kubernetes/pkg/apis/rbac"
"k8s.io/kubernetes/pkg/apis/rbac/validation"
"k8s.io/kubernetes/pkg/runtime"
)
var groupResource = rbac.Resource("clusterrolebindings")
type Storage struct {
rest.StandardStorage
ruleResolver validation.AuthorizationRuleResolver
// user which skips privilege escalation checks
superUser string
}
func NewStorage(s rest.StandardStorage, ruleResolver validation.AuthorizationRuleResolver, superUser string) *Storage {
return &Storage{s, ruleResolver, superUser}
}
func (s *Storage) Create(ctx api.Context, obj runtime.Object) (runtime.Object, error) {
if user, ok := api.UserFrom(ctx); ok {
if s.superUser != "" && user.GetName() == s.superUser {
return s.StandardStorage.Create(ctx, obj)
}
}
clusterRoleBinding := obj.(*rbac.ClusterRoleBinding)
rules, err := s.ruleResolver.GetRoleReferenceRules(ctx, clusterRoleBinding.RoleRef, clusterRoleBinding.Namespace)
if err != nil {
return nil, err
}
if err := validation.ConfirmNoEscalation(ctx, s.ruleResolver, rules); err != nil {
return nil, errors.NewForbidden(groupResource, clusterRoleBinding.Name, err)
}
return s.StandardStorage.Create(ctx, obj)
}
func (s *Storage) Update(ctx api.Context, name string, obj rest.UpdatedObjectInfo) (runtime.Object, bool, error) {
if user, ok := api.UserFrom(ctx); ok {
if s.superUser != "" && user.GetName() == s.superUser {
return s.StandardStorage.Update(ctx, name, obj)
}
}
nonEscalatingInfo := wrapUpdatedObjectInfo(obj, func(ctx api.Context, obj runtime.Object, oldObj runtime.Object) (runtime.Object, error) {
clusterRoleBinding := obj.(*rbac.ClusterRoleBinding)
rules, err := s.ruleResolver.GetRoleReferenceRules(ctx, clusterRoleBinding.RoleRef, clusterRoleBinding.Namespace)
if err != nil {
return nil, err
}
if err := validation.ConfirmNoEscalation(ctx, s.ruleResolver, rules); err != nil {
return nil, errors.NewForbidden(groupResource, clusterRoleBinding.Name, err)
}
return obj, nil
})
return s.StandardStorage.Update(ctx, name, nonEscalatingInfo)
}
// TODO(ericchiang): This logic is copied from #26240. Replace with once that PR is merged into master.
type wrappedUpdatedObjectInfo struct {
objInfo rest.UpdatedObjectInfo
transformFunc rest.TransformFunc
}
func wrapUpdatedObjectInfo(objInfo rest.UpdatedObjectInfo, transformFunc rest.TransformFunc) rest.UpdatedObjectInfo {
return &wrappedUpdatedObjectInfo{objInfo, transformFunc}
}
func (i *wrappedUpdatedObjectInfo) Preconditions() *api.Preconditions {
return i.objInfo.Preconditions()
}
func (i *wrappedUpdatedObjectInfo) UpdatedObject(ctx api.Context, oldObj runtime.Object) (runtime.Object, error) {
obj, err := i.objInfo.UpdatedObject(ctx, oldObj)
if err != nil {
return obj, err
}
return i.transformFunc(ctx, obj, oldObj)
}

View File

@@ -0,0 +1,81 @@
/*
Copyright 2016 The Kubernetes Authors 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 clusterrolebinding
import (
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/rest"
"k8s.io/kubernetes/pkg/apis/rbac"
"k8s.io/kubernetes/pkg/watch"
)
// Registry is an interface for things that know how to store ClusterRoleBindings.
type Registry interface {
ListClusterRoleBindings(ctx api.Context, options *api.ListOptions) (*rbac.ClusterRoleBindingList, error)
CreateClusterRoleBinding(ctx api.Context, clusterRoleBinding *rbac.ClusterRoleBinding) error
UpdateClusterRoleBinding(ctx api.Context, clusterRoleBinding *rbac.ClusterRoleBinding) error
GetClusterRoleBinding(ctx api.Context, name string) (*rbac.ClusterRoleBinding, error)
DeleteClusterRoleBinding(ctx api.Context, name string) error
WatchClusterRoleBindings(ctx api.Context, options *api.ListOptions) (watch.Interface, error)
}
// storage puts strong typing around storage calls
type storage struct {
rest.StandardStorage
}
// NewRegistry returns a new Registry interface for the given Storage. Any mismatched
// types will panic.
func NewRegistry(s rest.StandardStorage) Registry {
return &storage{s}
}
func (s *storage) ListClusterRoleBindings(ctx api.Context, options *api.ListOptions) (*rbac.ClusterRoleBindingList, error) {
obj, err := s.List(ctx, options)
if err != nil {
return nil, err
}
return obj.(*rbac.ClusterRoleBindingList), nil
}
func (s *storage) CreateClusterRoleBinding(ctx api.Context, clusterRoleBinding *rbac.ClusterRoleBinding) error {
_, err := s.Create(ctx, clusterRoleBinding)
return err
}
func (s *storage) UpdateClusterRoleBinding(ctx api.Context, clusterRoleBinding *rbac.ClusterRoleBinding) error {
_, _, err := s.Update(ctx, clusterRoleBinding.Name, rest.DefaultUpdatedObjectInfo(clusterRoleBinding, api.Scheme))
return err
}
func (s *storage) WatchClusterRoleBindings(ctx api.Context, options *api.ListOptions) (watch.Interface, error) {
return s.Watch(ctx, options)
}
func (s *storage) GetClusterRoleBinding(ctx api.Context, name string) (*rbac.ClusterRoleBinding, error) {
obj, err := s.Get(ctx, name)
if err != nil {
return nil, err
}
return obj.(*rbac.ClusterRoleBinding), nil
}
func (s *storage) DeleteClusterRoleBinding(ctx api.Context, name string) error {
_, err := s.Delete(ctx, name, nil)
return err
}

View File

@@ -0,0 +1,119 @@
/*
Copyright 2016 The Kubernetes Authors 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 clusterrolebinding
import (
"fmt"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/rest"
"k8s.io/kubernetes/pkg/apis/rbac"
"k8s.io/kubernetes/pkg/apis/rbac/validation"
"k8s.io/kubernetes/pkg/fields"
"k8s.io/kubernetes/pkg/labels"
"k8s.io/kubernetes/pkg/registry/generic"
"k8s.io/kubernetes/pkg/runtime"
"k8s.io/kubernetes/pkg/util/validation/field"
)
// strategy implements behavior for ClusterRoleBindings
type strategy struct {
runtime.ObjectTyper
api.NameGenerator
}
// strategy is the default logic that applies when creating and updating
// ClusterRoleBinding objects.
var Strategy = strategy{api.Scheme, api.SimpleNameGenerator}
// Strategy should implement rest.RESTCreateStrategy
var _ rest.RESTCreateStrategy = Strategy
// Strategy should implement rest.RESTUpdateStrategy
var _ rest.RESTUpdateStrategy = Strategy
// NamespaceScoped is true for ClusterRoleBindings.
func (strategy) NamespaceScoped() bool {
return false
}
// AllowCreateOnUpdate is true for ClusterRoleBindings.
func (strategy) AllowCreateOnUpdate() bool {
return true
}
// PrepareForCreate clears fields that are not allowed to be set by end users
// on creation.
func (strategy) PrepareForCreate(obj runtime.Object) {
_ = obj.(*rbac.ClusterRoleBinding)
}
// PrepareForUpdate clears fields that are not allowed to be set by end users on update.
func (strategy) PrepareForUpdate(obj, old runtime.Object) {
newClusterRoleBinding := obj.(*rbac.ClusterRoleBinding)
oldClusterRoleBinding := old.(*rbac.ClusterRoleBinding)
_, _ = newClusterRoleBinding, oldClusterRoleBinding
}
// Validate validates a new ClusterRoleBinding. Validation must check for a correct signature.
func (strategy) Validate(ctx api.Context, obj runtime.Object) field.ErrorList {
clusterRoleBinding := obj.(*rbac.ClusterRoleBinding)
return validation.ValidateClusterRoleBinding(clusterRoleBinding)
}
// Canonicalize normalizes the object after validation.
func (strategy) Canonicalize(obj runtime.Object) {
_ = obj.(*rbac.ClusterRoleBinding)
}
// ValidateUpdate is the default update validation for an end user.
func (strategy) ValidateUpdate(ctx api.Context, obj, old runtime.Object) field.ErrorList {
newObj := obj.(*rbac.ClusterRoleBinding)
errorList := validation.ValidateClusterRoleBinding(newObj)
return append(errorList, validation.ValidateClusterRoleBindingUpdate(newObj, old.(*rbac.ClusterRoleBinding))...)
}
// If AllowUnconditionalUpdate() is true and the object specified by
// the user does not have a resource version, then generic Update()
// populates it with the latest version. Else, it checks that the
// version specified by the user matches the version of latest etcd
// object.
func (strategy) AllowUnconditionalUpdate() bool {
return true
}
func (s strategy) Export(obj runtime.Object, exact bool) error {
return nil
}
// Matcher returns a generic matcher for a given label and field selector.
func Matcher(label labels.Selector, field fields.Selector) generic.Matcher {
return generic.MatcherFunc(func(obj runtime.Object) (bool, error) {
sa, ok := obj.(*rbac.ClusterRoleBinding)
if !ok {
return false, fmt.Errorf("not a ClusterRoleBinding")
}
fields := SelectableFields(sa)
return label.Matches(labels.Set(sa.Labels)) && field.Matches(fields), nil
})
}
// SelectableFields returns a label set that can be used for filter selection
func SelectableFields(obj *rbac.ClusterRoleBinding) labels.Set {
return labels.Set{}
}

19
pkg/registry/role/doc.go Normal file
View File

@@ -0,0 +1,19 @@
/*
Copyright 2016 The Kubernetes Authors 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 certificates provides Registry interface and its RESTStorage
// implementation for storing Role objects.
package role

View File

@@ -0,0 +1,76 @@
/*
Copyright 2016 The Kubernetes Authors 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 etcd
import (
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/apis/rbac"
"k8s.io/kubernetes/pkg/fields"
"k8s.io/kubernetes/pkg/labels"
"k8s.io/kubernetes/pkg/registry/cachesize"
"k8s.io/kubernetes/pkg/registry/generic"
"k8s.io/kubernetes/pkg/registry/generic/registry"
"k8s.io/kubernetes/pkg/registry/role"
"k8s.io/kubernetes/pkg/runtime"
)
// REST implements a RESTStorage for Role against etcd
type REST struct {
*registry.Store
}
// NewREST returns a RESTStorage object that will work against Role objects.
func NewREST(opts generic.RESTOptions) *REST {
prefix := "/roles"
newListFunc := func() runtime.Object { return &rbac.RoleList{} }
storageInterface := opts.Decorator(
opts.Storage,
cachesize.GetWatchCacheSizeByResource(cachesize.Roles),
&rbac.Role{},
prefix,
role.Strategy,
newListFunc,
)
store := &registry.Store{
NewFunc: func() runtime.Object { return &rbac.Role{} },
NewListFunc: newListFunc,
KeyRootFunc: func(ctx api.Context) string {
return registry.NamespaceKeyRootFunc(ctx, prefix)
},
KeyFunc: func(ctx api.Context, id string) (string, error) {
return registry.NamespaceKeyFunc(ctx, prefix, id)
},
ObjectNameFunc: func(obj runtime.Object) (string, error) {
return obj.(*rbac.Role).Name, nil
},
PredicateFunc: func(label labels.Selector, field fields.Selector) generic.Matcher {
return role.Matcher(label, field)
},
QualifiedResource: rbac.Resource("roles"),
DeleteCollectionWorkers: opts.DeleteCollectionWorkers,
CreateStrategy: role.Strategy,
UpdateStrategy: role.Strategy,
DeleteStrategy: role.Strategy,
Storage: storageInterface,
}
return &REST{store}
}

View File

@@ -0,0 +1,100 @@
/*
Copyright 2016 The Kubernetes Authors 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 policybased implements a standard storage for Role that prevents privilege escalation.
package policybased
import (
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/errors"
"k8s.io/kubernetes/pkg/api/rest"
"k8s.io/kubernetes/pkg/apis/rbac"
"k8s.io/kubernetes/pkg/apis/rbac/validation"
"k8s.io/kubernetes/pkg/runtime"
)
var groupResource = rbac.Resource("roles")
type Storage struct {
rest.StandardStorage
ruleResolver validation.AuthorizationRuleResolver
// user which skips privilege escalation checks
superUser string
}
func NewStorage(s rest.StandardStorage, ruleResolver validation.AuthorizationRuleResolver, superUser string) *Storage {
return &Storage{s, ruleResolver, superUser}
}
func (s *Storage) Create(ctx api.Context, obj runtime.Object) (runtime.Object, error) {
if user, ok := api.UserFrom(ctx); ok {
if s.superUser != "" && user.GetName() == s.superUser {
return s.StandardStorage.Create(ctx, obj)
}
}
role := obj.(*rbac.Role)
rules := role.Rules
if err := validation.ConfirmNoEscalation(ctx, s.ruleResolver, rules); err != nil {
return nil, errors.NewForbidden(groupResource, role.Name, err)
}
return s.StandardStorage.Create(ctx, obj)
}
func (s *Storage) Update(ctx api.Context, name string, obj rest.UpdatedObjectInfo) (runtime.Object, bool, error) {
if user, ok := api.UserFrom(ctx); ok {
if s.superUser != "" && user.GetName() == s.superUser {
return s.StandardStorage.Update(ctx, name, obj)
}
}
nonEscalatingInfo := wrapUpdatedObjectInfo(obj, func(ctx api.Context, obj runtime.Object, oldObj runtime.Object) (runtime.Object, error) {
role := obj.(*rbac.Role)
rules := role.Rules
if err := validation.ConfirmNoEscalation(ctx, s.ruleResolver, rules); err != nil {
return nil, errors.NewForbidden(groupResource, role.Name, err)
}
return obj, nil
})
return s.StandardStorage.Update(ctx, name, nonEscalatingInfo)
}
// TODO(ericchiang): This logic is copied from #26240. Replace with once that PR is merged into master.
type wrappedUpdatedObjectInfo struct {
objInfo rest.UpdatedObjectInfo
transformFunc rest.TransformFunc
}
func wrapUpdatedObjectInfo(objInfo rest.UpdatedObjectInfo, transformFunc rest.TransformFunc) rest.UpdatedObjectInfo {
return &wrappedUpdatedObjectInfo{objInfo, transformFunc}
}
func (i *wrappedUpdatedObjectInfo) Preconditions() *api.Preconditions {
return i.objInfo.Preconditions()
}
func (i *wrappedUpdatedObjectInfo) UpdatedObject(ctx api.Context, oldObj runtime.Object) (runtime.Object, error) {
obj, err := i.objInfo.UpdatedObject(ctx, oldObj)
if err != nil {
return obj, err
}
return i.transformFunc(ctx, obj, oldObj)
}

View File

@@ -0,0 +1,81 @@
/*
Copyright 2016 The Kubernetes Authors 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 role
import (
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/rest"
"k8s.io/kubernetes/pkg/apis/rbac"
"k8s.io/kubernetes/pkg/watch"
)
// Registry is an interface for things that know how to store Roles.
type Registry interface {
ListRoles(ctx api.Context, options *api.ListOptions) (*rbac.RoleList, error)
CreateRole(ctx api.Context, role *rbac.Role) error
UpdateRole(ctx api.Context, role *rbac.Role) error
GetRole(ctx api.Context, name string) (*rbac.Role, error)
DeleteRole(ctx api.Context, name string) error
WatchRoles(ctx api.Context, options *api.ListOptions) (watch.Interface, error)
}
// storage puts strong typing around storage calls
type storage struct {
rest.StandardStorage
}
// NewRegistry returns a new Registry interface for the given Storage. Any mismatched
// types will panic.
func NewRegistry(s rest.StandardStorage) Registry {
return &storage{s}
}
func (s *storage) ListRoles(ctx api.Context, options *api.ListOptions) (*rbac.RoleList, error) {
obj, err := s.List(ctx, options)
if err != nil {
return nil, err
}
return obj.(*rbac.RoleList), nil
}
func (s *storage) CreateRole(ctx api.Context, role *rbac.Role) error {
_, err := s.Create(ctx, role)
return err
}
func (s *storage) UpdateRole(ctx api.Context, role *rbac.Role) error {
_, _, err := s.Update(ctx, role.Name, rest.DefaultUpdatedObjectInfo(role, api.Scheme))
return err
}
func (s *storage) WatchRoles(ctx api.Context, options *api.ListOptions) (watch.Interface, error) {
return s.Watch(ctx, options)
}
func (s *storage) GetRole(ctx api.Context, name string) (*rbac.Role, error) {
obj, err := s.Get(ctx, name)
if err != nil {
return nil, err
}
return obj.(*rbac.Role), nil
}
func (s *storage) DeleteRole(ctx api.Context, name string) error {
_, err := s.Delete(ctx, name, nil)
return err
}

View File

@@ -0,0 +1,119 @@
/*
Copyright 2016 The Kubernetes Authors 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 role
import (
"fmt"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/rest"
"k8s.io/kubernetes/pkg/apis/rbac"
"k8s.io/kubernetes/pkg/apis/rbac/validation"
"k8s.io/kubernetes/pkg/fields"
"k8s.io/kubernetes/pkg/labels"
"k8s.io/kubernetes/pkg/registry/generic"
"k8s.io/kubernetes/pkg/runtime"
"k8s.io/kubernetes/pkg/util/validation/field"
)
// strategy implements behavior for Roles
type strategy struct {
runtime.ObjectTyper
api.NameGenerator
}
// strategy is the default logic that applies when creating and updating
// Role objects.
var Strategy = strategy{api.Scheme, api.SimpleNameGenerator}
// Strategy should implement rest.RESTCreateStrategy
var _ rest.RESTCreateStrategy = Strategy
// Strategy should implement rest.RESTUpdateStrategy
var _ rest.RESTUpdateStrategy = Strategy
// NamespaceScoped is true for Roles.
func (strategy) NamespaceScoped() bool {
return true
}
// AllowCreateOnUpdate is true for Roles.
func (strategy) AllowCreateOnUpdate() bool {
return true
}
// PrepareForCreate clears fields that are not allowed to be set by end users
// on creation.
func (strategy) PrepareForCreate(obj runtime.Object) {
_ = obj.(*rbac.Role)
}
// PrepareForUpdate clears fields that are not allowed to be set by end users on update.
func (strategy) PrepareForUpdate(obj, old runtime.Object) {
newRole := obj.(*rbac.Role)
oldRole := old.(*rbac.Role)
_, _ = newRole, oldRole
}
// Validate validates a new Role. Validation must check for a correct signature.
func (strategy) Validate(ctx api.Context, obj runtime.Object) field.ErrorList {
role := obj.(*rbac.Role)
return validation.ValidateRole(role)
}
// Canonicalize normalizes the object after validation.
func (strategy) Canonicalize(obj runtime.Object) {
_ = obj.(*rbac.Role)
}
// ValidateUpdate is the default update validation for an end user.
func (strategy) ValidateUpdate(ctx api.Context, obj, old runtime.Object) field.ErrorList {
newObj := obj.(*rbac.Role)
errorList := validation.ValidateRole(newObj)
return append(errorList, validation.ValidateRoleUpdate(newObj, old.(*rbac.Role))...)
}
// If AllowUnconditionalUpdate() is true and the object specified by
// the user does not have a resource version, then generic Update()
// populates it with the latest version. Else, it checks that the
// version specified by the user matches the version of latest etcd
// object.
func (strategy) AllowUnconditionalUpdate() bool {
return true
}
func (s strategy) Export(obj runtime.Object, exact bool) error {
return nil
}
// Matcher returns a generic matcher for a given label and field selector.
func Matcher(label labels.Selector, field fields.Selector) generic.Matcher {
return generic.MatcherFunc(func(obj runtime.Object) (bool, error) {
sa, ok := obj.(*rbac.Role)
if !ok {
return false, fmt.Errorf("not a Role")
}
fields := SelectableFields(sa)
return label.Matches(labels.Set(sa.Labels)) && field.Matches(fields), nil
})
}
// SelectableFields returns a label set that can be used for filter selection
func SelectableFields(obj *rbac.Role) labels.Set {
return labels.Set{}
}

View File

@@ -0,0 +1,19 @@
/*
Copyright 2016 The Kubernetes Authors 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 certificates provides Registry interface and its RESTStorage
// implementation for storing RoleBinding objects.
package rolebinding

View File

@@ -0,0 +1,76 @@
/*
Copyright 2016 The Kubernetes Authors 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 etcd
import (
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/apis/rbac"
"k8s.io/kubernetes/pkg/fields"
"k8s.io/kubernetes/pkg/labels"
"k8s.io/kubernetes/pkg/registry/cachesize"
"k8s.io/kubernetes/pkg/registry/generic"
"k8s.io/kubernetes/pkg/registry/generic/registry"
"k8s.io/kubernetes/pkg/registry/rolebinding"
"k8s.io/kubernetes/pkg/runtime"
)
// REST implements a RESTStorage for RoleBinding against etcd
type REST struct {
*registry.Store
}
// NewREST returns a RESTStorage object that will work against RoleBinding objects.
func NewREST(opts generic.RESTOptions) *REST {
prefix := "/rolebindings"
newListFunc := func() runtime.Object { return &rbac.RoleBindingList{} }
storageInterface := opts.Decorator(
opts.Storage,
cachesize.GetWatchCacheSizeByResource(cachesize.RoleBindings),
&rbac.RoleBinding{},
prefix,
rolebinding.Strategy,
newListFunc,
)
store := &registry.Store{
NewFunc: func() runtime.Object { return &rbac.RoleBinding{} },
NewListFunc: newListFunc,
KeyRootFunc: func(ctx api.Context) string {
return registry.NamespaceKeyRootFunc(ctx, prefix)
},
KeyFunc: func(ctx api.Context, id string) (string, error) {
return registry.NamespaceKeyFunc(ctx, prefix, id)
},
ObjectNameFunc: func(obj runtime.Object) (string, error) {
return obj.(*rbac.RoleBinding).Name, nil
},
PredicateFunc: func(label labels.Selector, field fields.Selector) generic.Matcher {
return rolebinding.Matcher(label, field)
},
QualifiedResource: rbac.Resource("rolebindings"),
DeleteCollectionWorkers: opts.DeleteCollectionWorkers,
CreateStrategy: rolebinding.Strategy,
UpdateStrategy: rolebinding.Strategy,
DeleteStrategy: rolebinding.Strategy,
Storage: storageInterface,
}
return &REST{store}
}

View File

@@ -0,0 +1,106 @@
/*
Copyright 2016 The Kubernetes Authors 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 policybased implements a standard storage for RoleBinding that prevents privilege escalation.
package policybased
import (
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/errors"
"k8s.io/kubernetes/pkg/api/rest"
"k8s.io/kubernetes/pkg/apis/rbac"
"k8s.io/kubernetes/pkg/apis/rbac/validation"
"k8s.io/kubernetes/pkg/runtime"
)
var groupResource = rbac.Resource("rolebindings")
type Storage struct {
rest.StandardStorage
ruleResolver validation.AuthorizationRuleResolver
// user which skips privilege escalation checks
superUser string
}
func NewStorage(s rest.StandardStorage, ruleResolver validation.AuthorizationRuleResolver, superUser string) *Storage {
return &Storage{s, ruleResolver, superUser}
}
func (s *Storage) Create(ctx api.Context, obj runtime.Object) (runtime.Object, error) {
if user, ok := api.UserFrom(ctx); ok {
if s.superUser != "" && user.GetName() == s.superUser {
return s.StandardStorage.Create(ctx, obj)
}
}
roleBinding := obj.(*rbac.RoleBinding)
rules, err := s.ruleResolver.GetRoleReferenceRules(ctx, roleBinding.RoleRef, roleBinding.Namespace)
if err != nil {
return nil, err
}
if err := validation.ConfirmNoEscalation(ctx, s.ruleResolver, rules); err != nil {
return nil, errors.NewForbidden(groupResource, roleBinding.Name, err)
}
return s.StandardStorage.Create(ctx, obj)
}
func (s *Storage) Update(ctx api.Context, name string, obj rest.UpdatedObjectInfo) (runtime.Object, bool, error) {
if user, ok := api.UserFrom(ctx); ok {
if s.superUser != "" && user.GetName() == s.superUser {
return s.StandardStorage.Update(ctx, name, obj)
}
}
nonEscalatingInfo := wrapUpdatedObjectInfo(obj, func(ctx api.Context, obj runtime.Object, oldObj runtime.Object) (runtime.Object, error) {
roleBinding := obj.(*rbac.RoleBinding)
rules, err := s.ruleResolver.GetRoleReferenceRules(ctx, roleBinding.RoleRef, roleBinding.Namespace)
if err != nil {
return nil, err
}
if err := validation.ConfirmNoEscalation(ctx, s.ruleResolver, rules); err != nil {
return nil, errors.NewForbidden(groupResource, roleBinding.Name, err)
}
return obj, nil
})
return s.StandardStorage.Update(ctx, name, nonEscalatingInfo)
}
// TODO(ericchiang): This logic is copied from #26240. Replace with once that PR is merged into master.
type wrappedUpdatedObjectInfo struct {
objInfo rest.UpdatedObjectInfo
transformFunc rest.TransformFunc
}
func wrapUpdatedObjectInfo(objInfo rest.UpdatedObjectInfo, transformFunc rest.TransformFunc) rest.UpdatedObjectInfo {
return &wrappedUpdatedObjectInfo{objInfo, transformFunc}
}
func (i *wrappedUpdatedObjectInfo) Preconditions() *api.Preconditions {
return i.objInfo.Preconditions()
}
func (i *wrappedUpdatedObjectInfo) UpdatedObject(ctx api.Context, oldObj runtime.Object) (runtime.Object, error) {
obj, err := i.objInfo.UpdatedObject(ctx, oldObj)
if err != nil {
return obj, err
}
return i.transformFunc(ctx, obj, oldObj)
}

View File

@@ -0,0 +1,82 @@
/*
Copyright 2016 The Kubernetes Authors 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 rolebinding
import (
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/rest"
"k8s.io/kubernetes/pkg/apis/rbac"
"k8s.io/kubernetes/pkg/watch"
)
// Registry is an interface for things that know how to store RoleBindings.
type Registry interface {
ListRoleBindings(ctx api.Context, options *api.ListOptions) (*rbac.RoleBindingList, error)
CreateRoleBinding(ctx api.Context, roleBinding *rbac.RoleBinding) error
UpdateRoleBinding(ctx api.Context, roleBinding *rbac.RoleBinding) error
GetRoleBinding(ctx api.Context, name string) (*rbac.RoleBinding, error)
DeleteRoleBinding(ctx api.Context, name string) error
WatchRoleBindings(ctx api.Context, options *api.ListOptions) (watch.Interface, error)
}
// storage puts strong typing around storage calls
type storage struct {
rest.StandardStorage
}
// NewRegistry returns a new Registry interface for the given Storage. Any mismatched
// types will panic.
func NewRegistry(s rest.StandardStorage) Registry {
return &storage{s}
}
func (s *storage) ListRoleBindings(ctx api.Context, options *api.ListOptions) (*rbac.RoleBindingList, error) {
obj, err := s.List(ctx, options)
if err != nil {
return nil, err
}
return obj.(*rbac.RoleBindingList), nil
}
func (s *storage) CreateRoleBinding(ctx api.Context, roleBinding *rbac.RoleBinding) error {
// TODO(ericchiang): add additional validation
_, err := s.Create(ctx, roleBinding)
return err
}
func (s *storage) UpdateRoleBinding(ctx api.Context, roleBinding *rbac.RoleBinding) error {
_, _, err := s.Update(ctx, roleBinding.Name, rest.DefaultUpdatedObjectInfo(roleBinding, api.Scheme))
return err
}
func (s *storage) WatchRoleBindings(ctx api.Context, options *api.ListOptions) (watch.Interface, error) {
return s.Watch(ctx, options)
}
func (s *storage) GetRoleBinding(ctx api.Context, name string) (*rbac.RoleBinding, error) {
obj, err := s.Get(ctx, name)
if err != nil {
return nil, err
}
return obj.(*rbac.RoleBinding), nil
}
func (s *storage) DeleteRoleBinding(ctx api.Context, name string) error {
_, err := s.Delete(ctx, name, nil)
return err
}

View File

@@ -0,0 +1,119 @@
/*
Copyright 2016 The Kubernetes Authors 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 rolebinding
import (
"fmt"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/rest"
"k8s.io/kubernetes/pkg/apis/rbac"
"k8s.io/kubernetes/pkg/apis/rbac/validation"
"k8s.io/kubernetes/pkg/fields"
"k8s.io/kubernetes/pkg/labels"
"k8s.io/kubernetes/pkg/registry/generic"
"k8s.io/kubernetes/pkg/runtime"
"k8s.io/kubernetes/pkg/util/validation/field"
)
// strategy implements behavior for RoleBindings
type strategy struct {
runtime.ObjectTyper
api.NameGenerator
}
// strategy is the default logic that applies when creating and updating
// RoleBinding objects.
var Strategy = strategy{api.Scheme, api.SimpleNameGenerator}
// Strategy should implement rest.RESTCreateStrategy
var _ rest.RESTCreateStrategy = Strategy
// Strategy should implement rest.RESTUpdateStrategy
var _ rest.RESTUpdateStrategy = Strategy
// NamespaceScoped is true for RoleBindings.
func (strategy) NamespaceScoped() bool {
return true
}
// AllowCreateOnUpdate is true for RoleBindings.
func (strategy) AllowCreateOnUpdate() bool {
return true
}
// PrepareForCreate clears fields that are not allowed to be set by end users
// on creation.
func (strategy) PrepareForCreate(obj runtime.Object) {
_ = obj.(*rbac.RoleBinding)
}
// PrepareForUpdate clears fields that are not allowed to be set by end users on update.
func (strategy) PrepareForUpdate(obj, old runtime.Object) {
newRoleBinding := obj.(*rbac.RoleBinding)
oldRoleBinding := old.(*rbac.RoleBinding)
_, _ = newRoleBinding, oldRoleBinding
}
// Validate validates a new RoleBinding. Validation must check for a correct signature.
func (strategy) Validate(ctx api.Context, obj runtime.Object) field.ErrorList {
roleBinding := obj.(*rbac.RoleBinding)
return validation.ValidateRoleBinding(roleBinding)
}
// Canonicalize normalizes the object after validation.
func (strategy) Canonicalize(obj runtime.Object) {
_ = obj.(*rbac.RoleBinding)
}
// ValidateUpdate is the default update validation for an end user.
func (strategy) ValidateUpdate(ctx api.Context, obj, old runtime.Object) field.ErrorList {
newObj := obj.(*rbac.RoleBinding)
errorList := validation.ValidateRoleBinding(newObj)
return append(errorList, validation.ValidateRoleBindingUpdate(newObj, old.(*rbac.RoleBinding))...)
}
// If AllowUnconditionalUpdate() is true and the object specified by
// the user does not have a resource version, then generic Update()
// populates it with the latest version. Else, it checks that the
// version specified by the user matches the version of latest etcd
// object.
func (strategy) AllowUnconditionalUpdate() bool {
return true
}
func (s strategy) Export(obj runtime.Object, exact bool) error {
return nil
}
// Matcher returns a generic matcher for a given label and field selector.
func Matcher(label labels.Selector, field fields.Selector) generic.Matcher {
return generic.MatcherFunc(func(obj runtime.Object) (bool, error) {
sa, ok := obj.(*rbac.RoleBinding)
if !ok {
return false, fmt.Errorf("not a RoleBinding")
}
fields := SelectableFields(sa)
return label.Matches(labels.Set(sa.Labels)) && field.Matches(fields), nil
})
}
// SelectableFields returns a label set that can be used for filter selection
func SelectableFields(obj *rbac.RoleBinding) labels.Set {
return labels.Set{}
}

View File

@@ -0,0 +1,79 @@
/*
Copyright 2016 The Kubernetes Authors 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 rbac implements the authorizer.Authorizer interface using roles base access control.
package rbac
import (
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/apis/rbac"
"k8s.io/kubernetes/pkg/apis/rbac/validation"
"k8s.io/kubernetes/pkg/auth/authorizer"
"k8s.io/kubernetes/pkg/auth/user"
"k8s.io/kubernetes/pkg/registry/clusterrole"
"k8s.io/kubernetes/pkg/registry/clusterrolebinding"
"k8s.io/kubernetes/pkg/registry/role"
"k8s.io/kubernetes/pkg/registry/rolebinding"
)
type RBACAuthorizer struct {
superUser string
authorizationRuleResolver validation.AuthorizationRuleResolver
}
func (r *RBACAuthorizer) Authorize(attr authorizer.Attributes) error {
if r.superUser != "" && attr.GetUserName() == r.superUser {
return nil
}
userInfo := &user.DefaultInfo{
Name: attr.GetUserName(),
Groups: attr.GetGroups(),
}
ctx := api.WithNamespace(api.WithUser(api.NewContext(), userInfo), attr.GetNamespace())
// Frame the authorization request as a privilege escalation check.
var requestedRule rbac.PolicyRule
if attr.IsResourceRequest() {
requestedRule = rbac.PolicyRule{
Verbs: []string{attr.GetVerb()},
APIGroups: []string{attr.GetAPIGroup()}, // TODO(ericchiang): add api version here too?
Resources: []string{attr.GetResource()},
ResourceNames: []string{attr.GetName()},
}
} else {
requestedRule = rbac.PolicyRule{
NonResourceURLs: []string{attr.GetPath()},
}
}
return validation.ConfirmNoEscalation(ctx, r.authorizationRuleResolver, []rbac.PolicyRule{requestedRule})
}
func New(roleRegistry role.Registry, roleBindingRegistry rolebinding.Registry, clusterRoleRegistry clusterrole.Registry, clusterRoleBindingRegistry clusterrolebinding.Registry, superUser string) (*RBACAuthorizer, error) {
authorizer := &RBACAuthorizer{
superUser: superUser,
authorizationRuleResolver: validation.NewDefaultRuleResolver(
roleRegistry,
roleBindingRegistry,
clusterRoleRegistry,
clusterRoleBindingRegistry,
),
}
return authorizer, nil
}

View File

@@ -0,0 +1,59 @@
/*
Copyright 2016 The Kubernetes Authors 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 rbac
import (
"testing"
"k8s.io/kubernetes/pkg/api/testapi"
"k8s.io/kubernetes/pkg/apis/rbac"
"k8s.io/kubernetes/pkg/registry/clusterrole"
clusterroleetcd "k8s.io/kubernetes/pkg/registry/clusterrole/etcd"
"k8s.io/kubernetes/pkg/registry/clusterrolebinding"
clusterrolebindingetcd "k8s.io/kubernetes/pkg/registry/clusterrolebinding/etcd"
"k8s.io/kubernetes/pkg/registry/generic"
"k8s.io/kubernetes/pkg/registry/role"
roleetcd "k8s.io/kubernetes/pkg/registry/role/etcd"
"k8s.io/kubernetes/pkg/registry/rolebinding"
rolebindingetcd "k8s.io/kubernetes/pkg/registry/rolebinding/etcd"
"k8s.io/kubernetes/pkg/storage/etcd"
"k8s.io/kubernetes/pkg/storage/etcd/etcdtest"
etcdtesting "k8s.io/kubernetes/pkg/storage/etcd/testing"
)
func TestNew(t *testing.T) {
// NOTE(ericchiang): Can't get this strategy to do reads. Get cryptic "client: etcd cluster is unavailable or misconfigured"
// Writes work fine, so use to test storing initial data.
server := etcdtesting.NewEtcdTestClientServer(t)
defer server.Terminate(t)
codec := testapi.Groups[rbac.GroupName].StorageCodec()
getRESTOptions := func(resource string) generic.RESTOptions {
cacheSize := etcdtest.DeserializationCacheSize
storage := etcd.NewEtcdStorage(server.Client, codec, resource, false, cacheSize)
return generic.RESTOptions{Storage: storage, Decorator: generic.UndecoratedStorage}
}
roleRegistry := role.NewRegistry(roleetcd.NewREST(getRESTOptions("roles")))
roleBindingRegistry := rolebinding.NewRegistry(rolebindingetcd.NewREST(getRESTOptions("rolebindings")))
clusterRoleRegistry := clusterrole.NewRegistry(clusterroleetcd.NewREST(getRESTOptions("clusterroles")))
clusterRoleBindingRegistry := clusterrolebinding.NewRegistry(clusterrolebindingetcd.NewREST(getRESTOptions("clusterrolebindings")))
_, err := New(roleRegistry, roleBindingRegistry, clusterRoleRegistry, clusterRoleBindingRegistry, "")
if err != nil {
t.Fatalf("failed to create authorizer: %v", err)
}
}

View File

@@ -35,6 +35,7 @@ import (
"k8s.io/kubernetes/pkg/apis/batch" "k8s.io/kubernetes/pkg/apis/batch"
"k8s.io/kubernetes/pkg/apis/extensions" "k8s.io/kubernetes/pkg/apis/extensions"
"k8s.io/kubernetes/pkg/apis/policy" "k8s.io/kubernetes/pkg/apis/policy"
"k8s.io/kubernetes/pkg/apis/rbac"
"k8s.io/kubernetes/pkg/apiserver" "k8s.io/kubernetes/pkg/apiserver"
clientset "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset" clientset "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
"k8s.io/kubernetes/pkg/client/record" "k8s.io/kubernetes/pkg/client/record"
@@ -183,6 +184,10 @@ func NewMasterConfig() *master.Config {
unversioned.GroupResource{Group: policy.GroupName, Resource: genericapiserver.AllResources}, unversioned.GroupResource{Group: policy.GroupName, Resource: genericapiserver.AllResources},
"", "",
NewSingleContentTypeSerializer(api.Scheme, testapi.Policy.Codec(), runtime.ContentTypeJSON)) NewSingleContentTypeSerializer(api.Scheme, testapi.Policy.Codec(), runtime.ContentTypeJSON))
storageFactory.SetSerializer(
unversioned.GroupResource{Group: rbac.GroupName, Resource: genericapiserver.AllResources},
"",
NewSingleContentTypeSerializer(api.Scheme, testapi.Rbac.Codec(), runtime.ContentTypeJSON))
return &master.Config{ return &master.Config{
Config: &genericapiserver.Config{ Config: &genericapiserver.Config{