mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-23 19:56:01 +00:00
add selfsubjectrulesreview api
This commit is contained in:
parent
6a845c67f0
commit
f14c138438
@ -441,7 +441,7 @@ func BuildGenericConfig(s *options.ServerRunOptions) (*genericapiserver.Config,
|
|||||||
return nil, nil, nil, nil, nil, fmt.Errorf("invalid authentication config: %v", err)
|
return nil, nil, nil, nil, nil, fmt.Errorf("invalid authentication config: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
genericConfig.Authorizer, err = BuildAuthorizer(s, sharedInformers)
|
genericConfig.Authorizer, genericConfig.RuleResolver, err = BuildAuthorizer(s, sharedInformers)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, nil, nil, nil, fmt.Errorf("invalid authorization config: %v", err)
|
return nil, nil, nil, nil, nil, fmt.Errorf("invalid authorization config: %v", err)
|
||||||
}
|
}
|
||||||
@ -542,7 +542,7 @@ func BuildAuthenticator(s *options.ServerRunOptions, storageFactory serverstorag
|
|||||||
}
|
}
|
||||||
|
|
||||||
// BuildAuthorizer constructs the authorizer
|
// BuildAuthorizer constructs the authorizer
|
||||||
func BuildAuthorizer(s *options.ServerRunOptions, sharedInformers informers.SharedInformerFactory) (authorizer.Authorizer, error) {
|
func BuildAuthorizer(s *options.ServerRunOptions, sharedInformers informers.SharedInformerFactory) (authorizer.Authorizer, authorizer.RuleResolver, error) {
|
||||||
authorizationConfig := s.Authorization.ToAuthorizationConfig(sharedInformers)
|
authorizationConfig := s.Authorization.ToAuthorizationConfig(sharedInformers)
|
||||||
return authorizationConfig.New()
|
return authorizationConfig.New()
|
||||||
}
|
}
|
||||||
|
@ -190,7 +190,7 @@ func NonBlockingRun(s *options.ServerRunOptions, stopCh <-chan struct{}) error {
|
|||||||
sharedInformers := informers.NewSharedInformerFactory(client, 10*time.Minute)
|
sharedInformers := informers.NewSharedInformerFactory(client, 10*time.Minute)
|
||||||
|
|
||||||
authorizationConfig := s.Authorization.ToAuthorizationConfig(sharedInformers)
|
authorizationConfig := s.Authorization.ToAuthorizationConfig(sharedInformers)
|
||||||
apiAuthorizer, err := authorizationConfig.New()
|
apiAuthorizer, _, err := authorizationConfig.New()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("invalid Authorization Config: %v", err)
|
return fmt.Errorf("invalid Authorization Config: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -39,7 +39,7 @@ func Install(groupFactoryRegistry announced.APIGroupFactoryRegistry, registry *r
|
|||||||
&announced.GroupMetaFactoryArgs{
|
&announced.GroupMetaFactoryArgs{
|
||||||
GroupName: authorization.GroupName,
|
GroupName: authorization.GroupName,
|
||||||
VersionPreferenceOrder: []string{v1.SchemeGroupVersion.Version, v1beta1.SchemeGroupVersion.Version},
|
VersionPreferenceOrder: []string{v1.SchemeGroupVersion.Version, v1beta1.SchemeGroupVersion.Version},
|
||||||
RootScopedKinds: sets.NewString("SubjectAccessReview", "SelfSubjectAccessReview"),
|
RootScopedKinds: sets.NewString("SubjectAccessReview", "SelfSubjectAccessReview", "SelfSubjectRulesReview"),
|
||||||
AddInternalObjectsToScheme: authorization.AddToScheme,
|
AddInternalObjectsToScheme: authorization.AddToScheme,
|
||||||
},
|
},
|
||||||
announced.VersionToSchemeFunc{
|
announced.VersionToSchemeFunc{
|
||||||
|
@ -44,6 +44,7 @@ var (
|
|||||||
|
|
||||||
func addKnownTypes(scheme *runtime.Scheme) error {
|
func addKnownTypes(scheme *runtime.Scheme) error {
|
||||||
scheme.AddKnownTypes(SchemeGroupVersion,
|
scheme.AddKnownTypes(SchemeGroupVersion,
|
||||||
|
&SelfSubjectRulesReview{},
|
||||||
&SelfSubjectAccessReview{},
|
&SelfSubjectAccessReview{},
|
||||||
&SubjectAccessReview{},
|
&SubjectAccessReview{},
|
||||||
&LocalSubjectAccessReview{},
|
&LocalSubjectAccessReview{},
|
||||||
|
@ -149,3 +149,74 @@ type SubjectAccessReviewStatus struct {
|
|||||||
// For instance, RBAC can be missing a role, but enough roles are still present and bound to reason about the request.
|
// For instance, RBAC can be missing a role, but enough roles are still present and bound to reason about the request.
|
||||||
EvaluationError string
|
EvaluationError string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// +genclient
|
||||||
|
// +genclient:nonNamespaced
|
||||||
|
// +genclient:noVerbs
|
||||||
|
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
||||||
|
|
||||||
|
// SelfSubjectRulesReview enumerates the set of actions the current user can perform within a namespace.
|
||||||
|
// The returned list of actions may be incomplete depending on the server's authorization mode,
|
||||||
|
// and any errors experienced during the evaluation. SelfSubjectRulesReview should be used by UIs to show/hide actions,
|
||||||
|
// or to quickly let an end user reason about their permissions. It should NOT Be used by external systems to
|
||||||
|
// drive authorization decisions as this raises confused deputy, cache lifetime/revocation, and correctness concerns.
|
||||||
|
// SubjectAccessReview, and LocalAccessReview are the correct way to defer authorization decisions to the API server.
|
||||||
|
type SelfSubjectRulesReview struct {
|
||||||
|
metav1.TypeMeta
|
||||||
|
metav1.ObjectMeta
|
||||||
|
|
||||||
|
// Spec holds information about the request being evaluated.
|
||||||
|
Spec SelfSubjectRulesReviewSpec
|
||||||
|
|
||||||
|
// Status is filled in by the server and indicates the set of actions a user can perform.
|
||||||
|
Status SubjectRulesReviewStatus
|
||||||
|
}
|
||||||
|
|
||||||
|
type SelfSubjectRulesReviewSpec struct {
|
||||||
|
// Namespace to evaluate rules for. Required.
|
||||||
|
Namespace string
|
||||||
|
}
|
||||||
|
|
||||||
|
// SubjectRulesReviewStatus contains the result of a rules check. This check can be incomplete depending on
|
||||||
|
// the set of authorizers the server is configured with and any errors experienced during evaluation.
|
||||||
|
// Because authorization rules are additive, if a rule appears in a list it's safe to assume the subject has that permission,
|
||||||
|
// even if that list is incomplete.
|
||||||
|
type SubjectRulesReviewStatus struct {
|
||||||
|
// ResourceRules is the list of actions the subject is allowed to perform on resources.
|
||||||
|
// The list ordering isn't significant, may contain duplicates, and possibly be incomplete.
|
||||||
|
ResourceRules []ResourceRule
|
||||||
|
// NonResourceRules is the list of actions the subject is allowed to perform on non-resources.
|
||||||
|
// The list ordering isn't significant, may contain duplicates, and possibly be incomplete.
|
||||||
|
NonResourceRules []NonResourceRule
|
||||||
|
// Incomplete is true when the rules returned by this call are incomplete. This is most commonly
|
||||||
|
// encountered when an authorizer, such as an external authorizer, doesn't support rules evaluation.
|
||||||
|
Incomplete bool
|
||||||
|
// EvaluationError can appear in combination with Rules. It indicates an error occurred during
|
||||||
|
// rule evaluation, such as an authorizer that doesn't support rule evaluation, and that
|
||||||
|
// ResourceRules and/or NonResourceRules may be incomplete.
|
||||||
|
EvaluationError string
|
||||||
|
}
|
||||||
|
|
||||||
|
// ResourceRule is the list of actions the subject is allowed to perform on resources. The list ordering isn't significant,
|
||||||
|
// may contain duplicates, and possibly be incomplete.
|
||||||
|
type ResourceRule struct {
|
||||||
|
// Verb is a list of kubernetes resource API verbs, like: get, list, watch, create, update, delete, proxy. "*" means all.
|
||||||
|
Verbs []string
|
||||||
|
// APIGroups is the name of the APIGroup that contains the resources. If multiple API groups are specified, any action requested against one of
|
||||||
|
// the enumerated resources in any API group will be allowed. "*" means all.
|
||||||
|
APIGroups []string
|
||||||
|
// Resources is a list of resources this rule applies to. ResourceAll represents all resources. "*" means all.
|
||||||
|
Resources []string
|
||||||
|
// ResourceNames is an optional white list of names that the rule applies to. An empty set means that everything is allowed. "*" means all.
|
||||||
|
ResourceNames []string
|
||||||
|
}
|
||||||
|
|
||||||
|
// NonResourceRule holds information that describes a rule for the non-resource
|
||||||
|
type NonResourceRule struct {
|
||||||
|
// Verb is a list of kubernetes non-resource API verbs, like: get, post, put, delete, patch, head, options. "*" means all.
|
||||||
|
Verbs []string
|
||||||
|
|
||||||
|
// NonResourceURLs is a set of partial urls that a user should have access to. *s are allowed, but only as the full,
|
||||||
|
// final step in the path. "*" means all.
|
||||||
|
NonResourceURLs []string
|
||||||
|
}
|
||||||
|
@ -50,6 +50,10 @@ func ValidateSelfSubjectAccessReviewSpec(spec authorizationapi.SelfSubjectAccess
|
|||||||
return allErrs
|
return allErrs
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ValidateSelfSubjectRulesReview(review *authorizationapi.SelfSubjectRulesReview) field.ErrorList {
|
||||||
|
return field.ErrorList{}
|
||||||
|
}
|
||||||
|
|
||||||
func ValidateSubjectAccessReview(sar *authorizationapi.SubjectAccessReview) field.ErrorList {
|
func ValidateSubjectAccessReview(sar *authorizationapi.SubjectAccessReview) field.ErrorList {
|
||||||
allErrs := ValidateSubjectAccessReviewSpec(sar.Spec, field.NewPath("spec"))
|
allErrs := ValidateSubjectAccessReviewSpec(sar.Spec, field.NewPath("spec"))
|
||||||
if !apiequality.Semantic.DeepEqual(metav1.ObjectMeta{}, sar.ObjectMeta) {
|
if !apiequality.Semantic.DeepEqual(metav1.ObjectMeta{}, sar.ObjectMeta) {
|
||||||
|
@ -28,6 +28,7 @@ import (
|
|||||||
"github.com/golang/glog"
|
"github.com/golang/glog"
|
||||||
|
|
||||||
"k8s.io/apimachinery/pkg/runtime"
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
|
"k8s.io/apiserver/pkg/authentication/user"
|
||||||
"k8s.io/apiserver/pkg/authorization/authorizer"
|
"k8s.io/apiserver/pkg/authorization/authorizer"
|
||||||
api "k8s.io/kubernetes/pkg/apis/abac"
|
api "k8s.io/kubernetes/pkg/apis/abac"
|
||||||
_ "k8s.io/kubernetes/pkg/apis/abac/latest"
|
_ "k8s.io/kubernetes/pkg/apis/abac/latest"
|
||||||
@ -114,7 +115,7 @@ func NewFromFile(path string) (policyList, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func matches(p api.Policy, a authorizer.Attributes) bool {
|
func matches(p api.Policy, a authorizer.Attributes) bool {
|
||||||
if subjectMatches(p, a) {
|
if subjectMatches(p, a.GetUser()) {
|
||||||
if verbMatches(p, a) {
|
if verbMatches(p, a) {
|
||||||
// Resource and non-resource requests are mutually exclusive, at most one will match a policy
|
// Resource and non-resource requests are mutually exclusive, at most one will match a policy
|
||||||
if resourceMatches(p, a) {
|
if resourceMatches(p, a) {
|
||||||
@ -129,15 +130,14 @@ func matches(p api.Policy, a authorizer.Attributes) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// subjectMatches returns true if specified user and group properties in the policy match the attributes
|
// subjectMatches returns true if specified user and group properties in the policy match the attributes
|
||||||
func subjectMatches(p api.Policy, a authorizer.Attributes) bool {
|
func subjectMatches(p api.Policy, user user.Info) bool {
|
||||||
matched := false
|
matched := false
|
||||||
|
|
||||||
username := ""
|
if user == nil {
|
||||||
groups := []string{}
|
return false
|
||||||
if user := a.GetUser(); user != nil {
|
|
||||||
username = user.GetName()
|
|
||||||
groups = user.GetGroups()
|
|
||||||
}
|
}
|
||||||
|
username := user.GetName()
|
||||||
|
groups := user.GetGroups()
|
||||||
|
|
||||||
// If the policy specified a user, ensure it matches
|
// If the policy specified a user, ensure it matches
|
||||||
if len(p.Spec.User) > 0 {
|
if len(p.Spec.User) > 0 {
|
||||||
@ -232,3 +232,42 @@ func (pl policyList) Authorize(a authorizer.Attributes) (bool, string, error) {
|
|||||||
// policy file, compared to other steps such as encoding/decoding.
|
// policy file, compared to other steps such as encoding/decoding.
|
||||||
// Then, add Caching only if needed.
|
// Then, add Caching only if needed.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (pl policyList) RulesFor(user user.Info, namespace string) ([]authorizer.ResourceRuleInfo, []authorizer.NonResourceRuleInfo, bool, error) {
|
||||||
|
var (
|
||||||
|
resourceRules []authorizer.ResourceRuleInfo
|
||||||
|
nonResourceRules []authorizer.NonResourceRuleInfo
|
||||||
|
)
|
||||||
|
|
||||||
|
for _, p := range pl {
|
||||||
|
if subjectMatches(*p, user) {
|
||||||
|
if p.Spec.Namespace == "*" || p.Spec.Namespace == namespace {
|
||||||
|
if len(p.Spec.Resource) > 0 {
|
||||||
|
r := authorizer.DefaultResourceRuleInfo{
|
||||||
|
Verbs: getVerbs(p.Spec.Readonly),
|
||||||
|
APIGroups: []string{p.Spec.APIGroup},
|
||||||
|
Resources: []string{p.Spec.Resource},
|
||||||
|
}
|
||||||
|
var resourceRule authorizer.ResourceRuleInfo = &r
|
||||||
|
resourceRules = append(resourceRules, resourceRule)
|
||||||
|
}
|
||||||
|
if len(p.Spec.NonResourcePath) > 0 {
|
||||||
|
r := authorizer.DefaultNonResourceRuleInfo{
|
||||||
|
Verbs: getVerbs(p.Spec.Readonly),
|
||||||
|
NonResourceURLs: []string{p.Spec.NonResourcePath},
|
||||||
|
}
|
||||||
|
var nonResourceRule authorizer.NonResourceRuleInfo = &r
|
||||||
|
nonResourceRules = append(nonResourceRules, nonResourceRule)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return resourceRules, nonResourceRules, false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getVerbs(isReadOnly bool) []string {
|
||||||
|
if isReadOnly {
|
||||||
|
return []string{"get", "list", "watch"}
|
||||||
|
}
|
||||||
|
return []string{"*"}
|
||||||
|
}
|
||||||
|
@ -19,6 +19,7 @@ package abac
|
|||||||
import (
|
import (
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"k8s.io/apimachinery/pkg/runtime"
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
@ -141,6 +142,203 @@ func TestAuthorizeV0(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getResourceRules(infos []authorizer.ResourceRuleInfo) []authorizer.DefaultResourceRuleInfo {
|
||||||
|
rules := make([]authorizer.DefaultResourceRuleInfo, len(infos))
|
||||||
|
for i, info := range infos {
|
||||||
|
rules[i] = authorizer.DefaultResourceRuleInfo{
|
||||||
|
Verbs: info.GetVerbs(),
|
||||||
|
APIGroups: info.GetAPIGroups(),
|
||||||
|
Resources: info.GetResources(),
|
||||||
|
ResourceNames: info.GetResourceNames(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return rules
|
||||||
|
}
|
||||||
|
|
||||||
|
func getNonResourceRules(infos []authorizer.NonResourceRuleInfo) []authorizer.DefaultNonResourceRuleInfo {
|
||||||
|
rules := make([]authorizer.DefaultNonResourceRuleInfo, len(infos))
|
||||||
|
for i, info := range infos {
|
||||||
|
rules[i] = authorizer.DefaultNonResourceRuleInfo{
|
||||||
|
Verbs: info.GetVerbs(),
|
||||||
|
NonResourceURLs: info.GetNonResourceURLs(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return rules
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRulesFor(t *testing.T) {
|
||||||
|
a, err := newWithContents(t, `
|
||||||
|
{ "readonly": true, "resource": "events" }
|
||||||
|
{"user":"scheduler", "readonly": true, "resource": "pods" }
|
||||||
|
{"user":"scheduler", "resource": "bindings" }
|
||||||
|
{"user":"kubelet", "readonly": true, "resource": "pods" }
|
||||||
|
{"user":"kubelet", "resource": "events" }
|
||||||
|
{"user":"alice", "namespace": "projectCaribou"}
|
||||||
|
{"user":"bob", "readonly": true, "namespace": "projectCaribou"}
|
||||||
|
{"user":"bob", "readonly": true, "nonResourcePath": "*"}
|
||||||
|
{"group":"a", "resource": "bindings" }
|
||||||
|
{"group":"b", "readonly": true, "nonResourcePath": "*"}
|
||||||
|
`)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unable to read policy file: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
authenticatedGroup := []string{user.AllAuthenticated}
|
||||||
|
|
||||||
|
uScheduler := user.DefaultInfo{Name: "scheduler", UID: "uid1", Groups: authenticatedGroup}
|
||||||
|
uKubelet := user.DefaultInfo{Name: "kubelet", UID: "uid2", Groups: []string{"a", "b"}}
|
||||||
|
uAlice := user.DefaultInfo{Name: "alice", UID: "uid3", Groups: authenticatedGroup}
|
||||||
|
uBob := user.DefaultInfo{Name: "bob", UID: "uid4", Groups: authenticatedGroup}
|
||||||
|
uChuck := user.DefaultInfo{Name: "chuck", UID: "uid5", Groups: []string{"a", "b"}}
|
||||||
|
|
||||||
|
testCases := []struct {
|
||||||
|
User user.DefaultInfo
|
||||||
|
Namespace string
|
||||||
|
ExpectResourceRules []authorizer.DefaultResourceRuleInfo
|
||||||
|
ExpectNonResourceRules []authorizer.DefaultNonResourceRuleInfo
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
User: uScheduler,
|
||||||
|
Namespace: "ns1",
|
||||||
|
ExpectResourceRules: []authorizer.DefaultResourceRuleInfo{
|
||||||
|
{
|
||||||
|
Verbs: []string{"get", "list", "watch"},
|
||||||
|
APIGroups: []string{"*"},
|
||||||
|
Resources: []string{"events"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Verbs: []string{"get", "list", "watch"},
|
||||||
|
APIGroups: []string{"*"},
|
||||||
|
Resources: []string{"pods"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Verbs: []string{"*"},
|
||||||
|
APIGroups: []string{"*"},
|
||||||
|
Resources: []string{"bindings"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ExpectNonResourceRules: []authorizer.DefaultNonResourceRuleInfo{},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
User: uKubelet,
|
||||||
|
Namespace: "ns1",
|
||||||
|
ExpectResourceRules: []authorizer.DefaultResourceRuleInfo{
|
||||||
|
{
|
||||||
|
Verbs: []string{"get", "list", "watch"},
|
||||||
|
APIGroups: []string{"*"},
|
||||||
|
Resources: []string{"pods"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Verbs: []string{"*"},
|
||||||
|
APIGroups: []string{"*"},
|
||||||
|
Resources: []string{"events"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Verbs: []string{"*"},
|
||||||
|
APIGroups: []string{"*"},
|
||||||
|
Resources: []string{"bindings"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Verbs: []string{"get", "list", "watch"},
|
||||||
|
APIGroups: []string{"*"},
|
||||||
|
Resources: []string{"*"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ExpectNonResourceRules: []authorizer.DefaultNonResourceRuleInfo{
|
||||||
|
{
|
||||||
|
Verbs: []string{"get", "list", "watch"},
|
||||||
|
NonResourceURLs: []string{"*"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
User: uAlice,
|
||||||
|
Namespace: "projectCaribou",
|
||||||
|
ExpectResourceRules: []authorizer.DefaultResourceRuleInfo{
|
||||||
|
{
|
||||||
|
Verbs: []string{"get", "list", "watch"},
|
||||||
|
APIGroups: []string{"*"},
|
||||||
|
Resources: []string{"events"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Verbs: []string{"*"},
|
||||||
|
APIGroups: []string{"*"},
|
||||||
|
Resources: []string{"*"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ExpectNonResourceRules: []authorizer.DefaultNonResourceRuleInfo{},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
User: uBob,
|
||||||
|
Namespace: "projectCaribou",
|
||||||
|
ExpectResourceRules: []authorizer.DefaultResourceRuleInfo{
|
||||||
|
{
|
||||||
|
Verbs: []string{"get", "list", "watch"},
|
||||||
|
APIGroups: []string{"*"},
|
||||||
|
Resources: []string{"events"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Verbs: []string{"get", "list", "watch"},
|
||||||
|
APIGroups: []string{"*"},
|
||||||
|
Resources: []string{"*"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Verbs: []string{"get", "list", "watch"},
|
||||||
|
APIGroups: []string{"*"},
|
||||||
|
Resources: []string{"*"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ExpectNonResourceRules: []authorizer.DefaultNonResourceRuleInfo{
|
||||||
|
{
|
||||||
|
Verbs: []string{"get", "list", "watch"},
|
||||||
|
NonResourceURLs: []string{"*"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
User: uChuck,
|
||||||
|
Namespace: "ns1",
|
||||||
|
ExpectResourceRules: []authorizer.DefaultResourceRuleInfo{
|
||||||
|
{
|
||||||
|
Verbs: []string{"*"},
|
||||||
|
APIGroups: []string{"*"},
|
||||||
|
Resources: []string{"bindings"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Verbs: []string{"get", "list", "watch"},
|
||||||
|
APIGroups: []string{"*"},
|
||||||
|
Resources: []string{"*"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ExpectNonResourceRules: []authorizer.DefaultNonResourceRuleInfo{
|
||||||
|
{
|
||||||
|
Verbs: []string{"get", "list", "watch"},
|
||||||
|
NonResourceURLs: []string{"*"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for i, tc := range testCases {
|
||||||
|
attr := authorizer.AttributesRecord{
|
||||||
|
User: &tc.User,
|
||||||
|
Namespace: tc.Namespace,
|
||||||
|
}
|
||||||
|
resourceRules, nonResourceRules, _, _ := a.RulesFor(attr.GetUser(), attr.GetNamespace())
|
||||||
|
actualResourceRules := getResourceRules(resourceRules)
|
||||||
|
if !reflect.DeepEqual(tc.ExpectResourceRules, actualResourceRules) {
|
||||||
|
t.Logf("tc: %v -> attr %v", tc, attr)
|
||||||
|
t.Errorf("%d: Expected: \n%#v\n but actual: \n%#v\n",
|
||||||
|
i, tc.ExpectResourceRules, actualResourceRules)
|
||||||
|
}
|
||||||
|
actualNonResourceRules := getNonResourceRules(nonResourceRules)
|
||||||
|
if !reflect.DeepEqual(tc.ExpectNonResourceRules, actualNonResourceRules) {
|
||||||
|
t.Logf("tc: %v -> attr %v", tc, attr)
|
||||||
|
t.Errorf("%d: Expected: \n%#v\n but actual: \n%#v\n",
|
||||||
|
i, tc.ExpectNonResourceRules, actualNonResourceRules)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestAuthorizeV1beta1(t *testing.T) {
|
func TestAuthorizeV1beta1(t *testing.T) {
|
||||||
a, err := newWithContents(t,
|
a, err := newWithContents(t,
|
||||||
`
|
`
|
||||||
@ -609,7 +807,7 @@ func TestSubjectMatches(t *testing.T) {
|
|||||||
attr := authorizer.AttributesRecord{
|
attr := authorizer.AttributesRecord{
|
||||||
User: &tc.User,
|
User: &tc.User,
|
||||||
}
|
}
|
||||||
actualMatch := subjectMatches(*policy, attr)
|
actualMatch := subjectMatches(*policy, attr.GetUser())
|
||||||
if tc.ExpectMatch != actualMatch {
|
if tc.ExpectMatch != actualMatch {
|
||||||
t.Errorf("%v: Expected actorMatches=%v but actually got=%v",
|
t.Errorf("%v: Expected actorMatches=%v but actually got=%v",
|
||||||
k, tc.ExpectMatch, actualMatch)
|
k, tc.ExpectMatch, actualMatch)
|
||||||
@ -617,7 +815,7 @@ func TestSubjectMatches(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func newWithContents(t *testing.T, contents string) (authorizer.Authorizer, error) {
|
func newWithContents(t *testing.T, contents string) (policyList, error) {
|
||||||
f, err := ioutil.TempFile("", "abac_test")
|
f, err := ioutil.TempFile("", "abac_test")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unexpected error creating policyfile: %v", err)
|
t.Fatalf("unexpected error creating policyfile: %v", err)
|
||||||
|
@ -359,6 +359,7 @@ var ignoredResources = map[schema.GroupResource]struct{}{
|
|||||||
{Group: "authorization.k8s.io", Resource: "subjectaccessreviews"}: {},
|
{Group: "authorization.k8s.io", Resource: "subjectaccessreviews"}: {},
|
||||||
{Group: "authorization.k8s.io", Resource: "selfsubjectaccessreviews"}: {},
|
{Group: "authorization.k8s.io", Resource: "selfsubjectaccessreviews"}: {},
|
||||||
{Group: "authorization.k8s.io", Resource: "localsubjectaccessreviews"}: {},
|
{Group: "authorization.k8s.io", Resource: "localsubjectaccessreviews"}: {},
|
||||||
|
{Group: "authorization.k8s.io", Resource: "selfsubjectrulesreviews"}: {},
|
||||||
{Group: "apiregistration.k8s.io", Resource: "apiservices"}: {},
|
{Group: "apiregistration.k8s.io", Resource: "apiservices"}: {},
|
||||||
{Group: "apiextensions.k8s.io", Resource: "customresourcedefinitions"}: {},
|
{Group: "apiextensions.k8s.io", Resource: "customresourcedefinitions"}: {},
|
||||||
}
|
}
|
||||||
|
@ -56,17 +56,20 @@ type AuthorizationConfig struct {
|
|||||||
|
|
||||||
// New returns the right sort of union of multiple authorizer.Authorizer objects
|
// New returns the right sort of union of multiple authorizer.Authorizer objects
|
||||||
// based on the authorizationMode or an error.
|
// based on the authorizationMode or an error.
|
||||||
func (config AuthorizationConfig) New() (authorizer.Authorizer, error) {
|
func (config AuthorizationConfig) New() (authorizer.Authorizer, authorizer.RuleResolver, error) {
|
||||||
if len(config.AuthorizationModes) == 0 {
|
if len(config.AuthorizationModes) == 0 {
|
||||||
return nil, errors.New("At least one authorization mode should be passed")
|
return nil, nil, errors.New("At least one authorization mode should be passed")
|
||||||
}
|
}
|
||||||
|
|
||||||
var authorizers []authorizer.Authorizer
|
var (
|
||||||
|
authorizers []authorizer.Authorizer
|
||||||
|
ruleResolvers []authorizer.RuleResolver
|
||||||
|
)
|
||||||
authorizerMap := make(map[string]bool)
|
authorizerMap := make(map[string]bool)
|
||||||
|
|
||||||
for _, authorizationMode := range config.AuthorizationModes {
|
for _, authorizationMode := range config.AuthorizationModes {
|
||||||
if authorizerMap[authorizationMode] {
|
if authorizerMap[authorizationMode] {
|
||||||
return nil, fmt.Errorf("Authorization mode %s specified more than once", authorizationMode)
|
return nil, nil, fmt.Errorf("Authorization mode %s specified more than once", authorizationMode)
|
||||||
}
|
}
|
||||||
// Keep cases in sync with constant list above.
|
// Keep cases in sync with constant list above.
|
||||||
switch authorizationMode {
|
switch authorizationMode {
|
||||||
@ -81,29 +84,35 @@ func (config AuthorizationConfig) New() (authorizer.Authorizer, error) {
|
|||||||
authorizers = append(authorizers, nodeAuthorizer)
|
authorizers = append(authorizers, nodeAuthorizer)
|
||||||
|
|
||||||
case modes.ModeAlwaysAllow:
|
case modes.ModeAlwaysAllow:
|
||||||
authorizers = append(authorizers, authorizerfactory.NewAlwaysAllowAuthorizer())
|
alwaysAllowAuthorizer := authorizerfactory.NewAlwaysAllowAuthorizer()
|
||||||
|
authorizers = append(authorizers, alwaysAllowAuthorizer)
|
||||||
|
ruleResolvers = append(ruleResolvers, alwaysAllowAuthorizer)
|
||||||
case modes.ModeAlwaysDeny:
|
case modes.ModeAlwaysDeny:
|
||||||
authorizers = append(authorizers, authorizerfactory.NewAlwaysDenyAuthorizer())
|
alwaysDenyAuthorizer := authorizerfactory.NewAlwaysDenyAuthorizer()
|
||||||
|
authorizers = append(authorizers, alwaysDenyAuthorizer)
|
||||||
|
ruleResolvers = append(ruleResolvers, alwaysDenyAuthorizer)
|
||||||
case modes.ModeABAC:
|
case modes.ModeABAC:
|
||||||
if config.PolicyFile == "" {
|
if config.PolicyFile == "" {
|
||||||
return nil, errors.New("ABAC's authorization policy file not passed")
|
return nil, nil, errors.New("ABAC's authorization policy file not passed")
|
||||||
}
|
}
|
||||||
abacAuthorizer, err := abac.NewFromFile(config.PolicyFile)
|
abacAuthorizer, err := abac.NewFromFile(config.PolicyFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
authorizers = append(authorizers, abacAuthorizer)
|
authorizers = append(authorizers, abacAuthorizer)
|
||||||
|
ruleResolvers = append(ruleResolvers, abacAuthorizer)
|
||||||
case modes.ModeWebhook:
|
case modes.ModeWebhook:
|
||||||
if config.WebhookConfigFile == "" {
|
if config.WebhookConfigFile == "" {
|
||||||
return nil, errors.New("Webhook's configuration file not passed")
|
return nil, nil, errors.New("Webhook's configuration file not passed")
|
||||||
}
|
}
|
||||||
webhookAuthorizer, err := webhook.New(config.WebhookConfigFile,
|
webhookAuthorizer, err := webhook.New(config.WebhookConfigFile,
|
||||||
config.WebhookCacheAuthorizedTTL,
|
config.WebhookCacheAuthorizedTTL,
|
||||||
config.WebhookCacheUnauthorizedTTL)
|
config.WebhookCacheUnauthorizedTTL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
authorizers = append(authorizers, webhookAuthorizer)
|
authorizers = append(authorizers, webhookAuthorizer)
|
||||||
|
ruleResolvers = append(ruleResolvers, webhookAuthorizer)
|
||||||
case modes.ModeRBAC:
|
case modes.ModeRBAC:
|
||||||
rbacAuthorizer := rbac.New(
|
rbacAuthorizer := rbac.New(
|
||||||
&rbac.RoleGetter{Lister: config.InformerFactory.Rbac().InternalVersion().Roles().Lister()},
|
&rbac.RoleGetter{Lister: config.InformerFactory.Rbac().InternalVersion().Roles().Lister()},
|
||||||
@ -112,18 +121,19 @@ func (config AuthorizationConfig) New() (authorizer.Authorizer, error) {
|
|||||||
&rbac.ClusterRoleBindingLister{Lister: config.InformerFactory.Rbac().InternalVersion().ClusterRoleBindings().Lister()},
|
&rbac.ClusterRoleBindingLister{Lister: config.InformerFactory.Rbac().InternalVersion().ClusterRoleBindings().Lister()},
|
||||||
)
|
)
|
||||||
authorizers = append(authorizers, rbacAuthorizer)
|
authorizers = append(authorizers, rbacAuthorizer)
|
||||||
|
ruleResolvers = append(ruleResolvers, rbacAuthorizer)
|
||||||
default:
|
default:
|
||||||
return nil, fmt.Errorf("Unknown authorization mode %s specified", authorizationMode)
|
return nil, nil, fmt.Errorf("Unknown authorization mode %s specified", authorizationMode)
|
||||||
}
|
}
|
||||||
authorizerMap[authorizationMode] = true
|
authorizerMap[authorizationMode] = true
|
||||||
}
|
}
|
||||||
|
|
||||||
if !authorizerMap[modes.ModeABAC] && config.PolicyFile != "" {
|
if !authorizerMap[modes.ModeABAC] && config.PolicyFile != "" {
|
||||||
return nil, errors.New("Cannot specify --authorization-policy-file without mode ABAC")
|
return nil, nil, errors.New("Cannot specify --authorization-policy-file without mode ABAC")
|
||||||
}
|
}
|
||||||
if !authorizerMap[modes.ModeWebhook] && config.WebhookConfigFile != "" {
|
if !authorizerMap[modes.ModeWebhook] && config.WebhookConfigFile != "" {
|
||||||
return nil, errors.New("Cannot specify --authorization-webhook-config-file without mode Webhook")
|
return nil, nil, errors.New("Cannot specify --authorization-webhook-config-file without mode Webhook")
|
||||||
}
|
}
|
||||||
|
|
||||||
return union.New(authorizers...), nil
|
return union.New(authorizers...), union.NewRuleResolvers(ruleResolvers...), nil
|
||||||
}
|
}
|
||||||
|
@ -91,7 +91,7 @@ func TestNew(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
_, err := tt.config.New()
|
_, _, err := tt.config.New()
|
||||||
if tt.wantErr && (err == nil) {
|
if tt.wantErr && (err == nil) {
|
||||||
t.Errorf("New %s", tt.msg)
|
t.Errorf("New %s", tt.msg)
|
||||||
} else if !tt.wantErr && (err != nil) {
|
} else if !tt.wantErr && (err != nil) {
|
||||||
|
@ -256,7 +256,7 @@ func (c completedConfig) New(delegationTarget genericapiserver.DelegationTarget)
|
|||||||
// handlers that we have.
|
// handlers that we have.
|
||||||
restStorageProviders := []RESTStorageProvider{
|
restStorageProviders := []RESTStorageProvider{
|
||||||
authenticationrest.RESTStorageProvider{Authenticator: c.GenericConfig.Authenticator},
|
authenticationrest.RESTStorageProvider{Authenticator: c.GenericConfig.Authenticator},
|
||||||
authorizationrest.RESTStorageProvider{Authorizer: c.GenericConfig.Authorizer},
|
authorizationrest.RESTStorageProvider{Authorizer: c.GenericConfig.Authorizer, RuleResolver: c.GenericConfig.RuleResolver},
|
||||||
autoscalingrest.RESTStorageProvider{},
|
autoscalingrest.RESTStorageProvider{},
|
||||||
batchrest.RESTStorageProvider{},
|
batchrest.RESTStorageProvider{},
|
||||||
certificatesrest.RESTStorageProvider{},
|
certificatesrest.RESTStorageProvider{},
|
||||||
|
@ -28,11 +28,13 @@ import (
|
|||||||
"k8s.io/kubernetes/pkg/apis/authorization"
|
"k8s.io/kubernetes/pkg/apis/authorization"
|
||||||
"k8s.io/kubernetes/pkg/registry/authorization/localsubjectaccessreview"
|
"k8s.io/kubernetes/pkg/registry/authorization/localsubjectaccessreview"
|
||||||
"k8s.io/kubernetes/pkg/registry/authorization/selfsubjectaccessreview"
|
"k8s.io/kubernetes/pkg/registry/authorization/selfsubjectaccessreview"
|
||||||
|
"k8s.io/kubernetes/pkg/registry/authorization/selfsubjectrulesreview"
|
||||||
"k8s.io/kubernetes/pkg/registry/authorization/subjectaccessreview"
|
"k8s.io/kubernetes/pkg/registry/authorization/subjectaccessreview"
|
||||||
)
|
)
|
||||||
|
|
||||||
type RESTStorageProvider struct {
|
type RESTStorageProvider struct {
|
||||||
Authorizer authorizer.Authorizer
|
Authorizer authorizer.Authorizer
|
||||||
|
RuleResolver authorizer.RuleResolver
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p RESTStorageProvider) NewRESTStorage(apiResourceConfigSource serverstorage.APIResourceConfigSource, restOptionsGetter generic.RESTOptionsGetter) (genericapiserver.APIGroupInfo, bool) {
|
func (p RESTStorageProvider) NewRESTStorage(apiResourceConfigSource serverstorage.APIResourceConfigSource, restOptionsGetter generic.RESTOptionsGetter) (genericapiserver.APIGroupInfo, bool) {
|
||||||
@ -70,6 +72,9 @@ func (p RESTStorageProvider) v1beta1Storage(apiResourceConfigSource serverstorag
|
|||||||
if apiResourceConfigSource.ResourceEnabled(version.WithResource("localsubjectaccessreviews")) {
|
if apiResourceConfigSource.ResourceEnabled(version.WithResource("localsubjectaccessreviews")) {
|
||||||
storage["localsubjectaccessreviews"] = localsubjectaccessreview.NewREST(p.Authorizer)
|
storage["localsubjectaccessreviews"] = localsubjectaccessreview.NewREST(p.Authorizer)
|
||||||
}
|
}
|
||||||
|
if apiResourceConfigSource.ResourceEnabled(version.WithResource("selfsubjectrulesreviews")) {
|
||||||
|
storage["selfsubjectrulesreviews"] = selfsubjectrulesreview.NewREST(p.RuleResolver)
|
||||||
|
}
|
||||||
|
|
||||||
return storage
|
return storage
|
||||||
}
|
}
|
||||||
@ -87,6 +92,9 @@ func (p RESTStorageProvider) v1Storage(apiResourceConfigSource serverstorage.API
|
|||||||
if apiResourceConfigSource.ResourceEnabled(version.WithResource("localsubjectaccessreviews")) {
|
if apiResourceConfigSource.ResourceEnabled(version.WithResource("localsubjectaccessreviews")) {
|
||||||
storage["localsubjectaccessreviews"] = localsubjectaccessreview.NewREST(p.Authorizer)
|
storage["localsubjectaccessreviews"] = localsubjectaccessreview.NewREST(p.Authorizer)
|
||||||
}
|
}
|
||||||
|
if apiResourceConfigSource.ResourceEnabled(version.WithResource("selfsubjectrulesreviews")) {
|
||||||
|
storage["selfsubjectrulesreviews"] = selfsubjectrulesreview.NewREST(p.RuleResolver)
|
||||||
|
}
|
||||||
|
|
||||||
return storage
|
return storage
|
||||||
}
|
}
|
||||||
|
99
pkg/registry/authorization/selfsubjectrulesreview/rest.go
Normal file
99
pkg/registry/authorization/selfsubjectrulesreview/rest.go
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
/*
|
||||||
|
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 selfsubjectrulesreview
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||||
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
|
"k8s.io/apiserver/pkg/authorization/authorizer"
|
||||||
|
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
|
||||||
|
authorizationapi "k8s.io/kubernetes/pkg/apis/authorization"
|
||||||
|
)
|
||||||
|
|
||||||
|
// REST implements a RESTStorage for selfsubjectrulesreview.
|
||||||
|
type REST struct {
|
||||||
|
ruleResolver authorizer.RuleResolver
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewREST returns a RESTStorage object that will work against selfsubjectrulesreview.
|
||||||
|
func NewREST(ruleResolver authorizer.RuleResolver) *REST {
|
||||||
|
return &REST{ruleResolver}
|
||||||
|
}
|
||||||
|
|
||||||
|
// New creates a new selfsubjectrulesreview object.
|
||||||
|
func (r *REST) New() runtime.Object {
|
||||||
|
return &authorizationapi.SelfSubjectRulesReview{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create attempts to get self subject rules in specific namespace.
|
||||||
|
func (r *REST) Create(ctx genericapirequest.Context, obj runtime.Object, includeUninitialized bool) (runtime.Object, error) {
|
||||||
|
selfSRR, ok := obj.(*authorizationapi.SelfSubjectRulesReview)
|
||||||
|
if !ok {
|
||||||
|
return nil, apierrors.NewBadRequest(fmt.Sprintf("not a SelfSubjectRulesReview: %#v", obj))
|
||||||
|
}
|
||||||
|
|
||||||
|
user, ok := genericapirequest.UserFrom(ctx)
|
||||||
|
if !ok {
|
||||||
|
return nil, apierrors.NewBadRequest("no user present on request")
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace := selfSRR.Spec.Namespace
|
||||||
|
if namespace == "" {
|
||||||
|
return nil, apierrors.NewBadRequest("no namespace on request")
|
||||||
|
}
|
||||||
|
resourceInfo, nonResourceInfo, incomplete, err := r.ruleResolver.RulesFor(user, namespace)
|
||||||
|
|
||||||
|
ret := &authorizationapi.SelfSubjectRulesReview{
|
||||||
|
Status: authorizationapi.SubjectRulesReviewStatus{
|
||||||
|
ResourceRules: getResourceRules(resourceInfo),
|
||||||
|
NonResourceRules: getNonResourceRules(nonResourceInfo),
|
||||||
|
Incomplete: incomplete,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
ret.Status.EvaluationError = err.Error()
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getResourceRules(infos []authorizer.ResourceRuleInfo) []authorizationapi.ResourceRule {
|
||||||
|
rules := make([]authorizationapi.ResourceRule, len(infos))
|
||||||
|
for i, info := range infos {
|
||||||
|
rules[i] = authorizationapi.ResourceRule{
|
||||||
|
Verbs: info.GetVerbs(),
|
||||||
|
APIGroups: info.GetAPIGroups(),
|
||||||
|
Resources: info.GetResources(),
|
||||||
|
ResourceNames: info.GetResourceNames(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return rules
|
||||||
|
}
|
||||||
|
|
||||||
|
func getNonResourceRules(infos []authorizer.NonResourceRuleInfo) []authorizationapi.NonResourceRule {
|
||||||
|
rules := make([]authorizationapi.NonResourceRule, len(infos))
|
||||||
|
for i, info := range infos {
|
||||||
|
rules[i] = authorizationapi.NonResourceRule{
|
||||||
|
Verbs: info.GetVerbs(),
|
||||||
|
NonResourceURLs: info.GetNonResourceURLs(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return rules
|
||||||
|
}
|
@ -123,6 +123,36 @@ func (r *RBACAuthorizer) Authorize(requestAttributes authorizer.Attributes) (boo
|
|||||||
return false, reason, nil
|
return false, 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 {
|
func New(roles rbacregistryvalidation.RoleGetter, roleBindings rbacregistryvalidation.RoleBindingLister, clusterRoles rbacregistryvalidation.ClusterRoleGetter, clusterRoleBindings rbacregistryvalidation.ClusterRoleBindingLister) *RBACAuthorizer {
|
||||||
authorizer := &RBACAuthorizer{
|
authorizer := &RBACAuthorizer{
|
||||||
authorizationRuleResolver: rbacregistryvalidation.NewDefaultRuleResolver(
|
authorizationRuleResolver: rbacregistryvalidation.NewDefaultRuleResolver(
|
||||||
|
@ -44,6 +44,7 @@ var (
|
|||||||
// Adds the list of known types to api.Scheme.
|
// Adds the list of known types to api.Scheme.
|
||||||
func addKnownTypes(scheme *runtime.Scheme) error {
|
func addKnownTypes(scheme *runtime.Scheme) error {
|
||||||
scheme.AddKnownTypes(SchemeGroupVersion,
|
scheme.AddKnownTypes(SchemeGroupVersion,
|
||||||
|
&SelfSubjectRulesReview{},
|
||||||
&SelfSubjectAccessReview{},
|
&SelfSubjectAccessReview{},
|
||||||
&SubjectAccessReview{},
|
&SubjectAccessReview{},
|
||||||
&LocalSubjectAccessReview{},
|
&LocalSubjectAccessReview{},
|
||||||
|
@ -180,3 +180,82 @@ type SubjectAccessReviewStatus struct {
|
|||||||
// +optional
|
// +optional
|
||||||
EvaluationError string `json:"evaluationError,omitempty" protobuf:"bytes,3,opt,name=evaluationError"`
|
EvaluationError string `json:"evaluationError,omitempty" protobuf:"bytes,3,opt,name=evaluationError"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// +genclient
|
||||||
|
// +genclient:nonNamespaced
|
||||||
|
// +genclient:noVerbs
|
||||||
|
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
||||||
|
|
||||||
|
// SelfSubjectRulesReview enumerates the set of actions the current user can perform within a namespace.
|
||||||
|
// The returned list of actions may be incomplete depending on the server's authorization mode,
|
||||||
|
// and any errors experienced during the evaluation. SelfSubjectRulesReview should be used by UIs to show/hide actions,
|
||||||
|
// or to quickly let an end user reason about their permissions. It should NOT Be used by external systems to
|
||||||
|
// drive authorization decisions as this raises confused deputy, cache lifetime/revocation, and correctness concerns.
|
||||||
|
// SubjectAccessReview, and LocalAccessReview are the correct way to defer authorization decisions to the API server.
|
||||||
|
type SelfSubjectRulesReview struct {
|
||||||
|
metav1.TypeMeta `json:",inline"`
|
||||||
|
// +optional
|
||||||
|
metav1.ObjectMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"`
|
||||||
|
|
||||||
|
// Spec holds information about the request being evaluated.
|
||||||
|
Spec SelfSubjectRulesReviewSpec `json:"spec" protobuf:"bytes,2,opt,name=spec"`
|
||||||
|
|
||||||
|
// Status is filled in by the server and indicates the set of actions a user can perform.
|
||||||
|
// +optional
|
||||||
|
Status SubjectRulesReviewStatus `json:"status,omitempty" protobuf:"bytes,3,opt,name=status"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type SelfSubjectRulesReviewSpec struct {
|
||||||
|
// Namespace to evaluate rules for. Required.
|
||||||
|
Namespace string `json:"namespace,omitempty" protobuf:"bytes,1,opt,name=namespace"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// SubjectRulesReviewStatus contains the result of a rules check. This check can be incomplete depending on
|
||||||
|
// the set of authorizers the server is configured with and any errors experienced during evaluation.
|
||||||
|
// Because authorization rules are additive, if a rule appears in a list it's safe to assume the subject has that permission,
|
||||||
|
// even if that list is incomplete.
|
||||||
|
type SubjectRulesReviewStatus struct {
|
||||||
|
// ResourceRules is the list of actions the subject is allowed to perform on resources.
|
||||||
|
// The list ordering isn't significant, may contain duplicates, and possibly be incomplete.
|
||||||
|
ResourceRules []ResourceRule `json:"resourceRules" protobuf:"bytes,1,rep,name=resourceRules"`
|
||||||
|
// NonResourceRules is the list of actions the subject is allowed to perform on non-resources.
|
||||||
|
// The list ordering isn't significant, may contain duplicates, and possibly be incomplete.
|
||||||
|
NonResourceRules []NonResourceRule `json:"nonResourceRules" protobuf:"bytes,2,rep,name=nonResourceRules"`
|
||||||
|
// Incomplete is true when the rules returned by this call are incomplete. This is most commonly
|
||||||
|
// encountered when an authorizer, such as an external authorizer, doesn't support rules evaluation.
|
||||||
|
Incomplete bool `json:"incomplete" protobuf:"bytes,3,rep,name=incomplete"`
|
||||||
|
// EvaluationError can appear in combination with Rules. It indicates an error occurred during
|
||||||
|
// rule evaluation, such as an authorizer that doesn't support rule evaluation, and that
|
||||||
|
// ResourceRules and/or NonResourceRules may be incomplete.
|
||||||
|
// +optional
|
||||||
|
EvaluationError string `json:"evaluationError,omitempty" protobuf:"bytes,4,opt,name=evaluationError"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ResourceRule is the list of actions the subject is allowed to perform on resources. The list ordering isn't significant,
|
||||||
|
// may contain duplicates, and possibly be incomplete.
|
||||||
|
type ResourceRule struct {
|
||||||
|
// Verb is a list of kubernetes resource API verbs, like: get, list, watch, create, update, delete, proxy. "*" means all.
|
||||||
|
Verbs []string `json:"verbs" protobuf:"bytes,1,rep,name=verbs"`
|
||||||
|
|
||||||
|
// APIGroups is the name of the APIGroup that contains the resources. If multiple API groups are specified, any action requested against one of
|
||||||
|
// the enumerated resources in any API group will be allowed. "*" means all.
|
||||||
|
// +optional
|
||||||
|
APIGroups []string `json:"apiGroups,omitempty" protobuf:"bytes,2,rep,name=apiGroups"`
|
||||||
|
// Resources is a list of resources this rule applies to. ResourceAll represents all resources. "*" means all.
|
||||||
|
// +optional
|
||||||
|
Resources []string `json:"resources,omitempty" protobuf:"bytes,3,rep,name=resources"`
|
||||||
|
// ResourceNames is an optional white list of names that the rule applies to. An empty set means that everything is allowed. "*" means all.
|
||||||
|
// +optional
|
||||||
|
ResourceNames []string `json:"resourceNames,omitempty" protobuf:"bytes,4,rep,name=resourceNames"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// NonResourceRule holds information that describes a rule for the non-resource
|
||||||
|
type NonResourceRule struct {
|
||||||
|
// Verb is a list of kubernetes non-resource API verbs, like: get, post, put, delete, patch, head, options. "*" means all.
|
||||||
|
Verbs []string `json:"verbs" protobuf:"bytes,1,rep,name=verbs"`
|
||||||
|
|
||||||
|
// NonResourceURLs is a set of partial urls that a user should have access to. *s are allowed, but only as the full,
|
||||||
|
// final step in the path. "*" means all.
|
||||||
|
// +optional
|
||||||
|
NonResourceURLs []string `json:"nonResourceURLs,omitempty" protobuf:"bytes,2,rep,name=nonResourceURLs"`
|
||||||
|
}
|
||||||
|
@ -44,6 +44,7 @@ var (
|
|||||||
// Adds the list of known types to api.Scheme.
|
// Adds the list of known types to api.Scheme.
|
||||||
func addKnownTypes(scheme *runtime.Scheme) error {
|
func addKnownTypes(scheme *runtime.Scheme) error {
|
||||||
scheme.AddKnownTypes(SchemeGroupVersion,
|
scheme.AddKnownTypes(SchemeGroupVersion,
|
||||||
|
&SelfSubjectRulesReview{},
|
||||||
&SelfSubjectAccessReview{},
|
&SelfSubjectAccessReview{},
|
||||||
&SubjectAccessReview{},
|
&SubjectAccessReview{},
|
||||||
&LocalSubjectAccessReview{},
|
&LocalSubjectAccessReview{},
|
||||||
|
@ -180,3 +180,82 @@ type SubjectAccessReviewStatus struct {
|
|||||||
// +optional
|
// +optional
|
||||||
EvaluationError string `json:"evaluationError,omitempty" protobuf:"bytes,3,opt,name=evaluationError"`
|
EvaluationError string `json:"evaluationError,omitempty" protobuf:"bytes,3,opt,name=evaluationError"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// +genclient
|
||||||
|
// +genclient:nonNamespaced
|
||||||
|
// +genclient:noVerbs
|
||||||
|
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
||||||
|
|
||||||
|
// SelfSubjectRulesReview enumerates the set of actions the current user can perform within a namespace.
|
||||||
|
// The returned list of actions may be incomplete depending on the server's authorization mode,
|
||||||
|
// and any errors experienced during the evaluation. SelfSubjectRulesReview should be used by UIs to show/hide actions,
|
||||||
|
// or to quickly let an end user reason about their permissions. It should NOT Be used by external systems to
|
||||||
|
// drive authorization decisions as this raises confused deputy, cache lifetime/revocation, and correctness concerns.
|
||||||
|
// SubjectAccessReview, and LocalAccessReview are the correct way to defer authorization decisions to the API server.
|
||||||
|
type SelfSubjectRulesReview struct {
|
||||||
|
metav1.TypeMeta `json:",inline"`
|
||||||
|
// +optional
|
||||||
|
metav1.ObjectMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"`
|
||||||
|
|
||||||
|
// Spec holds information about the request being evaluated.
|
||||||
|
Spec SelfSubjectRulesReviewSpec `json:"spec" protobuf:"bytes,2,opt,name=spec"`
|
||||||
|
|
||||||
|
// Status is filled in by the server and indicates the set of actions a user can perform.
|
||||||
|
// +optional
|
||||||
|
Status SubjectRulesReviewStatus `json:"status,omitempty" protobuf:"bytes,3,opt,name=status"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type SelfSubjectRulesReviewSpec struct {
|
||||||
|
// Namespace to evaluate rules for. Required.
|
||||||
|
Namespace string `json:"namespace,omitempty" protobuf:"bytes,1,opt,name=namespace"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// SubjectRulesReviewStatus contains the result of a rules check. This check can be incomplete depending on
|
||||||
|
// the set of authorizers the server is configured with and any errors experienced during evaluation.
|
||||||
|
// Because authorization rules are additive, if a rule appears in a list it's safe to assume the subject has that permission,
|
||||||
|
// even if that list is incomplete.
|
||||||
|
type SubjectRulesReviewStatus struct {
|
||||||
|
// ResourceRules is the list of actions the subject is allowed to perform on resources.
|
||||||
|
// The list ordering isn't significant, may contain duplicates, and possibly be incomplete.
|
||||||
|
ResourceRules []ResourceRule `json:"resourceRules" protobuf:"bytes,1,rep,name=resourceRules"`
|
||||||
|
// NonResourceRules is the list of actions the subject is allowed to perform on non-resources.
|
||||||
|
// The list ordering isn't significant, may contain duplicates, and possibly be incomplete.
|
||||||
|
NonResourceRules []NonResourceRule `json:"nonResourceRules" protobuf:"bytes,2,rep,name=nonResourceRules"`
|
||||||
|
// Incomplete is true when the rules returned by this call are incomplete. This is most commonly
|
||||||
|
// encountered when an authorizer, such as an external authorizer, doesn't support rules evaluation.
|
||||||
|
Incomplete bool `json:"incomplete" protobuf:"bytes,3,rep,name=incomplete"`
|
||||||
|
// EvaluationError can appear in combination with Rules. It indicates an error occurred during
|
||||||
|
// rule evaluation, such as an authorizer that doesn't support rule evaluation, and that
|
||||||
|
// ResourceRules and/or NonResourceRules may be incomplete.
|
||||||
|
// +optional
|
||||||
|
EvaluationError string `json:"evaluationError,omitempty" protobuf:"bytes,4,opt,name=evaluationError"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ResourceRule is the list of actions the subject is allowed to perform on resources. The list ordering isn't significant,
|
||||||
|
// may contain duplicates, and possibly be incomplete.
|
||||||
|
type ResourceRule struct {
|
||||||
|
// Verb is a list of kubernetes resource API verbs, like: get, list, watch, create, update, delete, proxy. "*" means all.
|
||||||
|
Verbs []string `json:"verbs" protobuf:"bytes,1,rep,name=verbs"`
|
||||||
|
|
||||||
|
// APIGroups is the name of the APIGroup that contains the resources. If multiple API groups are specified, any action requested against one of
|
||||||
|
// the enumerated resources in any API group will be allowed. "*" means all.
|
||||||
|
// +optional
|
||||||
|
APIGroups []string `json:"apiGroups,omitempty" protobuf:"bytes,2,rep,name=apiGroups"`
|
||||||
|
// Resources is a list of resources this rule applies to. ResourceAll represents all resources. "*" means all.
|
||||||
|
// +optional
|
||||||
|
Resources []string `json:"resources,omitempty" protobuf:"bytes,3,rep,name=resources"`
|
||||||
|
// ResourceNames is an optional white list of names that the rule applies to. An empty set means that everything is allowed. "*" means all.
|
||||||
|
// +optional
|
||||||
|
ResourceNames []string `json:"resourceNames,omitempty" protobuf:"bytes,4,rep,name=resourceNames"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// NonResourceRule holds information that describes a rule for the non-resource
|
||||||
|
type NonResourceRule struct {
|
||||||
|
// Verb is a list of kubernetes non-resource API verbs, like: get, post, put, delete, patch, head, options. "*" means all.
|
||||||
|
Verbs []string `json:"verbs" protobuf:"bytes,1,rep,name=verbs"`
|
||||||
|
|
||||||
|
// NonResourceURLs is a set of partial urls that a user should have access to. *s are allowed, but only as the full,
|
||||||
|
// final step in the path. "*" means all.
|
||||||
|
// +optional
|
||||||
|
NonResourceURLs []string `json:"nonResourceURLs,omitempty" protobuf:"bytes,2,rep,name=nonResourceURLs"`
|
||||||
|
}
|
||||||
|
@ -76,6 +76,12 @@ func (f AuthorizerFunc) Authorize(a Attributes) (bool, string, error) {
|
|||||||
return f(a)
|
return f(a)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RuleResolver provides a mechanism for resolving the list of rules that apply to a given user within a namespace.
|
||||||
|
type RuleResolver interface {
|
||||||
|
// RulesFor get the list of cluster wide rules, the list of rules in the specific namespace, incomplete status and errors.
|
||||||
|
RulesFor(user user.Info, namespace string) ([]ResourceRuleInfo, []NonResourceRuleInfo, bool, error)
|
||||||
|
}
|
||||||
|
|
||||||
// RequestAttributesGetter provides a function that extracts Attributes from an http.Request
|
// RequestAttributesGetter provides a function that extracts Attributes from an http.Request
|
||||||
type RequestAttributesGetter interface {
|
type RequestAttributesGetter interface {
|
||||||
GetRequestAttributes(user.Info, *http.Request) Attributes
|
GetRequestAttributes(user.Info, *http.Request) Attributes
|
||||||
|
@ -0,0 +1,73 @@
|
|||||||
|
/*
|
||||||
|
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 authorizer
|
||||||
|
|
||||||
|
type ResourceRuleInfo interface {
|
||||||
|
// GetVerbs returns a list of kubernetes resource API verbs.
|
||||||
|
GetVerbs() []string
|
||||||
|
// GetAPIGroups return the names of the APIGroup that contains the resources.
|
||||||
|
GetAPIGroups() []string
|
||||||
|
// GetResources return a list of resources the rule applies to.
|
||||||
|
GetResources() []string
|
||||||
|
// GetResourceNames return a white list of names that the rule applies to.
|
||||||
|
GetResourceNames() []string
|
||||||
|
}
|
||||||
|
|
||||||
|
// DefaultResourceRuleInfo holds information that describes a rule for the resource
|
||||||
|
type DefaultResourceRuleInfo struct {
|
||||||
|
Verbs []string
|
||||||
|
APIGroups []string
|
||||||
|
Resources []string
|
||||||
|
ResourceNames []string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *DefaultResourceRuleInfo) GetVerbs() []string {
|
||||||
|
return i.Verbs
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *DefaultResourceRuleInfo) GetAPIGroups() []string {
|
||||||
|
return i.APIGroups
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *DefaultResourceRuleInfo) GetResources() []string {
|
||||||
|
return i.Resources
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *DefaultResourceRuleInfo) GetResourceNames() []string {
|
||||||
|
return i.ResourceNames
|
||||||
|
}
|
||||||
|
|
||||||
|
type NonResourceRuleInfo interface {
|
||||||
|
// GetVerbs returns a list of kubernetes resource API verbs.
|
||||||
|
GetVerbs() []string
|
||||||
|
// GetNonResourceURLs return a set of partial urls that a user should have access to.
|
||||||
|
GetNonResourceURLs() []string
|
||||||
|
}
|
||||||
|
|
||||||
|
// DefaultNonResourceRuleInfo holds information that describes a rule for the non-resource
|
||||||
|
type DefaultNonResourceRuleInfo struct {
|
||||||
|
Verbs []string
|
||||||
|
NonResourceURLs []string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *DefaultNonResourceRuleInfo) GetVerbs() []string {
|
||||||
|
return i.Verbs
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *DefaultNonResourceRuleInfo) GetNonResourceURLs() []string {
|
||||||
|
return i.NonResourceURLs
|
||||||
|
}
|
@ -19,6 +19,7 @@ package authorizerfactory
|
|||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
|
||||||
|
"k8s.io/apiserver/pkg/authentication/user"
|
||||||
"k8s.io/apiserver/pkg/authorization/authorizer"
|
"k8s.io/apiserver/pkg/authorization/authorizer"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -31,7 +32,22 @@ func (alwaysAllowAuthorizer) Authorize(a authorizer.Attributes) (authorized bool
|
|||||||
return true, "", nil
|
return true, "", nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewAlwaysAllowAuthorizer() authorizer.Authorizer {
|
func (alwaysAllowAuthorizer) RulesFor(user user.Info, namespace string) ([]authorizer.ResourceRuleInfo, []authorizer.NonResourceRuleInfo, bool, error) {
|
||||||
|
return []authorizer.ResourceRuleInfo{
|
||||||
|
&authorizer.DefaultResourceRuleInfo{
|
||||||
|
Verbs: []string{"*"},
|
||||||
|
APIGroups: []string{"*"},
|
||||||
|
Resources: []string{"*"},
|
||||||
|
},
|
||||||
|
}, []authorizer.NonResourceRuleInfo{
|
||||||
|
&authorizer.DefaultNonResourceRuleInfo{
|
||||||
|
Verbs: []string{"*"},
|
||||||
|
NonResourceURLs: []string{"*"},
|
||||||
|
},
|
||||||
|
}, false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewAlwaysAllowAuthorizer() *alwaysAllowAuthorizer {
|
||||||
return new(alwaysAllowAuthorizer)
|
return new(alwaysAllowAuthorizer)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -44,7 +60,11 @@ func (alwaysDenyAuthorizer) Authorize(a authorizer.Attributes) (authorized bool,
|
|||||||
return false, "Everything is forbidden.", nil
|
return false, "Everything is forbidden.", nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewAlwaysDenyAuthorizer() authorizer.Authorizer {
|
func (alwaysDenyAuthorizer) RulesFor(user user.Info, namespace string) ([]authorizer.ResourceRuleInfo, []authorizer.NonResourceRuleInfo, bool, error) {
|
||||||
|
return []authorizer.ResourceRuleInfo{}, []authorizer.NonResourceRuleInfo{}, false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewAlwaysDenyAuthorizer() *alwaysDenyAuthorizer {
|
||||||
return new(alwaysDenyAuthorizer)
|
return new(alwaysDenyAuthorizer)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -20,6 +20,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
utilerrors "k8s.io/apimachinery/pkg/util/errors"
|
utilerrors "k8s.io/apimachinery/pkg/util/errors"
|
||||||
|
"k8s.io/apiserver/pkg/authentication/user"
|
||||||
"k8s.io/apiserver/pkg/authorization/authorizer"
|
"k8s.io/apiserver/pkg/authorization/authorizer"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -55,3 +56,40 @@ func (authzHandler unionAuthzHandler) Authorize(a authorizer.Attributes) (bool,
|
|||||||
|
|
||||||
return false, strings.Join(reasonlist, "\n"), utilerrors.NewAggregate(errlist)
|
return false, strings.Join(reasonlist, "\n"), utilerrors.NewAggregate(errlist)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// unionAuthzRulesHandler authorizer against a chain of authorizer.RuleResolver
|
||||||
|
type unionAuthzRulesHandler []authorizer.RuleResolver
|
||||||
|
|
||||||
|
// NewRuleResolvers returns an authorizer that authorizes against a chain of authorizer.Authorizer objects
|
||||||
|
func NewRuleResolvers(authorizationHandlers ...authorizer.RuleResolver) authorizer.RuleResolver {
|
||||||
|
return unionAuthzRulesHandler(authorizationHandlers)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RulesFor against a chain of authorizer.RuleResolver objects and returns nil if successful and returns error if unsuccessful
|
||||||
|
func (authzHandler unionAuthzRulesHandler) RulesFor(user user.Info, namespace string) ([]authorizer.ResourceRuleInfo, []authorizer.NonResourceRuleInfo, bool, error) {
|
||||||
|
var (
|
||||||
|
errList []error
|
||||||
|
resourceRulesList []authorizer.ResourceRuleInfo
|
||||||
|
nonResourceRulesList []authorizer.NonResourceRuleInfo
|
||||||
|
)
|
||||||
|
incompleteStatus := false
|
||||||
|
|
||||||
|
for _, currAuthzHandler := range authzHandler {
|
||||||
|
resourceRules, nonResourceRules, incomplete, err := currAuthzHandler.RulesFor(user, namespace)
|
||||||
|
|
||||||
|
if incomplete == true {
|
||||||
|
incompleteStatus = true
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
errList = append(errList, err)
|
||||||
|
}
|
||||||
|
if len(resourceRules) > 0 {
|
||||||
|
resourceRulesList = append(resourceRulesList, resourceRules...)
|
||||||
|
}
|
||||||
|
if len(nonResourceRules) > 0 {
|
||||||
|
nonResourceRulesList = append(nonResourceRulesList, nonResourceRules...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return resourceRulesList, nonResourceRulesList, incompleteStatus, utilerrors.NewAggregate(errList)
|
||||||
|
}
|
||||||
|
@ -18,8 +18,10 @@ package union
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"k8s.io/apiserver/pkg/authentication/user"
|
||||||
"k8s.io/apiserver/pkg/authorization/authorizer"
|
"k8s.io/apiserver/pkg/authorization/authorizer"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -81,3 +83,143 @@ func TestAuthorizationError(t *testing.T) {
|
|||||||
t.Errorf("Expected error: %v", err)
|
t.Errorf("Expected error: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type mockAuthzRuleHandler struct {
|
||||||
|
resourceRules []authorizer.ResourceRuleInfo
|
||||||
|
nonResourceRules []authorizer.NonResourceRuleInfo
|
||||||
|
err error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mock *mockAuthzRuleHandler) RulesFor(user user.Info, namespace string) ([]authorizer.ResourceRuleInfo, []authorizer.NonResourceRuleInfo, bool, error) {
|
||||||
|
if mock.err != nil {
|
||||||
|
return []authorizer.ResourceRuleInfo{}, []authorizer.NonResourceRuleInfo{}, false, mock.err
|
||||||
|
}
|
||||||
|
return mock.resourceRules, mock.nonResourceRules, false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAuthorizationResourceRules(t *testing.T) {
|
||||||
|
handler1 := &mockAuthzRuleHandler{
|
||||||
|
resourceRules: []authorizer.ResourceRuleInfo{
|
||||||
|
&authorizer.DefaultResourceRuleInfo{
|
||||||
|
Verbs: []string{"*"},
|
||||||
|
APIGroups: []string{"*"},
|
||||||
|
Resources: []string{"bindings"},
|
||||||
|
},
|
||||||
|
&authorizer.DefaultResourceRuleInfo{
|
||||||
|
Verbs: []string{"get", "list", "watch"},
|
||||||
|
APIGroups: []string{"*"},
|
||||||
|
Resources: []string{"*"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
handler2 := &mockAuthzRuleHandler{
|
||||||
|
resourceRules: []authorizer.ResourceRuleInfo{
|
||||||
|
&authorizer.DefaultResourceRuleInfo{
|
||||||
|
Verbs: []string{"*"},
|
||||||
|
APIGroups: []string{"*"},
|
||||||
|
Resources: []string{"events"},
|
||||||
|
},
|
||||||
|
&authorizer.DefaultResourceRuleInfo{
|
||||||
|
Verbs: []string{"get"},
|
||||||
|
APIGroups: []string{"*"},
|
||||||
|
Resources: []string{"*"},
|
||||||
|
ResourceNames: []string{"foo"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
expected := []authorizer.DefaultResourceRuleInfo{
|
||||||
|
{
|
||||||
|
Verbs: []string{"*"},
|
||||||
|
APIGroups: []string{"*"},
|
||||||
|
Resources: []string{"bindings"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Verbs: []string{"get", "list", "watch"},
|
||||||
|
APIGroups: []string{"*"},
|
||||||
|
Resources: []string{"*"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Verbs: []string{"*"},
|
||||||
|
APIGroups: []string{"*"},
|
||||||
|
Resources: []string{"events"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Verbs: []string{"get"},
|
||||||
|
APIGroups: []string{"*"},
|
||||||
|
Resources: []string{"*"},
|
||||||
|
ResourceNames: []string{"foo"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
authzRulesHandler := NewRuleResolvers(handler1, handler2)
|
||||||
|
|
||||||
|
rules, _, _, _ := authzRulesHandler.RulesFor(nil, "")
|
||||||
|
actual := getResourceRules(rules)
|
||||||
|
if !reflect.DeepEqual(expected, actual) {
|
||||||
|
t.Errorf("Expected: \n%#v\n but actual: \n%#v\n", expected, actual)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAuthorizationNonResourceRules(t *testing.T) {
|
||||||
|
handler1 := &mockAuthzRuleHandler{
|
||||||
|
nonResourceRules: []authorizer.NonResourceRuleInfo{
|
||||||
|
&authorizer.DefaultNonResourceRuleInfo{
|
||||||
|
Verbs: []string{"get"},
|
||||||
|
NonResourceURLs: []string{"/api"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
handler2 := &mockAuthzRuleHandler{
|
||||||
|
nonResourceRules: []authorizer.NonResourceRuleInfo{
|
||||||
|
&authorizer.DefaultNonResourceRuleInfo{
|
||||||
|
Verbs: []string{"get"},
|
||||||
|
NonResourceURLs: []string{"/api/*"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
expected := []authorizer.DefaultNonResourceRuleInfo{
|
||||||
|
{
|
||||||
|
Verbs: []string{"get"},
|
||||||
|
NonResourceURLs: []string{"/api"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Verbs: []string{"get"},
|
||||||
|
NonResourceURLs: []string{"/api/*"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
authzRulesHandler := NewRuleResolvers(handler1, handler2)
|
||||||
|
|
||||||
|
_, rules, _, _ := authzRulesHandler.RulesFor(nil, "")
|
||||||
|
actual := getNonResourceRules(rules)
|
||||||
|
if !reflect.DeepEqual(expected, actual) {
|
||||||
|
t.Errorf("Expected: \n%#v\n but actual: \n%#v\n", expected, actual)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func getResourceRules(infos []authorizer.ResourceRuleInfo) []authorizer.DefaultResourceRuleInfo {
|
||||||
|
rules := make([]authorizer.DefaultResourceRuleInfo, len(infos))
|
||||||
|
for i, info := range infos {
|
||||||
|
rules[i] = authorizer.DefaultResourceRuleInfo{
|
||||||
|
Verbs: info.GetVerbs(),
|
||||||
|
APIGroups: info.GetAPIGroups(),
|
||||||
|
Resources: info.GetResources(),
|
||||||
|
ResourceNames: info.GetResourceNames(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return rules
|
||||||
|
}
|
||||||
|
|
||||||
|
func getNonResourceRules(infos []authorizer.NonResourceRuleInfo) []authorizer.DefaultNonResourceRuleInfo {
|
||||||
|
rules := make([]authorizer.DefaultNonResourceRuleInfo, len(infos))
|
||||||
|
for i, info := range infos {
|
||||||
|
rules[i] = authorizer.DefaultNonResourceRuleInfo{
|
||||||
|
Verbs: info.GetVerbs(),
|
||||||
|
NonResourceURLs: info.GetNonResourceURLs(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return rules
|
||||||
|
}
|
||||||
|
@ -87,6 +87,9 @@ type Config struct {
|
|||||||
// Authorizer determines whether the subject is allowed to make the request based only
|
// Authorizer determines whether the subject is allowed to make the request based only
|
||||||
// on the RequestURI
|
// on the RequestURI
|
||||||
Authorizer authorizer.Authorizer
|
Authorizer authorizer.Authorizer
|
||||||
|
// RuleResolver is required to get the list of rules that apply to a given user
|
||||||
|
// in a given namespace
|
||||||
|
RuleResolver authorizer.RuleResolver
|
||||||
// AdmissionControl performs deep inspection of a given request (including content)
|
// AdmissionControl performs deep inspection of a given request (including content)
|
||||||
// to set values and determine whether its allowed
|
// to set values and determine whether its allowed
|
||||||
AdmissionControl admission.Interface
|
AdmissionControl admission.Interface
|
||||||
|
@ -28,6 +28,7 @@ import (
|
|||||||
"k8s.io/apimachinery/pkg/apimachinery/registered"
|
"k8s.io/apimachinery/pkg/apimachinery/registered"
|
||||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||||
"k8s.io/apimachinery/pkg/util/cache"
|
"k8s.io/apimachinery/pkg/util/cache"
|
||||||
|
"k8s.io/apiserver/pkg/authentication/user"
|
||||||
"k8s.io/apiserver/pkg/authorization/authorizer"
|
"k8s.io/apiserver/pkg/authorization/authorizer"
|
||||||
"k8s.io/apiserver/pkg/util/webhook"
|
"k8s.io/apiserver/pkg/util/webhook"
|
||||||
"k8s.io/client-go/kubernetes/scheme"
|
"k8s.io/client-go/kubernetes/scheme"
|
||||||
@ -196,6 +197,16 @@ func (w *WebhookAuthorizer) Authorize(attr authorizer.Attributes) (authorized bo
|
|||||||
return r.Status.Allowed, r.Status.Reason, nil
|
return r.Status.Allowed, r.Status.Reason, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//TODO: need to finish the method to get the rules when using webhook mode
|
||||||
|
func (w *WebhookAuthorizer) RulesFor(user user.Info, namespace string) ([]authorizer.ResourceRuleInfo, []authorizer.NonResourceRuleInfo, bool, error) {
|
||||||
|
var (
|
||||||
|
resourceRules []authorizer.ResourceRuleInfo
|
||||||
|
nonResourceRules []authorizer.NonResourceRuleInfo
|
||||||
|
)
|
||||||
|
incomplete := true
|
||||||
|
return resourceRules, nonResourceRules, incomplete, fmt.Errorf("webhook authorizer does not support user rule resolution")
|
||||||
|
}
|
||||||
|
|
||||||
func convertToSARExtra(extra map[string][]string) map[string]authorization.ExtraValue {
|
func convertToSARExtra(extra map[string][]string) map[string]authorization.ExtraValue {
|
||||||
if extra == nil {
|
if extra == nil {
|
||||||
return nil
|
return nil
|
||||||
|
@ -74,7 +74,7 @@ func TestNodeAuthorizer(t *testing.T) {
|
|||||||
AuthorizationModes: []string{"Node", "RBAC"},
|
AuthorizationModes: []string{"Node", "RBAC"},
|
||||||
InformerFactory: informerFactory,
|
InformerFactory: informerFactory,
|
||||||
}
|
}
|
||||||
nodeRBACAuthorizer, err := authorizerConfig.New()
|
nodeRBACAuthorizer, _, err := authorizerConfig.New()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
@ -394,6 +394,8 @@ var ephemeralWhiteList = createEphemeralWhiteList(
|
|||||||
|
|
||||||
// k8s.io/kubernetes/pkg/apis/authorization/v1beta1
|
// k8s.io/kubernetes/pkg/apis/authorization/v1beta1
|
||||||
|
|
||||||
|
// SRR objects that are not stored in etcd
|
||||||
|
gvr("authorization.k8s.io", "v1beta1", "selfsubjectrulesreviews"),
|
||||||
// SAR objects that are not stored in etcd
|
// SAR objects that are not stored in etcd
|
||||||
gvr("authorization.k8s.io", "v1beta1", "selfsubjectaccessreviews"),
|
gvr("authorization.k8s.io", "v1beta1", "selfsubjectaccessreviews"),
|
||||||
gvr("authorization.k8s.io", "v1beta1", "localsubjectaccessreviews"),
|
gvr("authorization.k8s.io", "v1beta1", "localsubjectaccessreviews"),
|
||||||
@ -402,6 +404,8 @@ var ephemeralWhiteList = createEphemeralWhiteList(
|
|||||||
|
|
||||||
// k8s.io/kubernetes/pkg/apis/authorization/v1
|
// k8s.io/kubernetes/pkg/apis/authorization/v1
|
||||||
|
|
||||||
|
// SRR objects that are not stored in etcd
|
||||||
|
gvr("authorization.k8s.io", "v1", "selfsubjectrulesreviews"),
|
||||||
// SAR objects that are not stored in etcd
|
// SAR objects that are not stored in etcd
|
||||||
gvr("authorization.k8s.io", "v1", "selfsubjectaccessreviews"),
|
gvr("authorization.k8s.io", "v1", "selfsubjectaccessreviews"),
|
||||||
gvr("authorization.k8s.io", "v1", "localsubjectaccessreviews"),
|
gvr("authorization.k8s.io", "v1", "localsubjectaccessreviews"),
|
||||||
|
Loading…
Reference in New Issue
Block a user