Skip to content

Commit 06eee3a

Browse files
some cleanup
1 parent b3b0801 commit 06eee3a

6 files changed

Lines changed: 168 additions & 68 deletions

File tree

README.md

Lines changed: 70 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,10 @@ policies and publishing them to Plural with
55
[Terraform](https://github.com/pluralsh/terraform-provider-plural).
66

77
The included workbench policy denies Kubernetes deletes in the `kube-system`
8-
namespace. A binding policy automatically attaches it to workbenches whose
9-
names begin with `demo-`.
8+
namespace unless the actor belongs to the `sre` group. It also automatically
9+
approves Kubernetes updates by SREs outside `kube-system`. A binding policy
10+
automatically attaches these guardrails to workbenches whose names begin with
11+
`demo-`.
1012

1113
## Repository layout
1214

@@ -32,19 +34,24 @@ to either the `deny` or `approve` set:
3234
package plrl.wb.admission
3335
3436
deny[{"msg": "a useful reason for the denial"}] if {
35-
input.input.some_field == "some-value"
37+
input.tool_name == "some_tool"
38+
input.tool.some_field == "some-value"
3639
}
3740
```
3841

39-
Plural passes workbench tool arguments under `input.input`. It may also provide
40-
the current user under `input.actor`. Any value added to `deny` blocks the tool
41-
call. Denials must be objects with a `msg` string, which Plural presents as the
42-
reason. Values added to `approve` require an approval before the tool runs.
42+
Plural provides:
4343

44-
The example policy is attached only to the workbench `delete_k8s_resource`
45-
tool. The Terraform binding uses the tool match expression
46-
`^delete_k8s_resource$`. Tool matching determines when a policy runs; the Rego
47-
file receives the tool arguments, not the tool name.
44+
- `input.tool_name`: the name of the tool being evaluated
45+
- `input.tool`: the tool arguments
46+
- `input.actor`: the current user, including a `groups` array when available
47+
48+
Any value added to `deny` blocks the tool call. Denials must contain a `msg`
49+
string. Values added to `approve` automatically approve tools that support
50+
approval and use the decision's `reason` in the audit trail.
51+
52+
The example explicitly checks `delete_k8s_resource` and
53+
`update_k8s_resource`. The Terraform binding includes both exact tool-name
54+
matches so the policy is evaluated for both operations.
4855

4956
### Binding policies
5057

@@ -55,7 +62,7 @@ workbench itself:
5562
package plrl.binding
5663
5764
bind if {
58-
startswith(input.name, "demo-")
65+
startswith(input.workbench.name, "demo-")
5966
}
6067
```
6168

@@ -64,52 +71,59 @@ true result attaches the associated workbench policy; a false result removes
6471
it. The Terraform `plural_binding_policy` resource connects the workbench
6572
policy, binding policy, and tool match expressions.
6673

67-
## Test policies locally
68-
69-
Install the [OPA CLI](https://www.openpolicyagent.org/docs/latest/#running-opa)
70-
and run:
71-
72-
```sh
73-
opa fmt --fail policies
74-
opa test --verbose policies
74+
## Test policies
75+
76+
See [`.github/workflows/test.yaml`](.github/workflows/test.yaml) for the OPA
77+
version, formatting check, and test command used by this repository. Tests live
78+
beside each policy and end in `_test.rego`.
79+
80+
## Deploy as a Plural stack
81+
82+
The configuration in `terraform/main.tf` loads the Rego files and creates the
83+
workbench policy, binding policy, and binding. The recommended deployment is an
84+
`InfrastructureStack`, which gives the Terraform configuration managed state,
85+
plans, approvals, and Plural credentials at runtime.
86+
87+
Create or reuse `GitRepository` and `Cluster` resources, then point an
88+
`InfrastructureStack` at this repository's `terraform` directory:
89+
90+
```yaml
91+
apiVersion: deployments.plural.sh/v1alpha1
92+
kind: GitRepository
93+
metadata:
94+
name: policy-examples
95+
namespace: infra
96+
spec:
97+
url: https://github.com/your-org/policy-examples.git
98+
---
99+
apiVersion: deployments.plural.sh/v1alpha1
100+
kind: InfrastructureStack
101+
metadata:
102+
name: policy-examples
103+
namespace: infra
104+
spec:
105+
name: policy-examples
106+
type: TERRAFORM
107+
approval: true
108+
manageState: true
109+
repositoryRef:
110+
name: policy-examples
111+
namespace: infra
112+
clusterRef:
113+
name: mgmt
114+
namespace: infra
115+
git:
116+
ref: main
117+
folder: terraform
75118
```
76119
77-
Tests live beside each policy and end in `_test.rego`. Add both denied and
78-
allowed cases whenever a policy changes.
79-
80-
## Publish policies with Terraform
81-
82-
The configuration in `terraform/main.tf` uses the `plural_policy` resource and
83-
loads the Rego source directly from `policies/`. Authenticate with environment
84-
variables so credentials do not enter Terraform source or variable files:
85-
86-
```sh
87-
export PLURAL_CONSOLE_URL="https://console.example.com"
88-
export PLURAL_ACCESS_TOKEN="..."
89-
90-
terraform -chdir=terraform init
91-
terraform -chdir=terraform fmt -check
92-
terraform -chdir=terraform plan
93-
terraform -chdir=terraform apply
94-
```
95-
96-
The access token needs permission to manage policies in the selected project.
97-
For local use, the provider can alternatively read credentials from `plural cd
98-
login` by setting `PLURAL_USE_CLI=true`.
120+
Replace the repository URL and `clusterRef` with resources from your management
121+
cluster. The stack runner supplies `PLURAL_CONSOLE_URL` and
122+
`PLURAL_ACCESS_TOKEN`; do not commit them to Terraform variables or manifests.
99123

100124
The example looks up the Plural project named `default`. Change the
101-
`plural_project` data source if policies belong to another project. Terraform
102-
creates both policies and reconciles the workbench attachments hourly. Before
103-
using this repository with a team, configure a remote Terraform backend so
104-
state is shared and protected.
105-
106-
## CI
107-
108-
`.github/workflows/test.yaml` runs on every pull request and on pushes to
109-
`main`. It checks Rego formatting and runs all OPA tests.
110-
111-
CI does not apply Terraform and therefore needs no Plural credentials. Apply
112-
from your normal infrastructure delivery workflow after review.
125+
`plural_project` data source in `terraform/main.tf` if the policies belong to
126+
another project. The binding is reconciled hourly.
113127

114128
## Add another policy
115129

@@ -119,4 +133,5 @@ from your normal infrastructure delivery workflow after review.
119133
reads the new file.
120134
4. Reuse or add a policy under `policies/binding/`, then connect the two with a
121135
`plural_binding_policy` resource and the appropriate tool regexes.
122-
5. Run the OPA and Terraform checks locally.
136+
5. Add denied, allowed, and approval cases to the policy tests; use the
137+
repository workflow as the source of truth.
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
package plrl.binding
22

33
bind if {
4-
startswith(input.name, "demo-")
4+
startswith(input.workbench.name, "demo-")
55
}

policies/binding/demo_workbenches_test.rego

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,24 @@ package plrl.binding
22

33
test_binds_demo_workbench if {
44
bind with input as {
5-
"name": "demo-production-operations",
5+
"workbench": {
6+
"name": "demo-production-operations",
7+
},
68
}
79
}
810

911
test_does_not_bind_other_workbench if {
1012
not bind with input as {
11-
"name": "production-operations",
13+
"workbench": {
14+
"name": "production-operations",
15+
},
1216
}
1317
}
1418

1519
test_requires_demo_prefix if {
1620
not bind with input as {
17-
"name": "my-demo-workbench",
21+
"workbench": {
22+
"name": "my-demo-workbench",
23+
},
1824
}
1925
}
Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,17 @@
11
package plrl.wb.admission
22

3+
actor_is_sre if {
4+
input.actor.groups[_] == "sre"
5+
}
6+
37
deny[{"msg": "deleting resources in the kube-system namespace is not allowed"}] if {
4-
input.input.namespace == "kube-system"
8+
input.tool_name == "delete_k8s_resource"
9+
input.tool.namespace == "kube-system"
10+
not actor_is_sre
11+
}
12+
13+
approve[{"reason": "SREs may update resources outside the kube-system namespace"}] if {
14+
input.tool_name == "update_k8s_resource"
15+
input.tool.namespace != "kube-system"
16+
actor_is_sre
517
}

policies/workbench/deny_kube_system_deletes_test.rego

Lines changed: 70 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,32 +2,96 @@ package plrl.wb.admission
22

33
test_denies_delete_in_kube_system if {
44
deny[{"msg": "deleting resources in the kube-system namespace is not allowed"}] with input as {
5-
"input": {
5+
"tool_name": "delete_k8s_resource",
6+
"tool": {
67
"cluster": "production",
78
"kind": "Deployment",
89
"name": "coredns",
910
"namespace": "kube-system",
1011
},
12+
"actor": {
13+
"groups": ["developers"],
14+
},
15+
}
16+
}
17+
18+
test_allows_sre_delete_in_kube_system if {
19+
count(deny) == 0 with input as {
20+
"tool_name": "delete_k8s_resource",
21+
"tool": {
22+
"cluster": "production",
23+
"kind": "Deployment",
24+
"name": "coredns",
25+
"namespace": "kube-system",
26+
},
27+
"actor": {
28+
"groups": ["developers", "sre"],
29+
},
1130
}
1231
}
1332

1433
test_allows_delete_outside_kube_system if {
1534
count(deny) == 0 with input as {
16-
"input": {
35+
"tool_name": "delete_k8s_resource",
36+
"tool": {
1737
"cluster": "production",
1838
"kind": "Deployment",
1939
"name": "api",
2040
"namespace": "default",
2141
},
42+
"actor": {
43+
"groups": ["developers"],
44+
},
2245
}
2346
}
2447

25-
test_allows_cluster_scoped_delete if {
48+
test_does_not_deny_other_tools if {
2649
count(deny) == 0 with input as {
27-
"input": {
50+
"tool_name": "get_k8s_resource",
51+
"tool": {
52+
"namespace": "kube-system",
53+
},
54+
"actor": {
55+
"groups": ["developers"],
56+
},
57+
}
58+
}
59+
60+
test_approves_sre_update_outside_kube_system if {
61+
approve[{"reason": "SREs may update resources outside the kube-system namespace"}] with input as {
62+
"tool_name": "update_k8s_resource",
63+
"tool": {
2864
"cluster": "production",
29-
"kind": "Namespace",
30-
"name": "staging",
65+
"kind": "Deployment",
66+
"name": "api",
67+
"namespace": "default",
68+
},
69+
"actor": {
70+
"groups": ["developers", "sre"],
71+
},
72+
}
73+
}
74+
75+
test_does_not_approve_non_sre_update if {
76+
count(approve) == 0 with input as {
77+
"tool_name": "update_k8s_resource",
78+
"tool": {
79+
"namespace": "default",
80+
},
81+
"actor": {
82+
"groups": ["developers"],
83+
},
84+
}
85+
}
86+
87+
test_does_not_approve_sre_update_in_kube_system if {
88+
count(approve) == 0 with input as {
89+
"tool_name": "update_k8s_resource",
90+
"tool": {
91+
"namespace": "kube-system",
92+
},
93+
"actor": {
94+
"groups": ["sre"],
3195
},
3296
}
3397
}

terraform/main.tf

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ data "plural_project" "project" {
1818
resource "plural_policy" "deny_kube_system_deletes" {
1919
name = "deny-kube-system-deletes"
2020
type = "WORKBENCH"
21-
description = "Prevents workbench agents from deleting Kubernetes resources in kube-system."
21+
description = "Guards Kubernetes deletes and automatically approves safe SRE updates."
2222
project_id = data.plural_project.project.id
2323
policy = file("${path.module}/../policies/workbench/deny_kube_system_deletes.rego")
2424
}
@@ -39,7 +39,10 @@ resource "plural_binding_policy" "deny_kube_system_deletes_for_demos" {
3939

4040
matches = {
4141
workbench = {
42-
regexes = ["^delete_k8s_resource$"]
42+
regexes = [
43+
"^delete_k8s_resource$",
44+
"^update_k8s_resource$",
45+
]
4346
}
4447
}
4548
}

0 commit comments

Comments
 (0)