mirror of
https://github.com/ceph/ceph-csi.git
synced 2025-06-13 18:43:34 +00:00
vendor files
This commit is contained in:
29
vendor/k8s.io/kubernetes/plugin/pkg/auth/BUILD
generated
vendored
Normal file
29
vendor/k8s.io/kubernetes/plugin/pkg/auth/BUILD
generated
vendored
Normal file
@ -0,0 +1,29 @@
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_library",
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = ["doc.go"],
|
||||
importpath = "k8s.io/kubernetes/plugin/pkg/auth",
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [
|
||||
":package-srcs",
|
||||
"//plugin/pkg/auth/authenticator/token/bootstrap:all-srcs",
|
||||
"//plugin/pkg/auth/authorizer:all-srcs",
|
||||
],
|
||||
tags = ["automanaged"],
|
||||
)
|
10
vendor/k8s.io/kubernetes/plugin/pkg/auth/OWNERS
generated
vendored
Normal file
10
vendor/k8s.io/kubernetes/plugin/pkg/auth/OWNERS
generated
vendored
Normal file
@ -0,0 +1,10 @@
|
||||
approvers:
|
||||
- erictune
|
||||
- liggitt
|
||||
- deads2k
|
||||
reviewers:
|
||||
- erictune
|
||||
- liggitt
|
||||
- deads2k
|
||||
- ericchiang
|
||||
- enj
|
51
vendor/k8s.io/kubernetes/plugin/pkg/auth/authenticator/token/bootstrap/BUILD
generated
vendored
Normal file
51
vendor/k8s.io/kubernetes/plugin/pkg/auth/authenticator/token/bootstrap/BUILD
generated
vendored
Normal file
@ -0,0 +1,51 @@
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_library",
|
||||
"go_test",
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = ["bootstrap_test.go"],
|
||||
importpath = "k8s.io/kubernetes/plugin/pkg/auth/authenticator/token/bootstrap",
|
||||
library = ":go_default_library",
|
||||
deps = [
|
||||
"//pkg/apis/core:go_default_library",
|
||||
"//pkg/bootstrap/api:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/api/errors:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/labels:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/authentication/user:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = ["bootstrap.go"],
|
||||
importpath = "k8s.io/kubernetes/plugin/pkg/auth/authenticator/token/bootstrap",
|
||||
deps = [
|
||||
"//pkg/apis/core:go_default_library",
|
||||
"//pkg/bootstrap/api:go_default_library",
|
||||
"//pkg/client/listers/core/internalversion:go_default_library",
|
||||
"//vendor/github.com/golang/glog:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/api/errors:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/sets:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/authentication/user:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
tags = ["automanaged"],
|
||||
)
|
220
vendor/k8s.io/kubernetes/plugin/pkg/auth/authenticator/token/bootstrap/bootstrap.go
generated
vendored
Normal file
220
vendor/k8s.io/kubernetes/plugin/pkg/auth/authenticator/token/bootstrap/bootstrap.go
generated
vendored
Normal file
@ -0,0 +1,220 @@
|
||||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
/*
|
||||
Package bootstrap provides a token authenticator for TLS bootstrap secrets.
|
||||
*/
|
||||
package bootstrap
|
||||
|
||||
import (
|
||||
"crypto/subtle"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/golang/glog"
|
||||
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
"k8s.io/apiserver/pkg/authentication/user"
|
||||
api "k8s.io/kubernetes/pkg/apis/core"
|
||||
bootstrapapi "k8s.io/kubernetes/pkg/bootstrap/api"
|
||||
"k8s.io/kubernetes/pkg/client/listers/core/internalversion"
|
||||
)
|
||||
|
||||
// TODO: A few methods in this package is copied from other sources. Either
|
||||
// because the existing functionality isn't exported or because it is in a
|
||||
// package that shouldn't be directly imported by this packages.
|
||||
|
||||
// NewTokenAuthenticator initializes a bootstrap token authenticator.
|
||||
//
|
||||
// Lister is expected to be for the "kube-system" namespace.
|
||||
func NewTokenAuthenticator(lister internalversion.SecretNamespaceLister) *TokenAuthenticator {
|
||||
return &TokenAuthenticator{lister}
|
||||
}
|
||||
|
||||
// TokenAuthenticator authenticates bootstrap tokens from secrets in the API server.
|
||||
type TokenAuthenticator struct {
|
||||
lister internalversion.SecretNamespaceLister
|
||||
}
|
||||
|
||||
// tokenErrorf prints a error message for a secret that has matched a bearer
|
||||
// token but fails to meet some other criteria.
|
||||
//
|
||||
// tokenErrorf(secret, "has invalid value for key %s", key)
|
||||
//
|
||||
func tokenErrorf(s *api.Secret, format string, i ...interface{}) {
|
||||
format = fmt.Sprintf("Bootstrap secret %s/%s matching bearer token ", s.Namespace, s.Name) + format
|
||||
glog.V(3).Infof(format, i...)
|
||||
}
|
||||
|
||||
// AuthenticateToken tries to match the provided token to a bootstrap token secret
|
||||
// in a given namespace. If found, it authenticates the token in the
|
||||
// "system:bootstrappers" group and with the "system:bootstrap:(token-id)" username.
|
||||
//
|
||||
// All secrets must be of type "bootstrap.kubernetes.io/token". An example secret:
|
||||
//
|
||||
// apiVersion: v1
|
||||
// kind: Secret
|
||||
// metadata:
|
||||
// # Name MUST be of form "bootstrap-token-( token id )".
|
||||
// name: bootstrap-token-( token id )
|
||||
// namespace: kube-system
|
||||
// # Only secrets of this type will be evaluated.
|
||||
// type: bootstrap.kubernetes.io/token
|
||||
// data:
|
||||
// token-secret: ( private part of token )
|
||||
// token-id: ( token id )
|
||||
// # Required key usage.
|
||||
// usage-bootstrap-authentication: true
|
||||
// auth-extra-groups: "system:bootstrappers:custom-group1,system:bootstrappers:custom-group2"
|
||||
// # May also contain an expiry.
|
||||
//
|
||||
// Tokens are expected to be of the form:
|
||||
//
|
||||
// ( token-id ).( token-secret )
|
||||
//
|
||||
func (t *TokenAuthenticator) AuthenticateToken(token string) (user.Info, bool, error) {
|
||||
tokenID, tokenSecret, err := parseToken(token)
|
||||
if err != nil {
|
||||
// Token isn't of the correct form, ignore it.
|
||||
return nil, false, nil
|
||||
}
|
||||
|
||||
secretName := bootstrapapi.BootstrapTokenSecretPrefix + tokenID
|
||||
secret, err := t.lister.Get(secretName)
|
||||
if err != nil {
|
||||
if errors.IsNotFound(err) {
|
||||
glog.V(3).Infof("No secret of name %s to match bootstrap bearer token", secretName)
|
||||
return nil, false, nil
|
||||
}
|
||||
return nil, false, err
|
||||
}
|
||||
|
||||
if secret.DeletionTimestamp != nil {
|
||||
tokenErrorf(secret, "is deleted and awaiting removal")
|
||||
return nil, false, nil
|
||||
}
|
||||
|
||||
if string(secret.Type) != string(bootstrapapi.SecretTypeBootstrapToken) || secret.Data == nil {
|
||||
tokenErrorf(secret, "has invalid type, expected %s.", bootstrapapi.SecretTypeBootstrapToken)
|
||||
return nil, false, nil
|
||||
}
|
||||
|
||||
ts := getSecretString(secret, bootstrapapi.BootstrapTokenSecretKey)
|
||||
if subtle.ConstantTimeCompare([]byte(ts), []byte(tokenSecret)) != 1 {
|
||||
tokenErrorf(secret, "has invalid value for key %s, expected %s.", bootstrapapi.BootstrapTokenSecretKey, tokenSecret)
|
||||
return nil, false, nil
|
||||
}
|
||||
|
||||
id := getSecretString(secret, bootstrapapi.BootstrapTokenIDKey)
|
||||
if id != tokenID {
|
||||
tokenErrorf(secret, "has invalid value for key %s, expected %s.", bootstrapapi.BootstrapTokenIDKey, tokenID)
|
||||
return nil, false, nil
|
||||
}
|
||||
|
||||
if isSecretExpired(secret) {
|
||||
// logging done in isSecretExpired method.
|
||||
return nil, false, nil
|
||||
}
|
||||
|
||||
if getSecretString(secret, bootstrapapi.BootstrapTokenUsageAuthentication) != "true" {
|
||||
tokenErrorf(secret, "not marked %s=true.", bootstrapapi.BootstrapTokenUsageAuthentication)
|
||||
return nil, false, nil
|
||||
}
|
||||
|
||||
groups, err := getGroups(secret)
|
||||
if err != nil {
|
||||
tokenErrorf(secret, "has invalid value for key %s: %v.", bootstrapapi.BootstrapTokenExtraGroupsKey, err)
|
||||
return nil, false, nil
|
||||
}
|
||||
|
||||
return &user.DefaultInfo{
|
||||
Name: bootstrapapi.BootstrapUserPrefix + string(id),
|
||||
Groups: groups,
|
||||
}, true, nil
|
||||
}
|
||||
|
||||
// Copied from k8s.io/kubernetes/pkg/bootstrap/api
|
||||
func getSecretString(secret *api.Secret, key string) string {
|
||||
if secret.Data == nil {
|
||||
return ""
|
||||
}
|
||||
if val, ok := secret.Data[key]; ok {
|
||||
return string(val)
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// Copied from k8s.io/kubernetes/pkg/bootstrap/api
|
||||
func isSecretExpired(secret *api.Secret) bool {
|
||||
expiration := getSecretString(secret, bootstrapapi.BootstrapTokenExpirationKey)
|
||||
if len(expiration) > 0 {
|
||||
expTime, err2 := time.Parse(time.RFC3339, expiration)
|
||||
if err2 != nil {
|
||||
tokenErrorf(secret, "has unparsable expiration time (%s). Treating as expired.", expiration)
|
||||
return true
|
||||
}
|
||||
if time.Now().After(expTime) {
|
||||
tokenErrorf(secret, "has expired.", expiration)
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Copied from kubernetes/cmd/kubeadm/app/util/token
|
||||
|
||||
var (
|
||||
tokenRegexpString = "^([a-z0-9]{6})\\.([a-z0-9]{16})$"
|
||||
tokenRegexp = regexp.MustCompile(tokenRegexpString)
|
||||
)
|
||||
|
||||
// parseToken tries and parse a valid token from a string.
|
||||
// A token ID and token secret are returned in case of success, an error otherwise.
|
||||
func parseToken(s string) (string, string, error) {
|
||||
split := tokenRegexp.FindStringSubmatch(s)
|
||||
if len(split) != 3 {
|
||||
return "", "", fmt.Errorf("token [%q] was not of form [%q]", s, tokenRegexpString)
|
||||
}
|
||||
return split[1], split[2], nil
|
||||
}
|
||||
|
||||
// getGroups loads and validates the bootstrapapi.BootstrapTokenExtraGroupsKey
|
||||
// key from the bootstrap token secret, returning a list of group names or an
|
||||
// error if any of the group names are invalid.
|
||||
func getGroups(secret *api.Secret) ([]string, error) {
|
||||
// always include the default group
|
||||
groups := sets.NewString(bootstrapapi.BootstrapDefaultGroup)
|
||||
|
||||
// grab any extra groups and if there are none, return just the default
|
||||
extraGroupsString := getSecretString(secret, bootstrapapi.BootstrapTokenExtraGroupsKey)
|
||||
if extraGroupsString == "" {
|
||||
return groups.List(), nil
|
||||
}
|
||||
|
||||
// validate the names of the extra groups
|
||||
for _, group := range strings.Split(extraGroupsString, ",") {
|
||||
if err := bootstrapapi.ValidateBootstrapGroupName(group); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
groups.Insert(group)
|
||||
}
|
||||
|
||||
// return the result as a deduplicated, sorted list
|
||||
return groups.List(), nil
|
||||
}
|
359
vendor/k8s.io/kubernetes/plugin/pkg/auth/authenticator/token/bootstrap/bootstrap_test.go
generated
vendored
Normal file
359
vendor/k8s.io/kubernetes/plugin/pkg/auth/authenticator/token/bootstrap/bootstrap_test.go
generated
vendored
Normal file
@ -0,0 +1,359 @@
|
||||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package bootstrap
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/apiserver/pkg/authentication/user"
|
||||
api "k8s.io/kubernetes/pkg/apis/core"
|
||||
bootstrapapi "k8s.io/kubernetes/pkg/bootstrap/api"
|
||||
)
|
||||
|
||||
type lister struct {
|
||||
secrets []*api.Secret
|
||||
}
|
||||
|
||||
func (l *lister) List(selector labels.Selector) (ret []*api.Secret, err error) {
|
||||
return l.secrets, nil
|
||||
}
|
||||
|
||||
func (l *lister) Get(name string) (*api.Secret, error) {
|
||||
for _, s := range l.secrets {
|
||||
if s.Name == name {
|
||||
return s, nil
|
||||
}
|
||||
}
|
||||
return nil, errors.NewNotFound(schema.GroupResource{}, name)
|
||||
}
|
||||
|
||||
const (
|
||||
tokenID = "foobar" // 6 letters
|
||||
tokenSecret = "circumnavigation" // 16 letters
|
||||
)
|
||||
|
||||
func TestTokenAuthenticator(t *testing.T) {
|
||||
now := metav1.Now()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
|
||||
secrets []*api.Secret
|
||||
token string
|
||||
|
||||
wantNotFound bool
|
||||
wantUser *user.DefaultInfo
|
||||
}{
|
||||
{
|
||||
name: "valid token",
|
||||
secrets: []*api.Secret{
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: bootstrapapi.BootstrapTokenSecretPrefix + tokenID,
|
||||
},
|
||||
Data: map[string][]byte{
|
||||
bootstrapapi.BootstrapTokenIDKey: []byte(tokenID),
|
||||
bootstrapapi.BootstrapTokenSecretKey: []byte(tokenSecret),
|
||||
bootstrapapi.BootstrapTokenUsageAuthentication: []byte("true"),
|
||||
},
|
||||
Type: "bootstrap.kubernetes.io/token",
|
||||
},
|
||||
},
|
||||
token: tokenID + "." + tokenSecret,
|
||||
wantUser: &user.DefaultInfo{
|
||||
Name: "system:bootstrap:" + tokenID,
|
||||
Groups: []string{"system:bootstrappers"},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "valid token with extra group",
|
||||
secrets: []*api.Secret{
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: bootstrapapi.BootstrapTokenSecretPrefix + tokenID,
|
||||
},
|
||||
Data: map[string][]byte{
|
||||
bootstrapapi.BootstrapTokenIDKey: []byte(tokenID),
|
||||
bootstrapapi.BootstrapTokenSecretKey: []byte(tokenSecret),
|
||||
bootstrapapi.BootstrapTokenUsageAuthentication: []byte("true"),
|
||||
bootstrapapi.BootstrapTokenExtraGroupsKey: []byte("system:bootstrappers:foo"),
|
||||
},
|
||||
Type: "bootstrap.kubernetes.io/token",
|
||||
},
|
||||
},
|
||||
token: tokenID + "." + tokenSecret,
|
||||
wantUser: &user.DefaultInfo{
|
||||
Name: "system:bootstrap:" + tokenID,
|
||||
Groups: []string{"system:bootstrappers", "system:bootstrappers:foo"},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "invalid group",
|
||||
secrets: []*api.Secret{
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: bootstrapapi.BootstrapTokenSecretPrefix + tokenID,
|
||||
},
|
||||
Data: map[string][]byte{
|
||||
bootstrapapi.BootstrapTokenIDKey: []byte(tokenID),
|
||||
bootstrapapi.BootstrapTokenSecretKey: []byte(tokenSecret),
|
||||
bootstrapapi.BootstrapTokenUsageAuthentication: []byte("true"),
|
||||
bootstrapapi.BootstrapTokenExtraGroupsKey: []byte("foo"),
|
||||
},
|
||||
Type: "bootstrap.kubernetes.io/token",
|
||||
},
|
||||
},
|
||||
token: tokenID + "." + tokenSecret,
|
||||
wantNotFound: true,
|
||||
},
|
||||
{
|
||||
name: "invalid secret name",
|
||||
secrets: []*api.Secret{
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "bad-name",
|
||||
},
|
||||
Data: map[string][]byte{
|
||||
bootstrapapi.BootstrapTokenIDKey: []byte(tokenID),
|
||||
bootstrapapi.BootstrapTokenSecretKey: []byte(tokenSecret),
|
||||
bootstrapapi.BootstrapTokenUsageAuthentication: []byte("true"),
|
||||
},
|
||||
Type: "bootstrap.kubernetes.io/token",
|
||||
},
|
||||
},
|
||||
token: tokenID + "." + tokenSecret,
|
||||
wantNotFound: true,
|
||||
},
|
||||
{
|
||||
name: "no usage",
|
||||
secrets: []*api.Secret{
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: bootstrapapi.BootstrapTokenSecretPrefix + tokenID,
|
||||
},
|
||||
Data: map[string][]byte{
|
||||
bootstrapapi.BootstrapTokenIDKey: []byte(tokenID),
|
||||
bootstrapapi.BootstrapTokenSecretKey: []byte(tokenSecret),
|
||||
},
|
||||
Type: "bootstrap.kubernetes.io/token",
|
||||
},
|
||||
},
|
||||
token: tokenID + "." + tokenSecret,
|
||||
wantNotFound: true,
|
||||
},
|
||||
{
|
||||
name: "wrong token",
|
||||
secrets: []*api.Secret{
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: bootstrapapi.BootstrapTokenSecretPrefix + tokenID,
|
||||
},
|
||||
Data: map[string][]byte{
|
||||
bootstrapapi.BootstrapTokenIDKey: []byte(tokenID),
|
||||
bootstrapapi.BootstrapTokenSecretKey: []byte(tokenSecret),
|
||||
bootstrapapi.BootstrapTokenUsageAuthentication: []byte("true"),
|
||||
},
|
||||
Type: "bootstrap.kubernetes.io/token",
|
||||
},
|
||||
},
|
||||
token: "barfoo" + "." + tokenSecret,
|
||||
wantNotFound: true,
|
||||
},
|
||||
{
|
||||
name: "deleted token",
|
||||
secrets: []*api.Secret{
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: bootstrapapi.BootstrapTokenSecretPrefix + tokenID,
|
||||
DeletionTimestamp: &now,
|
||||
},
|
||||
Data: map[string][]byte{
|
||||
bootstrapapi.BootstrapTokenIDKey: []byte(tokenID),
|
||||
bootstrapapi.BootstrapTokenSecretKey: []byte(tokenSecret),
|
||||
bootstrapapi.BootstrapTokenUsageAuthentication: []byte("true"),
|
||||
},
|
||||
Type: "bootstrap.kubernetes.io/token",
|
||||
},
|
||||
},
|
||||
token: tokenID + "." + tokenSecret,
|
||||
wantNotFound: true,
|
||||
},
|
||||
{
|
||||
name: "expired token",
|
||||
secrets: []*api.Secret{
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: bootstrapapi.BootstrapTokenSecretPrefix + tokenID,
|
||||
},
|
||||
Data: map[string][]byte{
|
||||
bootstrapapi.BootstrapTokenIDKey: []byte(tokenID),
|
||||
bootstrapapi.BootstrapTokenSecretKey: []byte(tokenSecret),
|
||||
bootstrapapi.BootstrapTokenUsageAuthentication: []byte("true"),
|
||||
bootstrapapi.BootstrapTokenExpirationKey: []byte("2009-11-10T23:00:00Z"),
|
||||
},
|
||||
Type: "bootstrap.kubernetes.io/token",
|
||||
},
|
||||
},
|
||||
token: tokenID + "." + tokenSecret,
|
||||
wantNotFound: true,
|
||||
},
|
||||
{
|
||||
name: "not expired token",
|
||||
secrets: []*api.Secret{
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: bootstrapapi.BootstrapTokenSecretPrefix + tokenID,
|
||||
},
|
||||
Data: map[string][]byte{
|
||||
bootstrapapi.BootstrapTokenIDKey: []byte(tokenID),
|
||||
bootstrapapi.BootstrapTokenSecretKey: []byte(tokenSecret),
|
||||
bootstrapapi.BootstrapTokenUsageAuthentication: []byte("true"),
|
||||
bootstrapapi.BootstrapTokenExpirationKey: []byte("2109-11-10T23:00:00Z"),
|
||||
},
|
||||
Type: "bootstrap.kubernetes.io/token",
|
||||
},
|
||||
},
|
||||
token: tokenID + "." + tokenSecret,
|
||||
wantUser: &user.DefaultInfo{
|
||||
Name: "system:bootstrap:" + tokenID,
|
||||
Groups: []string{"system:bootstrappers"},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "token id wrong length",
|
||||
secrets: []*api.Secret{
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: bootstrapapi.BootstrapTokenSecretPrefix + "foo",
|
||||
},
|
||||
Data: map[string][]byte{
|
||||
bootstrapapi.BootstrapTokenIDKey: []byte("foo"),
|
||||
bootstrapapi.BootstrapTokenSecretKey: []byte(tokenSecret),
|
||||
bootstrapapi.BootstrapTokenUsageAuthentication: []byte("true"),
|
||||
},
|
||||
Type: "bootstrap.kubernetes.io/token",
|
||||
},
|
||||
},
|
||||
// Token ID must be 6 characters.
|
||||
token: "foo" + "." + tokenSecret,
|
||||
wantNotFound: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
func() {
|
||||
a := NewTokenAuthenticator(&lister{test.secrets})
|
||||
u, found, err := a.AuthenticateToken(test.token)
|
||||
if err != nil {
|
||||
t.Errorf("test %q returned an error: %v", test.name, err)
|
||||
return
|
||||
}
|
||||
|
||||
if !found {
|
||||
if !test.wantNotFound {
|
||||
t.Errorf("test %q expected to get user", test.name)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if test.wantNotFound {
|
||||
t.Errorf("test %q expected to not get a user", test.name)
|
||||
return
|
||||
}
|
||||
|
||||
gotUser := u.(*user.DefaultInfo)
|
||||
|
||||
if !reflect.DeepEqual(gotUser, test.wantUser) {
|
||||
t.Errorf("test %q want user=%#v, got=%#v", test.name, test.wantUser, gotUser)
|
||||
}
|
||||
}()
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetGroups(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
secret *api.Secret
|
||||
expectResult []string
|
||||
expectError bool
|
||||
}{
|
||||
{
|
||||
name: "not set",
|
||||
secret: &api.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "test"},
|
||||
Data: map[string][]byte{},
|
||||
},
|
||||
expectResult: []string{"system:bootstrappers"},
|
||||
},
|
||||
{
|
||||
name: "set to empty value",
|
||||
secret: &api.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "test"},
|
||||
Data: map[string][]byte{
|
||||
bootstrapapi.BootstrapTokenExtraGroupsKey: []byte(""),
|
||||
},
|
||||
},
|
||||
expectResult: []string{"system:bootstrappers"},
|
||||
},
|
||||
{
|
||||
name: "invalid prefix",
|
||||
secret: &api.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "test"},
|
||||
Data: map[string][]byte{
|
||||
bootstrapapi.BootstrapTokenExtraGroupsKey: []byte("foo"),
|
||||
},
|
||||
},
|
||||
expectError: true,
|
||||
},
|
||||
{
|
||||
name: "valid",
|
||||
secret: &api.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "test"},
|
||||
Data: map[string][]byte{
|
||||
bootstrapapi.BootstrapTokenExtraGroupsKey: []byte("system:bootstrappers:foo,system:bootstrappers:bar,system:bootstrappers:bar"),
|
||||
},
|
||||
},
|
||||
// expect the results in deduplicated, sorted order
|
||||
expectResult: []string{
|
||||
"system:bootstrappers",
|
||||
"system:bootstrappers:bar",
|
||||
"system:bootstrappers:foo",
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
result, err := getGroups(test.secret)
|
||||
if test.expectError {
|
||||
if err == nil {
|
||||
t.Errorf("test %q expected an error, but didn't get one (result: %#v)", test.name, result)
|
||||
}
|
||||
continue
|
||||
}
|
||||
if err != nil {
|
||||
t.Errorf("test %q return an unexpected error: %v", test.name, err)
|
||||
continue
|
||||
}
|
||||
if !reflect.DeepEqual(result, test.expectResult) {
|
||||
t.Errorf("test %q expected %#v, got %#v", test.name, test.expectResult, result)
|
||||
}
|
||||
}
|
||||
}
|
29
vendor/k8s.io/kubernetes/plugin/pkg/auth/authorizer/BUILD
generated
vendored
Normal file
29
vendor/k8s.io/kubernetes/plugin/pkg/auth/authorizer/BUILD
generated
vendored
Normal file
@ -0,0 +1,29 @@
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_library",
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = ["doc.go"],
|
||||
importpath = "k8s.io/kubernetes/plugin/pkg/auth/authorizer",
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [
|
||||
":package-srcs",
|
||||
"//plugin/pkg/auth/authorizer/node:all-srcs",
|
||||
"//plugin/pkg/auth/authorizer/rbac:all-srcs",
|
||||
],
|
||||
tags = ["automanaged"],
|
||||
)
|
18
vendor/k8s.io/kubernetes/plugin/pkg/auth/authorizer/doc.go
generated
vendored
Normal file
18
vendor/k8s.io/kubernetes/plugin/pkg/auth/authorizer/doc.go
generated
vendored
Normal file
@ -0,0 +1,18 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
// Package authorizer contains implementations for pkg/auth/authorizer interfaces
|
||||
package authorizer // import "k8s.io/kubernetes/plugin/pkg/auth/authorizer"
|
63
vendor/k8s.io/kubernetes/plugin/pkg/auth/authorizer/node/BUILD
generated
vendored
Normal file
63
vendor/k8s.io/kubernetes/plugin/pkg/auth/authorizer/node/BUILD
generated
vendored
Normal file
@ -0,0 +1,63 @@
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_library",
|
||||
"go_test",
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = ["node_authorizer_test.go"],
|
||||
importpath = "k8s.io/kubernetes/plugin/pkg/auth/authorizer/node",
|
||||
library = ":go_default_library",
|
||||
deps = [
|
||||
"//pkg/apis/core:go_default_library",
|
||||
"//pkg/auth/nodeidentifier:go_default_library",
|
||||
"//plugin/pkg/auth/authorizer/rbac/bootstrappolicy:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/authentication/user:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/authorization/authorizer:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"graph.go",
|
||||
"graph_populator.go",
|
||||
"node_authorizer.go",
|
||||
],
|
||||
importpath = "k8s.io/kubernetes/plugin/pkg/auth/authorizer/node",
|
||||
deps = [
|
||||
"//pkg/api/persistentvolume:go_default_library",
|
||||
"//pkg/api/pod:go_default_library",
|
||||
"//pkg/apis/core:go_default_library",
|
||||
"//pkg/apis/rbac:go_default_library",
|
||||
"//pkg/auth/nodeidentifier:go_default_library",
|
||||
"//pkg/client/informers/informers_generated/internalversion/core/internalversion:go_default_library",
|
||||
"//pkg/features:go_default_library",
|
||||
"//plugin/pkg/auth/authorizer/rbac:go_default_library",
|
||||
"//third_party/forked/gonum/graph:go_default_library",
|
||||
"//third_party/forked/gonum/graph/simple:go_default_library",
|
||||
"//third_party/forked/gonum/graph/traverse:go_default_library",
|
||||
"//vendor/github.com/golang/glog:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/authorization/authorizer:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/util/feature:go_default_library",
|
||||
"//vendor/k8s.io/client-go/tools/cache:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
tags = ["automanaged"],
|
||||
)
|
9
vendor/k8s.io/kubernetes/plugin/pkg/auth/authorizer/node/OWNERS
generated
vendored
Normal file
9
vendor/k8s.io/kubernetes/plugin/pkg/auth/authorizer/node/OWNERS
generated
vendored
Normal file
@ -0,0 +1,9 @@
|
||||
approvers:
|
||||
- tallclair
|
||||
- liggitt
|
||||
- deads2k
|
||||
reviewers:
|
||||
- tallclair
|
||||
- liggitt
|
||||
- deads2k
|
||||
- ericchiang
|
265
vendor/k8s.io/kubernetes/plugin/pkg/auth/authorizer/node/graph.go
generated
vendored
Normal file
265
vendor/k8s.io/kubernetes/plugin/pkg/auth/authorizer/node/graph.go
generated
vendored
Normal file
@ -0,0 +1,265 @@
|
||||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package node
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
pvutil "k8s.io/kubernetes/pkg/api/persistentvolume"
|
||||
podutil "k8s.io/kubernetes/pkg/api/pod"
|
||||
api "k8s.io/kubernetes/pkg/apis/core"
|
||||
"k8s.io/kubernetes/third_party/forked/gonum/graph"
|
||||
"k8s.io/kubernetes/third_party/forked/gonum/graph/simple"
|
||||
)
|
||||
|
||||
// namedVertex implements graph.Node and remembers the type, namespace, and name of its related API object
|
||||
type namedVertex struct {
|
||||
name string
|
||||
namespace string
|
||||
id int
|
||||
vertexType vertexType
|
||||
}
|
||||
|
||||
func newNamedVertex(vertexType vertexType, namespace, name string, id int) *namedVertex {
|
||||
return &namedVertex{
|
||||
vertexType: vertexType,
|
||||
name: name,
|
||||
namespace: namespace,
|
||||
id: id,
|
||||
}
|
||||
}
|
||||
func (n *namedVertex) ID() int {
|
||||
return n.id
|
||||
}
|
||||
func (n *namedVertex) String() string {
|
||||
if len(n.namespace) == 0 {
|
||||
return vertexTypes[n.vertexType] + ":" + n.name
|
||||
}
|
||||
return vertexTypes[n.vertexType] + ":" + n.namespace + "/" + n.name
|
||||
}
|
||||
|
||||
// destinationEdge is a graph edge that includes a denormalized reference to the final destination vertex.
|
||||
// This should only be used when there is a single leaf vertex reachable from T.
|
||||
type destinationEdge struct {
|
||||
F graph.Node
|
||||
T graph.Node
|
||||
Destination graph.Node
|
||||
}
|
||||
|
||||
func newDestinationEdge(from, to, destination graph.Node) graph.Edge {
|
||||
return &destinationEdge{F: from, T: to, Destination: destination}
|
||||
}
|
||||
func (e *destinationEdge) From() graph.Node { return e.F }
|
||||
func (e *destinationEdge) To() graph.Node { return e.T }
|
||||
func (e *destinationEdge) Weight() float64 { return 0 }
|
||||
func (e *destinationEdge) DestinationID() int { return e.Destination.ID() }
|
||||
|
||||
// Graph holds graph vertices and a way to look up a vertex for a particular API type/namespace/name.
|
||||
// All edges point toward the vertices representing Kubernetes nodes:
|
||||
//
|
||||
// node <- pod
|
||||
// pod <- secret,configmap,pvc
|
||||
// pvc <- pv
|
||||
// pv <- secret
|
||||
type Graph struct {
|
||||
lock sync.RWMutex
|
||||
graph *simple.DirectedAcyclicGraph
|
||||
// vertices is a map of type -> namespace -> name -> vertex
|
||||
vertices map[vertexType]namespaceVertexMapping
|
||||
}
|
||||
|
||||
// namespaceVertexMapping is a map of namespace -> name -> vertex
|
||||
type namespaceVertexMapping map[string]nameVertexMapping
|
||||
|
||||
// nameVertexMapping is a map of name -> vertex
|
||||
type nameVertexMapping map[string]*namedVertex
|
||||
|
||||
func NewGraph() *Graph {
|
||||
return &Graph{
|
||||
vertices: map[vertexType]namespaceVertexMapping{},
|
||||
graph: simple.NewDirectedAcyclicGraph(0, 0),
|
||||
}
|
||||
}
|
||||
|
||||
// vertexType indicates the type of the API object the vertex represents.
|
||||
// represented as a byte to minimize space used in the vertices.
|
||||
type vertexType byte
|
||||
|
||||
const (
|
||||
configMapVertexType vertexType = iota
|
||||
nodeVertexType
|
||||
podVertexType
|
||||
pvcVertexType
|
||||
pvVertexType
|
||||
secretVertexType
|
||||
)
|
||||
|
||||
var vertexTypes = map[vertexType]string{
|
||||
configMapVertexType: "configmap",
|
||||
nodeVertexType: "node",
|
||||
podVertexType: "pod",
|
||||
pvcVertexType: "pvc",
|
||||
pvVertexType: "pv",
|
||||
secretVertexType: "secret",
|
||||
}
|
||||
|
||||
// must be called under a write lock
|
||||
func (g *Graph) getOrCreateVertex_locked(vertexType vertexType, namespace, name string) *namedVertex {
|
||||
if vertex, exists := g.getVertex_rlocked(vertexType, namespace, name); exists {
|
||||
return vertex
|
||||
}
|
||||
return g.createVertex_locked(vertexType, namespace, name)
|
||||
}
|
||||
|
||||
// must be called under a read lock
|
||||
func (g *Graph) getVertex_rlocked(vertexType vertexType, namespace, name string) (*namedVertex, bool) {
|
||||
vertex, exists := g.vertices[vertexType][namespace][name]
|
||||
return vertex, exists
|
||||
}
|
||||
|
||||
// must be called under a write lock
|
||||
func (g *Graph) createVertex_locked(vertexType vertexType, namespace, name string) *namedVertex {
|
||||
typedVertices, exists := g.vertices[vertexType]
|
||||
if !exists {
|
||||
typedVertices = namespaceVertexMapping{}
|
||||
g.vertices[vertexType] = typedVertices
|
||||
}
|
||||
|
||||
namespacedVertices, exists := typedVertices[namespace]
|
||||
if !exists {
|
||||
namespacedVertices = map[string]*namedVertex{}
|
||||
typedVertices[namespace] = namespacedVertices
|
||||
}
|
||||
|
||||
vertex := newNamedVertex(vertexType, namespace, name, g.graph.NewNodeID())
|
||||
namespacedVertices[name] = vertex
|
||||
g.graph.AddNode(vertex)
|
||||
|
||||
return vertex
|
||||
}
|
||||
|
||||
// must be called under write lock
|
||||
func (g *Graph) deleteVertex_locked(vertexType vertexType, namespace, name string) {
|
||||
vertex, exists := g.getVertex_rlocked(vertexType, namespace, name)
|
||||
if !exists {
|
||||
return
|
||||
}
|
||||
|
||||
// find existing neighbors with a single edge (meaning we are their only neighbor)
|
||||
neighborsToRemove := []graph.Node{}
|
||||
g.graph.VisitFrom(vertex, func(neighbor graph.Node) bool {
|
||||
// this downstream neighbor has only one edge (which must be from us), so remove them as well
|
||||
if g.graph.Degree(neighbor) == 1 {
|
||||
neighborsToRemove = append(neighborsToRemove, neighbor)
|
||||
}
|
||||
return true
|
||||
})
|
||||
g.graph.VisitTo(vertex, func(neighbor graph.Node) bool {
|
||||
// this upstream neighbor has only one edge (which must be to us), so remove them as well
|
||||
if g.graph.Degree(neighbor) == 1 {
|
||||
neighborsToRemove = append(neighborsToRemove, neighbor)
|
||||
}
|
||||
return true
|
||||
})
|
||||
|
||||
// remove the vertex
|
||||
g.graph.RemoveNode(vertex)
|
||||
delete(g.vertices[vertexType][namespace], name)
|
||||
if len(g.vertices[vertexType][namespace]) == 0 {
|
||||
delete(g.vertices[vertexType], namespace)
|
||||
}
|
||||
|
||||
// remove neighbors that are now edgeless
|
||||
for _, neighbor := range neighborsToRemove {
|
||||
g.graph.RemoveNode(neighbor)
|
||||
n := neighbor.(*namedVertex)
|
||||
delete(g.vertices[n.vertexType][n.namespace], n.name)
|
||||
if len(g.vertices[n.vertexType][n.namespace]) == 0 {
|
||||
delete(g.vertices[n.vertexType], n.namespace)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// AddPod should only be called once spec.NodeName is populated.
|
||||
// It sets up edges for the following relationships (which are immutable for a pod once bound to a node):
|
||||
//
|
||||
// pod -> node
|
||||
//
|
||||
// secret -> pod
|
||||
// configmap -> pod
|
||||
// pvc -> pod
|
||||
func (g *Graph) AddPod(pod *api.Pod) {
|
||||
g.lock.Lock()
|
||||
defer g.lock.Unlock()
|
||||
|
||||
g.deleteVertex_locked(podVertexType, pod.Namespace, pod.Name)
|
||||
podVertex := g.getOrCreateVertex_locked(podVertexType, pod.Namespace, pod.Name)
|
||||
nodeVertex := g.getOrCreateVertex_locked(nodeVertexType, "", pod.Spec.NodeName)
|
||||
g.graph.SetEdge(newDestinationEdge(podVertex, nodeVertex, nodeVertex))
|
||||
|
||||
podutil.VisitPodSecretNames(pod, func(secret string) bool {
|
||||
g.graph.SetEdge(newDestinationEdge(g.getOrCreateVertex_locked(secretVertexType, pod.Namespace, secret), podVertex, nodeVertex))
|
||||
return true
|
||||
})
|
||||
|
||||
podutil.VisitPodConfigmapNames(pod, func(configmap string) bool {
|
||||
g.graph.SetEdge(newDestinationEdge(g.getOrCreateVertex_locked(configMapVertexType, pod.Namespace, configmap), podVertex, nodeVertex))
|
||||
return true
|
||||
})
|
||||
|
||||
for _, v := range pod.Spec.Volumes {
|
||||
if v.PersistentVolumeClaim != nil {
|
||||
g.graph.SetEdge(newDestinationEdge(g.getOrCreateVertex_locked(pvcVertexType, pod.Namespace, v.PersistentVolumeClaim.ClaimName), podVertex, nodeVertex))
|
||||
}
|
||||
}
|
||||
}
|
||||
func (g *Graph) DeletePod(name, namespace string) {
|
||||
g.lock.Lock()
|
||||
defer g.lock.Unlock()
|
||||
g.deleteVertex_locked(podVertexType, namespace, name)
|
||||
}
|
||||
|
||||
// AddPV sets up edges for the following relationships:
|
||||
//
|
||||
// secret -> pv
|
||||
//
|
||||
// pv -> pvc
|
||||
func (g *Graph) AddPV(pv *api.PersistentVolume) {
|
||||
g.lock.Lock()
|
||||
defer g.lock.Unlock()
|
||||
|
||||
// clear existing edges
|
||||
g.deleteVertex_locked(pvVertexType, "", pv.Name)
|
||||
|
||||
// if we have a pvc, establish new edges
|
||||
if pv.Spec.ClaimRef != nil {
|
||||
pvVertex := g.getOrCreateVertex_locked(pvVertexType, "", pv.Name)
|
||||
|
||||
// since we don't know the other end of the pvc -> pod -> node chain (or it may not even exist yet), we can't decorate these edges with kubernetes node info
|
||||
g.graph.SetEdge(simple.Edge{F: pvVertex, T: g.getOrCreateVertex_locked(pvcVertexType, pv.Spec.ClaimRef.Namespace, pv.Spec.ClaimRef.Name)})
|
||||
pvutil.VisitPVSecretNames(pv, func(namespace, secret string) bool {
|
||||
// This grants access to the named secret in the same namespace as the bound PVC
|
||||
g.graph.SetEdge(simple.Edge{F: g.getOrCreateVertex_locked(secretVertexType, namespace, secret), T: pvVertex})
|
||||
return true
|
||||
})
|
||||
}
|
||||
}
|
||||
func (g *Graph) DeletePV(name string) {
|
||||
g.lock.Lock()
|
||||
defer g.lock.Unlock()
|
||||
g.deleteVertex_locked(pvVertexType, "", name)
|
||||
}
|
108
vendor/k8s.io/kubernetes/plugin/pkg/auth/authorizer/node/graph_populator.go
generated
vendored
Normal file
108
vendor/k8s.io/kubernetes/plugin/pkg/auth/authorizer/node/graph_populator.go
generated
vendored
Normal file
@ -0,0 +1,108 @@
|
||||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package node
|
||||
|
||||
import (
|
||||
"github.com/golang/glog"
|
||||
|
||||
"k8s.io/client-go/tools/cache"
|
||||
api "k8s.io/kubernetes/pkg/apis/core"
|
||||
coreinformers "k8s.io/kubernetes/pkg/client/informers/informers_generated/internalversion/core/internalversion"
|
||||
)
|
||||
|
||||
type graphPopulator struct {
|
||||
graph *Graph
|
||||
}
|
||||
|
||||
func AddGraphEventHandlers(graph *Graph, pods coreinformers.PodInformer, pvs coreinformers.PersistentVolumeInformer) {
|
||||
g := &graphPopulator{
|
||||
graph: graph,
|
||||
}
|
||||
|
||||
pods.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{
|
||||
AddFunc: g.addPod,
|
||||
UpdateFunc: g.updatePod,
|
||||
DeleteFunc: g.deletePod,
|
||||
})
|
||||
|
||||
pvs.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{
|
||||
AddFunc: g.addPV,
|
||||
UpdateFunc: g.updatePV,
|
||||
DeleteFunc: g.deletePV,
|
||||
})
|
||||
}
|
||||
|
||||
func (g *graphPopulator) addPod(obj interface{}) {
|
||||
g.updatePod(nil, obj)
|
||||
}
|
||||
|
||||
func (g *graphPopulator) updatePod(oldObj, obj interface{}) {
|
||||
pod := obj.(*api.Pod)
|
||||
if len(pod.Spec.NodeName) == 0 {
|
||||
// No node assigned
|
||||
glog.V(5).Infof("updatePod %s/%s, no node", pod.Namespace, pod.Name)
|
||||
return
|
||||
}
|
||||
if oldPod, ok := oldObj.(*api.Pod); ok && oldPod != nil {
|
||||
if (pod.Spec.NodeName == oldPod.Spec.NodeName) && (pod.UID == oldPod.UID) {
|
||||
// Node and uid are unchanged, all object references in the pod spec are immutable
|
||||
glog.V(5).Infof("updatePod %s/%s, node unchanged", pod.Namespace, pod.Name)
|
||||
return
|
||||
}
|
||||
}
|
||||
glog.V(4).Infof("updatePod %s/%s for node %s", pod.Namespace, pod.Name, pod.Spec.NodeName)
|
||||
g.graph.AddPod(pod)
|
||||
}
|
||||
|
||||
func (g *graphPopulator) deletePod(obj interface{}) {
|
||||
if tombstone, ok := obj.(cache.DeletedFinalStateUnknown); ok {
|
||||
obj = tombstone.Obj
|
||||
}
|
||||
pod, ok := obj.(*api.Pod)
|
||||
if !ok {
|
||||
glog.Infof("unexpected type %T", obj)
|
||||
return
|
||||
}
|
||||
if len(pod.Spec.NodeName) == 0 {
|
||||
glog.V(5).Infof("deletePod %s/%s, no node", pod.Namespace, pod.Name)
|
||||
return
|
||||
}
|
||||
glog.V(4).Infof("deletePod %s/%s for node %s", pod.Namespace, pod.Name, pod.Spec.NodeName)
|
||||
g.graph.DeletePod(pod.Name, pod.Namespace)
|
||||
}
|
||||
|
||||
func (g *graphPopulator) addPV(obj interface{}) {
|
||||
g.updatePV(nil, obj)
|
||||
}
|
||||
|
||||
func (g *graphPopulator) updatePV(oldObj, obj interface{}) {
|
||||
pv := obj.(*api.PersistentVolume)
|
||||
// TODO: skip add if uid, pvc, and secrets are all identical between old and new
|
||||
g.graph.AddPV(pv)
|
||||
}
|
||||
|
||||
func (g *graphPopulator) deletePV(obj interface{}) {
|
||||
if tombstone, ok := obj.(cache.DeletedFinalStateUnknown); ok {
|
||||
obj = tombstone.Obj
|
||||
}
|
||||
pv, ok := obj.(*api.PersistentVolume)
|
||||
if !ok {
|
||||
glog.Infof("unexpected type %T", obj)
|
||||
return
|
||||
}
|
||||
g.graph.DeletePV(pv.Name)
|
||||
}
|
199
vendor/k8s.io/kubernetes/plugin/pkg/auth/authorizer/node/node_authorizer.go
generated
vendored
Normal file
199
vendor/k8s.io/kubernetes/plugin/pkg/auth/authorizer/node/node_authorizer.go
generated
vendored
Normal file
@ -0,0 +1,199 @@
|
||||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package node
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/golang/glog"
|
||||
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/apiserver/pkg/authorization/authorizer"
|
||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||
api "k8s.io/kubernetes/pkg/apis/core"
|
||||
rbacapi "k8s.io/kubernetes/pkg/apis/rbac"
|
||||
"k8s.io/kubernetes/pkg/auth/nodeidentifier"
|
||||
"k8s.io/kubernetes/pkg/features"
|
||||
"k8s.io/kubernetes/plugin/pkg/auth/authorizer/rbac"
|
||||
"k8s.io/kubernetes/third_party/forked/gonum/graph"
|
||||
"k8s.io/kubernetes/third_party/forked/gonum/graph/traverse"
|
||||
)
|
||||
|
||||
// NodeAuthorizer authorizes requests from kubelets, with the following logic:
|
||||
// 1. If a request is not from a node (NodeIdentity() returns isNode=false), reject
|
||||
// 2. If a specific node cannot be identified (NodeIdentity() returns nodeName=""), reject
|
||||
// 3. If a request is for a secret, configmap, persistent volume or persistent volume claim, reject unless the verb is get, and the requested object is related to the requesting node:
|
||||
// node <- pod
|
||||
// node <- pod <- secret
|
||||
// node <- pod <- configmap
|
||||
// node <- pod <- pvc
|
||||
// node <- pod <- pvc <- pv
|
||||
// node <- pod <- pvc <- pv <- secret
|
||||
// 4. For other resources, authorize all nodes uniformly using statically defined rules
|
||||
type NodeAuthorizer struct {
|
||||
graph *Graph
|
||||
identifier nodeidentifier.NodeIdentifier
|
||||
nodeRules []rbacapi.PolicyRule
|
||||
}
|
||||
|
||||
// NewAuthorizer returns a new node authorizer
|
||||
func NewAuthorizer(graph *Graph, identifier nodeidentifier.NodeIdentifier, rules []rbacapi.PolicyRule) authorizer.Authorizer {
|
||||
return &NodeAuthorizer{
|
||||
graph: graph,
|
||||
identifier: identifier,
|
||||
nodeRules: rules,
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
configMapResource = api.Resource("configmaps")
|
||||
secretResource = api.Resource("secrets")
|
||||
pvcResource = api.Resource("persistentvolumeclaims")
|
||||
pvResource = api.Resource("persistentvolumes")
|
||||
)
|
||||
|
||||
func (r *NodeAuthorizer) Authorize(attrs authorizer.Attributes) (authorizer.Decision, string, error) {
|
||||
nodeName, isNode := r.identifier.NodeIdentity(attrs.GetUser())
|
||||
if !isNode {
|
||||
// reject requests from non-nodes
|
||||
return authorizer.DecisionNoOpinion, "", nil
|
||||
}
|
||||
if len(nodeName) == 0 {
|
||||
// reject requests from unidentifiable nodes
|
||||
glog.V(2).Infof("NODE DENY: unknown node for user %q", attrs.GetUser().GetName())
|
||||
return authorizer.DecisionNoOpinion, fmt.Sprintf("unknown node for user %q", attrs.GetUser().GetName()), nil
|
||||
}
|
||||
|
||||
// subdivide access to specific resources
|
||||
if attrs.IsResourceRequest() {
|
||||
requestResource := schema.GroupResource{Group: attrs.GetAPIGroup(), Resource: attrs.GetResource()}
|
||||
switch requestResource {
|
||||
case secretResource:
|
||||
return r.authorizeGet(nodeName, secretVertexType, attrs)
|
||||
case configMapResource:
|
||||
return r.authorizeGet(nodeName, configMapVertexType, attrs)
|
||||
case pvcResource:
|
||||
if utilfeature.DefaultFeatureGate.Enabled(features.ExpandPersistentVolumes) {
|
||||
if attrs.GetSubresource() == "status" {
|
||||
return r.authorizeStatusUpdate(nodeName, pvcVertexType, attrs)
|
||||
}
|
||||
}
|
||||
return r.authorizeGet(nodeName, pvcVertexType, attrs)
|
||||
case pvResource:
|
||||
return r.authorizeGet(nodeName, pvVertexType, attrs)
|
||||
}
|
||||
}
|
||||
|
||||
// Access to other resources is not subdivided, so just evaluate against the statically defined node rules
|
||||
if rbac.RulesAllow(attrs, r.nodeRules...) {
|
||||
return authorizer.DecisionAllow, "", nil
|
||||
}
|
||||
return authorizer.DecisionNoOpinion, "", nil
|
||||
}
|
||||
|
||||
// authorizeStatusUpdate authorizes get/update/patch requests to status subresources of the specified type if they are related to the specified node
|
||||
func (r *NodeAuthorizer) authorizeStatusUpdate(nodeName string, startingType vertexType, attrs authorizer.Attributes) (authorizer.Decision, string, error) {
|
||||
switch attrs.GetVerb() {
|
||||
case "update", "patch":
|
||||
// ok
|
||||
default:
|
||||
glog.V(2).Infof("NODE DENY: %s %#v", nodeName, attrs)
|
||||
return authorizer.DecisionNoOpinion, "can only get/update/patch this type", nil
|
||||
}
|
||||
|
||||
if attrs.GetSubresource() != "status" {
|
||||
glog.V(2).Infof("NODE DENY: %s %#v", nodeName, attrs)
|
||||
return authorizer.DecisionNoOpinion, "can only update status subresource", nil
|
||||
}
|
||||
|
||||
return r.authorize(nodeName, startingType, attrs)
|
||||
}
|
||||
|
||||
// authorizeGet authorizes "get" requests to objects of the specified type if they are related to the specified node
|
||||
func (r *NodeAuthorizer) authorizeGet(nodeName string, startingType vertexType, attrs authorizer.Attributes) (authorizer.Decision, string, error) {
|
||||
if attrs.GetVerb() != "get" {
|
||||
glog.V(2).Infof("NODE DENY: %s %#v", nodeName, attrs)
|
||||
return authorizer.DecisionNoOpinion, "can only get individual resources of this type", nil
|
||||
}
|
||||
if len(attrs.GetSubresource()) > 0 {
|
||||
glog.V(2).Infof("NODE DENY: %s %#v", nodeName, attrs)
|
||||
return authorizer.DecisionNoOpinion, "cannot get subresource", nil
|
||||
}
|
||||
return r.authorize(nodeName, startingType, attrs)
|
||||
}
|
||||
|
||||
func (r *NodeAuthorizer) authorize(nodeName string, startingType vertexType, attrs authorizer.Attributes) (authorizer.Decision, string, error) {
|
||||
if len(attrs.GetName()) == 0 {
|
||||
glog.V(2).Infof("NODE DENY: %s %#v", nodeName, attrs)
|
||||
return authorizer.DecisionNoOpinion, "No Object name found", nil
|
||||
}
|
||||
|
||||
ok, err := r.hasPathFrom(nodeName, startingType, attrs.GetNamespace(), attrs.GetName())
|
||||
if err != nil {
|
||||
glog.V(2).Infof("NODE DENY: %v", err)
|
||||
return authorizer.DecisionNoOpinion, "no path found to object", nil
|
||||
}
|
||||
if !ok {
|
||||
glog.V(2).Infof("NODE DENY: %q %#v", nodeName, attrs)
|
||||
return authorizer.DecisionNoOpinion, "no path found to object", nil
|
||||
}
|
||||
return authorizer.DecisionAllow, "", nil
|
||||
}
|
||||
|
||||
// hasPathFrom returns true if there is a directed path from the specified type/namespace/name to the specified Node
|
||||
func (r *NodeAuthorizer) hasPathFrom(nodeName string, startingType vertexType, startingNamespace, startingName string) (bool, error) {
|
||||
r.graph.lock.RLock()
|
||||
defer r.graph.lock.RUnlock()
|
||||
|
||||
nodeVertex, exists := r.graph.getVertex_rlocked(nodeVertexType, "", nodeName)
|
||||
if !exists {
|
||||
return false, fmt.Errorf("unknown node %q cannot get %s %s/%s", nodeName, vertexTypes[startingType], startingNamespace, startingName)
|
||||
}
|
||||
|
||||
startingVertex, exists := r.graph.getVertex_rlocked(startingType, startingNamespace, startingName)
|
||||
if !exists {
|
||||
return false, fmt.Errorf("node %q cannot get unknown %s %s/%s", nodeName, vertexTypes[startingType], startingNamespace, startingName)
|
||||
}
|
||||
|
||||
found := false
|
||||
traversal := &traverse.VisitingDepthFirst{
|
||||
EdgeFilter: func(edge graph.Edge) bool {
|
||||
if destinationEdge, ok := edge.(*destinationEdge); ok {
|
||||
if destinationEdge.DestinationID() != nodeVertex.ID() {
|
||||
// Don't follow edges leading to other nodes
|
||||
return false
|
||||
}
|
||||
// We found an edge leading to the node we want
|
||||
found = true
|
||||
}
|
||||
// Visit this edge
|
||||
return true
|
||||
},
|
||||
}
|
||||
traversal.Walk(r.graph.graph, startingVertex, func(n graph.Node) bool {
|
||||
if n.ID() == nodeVertex.ID() {
|
||||
// We found the node we want
|
||||
found = true
|
||||
}
|
||||
// Stop visiting if we've found the node we want
|
||||
return found
|
||||
})
|
||||
if !found {
|
||||
return false, fmt.Errorf("node %q cannot get %s %s/%s, no path was found", nodeName, vertexTypes[startingType], startingNamespace, startingName)
|
||||
}
|
||||
return true, nil
|
||||
}
|
437
vendor/k8s.io/kubernetes/plugin/pkg/auth/authorizer/node/node_authorizer_test.go
generated
vendored
Normal file
437
vendor/k8s.io/kubernetes/plugin/pkg/auth/authorizer/node/node_authorizer_test.go
generated
vendored
Normal file
@ -0,0 +1,437 @@
|
||||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package node
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"runtime"
|
||||
"runtime/pprof"
|
||||
"testing"
|
||||
|
||||
"os"
|
||||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apiserver/pkg/authentication/user"
|
||||
"k8s.io/apiserver/pkg/authorization/authorizer"
|
||||
api "k8s.io/kubernetes/pkg/apis/core"
|
||||
"k8s.io/kubernetes/pkg/auth/nodeidentifier"
|
||||
"k8s.io/kubernetes/plugin/pkg/auth/authorizer/rbac/bootstrappolicy"
|
||||
)
|
||||
|
||||
func TestAuthorizer(t *testing.T) {
|
||||
g := NewGraph()
|
||||
|
||||
opts := sampleDataOpts{
|
||||
nodes: 2,
|
||||
namespaces: 2,
|
||||
podsPerNode: 2,
|
||||
sharedConfigMapsPerPod: 0,
|
||||
uniqueConfigMapsPerPod: 1,
|
||||
sharedSecretsPerPod: 1,
|
||||
uniqueSecretsPerPod: 1,
|
||||
sharedPVCsPerPod: 0,
|
||||
uniquePVCsPerPod: 1,
|
||||
}
|
||||
pods, pvs := generate(opts)
|
||||
populate(g, pods, pvs)
|
||||
|
||||
identifier := nodeidentifier.NewDefaultNodeIdentifier()
|
||||
authz := NewAuthorizer(g, identifier, bootstrappolicy.NodeRules())
|
||||
|
||||
node0 := &user.DefaultInfo{Name: "system:node:node0", Groups: []string{"system:nodes"}}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
attrs authorizer.AttributesRecord
|
||||
expect authorizer.Decision
|
||||
}{
|
||||
{
|
||||
name: "allowed configmap",
|
||||
attrs: authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "get", Resource: "configmaps", Name: "configmap0-pod0-node0", Namespace: "ns0"},
|
||||
expect: authorizer.DecisionAllow,
|
||||
},
|
||||
{
|
||||
name: "allowed secret via pod",
|
||||
attrs: authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "get", Resource: "secrets", Name: "secret0-pod0-node0", Namespace: "ns0"},
|
||||
expect: authorizer.DecisionAllow,
|
||||
},
|
||||
{
|
||||
name: "allowed shared secret via pod",
|
||||
attrs: authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "get", Resource: "secrets", Name: "secret0-shared", Namespace: "ns0"},
|
||||
expect: authorizer.DecisionAllow,
|
||||
},
|
||||
{
|
||||
name: "allowed shared secret via pvc",
|
||||
attrs: authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "get", Resource: "secrets", Name: "secret-pv0-pod0-node0-ns0", Namespace: "ns0"},
|
||||
expect: authorizer.DecisionAllow,
|
||||
},
|
||||
{
|
||||
name: "allowed pvc",
|
||||
attrs: authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "get", Resource: "persistentvolumeclaims", Name: "pvc0-pod0-node0", Namespace: "ns0"},
|
||||
expect: authorizer.DecisionAllow,
|
||||
},
|
||||
{
|
||||
name: "allowed pv",
|
||||
attrs: authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "get", Resource: "persistentvolumes", Name: "pv0-pod0-node0-ns0", Namespace: ""},
|
||||
expect: authorizer.DecisionAllow,
|
||||
},
|
||||
|
||||
{
|
||||
name: "disallowed configmap",
|
||||
attrs: authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "get", Resource: "configmaps", Name: "configmap0-pod0-node1", Namespace: "ns0"},
|
||||
expect: authorizer.DecisionNoOpinion,
|
||||
},
|
||||
{
|
||||
name: "disallowed secret via pod",
|
||||
attrs: authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "get", Resource: "secrets", Name: "secret0-pod0-node1", Namespace: "ns0"},
|
||||
expect: authorizer.DecisionNoOpinion,
|
||||
},
|
||||
{
|
||||
name: "disallowed shared secret via pvc",
|
||||
attrs: authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "get", Resource: "secrets", Name: "secret-pv0-pod0-node1-ns0", Namespace: "ns0"},
|
||||
expect: authorizer.DecisionNoOpinion,
|
||||
},
|
||||
{
|
||||
name: "disallowed pvc",
|
||||
attrs: authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "get", Resource: "persistentvolumeclaims", Name: "pvc0-pod0-node1", Namespace: "ns0"},
|
||||
expect: authorizer.DecisionNoOpinion,
|
||||
},
|
||||
{
|
||||
name: "disallowed pv",
|
||||
attrs: authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "get", Resource: "persistentvolumes", Name: "pv0-pod0-node1-ns0", Namespace: ""},
|
||||
expect: authorizer.DecisionNoOpinion,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
decision, _, _ := authz.Authorize(tc.attrs)
|
||||
if decision != tc.expect {
|
||||
t.Errorf("expected %v, got %v", tc.expect, decision)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestAuthorizerSharedResources(t *testing.T) {
|
||||
g := NewGraph()
|
||||
identifier := nodeidentifier.NewDefaultNodeIdentifier()
|
||||
authz := NewAuthorizer(g, identifier, bootstrappolicy.NodeRules())
|
||||
|
||||
node1 := &user.DefaultInfo{Name: "system:node:node1", Groups: []string{"system:nodes"}}
|
||||
node2 := &user.DefaultInfo{Name: "system:node:node2", Groups: []string{"system:nodes"}}
|
||||
node3 := &user.DefaultInfo{Name: "system:node:node3", Groups: []string{"system:nodes"}}
|
||||
|
||||
g.AddPod(&api.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "pod1-node1", Namespace: "ns1"},
|
||||
Spec: api.PodSpec{
|
||||
NodeName: "node1",
|
||||
Volumes: []api.Volume{
|
||||
{VolumeSource: api.VolumeSource{Secret: &api.SecretVolumeSource{SecretName: "node1-only"}}},
|
||||
{VolumeSource: api.VolumeSource{Secret: &api.SecretVolumeSource{SecretName: "node1-node2-only"}}},
|
||||
{VolumeSource: api.VolumeSource{Secret: &api.SecretVolumeSource{SecretName: "shared-all"}}},
|
||||
},
|
||||
},
|
||||
})
|
||||
g.AddPod(&api.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "pod2-node2", Namespace: "ns1"},
|
||||
Spec: api.PodSpec{
|
||||
NodeName: "node2",
|
||||
Volumes: []api.Volume{
|
||||
{VolumeSource: api.VolumeSource{Secret: &api.SecretVolumeSource{SecretName: "node1-node2-only"}}},
|
||||
{VolumeSource: api.VolumeSource{Secret: &api.SecretVolumeSource{SecretName: "shared-all"}}},
|
||||
},
|
||||
},
|
||||
})
|
||||
g.AddPod(&api.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "pod3-node3", Namespace: "ns1"},
|
||||
Spec: api.PodSpec{
|
||||
NodeName: "node3",
|
||||
Volumes: []api.Volume{
|
||||
{VolumeSource: api.VolumeSource{Secret: &api.SecretVolumeSource{SecretName: "shared-all"}}},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
testcases := []struct {
|
||||
User user.Info
|
||||
Secret string
|
||||
ExpectAllowed bool
|
||||
}{
|
||||
{User: node1, ExpectAllowed: true, Secret: "node1-only"},
|
||||
{User: node1, ExpectAllowed: true, Secret: "node1-node2-only"},
|
||||
{User: node1, ExpectAllowed: true, Secret: "shared-all"},
|
||||
|
||||
{User: node2, ExpectAllowed: false, Secret: "node1-only"},
|
||||
{User: node2, ExpectAllowed: true, Secret: "node1-node2-only"},
|
||||
{User: node2, ExpectAllowed: true, Secret: "shared-all"},
|
||||
|
||||
{User: node3, ExpectAllowed: false, Secret: "node1-only"},
|
||||
{User: node3, ExpectAllowed: false, Secret: "node1-node2-only"},
|
||||
{User: node3, ExpectAllowed: true, Secret: "shared-all"},
|
||||
}
|
||||
|
||||
for i, tc := range testcases {
|
||||
decision, _, err := authz.Authorize(authorizer.AttributesRecord{User: tc.User, ResourceRequest: true, Verb: "get", Resource: "secrets", Namespace: "ns1", Name: tc.Secret})
|
||||
if err != nil {
|
||||
t.Errorf("%d: unexpected error: %v", i, err)
|
||||
continue
|
||||
}
|
||||
if (decision == authorizer.DecisionAllow) != tc.ExpectAllowed {
|
||||
t.Errorf("%d: expected %v, got %v", i, tc.ExpectAllowed, decision)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type sampleDataOpts struct {
|
||||
nodes int
|
||||
|
||||
namespaces int
|
||||
|
||||
podsPerNode int
|
||||
|
||||
sharedConfigMapsPerPod int
|
||||
sharedSecretsPerPod int
|
||||
sharedPVCsPerPod int
|
||||
|
||||
uniqueSecretsPerPod int
|
||||
uniqueConfigMapsPerPod int
|
||||
uniquePVCsPerPod int
|
||||
}
|
||||
|
||||
func BenchmarkPopulationAllocation(b *testing.B) {
|
||||
opts := sampleDataOpts{
|
||||
nodes: 500,
|
||||
namespaces: 200,
|
||||
podsPerNode: 200,
|
||||
sharedConfigMapsPerPod: 0,
|
||||
uniqueConfigMapsPerPod: 1,
|
||||
sharedSecretsPerPod: 1,
|
||||
uniqueSecretsPerPod: 1,
|
||||
sharedPVCsPerPod: 0,
|
||||
uniquePVCsPerPod: 1,
|
||||
}
|
||||
|
||||
pods, pvs := generate(opts)
|
||||
b.ResetTimer()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
g := NewGraph()
|
||||
populate(g, pods, pvs)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkPopulationRetention(b *testing.B) {
|
||||
|
||||
// Run with:
|
||||
// go test ./plugin/pkg/auth/authorizer/node -benchmem -bench . -run None -v -o node.test -timeout 300m
|
||||
|
||||
// Evaluate retained memory with:
|
||||
// go tool pprof --inuse_space node.test plugin/pkg/auth/authorizer/node/BenchmarkPopulationRetention.profile
|
||||
// list populate
|
||||
|
||||
opts := sampleDataOpts{
|
||||
nodes: 500,
|
||||
namespaces: 200,
|
||||
podsPerNode: 200,
|
||||
sharedConfigMapsPerPod: 0,
|
||||
uniqueConfigMapsPerPod: 1,
|
||||
sharedSecretsPerPod: 1,
|
||||
uniqueSecretsPerPod: 1,
|
||||
sharedPVCsPerPod: 0,
|
||||
uniquePVCsPerPod: 1,
|
||||
}
|
||||
|
||||
pods, pvs := generate(opts)
|
||||
// Garbage collect before the first iteration
|
||||
runtime.GC()
|
||||
b.ResetTimer()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
g := NewGraph()
|
||||
populate(g, pods, pvs)
|
||||
|
||||
if i == 0 {
|
||||
f, _ := os.Create("BenchmarkPopulationRetention.profile")
|
||||
runtime.GC()
|
||||
pprof.WriteHeapProfile(f)
|
||||
f.Close()
|
||||
// reference the graph to keep it from getting garbage collected
|
||||
_ = fmt.Sprintf("%T\n", g)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkAuthorization(b *testing.B) {
|
||||
g := NewGraph()
|
||||
|
||||
opts := sampleDataOpts{
|
||||
nodes: 500,
|
||||
namespaces: 200,
|
||||
podsPerNode: 200,
|
||||
sharedConfigMapsPerPod: 0,
|
||||
uniqueConfigMapsPerPod: 1,
|
||||
sharedSecretsPerPod: 1,
|
||||
uniqueSecretsPerPod: 1,
|
||||
sharedPVCsPerPod: 0,
|
||||
uniquePVCsPerPod: 1,
|
||||
}
|
||||
pods, pvs := generate(opts)
|
||||
populate(g, pods, pvs)
|
||||
|
||||
identifier := nodeidentifier.NewDefaultNodeIdentifier()
|
||||
authz := NewAuthorizer(g, identifier, bootstrappolicy.NodeRules())
|
||||
|
||||
node0 := &user.DefaultInfo{Name: "system:node:node0", Groups: []string{"system:nodes"}}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
attrs authorizer.AttributesRecord
|
||||
expect authorizer.Decision
|
||||
}{
|
||||
{
|
||||
name: "allowed configmap",
|
||||
attrs: authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "get", Resource: "configmaps", Name: "configmap0-pod0-node0", Namespace: "ns0"},
|
||||
expect: authorizer.DecisionAllow,
|
||||
},
|
||||
{
|
||||
name: "allowed secret via pod",
|
||||
attrs: authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "get", Resource: "secrets", Name: "secret0-pod0-node0", Namespace: "ns0"},
|
||||
expect: authorizer.DecisionAllow,
|
||||
},
|
||||
{
|
||||
name: "allowed shared secret via pod",
|
||||
attrs: authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "get", Resource: "secrets", Name: "secret0-shared", Namespace: "ns0"},
|
||||
expect: authorizer.DecisionAllow,
|
||||
},
|
||||
{
|
||||
name: "disallowed configmap",
|
||||
attrs: authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "get", Resource: "configmaps", Name: "configmap0-pod0-node1", Namespace: "ns0"},
|
||||
expect: authorizer.DecisionNoOpinion,
|
||||
},
|
||||
{
|
||||
name: "disallowed secret via pod",
|
||||
attrs: authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "get", Resource: "secrets", Name: "secret0-pod0-node1", Namespace: "ns0"},
|
||||
expect: authorizer.DecisionNoOpinion,
|
||||
},
|
||||
{
|
||||
name: "disallowed shared secret via pvc",
|
||||
attrs: authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "get", Resource: "secrets", Name: "secret-pv0-pod0-node1-ns0", Namespace: "ns0"},
|
||||
expect: authorizer.DecisionNoOpinion,
|
||||
},
|
||||
{
|
||||
name: "disallowed pvc",
|
||||
attrs: authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "get", Resource: "persistentvolumeclaims", Name: "pvc0-pod0-node1", Namespace: "ns0"},
|
||||
expect: authorizer.DecisionNoOpinion,
|
||||
},
|
||||
{
|
||||
name: "disallowed pv",
|
||||
attrs: authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "get", Resource: "persistentvolumes", Name: "pv0-pod0-node1-ns0", Namespace: ""},
|
||||
expect: authorizer.DecisionNoOpinion,
|
||||
},
|
||||
}
|
||||
|
||||
b.ResetTimer()
|
||||
for _, tc := range tests {
|
||||
b.Run(tc.name, func(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
decision, _, _ := authz.Authorize(tc.attrs)
|
||||
if decision != tc.expect {
|
||||
b.Errorf("expected %v, got %v", tc.expect, decision)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func populate(graph *Graph, pods []*api.Pod, pvs []*api.PersistentVolume) {
|
||||
p := &graphPopulator{}
|
||||
p.graph = graph
|
||||
for _, pod := range pods {
|
||||
p.addPod(pod)
|
||||
}
|
||||
for _, pv := range pvs {
|
||||
p.addPV(pv)
|
||||
}
|
||||
}
|
||||
|
||||
// generate creates sample pods and persistent volumes based on the provided options.
|
||||
// the secret/configmap/pvc/node references in the pod and pv objects are named to indicate the connections between the objects.
|
||||
// for example, secret0-pod0-node0 is a secret referenced by pod0 which is bound to node0.
|
||||
// when populated into the graph, the node authorizer should allow node0 to access that secret, but not node1.
|
||||
func generate(opts sampleDataOpts) ([]*api.Pod, []*api.PersistentVolume) {
|
||||
pods := make([]*api.Pod, 0, opts.nodes*opts.podsPerNode)
|
||||
pvs := make([]*api.PersistentVolume, 0, (opts.nodes*opts.podsPerNode*opts.uniquePVCsPerPod)+(opts.sharedPVCsPerPod*opts.namespaces))
|
||||
|
||||
for n := 0; n < opts.nodes; n++ {
|
||||
nodeName := fmt.Sprintf("node%d", n)
|
||||
for p := 0; p < opts.podsPerNode; p++ {
|
||||
pod := &api.Pod{}
|
||||
pod.Namespace = fmt.Sprintf("ns%d", p%opts.namespaces)
|
||||
pod.Name = fmt.Sprintf("pod%d-%s", p, nodeName)
|
||||
pod.Spec.NodeName = nodeName
|
||||
|
||||
for i := 0; i < opts.uniqueSecretsPerPod; i++ {
|
||||
pod.Spec.Volumes = append(pod.Spec.Volumes, api.Volume{VolumeSource: api.VolumeSource{
|
||||
Secret: &api.SecretVolumeSource{SecretName: fmt.Sprintf("secret%d-%s", i, pod.Name)},
|
||||
}})
|
||||
}
|
||||
for i := 0; i < opts.sharedSecretsPerPod; i++ {
|
||||
pod.Spec.Volumes = append(pod.Spec.Volumes, api.Volume{VolumeSource: api.VolumeSource{
|
||||
Secret: &api.SecretVolumeSource{SecretName: fmt.Sprintf("secret%d-shared", i)},
|
||||
}})
|
||||
}
|
||||
|
||||
for i := 0; i < opts.uniqueConfigMapsPerPod; i++ {
|
||||
pod.Spec.Volumes = append(pod.Spec.Volumes, api.Volume{VolumeSource: api.VolumeSource{
|
||||
ConfigMap: &api.ConfigMapVolumeSource{LocalObjectReference: api.LocalObjectReference{Name: fmt.Sprintf("configmap%d-%s", i, pod.Name)}},
|
||||
}})
|
||||
}
|
||||
for i := 0; i < opts.sharedConfigMapsPerPod; i++ {
|
||||
pod.Spec.Volumes = append(pod.Spec.Volumes, api.Volume{VolumeSource: api.VolumeSource{
|
||||
ConfigMap: &api.ConfigMapVolumeSource{LocalObjectReference: api.LocalObjectReference{Name: fmt.Sprintf("configmap%d-shared", i)}},
|
||||
}})
|
||||
}
|
||||
|
||||
for i := 0; i < opts.uniquePVCsPerPod; i++ {
|
||||
pv := &api.PersistentVolume{}
|
||||
pv.Name = fmt.Sprintf("pv%d-%s-%s", i, pod.Name, pod.Namespace)
|
||||
pv.Spec.FlexVolume = &api.FlexVolumeSource{SecretRef: &api.LocalObjectReference{Name: fmt.Sprintf("secret-%s", pv.Name)}}
|
||||
pv.Spec.ClaimRef = &api.ObjectReference{Name: fmt.Sprintf("pvc%d-%s", i, pod.Name), Namespace: pod.Namespace}
|
||||
pvs = append(pvs, pv)
|
||||
|
||||
pod.Spec.Volumes = append(pod.Spec.Volumes, api.Volume{VolumeSource: api.VolumeSource{
|
||||
PersistentVolumeClaim: &api.PersistentVolumeClaimVolumeSource{ClaimName: pv.Spec.ClaimRef.Name},
|
||||
}})
|
||||
}
|
||||
for i := 0; i < opts.sharedPVCsPerPod; i++ {
|
||||
pv := &api.PersistentVolume{}
|
||||
pv.Name = fmt.Sprintf("pv%d-shared-%s", i, pod.Namespace)
|
||||
pv.Spec.FlexVolume = &api.FlexVolumeSource{SecretRef: &api.LocalObjectReference{Name: fmt.Sprintf("secret-%s", pv.Name)}}
|
||||
pv.Spec.ClaimRef = &api.ObjectReference{Name: fmt.Sprintf("pvc%d-shared", i), Namespace: pod.Namespace}
|
||||
pvs = append(pvs, pv)
|
||||
|
||||
pod.Spec.Volumes = append(pod.Spec.Volumes, api.Volume{VolumeSource: api.VolumeSource{
|
||||
PersistentVolumeClaim: &api.PersistentVolumeClaimVolumeSource{ClaimName: pv.Spec.ClaimRef.Name},
|
||||
}})
|
||||
}
|
||||
|
||||
pods = append(pods, pod)
|
||||
}
|
||||
}
|
||||
return pods, pvs
|
||||
}
|
60
vendor/k8s.io/kubernetes/plugin/pkg/auth/authorizer/rbac/BUILD
generated
vendored
Normal file
60
vendor/k8s.io/kubernetes/plugin/pkg/auth/authorizer/rbac/BUILD
generated
vendored
Normal file
@ -0,0 +1,60 @@
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_library",
|
||||
"go_test",
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"rbac.go",
|
||||
"subject_locator.go",
|
||||
],
|
||||
importpath = "k8s.io/kubernetes/plugin/pkg/auth/authorizer/rbac",
|
||||
deps = [
|
||||
"//pkg/apis/rbac:go_default_library",
|
||||
"//pkg/client/listers/rbac/internalversion:go_default_library",
|
||||
"//pkg/registry/rbac/validation:go_default_library",
|
||||
"//vendor/github.com/golang/glog:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/labels:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/errors:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/authentication/user:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/authorization/authorizer:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = [
|
||||
"rbac_test.go",
|
||||
"subject_locator_test.go",
|
||||
],
|
||||
importpath = "k8s.io/kubernetes/plugin/pkg/auth/authorizer/rbac",
|
||||
library = ":go_default_library",
|
||||
deps = [
|
||||
"//pkg/apis/rbac:go_default_library",
|
||||
"//pkg/registry/rbac/validation:go_default_library",
|
||||
"//plugin/pkg/auth/authorizer/rbac/bootstrappolicy:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/authentication/user:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/authorization/authorizer:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [
|
||||
":package-srcs",
|
||||
"//plugin/pkg/auth/authorizer/rbac/bootstrappolicy:all-srcs",
|
||||
],
|
||||
tags = ["automanaged"],
|
||||
)
|
77
vendor/k8s.io/kubernetes/plugin/pkg/auth/authorizer/rbac/bootstrappolicy/BUILD
generated
vendored
Normal file
77
vendor/k8s.io/kubernetes/plugin/pkg/auth/authorizer/rbac/bootstrappolicy/BUILD
generated
vendored
Normal file
@ -0,0 +1,77 @@
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_library",
|
||||
"go_test",
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"controller_policy.go",
|
||||
"namespace_policy.go",
|
||||
"policy.go",
|
||||
],
|
||||
importpath = "k8s.io/kubernetes/plugin/pkg/auth/authorizer/rbac/bootstrappolicy",
|
||||
deps = [
|
||||
"//pkg/apis/rbac:go_default_library",
|
||||
"//pkg/features:go_default_library",
|
||||
"//vendor/github.com/golang/glog:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/api/meta:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/authentication/user:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/util/feature:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_xtest",
|
||||
srcs = ["policy_test.go"],
|
||||
data = glob([
|
||||
"testdata/*",
|
||||
]),
|
||||
importpath = "k8s.io/kubernetes/plugin/pkg/auth/authorizer/rbac/bootstrappolicy_test",
|
||||
deps = [
|
||||
":go_default_library",
|
||||
"//pkg/api/legacyscheme:go_default_library",
|
||||
"//pkg/apis/core:go_default_library",
|
||||
"//pkg/apis/core/install:go_default_library",
|
||||
"//pkg/apis/rbac:go_default_library",
|
||||
"//pkg/apis/rbac/install:go_default_library",
|
||||
"//pkg/registry/rbac/validation:go_default_library",
|
||||
"//vendor/github.com/ghodss/yaml:go_default_library",
|
||||
"//vendor/k8s.io/api/core/v1:go_default_library",
|
||||
"//vendor/k8s.io/api/rbac/v1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/api/meta:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/diff:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/sets:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = ["controller_policy_test.go"],
|
||||
data = glob(["testdata/**"]),
|
||||
importpath = "k8s.io/kubernetes/plugin/pkg/auth/authorizer/rbac/bootstrappolicy",
|
||||
library = ":go_default_library",
|
||||
deps = [
|
||||
"//vendor/k8s.io/apimachinery/pkg/api/meta:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/sets:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
tags = ["automanaged"],
|
||||
)
|
351
vendor/k8s.io/kubernetes/plugin/pkg/auth/authorizer/rbac/bootstrappolicy/controller_policy.go
generated
vendored
Normal file
351
vendor/k8s.io/kubernetes/plugin/pkg/auth/authorizer/rbac/bootstrappolicy/controller_policy.go
generated
vendored
Normal file
@ -0,0 +1,351 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package bootstrappolicy
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/golang/glog"
|
||||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||
rbac "k8s.io/kubernetes/pkg/apis/rbac"
|
||||
"k8s.io/kubernetes/pkg/features"
|
||||
)
|
||||
|
||||
const saRolePrefix = "system:controller:"
|
||||
|
||||
func addControllerRole(controllerRoles *[]rbac.ClusterRole, controllerRoleBindings *[]rbac.ClusterRoleBinding, role rbac.ClusterRole) {
|
||||
if !strings.HasPrefix(role.Name, saRolePrefix) {
|
||||
glog.Fatalf(`role %q must start with %q`, role.Name, saRolePrefix)
|
||||
}
|
||||
|
||||
for _, existingRole := range *controllerRoles {
|
||||
if role.Name == existingRole.Name {
|
||||
glog.Fatalf("role %q was already registered", role.Name)
|
||||
}
|
||||
}
|
||||
|
||||
*controllerRoles = append(*controllerRoles, role)
|
||||
addClusterRoleLabel(*controllerRoles)
|
||||
|
||||
*controllerRoleBindings = append(*controllerRoleBindings,
|
||||
rbac.NewClusterBinding(role.Name).SAs("kube-system", role.Name[len(saRolePrefix):]).BindingOrDie())
|
||||
addClusterRoleBindingLabel(*controllerRoleBindings)
|
||||
}
|
||||
|
||||
func eventsRule() rbac.PolicyRule {
|
||||
return rbac.NewRule("create", "update", "patch").Groups(legacyGroup).Resources("events").RuleOrDie()
|
||||
}
|
||||
|
||||
func buildControllerRoles() ([]rbac.ClusterRole, []rbac.ClusterRoleBinding) {
|
||||
// controllerRoles is a slice of roles used for controllers
|
||||
controllerRoles := []rbac.ClusterRole{}
|
||||
// controllerRoleBindings is a slice of roles used for controllers
|
||||
controllerRoleBindings := []rbac.ClusterRoleBinding{}
|
||||
|
||||
addControllerRole(&controllerRoles, &controllerRoleBindings, func() rbac.ClusterRole {
|
||||
role := rbac.ClusterRole{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: saRolePrefix + "attachdetach-controller"},
|
||||
Rules: []rbac.PolicyRule{
|
||||
rbac.NewRule("list", "watch").Groups(legacyGroup).Resources("persistentvolumes", "persistentvolumeclaims").RuleOrDie(),
|
||||
rbac.NewRule("get", "list", "watch").Groups(legacyGroup).Resources("nodes").RuleOrDie(),
|
||||
rbac.NewRule("patch", "update").Groups(legacyGroup).Resources("nodes/status").RuleOrDie(),
|
||||
rbac.NewRule("list", "watch").Groups(legacyGroup).Resources("pods").RuleOrDie(),
|
||||
eventsRule(),
|
||||
},
|
||||
}
|
||||
|
||||
if utilfeature.DefaultFeatureGate.Enabled(features.CSIPersistentVolume) {
|
||||
role.Rules = append(role.Rules, rbac.NewRule("get", "create", "delete", "list", "watch").Groups(storageGroup).Resources("volumeattachments").RuleOrDie())
|
||||
}
|
||||
|
||||
return role
|
||||
}())
|
||||
|
||||
addControllerRole(&controllerRoles, &controllerRoleBindings, rbac.ClusterRole{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: saRolePrefix + "clusterrole-aggregation-controller"},
|
||||
Rules: []rbac.PolicyRule{
|
||||
// this controller must have full permissions to allow it to mutate any role in any way
|
||||
rbac.NewRule("*").Groups("*").Resources("*").RuleOrDie(),
|
||||
rbac.NewRule("*").URLs("*").RuleOrDie(),
|
||||
},
|
||||
})
|
||||
addControllerRole(&controllerRoles, &controllerRoleBindings, rbac.ClusterRole{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: saRolePrefix + "cronjob-controller"},
|
||||
Rules: []rbac.PolicyRule{
|
||||
rbac.NewRule("get", "list", "watch", "update").Groups(batchGroup).Resources("cronjobs").RuleOrDie(),
|
||||
rbac.NewRule("get", "list", "watch", "create", "update", "delete", "patch").Groups(batchGroup).Resources("jobs").RuleOrDie(),
|
||||
rbac.NewRule("update").Groups(batchGroup).Resources("cronjobs/status").RuleOrDie(),
|
||||
rbac.NewRule("update").Groups(batchGroup).Resources("cronjobs/finalizers").RuleOrDie(),
|
||||
rbac.NewRule("list", "delete").Groups(legacyGroup).Resources("pods").RuleOrDie(),
|
||||
eventsRule(),
|
||||
},
|
||||
})
|
||||
addControllerRole(&controllerRoles, &controllerRoleBindings, rbac.ClusterRole{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: saRolePrefix + "daemon-set-controller"},
|
||||
Rules: []rbac.PolicyRule{
|
||||
rbac.NewRule("get", "list", "watch").Groups(extensionsGroup, appsGroup).Resources("daemonsets").RuleOrDie(),
|
||||
rbac.NewRule("update").Groups(extensionsGroup, appsGroup).Resources("daemonsets/status").RuleOrDie(),
|
||||
rbac.NewRule("update").Groups(extensionsGroup, appsGroup).Resources("daemonsets/finalizers").RuleOrDie(),
|
||||
rbac.NewRule("list", "watch").Groups(legacyGroup).Resources("nodes").RuleOrDie(),
|
||||
rbac.NewRule("list", "watch", "create", "delete", "patch").Groups(legacyGroup).Resources("pods").RuleOrDie(),
|
||||
rbac.NewRule("create").Groups(legacyGroup).Resources("pods/binding").RuleOrDie(),
|
||||
rbac.NewRule("list", "watch", "create", "delete", "update", "patch").Groups(appsGroup).Resources("controllerrevisions").RuleOrDie(),
|
||||
eventsRule(),
|
||||
},
|
||||
})
|
||||
addControllerRole(&controllerRoles, &controllerRoleBindings, rbac.ClusterRole{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: saRolePrefix + "deployment-controller"},
|
||||
Rules: []rbac.PolicyRule{
|
||||
rbac.NewRule("get", "list", "watch", "update").Groups(extensionsGroup, appsGroup).Resources("deployments").RuleOrDie(),
|
||||
rbac.NewRule("update").Groups(extensionsGroup, appsGroup).Resources("deployments/status").RuleOrDie(),
|
||||
rbac.NewRule("update").Groups(extensionsGroup, appsGroup).Resources("deployments/finalizers").RuleOrDie(),
|
||||
rbac.NewRule("get", "list", "watch", "create", "update", "patch", "delete").Groups(appsGroup, extensionsGroup).Resources("replicasets").RuleOrDie(),
|
||||
// TODO: remove "update" once
|
||||
// https://github.com/kubernetes/kubernetes/issues/36897 is resolved.
|
||||
rbac.NewRule("get", "list", "watch", "update").Groups(legacyGroup).Resources("pods").RuleOrDie(),
|
||||
eventsRule(),
|
||||
},
|
||||
})
|
||||
addControllerRole(&controllerRoles, &controllerRoleBindings, rbac.ClusterRole{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: saRolePrefix + "disruption-controller"},
|
||||
Rules: []rbac.PolicyRule{
|
||||
rbac.NewRule("get", "list", "watch").Groups(extensionsGroup, appsGroup).Resources("deployments").RuleOrDie(),
|
||||
rbac.NewRule("get", "list", "watch").Groups(appsGroup, extensionsGroup).Resources("replicasets").RuleOrDie(),
|
||||
rbac.NewRule("get", "list", "watch").Groups(legacyGroup).Resources("replicationcontrollers").RuleOrDie(),
|
||||
rbac.NewRule("get", "list", "watch").Groups(policyGroup).Resources("poddisruptionbudgets").RuleOrDie(),
|
||||
rbac.NewRule("get", "list", "watch").Groups(appsGroup).Resources("statefulsets").RuleOrDie(),
|
||||
rbac.NewRule("update").Groups(policyGroup).Resources("poddisruptionbudgets/status").RuleOrDie(),
|
||||
eventsRule(),
|
||||
},
|
||||
})
|
||||
addControllerRole(&controllerRoles, &controllerRoleBindings, rbac.ClusterRole{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: saRolePrefix + "endpoint-controller"},
|
||||
Rules: []rbac.PolicyRule{
|
||||
rbac.NewRule("get", "list", "watch").Groups(legacyGroup).Resources("services", "pods").RuleOrDie(),
|
||||
rbac.NewRule("get", "list", "create", "update", "delete").Groups(legacyGroup).Resources("endpoints").RuleOrDie(),
|
||||
rbac.NewRule("create").Groups(legacyGroup).Resources("endpoints/restricted").RuleOrDie(),
|
||||
eventsRule(),
|
||||
},
|
||||
})
|
||||
|
||||
if utilfeature.DefaultFeatureGate.Enabled(features.ExpandPersistentVolumes) {
|
||||
addControllerRole(&controllerRoles, &controllerRoleBindings, rbac.ClusterRole{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: saRolePrefix + "expand-controller"},
|
||||
Rules: []rbac.PolicyRule{
|
||||
rbac.NewRule("get", "list", "watch", "update", "patch").Groups(legacyGroup).Resources("persistentvolumes").RuleOrDie(),
|
||||
rbac.NewRule("update", "patch").Groups(legacyGroup).Resources("persistentvolumeclaims/status").RuleOrDie(),
|
||||
rbac.NewRule("get", "list", "watch").Groups(legacyGroup).Resources("persistentvolumeclaims").RuleOrDie(),
|
||||
// glusterfs
|
||||
rbac.NewRule("get", "list", "watch").Groups(storageGroup).Resources("storageclasses").RuleOrDie(),
|
||||
rbac.NewRule("get").Groups(legacyGroup).Resources("services", "endpoints").RuleOrDie(),
|
||||
rbac.NewRule("get").Groups(legacyGroup).Resources("secrets").RuleOrDie(),
|
||||
eventsRule(),
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
addControllerRole(&controllerRoles, &controllerRoleBindings, rbac.ClusterRole{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: saRolePrefix + "generic-garbage-collector"},
|
||||
Rules: []rbac.PolicyRule{
|
||||
// the GC controller needs to run list/watches, selective gets, and updates against any resource
|
||||
rbac.NewRule("get", "list", "watch", "patch", "update", "delete").Groups("*").Resources("*").RuleOrDie(),
|
||||
eventsRule(),
|
||||
},
|
||||
})
|
||||
addControllerRole(&controllerRoles, &controllerRoleBindings, rbac.ClusterRole{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: saRolePrefix + "horizontal-pod-autoscaler"},
|
||||
Rules: []rbac.PolicyRule{
|
||||
rbac.NewRule("get", "list", "watch").Groups(autoscalingGroup).Resources("horizontalpodautoscalers").RuleOrDie(),
|
||||
rbac.NewRule("update").Groups(autoscalingGroup).Resources("horizontalpodautoscalers/status").RuleOrDie(),
|
||||
rbac.NewRule("get", "update").Groups("*").Resources("*/scale").RuleOrDie(),
|
||||
rbac.NewRule("list").Groups(legacyGroup).Resources("pods").RuleOrDie(),
|
||||
// TODO: restrict this to the appropriate namespace
|
||||
rbac.NewRule("get").Groups(legacyGroup).Resources("services/proxy").Names("https:heapster:", "http:heapster:").RuleOrDie(),
|
||||
// allow listing resource metrics and custom metrics
|
||||
rbac.NewRule("list").Groups(resMetricsGroup).Resources("pods").RuleOrDie(),
|
||||
rbac.NewRule("get", "list").Groups(customMetricsGroup).Resources("*").RuleOrDie(),
|
||||
eventsRule(),
|
||||
},
|
||||
})
|
||||
addControllerRole(&controllerRoles, &controllerRoleBindings, rbac.ClusterRole{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: saRolePrefix + "job-controller"},
|
||||
Rules: []rbac.PolicyRule{
|
||||
rbac.NewRule("get", "list", "watch", "update").Groups(batchGroup).Resources("jobs").RuleOrDie(),
|
||||
rbac.NewRule("update").Groups(batchGroup).Resources("jobs/status").RuleOrDie(),
|
||||
rbac.NewRule("update").Groups(batchGroup).Resources("jobs/finalizers").RuleOrDie(),
|
||||
rbac.NewRule("list", "watch", "create", "delete", "patch").Groups(legacyGroup).Resources("pods").RuleOrDie(),
|
||||
eventsRule(),
|
||||
},
|
||||
})
|
||||
addControllerRole(&controllerRoles, &controllerRoleBindings, rbac.ClusterRole{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: saRolePrefix + "namespace-controller"},
|
||||
Rules: []rbac.PolicyRule{
|
||||
rbac.NewRule("get", "list", "watch", "delete").Groups(legacyGroup).Resources("namespaces").RuleOrDie(),
|
||||
rbac.NewRule("update").Groups(legacyGroup).Resources("namespaces/finalize", "namespaces/status").RuleOrDie(),
|
||||
rbac.NewRule("get", "list", "delete", "deletecollection").Groups("*").Resources("*").RuleOrDie(),
|
||||
},
|
||||
})
|
||||
addControllerRole(&controllerRoles, &controllerRoleBindings, rbac.ClusterRole{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: saRolePrefix + "node-controller"},
|
||||
Rules: []rbac.PolicyRule{
|
||||
rbac.NewRule("get", "list", "update", "delete", "patch").Groups(legacyGroup).Resources("nodes").RuleOrDie(),
|
||||
rbac.NewRule("patch", "update").Groups(legacyGroup).Resources("nodes/status").RuleOrDie(),
|
||||
// used for pod eviction
|
||||
rbac.NewRule("update").Groups(legacyGroup).Resources("pods/status").RuleOrDie(),
|
||||
rbac.NewRule("list", "delete").Groups(legacyGroup).Resources("pods").RuleOrDie(),
|
||||
eventsRule(),
|
||||
},
|
||||
})
|
||||
addControllerRole(&controllerRoles, &controllerRoleBindings, rbac.ClusterRole{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: saRolePrefix + "persistent-volume-binder"},
|
||||
Rules: []rbac.PolicyRule{
|
||||
rbac.NewRule("get", "list", "watch", "update", "create", "delete").Groups(legacyGroup).Resources("persistentvolumes").RuleOrDie(),
|
||||
rbac.NewRule("update").Groups(legacyGroup).Resources("persistentvolumes/status").RuleOrDie(),
|
||||
rbac.NewRule("get", "list", "watch", "update").Groups(legacyGroup).Resources("persistentvolumeclaims").RuleOrDie(),
|
||||
rbac.NewRule("update").Groups(legacyGroup).Resources("persistentvolumeclaims/status").RuleOrDie(),
|
||||
rbac.NewRule("list", "watch", "get", "create", "delete").Groups(legacyGroup).Resources("pods").RuleOrDie(),
|
||||
|
||||
// glusterfs
|
||||
rbac.NewRule("get", "list", "watch").Groups(storageGroup).Resources("storageclasses").RuleOrDie(),
|
||||
rbac.NewRule("get", "create", "delete").Groups(legacyGroup).Resources("services", "endpoints").RuleOrDie(),
|
||||
rbac.NewRule("get").Groups(legacyGroup).Resources("secrets").RuleOrDie(),
|
||||
// openstack
|
||||
rbac.NewRule("get", "list").Groups(legacyGroup).Resources("nodes").RuleOrDie(),
|
||||
|
||||
// recyclerClient.WatchPod
|
||||
rbac.NewRule("watch").Groups(legacyGroup).Resources("events").RuleOrDie(),
|
||||
|
||||
eventsRule(),
|
||||
},
|
||||
})
|
||||
addControllerRole(&controllerRoles, &controllerRoleBindings, rbac.ClusterRole{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: saRolePrefix + "pod-garbage-collector"},
|
||||
Rules: []rbac.PolicyRule{
|
||||
rbac.NewRule("list", "watch", "delete").Groups(legacyGroup).Resources("pods").RuleOrDie(),
|
||||
rbac.NewRule("list").Groups(legacyGroup).Resources("nodes").RuleOrDie(),
|
||||
},
|
||||
})
|
||||
addControllerRole(&controllerRoles, &controllerRoleBindings, rbac.ClusterRole{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: saRolePrefix + "replicaset-controller"},
|
||||
Rules: []rbac.PolicyRule{
|
||||
rbac.NewRule("get", "list", "watch", "update").Groups(appsGroup, extensionsGroup).Resources("replicasets").RuleOrDie(),
|
||||
rbac.NewRule("update").Groups(appsGroup, extensionsGroup).Resources("replicasets/status").RuleOrDie(),
|
||||
rbac.NewRule("update").Groups(appsGroup, extensionsGroup).Resources("replicasets/finalizers").RuleOrDie(),
|
||||
rbac.NewRule("list", "watch", "patch", "create", "delete").Groups(legacyGroup).Resources("pods").RuleOrDie(),
|
||||
eventsRule(),
|
||||
},
|
||||
})
|
||||
addControllerRole(&controllerRoles, &controllerRoleBindings, rbac.ClusterRole{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: saRolePrefix + "replication-controller"},
|
||||
Rules: []rbac.PolicyRule{
|
||||
// 1.0 controllers needed get, update, so without these old controllers break on new servers
|
||||
rbac.NewRule("get", "list", "watch", "update").Groups(legacyGroup).Resources("replicationcontrollers").RuleOrDie(),
|
||||
rbac.NewRule("update").Groups(legacyGroup).Resources("replicationcontrollers/status").RuleOrDie(),
|
||||
rbac.NewRule("update").Groups(legacyGroup).Resources("replicationcontrollers/finalizers").RuleOrDie(),
|
||||
rbac.NewRule("list", "watch", "patch", "create", "delete").Groups(legacyGroup).Resources("pods").RuleOrDie(),
|
||||
eventsRule(),
|
||||
},
|
||||
})
|
||||
addControllerRole(&controllerRoles, &controllerRoleBindings, rbac.ClusterRole{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: saRolePrefix + "resourcequota-controller"},
|
||||
Rules: []rbac.PolicyRule{
|
||||
// quota can count quota on anything for reconcilation, so it needs full viewing powers
|
||||
rbac.NewRule("list", "watch").Groups("*").Resources("*").RuleOrDie(),
|
||||
rbac.NewRule("update").Groups(legacyGroup).Resources("resourcequotas/status").RuleOrDie(),
|
||||
eventsRule(),
|
||||
},
|
||||
})
|
||||
addControllerRole(&controllerRoles, &controllerRoleBindings, rbac.ClusterRole{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: saRolePrefix + "route-controller"},
|
||||
Rules: []rbac.PolicyRule{
|
||||
rbac.NewRule("list", "watch").Groups(legacyGroup).Resources("nodes").RuleOrDie(),
|
||||
rbac.NewRule("patch").Groups(legacyGroup).Resources("nodes/status").RuleOrDie(),
|
||||
eventsRule(),
|
||||
},
|
||||
})
|
||||
addControllerRole(&controllerRoles, &controllerRoleBindings, rbac.ClusterRole{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: saRolePrefix + "service-account-controller"},
|
||||
Rules: []rbac.PolicyRule{
|
||||
rbac.NewRule("create").Groups(legacyGroup).Resources("serviceaccounts").RuleOrDie(),
|
||||
eventsRule(),
|
||||
},
|
||||
})
|
||||
addControllerRole(&controllerRoles, &controllerRoleBindings, rbac.ClusterRole{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: saRolePrefix + "service-controller"},
|
||||
Rules: []rbac.PolicyRule{
|
||||
rbac.NewRule("get", "list", "watch").Groups(legacyGroup).Resources("services").RuleOrDie(),
|
||||
rbac.NewRule("update").Groups(legacyGroup).Resources("services/status").RuleOrDie(),
|
||||
rbac.NewRule("list", "watch").Groups(legacyGroup).Resources("nodes").RuleOrDie(),
|
||||
eventsRule(),
|
||||
},
|
||||
})
|
||||
addControllerRole(&controllerRoles, &controllerRoleBindings, rbac.ClusterRole{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: saRolePrefix + "statefulset-controller"},
|
||||
Rules: []rbac.PolicyRule{
|
||||
rbac.NewRule("list", "watch").Groups(legacyGroup).Resources("pods").RuleOrDie(),
|
||||
rbac.NewRule("get", "list", "watch").Groups(appsGroup).Resources("statefulsets").RuleOrDie(),
|
||||
rbac.NewRule("update").Groups(appsGroup).Resources("statefulsets/status").RuleOrDie(),
|
||||
rbac.NewRule("update").Groups(appsGroup).Resources("statefulsets/finalizers").RuleOrDie(),
|
||||
rbac.NewRule("get", "create", "delete", "update", "patch").Groups(legacyGroup).Resources("pods").RuleOrDie(),
|
||||
rbac.NewRule("get", "create", "delete", "update", "patch", "list", "watch").Groups(appsGroup).Resources("controllerrevisions").RuleOrDie(),
|
||||
rbac.NewRule("get", "create").Groups(legacyGroup).Resources("persistentvolumeclaims").RuleOrDie(),
|
||||
eventsRule(),
|
||||
},
|
||||
})
|
||||
addControllerRole(&controllerRoles, &controllerRoleBindings, rbac.ClusterRole{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: saRolePrefix + "ttl-controller"},
|
||||
Rules: []rbac.PolicyRule{
|
||||
rbac.NewRule("update", "patch", "list", "watch").Groups(legacyGroup).Resources("nodes").RuleOrDie(),
|
||||
eventsRule(),
|
||||
},
|
||||
})
|
||||
addControllerRole(&controllerRoles, &controllerRoleBindings, rbac.ClusterRole{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: saRolePrefix + "certificate-controller"},
|
||||
Rules: []rbac.PolicyRule{
|
||||
rbac.NewRule("get", "list", "watch").Groups(certificatesGroup).Resources("certificatesigningrequests").RuleOrDie(),
|
||||
rbac.NewRule("update").Groups(certificatesGroup).Resources("certificatesigningrequests/status", "certificatesigningrequests/approval").RuleOrDie(),
|
||||
rbac.NewRule("create").Groups(authorizationGroup).Resources("subjectaccessreviews").RuleOrDie(),
|
||||
eventsRule(),
|
||||
},
|
||||
})
|
||||
if utilfeature.DefaultFeatureGate.Enabled(features.PVCProtection) {
|
||||
addControllerRole(&controllerRoles, &controllerRoleBindings, rbac.ClusterRole{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: saRolePrefix + "pvc-protection-controller"},
|
||||
Rules: []rbac.PolicyRule{
|
||||
rbac.NewRule("get", "list", "watch", "update").Groups(legacyGroup).Resources("persistentvolumeclaims").RuleOrDie(),
|
||||
rbac.NewRule("list", "watch", "get").Groups(legacyGroup).Resources("pods").RuleOrDie(),
|
||||
eventsRule(),
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
return controllerRoles, controllerRoleBindings
|
||||
}
|
||||
|
||||
// ControllerRoles returns the cluster roles used by controllers
|
||||
func ControllerRoles() []rbac.ClusterRole {
|
||||
controllerRoles, _ := buildControllerRoles()
|
||||
return controllerRoles
|
||||
}
|
||||
|
||||
// ControllerRoleBindings returns the role bindings used by controllers
|
||||
func ControllerRoleBindings() []rbac.ClusterRoleBinding {
|
||||
_, controllerRoleBindings := buildControllerRoles()
|
||||
return controllerRoleBindings
|
||||
}
|
92
vendor/k8s.io/kubernetes/plugin/pkg/auth/authorizer/rbac/bootstrappolicy/controller_policy_test.go
generated
vendored
Normal file
92
vendor/k8s.io/kubernetes/plugin/pkg/auth/authorizer/rbac/bootstrappolicy/controller_policy_test.go
generated
vendored
Normal file
@ -0,0 +1,92 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package bootstrappolicy
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"k8s.io/apimachinery/pkg/api/meta"
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
)
|
||||
|
||||
// rolesWithAllowStar are the controller roles which are allowed to contain a *. These are
|
||||
// namespace lifecycle and GC which have to delete anything. If you're adding to this list
|
||||
// tag sig-auth
|
||||
var rolesWithAllowStar = sets.NewString(
|
||||
saRolePrefix+"namespace-controller",
|
||||
saRolePrefix+"generic-garbage-collector",
|
||||
saRolePrefix+"resourcequota-controller",
|
||||
saRolePrefix+"horizontal-pod-autoscaler",
|
||||
saRolePrefix+"clusterrole-aggregation-controller",
|
||||
)
|
||||
|
||||
// TestNoStarsForControllers confirms that no controller role has star verbs, groups,
|
||||
// or resources. There are three known exceptions: namespace lifecycle and GC which have to
|
||||
// delete anything, and HPA, which has the power to read metrics associated
|
||||
// with any object.
|
||||
func TestNoStarsForControllers(t *testing.T) {
|
||||
for _, role := range ControllerRoles() {
|
||||
if rolesWithAllowStar.Has(role.Name) {
|
||||
continue
|
||||
}
|
||||
|
||||
for i, rule := range role.Rules {
|
||||
for j, verb := range rule.Verbs {
|
||||
if verb == "*" {
|
||||
t.Errorf("%s.Rule[%d].Verbs[%d] is star", role.Name, i, j)
|
||||
}
|
||||
}
|
||||
for j, group := range rule.APIGroups {
|
||||
if group == "*" {
|
||||
t.Errorf("%s.Rule[%d].APIGroups[%d] is star", role.Name, i, j)
|
||||
}
|
||||
}
|
||||
for j, resource := range rule.Resources {
|
||||
if resource == "*" {
|
||||
t.Errorf("%s.Rule[%d].Resources[%d] is star", role.Name, i, j)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestControllerRoleLabel(t *testing.T) {
|
||||
roles := ControllerRoles()
|
||||
for i := range roles {
|
||||
role := roles[i]
|
||||
accessor, err := meta.Accessor(&role)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if got, want := accessor.GetLabels(), map[string]string{"kubernetes.io/bootstrapping": "rbac-defaults"}; !reflect.DeepEqual(got, want) {
|
||||
t.Errorf("ClusterRole: %s GetLabels() = %s, want %s", accessor.GetName(), got, want)
|
||||
}
|
||||
}
|
||||
|
||||
rolebindings := ControllerRoleBindings()
|
||||
for i := range rolebindings {
|
||||
rolebinding := rolebindings[i]
|
||||
accessor, err := meta.Accessor(&rolebinding)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if got, want := accessor.GetLabels(), map[string]string{"kubernetes.io/bootstrapping": "rbac-defaults"}; !reflect.DeepEqual(got, want) {
|
||||
t.Errorf("ClusterRoleBinding: %s GetLabels() = %s, want %s", accessor.GetName(), got, want)
|
||||
}
|
||||
}
|
||||
}
|
153
vendor/k8s.io/kubernetes/plugin/pkg/auth/authorizer/rbac/bootstrappolicy/namespace_policy.go
generated
vendored
Normal file
153
vendor/k8s.io/kubernetes/plugin/pkg/auth/authorizer/rbac/bootstrappolicy/namespace_policy.go
generated
vendored
Normal file
@ -0,0 +1,153 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package bootstrappolicy
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/golang/glog"
|
||||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
rbac "k8s.io/kubernetes/pkg/apis/rbac"
|
||||
)
|
||||
|
||||
var (
|
||||
// namespaceRoles is a map of namespace to slice of roles to create
|
||||
namespaceRoles = map[string][]rbac.Role{}
|
||||
|
||||
// namespaceRoleBindings is a map of namespace to slice of roleBindings to create
|
||||
namespaceRoleBindings = map[string][]rbac.RoleBinding{}
|
||||
)
|
||||
|
||||
func addNamespaceRole(namespace string, role rbac.Role) {
|
||||
if !strings.HasPrefix(namespace, "kube-") {
|
||||
glog.Fatalf(`roles can only be bootstrapped into reserved namespaces starting with "kube-", not %q`, namespace)
|
||||
}
|
||||
|
||||
existingRoles := namespaceRoles[namespace]
|
||||
for _, existingRole := range existingRoles {
|
||||
if role.Name == existingRole.Name {
|
||||
glog.Fatalf("role %q was already registered in %q", role.Name, namespace)
|
||||
}
|
||||
}
|
||||
|
||||
role.Namespace = namespace
|
||||
addDefaultMetadata(&role)
|
||||
existingRoles = append(existingRoles, role)
|
||||
namespaceRoles[namespace] = existingRoles
|
||||
}
|
||||
|
||||
func addNamespaceRoleBinding(namespace string, roleBinding rbac.RoleBinding) {
|
||||
if !strings.HasPrefix(namespace, "kube-") {
|
||||
glog.Fatalf(`rolebindings can only be bootstrapped into reserved namespaces starting with "kube-", not %q`, namespace)
|
||||
}
|
||||
|
||||
existingRoleBindings := namespaceRoleBindings[namespace]
|
||||
for _, existingRoleBinding := range existingRoleBindings {
|
||||
if roleBinding.Name == existingRoleBinding.Name {
|
||||
glog.Fatalf("rolebinding %q was already registered in %q", roleBinding.Name, namespace)
|
||||
}
|
||||
}
|
||||
|
||||
roleBinding.Namespace = namespace
|
||||
addDefaultMetadata(&roleBinding)
|
||||
existingRoleBindings = append(existingRoleBindings, roleBinding)
|
||||
namespaceRoleBindings[namespace] = existingRoleBindings
|
||||
}
|
||||
|
||||
func init() {
|
||||
addNamespaceRole(metav1.NamespaceSystem, rbac.Role{
|
||||
// role for finding authentication config info for starting a server
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "extension-apiserver-authentication-reader"},
|
||||
Rules: []rbac.PolicyRule{
|
||||
// this particular config map is exposed and contains authentication configuration information
|
||||
rbac.NewRule("get").Groups(legacyGroup).Resources("configmaps").Names("extension-apiserver-authentication").RuleOrDie(),
|
||||
},
|
||||
})
|
||||
addNamespaceRole(metav1.NamespaceSystem, rbac.Role{
|
||||
// role for the bootstrap signer to be able to inspect kube-system secrets
|
||||
ObjectMeta: metav1.ObjectMeta{Name: saRolePrefix + "bootstrap-signer"},
|
||||
Rules: []rbac.PolicyRule{
|
||||
rbac.NewRule("get", "list", "watch").Groups(legacyGroup).Resources("secrets").RuleOrDie(),
|
||||
},
|
||||
})
|
||||
addNamespaceRole(metav1.NamespaceSystem, rbac.Role{
|
||||
// role for the cloud providers to access/create kube-system configmaps
|
||||
ObjectMeta: metav1.ObjectMeta{Name: saRolePrefix + "cloud-provider"},
|
||||
Rules: []rbac.PolicyRule{
|
||||
rbac.NewRule("create", "get", "list", "watch").Groups(legacyGroup).Resources("configmaps").RuleOrDie(),
|
||||
},
|
||||
})
|
||||
addNamespaceRole(metav1.NamespaceSystem, rbac.Role{
|
||||
// role for the token-cleaner to be able to remove secrets, but only in kube-system
|
||||
ObjectMeta: metav1.ObjectMeta{Name: saRolePrefix + "token-cleaner"},
|
||||
Rules: []rbac.PolicyRule{
|
||||
rbac.NewRule("get", "list", "watch", "delete").Groups(legacyGroup).Resources("secrets").RuleOrDie(),
|
||||
eventsRule(),
|
||||
},
|
||||
})
|
||||
// TODO: Create util on Role+Binding for leader locking if more cases evolve.
|
||||
addNamespaceRole(metav1.NamespaceSystem, rbac.Role{
|
||||
// role for the leader locking on supplied configmap
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "system::leader-locking-kube-controller-manager"},
|
||||
Rules: []rbac.PolicyRule{
|
||||
rbac.NewRule("watch").Groups(legacyGroup).Resources("configmaps").RuleOrDie(),
|
||||
rbac.NewRule("get", "update").Groups(legacyGroup).Resources("configmaps").Names("kube-controller-manager").RuleOrDie(),
|
||||
},
|
||||
})
|
||||
addNamespaceRole(metav1.NamespaceSystem, rbac.Role{
|
||||
// role for the leader locking on supplied configmap
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "system::leader-locking-kube-scheduler"},
|
||||
Rules: []rbac.PolicyRule{
|
||||
rbac.NewRule("watch").Groups(legacyGroup).Resources("configmaps").RuleOrDie(),
|
||||
rbac.NewRule("get", "update").Groups(legacyGroup).Resources("configmaps").Names("kube-scheduler").RuleOrDie(),
|
||||
},
|
||||
})
|
||||
addNamespaceRoleBinding(metav1.NamespaceSystem,
|
||||
rbac.NewRoleBinding("system::leader-locking-kube-controller-manager", metav1.NamespaceSystem).SAs(metav1.NamespaceSystem, "kube-controller-manager").BindingOrDie())
|
||||
addNamespaceRoleBinding(metav1.NamespaceSystem,
|
||||
rbac.NewRoleBinding("system::leader-locking-kube-scheduler", metav1.NamespaceSystem).SAs(metav1.NamespaceSystem, "kube-scheduler").BindingOrDie())
|
||||
addNamespaceRoleBinding(metav1.NamespaceSystem,
|
||||
rbac.NewRoleBinding(saRolePrefix+"bootstrap-signer", metav1.NamespaceSystem).SAs(metav1.NamespaceSystem, "bootstrap-signer").BindingOrDie())
|
||||
addNamespaceRoleBinding(metav1.NamespaceSystem,
|
||||
rbac.NewRoleBinding(saRolePrefix+"cloud-provider", metav1.NamespaceSystem).SAs(metav1.NamespaceSystem, "cloud-provider").BindingOrDie())
|
||||
addNamespaceRoleBinding(metav1.NamespaceSystem,
|
||||
rbac.NewRoleBinding(saRolePrefix+"token-cleaner", metav1.NamespaceSystem).SAs(metav1.NamespaceSystem, "token-cleaner").BindingOrDie())
|
||||
|
||||
addNamespaceRole(metav1.NamespacePublic, rbac.Role{
|
||||
// role for the bootstrap signer to be able to write its configmap
|
||||
ObjectMeta: metav1.ObjectMeta{Name: saRolePrefix + "bootstrap-signer"},
|
||||
Rules: []rbac.PolicyRule{
|
||||
rbac.NewRule("get", "list", "watch").Groups(legacyGroup).Resources("configmaps").RuleOrDie(),
|
||||
rbac.NewRule("update").Groups(legacyGroup).Resources("configmaps").Names("cluster-info").RuleOrDie(),
|
||||
eventsRule(),
|
||||
},
|
||||
})
|
||||
addNamespaceRoleBinding(metav1.NamespacePublic,
|
||||
rbac.NewRoleBinding(saRolePrefix+"bootstrap-signer", metav1.NamespacePublic).SAs(metav1.NamespaceSystem, "bootstrap-signer").BindingOrDie())
|
||||
|
||||
}
|
||||
|
||||
// NamespaceRoles returns a map of namespace to slice of roles to create
|
||||
func NamespaceRoles() map[string][]rbac.Role {
|
||||
return namespaceRoles
|
||||
}
|
||||
|
||||
// NamespaceRoleBindings returns a map of namespace to slice of roles to create
|
||||
func NamespaceRoleBindings() map[string][]rbac.RoleBinding {
|
||||
return namespaceRoleBindings
|
||||
}
|
513
vendor/k8s.io/kubernetes/plugin/pkg/auth/authorizer/rbac/bootstrappolicy/policy.go
generated
vendored
Normal file
513
vendor/k8s.io/kubernetes/plugin/pkg/auth/authorizer/rbac/bootstrappolicy/policy.go
generated
vendored
Normal file
@ -0,0 +1,513 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package bootstrappolicy
|
||||
|
||||
import (
|
||||
"k8s.io/apimachinery/pkg/api/meta"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apiserver/pkg/authentication/user"
|
||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||
rbac "k8s.io/kubernetes/pkg/apis/rbac"
|
||||
"k8s.io/kubernetes/pkg/features"
|
||||
)
|
||||
|
||||
var (
|
||||
ReadWrite = []string{"get", "list", "watch", "create", "update", "patch", "delete", "deletecollection"}
|
||||
Read = []string{"get", "list", "watch"}
|
||||
|
||||
Label = map[string]string{"kubernetes.io/bootstrapping": "rbac-defaults"}
|
||||
Annotation = map[string]string{rbac.AutoUpdateAnnotationKey: "true"}
|
||||
)
|
||||
|
||||
const (
|
||||
legacyGroup = ""
|
||||
appsGroup = "apps"
|
||||
authenticationGroup = "authentication.k8s.io"
|
||||
authorizationGroup = "authorization.k8s.io"
|
||||
autoscalingGroup = "autoscaling"
|
||||
batchGroup = "batch"
|
||||
certificatesGroup = "certificates.k8s.io"
|
||||
extensionsGroup = "extensions"
|
||||
policyGroup = "policy"
|
||||
rbacGroup = "rbac.authorization.k8s.io"
|
||||
storageGroup = "storage.k8s.io"
|
||||
resMetricsGroup = "metrics.k8s.io"
|
||||
customMetricsGroup = "custom.metrics.k8s.io"
|
||||
)
|
||||
|
||||
func addDefaultMetadata(obj runtime.Object) {
|
||||
metadata, err := meta.Accessor(obj)
|
||||
if err != nil {
|
||||
// if this happens, then some static code is broken
|
||||
panic(err)
|
||||
}
|
||||
|
||||
labels := metadata.GetLabels()
|
||||
if labels == nil {
|
||||
labels = map[string]string{}
|
||||
}
|
||||
for k, v := range Label {
|
||||
labels[k] = v
|
||||
}
|
||||
metadata.SetLabels(labels)
|
||||
|
||||
annotations := metadata.GetAnnotations()
|
||||
if annotations == nil {
|
||||
annotations = map[string]string{}
|
||||
}
|
||||
for k, v := range Annotation {
|
||||
annotations[k] = v
|
||||
}
|
||||
metadata.SetAnnotations(annotations)
|
||||
}
|
||||
|
||||
func addClusterRoleLabel(roles []rbac.ClusterRole) {
|
||||
for i := range roles {
|
||||
addDefaultMetadata(&roles[i])
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func addClusterRoleBindingLabel(rolebindings []rbac.ClusterRoleBinding) {
|
||||
for i := range rolebindings {
|
||||
addDefaultMetadata(&rolebindings[i])
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func NodeRules() []rbac.PolicyRule {
|
||||
nodePolicyRules := []rbac.PolicyRule{
|
||||
// Needed to check API access. These creates are non-mutating
|
||||
rbac.NewRule("create").Groups(authenticationGroup).Resources("tokenreviews").RuleOrDie(),
|
||||
rbac.NewRule("create").Groups(authorizationGroup).Resources("subjectaccessreviews", "localsubjectaccessreviews").RuleOrDie(),
|
||||
|
||||
// Needed to build serviceLister, to populate env vars for services
|
||||
rbac.NewRule(Read...).Groups(legacyGroup).Resources("services").RuleOrDie(),
|
||||
|
||||
// Nodes can register Node API objects and report status.
|
||||
// Use the NodeRestriction admission plugin to limit a node to creating/updating its own API object.
|
||||
rbac.NewRule("create", "get", "list", "watch").Groups(legacyGroup).Resources("nodes").RuleOrDie(),
|
||||
rbac.NewRule("update", "patch").Groups(legacyGroup).Resources("nodes/status").RuleOrDie(),
|
||||
rbac.NewRule("update", "patch", "delete").Groups(legacyGroup).Resources("nodes").RuleOrDie(),
|
||||
|
||||
// TODO: restrict to the bound node as creator in the NodeRestrictions admission plugin
|
||||
rbac.NewRule("create", "update", "patch").Groups(legacyGroup).Resources("events").RuleOrDie(),
|
||||
|
||||
// TODO: restrict to pods scheduled on the bound node once field selectors are supported by list/watch authorization
|
||||
rbac.NewRule(Read...).Groups(legacyGroup).Resources("pods").RuleOrDie(),
|
||||
|
||||
// Needed for the node to create/delete mirror pods.
|
||||
// Use the NodeRestriction admission plugin to limit a node to creating/deleting mirror pods bound to itself.
|
||||
rbac.NewRule("create", "delete").Groups(legacyGroup).Resources("pods").RuleOrDie(),
|
||||
// Needed for the node to report status of pods it is running.
|
||||
// Use the NodeRestriction admission plugin to limit a node to updating status of pods bound to itself.
|
||||
rbac.NewRule("update").Groups(legacyGroup).Resources("pods/status").RuleOrDie(),
|
||||
// Needed for the node to create pod evictions.
|
||||
// Use the NodeRestriction admission plugin to limit a node to creating evictions for pods bound to itself.
|
||||
rbac.NewRule("create").Groups(legacyGroup).Resources("pods/eviction").RuleOrDie(),
|
||||
|
||||
// Needed for imagepullsecrets, rbd/ceph and secret volumes, and secrets in envs
|
||||
// Needed for configmap volume and envs
|
||||
// Use the Node authorization mode to limit a node to get secrets/configmaps referenced by pods bound to itself.
|
||||
rbac.NewRule("get").Groups(legacyGroup).Resources("secrets", "configmaps").RuleOrDie(),
|
||||
// Needed for persistent volumes
|
||||
// Use the Node authorization mode to limit a node to get pv/pvc objects referenced by pods bound to itself.
|
||||
rbac.NewRule("get").Groups(legacyGroup).Resources("persistentvolumeclaims", "persistentvolumes").RuleOrDie(),
|
||||
|
||||
// TODO: add to the Node authorizer and restrict to endpoints referenced by pods or PVs bound to the node
|
||||
// Needed for glusterfs volumes
|
||||
rbac.NewRule("get").Groups(legacyGroup).Resources("endpoints").RuleOrDie(),
|
||||
// Used to create a certificatesigningrequest for a node-specific client certificate, and watch
|
||||
// for it to be signed. This allows the kubelet to rotate it's own certificate.
|
||||
rbac.NewRule("create", "get", "list", "watch").Groups(certificatesGroup).Resources("certificatesigningrequests").RuleOrDie(),
|
||||
}
|
||||
|
||||
if utilfeature.DefaultFeatureGate.Enabled(features.ExpandPersistentVolumes) {
|
||||
// Use the Node authorization mode to limit a node to update status of pvc objects referenced by pods bound to itself.
|
||||
// Use the NodeRestriction admission plugin to limit a node to just update the status stanza.
|
||||
pvcStatusPolicyRule := rbac.NewRule("get", "update", "patch").Groups(legacyGroup).Resources("persistentvolumeclaims/status").RuleOrDie()
|
||||
nodePolicyRules = append(nodePolicyRules, pvcStatusPolicyRule)
|
||||
}
|
||||
|
||||
// CSI
|
||||
if utilfeature.DefaultFeatureGate.Enabled(features.CSIPersistentVolume) {
|
||||
volAttachRule := rbac.NewRule("get").Groups(storageGroup).Resources("volumeattachments").RuleOrDie()
|
||||
nodePolicyRules = append(nodePolicyRules, volAttachRule)
|
||||
}
|
||||
return nodePolicyRules
|
||||
}
|
||||
|
||||
// ClusterRoles returns the cluster roles to bootstrap an API server with
|
||||
func ClusterRoles() []rbac.ClusterRole {
|
||||
roles := []rbac.ClusterRole{
|
||||
{
|
||||
// a "root" role which can do absolutely anything
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "cluster-admin"},
|
||||
Rules: []rbac.PolicyRule{
|
||||
rbac.NewRule("*").Groups("*").Resources("*").RuleOrDie(),
|
||||
rbac.NewRule("*").URLs("*").RuleOrDie(),
|
||||
},
|
||||
},
|
||||
{
|
||||
// a role which provides just enough power to determine if the server is ready and discover API versions for negotiation
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "system:discovery"},
|
||||
Rules: []rbac.PolicyRule{
|
||||
rbac.NewRule("get").URLs(
|
||||
"/healthz", "/version",
|
||||
// remove once swagger 1.2 support is removed
|
||||
"/swaggerapi", "/swaggerapi/*",
|
||||
// do not expand this pattern for openapi discovery docs
|
||||
// move to a single openapi endpoint that takes accept/accept-encoding headers
|
||||
"/swagger.json", "/swagger-2.0.0.pb-v1",
|
||||
"/api", "/api/*",
|
||||
"/apis", "/apis/*",
|
||||
).RuleOrDie(),
|
||||
},
|
||||
},
|
||||
{
|
||||
// a role which provides minimal resource access to allow a "normal" user to learn information about themselves
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "system:basic-user"},
|
||||
Rules: []rbac.PolicyRule{
|
||||
// TODO add future selfsubjectrulesreview, project request APIs, project listing APIs
|
||||
rbac.NewRule("create").Groups(authorizationGroup).Resources("selfsubjectaccessreviews", "selfsubjectrulesreviews").RuleOrDie(),
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
// a role for a namespace level admin. It is `edit` plus the power to grant permissions to other users.
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "admin"},
|
||||
AggregationRule: &rbac.AggregationRule{
|
||||
ClusterRoleSelectors: []metav1.LabelSelector{{MatchLabels: map[string]string{"rbac.authorization.k8s.io/aggregate-to-admin": "true"}}},
|
||||
},
|
||||
},
|
||||
{
|
||||
// a role for a namespace level editor. It grants access to all user level actions in a namespace.
|
||||
// It does not grant powers for "privileged" resources which are domain of the system: `/status`
|
||||
// subresources or `quota`/`limits` which are used to control namespaces
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "edit"},
|
||||
AggregationRule: &rbac.AggregationRule{
|
||||
ClusterRoleSelectors: []metav1.LabelSelector{{MatchLabels: map[string]string{"rbac.authorization.k8s.io/aggregate-to-edit": "true"}}},
|
||||
},
|
||||
},
|
||||
{
|
||||
// a role for namespace level viewing. It grants Read-only access to non-escalating resources in
|
||||
// a namespace.
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "view"},
|
||||
AggregationRule: &rbac.AggregationRule{
|
||||
ClusterRoleSelectors: []metav1.LabelSelector{{MatchLabels: map[string]string{"rbac.authorization.k8s.io/aggregate-to-view": "true"}}},
|
||||
},
|
||||
},
|
||||
{
|
||||
// a role for a namespace level admin. It is `edit` plus the power to grant permissions to other users.
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "system:aggregate-to-admin", Labels: map[string]string{"rbac.authorization.k8s.io/aggregate-to-admin": "true"}},
|
||||
Rules: []rbac.PolicyRule{
|
||||
rbac.NewRule(ReadWrite...).Groups(legacyGroup).Resources("pods", "pods/attach", "pods/proxy", "pods/exec", "pods/portforward").RuleOrDie(),
|
||||
rbac.NewRule(ReadWrite...).Groups(legacyGroup).Resources("replicationcontrollers", "replicationcontrollers/scale", "serviceaccounts",
|
||||
"services", "services/proxy", "endpoints", "persistentvolumeclaims", "configmaps", "secrets").RuleOrDie(),
|
||||
rbac.NewRule(Read...).Groups(legacyGroup).Resources("limitranges", "resourcequotas", "bindings", "events",
|
||||
"pods/status", "resourcequotas/status", "namespaces/status", "replicationcontrollers/status", "pods/log").RuleOrDie(),
|
||||
// read access to namespaces at the namespace scope means you can read *this* namespace. This can be used as an
|
||||
// indicator of which namespaces you have access to.
|
||||
rbac.NewRule(Read...).Groups(legacyGroup).Resources("namespaces").RuleOrDie(),
|
||||
rbac.NewRule("impersonate").Groups(legacyGroup).Resources("serviceaccounts").RuleOrDie(),
|
||||
|
||||
rbac.NewRule(ReadWrite...).Groups(appsGroup).Resources("statefulsets",
|
||||
"daemonsets",
|
||||
"deployments", "deployments/scale", "deployments/rollback",
|
||||
"replicasets", "replicasets/scale").RuleOrDie(),
|
||||
|
||||
rbac.NewRule(ReadWrite...).Groups(autoscalingGroup).Resources("horizontalpodautoscalers").RuleOrDie(),
|
||||
|
||||
rbac.NewRule(ReadWrite...).Groups(batchGroup).Resources("jobs", "cronjobs").RuleOrDie(),
|
||||
|
||||
rbac.NewRule(ReadWrite...).Groups(extensionsGroup).Resources("daemonsets",
|
||||
"deployments", "deployments/scale", "deployments/rollback", "ingresses",
|
||||
"replicasets", "replicasets/scale", "replicationcontrollers/scale").RuleOrDie(),
|
||||
|
||||
rbac.NewRule(ReadWrite...).Groups(policyGroup).Resources("poddisruptionbudgets").RuleOrDie(),
|
||||
|
||||
// additional admin powers
|
||||
rbac.NewRule("create").Groups(authorizationGroup).Resources("localsubjectaccessreviews").RuleOrDie(),
|
||||
rbac.NewRule(ReadWrite...).Groups(rbacGroup).Resources("roles", "rolebindings").RuleOrDie(),
|
||||
},
|
||||
},
|
||||
{
|
||||
// a role for a namespace level editor. It grants access to all user level actions in a namespace.
|
||||
// It does not grant powers for "privileged" resources which are domain of the system: `/status`
|
||||
// subresources or `quota`/`limits` which are used to control namespaces
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "system:aggregate-to-edit", Labels: map[string]string{"rbac.authorization.k8s.io/aggregate-to-edit": "true"}},
|
||||
Rules: []rbac.PolicyRule{
|
||||
rbac.NewRule(ReadWrite...).Groups(legacyGroup).Resources("pods", "pods/attach", "pods/proxy", "pods/exec", "pods/portforward").RuleOrDie(),
|
||||
rbac.NewRule(ReadWrite...).Groups(legacyGroup).Resources("replicationcontrollers", "replicationcontrollers/scale", "serviceaccounts",
|
||||
"services", "services/proxy", "endpoints", "persistentvolumeclaims", "configmaps", "secrets").RuleOrDie(),
|
||||
rbac.NewRule(Read...).Groups(legacyGroup).Resources("limitranges", "resourcequotas", "bindings", "events",
|
||||
"pods/status", "resourcequotas/status", "namespaces/status", "replicationcontrollers/status", "pods/log").RuleOrDie(),
|
||||
// read access to namespaces at the namespace scope means you can read *this* namespace. This can be used as an
|
||||
// indicator of which namespaces you have access to.
|
||||
rbac.NewRule(Read...).Groups(legacyGroup).Resources("namespaces").RuleOrDie(),
|
||||
rbac.NewRule("impersonate").Groups(legacyGroup).Resources("serviceaccounts").RuleOrDie(),
|
||||
|
||||
rbac.NewRule(ReadWrite...).Groups(appsGroup).Resources("statefulsets",
|
||||
"daemonsets",
|
||||
"deployments", "deployments/scale", "deployments/rollback",
|
||||
"replicasets", "replicasets/scale").RuleOrDie(),
|
||||
|
||||
rbac.NewRule(ReadWrite...).Groups(autoscalingGroup).Resources("horizontalpodautoscalers").RuleOrDie(),
|
||||
|
||||
rbac.NewRule(ReadWrite...).Groups(batchGroup).Resources("jobs", "cronjobs").RuleOrDie(),
|
||||
|
||||
rbac.NewRule(ReadWrite...).Groups(extensionsGroup).Resources("daemonsets",
|
||||
"deployments", "deployments/scale", "deployments/rollback", "ingresses",
|
||||
"replicasets", "replicasets/scale", "replicationcontrollers/scale").RuleOrDie(),
|
||||
|
||||
rbac.NewRule(ReadWrite...).Groups(policyGroup).Resources("poddisruptionbudgets").RuleOrDie(),
|
||||
},
|
||||
},
|
||||
{
|
||||
// a role for namespace level viewing. It grants Read-only access to non-escalating resources in
|
||||
// a namespace.
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "system:aggregate-to-view", Labels: map[string]string{"rbac.authorization.k8s.io/aggregate-to-view": "true"}},
|
||||
Rules: []rbac.PolicyRule{
|
||||
rbac.NewRule(Read...).Groups(legacyGroup).Resources("pods", "replicationcontrollers", "replicationcontrollers/scale", "serviceaccounts",
|
||||
"services", "endpoints", "persistentvolumeclaims", "configmaps").RuleOrDie(),
|
||||
rbac.NewRule(Read...).Groups(legacyGroup).Resources("limitranges", "resourcequotas", "bindings", "events",
|
||||
"pods/status", "resourcequotas/status", "namespaces/status", "replicationcontrollers/status", "pods/log").RuleOrDie(),
|
||||
// read access to namespaces at the namespace scope means you can read *this* namespace. This can be used as an
|
||||
// indicator of which namespaces you have access to.
|
||||
rbac.NewRule(Read...).Groups(legacyGroup).Resources("namespaces").RuleOrDie(),
|
||||
|
||||
rbac.NewRule(Read...).Groups(appsGroup).Resources("statefulsets",
|
||||
"daemonsets",
|
||||
"deployments", "deployments/scale",
|
||||
"replicasets", "replicasets/scale").RuleOrDie(),
|
||||
|
||||
rbac.NewRule(Read...).Groups(autoscalingGroup).Resources("horizontalpodautoscalers").RuleOrDie(),
|
||||
|
||||
rbac.NewRule(Read...).Groups(batchGroup).Resources("jobs", "cronjobs").RuleOrDie(),
|
||||
|
||||
rbac.NewRule(Read...).Groups(extensionsGroup).Resources("daemonsets", "deployments", "deployments/scale",
|
||||
"ingresses", "replicasets", "replicasets/scale", "replicationcontrollers/scale").RuleOrDie(),
|
||||
|
||||
rbac.NewRule(Read...).Groups(policyGroup).Resources("poddisruptionbudgets").RuleOrDie(),
|
||||
},
|
||||
},
|
||||
{
|
||||
// a role to use for heapster's connections back to the API server
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "system:heapster"},
|
||||
Rules: []rbac.PolicyRule{
|
||||
rbac.NewRule(Read...).Groups(legacyGroup).Resources("events", "pods", "nodes", "namespaces").RuleOrDie(),
|
||||
rbac.NewRule(Read...).Groups(extensionsGroup).Resources("deployments").RuleOrDie(),
|
||||
},
|
||||
},
|
||||
{
|
||||
// a role for nodes to use to have the access they need for running pods
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "system:node"},
|
||||
Rules: NodeRules(),
|
||||
},
|
||||
{
|
||||
// a role to use for node-problem-detector access. It does not get bound to default location since
|
||||
// deployment locations can reasonably vary.
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "system:node-problem-detector"},
|
||||
Rules: []rbac.PolicyRule{
|
||||
rbac.NewRule("get").Groups(legacyGroup).Resources("nodes").RuleOrDie(),
|
||||
rbac.NewRule("patch").Groups(legacyGroup).Resources("nodes/status").RuleOrDie(),
|
||||
eventsRule(),
|
||||
},
|
||||
},
|
||||
{
|
||||
// a role to use for setting up a proxy
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "system:node-proxier"},
|
||||
Rules: []rbac.PolicyRule{
|
||||
// Used to build serviceLister
|
||||
rbac.NewRule("list", "watch").Groups(legacyGroup).Resources("services", "endpoints").RuleOrDie(),
|
||||
rbac.NewRule("get").Groups(legacyGroup).Resources("nodes").RuleOrDie(),
|
||||
|
||||
eventsRule(),
|
||||
},
|
||||
},
|
||||
{
|
||||
// a role to use for bootstrapping a node's client certificates
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "system:node-bootstrapper"},
|
||||
Rules: []rbac.PolicyRule{
|
||||
// used to create a certificatesigningrequest for a node-specific client certificate, and watch for it to be signed
|
||||
rbac.NewRule("create", "get", "list", "watch").Groups(certificatesGroup).Resources("certificatesigningrequests").RuleOrDie(),
|
||||
},
|
||||
},
|
||||
{
|
||||
// a role to use for allowing authentication and authorization delegation
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "system:auth-delegator"},
|
||||
Rules: []rbac.PolicyRule{
|
||||
// These creates are non-mutating
|
||||
rbac.NewRule("create").Groups(authenticationGroup).Resources("tokenreviews").RuleOrDie(),
|
||||
rbac.NewRule("create").Groups(authorizationGroup).Resources("subjectaccessreviews").RuleOrDie(),
|
||||
},
|
||||
},
|
||||
{
|
||||
// a role to use for the API registry, summarization, and proxy handling
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "system:kube-aggregator"},
|
||||
Rules: []rbac.PolicyRule{
|
||||
// it needs to see all services so that it knows whether the ones it points to exist or not
|
||||
rbac.NewRule(Read...).Groups(legacyGroup).Resources("services", "endpoints").RuleOrDie(),
|
||||
},
|
||||
},
|
||||
{
|
||||
// a role to use for bootstrapping the kube-controller-manager so it can create the shared informers
|
||||
// service accounts, and secrets that we need to create separate identities for other controllers
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "system:kube-controller-manager"},
|
||||
Rules: []rbac.PolicyRule{
|
||||
eventsRule(),
|
||||
rbac.NewRule("create").Groups(legacyGroup).Resources("endpoints", "secrets", "serviceaccounts").RuleOrDie(),
|
||||
rbac.NewRule("delete").Groups(legacyGroup).Resources("secrets").RuleOrDie(),
|
||||
rbac.NewRule("get").Groups(legacyGroup).Resources("endpoints", "namespaces", "secrets", "serviceaccounts").RuleOrDie(),
|
||||
rbac.NewRule("update").Groups(legacyGroup).Resources("endpoints", "secrets", "serviceaccounts").RuleOrDie(),
|
||||
// Needed to check API access. These creates are non-mutating
|
||||
rbac.NewRule("create").Groups(authenticationGroup).Resources("tokenreviews").RuleOrDie(),
|
||||
// Needed for all shared informers
|
||||
rbac.NewRule("list", "watch").Groups("*").Resources("*").RuleOrDie(),
|
||||
},
|
||||
},
|
||||
{
|
||||
// a role to use for the kube-scheduler
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "system:kube-scheduler"},
|
||||
Rules: []rbac.PolicyRule{
|
||||
eventsRule(),
|
||||
|
||||
// this is for leaderlease access
|
||||
// TODO: scope this to the kube-system namespace
|
||||
rbac.NewRule("create").Groups(legacyGroup).Resources("endpoints").RuleOrDie(),
|
||||
rbac.NewRule("get", "update", "patch", "delete").Groups(legacyGroup).Resources("endpoints").Names("kube-scheduler").RuleOrDie(),
|
||||
|
||||
// fundamental resources
|
||||
rbac.NewRule(Read...).Groups(legacyGroup).Resources("nodes").RuleOrDie(),
|
||||
rbac.NewRule("get", "list", "watch", "delete").Groups(legacyGroup).Resources("pods").RuleOrDie(),
|
||||
rbac.NewRule("create").Groups(legacyGroup).Resources("pods/binding", "bindings").RuleOrDie(),
|
||||
rbac.NewRule("patch", "update").Groups(legacyGroup).Resources("pods/status").RuleOrDie(),
|
||||
// things that select pods
|
||||
rbac.NewRule(Read...).Groups(legacyGroup).Resources("services", "replicationcontrollers").RuleOrDie(),
|
||||
rbac.NewRule(Read...).Groups(appsGroup, extensionsGroup).Resources("replicasets").RuleOrDie(),
|
||||
rbac.NewRule(Read...).Groups(appsGroup).Resources("statefulsets").RuleOrDie(),
|
||||
// things that pods use or applies to them
|
||||
rbac.NewRule(Read...).Groups(policyGroup).Resources("poddisruptionbudgets").RuleOrDie(),
|
||||
rbac.NewRule(Read...).Groups(legacyGroup).Resources("persistentvolumeclaims", "persistentvolumes").RuleOrDie(),
|
||||
},
|
||||
},
|
||||
{
|
||||
// a role to use for the kube-dns pod
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "system:kube-dns"},
|
||||
Rules: []rbac.PolicyRule{
|
||||
rbac.NewRule("list", "watch").Groups(legacyGroup).Resources("endpoints", "services").RuleOrDie(),
|
||||
},
|
||||
},
|
||||
{
|
||||
// a role for an external/out-of-tree persistent volume provisioner
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "system:persistent-volume-provisioner"},
|
||||
Rules: []rbac.PolicyRule{
|
||||
rbac.NewRule("get", "list", "watch", "create", "delete").Groups(legacyGroup).Resources("persistentvolumes").RuleOrDie(),
|
||||
// update is needed in addition to read access for setting lock annotations on PVCs
|
||||
rbac.NewRule("get", "list", "watch", "update").Groups(legacyGroup).Resources("persistentvolumeclaims").RuleOrDie(),
|
||||
rbac.NewRule(Read...).Groups(storageGroup).Resources("storageclasses").RuleOrDie(),
|
||||
|
||||
// Needed for watching provisioning success and failure events
|
||||
rbac.NewRule("watch").Groups(legacyGroup).Resources("events").RuleOrDie(),
|
||||
|
||||
eventsRule(),
|
||||
},
|
||||
},
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "system:aws-cloud-provider"},
|
||||
Rules: []rbac.PolicyRule{
|
||||
rbac.NewRule("get", "patch").Groups(legacyGroup).Resources("nodes").RuleOrDie(),
|
||||
eventsRule(),
|
||||
},
|
||||
},
|
||||
{
|
||||
// a role making the csrapprover controller approve a node client CSR
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "system:certificates.k8s.io:certificatesigningrequests:nodeclient"},
|
||||
Rules: []rbac.PolicyRule{
|
||||
rbac.NewRule("create").Groups(certificatesGroup).Resources("certificatesigningrequests/nodeclient").RuleOrDie(),
|
||||
},
|
||||
},
|
||||
{
|
||||
// a role making the csrapprover controller approve a node client CSR requested by the node itself
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "system:certificates.k8s.io:certificatesigningrequests:selfnodeclient"},
|
||||
Rules: []rbac.PolicyRule{
|
||||
rbac.NewRule("create").Groups(certificatesGroup).Resources("certificatesigningrequests/selfnodeclient").RuleOrDie(),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
if utilfeature.DefaultFeatureGate.Enabled(features.RotateKubeletServerCertificate) {
|
||||
roles = append(roles, rbac.ClusterRole{
|
||||
// a role making the csrapprover controller approve a node server CSR requested by the node itself
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "system:certificates.k8s.io:certificatesigningrequests:selfnodeserver"},
|
||||
Rules: []rbac.PolicyRule{
|
||||
rbac.NewRule("create").Groups(certificatesGroup).Resources("certificatesigningrequests/selfnodeserver").RuleOrDie(),
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
if utilfeature.DefaultFeatureGate.Enabled(features.VolumeScheduling) {
|
||||
// Find the scheduler role
|
||||
for i, role := range roles {
|
||||
if role.Name == "system:kube-scheduler" {
|
||||
pvRule := rbac.NewRule("update").Groups(legacyGroup).Resources("persistentvolumes").RuleOrDie()
|
||||
scRule := rbac.NewRule(Read...).Groups(storageGroup).Resources("storageclasses").RuleOrDie()
|
||||
roles[i].Rules = append(role.Rules, pvRule, scRule)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
addClusterRoleLabel(roles)
|
||||
return roles
|
||||
}
|
||||
|
||||
const systemNodeRoleName = "system:node"
|
||||
|
||||
// ClusterRoleBindings return default rolebindings to the default roles
|
||||
func ClusterRoleBindings() []rbac.ClusterRoleBinding {
|
||||
rolebindings := []rbac.ClusterRoleBinding{
|
||||
rbac.NewClusterBinding("cluster-admin").Groups(user.SystemPrivilegedGroup).BindingOrDie(),
|
||||
rbac.NewClusterBinding("system:discovery").Groups(user.AllAuthenticated, user.AllUnauthenticated).BindingOrDie(),
|
||||
rbac.NewClusterBinding("system:basic-user").Groups(user.AllAuthenticated, user.AllUnauthenticated).BindingOrDie(),
|
||||
rbac.NewClusterBinding("system:node-proxier").Users(user.KubeProxy).BindingOrDie(),
|
||||
rbac.NewClusterBinding("system:kube-controller-manager").Users(user.KubeControllerManager).BindingOrDie(),
|
||||
rbac.NewClusterBinding("system:kube-dns").SAs("kube-system", "kube-dns").BindingOrDie(),
|
||||
rbac.NewClusterBinding("system:kube-scheduler").Users(user.KubeScheduler).BindingOrDie(),
|
||||
rbac.NewClusterBinding("system:aws-cloud-provider").SAs("kube-system", "aws-cloud-provider").BindingOrDie(),
|
||||
|
||||
// This default binding of the system:node role to the system:nodes group is deprecated in 1.7 with the availability of the Node authorizer.
|
||||
// This leaves the binding, but with an empty set of subjects, so that tightening reconciliation can remove the subject.
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: systemNodeRoleName},
|
||||
RoleRef: rbac.RoleRef{APIGroup: rbac.GroupName, Kind: "ClusterRole", Name: systemNodeRoleName},
|
||||
},
|
||||
}
|
||||
|
||||
addClusterRoleBindingLabel(rolebindings)
|
||||
|
||||
return rolebindings
|
||||
}
|
||||
|
||||
func ClusterRolesToAggregate() map[string]string {
|
||||
return map[string]string{
|
||||
"admin": "system:aggregate-to-admin",
|
||||
"edit": "system:aggregate-to-edit",
|
||||
"view": "system:aggregate-to-view",
|
||||
}
|
||||
}
|
339
vendor/k8s.io/kubernetes/plugin/pkg/auth/authorizer/rbac/bootstrappolicy/policy_test.go
generated
vendored
Normal file
339
vendor/k8s.io/kubernetes/plugin/pkg/auth/authorizer/rbac/bootstrappolicy/policy_test.go
generated
vendored
Normal file
@ -0,0 +1,339 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package bootstrappolicy_test
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/ghodss/yaml"
|
||||
|
||||
"k8s.io/api/core/v1"
|
||||
rbacv1 "k8s.io/api/rbac/v1"
|
||||
"k8s.io/apimachinery/pkg/api/meta"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/util/diff"
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
"k8s.io/kubernetes/pkg/api/legacyscheme"
|
||||
api "k8s.io/kubernetes/pkg/apis/core"
|
||||
_ "k8s.io/kubernetes/pkg/apis/core/install"
|
||||
"k8s.io/kubernetes/pkg/apis/rbac"
|
||||
_ "k8s.io/kubernetes/pkg/apis/rbac/install"
|
||||
rbacregistryvalidation "k8s.io/kubernetes/pkg/registry/rbac/validation"
|
||||
"k8s.io/kubernetes/plugin/pkg/auth/authorizer/rbac/bootstrappolicy"
|
||||
)
|
||||
|
||||
// semanticRoles is a few enumerated roles for which the relationships are well established
|
||||
// and we want to maintain symmetric roles
|
||||
type semanticRoles struct {
|
||||
admin *rbac.ClusterRole
|
||||
edit *rbac.ClusterRole
|
||||
view *rbac.ClusterRole
|
||||
}
|
||||
|
||||
func getSemanticRoles(roles []rbac.ClusterRole) semanticRoles {
|
||||
ret := semanticRoles{}
|
||||
for i := range roles {
|
||||
role := roles[i]
|
||||
switch role.Name {
|
||||
case "system:aggregate-to-admin":
|
||||
ret.admin = &role
|
||||
case "system:aggregate-to-edit":
|
||||
ret.edit = &role
|
||||
case "system:aggregate-to-view":
|
||||
ret.view = &role
|
||||
}
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
// Some roles should always cover others
|
||||
func TestCovers(t *testing.T) {
|
||||
semanticRoles := getSemanticRoles(bootstrappolicy.ClusterRoles())
|
||||
|
||||
if covers, miss := rbacregistryvalidation.Covers(semanticRoles.admin.Rules, semanticRoles.edit.Rules); !covers {
|
||||
t.Errorf("failed to cover: %#v", miss)
|
||||
}
|
||||
if covers, miss := rbacregistryvalidation.Covers(semanticRoles.admin.Rules, semanticRoles.view.Rules); !covers {
|
||||
t.Errorf("failed to cover: %#v", miss)
|
||||
}
|
||||
if covers, miss := rbacregistryvalidation.Covers(semanticRoles.edit.Rules, semanticRoles.view.Rules); !covers {
|
||||
t.Errorf("failed to cover: %#v", miss)
|
||||
}
|
||||
}
|
||||
|
||||
// additionalAdminPowers is the list of powers that we expect to be different than the editor role.
|
||||
// one resource per rule to make the "does not already contain" check easy
|
||||
var additionalAdminPowers = []rbac.PolicyRule{
|
||||
rbac.NewRule("create").Groups("authorization.k8s.io").Resources("localsubjectaccessreviews").RuleOrDie(),
|
||||
rbac.NewRule(bootstrappolicy.ReadWrite...).Groups("rbac.authorization.k8s.io").Resources("rolebindings").RuleOrDie(),
|
||||
rbac.NewRule(bootstrappolicy.ReadWrite...).Groups("rbac.authorization.k8s.io").Resources("roles").RuleOrDie(),
|
||||
}
|
||||
|
||||
func TestAdminEditRelationship(t *testing.T) {
|
||||
semanticRoles := getSemanticRoles(bootstrappolicy.ClusterRoles())
|
||||
|
||||
// confirm that the edit role doesn't already have extra powers
|
||||
for _, rule := range additionalAdminPowers {
|
||||
if covers, _ := rbacregistryvalidation.Covers(semanticRoles.edit.Rules, []rbac.PolicyRule{rule}); covers {
|
||||
t.Errorf("edit has extra powers: %#v", rule)
|
||||
}
|
||||
}
|
||||
semanticRoles.edit.Rules = append(semanticRoles.edit.Rules, additionalAdminPowers...)
|
||||
|
||||
// at this point, we should have a two way covers relationship
|
||||
if covers, miss := rbacregistryvalidation.Covers(semanticRoles.admin.Rules, semanticRoles.edit.Rules); !covers {
|
||||
t.Errorf("admin has lost rules for: %#v", miss)
|
||||
}
|
||||
if covers, miss := rbacregistryvalidation.Covers(semanticRoles.edit.Rules, semanticRoles.admin.Rules); !covers {
|
||||
t.Errorf("edit is missing rules for: %#v\nIf these should only be admin powers, add them to the list. Otherwise, add them to the edit role.", miss)
|
||||
}
|
||||
}
|
||||
|
||||
// viewEscalatingNamespaceResources is the list of rules that would allow privilege escalation attacks based on
|
||||
// ability to view (GET) them
|
||||
var viewEscalatingNamespaceResources = []rbac.PolicyRule{
|
||||
rbac.NewRule(bootstrappolicy.Read...).Groups("").Resources("pods/attach").RuleOrDie(),
|
||||
rbac.NewRule(bootstrappolicy.Read...).Groups("").Resources("pods/proxy").RuleOrDie(),
|
||||
rbac.NewRule(bootstrappolicy.Read...).Groups("").Resources("pods/exec").RuleOrDie(),
|
||||
rbac.NewRule(bootstrappolicy.Read...).Groups("").Resources("pods/portforward").RuleOrDie(),
|
||||
rbac.NewRule(bootstrappolicy.Read...).Groups("").Resources("secrets").RuleOrDie(),
|
||||
rbac.NewRule(bootstrappolicy.Read...).Groups("").Resources("services/proxy").RuleOrDie(),
|
||||
}
|
||||
|
||||
// ungettableResources is the list of rules that don't allow to view (GET) them
|
||||
// this is purposefully separate list to distinguish from escalating privs
|
||||
var ungettableResources = []rbac.PolicyRule{
|
||||
rbac.NewRule(bootstrappolicy.Read...).Groups("apps", "extensions").Resources("deployments/rollback").RuleOrDie(),
|
||||
}
|
||||
|
||||
func TestEditViewRelationship(t *testing.T) {
|
||||
readVerbs := sets.NewString(bootstrappolicy.Read...)
|
||||
semanticRoles := getSemanticRoles(bootstrappolicy.ClusterRoles())
|
||||
|
||||
// modify the edit role rules to make then read-only for comparison against view role rules
|
||||
for i := range semanticRoles.edit.Rules {
|
||||
rule := semanticRoles.edit.Rules[i]
|
||||
remainingVerbs := []string{}
|
||||
for _, verb := range rule.Verbs {
|
||||
if readVerbs.Has(verb) {
|
||||
remainingVerbs = append(remainingVerbs, verb)
|
||||
}
|
||||
}
|
||||
rule.Verbs = remainingVerbs
|
||||
semanticRoles.edit.Rules[i] = rule
|
||||
}
|
||||
|
||||
// confirm that the view role doesn't already have extra powers
|
||||
for _, rule := range viewEscalatingNamespaceResources {
|
||||
if covers, _ := rbacregistryvalidation.Covers(semanticRoles.view.Rules, []rbac.PolicyRule{rule}); covers {
|
||||
t.Errorf("view has extra powers: %#v", rule)
|
||||
}
|
||||
}
|
||||
semanticRoles.view.Rules = append(semanticRoles.view.Rules, viewEscalatingNamespaceResources...)
|
||||
|
||||
// confirm that the view role doesn't have ungettable resources
|
||||
for _, rule := range ungettableResources {
|
||||
if covers, _ := rbacregistryvalidation.Covers(semanticRoles.view.Rules, []rbac.PolicyRule{rule}); covers {
|
||||
t.Errorf("view has ungettable resource: %#v", rule)
|
||||
}
|
||||
}
|
||||
semanticRoles.view.Rules = append(semanticRoles.view.Rules, ungettableResources...)
|
||||
|
||||
// at this point, we should have a two way covers relationship
|
||||
if covers, miss := rbacregistryvalidation.Covers(semanticRoles.edit.Rules, semanticRoles.view.Rules); !covers {
|
||||
t.Errorf("edit has lost rules for: %#v", miss)
|
||||
}
|
||||
if covers, miss := rbacregistryvalidation.Covers(semanticRoles.view.Rules, semanticRoles.edit.Rules); !covers {
|
||||
t.Errorf("view is missing rules for: %#v\nIf these are escalating powers, add them to the list. Otherwise, add them to the view role.", miss)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBootstrapNamespaceRoles(t *testing.T) {
|
||||
list := &api.List{}
|
||||
names := sets.NewString()
|
||||
roles := map[string]runtime.Object{}
|
||||
|
||||
namespaceRoles := bootstrappolicy.NamespaceRoles()
|
||||
for _, namespace := range sets.StringKeySet(namespaceRoles).List() {
|
||||
bootstrapRoles := namespaceRoles[namespace]
|
||||
for i := range bootstrapRoles {
|
||||
role := bootstrapRoles[i]
|
||||
names.Insert(role.Name)
|
||||
roles[role.Name] = &role
|
||||
}
|
||||
|
||||
for _, name := range names.List() {
|
||||
list.Items = append(list.Items, roles[name])
|
||||
}
|
||||
}
|
||||
|
||||
testObjects(t, list, "namespace-roles.yaml")
|
||||
}
|
||||
|
||||
func TestBootstrapNamespaceRoleBindings(t *testing.T) {
|
||||
list := &api.List{}
|
||||
names := sets.NewString()
|
||||
roleBindings := map[string]runtime.Object{}
|
||||
|
||||
namespaceRoleBindings := bootstrappolicy.NamespaceRoleBindings()
|
||||
for _, namespace := range sets.StringKeySet(namespaceRoleBindings).List() {
|
||||
bootstrapRoleBindings := namespaceRoleBindings[namespace]
|
||||
for i := range bootstrapRoleBindings {
|
||||
roleBinding := bootstrapRoleBindings[i]
|
||||
names.Insert(roleBinding.Name)
|
||||
roleBindings[roleBinding.Name] = &roleBinding
|
||||
}
|
||||
|
||||
for _, name := range names.List() {
|
||||
list.Items = append(list.Items, roleBindings[name])
|
||||
}
|
||||
}
|
||||
|
||||
testObjects(t, list, "namespace-role-bindings.yaml")
|
||||
}
|
||||
|
||||
func TestBootstrapClusterRoles(t *testing.T) {
|
||||
list := &api.List{}
|
||||
names := sets.NewString()
|
||||
roles := map[string]runtime.Object{}
|
||||
bootstrapRoles := bootstrappolicy.ClusterRoles()
|
||||
for i := range bootstrapRoles {
|
||||
role := bootstrapRoles[i]
|
||||
names.Insert(role.Name)
|
||||
roles[role.Name] = &role
|
||||
}
|
||||
for _, name := range names.List() {
|
||||
list.Items = append(list.Items, roles[name])
|
||||
}
|
||||
testObjects(t, list, "cluster-roles.yaml")
|
||||
}
|
||||
|
||||
func TestBootstrapClusterRoleBindings(t *testing.T) {
|
||||
list := &api.List{}
|
||||
names := sets.NewString()
|
||||
roleBindings := map[string]runtime.Object{}
|
||||
bootstrapRoleBindings := bootstrappolicy.ClusterRoleBindings()
|
||||
for i := range bootstrapRoleBindings {
|
||||
role := bootstrapRoleBindings[i]
|
||||
names.Insert(role.Name)
|
||||
roleBindings[role.Name] = &role
|
||||
}
|
||||
for _, name := range names.List() {
|
||||
list.Items = append(list.Items, roleBindings[name])
|
||||
}
|
||||
testObjects(t, list, "cluster-role-bindings.yaml")
|
||||
}
|
||||
|
||||
func TestBootstrapControllerRoles(t *testing.T) {
|
||||
list := &api.List{}
|
||||
names := sets.NewString()
|
||||
roles := map[string]runtime.Object{}
|
||||
bootstrapRoles := bootstrappolicy.ControllerRoles()
|
||||
for i := range bootstrapRoles {
|
||||
role := bootstrapRoles[i]
|
||||
names.Insert(role.Name)
|
||||
roles[role.Name] = &role
|
||||
}
|
||||
for _, name := range names.List() {
|
||||
list.Items = append(list.Items, roles[name])
|
||||
}
|
||||
testObjects(t, list, "controller-roles.yaml")
|
||||
}
|
||||
|
||||
func TestBootstrapControllerRoleBindings(t *testing.T) {
|
||||
list := &api.List{}
|
||||
names := sets.NewString()
|
||||
roleBindings := map[string]runtime.Object{}
|
||||
bootstrapRoleBindings := bootstrappolicy.ControllerRoleBindings()
|
||||
for i := range bootstrapRoleBindings {
|
||||
roleBinding := bootstrapRoleBindings[i]
|
||||
names.Insert(roleBinding.Name)
|
||||
roleBindings[roleBinding.Name] = &roleBinding
|
||||
}
|
||||
for _, name := range names.List() {
|
||||
list.Items = append(list.Items, roleBindings[name])
|
||||
}
|
||||
testObjects(t, list, "controller-role-bindings.yaml")
|
||||
}
|
||||
|
||||
func testObjects(t *testing.T, list *api.List, fixtureFilename string) {
|
||||
filename := filepath.Join("testdata", fixtureFilename)
|
||||
expectedYAML, err := ioutil.ReadFile(filename)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if err := runtime.EncodeList(legacyscheme.Codecs.LegacyCodec(v1.SchemeGroupVersion, rbacv1.SchemeGroupVersion), list.Items); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
jsonData, err := runtime.Encode(legacyscheme.Codecs.LegacyCodec(v1.SchemeGroupVersion, rbacv1.SchemeGroupVersion), list)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
yamlData, err := yaml.JSONToYAML(jsonData)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if string(yamlData) != string(expectedYAML) {
|
||||
t.Errorf("Bootstrap policy data does not match the test fixture in %s", filename)
|
||||
|
||||
const updateEnvVar = "UPDATE_BOOTSTRAP_POLICY_FIXTURE_DATA"
|
||||
if os.Getenv(updateEnvVar) == "true" {
|
||||
if err := ioutil.WriteFile(filename, []byte(yamlData), os.FileMode(0755)); err == nil {
|
||||
t.Logf("Updated data in %s", filename)
|
||||
t.Logf("Verify the diff, commit changes, and rerun the tests")
|
||||
} else {
|
||||
t.Logf("Could not update data in %s: %v", filename, err)
|
||||
}
|
||||
} else {
|
||||
t.Logf("Diff between bootstrap data and fixture data in %s:\n-------------\n%s", filename, diff.StringDiff(string(yamlData), string(expectedYAML)))
|
||||
t.Logf("If the change is expected, re-run with %s=true to update the fixtures", updateEnvVar)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestClusterRoleLabel(t *testing.T) {
|
||||
roles := bootstrappolicy.ClusterRoles()
|
||||
for i := range roles {
|
||||
role := roles[i]
|
||||
accessor, err := meta.Accessor(&role)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
if accessor.GetLabels()["kubernetes.io/bootstrapping"] != "rbac-defaults" {
|
||||
t.Errorf("ClusterRole: %s GetLabels() = %s, want %s", accessor.GetName(), accessor.GetLabels(), map[string]string{"kubernetes.io/bootstrapping": "rbac-defaults"})
|
||||
}
|
||||
}
|
||||
|
||||
rolebindings := bootstrappolicy.ClusterRoleBindings()
|
||||
for i := range rolebindings {
|
||||
rolebinding := rolebindings[i]
|
||||
accessor, err := meta.Accessor(&rolebinding)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if got, want := accessor.GetLabels(), map[string]string{"kubernetes.io/bootstrapping": "rbac-defaults"}; !reflect.DeepEqual(got, want) {
|
||||
t.Errorf("ClusterRoleBinding: %s GetLabels() = %s, want %s", accessor.GetName(), got, want)
|
||||
}
|
||||
}
|
||||
}
|
160
vendor/k8s.io/kubernetes/plugin/pkg/auth/authorizer/rbac/bootstrappolicy/testdata/cluster-role-bindings.yaml
generated
vendored
Normal file
160
vendor/k8s.io/kubernetes/plugin/pkg/auth/authorizer/rbac/bootstrappolicy/testdata/cluster-role-bindings.yaml
generated
vendored
Normal file
@ -0,0 +1,160 @@
|
||||
apiVersion: v1
|
||||
items:
|
||||
- apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRoleBinding
|
||||
metadata:
|
||||
annotations:
|
||||
rbac.authorization.kubernetes.io/autoupdate: "true"
|
||||
creationTimestamp: null
|
||||
labels:
|
||||
kubernetes.io/bootstrapping: rbac-defaults
|
||||
name: cluster-admin
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: ClusterRole
|
||||
name: cluster-admin
|
||||
subjects:
|
||||
- apiGroup: rbac.authorization.k8s.io
|
||||
kind: Group
|
||||
name: system:masters
|
||||
- apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRoleBinding
|
||||
metadata:
|
||||
annotations:
|
||||
rbac.authorization.kubernetes.io/autoupdate: "true"
|
||||
creationTimestamp: null
|
||||
labels:
|
||||
kubernetes.io/bootstrapping: rbac-defaults
|
||||
name: system:aws-cloud-provider
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: ClusterRole
|
||||
name: system:aws-cloud-provider
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: aws-cloud-provider
|
||||
namespace: kube-system
|
||||
- apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRoleBinding
|
||||
metadata:
|
||||
annotations:
|
||||
rbac.authorization.kubernetes.io/autoupdate: "true"
|
||||
creationTimestamp: null
|
||||
labels:
|
||||
kubernetes.io/bootstrapping: rbac-defaults
|
||||
name: system:basic-user
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: ClusterRole
|
||||
name: system:basic-user
|
||||
subjects:
|
||||
- apiGroup: rbac.authorization.k8s.io
|
||||
kind: Group
|
||||
name: system:authenticated
|
||||
- apiGroup: rbac.authorization.k8s.io
|
||||
kind: Group
|
||||
name: system:unauthenticated
|
||||
- apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRoleBinding
|
||||
metadata:
|
||||
annotations:
|
||||
rbac.authorization.kubernetes.io/autoupdate: "true"
|
||||
creationTimestamp: null
|
||||
labels:
|
||||
kubernetes.io/bootstrapping: rbac-defaults
|
||||
name: system:discovery
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: ClusterRole
|
||||
name: system:discovery
|
||||
subjects:
|
||||
- apiGroup: rbac.authorization.k8s.io
|
||||
kind: Group
|
||||
name: system:authenticated
|
||||
- apiGroup: rbac.authorization.k8s.io
|
||||
kind: Group
|
||||
name: system:unauthenticated
|
||||
- apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRoleBinding
|
||||
metadata:
|
||||
annotations:
|
||||
rbac.authorization.kubernetes.io/autoupdate: "true"
|
||||
creationTimestamp: null
|
||||
labels:
|
||||
kubernetes.io/bootstrapping: rbac-defaults
|
||||
name: system:kube-controller-manager
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: ClusterRole
|
||||
name: system:kube-controller-manager
|
||||
subjects:
|
||||
- apiGroup: rbac.authorization.k8s.io
|
||||
kind: User
|
||||
name: system:kube-controller-manager
|
||||
- apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRoleBinding
|
||||
metadata:
|
||||
annotations:
|
||||
rbac.authorization.kubernetes.io/autoupdate: "true"
|
||||
creationTimestamp: null
|
||||
labels:
|
||||
kubernetes.io/bootstrapping: rbac-defaults
|
||||
name: system:kube-dns
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: ClusterRole
|
||||
name: system:kube-dns
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: kube-dns
|
||||
namespace: kube-system
|
||||
- apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRoleBinding
|
||||
metadata:
|
||||
annotations:
|
||||
rbac.authorization.kubernetes.io/autoupdate: "true"
|
||||
creationTimestamp: null
|
||||
labels:
|
||||
kubernetes.io/bootstrapping: rbac-defaults
|
||||
name: system:kube-scheduler
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: ClusterRole
|
||||
name: system:kube-scheduler
|
||||
subjects:
|
||||
- apiGroup: rbac.authorization.k8s.io
|
||||
kind: User
|
||||
name: system:kube-scheduler
|
||||
- apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRoleBinding
|
||||
metadata:
|
||||
annotations:
|
||||
rbac.authorization.kubernetes.io/autoupdate: "true"
|
||||
creationTimestamp: null
|
||||
labels:
|
||||
kubernetes.io/bootstrapping: rbac-defaults
|
||||
name: system:node
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: ClusterRole
|
||||
name: system:node
|
||||
subjects: null
|
||||
- apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRoleBinding
|
||||
metadata:
|
||||
annotations:
|
||||
rbac.authorization.kubernetes.io/autoupdate: "true"
|
||||
creationTimestamp: null
|
||||
labels:
|
||||
kubernetes.io/bootstrapping: rbac-defaults
|
||||
name: system:node-proxier
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: ClusterRole
|
||||
name: system:node-proxier
|
||||
subjects:
|
||||
- apiGroup: rbac.authorization.k8s.io
|
||||
kind: User
|
||||
name: system:kube-proxy
|
||||
kind: List
|
||||
metadata: {}
|
1111
vendor/k8s.io/kubernetes/plugin/pkg/auth/authorizer/rbac/bootstrappolicy/testdata/cluster-roles.yaml
generated
vendored
Normal file
1111
vendor/k8s.io/kubernetes/plugin/pkg/auth/authorizer/rbac/bootstrappolicy/testdata/cluster-roles.yaml
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
395
vendor/k8s.io/kubernetes/plugin/pkg/auth/authorizer/rbac/bootstrappolicy/testdata/controller-role-bindings.yaml
generated
vendored
Normal file
395
vendor/k8s.io/kubernetes/plugin/pkg/auth/authorizer/rbac/bootstrappolicy/testdata/controller-role-bindings.yaml
generated
vendored
Normal file
@ -0,0 +1,395 @@
|
||||
apiVersion: v1
|
||||
items:
|
||||
- apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRoleBinding
|
||||
metadata:
|
||||
annotations:
|
||||
rbac.authorization.kubernetes.io/autoupdate: "true"
|
||||
creationTimestamp: null
|
||||
labels:
|
||||
kubernetes.io/bootstrapping: rbac-defaults
|
||||
name: system:controller:attachdetach-controller
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: ClusterRole
|
||||
name: system:controller:attachdetach-controller
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: attachdetach-controller
|
||||
namespace: kube-system
|
||||
- apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRoleBinding
|
||||
metadata:
|
||||
annotations:
|
||||
rbac.authorization.kubernetes.io/autoupdate: "true"
|
||||
creationTimestamp: null
|
||||
labels:
|
||||
kubernetes.io/bootstrapping: rbac-defaults
|
||||
name: system:controller:certificate-controller
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: ClusterRole
|
||||
name: system:controller:certificate-controller
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: certificate-controller
|
||||
namespace: kube-system
|
||||
- apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRoleBinding
|
||||
metadata:
|
||||
annotations:
|
||||
rbac.authorization.kubernetes.io/autoupdate: "true"
|
||||
creationTimestamp: null
|
||||
labels:
|
||||
kubernetes.io/bootstrapping: rbac-defaults
|
||||
name: system:controller:clusterrole-aggregation-controller
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: ClusterRole
|
||||
name: system:controller:clusterrole-aggregation-controller
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: clusterrole-aggregation-controller
|
||||
namespace: kube-system
|
||||
- apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRoleBinding
|
||||
metadata:
|
||||
annotations:
|
||||
rbac.authorization.kubernetes.io/autoupdate: "true"
|
||||
creationTimestamp: null
|
||||
labels:
|
||||
kubernetes.io/bootstrapping: rbac-defaults
|
||||
name: system:controller:cronjob-controller
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: ClusterRole
|
||||
name: system:controller:cronjob-controller
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: cronjob-controller
|
||||
namespace: kube-system
|
||||
- apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRoleBinding
|
||||
metadata:
|
||||
annotations:
|
||||
rbac.authorization.kubernetes.io/autoupdate: "true"
|
||||
creationTimestamp: null
|
||||
labels:
|
||||
kubernetes.io/bootstrapping: rbac-defaults
|
||||
name: system:controller:daemon-set-controller
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: ClusterRole
|
||||
name: system:controller:daemon-set-controller
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: daemon-set-controller
|
||||
namespace: kube-system
|
||||
- apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRoleBinding
|
||||
metadata:
|
||||
annotations:
|
||||
rbac.authorization.kubernetes.io/autoupdate: "true"
|
||||
creationTimestamp: null
|
||||
labels:
|
||||
kubernetes.io/bootstrapping: rbac-defaults
|
||||
name: system:controller:deployment-controller
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: ClusterRole
|
||||
name: system:controller:deployment-controller
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: deployment-controller
|
||||
namespace: kube-system
|
||||
- apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRoleBinding
|
||||
metadata:
|
||||
annotations:
|
||||
rbac.authorization.kubernetes.io/autoupdate: "true"
|
||||
creationTimestamp: null
|
||||
labels:
|
||||
kubernetes.io/bootstrapping: rbac-defaults
|
||||
name: system:controller:disruption-controller
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: ClusterRole
|
||||
name: system:controller:disruption-controller
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: disruption-controller
|
||||
namespace: kube-system
|
||||
- apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRoleBinding
|
||||
metadata:
|
||||
annotations:
|
||||
rbac.authorization.kubernetes.io/autoupdate: "true"
|
||||
creationTimestamp: null
|
||||
labels:
|
||||
kubernetes.io/bootstrapping: rbac-defaults
|
||||
name: system:controller:endpoint-controller
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: ClusterRole
|
||||
name: system:controller:endpoint-controller
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: endpoint-controller
|
||||
namespace: kube-system
|
||||
- apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRoleBinding
|
||||
metadata:
|
||||
annotations:
|
||||
rbac.authorization.kubernetes.io/autoupdate: "true"
|
||||
creationTimestamp: null
|
||||
labels:
|
||||
kubernetes.io/bootstrapping: rbac-defaults
|
||||
name: system:controller:generic-garbage-collector
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: ClusterRole
|
||||
name: system:controller:generic-garbage-collector
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: generic-garbage-collector
|
||||
namespace: kube-system
|
||||
- apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRoleBinding
|
||||
metadata:
|
||||
annotations:
|
||||
rbac.authorization.kubernetes.io/autoupdate: "true"
|
||||
creationTimestamp: null
|
||||
labels:
|
||||
kubernetes.io/bootstrapping: rbac-defaults
|
||||
name: system:controller:horizontal-pod-autoscaler
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: ClusterRole
|
||||
name: system:controller:horizontal-pod-autoscaler
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: horizontal-pod-autoscaler
|
||||
namespace: kube-system
|
||||
- apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRoleBinding
|
||||
metadata:
|
||||
annotations:
|
||||
rbac.authorization.kubernetes.io/autoupdate: "true"
|
||||
creationTimestamp: null
|
||||
labels:
|
||||
kubernetes.io/bootstrapping: rbac-defaults
|
||||
name: system:controller:job-controller
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: ClusterRole
|
||||
name: system:controller:job-controller
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: job-controller
|
||||
namespace: kube-system
|
||||
- apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRoleBinding
|
||||
metadata:
|
||||
annotations:
|
||||
rbac.authorization.kubernetes.io/autoupdate: "true"
|
||||
creationTimestamp: null
|
||||
labels:
|
||||
kubernetes.io/bootstrapping: rbac-defaults
|
||||
name: system:controller:namespace-controller
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: ClusterRole
|
||||
name: system:controller:namespace-controller
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: namespace-controller
|
||||
namespace: kube-system
|
||||
- apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRoleBinding
|
||||
metadata:
|
||||
annotations:
|
||||
rbac.authorization.kubernetes.io/autoupdate: "true"
|
||||
creationTimestamp: null
|
||||
labels:
|
||||
kubernetes.io/bootstrapping: rbac-defaults
|
||||
name: system:controller:node-controller
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: ClusterRole
|
||||
name: system:controller:node-controller
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: node-controller
|
||||
namespace: kube-system
|
||||
- apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRoleBinding
|
||||
metadata:
|
||||
annotations:
|
||||
rbac.authorization.kubernetes.io/autoupdate: "true"
|
||||
creationTimestamp: null
|
||||
labels:
|
||||
kubernetes.io/bootstrapping: rbac-defaults
|
||||
name: system:controller:persistent-volume-binder
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: ClusterRole
|
||||
name: system:controller:persistent-volume-binder
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: persistent-volume-binder
|
||||
namespace: kube-system
|
||||
- apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRoleBinding
|
||||
metadata:
|
||||
annotations:
|
||||
rbac.authorization.kubernetes.io/autoupdate: "true"
|
||||
creationTimestamp: null
|
||||
labels:
|
||||
kubernetes.io/bootstrapping: rbac-defaults
|
||||
name: system:controller:pod-garbage-collector
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: ClusterRole
|
||||
name: system:controller:pod-garbage-collector
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: pod-garbage-collector
|
||||
namespace: kube-system
|
||||
- apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRoleBinding
|
||||
metadata:
|
||||
annotations:
|
||||
rbac.authorization.kubernetes.io/autoupdate: "true"
|
||||
creationTimestamp: null
|
||||
labels:
|
||||
kubernetes.io/bootstrapping: rbac-defaults
|
||||
name: system:controller:replicaset-controller
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: ClusterRole
|
||||
name: system:controller:replicaset-controller
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: replicaset-controller
|
||||
namespace: kube-system
|
||||
- apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRoleBinding
|
||||
metadata:
|
||||
annotations:
|
||||
rbac.authorization.kubernetes.io/autoupdate: "true"
|
||||
creationTimestamp: null
|
||||
labels:
|
||||
kubernetes.io/bootstrapping: rbac-defaults
|
||||
name: system:controller:replication-controller
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: ClusterRole
|
||||
name: system:controller:replication-controller
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: replication-controller
|
||||
namespace: kube-system
|
||||
- apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRoleBinding
|
||||
metadata:
|
||||
annotations:
|
||||
rbac.authorization.kubernetes.io/autoupdate: "true"
|
||||
creationTimestamp: null
|
||||
labels:
|
||||
kubernetes.io/bootstrapping: rbac-defaults
|
||||
name: system:controller:resourcequota-controller
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: ClusterRole
|
||||
name: system:controller:resourcequota-controller
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: resourcequota-controller
|
||||
namespace: kube-system
|
||||
- apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRoleBinding
|
||||
metadata:
|
||||
annotations:
|
||||
rbac.authorization.kubernetes.io/autoupdate: "true"
|
||||
creationTimestamp: null
|
||||
labels:
|
||||
kubernetes.io/bootstrapping: rbac-defaults
|
||||
name: system:controller:route-controller
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: ClusterRole
|
||||
name: system:controller:route-controller
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: route-controller
|
||||
namespace: kube-system
|
||||
- apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRoleBinding
|
||||
metadata:
|
||||
annotations:
|
||||
rbac.authorization.kubernetes.io/autoupdate: "true"
|
||||
creationTimestamp: null
|
||||
labels:
|
||||
kubernetes.io/bootstrapping: rbac-defaults
|
||||
name: system:controller:service-account-controller
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: ClusterRole
|
||||
name: system:controller:service-account-controller
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: service-account-controller
|
||||
namespace: kube-system
|
||||
- apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRoleBinding
|
||||
metadata:
|
||||
annotations:
|
||||
rbac.authorization.kubernetes.io/autoupdate: "true"
|
||||
creationTimestamp: null
|
||||
labels:
|
||||
kubernetes.io/bootstrapping: rbac-defaults
|
||||
name: system:controller:service-controller
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: ClusterRole
|
||||
name: system:controller:service-controller
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: service-controller
|
||||
namespace: kube-system
|
||||
- apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRoleBinding
|
||||
metadata:
|
||||
annotations:
|
||||
rbac.authorization.kubernetes.io/autoupdate: "true"
|
||||
creationTimestamp: null
|
||||
labels:
|
||||
kubernetes.io/bootstrapping: rbac-defaults
|
||||
name: system:controller:statefulset-controller
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: ClusterRole
|
||||
name: system:controller:statefulset-controller
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: statefulset-controller
|
||||
namespace: kube-system
|
||||
- apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRoleBinding
|
||||
metadata:
|
||||
annotations:
|
||||
rbac.authorization.kubernetes.io/autoupdate: "true"
|
||||
creationTimestamp: null
|
||||
labels:
|
||||
kubernetes.io/bootstrapping: rbac-defaults
|
||||
name: system:controller:ttl-controller
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: ClusterRole
|
||||
name: system:controller:ttl-controller
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: ttl-controller
|
||||
namespace: kube-system
|
||||
kind: List
|
||||
metadata: {}
|
1092
vendor/k8s.io/kubernetes/plugin/pkg/auth/authorizer/rbac/bootstrappolicy/testdata/controller-roles.yaml
generated
vendored
Normal file
1092
vendor/k8s.io/kubernetes/plugin/pkg/auth/authorizer/rbac/bootstrappolicy/testdata/controller-roles.yaml
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
112
vendor/k8s.io/kubernetes/plugin/pkg/auth/authorizer/rbac/bootstrappolicy/testdata/namespace-role-bindings.yaml
generated
vendored
Normal file
112
vendor/k8s.io/kubernetes/plugin/pkg/auth/authorizer/rbac/bootstrappolicy/testdata/namespace-role-bindings.yaml
generated
vendored
Normal file
@ -0,0 +1,112 @@
|
||||
apiVersion: v1
|
||||
items:
|
||||
- apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: RoleBinding
|
||||
metadata:
|
||||
annotations:
|
||||
rbac.authorization.kubernetes.io/autoupdate: "true"
|
||||
creationTimestamp: null
|
||||
labels:
|
||||
kubernetes.io/bootstrapping: rbac-defaults
|
||||
name: system:controller:bootstrap-signer
|
||||
namespace: kube-public
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: Role
|
||||
name: system:controller:bootstrap-signer
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: bootstrap-signer
|
||||
namespace: kube-system
|
||||
- apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: RoleBinding
|
||||
metadata:
|
||||
annotations:
|
||||
rbac.authorization.kubernetes.io/autoupdate: "true"
|
||||
creationTimestamp: null
|
||||
labels:
|
||||
kubernetes.io/bootstrapping: rbac-defaults
|
||||
name: system::leader-locking-kube-controller-manager
|
||||
namespace: kube-system
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: Role
|
||||
name: system::leader-locking-kube-controller-manager
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: kube-controller-manager
|
||||
namespace: kube-system
|
||||
- apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: RoleBinding
|
||||
metadata:
|
||||
annotations:
|
||||
rbac.authorization.kubernetes.io/autoupdate: "true"
|
||||
creationTimestamp: null
|
||||
labels:
|
||||
kubernetes.io/bootstrapping: rbac-defaults
|
||||
name: system::leader-locking-kube-scheduler
|
||||
namespace: kube-system
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: Role
|
||||
name: system::leader-locking-kube-scheduler
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: kube-scheduler
|
||||
namespace: kube-system
|
||||
- apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: RoleBinding
|
||||
metadata:
|
||||
annotations:
|
||||
rbac.authorization.kubernetes.io/autoupdate: "true"
|
||||
creationTimestamp: null
|
||||
labels:
|
||||
kubernetes.io/bootstrapping: rbac-defaults
|
||||
name: system:controller:bootstrap-signer
|
||||
namespace: kube-system
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: Role
|
||||
name: system:controller:bootstrap-signer
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: bootstrap-signer
|
||||
namespace: kube-system
|
||||
- apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: RoleBinding
|
||||
metadata:
|
||||
annotations:
|
||||
rbac.authorization.kubernetes.io/autoupdate: "true"
|
||||
creationTimestamp: null
|
||||
labels:
|
||||
kubernetes.io/bootstrapping: rbac-defaults
|
||||
name: system:controller:cloud-provider
|
||||
namespace: kube-system
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: Role
|
||||
name: system:controller:cloud-provider
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: cloud-provider
|
||||
namespace: kube-system
|
||||
- apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: RoleBinding
|
||||
metadata:
|
||||
annotations:
|
||||
rbac.authorization.kubernetes.io/autoupdate: "true"
|
||||
creationTimestamp: null
|
||||
labels:
|
||||
kubernetes.io/bootstrapping: rbac-defaults
|
||||
name: system:controller:token-cleaner
|
||||
namespace: kube-system
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: Role
|
||||
name: system:controller:token-cleaner
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: token-cleaner
|
||||
namespace: kube-system
|
||||
kind: List
|
||||
metadata: {}
|
177
vendor/k8s.io/kubernetes/plugin/pkg/auth/authorizer/rbac/bootstrappolicy/testdata/namespace-roles.yaml
generated
vendored
Normal file
177
vendor/k8s.io/kubernetes/plugin/pkg/auth/authorizer/rbac/bootstrappolicy/testdata/namespace-roles.yaml
generated
vendored
Normal file
@ -0,0 +1,177 @@
|
||||
apiVersion: v1
|
||||
items:
|
||||
- apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: Role
|
||||
metadata:
|
||||
annotations:
|
||||
rbac.authorization.kubernetes.io/autoupdate: "true"
|
||||
creationTimestamp: null
|
||||
labels:
|
||||
kubernetes.io/bootstrapping: rbac-defaults
|
||||
name: system:controller:bootstrap-signer
|
||||
namespace: kube-public
|
||||
rules:
|
||||
- apiGroups:
|
||||
- ""
|
||||
resources:
|
||||
- configmaps
|
||||
verbs:
|
||||
- get
|
||||
- list
|
||||
- watch
|
||||
- apiGroups:
|
||||
- ""
|
||||
resourceNames:
|
||||
- cluster-info
|
||||
resources:
|
||||
- configmaps
|
||||
verbs:
|
||||
- update
|
||||
- apiGroups:
|
||||
- ""
|
||||
resources:
|
||||
- events
|
||||
verbs:
|
||||
- create
|
||||
- patch
|
||||
- update
|
||||
- apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: Role
|
||||
metadata:
|
||||
annotations:
|
||||
rbac.authorization.kubernetes.io/autoupdate: "true"
|
||||
creationTimestamp: null
|
||||
labels:
|
||||
kubernetes.io/bootstrapping: rbac-defaults
|
||||
name: extension-apiserver-authentication-reader
|
||||
namespace: kube-system
|
||||
rules:
|
||||
- apiGroups:
|
||||
- ""
|
||||
resourceNames:
|
||||
- extension-apiserver-authentication
|
||||
resources:
|
||||
- configmaps
|
||||
verbs:
|
||||
- get
|
||||
- apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: Role
|
||||
metadata:
|
||||
annotations:
|
||||
rbac.authorization.kubernetes.io/autoupdate: "true"
|
||||
creationTimestamp: null
|
||||
labels:
|
||||
kubernetes.io/bootstrapping: rbac-defaults
|
||||
name: system::leader-locking-kube-controller-manager
|
||||
namespace: kube-system
|
||||
rules:
|
||||
- apiGroups:
|
||||
- ""
|
||||
resources:
|
||||
- configmaps
|
||||
verbs:
|
||||
- watch
|
||||
- apiGroups:
|
||||
- ""
|
||||
resourceNames:
|
||||
- kube-controller-manager
|
||||
resources:
|
||||
- configmaps
|
||||
verbs:
|
||||
- get
|
||||
- update
|
||||
- apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: Role
|
||||
metadata:
|
||||
annotations:
|
||||
rbac.authorization.kubernetes.io/autoupdate: "true"
|
||||
creationTimestamp: null
|
||||
labels:
|
||||
kubernetes.io/bootstrapping: rbac-defaults
|
||||
name: system::leader-locking-kube-scheduler
|
||||
namespace: kube-system
|
||||
rules:
|
||||
- apiGroups:
|
||||
- ""
|
||||
resources:
|
||||
- configmaps
|
||||
verbs:
|
||||
- watch
|
||||
- apiGroups:
|
||||
- ""
|
||||
resourceNames:
|
||||
- kube-scheduler
|
||||
resources:
|
||||
- configmaps
|
||||
verbs:
|
||||
- get
|
||||
- update
|
||||
- apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: Role
|
||||
metadata:
|
||||
annotations:
|
||||
rbac.authorization.kubernetes.io/autoupdate: "true"
|
||||
creationTimestamp: null
|
||||
labels:
|
||||
kubernetes.io/bootstrapping: rbac-defaults
|
||||
name: system:controller:bootstrap-signer
|
||||
namespace: kube-system
|
||||
rules:
|
||||
- apiGroups:
|
||||
- ""
|
||||
resources:
|
||||
- secrets
|
||||
verbs:
|
||||
- get
|
||||
- list
|
||||
- watch
|
||||
- apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: Role
|
||||
metadata:
|
||||
annotations:
|
||||
rbac.authorization.kubernetes.io/autoupdate: "true"
|
||||
creationTimestamp: null
|
||||
labels:
|
||||
kubernetes.io/bootstrapping: rbac-defaults
|
||||
name: system:controller:cloud-provider
|
||||
namespace: kube-system
|
||||
rules:
|
||||
- apiGroups:
|
||||
- ""
|
||||
resources:
|
||||
- configmaps
|
||||
verbs:
|
||||
- create
|
||||
- get
|
||||
- list
|
||||
- watch
|
||||
- apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: Role
|
||||
metadata:
|
||||
annotations:
|
||||
rbac.authorization.kubernetes.io/autoupdate: "true"
|
||||
creationTimestamp: null
|
||||
labels:
|
||||
kubernetes.io/bootstrapping: rbac-defaults
|
||||
name: system:controller:token-cleaner
|
||||
namespace: kube-system
|
||||
rules:
|
||||
- apiGroups:
|
||||
- ""
|
||||
resources:
|
||||
- secrets
|
||||
verbs:
|
||||
- delete
|
||||
- get
|
||||
- list
|
||||
- watch
|
||||
- apiGroups:
|
||||
- ""
|
||||
resources:
|
||||
- events
|
||||
verbs:
|
||||
- create
|
||||
- patch
|
||||
- update
|
||||
kind: List
|
||||
metadata: {}
|
222
vendor/k8s.io/kubernetes/plugin/pkg/auth/authorizer/rbac/rbac.go
generated
vendored
Normal file
222
vendor/k8s.io/kubernetes/plugin/pkg/auth/authorizer/rbac/rbac.go
generated
vendored
Normal file
@ -0,0 +1,222 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
// Package rbac implements the authorizer.Authorizer interface using roles base access control.
|
||||
package rbac
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/golang/glog"
|
||||
|
||||
"bytes"
|
||||
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
utilerrors "k8s.io/apimachinery/pkg/util/errors"
|
||||
"k8s.io/apiserver/pkg/authentication/user"
|
||||
"k8s.io/apiserver/pkg/authorization/authorizer"
|
||||
"k8s.io/kubernetes/pkg/apis/rbac"
|
||||
rbaclisters "k8s.io/kubernetes/pkg/client/listers/rbac/internalversion"
|
||||
rbacregistryvalidation "k8s.io/kubernetes/pkg/registry/rbac/validation"
|
||||
)
|
||||
|
||||
type RequestToRuleMapper interface {
|
||||
// RulesFor returns all known PolicyRules and any errors that happened while locating those rules.
|
||||
// Any rule returned is still valid, since rules are deny by default. If you can pass with the rules
|
||||
// supplied, you do not have to fail the request. If you cannot, you should indicate the error along
|
||||
// with your denial.
|
||||
RulesFor(subject user.Info, namespace string) ([]rbac.PolicyRule, error)
|
||||
|
||||
// VisitRulesFor invokes visitor() with each rule that applies to a given user in a given namespace,
|
||||
// and each error encountered resolving those rules. Rule may be nil if err is non-nil.
|
||||
// If visitor() returns false, visiting is short-circuited.
|
||||
VisitRulesFor(user user.Info, namespace string, visitor func(rule *rbac.PolicyRule, err error) bool)
|
||||
}
|
||||
|
||||
type RBACAuthorizer struct {
|
||||
authorizationRuleResolver RequestToRuleMapper
|
||||
}
|
||||
|
||||
// authorizingVisitor short-circuits once allowed, and collects any resolution errors encountered
|
||||
type authorizingVisitor struct {
|
||||
requestAttributes authorizer.Attributes
|
||||
|
||||
allowed bool
|
||||
errors []error
|
||||
}
|
||||
|
||||
func (v *authorizingVisitor) visit(rule *rbac.PolicyRule, err error) bool {
|
||||
if rule != nil && RuleAllows(v.requestAttributes, rule) {
|
||||
v.allowed = true
|
||||
return false
|
||||
}
|
||||
if err != nil {
|
||||
v.errors = append(v.errors, err)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (r *RBACAuthorizer) Authorize(requestAttributes authorizer.Attributes) (authorizer.Decision, string, error) {
|
||||
ruleCheckingVisitor := &authorizingVisitor{requestAttributes: requestAttributes}
|
||||
|
||||
r.authorizationRuleResolver.VisitRulesFor(requestAttributes.GetUser(), requestAttributes.GetNamespace(), ruleCheckingVisitor.visit)
|
||||
if ruleCheckingVisitor.allowed {
|
||||
return authorizer.DecisionAllow, "", nil
|
||||
}
|
||||
|
||||
// Build a detailed log of the denial.
|
||||
// Make the whole block conditional so we don't do a lot of string-building we won't use.
|
||||
if glog.V(2) {
|
||||
var operation string
|
||||
if requestAttributes.IsResourceRequest() {
|
||||
b := &bytes.Buffer{}
|
||||
b.WriteString(`"`)
|
||||
b.WriteString(requestAttributes.GetVerb())
|
||||
b.WriteString(`" resource "`)
|
||||
b.WriteString(requestAttributes.GetResource())
|
||||
if len(requestAttributes.GetAPIGroup()) > 0 {
|
||||
b.WriteString(`.`)
|
||||
b.WriteString(requestAttributes.GetAPIGroup())
|
||||
}
|
||||
if len(requestAttributes.GetSubresource()) > 0 {
|
||||
b.WriteString(`/`)
|
||||
b.WriteString(requestAttributes.GetSubresource())
|
||||
}
|
||||
b.WriteString(`"`)
|
||||
if len(requestAttributes.GetName()) > 0 {
|
||||
b.WriteString(` named "`)
|
||||
b.WriteString(requestAttributes.GetName())
|
||||
b.WriteString(`"`)
|
||||
}
|
||||
operation = b.String()
|
||||
} else {
|
||||
operation = fmt.Sprintf("%q nonResourceURL %q", requestAttributes.GetVerb(), requestAttributes.GetPath())
|
||||
}
|
||||
|
||||
var scope string
|
||||
if ns := requestAttributes.GetNamespace(); len(ns) > 0 {
|
||||
scope = fmt.Sprintf("in namespace %q", ns)
|
||||
} else {
|
||||
scope = "cluster-wide"
|
||||
}
|
||||
|
||||
glog.Infof("RBAC DENY: user %q groups %q cannot %s %s", requestAttributes.GetUser().GetName(), requestAttributes.GetUser().GetGroups(), operation, scope)
|
||||
}
|
||||
|
||||
reason := ""
|
||||
if len(ruleCheckingVisitor.errors) > 0 {
|
||||
reason = fmt.Sprintf("%v", utilerrors.NewAggregate(ruleCheckingVisitor.errors))
|
||||
}
|
||||
return authorizer.DecisionNoOpinion, reason, nil
|
||||
}
|
||||
|
||||
func (r *RBACAuthorizer) RulesFor(user user.Info, namespace string) ([]authorizer.ResourceRuleInfo, []authorizer.NonResourceRuleInfo, bool, error) {
|
||||
var (
|
||||
resourceRules []authorizer.ResourceRuleInfo
|
||||
nonResourceRules []authorizer.NonResourceRuleInfo
|
||||
)
|
||||
|
||||
policyRules, err := r.authorizationRuleResolver.RulesFor(user, namespace)
|
||||
for _, policyRule := range policyRules {
|
||||
if len(policyRule.Resources) > 0 {
|
||||
r := authorizer.DefaultResourceRuleInfo{
|
||||
Verbs: policyRule.Verbs,
|
||||
APIGroups: policyRule.APIGroups,
|
||||
Resources: policyRule.Resources,
|
||||
ResourceNames: policyRule.ResourceNames,
|
||||
}
|
||||
var resourceRule authorizer.ResourceRuleInfo = &r
|
||||
resourceRules = append(resourceRules, resourceRule)
|
||||
}
|
||||
if len(policyRule.NonResourceURLs) > 0 {
|
||||
r := authorizer.DefaultNonResourceRuleInfo{
|
||||
Verbs: policyRule.Verbs,
|
||||
NonResourceURLs: policyRule.NonResourceURLs,
|
||||
}
|
||||
var nonResourceRule authorizer.NonResourceRuleInfo = &r
|
||||
nonResourceRules = append(nonResourceRules, nonResourceRule)
|
||||
}
|
||||
}
|
||||
return resourceRules, nonResourceRules, false, err
|
||||
}
|
||||
|
||||
func New(roles rbacregistryvalidation.RoleGetter, roleBindings rbacregistryvalidation.RoleBindingLister, clusterRoles rbacregistryvalidation.ClusterRoleGetter, clusterRoleBindings rbacregistryvalidation.ClusterRoleBindingLister) *RBACAuthorizer {
|
||||
authorizer := &RBACAuthorizer{
|
||||
authorizationRuleResolver: rbacregistryvalidation.NewDefaultRuleResolver(
|
||||
roles, roleBindings, clusterRoles, clusterRoleBindings,
|
||||
),
|
||||
}
|
||||
return authorizer
|
||||
}
|
||||
|
||||
func RulesAllow(requestAttributes authorizer.Attributes, rules ...rbac.PolicyRule) bool {
|
||||
for i := range rules {
|
||||
if RuleAllows(requestAttributes, &rules[i]) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func RuleAllows(requestAttributes authorizer.Attributes, rule *rbac.PolicyRule) bool {
|
||||
if requestAttributes.IsResourceRequest() {
|
||||
combinedResource := requestAttributes.GetResource()
|
||||
if len(requestAttributes.GetSubresource()) > 0 {
|
||||
combinedResource = requestAttributes.GetResource() + "/" + requestAttributes.GetSubresource()
|
||||
}
|
||||
|
||||
return rbac.VerbMatches(rule, requestAttributes.GetVerb()) &&
|
||||
rbac.APIGroupMatches(rule, requestAttributes.GetAPIGroup()) &&
|
||||
rbac.ResourceMatches(rule, combinedResource, requestAttributes.GetSubresource()) &&
|
||||
rbac.ResourceNameMatches(rule, requestAttributes.GetName())
|
||||
}
|
||||
|
||||
return rbac.VerbMatches(rule, requestAttributes.GetVerb()) &&
|
||||
rbac.NonResourceURLMatches(rule, requestAttributes.GetPath())
|
||||
}
|
||||
|
||||
type RoleGetter struct {
|
||||
Lister rbaclisters.RoleLister
|
||||
}
|
||||
|
||||
func (g *RoleGetter) GetRole(namespace, name string) (*rbac.Role, error) {
|
||||
return g.Lister.Roles(namespace).Get(name)
|
||||
}
|
||||
|
||||
type RoleBindingLister struct {
|
||||
Lister rbaclisters.RoleBindingLister
|
||||
}
|
||||
|
||||
func (l *RoleBindingLister) ListRoleBindings(namespace string) ([]*rbac.RoleBinding, error) {
|
||||
return l.Lister.RoleBindings(namespace).List(labels.Everything())
|
||||
}
|
||||
|
||||
type ClusterRoleGetter struct {
|
||||
Lister rbaclisters.ClusterRoleLister
|
||||
}
|
||||
|
||||
func (g *ClusterRoleGetter) GetClusterRole(name string) (*rbac.ClusterRole, error) {
|
||||
return g.Lister.Get(name)
|
||||
}
|
||||
|
||||
type ClusterRoleBindingLister struct {
|
||||
Lister rbaclisters.ClusterRoleBindingLister
|
||||
}
|
||||
|
||||
func (l *ClusterRoleBindingLister) ListClusterRoleBindings() ([]*rbac.ClusterRoleBinding, error) {
|
||||
return l.Lister.List(labels.Everything())
|
||||
}
|
522
vendor/k8s.io/kubernetes/plugin/pkg/auth/authorizer/rbac/rbac_test.go
generated
vendored
Normal file
522
vendor/k8s.io/kubernetes/plugin/pkg/auth/authorizer/rbac/rbac_test.go
generated
vendored
Normal file
@ -0,0 +1,522 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package rbac
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apiserver/pkg/authentication/user"
|
||||
"k8s.io/apiserver/pkg/authorization/authorizer"
|
||||
"k8s.io/kubernetes/pkg/apis/rbac"
|
||||
rbacregistryvalidation "k8s.io/kubernetes/pkg/registry/rbac/validation"
|
||||
"k8s.io/kubernetes/plugin/pkg/auth/authorizer/rbac/bootstrappolicy"
|
||||
)
|
||||
|
||||
func newRule(verbs, apiGroups, resources, nonResourceURLs string) rbac.PolicyRule {
|
||||
return rbac.PolicyRule{
|
||||
Verbs: strings.Split(verbs, ","),
|
||||
APIGroups: strings.Split(apiGroups, ","),
|
||||
Resources: strings.Split(resources, ","),
|
||||
NonResourceURLs: strings.Split(nonResourceURLs, ","),
|
||||
}
|
||||
}
|
||||
|
||||
func newRole(name, namespace string, rules ...rbac.PolicyRule) *rbac.Role {
|
||||
return &rbac.Role{ObjectMeta: metav1.ObjectMeta{Namespace: namespace, Name: name}, Rules: rules}
|
||||
}
|
||||
|
||||
func newClusterRole(name string, rules ...rbac.PolicyRule) *rbac.ClusterRole {
|
||||
return &rbac.ClusterRole{ObjectMeta: metav1.ObjectMeta{Name: name}, Rules: rules}
|
||||
}
|
||||
|
||||
const (
|
||||
bindToRole uint16 = 0x0
|
||||
bindToClusterRole uint16 = 0x1
|
||||
)
|
||||
|
||||
func newClusterRoleBinding(roleName string, subjects ...string) *rbac.ClusterRoleBinding {
|
||||
r := &rbac.ClusterRoleBinding{
|
||||
ObjectMeta: metav1.ObjectMeta{},
|
||||
RoleRef: rbac.RoleRef{
|
||||
APIGroup: rbac.GroupName,
|
||||
Kind: "ClusterRole", // ClusterRoleBindings can only refer to ClusterRole
|
||||
Name: roleName,
|
||||
},
|
||||
}
|
||||
|
||||
r.Subjects = make([]rbac.Subject, len(subjects))
|
||||
for i, subject := range subjects {
|
||||
split := strings.SplitN(subject, ":", 2)
|
||||
r.Subjects[i].Kind, r.Subjects[i].Name = split[0], split[1]
|
||||
|
||||
switch r.Subjects[i].Kind {
|
||||
case rbac.ServiceAccountKind:
|
||||
r.Subjects[i].APIGroup = ""
|
||||
case rbac.UserKind, rbac.GroupKind:
|
||||
r.Subjects[i].APIGroup = rbac.GroupName
|
||||
default:
|
||||
panic(fmt.Errorf("invalid kind %s", r.Subjects[i].Kind))
|
||||
}
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
func newRoleBinding(namespace, roleName string, bindType uint16, subjects ...string) *rbac.RoleBinding {
|
||||
r := &rbac.RoleBinding{ObjectMeta: metav1.ObjectMeta{Namespace: namespace}}
|
||||
|
||||
switch bindType {
|
||||
case bindToRole:
|
||||
r.RoleRef = rbac.RoleRef{APIGroup: rbac.GroupName, Kind: "Role", Name: roleName}
|
||||
case bindToClusterRole:
|
||||
r.RoleRef = rbac.RoleRef{APIGroup: rbac.GroupName, Kind: "ClusterRole", Name: roleName}
|
||||
}
|
||||
|
||||
r.Subjects = make([]rbac.Subject, len(subjects))
|
||||
for i, subject := range subjects {
|
||||
split := strings.SplitN(subject, ":", 2)
|
||||
r.Subjects[i].Kind, r.Subjects[i].Name = split[0], split[1]
|
||||
|
||||
switch r.Subjects[i].Kind {
|
||||
case rbac.ServiceAccountKind:
|
||||
r.Subjects[i].APIGroup = ""
|
||||
case rbac.UserKind, rbac.GroupKind:
|
||||
r.Subjects[i].APIGroup = rbac.GroupName
|
||||
default:
|
||||
panic(fmt.Errorf("invalid kind %s", r.Subjects[i].Kind))
|
||||
}
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
type defaultAttributes struct {
|
||||
user string
|
||||
groups string
|
||||
verb string
|
||||
resource string
|
||||
subresource string
|
||||
namespace string
|
||||
apiGroup string
|
||||
}
|
||||
|
||||
func (d *defaultAttributes) String() string {
|
||||
return fmt.Sprintf("user=(%s), groups=(%s), verb=(%s), resource=(%s), namespace=(%s), apiGroup=(%s)",
|
||||
d.user, strings.Split(d.groups, ","), d.verb, d.resource, d.namespace, d.apiGroup)
|
||||
}
|
||||
|
||||
func (d *defaultAttributes) GetUser() user.Info {
|
||||
return &user.DefaultInfo{Name: d.user, Groups: strings.Split(d.groups, ",")}
|
||||
}
|
||||
func (d *defaultAttributes) GetVerb() string { return d.verb }
|
||||
func (d *defaultAttributes) IsReadOnly() bool { return d.verb == "get" || d.verb == "watch" }
|
||||
func (d *defaultAttributes) GetNamespace() string { return d.namespace }
|
||||
func (d *defaultAttributes) GetResource() string { return d.resource }
|
||||
func (d *defaultAttributes) GetSubresource() string { return d.subresource }
|
||||
func (d *defaultAttributes) GetName() string { return "" }
|
||||
func (d *defaultAttributes) GetAPIGroup() string { return d.apiGroup }
|
||||
func (d *defaultAttributes) GetAPIVersion() string { return "" }
|
||||
func (d *defaultAttributes) IsResourceRequest() bool { return true }
|
||||
func (d *defaultAttributes) GetPath() string { return "" }
|
||||
|
||||
func TestAuthorizer(t *testing.T) {
|
||||
tests := []struct {
|
||||
roles []*rbac.Role
|
||||
roleBindings []*rbac.RoleBinding
|
||||
clusterRoles []*rbac.ClusterRole
|
||||
clusterRoleBindings []*rbac.ClusterRoleBinding
|
||||
|
||||
shouldPass []authorizer.Attributes
|
||||
shouldFail []authorizer.Attributes
|
||||
}{
|
||||
{
|
||||
clusterRoles: []*rbac.ClusterRole{
|
||||
newClusterRole("admin", newRule("*", "*", "*", "*")),
|
||||
},
|
||||
roleBindings: []*rbac.RoleBinding{
|
||||
newRoleBinding("ns1", "admin", bindToClusterRole, "User:admin", "Group:admins"),
|
||||
},
|
||||
shouldPass: []authorizer.Attributes{
|
||||
&defaultAttributes{"admin", "", "get", "Pods", "", "ns1", ""},
|
||||
&defaultAttributes{"admin", "", "watch", "Pods", "", "ns1", ""},
|
||||
&defaultAttributes{"admin", "group1", "watch", "Foobar", "", "ns1", ""},
|
||||
&defaultAttributes{"joe", "admins", "watch", "Foobar", "", "ns1", ""},
|
||||
&defaultAttributes{"joe", "group1,admins", "watch", "Foobar", "", "ns1", ""},
|
||||
},
|
||||
shouldFail: []authorizer.Attributes{
|
||||
&defaultAttributes{"admin", "", "GET", "Pods", "", "ns2", ""},
|
||||
&defaultAttributes{"admin", "", "GET", "Nodes", "", "", ""},
|
||||
&defaultAttributes{"admin", "admins", "GET", "Pods", "", "ns2", ""},
|
||||
&defaultAttributes{"admin", "admins", "GET", "Nodes", "", "", ""},
|
||||
},
|
||||
},
|
||||
{
|
||||
// Non-resource-url tests
|
||||
clusterRoles: []*rbac.ClusterRole{
|
||||
newClusterRole("non-resource-url-getter", newRule("get", "", "", "/apis")),
|
||||
newClusterRole("non-resource-url", newRule("*", "", "", "/apis")),
|
||||
newClusterRole("non-resource-url-prefix", newRule("get", "", "", "/apis/*")),
|
||||
},
|
||||
clusterRoleBindings: []*rbac.ClusterRoleBinding{
|
||||
newClusterRoleBinding("non-resource-url-getter", "User:foo", "Group:bar"),
|
||||
newClusterRoleBinding("non-resource-url", "User:admin", "Group:admin"),
|
||||
newClusterRoleBinding("non-resource-url-prefix", "User:prefixed", "Group:prefixed"),
|
||||
},
|
||||
shouldPass: []authorizer.Attributes{
|
||||
authorizer.AttributesRecord{User: &user.DefaultInfo{Name: "foo"}, Verb: "get", Path: "/apis"},
|
||||
authorizer.AttributesRecord{User: &user.DefaultInfo{Groups: []string{"bar"}}, Verb: "get", Path: "/apis"},
|
||||
authorizer.AttributesRecord{User: &user.DefaultInfo{Name: "admin"}, Verb: "get", Path: "/apis"},
|
||||
authorizer.AttributesRecord{User: &user.DefaultInfo{Groups: []string{"admin"}}, Verb: "get", Path: "/apis"},
|
||||
authorizer.AttributesRecord{User: &user.DefaultInfo{Name: "admin"}, Verb: "watch", Path: "/apis"},
|
||||
authorizer.AttributesRecord{User: &user.DefaultInfo{Groups: []string{"admin"}}, Verb: "watch", Path: "/apis"},
|
||||
|
||||
authorizer.AttributesRecord{User: &user.DefaultInfo{Name: "prefixed"}, Verb: "get", Path: "/apis/v1"},
|
||||
authorizer.AttributesRecord{User: &user.DefaultInfo{Groups: []string{"prefixed"}}, Verb: "get", Path: "/apis/v1"},
|
||||
authorizer.AttributesRecord{User: &user.DefaultInfo{Name: "prefixed"}, Verb: "get", Path: "/apis/v1/foobar"},
|
||||
authorizer.AttributesRecord{User: &user.DefaultInfo{Groups: []string{"prefixed"}}, Verb: "get", Path: "/apis/v1/foorbar"},
|
||||
},
|
||||
shouldFail: []authorizer.Attributes{
|
||||
// wrong verb
|
||||
authorizer.AttributesRecord{User: &user.DefaultInfo{Name: "foo"}, Verb: "watch", Path: "/apis"},
|
||||
authorizer.AttributesRecord{User: &user.DefaultInfo{Groups: []string{"bar"}}, Verb: "watch", Path: "/apis"},
|
||||
|
||||
// wrong path
|
||||
authorizer.AttributesRecord{User: &user.DefaultInfo{Name: "foo"}, Verb: "get", Path: "/api/v1"},
|
||||
authorizer.AttributesRecord{User: &user.DefaultInfo{Groups: []string{"bar"}}, Verb: "get", Path: "/api/v1"},
|
||||
authorizer.AttributesRecord{User: &user.DefaultInfo{Name: "admin"}, Verb: "get", Path: "/api/v1"},
|
||||
authorizer.AttributesRecord{User: &user.DefaultInfo{Groups: []string{"admin"}}, Verb: "get", Path: "/api/v1"},
|
||||
|
||||
// not covered by prefix
|
||||
authorizer.AttributesRecord{User: &user.DefaultInfo{Name: "prefixed"}, Verb: "get", Path: "/api/v1"},
|
||||
authorizer.AttributesRecord{User: &user.DefaultInfo{Groups: []string{"prefixed"}}, Verb: "get", Path: "/api/v1"},
|
||||
},
|
||||
},
|
||||
{
|
||||
// test subresource resolution
|
||||
clusterRoles: []*rbac.ClusterRole{
|
||||
newClusterRole("admin", newRule("*", "*", "pods", "*")),
|
||||
},
|
||||
roleBindings: []*rbac.RoleBinding{
|
||||
newRoleBinding("ns1", "admin", bindToClusterRole, "User:admin", "Group:admins"),
|
||||
},
|
||||
shouldPass: []authorizer.Attributes{
|
||||
&defaultAttributes{"admin", "", "get", "pods", "", "ns1", ""},
|
||||
},
|
||||
shouldFail: []authorizer.Attributes{
|
||||
&defaultAttributes{"admin", "", "get", "pods", "status", "ns1", ""},
|
||||
},
|
||||
},
|
||||
{
|
||||
// test subresource resolution
|
||||
clusterRoles: []*rbac.ClusterRole{
|
||||
newClusterRole("admin",
|
||||
newRule("*", "*", "pods/status", "*"),
|
||||
newRule("*", "*", "*/scale", "*"),
|
||||
),
|
||||
},
|
||||
roleBindings: []*rbac.RoleBinding{
|
||||
newRoleBinding("ns1", "admin", bindToClusterRole, "User:admin", "Group:admins"),
|
||||
},
|
||||
shouldPass: []authorizer.Attributes{
|
||||
&defaultAttributes{"admin", "", "get", "pods", "status", "ns1", ""},
|
||||
&defaultAttributes{"admin", "", "get", "pods", "scale", "ns1", ""},
|
||||
&defaultAttributes{"admin", "", "get", "deployments", "scale", "ns1", ""},
|
||||
&defaultAttributes{"admin", "", "get", "anything", "scale", "ns1", ""},
|
||||
},
|
||||
shouldFail: []authorizer.Attributes{
|
||||
&defaultAttributes{"admin", "", "get", "pods", "", "ns1", ""},
|
||||
},
|
||||
},
|
||||
}
|
||||
for i, tt := range tests {
|
||||
ruleResolver, _ := rbacregistryvalidation.NewTestRuleResolver(tt.roles, tt.roleBindings, tt.clusterRoles, tt.clusterRoleBindings)
|
||||
a := RBACAuthorizer{ruleResolver}
|
||||
for _, attr := range tt.shouldPass {
|
||||
if decision, _, _ := a.Authorize(attr); decision != authorizer.DecisionAllow {
|
||||
t.Errorf("case %d: incorrectly restricted %s", i, attr)
|
||||
}
|
||||
}
|
||||
|
||||
for _, attr := range tt.shouldFail {
|
||||
if decision, _, _ := a.Authorize(attr); decision == authorizer.DecisionAllow {
|
||||
t.Errorf("case %d: incorrectly passed %s", i, attr)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestRuleMatches(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
rule rbac.PolicyRule
|
||||
|
||||
requestsToExpected map[authorizer.AttributesRecord]bool
|
||||
}{
|
||||
{
|
||||
name: "star verb, exact match other",
|
||||
rule: rbac.NewRule("*").Groups("group1").Resources("resource1").RuleOrDie(),
|
||||
requestsToExpected: map[authorizer.AttributesRecord]bool{
|
||||
resourceRequest("verb1").Group("group1").Resource("resource1").New(): true,
|
||||
resourceRequest("verb1").Group("group2").Resource("resource1").New(): false,
|
||||
resourceRequest("verb1").Group("group1").Resource("resource2").New(): false,
|
||||
resourceRequest("verb1").Group("group2").Resource("resource2").New(): false,
|
||||
resourceRequest("verb2").Group("group1").Resource("resource1").New(): true,
|
||||
resourceRequest("verb2").Group("group2").Resource("resource1").New(): false,
|
||||
resourceRequest("verb2").Group("group1").Resource("resource2").New(): false,
|
||||
resourceRequest("verb2").Group("group2").Resource("resource2").New(): false,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "star group, exact match other",
|
||||
rule: rbac.NewRule("verb1").Groups("*").Resources("resource1").RuleOrDie(),
|
||||
requestsToExpected: map[authorizer.AttributesRecord]bool{
|
||||
resourceRequest("verb1").Group("group1").Resource("resource1").New(): true,
|
||||
resourceRequest("verb1").Group("group2").Resource("resource1").New(): true,
|
||||
resourceRequest("verb1").Group("group1").Resource("resource2").New(): false,
|
||||
resourceRequest("verb1").Group("group2").Resource("resource2").New(): false,
|
||||
resourceRequest("verb2").Group("group1").Resource("resource1").New(): false,
|
||||
resourceRequest("verb2").Group("group2").Resource("resource1").New(): false,
|
||||
resourceRequest("verb2").Group("group1").Resource("resource2").New(): false,
|
||||
resourceRequest("verb2").Group("group2").Resource("resource2").New(): false,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "star resource, exact match other",
|
||||
rule: rbac.NewRule("verb1").Groups("group1").Resources("*").RuleOrDie(),
|
||||
requestsToExpected: map[authorizer.AttributesRecord]bool{
|
||||
resourceRequest("verb1").Group("group1").Resource("resource1").New(): true,
|
||||
resourceRequest("verb1").Group("group2").Resource("resource1").New(): false,
|
||||
resourceRequest("verb1").Group("group1").Resource("resource2").New(): true,
|
||||
resourceRequest("verb1").Group("group2").Resource("resource2").New(): false,
|
||||
resourceRequest("verb2").Group("group1").Resource("resource1").New(): false,
|
||||
resourceRequest("verb2").Group("group2").Resource("resource1").New(): false,
|
||||
resourceRequest("verb2").Group("group1").Resource("resource2").New(): false,
|
||||
resourceRequest("verb2").Group("group2").Resource("resource2").New(): false,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "tuple expansion",
|
||||
rule: rbac.NewRule("verb1", "verb2").Groups("group1", "group2").Resources("resource1", "resource2").RuleOrDie(),
|
||||
requestsToExpected: map[authorizer.AttributesRecord]bool{
|
||||
resourceRequest("verb1").Group("group1").Resource("resource1").New(): true,
|
||||
resourceRequest("verb1").Group("group2").Resource("resource1").New(): true,
|
||||
resourceRequest("verb1").Group("group1").Resource("resource2").New(): true,
|
||||
resourceRequest("verb1").Group("group2").Resource("resource2").New(): true,
|
||||
resourceRequest("verb2").Group("group1").Resource("resource1").New(): true,
|
||||
resourceRequest("verb2").Group("group2").Resource("resource1").New(): true,
|
||||
resourceRequest("verb2").Group("group1").Resource("resource2").New(): true,
|
||||
resourceRequest("verb2").Group("group2").Resource("resource2").New(): true,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "subresource expansion",
|
||||
rule: rbac.NewRule("*").Groups("*").Resources("resource1/subresource1").RuleOrDie(),
|
||||
requestsToExpected: map[authorizer.AttributesRecord]bool{
|
||||
resourceRequest("verb1").Group("group1").Resource("resource1").Subresource("subresource1").New(): true,
|
||||
resourceRequest("verb1").Group("group2").Resource("resource1").Subresource("subresource2").New(): false,
|
||||
resourceRequest("verb1").Group("group1").Resource("resource2").Subresource("subresource1").New(): false,
|
||||
resourceRequest("verb1").Group("group2").Resource("resource2").Subresource("subresource1").New(): false,
|
||||
resourceRequest("verb2").Group("group1").Resource("resource1").Subresource("subresource1").New(): true,
|
||||
resourceRequest("verb2").Group("group2").Resource("resource1").Subresource("subresource2").New(): false,
|
||||
resourceRequest("verb2").Group("group1").Resource("resource2").Subresource("subresource1").New(): false,
|
||||
resourceRequest("verb2").Group("group2").Resource("resource2").Subresource("subresource1").New(): false,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "star nonresource, exact match other",
|
||||
rule: rbac.NewRule("verb1").URLs("*").RuleOrDie(),
|
||||
requestsToExpected: map[authorizer.AttributesRecord]bool{
|
||||
nonresourceRequest("verb1").URL("/foo").New(): true,
|
||||
nonresourceRequest("verb1").URL("/foo/bar").New(): true,
|
||||
nonresourceRequest("verb1").URL("/foo/baz").New(): true,
|
||||
nonresourceRequest("verb1").URL("/foo/bar/one").New(): true,
|
||||
nonresourceRequest("verb1").URL("/foo/baz/one").New(): true,
|
||||
nonresourceRequest("verb2").URL("/foo").New(): false,
|
||||
nonresourceRequest("verb2").URL("/foo/bar").New(): false,
|
||||
nonresourceRequest("verb2").URL("/foo/baz").New(): false,
|
||||
nonresourceRequest("verb2").URL("/foo/bar/one").New(): false,
|
||||
nonresourceRequest("verb2").URL("/foo/baz/one").New(): false,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "star nonresource subpath",
|
||||
rule: rbac.NewRule("verb1").URLs("/foo/*").RuleOrDie(),
|
||||
requestsToExpected: map[authorizer.AttributesRecord]bool{
|
||||
nonresourceRequest("verb1").URL("/foo").New(): false,
|
||||
nonresourceRequest("verb1").URL("/foo/bar").New(): true,
|
||||
nonresourceRequest("verb1").URL("/foo/baz").New(): true,
|
||||
nonresourceRequest("verb1").URL("/foo/bar/one").New(): true,
|
||||
nonresourceRequest("verb1").URL("/foo/baz/one").New(): true,
|
||||
nonresourceRequest("verb1").URL("/notfoo").New(): false,
|
||||
nonresourceRequest("verb1").URL("/notfoo/bar").New(): false,
|
||||
nonresourceRequest("verb1").URL("/notfoo/baz").New(): false,
|
||||
nonresourceRequest("verb1").URL("/notfoo/bar/one").New(): false,
|
||||
nonresourceRequest("verb1").URL("/notfoo/baz/one").New(): false,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "star verb, exact nonresource",
|
||||
rule: rbac.NewRule("*").URLs("/foo", "/foo/bar/one").RuleOrDie(),
|
||||
requestsToExpected: map[authorizer.AttributesRecord]bool{
|
||||
nonresourceRequest("verb1").URL("/foo").New(): true,
|
||||
nonresourceRequest("verb1").URL("/foo/bar").New(): false,
|
||||
nonresourceRequest("verb1").URL("/foo/baz").New(): false,
|
||||
nonresourceRequest("verb1").URL("/foo/bar/one").New(): true,
|
||||
nonresourceRequest("verb1").URL("/foo/baz/one").New(): false,
|
||||
nonresourceRequest("verb2").URL("/foo").New(): true,
|
||||
nonresourceRequest("verb2").URL("/foo/bar").New(): false,
|
||||
nonresourceRequest("verb2").URL("/foo/baz").New(): false,
|
||||
nonresourceRequest("verb2").URL("/foo/bar/one").New(): true,
|
||||
nonresourceRequest("verb2").URL("/foo/baz/one").New(): false,
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tc := range tests {
|
||||
for request, expected := range tc.requestsToExpected {
|
||||
if e, a := expected, RuleAllows(request, &tc.rule); e != a {
|
||||
t.Errorf("%q: expected %v, got %v for %v", tc.name, e, a, request)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type requestAttributeBuilder struct {
|
||||
request authorizer.AttributesRecord
|
||||
}
|
||||
|
||||
func resourceRequest(verb string) *requestAttributeBuilder {
|
||||
return &requestAttributeBuilder{
|
||||
request: authorizer.AttributesRecord{ResourceRequest: true, Verb: verb},
|
||||
}
|
||||
}
|
||||
|
||||
func nonresourceRequest(verb string) *requestAttributeBuilder {
|
||||
return &requestAttributeBuilder{
|
||||
request: authorizer.AttributesRecord{ResourceRequest: false, Verb: verb},
|
||||
}
|
||||
}
|
||||
|
||||
func (r *requestAttributeBuilder) Group(group string) *requestAttributeBuilder {
|
||||
r.request.APIGroup = group
|
||||
return r
|
||||
}
|
||||
|
||||
func (r *requestAttributeBuilder) Resource(resource string) *requestAttributeBuilder {
|
||||
r.request.Resource = resource
|
||||
return r
|
||||
}
|
||||
|
||||
func (r *requestAttributeBuilder) Subresource(subresource string) *requestAttributeBuilder {
|
||||
r.request.Subresource = subresource
|
||||
return r
|
||||
}
|
||||
|
||||
func (r *requestAttributeBuilder) Name(name string) *requestAttributeBuilder {
|
||||
r.request.Name = name
|
||||
return r
|
||||
}
|
||||
|
||||
func (r *requestAttributeBuilder) URL(url string) *requestAttributeBuilder {
|
||||
r.request.Path = url
|
||||
return r
|
||||
}
|
||||
|
||||
func (r *requestAttributeBuilder) New() authorizer.AttributesRecord {
|
||||
return r.request
|
||||
}
|
||||
|
||||
func BenchmarkAuthorize(b *testing.B) {
|
||||
bootstrapRoles := []rbac.ClusterRole{}
|
||||
bootstrapRoles = append(bootstrapRoles, bootstrappolicy.ControllerRoles()...)
|
||||
bootstrapRoles = append(bootstrapRoles, bootstrappolicy.ClusterRoles()...)
|
||||
|
||||
bootstrapBindings := []rbac.ClusterRoleBinding{}
|
||||
bootstrapBindings = append(bootstrapBindings, bootstrappolicy.ClusterRoleBindings()...)
|
||||
bootstrapBindings = append(bootstrapBindings, bootstrappolicy.ControllerRoleBindings()...)
|
||||
|
||||
clusterRoles := []*rbac.ClusterRole{}
|
||||
for i := range bootstrapRoles {
|
||||
clusterRoles = append(clusterRoles, &bootstrapRoles[i])
|
||||
}
|
||||
clusterRoleBindings := []*rbac.ClusterRoleBinding{}
|
||||
for i := range bootstrapBindings {
|
||||
clusterRoleBindings = append(clusterRoleBindings, &bootstrapBindings[i])
|
||||
}
|
||||
|
||||
_, resolver := rbacregistryvalidation.NewTestRuleResolver(nil, nil, clusterRoles, clusterRoleBindings)
|
||||
|
||||
authz := New(resolver, resolver, resolver, resolver)
|
||||
|
||||
nodeUser := &user.DefaultInfo{Name: "system:node:node1", Groups: []string{"system:nodes", "system:authenticated"}}
|
||||
requests := []struct {
|
||||
name string
|
||||
attrs authorizer.Attributes
|
||||
}{
|
||||
{
|
||||
"allow list pods",
|
||||
authorizer.AttributesRecord{
|
||||
ResourceRequest: true,
|
||||
User: nodeUser,
|
||||
Verb: "list",
|
||||
Resource: "pods",
|
||||
Subresource: "",
|
||||
Name: "",
|
||||
Namespace: "",
|
||||
APIGroup: "",
|
||||
APIVersion: "v1",
|
||||
},
|
||||
},
|
||||
{
|
||||
"allow update pods/status",
|
||||
authorizer.AttributesRecord{
|
||||
ResourceRequest: true,
|
||||
User: nodeUser,
|
||||
Verb: "update",
|
||||
Resource: "pods",
|
||||
Subresource: "status",
|
||||
Name: "mypods",
|
||||
Namespace: "myns",
|
||||
APIGroup: "",
|
||||
APIVersion: "v1",
|
||||
},
|
||||
},
|
||||
{
|
||||
"forbid educate dolphins",
|
||||
authorizer.AttributesRecord{
|
||||
ResourceRequest: true,
|
||||
User: nodeUser,
|
||||
Verb: "educate",
|
||||
Resource: "dolphins",
|
||||
Subresource: "",
|
||||
Name: "",
|
||||
Namespace: "",
|
||||
APIGroup: "",
|
||||
APIVersion: "v1",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
b.ResetTimer()
|
||||
for _, request := range requests {
|
||||
b.Run(request.name, func(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
authz.Authorize(request.attrs)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
123
vendor/k8s.io/kubernetes/plugin/pkg/auth/authorizer/rbac/subject_locator.go
generated
vendored
Normal file
123
vendor/k8s.io/kubernetes/plugin/pkg/auth/authorizer/rbac/subject_locator.go
generated
vendored
Normal file
@ -0,0 +1,123 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
// Package rbac implements the authorizer.Authorizer interface using roles base access control.
|
||||
package rbac
|
||||
|
||||
import (
|
||||
utilerrors "k8s.io/apimachinery/pkg/util/errors"
|
||||
"k8s.io/apiserver/pkg/authentication/user"
|
||||
"k8s.io/apiserver/pkg/authorization/authorizer"
|
||||
"k8s.io/kubernetes/pkg/apis/rbac"
|
||||
rbacregistryvalidation "k8s.io/kubernetes/pkg/registry/rbac/validation"
|
||||
)
|
||||
|
||||
type RoleToRuleMapper interface {
|
||||
// GetRoleReferenceRules attempts to resolve the role reference of a RoleBinding or ClusterRoleBinding. The passed namespace should be the namespace
|
||||
// of the role binding, the empty string if a cluster role binding.
|
||||
GetRoleReferenceRules(roleRef rbac.RoleRef, namespace string) ([]rbac.PolicyRule, error)
|
||||
}
|
||||
|
||||
type SubjectLocator interface {
|
||||
AllowedSubjects(attributes authorizer.Attributes) ([]rbac.Subject, error)
|
||||
}
|
||||
|
||||
var _ = SubjectLocator(&SubjectAccessEvaluator{})
|
||||
|
||||
type SubjectAccessEvaluator struct {
|
||||
superUser string
|
||||
|
||||
roleBindingLister rbacregistryvalidation.RoleBindingLister
|
||||
clusterRoleBindingLister rbacregistryvalidation.ClusterRoleBindingLister
|
||||
roleToRuleMapper RoleToRuleMapper
|
||||
}
|
||||
|
||||
func NewSubjectAccessEvaluator(roles rbacregistryvalidation.RoleGetter, roleBindings rbacregistryvalidation.RoleBindingLister, clusterRoles rbacregistryvalidation.ClusterRoleGetter, clusterRoleBindings rbacregistryvalidation.ClusterRoleBindingLister, superUser string) *SubjectAccessEvaluator {
|
||||
subjectLocator := &SubjectAccessEvaluator{
|
||||
superUser: superUser,
|
||||
roleBindingLister: roleBindings,
|
||||
clusterRoleBindingLister: clusterRoleBindings,
|
||||
roleToRuleMapper: rbacregistryvalidation.NewDefaultRuleResolver(
|
||||
roles, roleBindings, clusterRoles, clusterRoleBindings,
|
||||
),
|
||||
}
|
||||
return subjectLocator
|
||||
}
|
||||
|
||||
// AllowedSubjects returns the subjects that can perform an action and any errors encountered while computing the list.
|
||||
// It is possible to have both subjects and errors returned if some rolebindings couldn't be resolved, but others could be.
|
||||
func (r *SubjectAccessEvaluator) AllowedSubjects(requestAttributes authorizer.Attributes) ([]rbac.Subject, error) {
|
||||
subjects := []rbac.Subject{{Kind: rbac.GroupKind, APIGroup: rbac.GroupName, Name: user.SystemPrivilegedGroup}}
|
||||
if len(r.superUser) > 0 {
|
||||
subjects = append(subjects, rbac.Subject{Kind: rbac.UserKind, APIGroup: rbac.GroupName, Name: r.superUser})
|
||||
}
|
||||
errorlist := []error{}
|
||||
|
||||
if clusterRoleBindings, err := r.clusterRoleBindingLister.ListClusterRoleBindings(); err != nil {
|
||||
errorlist = append(errorlist, err)
|
||||
|
||||
} else {
|
||||
for _, clusterRoleBinding := range clusterRoleBindings {
|
||||
rules, err := r.roleToRuleMapper.GetRoleReferenceRules(clusterRoleBinding.RoleRef, "")
|
||||
if err != nil {
|
||||
// if we have an error, just keep track of it and keep processing. Since rules are additive,
|
||||
// missing a reference is bad, but we can continue with other rolebindings and still have a list
|
||||
// that does not contain any invalid values
|
||||
errorlist = append(errorlist, err)
|
||||
}
|
||||
if RulesAllow(requestAttributes, rules...) {
|
||||
subjects = append(subjects, clusterRoleBinding.Subjects...)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if namespace := requestAttributes.GetNamespace(); len(namespace) > 0 {
|
||||
if roleBindings, err := r.roleBindingLister.ListRoleBindings(namespace); err != nil {
|
||||
errorlist = append(errorlist, err)
|
||||
|
||||
} else {
|
||||
for _, roleBinding := range roleBindings {
|
||||
rules, err := r.roleToRuleMapper.GetRoleReferenceRules(roleBinding.RoleRef, namespace)
|
||||
if err != nil {
|
||||
// if we have an error, just keep track of it and keep processing. Since rules are additive,
|
||||
// missing a reference is bad, but we can continue with other rolebindings and still have a list
|
||||
// that does not contain any invalid values
|
||||
errorlist = append(errorlist, err)
|
||||
}
|
||||
if RulesAllow(requestAttributes, rules...) {
|
||||
subjects = append(subjects, roleBinding.Subjects...)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dedupedSubjects := []rbac.Subject{}
|
||||
for _, subject := range subjects {
|
||||
found := false
|
||||
for _, curr := range dedupedSubjects {
|
||||
if curr == subject {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !found {
|
||||
dedupedSubjects = append(dedupedSubjects, subject)
|
||||
}
|
||||
}
|
||||
|
||||
return subjects, utilerrors.NewAggregate(errorlist)
|
||||
}
|
151
vendor/k8s.io/kubernetes/plugin/pkg/auth/authorizer/rbac/subject_locator_test.go
generated
vendored
Normal file
151
vendor/k8s.io/kubernetes/plugin/pkg/auth/authorizer/rbac/subject_locator_test.go
generated
vendored
Normal file
@ -0,0 +1,151 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package rbac
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"k8s.io/apiserver/pkg/authentication/user"
|
||||
"k8s.io/apiserver/pkg/authorization/authorizer"
|
||||
"k8s.io/kubernetes/pkg/apis/rbac"
|
||||
rbacregistryvalidation "k8s.io/kubernetes/pkg/registry/rbac/validation"
|
||||
)
|
||||
|
||||
func TestSubjectLocator(t *testing.T) {
|
||||
type actionToSubjects struct {
|
||||
action authorizer.Attributes
|
||||
subjects []rbac.Subject
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
roles []*rbac.Role
|
||||
roleBindings []*rbac.RoleBinding
|
||||
clusterRoles []*rbac.ClusterRole
|
||||
clusterRoleBindings []*rbac.ClusterRoleBinding
|
||||
|
||||
superUser string
|
||||
|
||||
actionsToSubjects []actionToSubjects
|
||||
}{
|
||||
{
|
||||
name: "no super user, star matches star",
|
||||
clusterRoles: []*rbac.ClusterRole{
|
||||
newClusterRole("admin", newRule("*", "*", "*", "*")),
|
||||
},
|
||||
clusterRoleBindings: []*rbac.ClusterRoleBinding{
|
||||
newClusterRoleBinding("admin", "User:super-admin", "Group:super-admins"),
|
||||
},
|
||||
roleBindings: []*rbac.RoleBinding{
|
||||
newRoleBinding("ns1", "admin", bindToClusterRole, "User:admin", "Group:admins"),
|
||||
},
|
||||
actionsToSubjects: []actionToSubjects{
|
||||
{
|
||||
&defaultAttributes{"", "", "get", "Pods", "", "ns1", ""},
|
||||
[]rbac.Subject{
|
||||
{Kind: rbac.GroupKind, APIGroup: rbac.GroupName, Name: user.SystemPrivilegedGroup},
|
||||
{Kind: rbac.UserKind, APIGroup: rbac.GroupName, Name: "super-admin"},
|
||||
{Kind: rbac.GroupKind, APIGroup: rbac.GroupName, Name: "super-admins"},
|
||||
{Kind: rbac.UserKind, APIGroup: rbac.GroupName, Name: "admin"},
|
||||
{Kind: rbac.GroupKind, APIGroup: rbac.GroupName, Name: "admins"},
|
||||
},
|
||||
},
|
||||
{
|
||||
// cluster role matches star in namespace
|
||||
&defaultAttributes{"", "", "*", "Pods", "", "*", ""},
|
||||
[]rbac.Subject{
|
||||
{Kind: rbac.GroupKind, APIGroup: rbac.GroupName, Name: user.SystemPrivilegedGroup},
|
||||
{Kind: rbac.UserKind, APIGroup: rbac.GroupName, Name: "super-admin"},
|
||||
{Kind: rbac.GroupKind, APIGroup: rbac.GroupName, Name: "super-admins"},
|
||||
},
|
||||
},
|
||||
{
|
||||
// empty ns
|
||||
&defaultAttributes{"", "", "*", "Pods", "", "", ""},
|
||||
[]rbac.Subject{
|
||||
{Kind: rbac.GroupKind, APIGroup: rbac.GroupName, Name: user.SystemPrivilegedGroup},
|
||||
{Kind: rbac.UserKind, APIGroup: rbac.GroupName, Name: "super-admin"},
|
||||
{Kind: rbac.GroupKind, APIGroup: rbac.GroupName, Name: "super-admins"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "super user, local roles work",
|
||||
superUser: "foo",
|
||||
clusterRoles: []*rbac.ClusterRole{
|
||||
newClusterRole("admin", newRule("*", "*", "*", "*")),
|
||||
},
|
||||
clusterRoleBindings: []*rbac.ClusterRoleBinding{
|
||||
newClusterRoleBinding("admin", "User:super-admin", "Group:super-admins"),
|
||||
},
|
||||
roles: []*rbac.Role{
|
||||
newRole("admin", "ns1", newRule("get", "*", "Pods", "*")),
|
||||
},
|
||||
roleBindings: []*rbac.RoleBinding{
|
||||
newRoleBinding("ns1", "admin", bindToRole, "User:admin", "Group:admins"),
|
||||
},
|
||||
actionsToSubjects: []actionToSubjects{
|
||||
{
|
||||
&defaultAttributes{"", "", "get", "Pods", "", "ns1", ""},
|
||||
[]rbac.Subject{
|
||||
{Kind: rbac.GroupKind, APIGroup: rbac.GroupName, Name: user.SystemPrivilegedGroup},
|
||||
{Kind: rbac.UserKind, APIGroup: rbac.GroupName, Name: "foo"},
|
||||
{Kind: rbac.UserKind, APIGroup: rbac.GroupName, Name: "super-admin"},
|
||||
{Kind: rbac.GroupKind, APIGroup: rbac.GroupName, Name: "super-admins"},
|
||||
{Kind: rbac.UserKind, APIGroup: rbac.GroupName, Name: "admin"},
|
||||
{Kind: rbac.GroupKind, APIGroup: rbac.GroupName, Name: "admins"},
|
||||
},
|
||||
},
|
||||
{
|
||||
// verb matchies correctly
|
||||
&defaultAttributes{"", "", "create", "Pods", "", "ns1", ""},
|
||||
[]rbac.Subject{
|
||||
{Kind: rbac.GroupKind, APIGroup: rbac.GroupName, Name: user.SystemPrivilegedGroup},
|
||||
{Kind: rbac.UserKind, APIGroup: rbac.GroupName, Name: "foo"},
|
||||
{Kind: rbac.UserKind, APIGroup: rbac.GroupName, Name: "super-admin"},
|
||||
{Kind: rbac.GroupKind, APIGroup: rbac.GroupName, Name: "super-admins"},
|
||||
},
|
||||
},
|
||||
{
|
||||
// binding only works in correct ns
|
||||
&defaultAttributes{"", "", "get", "Pods", "", "ns2", ""},
|
||||
[]rbac.Subject{
|
||||
{Kind: rbac.GroupKind, APIGroup: rbac.GroupName, Name: user.SystemPrivilegedGroup},
|
||||
{Kind: rbac.UserKind, APIGroup: rbac.GroupName, Name: "foo"},
|
||||
{Kind: rbac.UserKind, APIGroup: rbac.GroupName, Name: "super-admin"},
|
||||
{Kind: rbac.GroupKind, APIGroup: rbac.GroupName, Name: "super-admins"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
ruleResolver, lister := rbacregistryvalidation.NewTestRuleResolver(tt.roles, tt.roleBindings, tt.clusterRoles, tt.clusterRoleBindings)
|
||||
a := SubjectAccessEvaluator{tt.superUser, lister, lister, ruleResolver}
|
||||
for i, action := range tt.actionsToSubjects {
|
||||
actualSubjects, err := a.AllowedSubjects(action.action)
|
||||
if err != nil {
|
||||
t.Errorf("case %q %d: error %v", tt.name, i, err)
|
||||
}
|
||||
if !reflect.DeepEqual(actualSubjects, action.subjects) {
|
||||
t.Errorf("case %q %d: expected\n%v\nactual\n%v", tt.name, i, action.subjects, actualSubjects)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
18
vendor/k8s.io/kubernetes/plugin/pkg/auth/doc.go
generated
vendored
Normal file
18
vendor/k8s.io/kubernetes/plugin/pkg/auth/doc.go
generated
vendored
Normal file
@ -0,0 +1,18 @@
|
||||
/*
|
||||
Copyright 2014 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
// Package auth contains implementations for interfaces in the pkg/auth package
|
||||
package auth // import "k8s.io/kubernetes/plugin/pkg/auth"
|
Reference in New Issue
Block a user