Repository: Kong/kubernetes-ingress-controller
Pull request #6324 (2024-07-17 02:02:41)

chore(deps): update istio/istioctl docker tag to v1.22.3
[![Mend Renovate](https://app.renovatebot.com/images/banner.svg)](https://renovatebot.com)

This PR contains the following updates:

| Package | Update | Change |
|---|---|---|
| istio/istioctl | patch | `1.22.2` -> `1.22.3` |

---

> [!WARNING]
> Some dependencies could not be looked up. Check the warning logs for more information.

---

### Configuration

📅 **Schedule**: Branch creation - "before 5am every weekday" (UTC), Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update again.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box

---

This PR has been generated by [Mend Renovate](https://www.mend.io/free-developer-tools/renovate/). View repository job log [here](https://developer.mend.io/github/Kong/kubernetes-ingress-controller).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzNy40MzEuNCIsInVwZGF0ZWRJblZlciI6IjM3LjQzMS40IiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6WyJkZXBlbmRlbmNpZXMiXX0=-->



Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 987b218ff..9ca054e1d 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -102,6 +102,11 @@ Adding a new version? You'll need three changes:
 - It is now possible to disable synchronization of consumers to Konnect through the
   flag `--konnect-disable-consumers-sync`.
   [#6313](https://github.com/Kong/kubernetes-ingress-controller/pull/6313)
+- Allow `KongCustomEntity` to refer to plugins in another namespace via 
+  `spec.parentRef.namespace`. The reference is allowed only when there is a
+  `ReferenceGrant` in the namespace of the `KongPlugin` to grant permissions
+  to `KongCustomEntity` of referring to `KongPlugin`.
+  [#6289](https://github.com/Kong/kubernetes-ingress-controller/pull/6289)
 
 ### Fixed
 
diff --git a/go.mod b/go.mod
index 597c990f1..26170a62f 100644
--- a/go.mod
+++ b/go.mod
@@ -30,7 +30,7 @@ require (
 	github.com/google/go-cmp v0.6.0
 	github.com/google/uuid v1.6.0
 	github.com/jpillora/backoff v1.0.0
-	github.com/kong/go-database-reconciler v1.13.0
+	github.com/kong/go-database-reconciler v1.14.0
 	github.com/kong/go-kong v0.57.0
 	github.com/kong/kubernetes-telemetry v0.1.4
 	github.com/kong/kubernetes-testing-framework v0.47.1
@@ -50,10 +50,10 @@ require (
 	github.com/testcontainers/testcontainers-go v0.31.0
 	go.uber.org/zap v1.27.0
 	google.golang.org/api v0.188.0
-	k8s.io/api v0.30.2
+	k8s.io/api v0.30.3
 	k8s.io/apiextensions-apiserver v0.30.2
-	k8s.io/apimachinery v0.30.2
-	k8s.io/client-go v0.30.2
+	k8s.io/apimachinery v0.30.3
+	k8s.io/client-go v0.30.3
 	k8s.io/component-base v0.30.2
 	sigs.k8s.io/controller-runtime v0.18.4
 	sigs.k8s.io/e2e-framework v0.4.0
diff --git a/go.sum b/go.sum
index 469f5e7ad..48d16f0cf 100644
--- a/go.sum
+++ b/go.sum
@@ -238,8 +238,8 @@ github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI
 github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
 github.com/klauspost/compress v1.17.0 h1:Rnbp4K9EjcDuVuHtd0dgA4qNuv9yKDYKK1ulpJwgrqM=
 github.com/klauspost/compress v1.17.0/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE=
-github.com/kong/go-database-reconciler v1.13.0 h1:6gRGeLDep0mvzT/5fCQY5waQp0F/T9MsyZ9fJmJq+Cc=
-github.com/kong/go-database-reconciler v1.13.0/go.mod h1:bUPJkoeW//x4hzNxewQMoIkeoDzJzunI0stDMYJ3BkU=
+github.com/kong/go-database-reconciler v1.14.0 h1:QTkyO5CO5GHCWUU5ZVJYINRgxp2H/8LM3WDeHT1qJ7U=
+github.com/kong/go-database-reconciler v1.14.0/go.mod h1:OsyUWH3SUvXAd5HJx3PhRU3PyzH0uCy7bTlzoSfw8Ew=
 github.com/kong/go-kong v0.57.0 h1:e+4bHTzcO0xhFPVyGIUtlO+B4E4l14k55NH8Vjw6ORY=
 github.com/kong/go-kong v0.57.0/go.mod h1:gyNwyP1fzztT6sX/0/ygMQ30OiRMIQ51b2jSfstMrcU=
 github.com/kong/kubernetes-telemetry v0.1.4 h1:Yz7OlECxWKgNRG1wJ5imA4+H0dQEpdU9d86uhwUVpu4=
@@ -618,16 +618,16 @@ gotest.tools/v3 v3.5.0 h1:Ljk6PdHdOhAb5aDMWXjDLMMhph+BpztA4v1QdqEW2eY=
 gotest.tools/v3 v3.5.0/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU=
 honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
 honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
-k8s.io/api v0.30.2 h1:+ZhRj+28QT4UOH+BKznu4CBgPWgkXO7XAvMcMl0qKvI=
-k8s.io/api v0.30.2/go.mod h1:ULg5g9JvOev2dG0u2hig4Z7tQ2hHIuS+m8MNZ+X6EmI=
+k8s.io/api v0.30.3 h1:ImHwK9DCsPA9uoU3rVh4QHAHHK5dTSv1nxJUapx8hoQ=
+k8s.io/api v0.30.3/go.mod h1:GPc8jlzoe5JG3pb0KJCSLX5oAFIW3/qNJITlDj8BH04=
 k8s.io/apiextensions-apiserver v0.30.2 h1:l7Eue2t6QiLHErfn2vwK4KgF4NeDgjQkCXtEbOocKIE=
 k8s.io/apiextensions-apiserver v0.30.2/go.mod h1:lsJFLYyK40iguuinsb3nt+Sj6CmodSI4ACDLep1rgjw=
-k8s.io/apimachinery v0.30.2 h1:fEMcnBj6qkzzPGSVsAZtQThU62SmQ4ZymlXRC5yFSCg=
-k8s.io/apimachinery v0.30.2/go.mod h1:iexa2somDaxdnj7bha06bhb43Zpa6eWH8N8dbqVjTUc=
+k8s.io/apimachinery v0.30.3 h1:q1laaWCmrszyQuSQCfNB8cFgCuDAoPszKY4ucAjDwHc=
+k8s.io/apimachinery v0.30.3/go.mod h1:iexa2somDaxdnj7bha06bhb43Zpa6eWH8N8dbqVjTUc=
 k8s.io/cli-runtime v0.30.2 h1:ooM40eEJusbgHNEqnHziN9ZpLN5U4WcQGsdLKVxpkKE=
 k8s.io/cli-runtime v0.30.2/go.mod h1:Y4g/2XezFyTATQUbvV5WaChoUGhojv/jZAtdp5Zkm0A=
-k8s.io/client-go v0.30.2 h1:sBIVJdojUNPDU/jObC+18tXWcTJVcwyqS9diGdWHk50=
-k8s.io/client-go v0.30.2/go.mod h1:JglKSWULm9xlJLx4KCkfLLQ7XwtlbflV6uFFSHTMgVs=
+k8s.io/client-go v0.30.3 h1:bHrJu3xQZNXIi8/MoxYtZBBWQQXwy16zqJwloXXfD3k=
+k8s.io/client-go v0.30.3/go.mod h1:8d4pf8vYu665/kUbsxWAQ/JDBNWqfFeZnvFiVdmx89U=
 k8s.io/component-base v0.30.2 h1:pqGBczYoW1sno8q9ObExUqrYSKhtE5rW3y6gX88GZII=
 k8s.io/component-base v0.30.2/go.mod h1:yQLkQDrkK8J6NtP+MGJOws+/PPeEXNpwFixsUI7h/OE=
 k8s.io/klog/v2 v2.120.1 h1:QXU6cPEOIslTGvZaXvFWiP9VKyeet3sawzTOvdXb4Vw=
diff --git a/internal/dataplane/kongstate/customentity.go b/internal/dataplane/kongstate/customentity.go
index 3177daf05..67d2892b8 100644
--- a/internal/dataplane/kongstate/customentity.go
+++ b/internal/dataplane/kongstate/customentity.go
@@ -11,6 +11,7 @@ import (
 	"github.com/kong/go-kong/kong/custom"
 	"github.com/samber/lo"
 
+	"github.com/kong/kubernetes-ingress-controller/v3/internal/annotations"
 	"github.com/kong/kubernetes-ingress-controller/v3/internal/dataplane/failures"
 	"github.com/kong/kubernetes-ingress-controller/v3/internal/store"
 	"github.com/kong/kubernetes-ingress-controller/v3/internal/util"
@@ -195,7 +196,11 @@ func (ks *KongState) FillCustomEntities(
 
 		// Fill the "foreign" fields if the entity has such fields referencing services/routes/consumers.
 		// First Find out possible foreign field combinations attached to the KCE resource.
-		foreignFieldCombinations := findCustomEntityForeignFields(logger, entity, schema, pluginRels, workspace)
+		foreignFieldCombinations, err := findCustomEntityForeignFields(logger, s, entity, schema, pluginRels, workspace)
+		if err != nil {
+			failuresCollector.PushResourceFailure(fmt.Sprintf("failed to find attached foreign entities for custom entity: %v", err), entity)
+			continue
+		}
 		// generate Kong entities from the fields in the KCE itself and attached foreign entities.
 		generatedEntities, err := generateCustomEntities(entity, foreignFieldCombinations)
 		if err != nil {
@@ -275,43 +280,55 @@ func (ks *KongState) sortCustomEntities() {
 	}
 }
 
-func findCustomEntityRelatedPlugin(k8sEntity *kongv1alpha1.KongCustomEntity) (string, bool) {
+func findCustomEntityRelatedPlugin(logger logr.Logger, cacheStore store.Storer, k8sEntity *kongv1alpha1.KongCustomEntity) (string, bool, error) {
 	// Find referred entity via the plugin in its spec.parentRef.
 	// Then we can fetch the referred service/route/consumer from the reference relations of the plugin.
 	parentRef := k8sEntity.Spec.ParentRef
-	var namespace string
 	// Abort if the parentRef is empty or does not refer to a plugin.
 	if parentRef == nil ||
 		(parentRef.Group == nil || *parentRef.Group != kongv1alpha1.GroupVersion.Group) {
-		return "", false
+		return "", false, nil
 	}
 	if parentRef.Kind == nil || (*parentRef.Kind != "KongPlugin" && *parentRef.Kind != "KongClusterPlugin") {
-		return "", false
+		return "", false, nil
 	}
+
 	// Extract the plugin key to get the plugin relations.
-	if parentRef.Namespace == nil || *parentRef.Namespace == "" {
-		namespace = k8sEntity.Namespace
-	} else {
-		namespace = *parentRef.Namespace
+	paretRefNamespace := lo.FromPtrOr(parentRef.Namespace, "")
+	// if the namespace in parentRef is not same as the namespace of KCE itself, check if the reference is allowed by ReferenceGrant.
+	if paretRefNamespace != "" && paretRefNamespace != k8sEntity.Namespace {
+		paretRefNamespace, err := extractReferredPluginNamespace(logger, cacheStore, k8sEntity, annotations.NamespacedKongPlugin{
+			Namespace: paretRefNamespace,
+			Name:      parentRef.Name,
+		})
+		if err != nil {
+			return "", false, err
+		}
+		return paretRefNamespace + ":" + parentRef.Name, true, nil
 	}
-	return namespace + ":" + parentRef.Name, true
+
+	return k8sEntity.Namespace + ":" + parentRef.Name, true, nil
 }
 
 func findCustomEntityForeignFields(
 	logger logr.Logger,
+	cacheStore store.Storer,
 	k8sEntity *kongv1alpha1.KongCustomEntity,
 	schema EntitySchema,
 	pluginRelEntities PluginRelatedEntitiesRefs,
 	workspace string,
-) [][]entityForeignFieldValue {
-	pluginKey, ok := findCustomEntityRelatedPlugin(k8sEntity)
+) ([][]entityForeignFieldValue, error) {
+	pluginKey, ok, err := findCustomEntityRelatedPlugin(logger, cacheStore, k8sEntity)
+	if err != nil {
+		return nil, err
+	}
 	if !ok {
-		return nil
+		return nil, nil
 	}
 	// Get the relations with other entities of the plugin.
 	rels, ok := pluginRelEntities.RelatedEntities[pluginKey]
 	if !ok {
-		return nil
+		return nil, nil
 	}
 
 	var (
@@ -378,7 +395,7 @@ func findCustomEntityForeignFields(
 		ret = append(ret, foreignFieldValues)
 	}
 
-	return ret
+	return ret, nil
 }
 
 // generateCustomEntities generates Kong entities from KongCustomEntity resource and combinations of attached foreign entities.
diff --git a/third_party/go.mod b/third_party/go.mod
index 2f5e1b90c..49866aac5 100644
--- a/third_party/go.mod
+++ b/third_party/go.mod
@@ -3,7 +3,7 @@ module github.com/kong/kubernetes-ingress-controller/tools
 go 1.22
 
 require (
-	github.com/go-delve/delve v1.22.1
+	github.com/go-delve/delve v1.23.0
 	github.com/kyoh86/looppointer v0.2.1
 
 )
@@ -37,6 +37,5 @@ require (
 	golang.org/x/sys v0.19.0 // indirect
 	golang.org/x/tools v0.20.0 // indirect
 	gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
-	gopkg.in/yaml.v2 v2.4.0 // indirect
 	gopkg.in/yaml.v3 v3.0.1 // indirect
 )
diff --git a/third_party/go.sum b/third_party/go.sum
index df4fd1141..42cb6199f 100644
--- a/third_party/go.sum
+++ b/third_party/go.sum
@@ -12,8 +12,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
 github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/derekparker/trie v0.0.0-20230829180723-39f4de51ef7d h1:hUWoLdw5kvo2xCsqlsIBMvWUc1QCSsCYD2J2+Fg6YoU=
 github.com/derekparker/trie v0.0.0-20230829180723-39f4de51ef7d/go.mod h1:C7Es+DLenIpPc9J6IYw4jrK0h7S9bKj4DNl8+KxGEXU=
-github.com/go-delve/delve v1.22.1 h1:LQSF2sv+lP3mmOzMkadl5HGQGgSS2bFg2tbyALqHu8Y=
-github.com/go-delve/delve v1.22.1/go.mod h1:TfOb+G5H6YYKheZYAmA59ojoHbOimGfs5trbghHdLbM=
+github.com/go-delve/delve v1.23.0 h1:jYgZISZ14KAO3ys8kD07kjrowrygE9F9SIwnpz9xXys=
+github.com/go-delve/delve v1.23.0/go.mod h1:S3SLuEE2mn7wipKilTvk1p9HdTMnXXElcEpiZ+VcuqU=
 github.com/go-delve/liner v1.2.3-0.20231231155935-4726ab1d7f62 h1:IGtvsNyIuRjl04XAOFGACozgUD7A82UffYxZt4DWbvA=
 github.com/go-delve/liner v1.2.3-0.20231231155935-4726ab1d7f62/go.mod h1:biJCRbqp51wS+I92HMqn5H8/A0PAhxn2vyOT+JqhiGI=
 github.com/go-quicktest/qt v1.101.0 h1:O1K29Txy5P2OK0dGo59b7b0LR6wKfIhttaAhHUyn7eI=
@@ -106,8 +106,6 @@ google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHh
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
 gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
-gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
-gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
 gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
 gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
 gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=


Test patch:
diff --git a/.github/test_dependencies.yaml b/.github/test_dependencies.yaml
index 782d76efa..3d6b15ad2 100644
--- a/.github/test_dependencies.yaml
+++ b/.github/test_dependencies.yaml
@@ -20,7 +20,7 @@ e2e:
     - # renovate: datasource=docker depName=kindest/node versioning=docker
       kind: 'v1.30.2'
       # renovate: datasource=docker depName=istio/istioctl versioning=docker
-      istio: '1.22.2'
+      istio: '1.22.3'
     - # renovate: datasource=docker depName=kindest/node@only-patch versioning=docker
       kind: 'v1.30.0'
       # renovate: datasource=docker depName=istio/istioctl@only-patch versioning=docker
diff --git a/internal/dataplane/kongstate/customentity_test.go b/internal/dataplane/kongstate/customentity_test.go
index c0917cb2a..ae6922401 100644
--- a/internal/dataplane/kongstate/customentity_test.go
+++ b/internal/dataplane/kongstate/customentity_test.go
@@ -18,12 +18,18 @@ import (
 
 	"github.com/kong/kubernetes-ingress-controller/v3/internal/annotations"
 	"github.com/kong/kubernetes-ingress-controller/v3/internal/dataplane/failures"
+	"github.com/kong/kubernetes-ingress-controller/v3/internal/gatewayapi"
 	"github.com/kong/kubernetes-ingress-controller/v3/internal/store"
 	"github.com/kong/kubernetes-ingress-controller/v3/internal/util"
 	kongv1 "github.com/kong/kubernetes-ingress-controller/v3/pkg/apis/configuration/v1"
 	kongv1alpha1 "github.com/kong/kubernetes-ingress-controller/v3/pkg/apis/configuration/v1alpha1"
 )
 
+var customEntityTypeMeta = metav1.TypeMeta{
+	APIVersion: kongv1alpha1.GroupVersion.Group + "/" + kongv1alpha1.GroupVersion.Version,
+	Kind:       "KongCustomEntity",
+}
+
 func TestExtractEntityFieldDefinitions(t *testing.T) {
 	testCases := []struct {
 		name           string
@@ -224,6 +230,7 @@ func TestSortCustomEntities(t *testing.T) {
 
 func TestFindCustomEntityForeignFields(t *testing.T) {
 	testCustomEntity := &kongv1alpha1.KongCustomEntity{
+		TypeMeta: customEntityTypeMeta,
 		ObjectMeta: metav1.ObjectMeta{
 			Namespace: "default",
 			Name:      "fake-entity",
@@ -267,9 +274,11 @@ func TestFindCustomEntityForeignFields(t *testing.T) {
 	}
 	testCases := []struct {
 		name                     string
+		referenceGrants          []*gatewayapi.ReferenceGrant
 		customEntity             *kongv1alpha1.KongCustomEntity
 		schema                   EntitySchema
 		pluginRelEntities        PluginRelatedEntitiesRefs
+		expectError              bool
 		foreignFieldCombinations [][]entityForeignFieldValue
 	}{
 		{
@@ -359,17 +368,167 @@ func TestFindCustomEntityForeignFields(t *testing.T) {
 				},
 			},
 		},
+		{
+			name: "attached to foreign plugin with reference grant allowed",
+			customEntity: func() *kongv1alpha1.KongCustomEntity {
+				e := testCustomEntity.DeepCopy()
+				e.Spec.ParentRef.Namespace = lo.ToPtr("another-namespace")
+				return e
+			}(),
+			schema: EntitySchema{
+				Fields: map[string]EntityField{
+					"foo":     {Name: "foo", Type: EntityFieldTypeString, Required: true},
+					"service": {Name: "service", Type: EntityFieldTypeForeign, Reference: "services"},
+				},
+			},
+			referenceGrants: []*gatewayapi.ReferenceGrant{
+				{
+					ObjectMeta: metav1.ObjectMeta{
+						Namespace: "another-namespace",
+						Name:      "grant-kce-to-plugin",
+					},
+					Spec: gatewayapi.ReferenceGrantSpec{
+						From: []gatewayapi.ReferenceGrantFrom{
+							{
+								Namespace: gatewayapi.Namespace("default"),
+								Group:     gatewayapi.Group(kongv1alpha1.GroupVersion.Group),
+								Kind:      "KongCustomEntity",
+							},
+						},
+						To: []gatewayapi.ReferenceGrantTo{
+							{
+								Group: gatewayapi.Group(kongv1alpha1.GroupVersion.Group),
+								Kind:  gatewayapi.Kind("KongPlugin"),
+							},
+						},
+					},
+				},
+			},
+			pluginRelEntities: PluginRelatedEntitiesRefs{
+				RelatedEntities: map[string]RelatedEntitiesRef{
+					"another-namespace:fake-plugin": {
+						Services: []*Service{
+							{
+								Service: kongService1,
+							},
+							{
+								Service: kongService2,
+							},
+						},
+					},
+				},
+			},
+			foreignFieldCombinations: [][]entityForeignFieldValue{
+				{
+					{fieldName: "service", foreignEntityType: kong.EntityTypeServices, foreignEntityID: "service1"},
+				},
+				{
+					{fieldName: "service", foreignEntityType: kong.EntityTypeServices, foreignEntityID: "service2"},
+				},
+			},
+		},
+		{
+			name: "attached to foreign plugin without reference grant allowed should fail",
+			customEntity: func() *kongv1alpha1.KongCustomEntity {
+				e := testCustomEntity.DeepCopy()
+				e.Spec.ParentRef.Namespace = lo.ToPtr("another-namespace")
+				return e
+			}(),
+			schema: EntitySchema{
+				Fields: map[string]EntityField{
+					"foo":     {Name: "foo", Type: EntityFieldTypeString, Required: true},
+					"service": {Name: "service", Type: EntityFieldTypeForeign, Reference: "services"},
+				},
+			},
+			pluginRelEntities: PluginRelatedEntitiesRefs{
+				RelatedEntities: map[string]RelatedEntitiesRef{
+					"another-namespace:fake-plugin": {
+						Services: []*Service{
+							{
+								Service: kongService1,
+							},
+							{
+								Service: kongService2,
+							},
+						},
+					},
+				},
+			},
+			expectError: true,
+		},
+		{
+			name: "attached to foreign plugin with misconfigured reference grant should fail",
+			customEntity: func() *kongv1alpha1.KongCustomEntity {
+				e := testCustomEntity.DeepCopy()
+				e.Spec.ParentRef.Namespace = lo.ToPtr("another-namespace")
+				return e
+			}(),
+			schema: EntitySchema{
+				Fields: map[string]EntityField{
+					"foo":     {Name: "foo", Type: EntityFieldTypeString, Required: true},
+					"service": {Name: "service", Type: EntityFieldTypeForeign, Reference: "services"},
+				},
+			},
+			referenceGrants: []*gatewayapi.ReferenceGrant{
+				{
+					ObjectMeta: metav1.ObjectMeta{
+						Namespace: "default", // same namespace as KCE but not same as KongPlugin
+						Name:      "grant-kce-to-plugin-misconfigured",
+					},
+					Spec: gatewayapi.ReferenceGrantSpec{
+						From: []gatewayapi.ReferenceGrantFrom{
+							{
+								Namespace: gatewayapi.Namespace("default"),
+								Group:     gatewayapi.Group(kongv1alpha1.GroupVersion.Group),
+								Kind:      "KongCustomEntity",
+							},
+						},
+						To: []gatewayapi.ReferenceGrantTo{
+							{
+								Group: gatewayapi.Group(kongv1alpha1.GroupVersion.Group),
+								Kind:  gatewayapi.Kind("KongPlugin"),
+							},
+						},
+					},
+				},
+			},
+			pluginRelEntities: PluginRelatedEntitiesRefs{
+				RelatedEntities: map[string]RelatedEntitiesRef{
+					"another-namespace:fake-plugin": {
+						Services: []*Service{
+							{
+								Service: kongService1,
+							},
+							{
+								Service: kongService2,
+							},
+						},
+					},
+				},
+			},
+			expectError: true,
+		},
 	}
 
 	for _, tc := range testCases {
 		t.Run(tc.name, func(t *testing.T) {
-			combinations := findCustomEntityForeignFields(
+			cacheStore, err := store.NewFakeStore(store.FakeObjects{
+				ReferenceGrants: tc.referenceGrants,
+			})
+			require.NoError(t, err)
+			combinations, err := findCustomEntityForeignFields(
 				logr.Discard(),
+				cacheStore,
 				tc.customEntity,
 				tc.schema,
 				tc.pluginRelEntities,
 				"",
 			)
+			if tc.expectError {
+				require.Error(t, err)
+				return
+			}
+			require.NoError(t, err)
 			for index, combination := range combinations {
 				sort.SliceStable(combination, func(i, j int) bool {
 					return combination[i].fieldName < combination[j].fieldName
@@ -384,10 +543,6 @@ func TestFindCustomEntityForeignFields(t *testing.T) {
 }
 
 func TestKongState_FillCustomEntities(t *testing.T) {
-	customEntityTypeMeta := metav1.TypeMeta{
-		APIVersion: kongv1alpha1.GroupVersion.Group + "/" + kongv1alpha1.GroupVersion.Version,
-		Kind:       "KongCustomEntity",
-	}
 	kongService1 := kong.Service{
 		Name: kong.String("service1"),
 		ID:   kong.String("service1"),