Skip to content
Merged
Show file tree
Hide file tree
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
9 changes: 8 additions & 1 deletion docs/policygenerator-reference.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,10 @@ policyDefaults:
# Optional. Labels that the policy will include under its metadata.labels. It will be applied for all
# policies unless specified in the policy.
policyLabels: {}

# Optional. Overrides the spec.enforcementAction field of a Gatekeeper constraint.
# This only applies to Gatekeeper constraints and is ignored by other manifests.
# If not set, the spec.enforcementAction field is not changed.
gatekeeperEnforcementAction: "deny"
# Optional. Defaults for policy set generation. Any default value listed here can be overridden under an entry in the
# policySets array.
policySetDefaults:
Expand Down Expand Up @@ -260,6 +263,8 @@ policies:
# Optional. (See policyDefaults.severity for description.)
# Cannot be specified when policyDefaults.consolidateManifests is set to true.
severity: "low"
# Optional. (See policyDefaults.gatekeeperEnforcementAction for description.)
gatekeeperEnforcementAction: "warn"
# (Note: a path to a directory containing a Kustomize manifest is a supported alternative.) Optional. A
# Kustomize patch to apply to the manifest(s) at the path. If there are multiple manifests, the patch requires
# the apiVersion, kind, metadata.name, and metadata.namespace (if applicable) fields to be set so Kustomize can
Expand Down Expand Up @@ -351,6 +356,8 @@ policies:
policyAnnotations: {}
# Optional. (See policyDefaults.policyLabels for description.)
policyLabels: {}
# Optional. (See policyDefaults.gatekeeperEnforcementAction for description.)
gatekeeperEnforcementAction: "dryrun"

# Optional. The list of policy sets to create. To include a policy in a policy set, use policies[*].policySets or
# policyDefaults.policySets or policySets.policies.
Expand Down
8 changes: 8 additions & 0 deletions internal/plugin.go
Original file line number Diff line number Diff line change
Expand Up @@ -579,6 +579,10 @@ func (p *Plugin) applyDefaults(unmarshaledConfig map[string]interface{}) {
policy.MetadataComplianceType = p.PolicyDefaults.MetadataComplianceType
}

if policy.GatekeeperEnforcementAction == "" {
policy.GatekeeperEnforcementAction = p.PolicyDefaults.GatekeeperEnforcementAction
}

// Only use the policyDefault evaluationInterval value when it's not explicitly set on the policy.
if policy.EvaluationInterval.Compliant == "" {
set := isEvaluationIntervalSet(unmarshaledConfig, i, "compliant")
Expand Down Expand Up @@ -745,6 +749,10 @@ func (p *Plugin) applyDefaults(unmarshaledConfig map[string]interface{}) {
manifest.RecordDiff = policy.RecordDiff
}

if manifest.GatekeeperEnforcementAction == "" {
manifest.GatekeeperEnforcementAction = policy.GatekeeperEnforcementAction
}

if isManifestFieldSet(unmarshaledConfig, i, j, "extraDependencies") {
applyDefaultDependencyFields(manifest.ExtraDependencies, p.PolicyDefaults.Namespace)
} else {
Expand Down
143 changes: 143 additions & 0 deletions internal/plugin_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,7 @@ metadata:
policyDefaults:
namespace: my-policies
consolidateManifests: false
gatekeeperEnforcementAction: deny
%s: %s
policies:
- name: policy-app
Expand All @@ -281,6 +282,7 @@ policies:
t.Fatal("Unexpected error", err)
}

assertEqual(t, p.PolicyDefaults.GatekeeperEnforcementAction, "deny")
assertEqual(t, p.Policies[0].ConsolidateManifests, false)

output, err := p.Generate()
Expand Down Expand Up @@ -1760,6 +1762,147 @@ spec:
assertEqual(t, output, expected)
}

func TestOverrideConstraintEnforcementAction(t *testing.T) {
t.Parallel()
tmpDir := t.TempDir()
gatekeeperPath := path.Join(tmpDir, "gatekeeper.yaml")
yamlContent := `
apiVersion: constraints.gatekeeper.sh/v1
kind: MyConstrainingTemplate
metadata:
name: thisthingimconstraining
`

common := `
---
apiVersion: policy.open-cluster-management.io/v1
kind: Policy
metadata:
annotations:
policy.open-cluster-management.io/categories: CM Configuration Management
policy.open-cluster-management.io/controls: CM-2 Baseline Configuration
policy.open-cluster-management.io/description: ""
policy.open-cluster-management.io/standards: NIST SP 800-53
name: policy-gatekeeper
namespace: gatekeeper-policies
spec:
disabled: false
policy-templates:
- objectDefinition:
apiVersion: constraints.gatekeeper.sh/v1
kind: MyConstrainingTemplate
metadata:
annotations:
policy.open-cluster-management.io/severity: low
name: thisthingimconstraining
spec:
enforcementAction: `

err := os.WriteFile(gatekeeperPath, []byte(yamlContent), 0o666)
if err != nil {
t.Fatalf("Failed to write %s", gatekeeperPath)
}

tests := []struct {
policyConf types.PolicyConfig
policyDefaultEA string
expectedEA string
}{
{
policyConf: types.PolicyConfig{
Name: "policy-gatekeeper",
Manifests: []types.Manifest{
{
Path: gatekeeperPath,
GatekeeperOptions: types.GatekeeperOptions{GatekeeperEnforcementAction: "deny"},
},
},
},
policyDefaultEA: "",
expectedEA: "deny",
},
{
policyConf: types.PolicyConfig{
Name: "policy-gatekeeper",
Manifests: []types.Manifest{
{
Path: gatekeeperPath,
GatekeeperOptions: types.GatekeeperOptions{GatekeeperEnforcementAction: "warn"},
},
},
},
policyDefaultEA: "",
expectedEA: "warn",
},
{
policyConf: types.PolicyConfig{
Name: "policy-gatekeeper",
Manifests: []types.Manifest{
{
Path: gatekeeperPath,
},
},
},
policyDefaultEA: "deny",
expectedEA: "deny",
},
{
policyConf: types.PolicyConfig{
Name: "policy-gatekeeper",
Manifests: []types.Manifest{
{
Path: gatekeeperPath,
GatekeeperOptions: types.GatekeeperOptions{GatekeeperEnforcementAction: "dryrun"},
},
},
},
policyDefaultEA: "deny",
expectedEA: "dryrun",
},
{
policyConf: types.PolicyConfig{
Name: "policy-gatekeeper",
GatekeeperOptions: types.GatekeeperOptions{GatekeeperEnforcementAction: "dryrun"},
Manifests: []types.Manifest{
{
Path: gatekeeperPath,
},
},
},
policyDefaultEA: "deny",
expectedEA: "dryrun",
},
}

for _, tc := range tests {
p := Plugin{}

p.PolicyDefaults.Namespace = "gatekeeper-policies"
p.PolicyDefaults.InformGatekeeperPolicies = false

p.PolicyDefaults.GatekeeperEnforcementAction = tc.policyDefaultEA

p.Policies = append(p.Policies, tc.policyConf)

p.applyDefaults(map[string]interface{}{
"policyDefaults": map[string]interface{}{
"informGatekeeperPolicies": false,
},
})

err = p.createPolicy(&p.Policies[0])
if err != nil {
t.Fatal(err.Error())
}

output := p.outputBuffer.String()

expected := strings.TrimPrefix(common+tc.expectedEA+"\n", "\n")

assertEqual(t, output, expected)
}
}

func TestCreatePolicyWithDifferentRemediationAction(t *testing.T) {
t.Parallel()
tmpDir := t.TempDir()
Expand Down
7 changes: 7 additions & 0 deletions internal/types/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,13 @@ type ConfigurationPolicyOptions struct {
RecreateOption string `json:"recreateOption,omitempty" yaml:"recreateOption,omitempty"`
}

type GatekeeperOptions struct {
GatekeeperEnforcementAction string `json:"gatekeeperEnforcementAction,omitempty" yaml:"gatekeeperEnforcementAction,omitempty"`
}

type Manifest struct {
ConfigurationPolicyOptions `json:",inline" yaml:",inline"`
GatekeeperOptions `json:",inline" yaml:",inline"`
Patches []map[string]interface{} `json:"patches,omitempty" yaml:"patches,omitempty"`
Path string `json:"path,omitempty" yaml:"path,omitempty"`
ExtraDependencies []PolicyDependency `json:"extraDependencies,omitempty" yaml:"extraDependencies,omitempty"`
Expand Down Expand Up @@ -105,6 +110,7 @@ type EvaluationInterval struct {
type PolicyConfig struct {
PolicyOptions `json:",inline" yaml:",inline"`
ConfigurationPolicyOptions `json:",inline" yaml:",inline"`
GatekeeperOptions `json:",inline" yaml:",inline"`
Name string `json:"name,omitempty" yaml:"name,omitempty"`
// This a slice of structs to allow additional configuration related to a manifest such as
// accepting patches.
Expand All @@ -114,6 +120,7 @@ type PolicyConfig struct {
type PolicyDefaults struct {
PolicyOptions `json:",inline" yaml:",inline"`
ConfigurationPolicyOptions `json:",inline" yaml:",inline"`
GatekeeperOptions `json:",inline" yaml:",inline"`
Namespace string `json:"namespace,omitempty" yaml:"namespace,omitempty"`
OrderPolicies bool `json:"orderPolicies,omitempty" yaml:"orderPolicies,omitempty"`
}
Expand Down
25 changes: 25 additions & 0 deletions internal/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,13 @@ func getPolicyTemplates(policyConf *types.PolicyConfig) ([]map[string]interface{
extraDeps := policyConf.Manifests[i].ExtraDependencies

for _, manifest := range manifestGroup {
err := setGatekeeperEnforcementAction(manifest,
policyConf.Manifests[i].GatekeeperEnforcementAction)
if err != nil {
return nil, fmt.Errorf("err in setting constraint.spec.enforcementAction has "+
"an error %w in manifest index: %v", err, i)
}

isPolicyTypeManifest, isOcmPolicy, err := isPolicyTypeManifest(
manifest, policyConf.InformGatekeeperPolicies)
if err != nil {
Expand Down Expand Up @@ -310,6 +317,24 @@ func getPolicyTemplates(policyConf *types.PolicyConfig) ([]map[string]interface{
return policyTemplates, nil
}

// setGatekeeperEnforcementAction function override gatekeeper.constraint.enforcementAction
func setGatekeeperEnforcementAction(manifest map[string]interface{}, enforcementAction string) error {
if enforcementAction == "" {
return nil
}

apiVersion, _, _ := unstructured.NestedString(manifest, "apiVersion")

if strings.HasPrefix(apiVersion, "constraints.gatekeeper.sh") {
err := unstructured.SetNestedField(manifest, enforcementAction, "spec", "enforcementAction")
if err != nil {
return err
}
}

return nil
}

func setTemplateOptions(tmpl map[string]interface{}, ignorePending bool, extraDeps []types.PolicyDependency) {
if ignorePending {
tmpl["ignorePending"] = ignorePending
Expand Down