Skip to content

Commit 4e9bf29

Browse files
authored
Gatekeeper policy generation (#12)
* Fix reference yaml to be valid Signed-off-by: Dale Haiducek <[email protected]> * Update contributing guide Signed-off-by: Dale Haiducek <[email protected]> * Add support for informing on Gatekeeper policy violations Signed-off-by: Dale Haiducek <[email protected]> * Document the expanders and code structure Signed-off-by: Dale Haiducek <[email protected]> * Update with Gatekeeper examples Signed-off-by: Dale Haiducek <[email protected]> * Add Gatekeeper expander tests Signed-off-by: Dale Haiducek <[email protected]>
1 parent 8b797b9 commit 4e9bf29

File tree

17 files changed

+463
-59
lines changed

17 files changed

+463
-59
lines changed

CONTRIBUTING.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,10 +62,12 @@ comment on the issue or pull request (PR).
6262

6363
## Pre-check before submitting a PR
6464

65-
Before submitting a PR, please perform the following steps:
65+
Before submitting a PR, perform the following steps:
6666

6767
```shell
6868
make fmt
6969
make lint
7070
make test
7171
```
72+
73+
If there are any updates to the YAML, also make sure to update the [policy-generator.yaml](./docs/policy-generator.yaml) file.

docs/policygenerator-reference.yaml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,8 @@ policies:
9595
# a namespace is specified in the manifest, the selector is not necessary. This defaults to no
9696
# selectors.
9797
namespaceSelector:
98-
include: [] exclude: []
98+
include: []
99+
exclude: []
99100
# Optional. (See policyDefaults.placement for description.)
100101
placement: {}
101102
# Optional. (See policyDefaults.remediationAction for description.)

docs/policygenerator.md

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,3 +22,50 @@ PlacementBinding, either specify `placement.placementRulePath` to an existing Pl
2222
set `placement.name` along with `placement.clusterSelectors`. When the PlacementBinding is
2323
consolidated in this way, `placementBindingDefaults.name` must be specified so that the generator
2424
can create unique names for the bindings.
25+
26+
## Policy expanders
27+
28+
Policy expanders provide logic to create additional policies based on a given kind to give a
29+
complete picture of violations or status using Open Cluster Management policies. Generally the
30+
expanders point to kinds provided by policy engines such as [Kyverno](https://kyverno.io/) and
31+
[Gatekeeper](https://open-policy-agent.github.io/gatekeeper/). These expanders are enabled by
32+
default but can be disabled individually by setting the flag associated with the expander in the
33+
`PolicyGenerator` manifest either in `policyDefaults` or for a particular manifest in the `policies`
34+
array.
35+
36+
### Contributing a policy expander
37+
38+
To contribute a policy expander, you'll need to:
39+
40+
1. Add your expander to the `getExpanders()` method in
41+
[expanders.go](../internal/expanders/expanders.go) and familiarize yourself with the Interface
42+
there that you'll be implementing.
43+
2. Add your expander file to the [internal/expanders/](../internal/expanders/) directory. You can
44+
follow the other files there as an example.
45+
3. Choose a name for your boolean expander setting. (Existing names have followed the pattern
46+
`Inform<engine-name>Policies`.)
47+
4. Add your expander setting to both the `PolicyDefaults` and the `PolicyConfig` array in
48+
[types.go](../types/types.go)
49+
5. Add your expander setting to the `applyDefaults()` method in [plugin.go](../internal/plugin.go)
50+
to set a defaults for both `PolicyDefaults` and `Policies`.
51+
6. Update the [policygenerator-reference.yaml](./policygenerator-reference.yaml) with your expander
52+
setting.
53+
7. Add tests for your expander to the [internal/expanders/](../internal/expanders/) directory.
54+
55+
## PolicyGenerator code structure
56+
57+
```
58+
DIRECTORY TREE PACKAGE DESCRIPTION
59+
================================================================================================
60+
.
61+
├── cmd
62+
│   └── main.go main Parent binary (imports the internal package)
63+
└── internal
64+
├── expanders
65+
│   ├── expanders.go expanders Policy expander interface
66+
├── types
67+
│   └── types.go types Generator structs
68+
├── patches.go internal Code to patch input manifests
69+
├── plugin.go internal Primary generator methods
70+
├── utils.go internal Helper/utility methods
71+
```
File renamed without changes.
File renamed without changes.
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
# Taken from https://open-policy-agent.github.io/gatekeeper/website/docs/howto
2+
---
3+
apiVersion: templates.gatekeeper.sh/v1beta1
4+
kind: ConstraintTemplate
5+
metadata:
6+
name: k8srequiredlabels
7+
spec:
8+
crd:
9+
spec:
10+
names:
11+
kind: K8sRequiredLabels
12+
validation:
13+
# Schema for the `parameters` field
14+
openAPIV3Schema:
15+
properties:
16+
labels:
17+
type: array
18+
items: string
19+
targets:
20+
- target: admission.k8s.gatekeeper.sh
21+
rego: |
22+
package k8srequiredlabels
23+
24+
violation[{"msg": msg, "details": {"missing_labels": missing}}] {
25+
provided := {label | input.review.object.metadata.labels[label]}
26+
required := {label | label := input.parameters.labels[_]}
27+
missing := required - provided
28+
count(missing) > 0
29+
msg := sprintf("you must provide labels: %v", [missing])
30+
}
31+
---
32+
apiVersion: constraints.gatekeeper.sh/v1beta1
33+
kind: K8sRequiredLabels
34+
metadata:
35+
name: ns-must-have-gk
36+
spec:
37+
match:
38+
kinds:
39+
- apiGroups: [""]
40+
kinds: ["Namespace"]
41+
parameters:
42+
labels: ["gatekeeper"]
File renamed without changes.

examples/policyGenerator.yaml

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ policies:
3434
- name: policy-app-config-others
3535
disabled: true
3636
manifests:
37-
- path: input2/
37+
- path: input-folder/
3838
patches:
3939
- apiVersion: v1
4040
kind: ConfigMap
@@ -46,4 +46,7 @@ policies:
4646
- name: policy-require-labels
4747
disabled: true
4848
manifests:
49-
- path: input3/
49+
- path: input-kyverno/
50+
- name: policy-require-ns-labels
51+
manifests:
52+
- path: input-gatekeeper/

internal/expanders/expanders.go

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,21 @@ import (
55
"github.com/open-cluster-management/policy-generator-plugin/internal/types"
66
)
77

8+
// GetExpanders returns the list of available expanders.
89
func GetExpanders() map[string]Expander {
910
return map[string]Expander{
10-
"kyverno": KyvernoPolicyExpander{},
11+
"gatekeeper": GatekeeperPolicyExpander{},
12+
"kyverno": KyvernoPolicyExpander{},
1113
}
1214
}
1315

16+
// Expander is the interface for all policy expander instances.
1417
type Expander interface {
18+
// CanHandle determines if the manifest is a policy that can be expanded.
1519
CanHandle(manifest map[string]interface{}) bool
20+
// Enabled determines if the policy configuration allows a policy to be expanded.
1621
Enabled(policyConf *types.PolicyConfig) bool
22+
// Expand will generate additional policy templates for the policy for auditing purposes.
1723
Expand(manifest map[string]interface{}, severity string) []map[string]map[string]interface{}
1824
}
1925

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package expanders
2+
3+
import (
4+
"reflect"
5+
"testing"
6+
)
7+
8+
func assertEqual(t *testing.T, a interface{}, b interface{}) {
9+
t.Helper()
10+
if a != b {
11+
t.Fatalf("%s != %s", a, b)
12+
}
13+
}
14+
15+
func assertReflectEqual(t *testing.T, a interface{}, b interface{}) {
16+
t.Helper()
17+
if !reflect.DeepEqual(a, b) {
18+
t.Fatalf("%s != %s", a, b)
19+
}
20+
}

0 commit comments

Comments
 (0)