Skip to content

Commit 365b700

Browse files
committed
Updated documentation
Signed-off-by: Micah Hausler <[email protected]>
1 parent f305d11 commit 365b700

File tree

7 files changed

+124
-31
lines changed

7 files changed

+124
-31
lines changed

docs/CedarIntroduction.md

+9-3
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ forbid (
6464

6565
How does Cedar know if a principal is in a group?
6666
When evaluating a request, Cedar has several inputs.
67-
(You can play with some examples in the [Cedar Playground](https://www.cedarpolicy.com/en/playground)):
67+
(You can play with some examples for Kubernetes in the [Cedar Playground](https://www.cedarpolicy.com/en/playground)):
6868

6969
* A JSON list of entity structures to be considered
7070
* A principal identifier, which must be in the entities list
@@ -114,12 +114,12 @@ If an entity is a member of another entity type, the Entities list must say so.
114114
```
115115

116116
Given the above input, the entity with the id `507B11AD-4DE0-44B1-AB7C-99C0C04854B1` and the name `alice` has a `parents` reference to the Group type named `viewers`, so in this case Alice is a member of the group `viewers`.
117-
Cedar also supports membership on resource entities and verbs, but we don't use them in this project.
117+
Cedar also supports membership on resource entities and verbs, which we'll get to later.
118118

119119
## Schema
120120

121121
When writing policy how do you know what is a valid attribute of a type so you can write policy against it?
122-
Cedar policy supports a [schema] defining all valid entities (principals and resources), their attributes, actions, and which actions apply to which entities (principal and resources).
122+
Cedar policy supports a [schema] defining all valid entities (principals and resources), their attributes, actions, which actions apply to which entities (principal and resources), and what the context structure for a given action is.
123123
You can see the Cedar schema used for Kubernetes authorization this project in [human][authz_human_schema] and [json][authz_json_schema] format.
124124

125125
[schema]: https://docs.cedarpolicy.com/schema/schema.html
@@ -150,6 +150,12 @@ cedar validate -s ./cedarschema/k8s-full.cedarschema --schema-format cedar -p al
150150
[cedar_go]: https://pkg.go.dev/github.com/cedar-policy/cedar-go
151151
[cedar_cli]: https://crates.io/crates/cedar-policy-cli
152152

153+
The Makefile includes a target to validate all `.cedar` poilcy files in this repostiory.
154+
155+
```bash
156+
make validate
157+
```
158+
153159
To regenerate all schema files in both JSON and cedar formats (`k8s-full.cedarschema` and `k8s-full.cedarschema.json`), run:
154160

155161
```sh

docs/CedarSchemas.md

+86-18
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ The referenced schemas are primarily created to document the entity shapes and a
1212

1313
## Authorizer cedarschema
1414

15-
For the full authorization schema, see [cedarschema/k8s-authorization.cedarschema](../cedarschema/k8s-authorization.cedarschema).
15+
For the authorization schema, see [cedarschema/k8s-authorization.cedarschema](../cedarschema/k8s-authorization.cedarschema).
1616

1717
### Principals
1818

@@ -30,10 +30,10 @@ This project supports the following Principal entities:
3030
"extra"?: Set < ExtraAttribute >,
3131
"name": __cedar::String
3232
};
33-
type Extra = {
34-
"key": __cedar::String,
35-
"values"?: Set < __cedar::String >
36-
};
33+
type Extra = {
34+
"key": __cedar::String,
35+
"values"?: Set < __cedar::String >
36+
};
3737
```
3838
* `k8s::ServiceAccount`. When a user's name in a [SubjectAccessReview] starts with `system:serviceaccount:`, the authorizer sets the principal type to `k8s::ServiceAccount` with the following attributes.
3939
```cedarschema
@@ -70,7 +70,7 @@ permit (
7070
};
7171
```
7272

73-
We do have a resource group for all readOnly actions. It encompasses `get`/`list`/`watch`, and is called `readOnly`, and only applies to `k8s::Resource` resources.
73+
We do have an action group for all read-only actions. It encompasses `get`/`list`/`watch`, and is called `readOnly`, and only applies to `k8s::Resource` resources.
7474

7575
```cedar
7676
permit (
@@ -85,15 +85,19 @@ permit (
8585

8686
### Resources
8787

88-
> Note: `"resource"`, `"k8s::Resource"`, and `"resource.resource`, why the redundancy?!
88+
> **`"resource"`, `"k8s::Resource"`, and `"resource.resource`, why the redundancy?!**
8989
>
9090
> This is an unfortunate naming collision. Cedar policies always have a `resource` as part of the policy.
9191
> We call Kubernetes typed objects `k8s::Resource` as opposed to the `k8s::NonResourceURL` type, because that's what Kubernetes calls them.
9292
> And finally, Kubernetes authorization checks refer to they type of object as a `resource`, along with the object's `apiGroup`, `namespace`, `name`, etc.
9393
9494
We define two primary resource types for this authorizer:
9595

96-
* `NonResourceURL`: This is for non-resource requests made to the Kubernetes API server. Examples include `/healthz`, `/livez`, `/metrics`, and subpaths. (Hint: run `kubectl get --raw /` to see others) Paths can match a `*` on the suffix.
96+
* `NonResourceURL`: This is for non-resource requests made to the Kubernetes API server.
97+
Examples include `/healthz`, `/livez`, `/metrics`, and subpaths
98+
(Hint: run `kubectl get --raw /` to see others).
99+
A request's path is also used as the identifier in the entity list when evaluated for authorization.
100+
Paths can match a `*` on the suffix.
97101
```cedarschema
98102
entity NonResourceURL = {
99103
"path": __cedar::String
@@ -117,7 +121,8 @@ We define two primary resource types for this authorizer:
117121
resource == k8s::NonResourceURL::"/version"
118122
);
119123
```
120-
* `Resource`: This is for resource requests made to the Kubernetes API server. Entity IDs on resources are the constructed URL path being made for the request.
124+
* `Resource`: This is for resource requests made to the Kubernetes API server.
125+
Entity IDs on resources are the constructed URL path being made for the request.
121126
```cedarschema
122127
entity Resource = {
123128
"apiGroup": __cedar::String,
@@ -166,7 +171,7 @@ We define two primary resource types for this authorizer:
166171
};
167172
```
168173
169-
`Resource` has a `fieldSelector` and `labelSelector` types. These were added in Kubernetes 1.31 behind the [`AuthorizeWithSelectors`][AuthorizeWithSelectors] feature gate so authorizers can enforce that a watch or list request has a field or label selector:
174+
`Resource` has a `fieldSelector` and `labelSelector` types. These were [added in Kubernetes 1.31][AuthorizeWithSelectors] behind the `AuthorizeWithSelectors` feature gate so authorizers can enforce that a watch or list request has a field or label selector:
170175
171176
[AuthorizeWithSelectors]: https://kubernetes.io/docs/reference/command-line-tools-reference/feature-gates/
172177
@@ -184,10 +189,10 @@ type LabelRequirement = {
184189
};
185190
```
186191

187-
This can be used to enforce attribute-based access policies, such as a user can only get/list/watch resources where the label `owner` equals the user's name
192+
Selectors can be used to enforce attribute-based access policies, such as enforcing that a user can only get/list/watch resources where the label `owner` equals the user's name
188193
```cedar
189194
permit (
190-
principal is User,
195+
principal is k8s::User,
191196
action in [k8s::Action::"list", k8s::Action::"watch"],
192197
resource is k8s::Resource
193198
) when {
@@ -220,7 +225,7 @@ other-example-secret Opaque 1 2d20h owner=prod-user
220225

221226
#### Impersonated resources
222227

223-
To make an impersonated request as another user, Kubernetes sends multiple authorization requests to an authorizer: one for each attribute being impersonated: The user's name, the UID (if set), the groups (if set), and the userInfo extra key/value map (entity tags are [not yet supported in cedar-go](https://github.com/cedar-policy/cedar-go/issues/47)). To support this, we define a few types:
228+
To make an impersonated request as another user, Kubernetes sends multiple authorization requests to an authorizer: one for each attribute being impersonated: The user's name, the UID (if set), the groups (if set), and the userInfo extra key/value map. To support this, we define a few types:
224229

225230
* `Group`. This structure is the same from the principal type. This only functions if the user can also impersonate the requested username.:
226231
```cedar
@@ -255,7 +260,10 @@ To make an impersonated request as another user, Kubernetes sends multiple autho
255260
```
256261
* `Extra`: To allow impersonating a principal's key/values extra info, the policy's resource type must be `Extra`. This only functions if the user can also impersonate the requested username.
257262
```cedarschema
258-
entity PrincipalUID;
263+
entity Extra = {
264+
"key": __cedar::String,
265+
"values"?: Set < __cedar::String >
266+
};
259267
```
260268
Examples:
261269
```cedar
@@ -344,10 +352,10 @@ forbid (
344352
);
345353
```
346354

347-
All admission actions currently apply to any Kubernetes type that have a [`metav1.ObjectMeta`][objmeta] or [`corev1.ListMeta`][listmeta].
355+
Most admission actions currently apply to any Kubernetes type that have a [`metav1.ObjectMeta`][objmeta].
348356

349357
[objmeta]: https://pkg.go.dev/k8s.io/apimachinery/pkg/apis/meta/v1#ObjectMeta
350-
[listmeta]: https://pkg.go.dev/k8s.io/apimachinery/pkg/apis/meta/v1#ListMeta
358+
351359

352360
### Resources
353361

@@ -358,7 +366,8 @@ The resource entity structure matches that of the Kubernetes API structure, with
358366
// Forbid pods with hostNetwork in namespaces other than kube-system
359367
forbid (
360368
principal,
361-
action in [k8s::admission::Action::"create", k8s::admission::Action::"update"],
369+
action in [
370+
k8s::admission::Action::"create", k8s::admission::Action::"update"],
362371
resource is core::v1::Pod
363372
) when {
364373
resource has spec &&
@@ -371,7 +380,7 @@ forbid (
371380
};
372381
```
373382

374-
Until [cedar-go supports entity maps][go-entity-maps], we've manually added `KeyValue` and `KeyValues` types into the `meta::v1` namespace to support key/value labels.
383+
Until [cedar-go supports entity maps][go-entity-maps], we've manually added `KeyValue` and `KeyValueStringSlice` types into the `meta::v1` namespace to support key/value labels.
375384
Any Kubernetes types that consist of `map[string]string{}` or `map[string][]string{}` are converted to a Set of KeyValue or KeyValueStringSlice.
376385
```cedarschema
377386
namespace meta::v1 {
@@ -397,3 +406,62 @@ namespace meta::v1 {
397406
```
398407

399408
[go-entity-maps]: https://github.com/cedar-policy/cedar-go/issues/47
409+
410+
The Kubernetes `CONNECT` admission action only applies to a small set of structures that don't appear in the Kubernetes OpenAPI Schema, so we inject them manually:
411+
```cedarschema
412+
namespace core::v1 {
413+
// other types and entities
414+
entity NodeProxyOptions = {
415+
"apiVersion": __cedar::String,
416+
"kind": __cedar::String,
417+
"path": __cedar::String
418+
};
419+
entity PodAttachOptions = {
420+
"apiVersion": __cedar::String,
421+
"command": Set < __cedar::String >,
422+
"container": __cedar::String,
423+
"kind": __cedar::String,
424+
"stderr": __cedar::Bool,
425+
"stdin": __cedar::Bool,
426+
"stdout": __cedar::Bool,
427+
"tty": __cedar::Bool
428+
};
429+
entity PodExecOptions = {
430+
"apiVersion": __cedar::String,
431+
"command": Set < __cedar::String >,
432+
"container": __cedar::String,
433+
"kind": __cedar::String,
434+
"stderr": __cedar::Bool,
435+
"stdin": __cedar::Bool,
436+
"stdout": __cedar::Bool,
437+
"tty": __cedar::Bool
438+
};
439+
entity PodPortForwardOptions = {
440+
"apiVersion": __cedar::String,
441+
"kind": __cedar::String,
442+
"ports"?: Set < __cedar::String >
443+
};
444+
entity PodProxyOptions = {
445+
"apiVersion": __cedar::String,
446+
"kind": __cedar::String,
447+
"path": __cedar::String
448+
};
449+
entity ServiceProxyOptions = {
450+
"apiVersion": __cedar::String,
451+
"kind": __cedar::String,
452+
"path": __cedar::String
453+
};
454+
}
455+
```
456+
457+
Policy can be used to forbid proxying to those types:
458+
```cedar
459+
// deny policy on exec unless command is `whoami`
460+
forbid (
461+
principal,
462+
action == k8s::admission::Action::"connect",
463+
resource is core::v1::PodExecOptions
464+
) unless {
465+
resource.command = ["whoami"]
466+
};
467+
```

docs/ConvertRBAC.md

+2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
# Convert RBAC policies
22

33
There's an RBAC converter that works on ClusterRoleBindings or RoleBindings.
4+
This converter is intended to help you transition any RBAC policies you want to add conditions to into Cedar.
5+
Any existing RBAC policies that aren't denied by a Cedar policy will still work.
46

57
If not done already, clone this repository to your local environment or IDE.
68

docs/Demo.md

+21-5
Original file line numberDiff line numberDiff line change
@@ -25,13 +25,19 @@ We have a couple of policies already written on the [`demo/authorization-policy.
2525
@description("test-user can get/list/watch pods")
2626
permit (
2727
principal is k8s::User,
28-
action in [k8s::Action::"get", k8s::Action::"list", k8s::Action::"watch"],
28+
action in [
29+
k8s::Action::"get",
30+
k8s::Action::"list",
31+
k8s::Action::"watch"],
2932
resource is k8s::Resource
3033
) when {
3134
principal.name == "test-user" &&
3235
// "" is the core API group in Kubernetes
3336
resource.apiGroup == "" &&
3437
resource.resource == "pods"
38+
} unless {
39+
// we don't want to allow a GET on a portforward subresource
40+
resource has subresource
3541
};
3642
3743
@description("forbid test-user to get/list/watch nodes")
@@ -93,7 +99,10 @@ Given that the test-user is in the group `viewers`, lets leverage that group by
9399
// viewer group members can get/list/watch any Resource other than secrets in the default namespace
94100
permit (
95101
principal in k8s::Group::"viewers",
96-
action in [ k8s::Action::"get", k8s::Action::"list", k8s::Action::"watch"],
102+
action in [
103+
k8s::Action::"get",
104+
k8s::Action::"list",
105+
k8s::Action::"watch"],
97106
resource is k8s::Resource
98107
) unless {
99108
resource.resource == "secrets" &&
@@ -274,7 +283,9 @@ permit (
274283
// Admission policy preventing test-user from creating/updating configmaps with name starting with "prod"
275284
forbid (
276285
principal is k8s::User,
277-
action in [k8s::admission::Action::"create", k8s::admission::Action::"update"],
286+
action in [
287+
k8s::admission::Action::"create",
288+
k8s::admission::Action::"update"],
278289
resource is core::v1::ConfigMap
279290
) when {
280291
principal.name == "test-user" &&
@@ -339,7 +350,9 @@ permit (
339350
// without label selector owner={principal.name}
340351
forbid (
341352
principal is k8s::User in k8s::Group::"requires-labels",
342-
action in [k8s::Action::"list", k8s::Action::"watch"],
353+
action in [
354+
k8s::Action::"list",
355+
k8s::Action::"watch"],
343356
resource is k8s::Resource
344357
) unless {
345358
resource has labelSelector &&
@@ -353,7 +366,10 @@ forbid (
353366
// admission policy to forbid resource creation without an owner key
354367
forbid (
355368
principal is k8s::User in k8s::Group::"requires-labels",
356-
action in [k8s::admission::Action::"create", k8s::admission::Action::"update", k8s::admission::Action::"delete"],
369+
action in [
370+
k8s::admission::Action::"create",
371+
k8s::admission::Action::"update",
372+
k8s::admission::Action::"delete"],
357373
resource
358374
) unless {
359375
resource has metadata &&

docs/Limitations.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ RBAC would require the following policy to permit a port-forward request.
2727
apiVersion: rbac.authorization.k8s.io/v1
2828
kind: ClusterRole
2929
metadata:
30-
name: deployment-manager
30+
name: service-port-forwarder
3131
rules:
3232
- apiGroups:
3333
- ""
@@ -109,7 +109,7 @@ namespace k8s {
109109
## Expressiveness limitations
110110

111111
A core tenet of Cedar is to be analyzable, meaning that the language can verify that a policy is valid and will not error.
112-
A general `map`/`filter` function on dynamic inputs [is not analyzible][rfc21], and not a candidate for the project.
112+
A general `map`/`filter` function on dynamic inputs and ordered lists [are not analyzible][rfc21], and not a candidate for Cedar.
113113
This prevents specifically checking subfields over sets of structures, which is a common Kubernetes policy management requirement.
114114
Cedar is powered by [automated reasoning], including an [SMT solver], which does not implement loops or map functions.
115115
Rather than viewing Cedar as a replacement for admission restrictions tools like [Open Policy Agent/Gatekeeper][gatekeeper] or [Kyverno][kyverno], it is best seen as an additional tool for access control enforcement.

internal/server/authorizer/entitiy_builders.go

-3
Original file line numberDiff line numberDiff line change
@@ -149,8 +149,5 @@ func ResourceToCedarEntity(attributes authorizer.Attributes) cedartypes.Entity {
149149
ID: cedartypes.String(entities.ResourceRequestToPath(attributes)),
150150
},
151151
Attributes: cedartypes.NewRecord(respAttributes),
152-
// TODO: Parent of Namespace Entity for namespaced resources?
153-
// maybe the best argument for a namespaced resource
154-
// or everything has a namespace parent of "all"?
155152
}
156153
}

manifests/cedar-authorization-webhook.yaml

+4
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,12 @@ spec:
3737
periodSeconds: 1
3838
timeoutSeconds: 15
3939
resources:
40+
limits:
41+
cpu: 500m
42+
memory: 256Mi
4043
requests:
4144
cpu: 250m
45+
memory: 128Mi
4246
startupProbe:
4347
failureThreshold: 24
4448
httpGet:

0 commit comments

Comments
 (0)