Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
128 changes: 128 additions & 0 deletions contributors/design-proposals/rules-review-api.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
# "What can I do?" API

Author: Eric Chiang (eric.chiang@coreos.com)

## Overview

Currently, to determine if a user is authorized to perform a set of actions, that user has to query each action individually through a `SelfSubjectAccessReview`.

Beyond making the authorization layer hard to reason about, it means web interfaces such as the OpenShift Web Console, Tectonic Console, and Kubernetes Dashboard, have to perform individual calls for _every resource_ a page displays. There's no way for a user, or an application acting on behalf of a user, to ask for all the permissions a user can make in bulk. This makes its hard to build pages that are proactive about what's displayed or grayed out based on the end user's permissions. UIs can only handle 403 responses after a user has already performed a forbidden action.

This is a proposal to add authorization APIs that allow a client to determine what actions they can make within a namespace. We expect this API to be used by UIs to show/hide actions, or to quickly let an end user reason about their permissions. This API should NOT be used by external systems to drive their own authorization decisions, as this raises confused deputy, cache lifetime/revocation, and correctness concerns. The `*AccessReview` APIs remain the correct way to defer authorization decisions to the API server.

OpenShift adopted a [`RulesReview` API][openshift-rules-review] to accomplish this same goal, and this proposal is largely a port of that implementation.

[kubernetes/kubernetes#48051](https://github.com/kubernetes/kubernetes/pull/48051) implements most of this proposal.

## API additions

Add a top level type to the `authorization.k8s.io` API group called `SelfSubjectRulesReview`. This mirrors the existing `SelfSubjectAccessReview`.

```
type SelfSubjectRulesReview struct {
metav1.TypeMeta

Spec SelfSubjectRulesReviewSpec

// Status is filled in by the server and represents the set of actions a user can perform.
Status SubjectRulesReviewStatus
}

type SelfSubjectRulesReviewSpec struct {
// Namespace to evaluate rules for. Required.
Namespace string
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Having this all be dependent on the user knowing which Namespaces they have some form of access to might be quite inconvenient for a UI.
e.g. the kubernetes/dashboard currently (1) queries for the available namespaces and then (2) allows viewing the resources in each of those namespaces. If I understand the proposal correctly this would support the (2) part of this flow, but no way to support for the (1) part of this flow.
Is there room for a non namespaced version of this Spec, or a "Which namespaces have I more than zero access to" api? This would be a helpful part of this API for the dashboard

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@rf232 for some background querying resources across all namespaces was deemed too expensive.

I know the actual namespace object is a bit special. I think users can "GET" the namespace object resource by name if they have any access in it already (couldn't find the code path that allows this). CoreOS has internally built apps that do something like:

  • privileged service account lists all namespace
  • looks up if a user can get each specific namespace
  • builds list to display user from that

Agree that probably doesn't work at scale, but a "what namespaces can I see" might be a different API. I think openshift just implemented namespace filtering, so there's no prior art there.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there room for a non namespaced version of this Spec

Yes, but it wouldn't do what you describe, it would tell you the things you could do across all namespaces (e.g. kubectl get pods --all-namespaces)

or a "Which namespaces have I more than zero access to" api? This would be a helpful part of this API for the dashboard

Agree it is useful (we built something close to that API in openshift), but it's a different API than this one and requires more capabilities from the authorization interface to implement performantly

I think users can "GET" the namespace object resource by name if they have any access in it already

Not quite, the admin/edit/view roles simply grant "get" "namespace", so when those roles are bound in a particular namespace, the ability to get that namespace is granted.

Copy link
Contributor

@rf232 rf232 Aug 17, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • privileged service account lists all namespace
  • looks up if a user can get each specific namespace
  • builds list to display user from that

This is a possibility, but recently at the dashboard we were looking at reducing the privileges the dashboard itself needs to lower the risk of privilege escalations with the dashboard.

}

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
// 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
// Incomplete indicates that the returned list is known to be incomplete.
Incomplete bool
}
```

The `ResourceRules` and `NonResourceRules` rules are similar to the types use by RBAC and the internal authorization system.

```
# docstrings omitted for brevity.
type ResourceRule struct {
Verbs []string
APIGroups []string
Resources []string
ResourceNames []string
}

type NonResourceRule struct {
Verbs []string
NonResourceURLs []string
}
```

All of these fields can include the string `*` to indicate all values are allowed.

### Differences from OpenShift: user extras vs. scopes
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@deads2k tired to articulate my concerns about making user extras configurable. ptal


OpenShift `SelfSubjectRulesReviewSpec` takes a set of [`Scopes`][openshift-scopes]. This lets OpenShift clients use the API for queries such as _"what could I do if I provide this scope to limit my credentials?"_

In core kube, scopes are replaced by "user extras" field, a map of opaque strings that can be used for implementation specific user data. Unlike OpenShift, where scopes are always used to restrict credential powers, user extras are commonly used to expand powers. For example, the proposed [Keystone authentiator][keystone-authn] used them to include additional roles and project fields.

Since user extras can be used to expand the power of users, instead of only restricting, this proposal argues that `SelfSubjectRulesReview` shouldn't let a client specify them like `Scopes`. It wouldn't be within the spirit of a `SelfSubject` resource to let a user determine information about other projects or roles.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unless there's an authorizer that actually conditionally allows access to particularly named resources based one their actual, I don't think this allows you to go fishing in other namespaces. I don't care enough to try to stop the good bits of the API, but is there any other way?

Copy link
Contributor Author

@ericchiang ericchiang Aug 9, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unless there's an authorizer that actually conditionally allows access to particularly named resources based one their actual

From what I can see @dims' keystone authorizer does that https://github.com/dims/k8s-keystone-auth

@cjcullen any other insight into how/if GKE uses user extra?

Maybe we could re-word user-extra to try to emphasis its usage as part of this change?

Extra fields should NOT be used to grant access to new namespaces. Only to modify the
access of a user within a namespace.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extra fields should NOT be used to grant access to new namespaces. Only to modify the
access of a user within a namespace.

Granting access to other namespaces does not leak information, since if I lie about my extra and see "yes you have access to namespace X", I'd get that result regardless of whether the namespace exists.

Copy link
Contributor Author

@ericchiang ericchiang Aug 10, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Granting access to other namespaces does not leak information, since if I lie about my extra and see "yes you have access to namespace X", I'd get that result regardless of whether the namespace exists.

You're still leaking information about what users have been given access to what though, which could correlate to what exists. I could find out that project team-foo doesn't have any powers in namespace secret-project-bar but does have powers in namespace secret-project-spam.

It's probably not the biggest concern, but it feels weird to allow a user to control any fields that extend their access.

Can we let clients provide something like an additive "Impersonate-Extra-*" header that would only add extras to the authenticated user info? That way an admin could declare what extras are okay to freely add to a user:

apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRole
metadata:
  # Any user bound to this role can add a "scopes" user extra field to their user info
  # through an HTTP header.
  name: scopes-adder
- apiGroups: ["authentication.k8s.io"]
  resources: ["userextras/scopes"]
  verbs: ["impersonate:additive"] # this is a bad name for the verb

This would let them use both SelfSubjectRulesReview with whatever extras an admin deemed okay, and would extend those capabilities to other commands.


This could hopefully be solved by introducing a `SubjectRulesReview` API to query the rules for any user. An aggregated API server could use the `SubjectRulesReview` to back an API resource that let a user provide restrictive user extras, such as scopes.

## Webhook authorizers
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@cjcullen @dims for proposed webhook integration.


Some authorizers live external to Kubernetes through an API server webhook and wouldn't immediately support a rules review query.

To communicate with external authorizers, the following types will be defined to query the rules for an arbitrary user. This proposal does NOT propose adding these types to the API immediately, since clients can use user impersonation and a `SelfSubjectRulesReview` to accomplish something similar.

```
type SubjectRulesReview struct {
metav1.TypeMeta

Spec SubjectRulesReviewSpec

// Status is filled in by the server and indicates the set of actions a user can perform.
Status SubjectRulesReviewStatus
}

type SubjectRulesReviewSpec struct {
// Namespace to evalue rules for. Required.
Namespace string

// User to be evaluated for.
UID string
User string
Groups []string
Extras map[string][]string
}
```

Currently, external authorizers are configured through the following API server flag and which POSTs a `SubjectAccessReview` to determine a user's access:

```
--authorization-webhook-config-file
```

The config file uses the kubeconfig format.

There are a few options to support a second kind of query.

* Add another webhook flag with a second config file.
* Introduce a [kubeconfig extension][kubeconfig-extension] that indicates the server can handle either a `SubjectRulesReview` or a `SubjectAccessReview`
* Introduce a second context in the kubeconfig for the `SubjectRulesReview`. Have some way of indicating which context for `SubjectRulesReview` and which is for `SubjectAccessReview`, for example by well-known context names for each.

The doc proposed adding a second webhook config for `RulesReview`, and not overloading the existing config passed to `--authorization-webhook-config-file`.

[openshift-rules-review]: https://github.com/openshift/origin/blob/v3.6.0/pkg/authorization/apis/authorization/types.go#L152
[openshift-scopes]: https://github.com/openshift/origin/blob/v3.6.0/pkg/authorization/apis/authorization/types.go#L164-L168
[keystone-authn]: https://github.com/kubernetes/kubernetes/pull/25624/files#diff-897f0cab87e784d9fc6813f04f128f62R40
[kubeconfig-extension]: https://github.com/kubernetes/client-go/blob/v3.0.0/tools/clientcmd/api/v1/types.go#L51