Skip to content

Commit 00d3bfa

Browse files
authored
feat: add namespace support for CEL and Rego engines (open-policy-agent#4285)
Signed-off-by: Jaydip Gabani <[email protected]>
1 parent d6355ea commit 00d3bfa

19 files changed

+790
-15
lines changed

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ require (
1515
github.com/google/uuid v1.6.0
1616
github.com/onsi/gomega v1.38.3
1717
github.com/open-policy-agent/cert-controller v0.14.1-0.20251031183427-40974f7dd486
18-
github.com/open-policy-agent/frameworks/constraint v0.0.0-20251212003702-cf69624a8e54
18+
github.com/open-policy-agent/frameworks/constraint v0.0.0-20251217214254-e3880ce65b48
1919
github.com/open-policy-agent/opa v1.10.0
2020
github.com/pkg/errors v0.9.1
2121
github.com/prometheus/client_golang v1.23.2

go.sum

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -305,8 +305,8 @@ github.com/onsi/gomega v1.38.3 h1:eTX+W6dobAYfFeGC2PV6RwXRu/MyT+cQguijutvkpSM=
305305
github.com/onsi/gomega v1.38.3/go.mod h1:ZCU1pkQcXDO5Sl9/VVEGlDyp+zm0m1cmeG5TOzLgdh4=
306306
github.com/open-policy-agent/cert-controller v0.14.1-0.20251031183427-40974f7dd486 h1:Ir1VtwXM/0OF22+klrZQpqbJQ4Vkpv1v4d7p61Xpzyg=
307307
github.com/open-policy-agent/cert-controller v0.14.1-0.20251031183427-40974f7dd486/go.mod h1:6zxrUxL0sFlTQzNFToeo2ysfQ9lloVXj2fitZBVdXWU=
308-
github.com/open-policy-agent/frameworks/constraint v0.0.0-20251212003702-cf69624a8e54 h1:lpHoyrcCmdhtVIdg7hz5wK1zR1HaGsBrWA5ddTgK7qc=
309-
github.com/open-policy-agent/frameworks/constraint v0.0.0-20251212003702-cf69624a8e54/go.mod h1:ZijN1cmV6XtRWHEWVSq/nWwa3ZAoO3d9OlfGsocbCsY=
308+
github.com/open-policy-agent/frameworks/constraint v0.0.0-20251217214254-e3880ce65b48 h1:cZPXsRvKW6nLqjNMt4Rm1EK93b9GPBM48BSr91nsKYo=
309+
github.com/open-policy-agent/frameworks/constraint v0.0.0-20251217214254-e3880ce65b48/go.mod h1:Kic6tRHuKSVP2cAn96UUbifEHvJf1bdu28fJCqNPahg=
310310
github.com/open-policy-agent/opa v1.10.0 h1:CzWR/2OhZ5yHrqiyyB1Z37mqLMowifAiFSasjLxBBpk=
311311
github.com/open-policy-agent/opa v1.10.0/go.mod h1:7uPI3iRpOalJ0BhK6s1JALWPU9HvaV1XeBSSMZnr/PM=
312312
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=

pkg/audit/manager.go

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -612,7 +612,14 @@ func (am *Manager) auditFromCache(ctx context.Context) ([]Result, []error) {
612612
Object: obj,
613613
Namespace: ns,
614614
}
615-
resp, err := am.opa.Review(ctx, au, reviews.EnforcementPoint(util.AuditEnforcementPoint), reviews.Stats(*logStatsAudit))
615+
opts := []reviews.ReviewOpt{
616+
reviews.EnforcementPoint(util.AuditEnforcementPoint),
617+
reviews.Stats(*logStatsAudit),
618+
}
619+
if opt := util.NamespaceReviewOpt(ns, am.log); opt != nil {
620+
opts = append(opts, opt)
621+
}
622+
resp, err := am.opa.Review(ctx, au, opts...)
616623
if err != nil {
617624
am.log.Error(err, fmt.Sprintf("Unable to review object from audit cache %v %s/%s", obj.GroupVersionKind().String(), obj.GetNamespace(), obj.GetName()))
618625
continue
@@ -703,7 +710,14 @@ func (am *Manager) reviewObjects(ctx context.Context, kind string, folderCount i
703710
Source: mutationtypes.SourceTypeOriginal,
704711
}
705712

706-
resp, err := am.opa.Review(ctx, augmentedObj, reviews.EnforcementPoint(util.AuditEnforcementPoint), reviews.Stats(*logStatsAudit))
713+
opts := []reviews.ReviewOpt{
714+
reviews.EnforcementPoint(util.AuditEnforcementPoint),
715+
reviews.Stats(*logStatsAudit),
716+
}
717+
if opt := util.NamespaceReviewOpt(ns, am.log); opt != nil {
718+
opts = append(opts, opt)
719+
}
720+
resp, err := am.opa.Review(ctx, augmentedObj, opts...)
707721
if err != nil {
708722
am.log.Error(err, "Unable to review object from file", "fileName", fileName, "objNs", objNs)
709723
continue
@@ -727,7 +741,14 @@ func (am *Manager) reviewObjects(ctx context.Context, kind string, folderCount i
727741
Namespace: ns,
728742
Source: mutationtypes.SourceTypeGenerated,
729743
}
730-
resultantResp, err := am.opa.Review(ctx, au, reviews.EnforcementPoint(util.AuditEnforcementPoint), reviews.Stats(*logStatsAudit))
744+
resultantOpts := []reviews.ReviewOpt{
745+
reviews.EnforcementPoint(util.AuditEnforcementPoint),
746+
reviews.Stats(*logStatsAudit),
747+
}
748+
if opt := util.NamespaceReviewOpt(ns, am.log); opt != nil {
749+
resultantOpts = append(resultantOpts, opt)
750+
}
751+
resultantResp, err := am.opa.Review(ctx, au, resultantOpts...)
731752
if err != nil {
732753
am.log.Error(err, "Unable to review expanded object", "objName", (*resultant.Obj).GetName(), "objNs", ns)
733754
continue

pkg/drivers/k8scel/driver.go

Lines changed: 41 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package k8scel
22

33
import (
44
"context"
5+
"encoding/json"
56
"errors"
67
"fmt"
78
"strings"
@@ -15,15 +16,18 @@ import (
1516
"github.com/open-policy-agent/frameworks/constraint/pkg/types"
1617
pSchema "github.com/open-policy-agent/gatekeeper/v3/pkg/drivers/k8scel/schema"
1718
"github.com/open-policy-agent/gatekeeper/v3/pkg/drivers/k8scel/transform"
19+
"github.com/open-policy-agent/gatekeeper/v3/pkg/logging"
1820
"github.com/open-policy-agent/opa/storage"
1921
admissionv1 "k8s.io/api/admission/v1"
22+
corev1 "k8s.io/api/core/v1"
2023
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
2124
"k8s.io/apimachinery/pkg/runtime"
2225
"k8s.io/apiserver/pkg/admission/plugin/cel"
2326
"k8s.io/apiserver/pkg/admission/plugin/policy/validating"
2427
"k8s.io/apiserver/pkg/admission/plugin/webhook/matchconditions"
2528
celAPI "k8s.io/apiserver/pkg/apis/cel"
2629
"k8s.io/apiserver/pkg/cel/environment"
30+
logf "sigs.k8s.io/controller-runtime/pkg/log"
2731
)
2832

2933
// NOTE: This is a PROTOTYPE driver. Do not use this for any critical work
@@ -48,7 +52,10 @@ const (
4852
runTimeNSDescription = "the number of nanoseconds it took to evaluate the constraint"
4953
)
5054

51-
var _ drivers.Driver = &Driver{}
55+
var (
56+
_ drivers.Driver = &Driver{}
57+
log = logf.Log.WithName("k8scel-driver").WithValues(logging.Process, "k8scel")
58+
)
5259

5360
type Driver struct {
5461
mux sync.RWMutex
@@ -198,8 +205,18 @@ func (d *Driver) Query(ctx context.Context, target string, constraints []*unstru
198205
return nil, fmt.Errorf("nil validator for constraint template %v", strings.ToLower(constraint.GetKind()))
199206
}
200207

201-
// TODO: should namespace be made available, if possible? Generally that context should be present
202-
response := validator.Validate(ctx, versionedAttr.GetResource(), versionedAttr, constraint, nil, celAPI.PerCallLimit, nil)
208+
// Convert namespace from map[string]interface{} to *corev1.Namespace if provided.
209+
// This enables CEL expressions to access namespaceObject for namespace-based policies.
210+
var namespace *corev1.Namespace
211+
if cfg.Namespace != nil {
212+
namespace, err = mapToNamespace(cfg.Namespace)
213+
if err != nil {
214+
log.Error(err, "failed to convert namespace to corev1.Namespace, continuing without namespace")
215+
namespace = nil
216+
}
217+
}
218+
219+
response := validator.Validate(ctx, versionedAttr.GetResource(), versionedAttr, constraint, namespace, celAPI.PerCallLimit, nil)
203220

204221
for _, decision := range response.Decisions {
205222
if decision.Action == validating.ActionDeny {
@@ -252,3 +269,24 @@ type ARGetter interface {
252269
type IsAdmissionGetter interface {
253270
IsAdmissionRequest() bool
254271
}
272+
273+
// mapToNamespace converts a map[string]interface{} representation of a namespace
274+
// to a *corev1.Namespace. This is used to pass the namespace object to the CEL
275+
// validator for namespaceObject support.
276+
func mapToNamespace(ns map[string]interface{}) (*corev1.Namespace, error) {
277+
if ns == nil {
278+
return nil, nil
279+
}
280+
281+
data, err := json.Marshal(ns)
282+
if err != nil {
283+
return nil, fmt.Errorf("failed to marshal namespace: %w", err)
284+
}
285+
286+
namespace := &corev1.Namespace{}
287+
if err := json.Unmarshal(data, namespace); err != nil {
288+
return nil, fmt.Errorf("failed to unmarshal namespace: %w", err)
289+
}
290+
291+
return namespace, nil
292+
}

0 commit comments

Comments
 (0)