-
Notifications
You must be signed in to change notification settings - Fork 4
fix: suppress cross-namespace Secret existence oracle in Consumer webhook #433
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -28,7 +28,10 @@ import ( | |
| "sigs.k8s.io/controller-runtime/pkg/client/fake" | ||
| gatewayv1 "sigs.k8s.io/gateway-api/apis/v1" | ||
|
|
||
| "sigs.k8s.io/gateway-api/apis/v1beta1" | ||
|
|
||
| apisixv1alpha1 "github.com/apache/apisix-ingress-controller/api/v1alpha1" | ||
| "github.com/apache/apisix-ingress-controller/internal/controller" | ||
| "github.com/apache/apisix-ingress-controller/internal/controller/config" | ||
| "github.com/apache/apisix-ingress-controller/internal/controller/indexer" | ||
| ) | ||
|
|
@@ -40,6 +43,7 @@ func buildConsumerValidator(t *testing.T, objects ...runtime.Object) *ConsumerCu | |
| require.NoError(t, clientgoscheme.AddToScheme(scheme)) | ||
| require.NoError(t, apisixv1alpha1.AddToScheme(scheme)) | ||
| require.NoError(t, gatewayv1.Install(scheme)) | ||
| require.NoError(t, v1beta1.Install(scheme)) | ||
|
|
||
| managed := []runtime.Object{ | ||
| &gatewayv1.GatewayClass{ | ||
|
|
@@ -89,7 +93,52 @@ func TestConsumerValidator_MissingSecretDefaultNamespace(t *testing.T) { | |
| require.Contains(t, warnings[0], "Referenced Secret 'default/jwt-secret' not found") | ||
| } | ||
|
|
||
| func TestConsumerValidator_MissingSecretCustomNamespace(t *testing.T) { | ||
| // Cross-namespace refs without a permitting ReferenceGrant must not reveal whether | ||
| // the Secret exists: found and not-found both yield the same uniform warning. | ||
| func TestConsumerValidator_CrossNamespaceSecretOracleSuppressed(t *testing.T) { | ||
| ns := "auth" | ||
| newConsumer := func() *apisixv1alpha1.Consumer { | ||
| return &apisixv1alpha1.Consumer{ | ||
| ObjectMeta: metav1.ObjectMeta{ | ||
| Name: "demo", | ||
| Namespace: "default", | ||
| }, | ||
| Spec: apisixv1alpha1.ConsumerSpec{ | ||
| GatewayRef: apisixv1alpha1.GatewayRef{Name: "test-gateway"}, | ||
| Credentials: []apisixv1alpha1.Credential{{ | ||
| Type: "jwt-auth", | ||
| SecretRef: &apisixv1alpha1.SecretReference{ | ||
| Name: "jwt-secret", | ||
| Namespace: &ns, | ||
| }, | ||
| }}, | ||
| }, | ||
| } | ||
| } | ||
|
|
||
| // Secret present in the target namespace, but no ReferenceGrant permits the ref. | ||
| present := buildConsumerValidator(t, &corev1.Secret{ | ||
| ObjectMeta: metav1.ObjectMeta{Name: "jwt-secret", Namespace: "auth"}, | ||
| }) | ||
| presentWarnings, err := present.ValidateCreate(context.Background(), newConsumer()) | ||
| require.NoError(t, err) | ||
|
|
||
| // Secret absent. | ||
| absent := buildConsumerValidator(t) | ||
| absentWarnings, err := absent.ValidateCreate(context.Background(), newConsumer()) | ||
| require.NoError(t, err) | ||
|
|
||
| // Identical output either way: no existence oracle. | ||
| require.Equal(t, absentWarnings, presentWarnings) | ||
| require.Len(t, presentWarnings, 1) | ||
| require.Contains(t, presentWarnings[0], "Referenced Secret 'auth/jwt-secret' is not accessible from this Consumer without a ReferenceGrant") | ||
| } | ||
|
|
||
| // A ReferenceGrant re-enables the normal existence check for the permitted namespace. | ||
| func TestConsumerValidator_CrossNamespaceSecretWithGrant(t *testing.T) { | ||
| controller.SetEnableReferenceGrant(true) | ||
| defer controller.SetEnableReferenceGrant(false) | ||
|
Comment on lines
+96
to
+140
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win Exercise denial with ReferenceGrant evaluation enabled and restore prior state. The oracle test currently passes when ReferenceGrant support is disabled, before grant lookup is exercised. Save the existing flag, enable it for both cross-namespace tests, and restore the saved value with cleanup; the current unconditional reset to 🤖 Prompt for AI Agents |
||
|
|
||
| ns := "auth" | ||
| consumer := &apisixv1alpha1.Consumer{ | ||
| ObjectMeta: metav1.ObjectMeta{ | ||
|
|
@@ -108,16 +157,38 @@ func TestConsumerValidator_MissingSecretCustomNamespace(t *testing.T) { | |
| }, | ||
| } | ||
|
|
||
| validator := buildConsumerValidator(t) | ||
| grant := &v1beta1.ReferenceGrant{ | ||
| ObjectMeta: metav1.ObjectMeta{Name: "allow-default-consumers", Namespace: "auth"}, | ||
| Spec: v1beta1.ReferenceGrantSpec{ | ||
| From: []v1beta1.ReferenceGrantFrom{{ | ||
| Group: v1beta1.Group("apisix.apache.org"), | ||
| Kind: v1beta1.Kind("Consumer"), | ||
| Namespace: v1beta1.Namespace("default"), | ||
| }}, | ||
| To: []v1beta1.ReferenceGrantTo{{ | ||
| Group: v1beta1.Group(""), | ||
| Kind: v1beta1.Kind("Secret"), | ||
| }}, | ||
| }, | ||
| } | ||
|
|
||
| warnings, err := validator.ValidateCreate(context.Background(), consumer) | ||
| // Grant + existing Secret: no warning. | ||
| allowed := buildConsumerValidator(t, grant, &corev1.Secret{ | ||
| ObjectMeta: metav1.ObjectMeta{Name: "jwt-secret", Namespace: "auth"}, | ||
| }) | ||
| warnings, err := allowed.ValidateCreate(context.Background(), consumer) | ||
| require.NoError(t, err) | ||
| require.Empty(t, warnings) | ||
|
|
||
| // Grant but Secret missing: the honest not-found warning is allowed again. | ||
| missing := buildConsumerValidator(t, grant) | ||
| warnings, err = missing.ValidateCreate(context.Background(), consumer) | ||
| require.NoError(t, err) | ||
| require.Len(t, warnings, 1) | ||
| require.Contains(t, warnings[0], "Referenced Secret 'auth/jwt-secret' not found") | ||
| } | ||
|
|
||
| func TestConsumerValidator_NoWarnings(t *testing.T) { | ||
| ns := "auth" | ||
| consumer := &apisixv1alpha1.Consumer{ | ||
| ObjectMeta: metav1.ObjectMeta{ | ||
| Name: "demo", | ||
|
|
@@ -128,8 +199,7 @@ func TestConsumerValidator_NoWarnings(t *testing.T) { | |
| Credentials: []apisixv1alpha1.Credential{{ | ||
| Type: "jwt-auth", | ||
| SecretRef: &apisixv1alpha1.SecretReference{ | ||
| Name: "jwt-secret", | ||
| Namespace: &ns, | ||
| Name: "jwt-secret", | ||
| }, | ||
| }, { | ||
| Type: "key-auth", | ||
|
|
@@ -141,7 +211,7 @@ func TestConsumerValidator_NoWarnings(t *testing.T) { | |
| } | ||
|
|
||
| objs := []runtime.Object{ | ||
| &corev1.Secret{ObjectMeta: metav1.ObjectMeta{Name: "jwt-secret", Namespace: "auth"}}, | ||
| &corev1.Secret{ObjectMeta: metav1.ObjectMeta{Name: "jwt-secret", Namespace: "default"}}, | ||
| &corev1.Secret{ObjectMeta: metav1.ObjectMeta{Name: "key-secret", Namespace: "default"}}, | ||
| } | ||
|
|
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win
Preserve ReferenceGrant lookup failures separately from denial.
checkReferenceGrantmapscli.Listerrors tofalse, so this helper makes the webhook report a missingReferenceGrantwhen authorization could not be evaluated. Keep the fail-closed/no-Secret-probe behavior, but propagate or log the lookup error and emit a neutral validation warning instead.As per coding guidelines, errors must be properly handled and not silently swallowed.
🤖 Prompt for AI Agents
Source: Coding guidelines