Skip to content

Commit

Permalink
adding scopedenforcementactions
Browse files Browse the repository at this point in the history
Signed-off-by: Jaydip Gabani <[email protected]>
  • Loading branch information
JaydipGabani committed Jun 25, 2024
1 parent 2af6dfa commit ad9bba6
Show file tree
Hide file tree
Showing 60 changed files with 989 additions and 725 deletions.
2 changes: 1 addition & 1 deletion cmd/gator/test/gatortest_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ func Test_formatOutput(t *testing.T) {
constraint:
object:
kind: kind
enforcementaction: ""
enforcementaction: []
violatingObject:
bar: xyz
trace: xyz
Expand Down
6 changes: 4 additions & 2 deletions cmd/gator/test/test.go
Original file line number Diff line number Diff line change
Expand Up @@ -206,8 +206,10 @@ func formatOutput(flagOutput string, results []*test.GatorResult, stats []*instr

func enforceableFailure(results []*test.GatorResult) bool {
for _, result := range results {
if result.EnforcementAction == string(util.Deny) {
return true
for _, action := range result.EnforcementAction {
if action == string(util.Deny) {
return true
}
}
}

Expand Down
2 changes: 2 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ module github.com/open-policy-agent/gatekeeper/v3

go 1.21

replace github.com/open-policy-agent/frameworks/constraint => /mount/d/go/src/github.com/open-policy-agent/frameworks/constraint

require (
cloud.google.com/go/trace v1.10.7
github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.44.0
Expand Down
6 changes: 2 additions & 4 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -287,8 +287,6 @@ github.com/onsi/gomega v1.33.1 h1:dsYjIxxSR755MDmKVsaFQTE22ChNBcuuTWgkUDSubOk=
github.com/onsi/gomega v1.33.1/go.mod h1:U4R44UsT+9eLIaYRB2a5qajjtQYn0hauxvRm16AVYg0=
github.com/open-policy-agent/cert-controller v0.10.1 h1:RXSYoyn8FdCenWecRP//UV5nbVfmstNpj4kHQFkvPK4=
github.com/open-policy-agent/cert-controller v0.10.1/go.mod h1:4uRbBLY5DsPOog+a9pqk3JLxuuhrWsbUedQW65HcLTI=
github.com/open-policy-agent/frameworks/constraint v0.0.0-20240524210416-5368a3b697f2 h1:zalTQAmgeS+PYcEFeDG0/iWaZyNseXdeBYxpOSR0+zE=
github.com/open-policy-agent/frameworks/constraint v0.0.0-20240524210416-5368a3b697f2/go.mod h1:oXEqMRD8wI59XYd1xpkg47RTdLACMPMX7XbKXXhIJZg=
github.com/open-policy-agent/opa v0.64.1 h1:n8IJTYlFWzqiOYx+JiawbErVxiqAyXohovcZxYbskxQ=
github.com/open-policy-agent/opa v0.64.1/go.mod h1:j4VeLorVpKipnkQ2TDjWshEuV3cvP/rHzQhYaraUXZY=
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
Expand Down Expand Up @@ -429,8 +427,8 @@ golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvx
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0=
golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA=
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
Expand Down
22 changes: 13 additions & 9 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ var (
disabledBuiltins = util.NewFlagSet()
enableK8sCel = flag.Bool("experimental-enable-k8s-native-validation", false, "Alpha: enable the validating admission policy driver")
externaldataProviderResponseCacheTTL = flag.Duration("external-data-provider-response-cache-ttl", 3*time.Minute, "TTL for the external data provider response cache. Specify the duration in 'h', 'm', or 's' for hours, minutes, or seconds respectively. Defaults to 3 minutes if unspecified. Setting the TTL to 0 disables the cache.")
deferAdmissionToVAP = flag.Bool("defer-admission-to-vap", false, "When set to false, Gatekeeper webhook can act as a fallback in case K8s' Validating Admission Policy fails. When set to true, Gatekeeper validating webhook will not evaluate a policy for an admission request it expects vap to enforce. May improve resource usage at the cost of race conditions detecting whether VAP enforcement is in effect. This does not impact audit results. Defaults to false.")
deferAdmissionToVAP = flag.Bool("defer-admission-to-vap", false, "When set to false, Gatekeeper webhook can act as a fallback in case K8s' Validating Admission Policy fails. When set to true, Gatekeeper validating webhook will not evaluate a policy for an admission request it expects vap to enforce. May improve resource usage at the cost of race conditions detecting whether VAP enforcement is in effect. This does not impact audit results. Defaults to false.")
)

func init() {
Expand All @@ -134,7 +134,6 @@ func init() {

// +kubebuilder:scaffold:scheme
flag.Var(disabledBuiltins, "disable-opa-builtin", "disable opa built-in function, this flag can be declared more than once.")
flag.Var(&constraint.VapEnforcement, "vap-enforcement", "control VAP resource generation. Allowed values are NONE: do not generate, GATEKEEPER_DEFAULT: do not generate unless label gatekeeper.sh/use-vap: yes is added to policy explicitly, VAP_DEFAULT: generate unless label gatekeeper.sh/use-vap: no is added to policy explicitly.")
}

func main() {
Expand Down Expand Up @@ -416,13 +415,8 @@ func setupControllers(ctx context.Context, mgr ctrl.Manager, sw *watch.Controlle
if *enableK8sCel {
// initialize K8sValidation
var k8scelArgs []k8scel.Arg
if *deferAdmissionToVAP && constraint.VapEnforcement != constraint.VapFlagNone {
switch constraint.VapEnforcement {
case constraint.VapFlagGatekeeperDefault:
k8scelArgs = append(k8scelArgs, k8scel.VAPGenerationDefault(k8scel.VAPDefaultNo))
case constraint.VapFlagVapDefault:
k8scelArgs = append(k8scelArgs, k8scel.VAPGenerationDefault(k8scel.VAPDefaultYes))
}
if *deferAdmissionToVAP {
k8scelArgs = append(k8scelArgs, k8scel.VAPGenerationDefault(*constraint.VapEnforcement))
}
k8sDriver, err := k8scel.New(k8scelArgs...)
if err != nil {
Expand All @@ -439,6 +433,16 @@ func setupControllers(ctx context.Context, mgr ctrl.Manager, sw *watch.Controlle
}
cfArgs = append(cfArgs, constraintclient.Driver(driver))

eps := []string{}
if operations.IsAssigned(operations.Audit) {
eps = append(eps, util.AuditEnforcementPoint)
}
if operations.IsAssigned(operations.Webhook) {
eps = append(eps, util.WebhookEnforcementPoint)
}

cfArgs = append(cfArgs, constraintclient.EnforcementPoints(eps...))

client, err := constraintclient.NewClient(cfArgs...)
if err != nil {
setupLog.Error(err, "unable to set up OPA client")
Expand Down
58 changes: 29 additions & 29 deletions pkg/audit/manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import (

"github.com/go-logr/logr"
constraintclient "github.com/open-policy-agent/frameworks/constraint/pkg/client"
"github.com/open-policy-agent/frameworks/constraint/pkg/client/drivers"
"github.com/open-policy-agent/frameworks/constraint/pkg/client/reviews"
"github.com/open-policy-agent/gatekeeper/v3/pkg/controller/config/process"
pubsubController "github.com/open-policy-agent/gatekeeper/v3/pkg/controller/pubsub"
"github.com/open-policy-agent/gatekeeper/v3/pkg/expansion"
Expand Down Expand Up @@ -96,13 +96,13 @@ type Manager struct {

// StatusViolation represents each violation under status.
type StatusViolation struct {
Group string `json:"group"`
Version string `json:"version"`
Kind string `json:"kind"`
Name string `json:"name"`
Namespace string `json:"namespace,omitempty"`
Message string `json:"message"`
EnforcementAction string `json:"enforcementAction"`
Group string `json:"group"`
Version string `json:"version"`
Kind string `json:"kind"`
Name string `json:"name"`
Namespace string `json:"namespace,omitempty"`
Message string `json:"message"`
EnforcementAction []string `json:"enforcementAction"`
}

// ConstraintMsg represents publish message for each constraint.
Expand All @@ -116,7 +116,7 @@ type PubsubMsg struct {
Name string `json:"name,omitempty"`
Namespace string `json:"namespace,omitempty"`
Message string `json:"message,omitempty"`
EnforcementAction string `json:"enforcementAction,omitempty"`
EnforcementAction []string `json:"enforcementAction,omitempty"`
ConstraintAnnotations map[string]string `json:"constraintAnnotations,omitempty"`
ResourceGroup string `json:"resourceGroup,omitempty"`
ResourceAPIVersion string `json:"resourceAPIVersion,omitempty"`
Expand Down Expand Up @@ -149,10 +149,7 @@ func (svq SVQueue) Less(i, j int) bool {
if svq[i].Name != svq[j].Name {
return svq[i].Name > svq[j].Name
}
if svq[i].Message != svq[j].Message {
return svq[i].Message > svq[j].Message
}
return svq[i].EnforcementAction > svq[j].EnforcementAction
return svq[i].Message > svq[j].Message
}

func (svq SVQueue) Swap(i, j int) {
Expand Down Expand Up @@ -269,6 +266,7 @@ func New(mgr manager.Manager, deps *Dependencies) (*Manager, error) {
expansionSystem: deps.ExpansionSystem,
pubsubSystem: deps.PubSubSystem,
}

return am, nil
}

Expand Down Expand Up @@ -607,7 +605,7 @@ func (am *Manager) auditFromCache(ctx context.Context) ([]Result, []error) {
Object: obj,
Namespace: ns,
}
resp, err := am.opa.Review(ctx, au, drivers.Stats(*logStatsAudit))
resp, err := am.opa.Review(ctx, au, reviews.SourceEP(util.AuditEnforcementPoint), reviews.Stats(*logStatsAudit))
if err != nil {
am.log.Error(err, fmt.Sprintf("Unable to review object from audit cache %v %s/%s", obj.GroupVersionKind().String(), obj.GetNamespace(), obj.GetName()))
continue
Expand Down Expand Up @@ -697,7 +695,7 @@ func (am *Manager) reviewObjects(ctx context.Context, kind string, folderCount i
Source: mutationtypes.SourceTypeOriginal,
}

resp, err := am.opa.Review(ctx, augmentedObj, drivers.Stats(*logStatsAudit))
resp, err := am.opa.Review(ctx, augmentedObj, reviews.SourceEP(util.AuditEnforcementPoint), reviews.Stats(*logStatsAudit))
if err != nil {
am.log.Error(err, "Unable to review object from file", "fileName", fileName, "objNs", objNs)
continue
Expand All @@ -721,7 +719,7 @@ func (am *Manager) reviewObjects(ctx context.Context, kind string, folderCount i
Namespace: ns,
Source: mutationtypes.SourceTypeGenerated,
}
resultantResp, err := am.opa.Review(ctx, au, drivers.Stats(*logStatsAudit))
resultantResp, err := am.opa.Review(ctx, au, reviews.SourceEP(util.AuditEnforcementPoint), reviews.Stats(*logStatsAudit))
if err != nil {
am.log.Error(err, "Unable to review expanded object", "objName", (*resultant.Obj).GetName(), "objNs", ns)
continue
Expand Down Expand Up @@ -873,8 +871,11 @@ func (am *Manager) addAuditResponsesToUpdateLists(
}

totalViolationsPerConstraint[key]++
ea := util.EnforcementAction(r.EnforcementAction)
totalViolationsPerEnforcementAction[ea]++

for _, action := range r.EnforcementAction {
ea := util.EnforcementAction(action)
totalViolationsPerEnforcementAction[ea]++
}

gvk := r.obj.GroupVersionKind()
namespace := r.obj.GetNamespace()
Expand All @@ -883,33 +884,32 @@ func (am *Manager) addAuditResponsesToUpdateLists(
if len(msg) > msgSize {
msg = truncateString(msg, msgSize)
}
action := string(ea)
violation := &StatusViolation{
Group: gvk.Group,
Version: gvk.Version,
Kind: gvk.Kind,
Namespace: namespace,
Name: name,
Message: msg,
EnforcementAction: action,
EnforcementAction: r.EnforcementAction,
}
// since keyQueue is a LimitQueue, it guarantees len <= limit after a push.
// the limit on size ensures Push() has O(1) time complexity.
keyQueue.Push(violation)

details := r.Metadata["details"]
labels := r.obj.GetLabels()
logViolation(am.log, constraint, ea, gvk, namespace, name, msg, details, labels)
logViolation(am.log, constraint, r.EnforcementAction, gvk, namespace, name, msg, details, labels)
if *pubsubController.PubsubEnabled {
err := am.pubsubSystem.Publish(context.Background(), *auditConnection, *auditChannel, violationMsg(constraint, ea, gvk, namespace, name, msg, details, labels, timestamp))
err := am.pubsubSystem.Publish(context.Background(), *auditConnection, *auditChannel, violationMsg(constraint, r.EnforcementAction, gvk, namespace, name, msg, details, labels, timestamp))
if err != nil {
am.log.Error(err, "pubsub audit Publishing")
}
}
if *emitAuditEvents {
uid := r.obj.GetUID()
rv := r.obj.GetResourceVersion()
emitEvent(constraint, timestamp, ea, gvk, namespace, name, rv, msg, am.gkNamespace, uid, am.eventRecorder)
emitEvent(constraint, timestamp, r.EnforcementAction, gvk, namespace, name, rv, msg, am.gkNamespace, uid, am.eventRecorder)
}
}
}
Expand Down Expand Up @@ -1140,7 +1140,7 @@ func logFinish(l logr.Logger) {
)
}

func logConstraint(l logr.Logger, gvknn *util.KindVersionName, enforcementAction string, totalViolations int64) {
func logConstraint(l logr.Logger, gvknn *util.KindVersionName, enforcementAction []string, totalViolations int64) {
l.Info(
"audit results for constraint",
logging.EventType, "constraint_audited",
Expand All @@ -1155,7 +1155,7 @@ func logConstraint(l logr.Logger, gvknn *util.KindVersionName, enforcementAction
)
}

func violationMsg(constraint *unstructured.Unstructured, enforcementAction util.EnforcementAction, resourceGroupVersionKind schema.GroupVersionKind, rnamespace, rname, message string, details interface{}, rlabels map[string]string, timestamp string) interface{} {
func violationMsg(constraint *unstructured.Unstructured, enforcementAction []string, resourceGroupVersionKind schema.GroupVersionKind, rnamespace, rname, message string, details interface{}, rlabels map[string]string, timestamp string) interface{} {
userConstraintAnnotations := constraint.GetAnnotations()
delete(userConstraintAnnotations, "kubectl.kubernetes.io/last-applied-configuration")

Expand All @@ -1169,7 +1169,7 @@ func violationMsg(constraint *unstructured.Unstructured, enforcementAction util.
Kind: constraint.GetKind(),
Name: constraint.GetName(),
Namespace: constraint.GetNamespace(),
EnforcementAction: string(enforcementAction),
EnforcementAction: enforcementAction,
ConstraintAnnotations: userConstraintAnnotations,
ResourceGroup: resourceGroupVersionKind.Group,
ResourceAPIVersion: resourceGroupVersionKind.Version,
Expand All @@ -1182,7 +1182,7 @@ func violationMsg(constraint *unstructured.Unstructured, enforcementAction util.

func logViolation(l logr.Logger,
constraint *unstructured.Unstructured,
enforcementAction util.EnforcementAction, resourceGroupVersionKind schema.GroupVersionKind, rnamespace, rname, message string, details interface{}, rlabels map[string]string,
enforcementAction []string, resourceGroupVersionKind schema.GroupVersionKind, rnamespace, rname, message string, details interface{}, rlabels map[string]string,
) {
userConstraintAnnotations := constraint.GetAnnotations()
delete(userConstraintAnnotations, "kubectl.kubernetes.io/last-applied-configuration")
Expand All @@ -1208,7 +1208,7 @@ func logViolation(l logr.Logger,
}

func emitEvent(constraint *unstructured.Unstructured,
timestamp string, enforcementAction util.EnforcementAction, resourceGroupVersionKind schema.GroupVersionKind, rnamespace, rname, rrv, message, gkNamespace string, ruid types.UID,
timestamp string, enforcementAction []string, resourceGroupVersionKind schema.GroupVersionKind, rnamespace, rname, rrv, message, gkNamespace string, ruid types.UID,
eventRecorder record.EventRecorder,
) {
annotations := map[string]string{
Expand All @@ -1220,7 +1220,7 @@ func emitEvent(constraint *unstructured.Unstructured,
logging.ConstraintKind: constraint.GetKind(),
logging.ConstraintName: constraint.GetName(),
logging.ConstraintNamespace: constraint.GetNamespace(),
logging.ConstraintAction: string(enforcementAction),
logging.ConstraintAction: strings.Join(enforcementAction, "/"),
logging.ResourceGroup: resourceGroupVersionKind.Group,
logging.ResourceAPIVersion: resourceGroupVersionKind.Version,
logging.ResourceKind: resourceGroupVersionKind.Kind,
Expand Down
26 changes: 23 additions & 3 deletions pkg/audit/manager_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
"github.com/open-policy-agent/gatekeeper/v3/pkg/controller/config/process"
"github.com/open-policy-agent/gatekeeper/v3/pkg/fakes"
"github.com/open-policy-agent/gatekeeper/v3/pkg/target"
"github.com/open-policy-agent/gatekeeper/v3/pkg/util"
"github.com/open-policy-agent/gatekeeper/v3/pkg/wildcard"
"github.com/pkg/errors"
"github.com/stretchr/testify/require"
Expand Down Expand Up @@ -96,32 +97,47 @@ func Test_auditFromCache(t *testing.T) {

driver, err := rego.New()
require.NoError(t, err)
client, err := constraintclient.NewClient(constraintclient.Targets(&target.K8sValidationTarget{}), constraintclient.Driver(driver))
client, err := constraintclient.NewClient(constraintclient.Targets(&target.K8sValidationTarget{}), constraintclient.Driver(driver), constraintclient.EnforcementPoints([]string{util.AuditEnforcementPoint}...))
require.NoError(t, err)

_, err = client.AddTemplate(context.Background(), fakes.DenyAllRegoTemplate())
require.NoError(t, err, "adding denyall constraint template")
_, err = client.AddConstraint(context.Background(), fakes.DenyAllConstraint())
require.NoError(t, err, "adding denyall constraint")

tests := []struct {
name string
processExcluder *process.Excluder
constraint *unstructured.Unstructured
wantViolation bool
}{
{
name: "obj excluded from audit",
processExcluder: processExcluderFor([]string{"test-namespace-1"}),
constraint: fakes.DenyAllConstraint(),
},
{
name: "obj not excluded from audit",
processExcluder: processExcluderFor([]string{}),
constraint: fakes.DenyAllConstraint(),
wantViolation: true,
},
{
name: "audit excluded from constraint",
processExcluder: processExcluderFor([]string{}),
constraint: fakes.WebhookDenyAllConstraint(),
},
{
name: "audit included in constraints",
processExcluder: processExcluderFor([]string{}),
constraint: fakes.AuditDenyAllConstraint(),
wantViolation: true,
},
}

for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
_, err = client.AddConstraint(context.Background(), tc.constraint)
require.NoError(t, err, "adding denyall constraint")

am := &Manager{
processExcluder: tc.processExcluder,
auditCache: testAuditCache,
Expand All @@ -136,6 +152,10 @@ func Test_auditFromCache(t *testing.T) {
} else {
require.Len(t, results, 0)
}

if _, err := client.RemoveConstraint(context.Background(), tc.constraint); err != nil {
t.Fatal(err)
}
})
}
}
Expand Down
2 changes: 1 addition & 1 deletion pkg/audit/result_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ func TestResult_ToResult(t *testing.T) {
aResult := types.Result{
Target: "targetA",
Msg: "violationA",
EnforcementAction: "deny",
EnforcementAction: []string{"deny"},
}

responses := types.Responses{
Expand Down
16 changes: 0 additions & 16 deletions pkg/controller/constraint/constants.go

This file was deleted.

Loading

0 comments on commit ad9bba6

Please sign in to comment.