Esempio n. 1
0
// OriginAuthorizerAttributes adapts Kubernetes authorization attributes to Origin authorization attributes
// Note that some info (like resourceName, apiVersion, apiGroup) is not available from the Kubernetes attributes
func OriginAuthorizerAttributes(kattrs kauthorizer.Attributes) (kapi.Context, oauthorizer.AuthorizationAttributes) {
	// Build a context to hold the namespace and user info
	ctx := kapi.NewContext()
	ctx = kapi.WithNamespace(ctx, kattrs.GetNamespace())
	ctx = kapi.WithUser(ctx, &user.DefaultInfo{
		Name:   kattrs.GetUserName(),
		Groups: kattrs.GetGroups(),
	})

	// If we recognize the type, use the embedded type.  Do NOT use it directly, because not all things that quack are ducks.
	if castAdapterAttributes, ok := kattrs.(AdapterAttributes); ok {
		return ctx, castAdapterAttributes.authorizationAttributes
	}

	// Otherwise build what we can
	oattrs := &oauthorizer.DefaultAuthorizationAttributes{
		Verb:         kattrs.GetVerb(),
		APIGroup:     kattrs.GetAPIGroup(),
		APIVersion:   kattrs.GetAPIVersion(),
		Resource:     kattrs.GetResource(),
		ResourceName: kattrs.GetName(),

		NonResourceURL: kattrs.IsResourceRequest() == false,
		URL:            kattrs.GetPath(),

		// TODO: add to kube authorizer attributes
		// RequestAttributes interface{}
	}
	if len(kattrs.GetSubresource()) > 0 {
		oattrs.Resource = kattrs.GetResource() + "/" + kattrs.GetSubresource()
	}

	return ctx, oattrs
}
Esempio n. 2
0
func (r *RBACAuthorizer) Authorize(attr authorizer.Attributes) (bool, string, error) {
	if r.superUser != "" && attr.GetUser() != nil && attr.GetUser().GetName() == r.superUser {
		return true, "", nil
	}

	ctx := api.WithNamespace(api.WithUser(api.NewContext(), attr.GetUser()), attr.GetNamespace())

	// Frame the authorization request as a privilege escalation check.
	var requestedRule rbac.PolicyRule
	if attr.IsResourceRequest() {
		requestedRule = rbac.PolicyRule{
			Verbs:         []string{attr.GetVerb()},
			APIGroups:     []string{attr.GetAPIGroup()}, // TODO(ericchiang): add api version here too?
			Resources:     []string{attr.GetResource()},
			ResourceNames: []string{attr.GetName()},
		}
	} else {
		requestedRule = rbac.PolicyRule{
			Verbs:           []string{attr.GetVerb()},
			NonResourceURLs: []string{attr.GetPath()},
		}
	}

	// TODO(nhlfr): Try to find more lightweight way to check attributes than escalation checks.
	err := validation.ConfirmNoEscalation(ctx, r.authorizationRuleResolver, []rbac.PolicyRule{requestedRule})
	if err != nil {
		return false, err.Error(), nil
	}

	return true, "", nil
}
Esempio n. 3
0
func (r *RBACAuthorizer) Authorize(attr authorizer.Attributes) error {
	if r.superUser != "" && attr.GetUserName() == r.superUser {
		return nil
	}

	userInfo := &user.DefaultInfo{
		Name:   attr.GetUserName(),
		Groups: attr.GetGroups(),
	}

	ctx := api.WithNamespace(api.WithUser(api.NewContext(), userInfo), attr.GetNamespace())

	// Frame the authorization request as a privilege escalation check.
	var requestedRule rbac.PolicyRule
	if attr.IsResourceRequest() {
		requestedRule = rbac.PolicyRule{
			Verbs:         []string{attr.GetVerb()},
			APIGroups:     []string{attr.GetAPIGroup()}, // TODO(ericchiang): add api version here too?
			Resources:     []string{attr.GetResource()},
			ResourceNames: []string{attr.GetName()},
		}
	} else {
		requestedRule = rbac.PolicyRule{
			NonResourceURLs: []string{attr.GetPath()},
		}
	}

	return validation.ConfirmNoEscalation(ctx, r.authorizationRuleResolver, []rbac.PolicyRule{requestedRule})
}
Esempio n. 4
0
func (ka *allowTestAuthorizer) Authorize(a authorizer.Attributes) (string, error) {
	var (
		tenantName string
		ns         *api.Namespace
		err        error
	)
	if authorizer.IsWhiteListedUser(a.GetUserName()) {
		return "", nil
	} else {
		if !a.IsReadOnly() && a.GetResource() == "tenants" {
			return "", errors.New("only admin can write tenant")
		}
	}
	if a.GetNamespace() != "" {
		ns, err = ka.kubeClient.Namespaces().Get(a.GetNamespace())
		if err != nil {
			glog.Error(err)
			return "", err
		}
		tenantName = ns.Tenant
	} else {
		if a.GetTenant() != "" {
			te, err := ka.kubeClient.Tenants().Get(a.GetTenant())
			if err != nil {
				glog.Error(err)
				return "", err
			}
			tenantName = te.Name
		}
	}
	if tenantName == "" || tenantName == TenantTest {
		return TenantTest, nil
	}
	return "", errors.New("Keystone authorization failed")
}
Esempio n. 5
0
// OriginAuthorizerAttributes adapts Kubernetes authorization attributes to Origin authorization attributes
// Note that some info (like resourceName, apiVersion, apiGroup) is not available from the Kubernetes attributes
func OriginAuthorizerAttributes(kattrs kauthorizer.Attributes) (kapi.Context, oauthorizer.AuthorizationAttributes) {
	// Build a context to hold the namespace and user info
	ctx := kapi.NewContext()
	ctx = kapi.WithNamespace(ctx, kattrs.GetNamespace())
	ctx = kapi.WithUser(ctx, &user.DefaultInfo{
		Name:   kattrs.GetUserName(),
		Groups: kattrs.GetGroups(),
	})

	// If the passed attributes already satisfy our interface, use it directly
	if oattrs, ok := kattrs.(oauthorizer.AuthorizationAttributes); ok {
		return ctx, oattrs
	}

	// Otherwise build what we can
	oattrs := &oauthorizer.DefaultAuthorizationAttributes{
		Verb:     kattrs.GetVerb(),
		Resource: kattrs.GetResource(),

		// TODO: add to kube authorizer attributes
		// APIVersion        string
		// APIGroup          string
		// ResourceName      string
		// RequestAttributes interface{}
		// NonResourceURL    bool
		// URL               string
	}
	return ctx, oattrs
}
Esempio n. 6
0
// Authorize makes a REST request to the remote service describing the attempted action as a JSON
// serialized api.authorization.v1beta1.SubjectAccessReview object. An example request body is
// provided bellow.
//
//     {
//       "apiVersion": "authorization.k8s.io/v1beta1",
//       "kind": "SubjectAccessReview",
//       "spec": {
//         "resourceAttributes": {
//           "namespace": "kittensandponies",
//           "verb": "GET",
//           "group": "group3",
//           "resource": "pods"
//         },
//         "user": "******",
//         "group": [
//           "group1",
//           "group2"
//         ]
//       }
//     }
//
// The remote service is expected to fill the SubjectAccessReviewStatus field to either allow or
// disallow access. A permissive response would return:
//
//     {
//       "apiVersion": "authorization.k8s.io/v1beta1",
//       "kind": "SubjectAccessReview",
//       "status": {
//         "allowed": true
//       }
//     }
//
// To disallow access, the remote service would return:
//
//     {
//       "apiVersion": "authorization.k8s.io/v1beta1",
//       "kind": "SubjectAccessReview",
//       "status": {
//         "allowed": false,
//         "reason": "user does not have read access to the namespace"
//       }
//     }
//
func (w *WebhookAuthorizer) Authorize(attr authorizer.Attributes) (err error) {
	r := &v1beta1.SubjectAccessReview{
		Spec: v1beta1.SubjectAccessReviewSpec{
			User:   attr.GetUserName(),
			Groups: attr.GetGroups(),
		},
	}
	if attr.IsResourceRequest() {
		r.Spec.ResourceAttributes = &v1beta1.ResourceAttributes{
			Namespace:   attr.GetNamespace(),
			Verb:        attr.GetVerb(),
			Group:       attr.GetAPIGroup(),
			Version:     attr.GetAPIVersion(),
			Resource:    attr.GetResource(),
			Subresource: attr.GetSubresource(),
			Name:        attr.GetName(),
		}
	} else {
		r.Spec.NonResourceAttributes = &v1beta1.NonResourceAttributes{
			Path: attr.GetPath(),
			Verb: attr.GetVerb(),
		}
	}
	key, err := json.Marshal(r.Spec)
	if err != nil {
		return err
	}
	if entry, ok := w.responseCache.Get(string(key)); ok {
		r.Status = entry.(v1beta1.SubjectAccessReviewStatus)
	} else {
		result := w.WithExponentialBackoff(func() restclient.Result {
			return w.RestClient.Post().Body(r).Do()
		})
		if err := result.Error(); err != nil {
			// An error here indicates bad configuration or an outage. Log for debugging.
			glog.Errorf("Failed to make webhook authorizer request: %v", err)
			return err
		}
		var statusCode int
		if result.StatusCode(&statusCode); statusCode < 200 || statusCode >= 300 {
			return fmt.Errorf("Error contacting webhook: %d", statusCode)
		}
		if err := result.Into(r); err != nil {
			return err
		}
		if r.Status.Allowed {
			w.responseCache.Add(string(key), r.Status, w.authorizedTTL)
		} else {
			w.responseCache.Add(string(key), r.Status, w.unauthorizedTTL)
		}
	}
	if r.Status.Allowed {
		return nil
	}
	if r.Status.Reason != "" {
		return errors.New(r.Status.Reason)
	}
	return errors.New("unauthorized")
}
Esempio n. 7
0
// Authorize makes a REST request to the remote service describing the attempted action as a JSON
// serialized api.authorization.v1beta1.SubjectAccessReview object. An example request body is
// provided bellow.
//
//     {
//       "apiVersion": "authorization.k8s.io/v1beta1",
//       "kind": "SubjectAccessReview",
//       "spec": {
//         "resourceAttributes": {
//           "namespace": "kittensandponies",
//           "verb": "GET",
//           "group": "group3",
//           "resource": "pods"
//         },
//         "user": "******",
//         "group": [
//           "group1",
//           "group2"
//         ]
//       }
//     }
//
// The remote service is expected to fill the SubjectAccessReviewStatus field to either allow or
// disallow access. A permissive response would return:
//
//     {
//       "apiVersion": "authorization.k8s.io/v1beta1",
//       "kind": "SubjectAccessReview",
//       "status": {
//         "allowed": true
//       }
//     }
//
// To disallow access, the remote service would return:
//
//     {
//       "apiVersion": "authorization.k8s.io/v1beta1",
//       "kind": "SubjectAccessReview",
//       "status": {
//         "allowed": false,
//         "reason": "user does not have read access to the namespace"
//       }
//     }
//
func (w *WebhookAuthorizer) Authorize(attr authorizer.Attributes) (authorized bool, reason string, err error) {
	r := &authorization.SubjectAccessReview{}
	if user := attr.GetUser(); user != nil {
		r.Spec = authorization.SubjectAccessReviewSpec{
			User:   user.GetName(),
			Groups: user.GetGroups(),
			Extra:  convertToSARExtra(user.GetExtra()),
		}
	}

	if attr.IsResourceRequest() {
		r.Spec.ResourceAttributes = &authorization.ResourceAttributes{
			Namespace:   attr.GetNamespace(),
			Verb:        attr.GetVerb(),
			Group:       attr.GetAPIGroup(),
			Version:     attr.GetAPIVersion(),
			Resource:    attr.GetResource(),
			Subresource: attr.GetSubresource(),
			Name:        attr.GetName(),
		}
	} else {
		r.Spec.NonResourceAttributes = &authorization.NonResourceAttributes{
			Path: attr.GetPath(),
			Verb: attr.GetVerb(),
		}
	}
	key, err := json.Marshal(r.Spec)
	if err != nil {
		return false, "", err
	}
	if entry, ok := w.responseCache.Get(string(key)); ok {
		r.Status = entry.(authorization.SubjectAccessReviewStatus)
	} else {
		var (
			result *authorization.SubjectAccessReview
			err    error
		)
		webhook.WithExponentialBackoff(w.initialBackoff, func() error {
			result, err = w.subjectAccessReview.Create(r)
			return err
		})
		if err != nil {
			// An error here indicates bad configuration or an outage. Log for debugging.
			glog.Errorf("Failed to make webhook authorizer request: %v", err)
			return false, "", err
		}
		r.Status = result.Status
		if r.Status.Allowed {
			w.responseCache.Add(string(key), r.Status, w.authorizedTTL)
		} else {
			w.responseCache.Add(string(key), r.Status, w.unauthorizedTTL)
		}
	}
	return r.Status.Allowed, r.Status.Reason, nil
}
Esempio n. 8
0
// Authorizer implements authorizer.Authorize
func (ka *keystoneAuthorizer) Authorize(a authorizer.Attributes) (string, error) {

	var (
		tenantName string
		ns         *api.Namespace
		err        error
	)
	if a.GetNamespace() != "" {
		ns, err = ka.kubeClient.Namespaces().Get(a.GetNamespace())
		if err != nil {
			return "", err
		}
		tenantName = ns.Tenant
	} else {
		if a.GetTenant() != "" {
			te, err := ka.kubeClient.Tenants().Get(a.GetTenant())
			if err != nil {
				return "", err
			}
			tenantName = te.Name
		}
	}
	if authorizer.IsWhiteListedUser(a.GetUserName()) {
		if a.GetUserName() != api.UserAdmin {
			return tenantName, nil
		} else {
			return api.TenantDefault, nil
		}
	} else {
		if !a.IsReadOnly() && a.GetResource() == "tenants" {
			return "", errors.New("only admin can write tenant")
		}
	}

	authConfig := &authConfig{
		AuthUrl:  ka.authUrl,
		Username: a.GetUserName(),
		Password: a.GetPassword(),
	}
	osClient, err := newOpenstackClient(authConfig)
	if err != nil {
		glog.Errorf("%v", err)
		return "", err
	}

	tenant, err := osClient.getTenant()
	if err != nil {
		glog.Errorf("%v", err)
		return "", err
	}
	if tenantName == "" || tenantName == tenant.Name {
		return tenant.Name, nil
	}
	return "", errors.New("Keystone authorization failed")
}
Esempio n. 9
0
func (r *RBACAuthorizer) Authorize(requestAttributes authorizer.Attributes) (bool, string, error) {
	rules, ruleResolutionError := r.authorizationRuleResolver.RulesFor(requestAttributes.GetUser(), requestAttributes.GetNamespace())
	if RulesAllow(requestAttributes, rules...) {
		return true, "", nil
	}

	glog.V(2).Infof("RBAC DENY: user %q groups %v cannot %q on \"%v.%v/%v\"", requestAttributes.GetUser().GetName(), requestAttributes.GetUser().GetGroups(),
		requestAttributes.GetVerb(), requestAttributes.GetResource(), requestAttributes.GetAPIGroup(), requestAttributes.GetSubresource())

	return false, "", ruleResolutionError
}
Esempio n. 10
0
// Authorize makes a REST request to the remote service describing the attempted action as a JSON
// serialized api.authorization.v1beta1.SubjectAccessReview object. An example request body is
// provided bellow.
//
//     {
//       "apiVersion": "authorization.k8s.io/v1beta1",
//       "kind": "SubjectAccessReview",
//       "spec": {
//         "resourceAttributes": {
//           "namespace": "kittensandponies",
//           "verb": "GET",
//           "group": "group3",
//           "resource": "pods"
//         },
//         "user": "******",
//         "group": [
//           "group1",
//           "group2"
//         ]
//       }
//     }
//
// The remote service is expected to fill the SubjectAccessReviewStatus field to either allow or
// disallow access. A permissive response would return:
//
//     {
//       "apiVersion": "authorization.k8s.io/v1beta1",
//       "kind": "SubjectAccessReview",
//       "status": {
//         "allowed": true
//       }
//     }
//
// To disallow access, the remote service would return:
//
//     {
//       "apiVersion": "authorization.k8s.io/v1beta1",
//       "kind": "SubjectAccessReview",
//       "status": {
//         "allowed": false,
//         "reason": "user does not have read access to the namespace"
//       }
//     }
//
func (w *WebhookAuthorizer) Authorize(attr authorizer.Attributes) (err error) {
	r := &v1beta1.SubjectAccessReview{
		Spec: v1beta1.SubjectAccessReviewSpec{
			User:   attr.GetUserName(),
			Groups: attr.GetGroups(),
		},
	}
	if attr.IsResourceRequest() {
		r.Spec.ResourceAttributes = &v1beta1.ResourceAttributes{
			Namespace:   attr.GetNamespace(),
			Verb:        attr.GetVerb(),
			Group:       attr.GetAPIGroup(),
			Version:     attr.GetAPIVersion(),
			Resource:    attr.GetResource(),
			Subresource: attr.GetSubresource(),
			Name:        attr.GetName(),
		}
	} else {
		r.Spec.NonResourceAttributes = &v1beta1.NonResourceAttributes{
			Path: attr.GetPath(),
			Verb: attr.GetVerb(),
		}
	}
	key, err := json.Marshal(r.Spec)
	if err != nil {
		return err
	}
	if entry, ok := w.responseCache.Get(string(key)); ok {
		r.Status = entry.(v1beta1.SubjectAccessReviewStatus)
	} else {
		result := w.RestClient.Post().Body(r).Do()
		if err := result.Error(); err != nil {
			return err
		}
		if err := result.Into(r); err != nil {
			return err
		}
		go func() {
			if r.Status.Allowed {
				w.responseCache.Add(string(key), r.Status, w.authorizedTTL)
			} else {
				w.responseCache.Add(string(key), r.Status, w.unauthorizedTTL)
			}
		}()
	}
	if r.Status.Allowed {
		return nil
	}
	if r.Status.Reason != "" {
		return errors.New(r.Status.Reason)
	}
	return errors.New("unauthorized")
}
Esempio n. 11
0
func (p policy) matches(a authorizer.Attributes) bool {
	if p.subjectMatches(a) {
		if p.Readonly == false || (p.Readonly == a.IsReadOnly()) {
			if p.Resource == "" || (p.Resource == a.GetResource()) {
				if p.Namespace == "" || (p.Namespace == a.GetNamespace()) {
					return true
				}
			}
		}
	}
	return false
}
Esempio n. 12
0
File: abac.go Progetto: 40a/bootkube
func resourceMatches(p api.Policy, a authorizer.Attributes) bool {
	// A resource policy cannot match a non-resource request
	if a.IsResourceRequest() {
		if p.Spec.Namespace == "*" || p.Spec.Namespace == a.GetNamespace() {
			if p.Spec.Resource == "*" || p.Spec.Resource == a.GetResource() {
				if p.Spec.APIGroup == "*" || p.Spec.APIGroup == a.GetAPIGroup() {
					return true
				}
			}
		}
	}
	return false
}
Esempio n. 13
0
func RuleAllows(requestAttributes authorizer.Attributes, rule rbac.PolicyRule) bool {
	if requestAttributes.IsResourceRequest() {
		resource := requestAttributes.GetResource()
		if len(requestAttributes.GetSubresource()) > 0 {
			resource = requestAttributes.GetResource() + "/" + requestAttributes.GetSubresource()
		}

		return rbac.VerbMatches(rule, requestAttributes.GetVerb()) &&
			rbac.APIGroupMatches(rule, requestAttributes.GetAPIGroup()) &&
			rbac.ResourceMatches(rule, resource) &&
			rbac.ResourceNameMatches(rule, requestAttributes.GetName())
	}

	return rbac.VerbMatches(rule, requestAttributes.GetVerb()) &&
		rbac.NonResourceURLMatches(rule, requestAttributes.GetPath())
}
Esempio n. 14
0
func forbiddenMessage(attributes authorizer.Attributes) string {
	username := ""
	if user := attributes.GetUser(); user != nil {
		username = user.GetName()
	}

	resource := attributes.GetResource()
	if group := attributes.GetAPIGroup(); len(group) > 0 {
		resource = resource + "." + group
	}

	if ns := attributes.GetNamespace(); len(ns) > 0 {
		return fmt.Sprintf("User %q cannot %s %s in the namespace %q.", username, attributes.GetVerb(), resource, ns)
	}

	return fmt.Sprintf("User %q cannot %s %s at the cluster scope.", username, attributes.GetVerb(), resource)
}
Esempio n. 15
0
func (impersonateAuthorizer) Authorize(a authorizer.Attributes) (authorized bool, reason string, err error) {
	user := a.GetUser()

	switch {
	case user.GetName() == "system:admin":
		return true, "", nil

	case user.GetName() == "tester":
		return false, "", fmt.Errorf("works on my machine")

	case user.GetName() == "deny-me":
		return false, "denied", nil
	}

	if len(user.GetGroups()) > 0 && user.GetGroups()[0] == "wheel" && a.GetVerb() == "impersonate" && a.GetResource() == "users" {
		return true, "", nil
	}

	if len(user.GetGroups()) > 0 && user.GetGroups()[0] == "sa-impersonater" && a.GetVerb() == "impersonate" && a.GetResource() == "serviceaccounts" {
		return true, "", nil
	}

	if len(user.GetGroups()) > 0 && user.GetGroups()[0] == "regular-impersonater" && a.GetVerb() == "impersonate" && a.GetResource() == "users" {
		return true, "", nil
	}

	if len(user.GetGroups()) > 1 && user.GetGroups()[1] == "group-impersonater" && a.GetVerb() == "impersonate" && a.GetResource() == "groups" {
		return true, "", nil
	}

	if len(user.GetGroups()) > 1 && user.GetGroups()[1] == "extra-setter-scopes" && a.GetVerb() == "impersonate" && a.GetResource() == "userextras" && a.GetSubresource() == "scopes" {
		return true, "", nil
	}

	if len(user.GetGroups()) > 1 && user.GetGroups()[1] == "extra-setter-particular-scopes" &&
		a.GetVerb() == "impersonate" && a.GetResource() == "userextras" && a.GetSubresource() == "scopes" && a.GetName() == "scope-a" {
		return true, "", nil
	}

	if len(user.GetGroups()) > 1 && user.GetGroups()[1] == "extra-setter-project" && a.GetVerb() == "impersonate" && a.GetResource() == "userextras" && a.GetSubresource() == "project" {
		return true, "", nil
	}

	return false, "deny by default", nil
}
func (p policy) matches(a authorizer.Attributes) bool {
	if p.subjectMatches(a) {
		if p.Readonly == false || (p.Readonly == a.IsReadOnly()) {
			switch {
			case p.NonResourcePath != "":
				if p.NonResourcePath == a.GetNonResourcePath() {
					return true
				}
			// When the path is a non-resource path it cannot match.
			case len(a.GetNonResourcePath()) == 0 && (p.Resource == "" || (p.Resource == a.GetResource())):
				if p.Namespace == "" || (p.Namespace == a.GetNamespace()) {
					return true
				}
			}
		}
	}

	return false
}
Esempio n. 17
0
// alice can't act as anyone and bob can't do anything but act-as someone
func (impersonateAuthorizer) Authorize(a authorizer.Attributes) error {
	// alice can impersonate service accounts and do other actions
	if a.GetUserName() == "alice" && a.GetVerb() == "impersonate" && a.GetResource() == "serviceaccounts" {
		return nil
	}
	if a.GetUserName() == "alice" && a.GetVerb() != "impersonate" {
		return nil
	}
	// bob can impersonate anyone, but that it
	if a.GetUserName() == "bob" && a.GetVerb() == "impersonate" {
		return nil
	}
	// service accounts can do everything
	if strings.HasPrefix(a.GetUserName(), serviceaccount.ServiceAccountUsernamePrefix) {
		return nil
	}

	return errors.New("I can't allow that.  Go ask alice.")
}
Esempio n. 18
0
func (fakeAuthorizer) Authorize(a authorizer.Attributes) (bool, string, error) {
	username := a.GetUser().GetName()

	if username == "non-deleter" {
		if a.GetVerb() == "delete" {
			return false, "", nil
		}
		return true, "", nil
	}

	if username == "non-pod-deleter" {
		if a.GetVerb() == "delete" && a.GetResource() == "pods" {
			return false, "", nil
		}
		return true, "", nil
	}

	return true, "", nil
}
Esempio n. 19
0
// Authorize makes a REST request to the remote service describing the attempted action as a JSON
// serialized api.authorization.v1beta1.SubjectAccessReview object. An example request body is
// provided bellow.
//
//     {
//       "apiVersion": "authorization.k8s.io/v1beta1",
//       "kind": "SubjectAccessReview",
//       "spec": {
//         "resourceAttributes": {
//           "namespace": "kittensandponies",
//           "verb": "GET",
//           "group": "group3",
//           "resource": "pods"
//         },
//         "user": "******",
//         "group": [
//           "group1",
//           "group2"
//         ]
//       }
//     }
//
// The remote service is expected to fill the SubjectAccessReviewStatus field to either allow or
// disallow access. A permissive response would return:
//
//     {
//       "apiVersion": "authorization.k8s.io/v1beta1",
//       "kind": "SubjectAccessReview",
//       "status": {
//         "allowed": true
//       }
//     }
//
// To disallow access, the remote service would return:
//
//     {
//       "apiVersion": "authorization.k8s.io/v1beta1",
//       "kind": "SubjectAccessReview",
//       "status": {
//         "allowed": false,
//         "reason": "user does not have read access to the namespace"
//       }
//     }
//
func (w *WebhookAuthorizer) Authorize(attr authorizer.Attributes) (err error) {
	r := &v1beta1.SubjectAccessReview{
		Spec: v1beta1.SubjectAccessReviewSpec{
			User:   attr.GetUserName(),
			Groups: attr.GetGroups(),
		},
	}
	if attr.IsResourceRequest() {
		r.Spec.ResourceAttributes = &v1beta1.ResourceAttributes{
			Namespace:   attr.GetNamespace(),
			Verb:        attr.GetVerb(),
			Group:       attr.GetAPIGroup(),
			Version:     attr.GetAPIVersion(),
			Resource:    attr.GetResource(),
			Subresource: attr.GetSubresource(),
			Name:        attr.GetName(),
		}
	} else {
		r.Spec.NonResourceAttributes = &v1beta1.NonResourceAttributes{
			Path: attr.GetPath(),
			Verb: attr.GetVerb(),
		}
	}
	result := w.RestClient.Post().Body(r).Do()
	if err := result.Error(); err != nil {
		return err
	}

	if err := result.Into(r); err != nil {
		return err
	}
	if r.Status.Allowed {
		return nil
	}
	if r.Status.Reason != "" {
		return errors.New(r.Status.Reason)
	}
	return errors.New("unauthorized")
}
Esempio n. 20
0
// alice can't act as anyone and bob can't do anything but act-as someone
func (impersonateAuthorizer) Authorize(a authorizer.Attributes) (bool, string, error) {
	// alice can impersonate service accounts and do other actions
	if a.GetUser() != nil && a.GetUser().GetName() == "alice" && a.GetVerb() == "impersonate" && a.GetResource() == "serviceaccounts" {
		return true, "", nil
	}
	if a.GetUser() != nil && a.GetUser().GetName() == "alice" && a.GetVerb() != "impersonate" {
		return true, "", nil
	}
	// bob can impersonate anyone, but that it
	if a.GetUser() != nil && a.GetUser().GetName() == "bob" && a.GetVerb() == "impersonate" {
		return true, "", nil
	}
	// service accounts can do everything
	if a.GetUser() != nil && strings.HasPrefix(a.GetUser().GetName(), serviceaccount.ServiceAccountUsernamePrefix) {
		return true, "", nil
	}

	return false, "I can't allow that.  Go ask alice.", nil
}