Skip to content

Admin API allowlist bypassed when ingress-controller enabled (+ missing IPv6) #921

@twellck

Description

@twellck

Description

The admin API allowlist logic in templates/configmap.yaml has two critical issues when ingress-controller.enabled: true:

  1. Security bypass: User-specified allowlist is overridden by unconditionally appending 0.0.0.0/0
  2. Missing IPv6 support: Only 0.0.0.0/0 is added, breaking IPv6-only clusters

Environment

  • Helm Chart Version: 2.12.4
  • Kubernetes Version: 1.34
  • Helm Version: 3.18.4 (through Terraform's Helm provider)

Current Behavior

The template always appends 0.0.0.0/0 when ingress-controller is enabled:

allow_admin:
{{- if .Values.apisix.admin.allow.ipList }}
  {{- range $ips := .Values.apisix.admin.allow.ipList }}
  - {{ $ips }}
  {{- end }}
{{- else }}
  - 0.0.0.0/0
{{- end}}
{{- if (index .Values "ingress-controller" "enabled") }}
  - 0.0.0.0/0  # Always appended - overrides user security
{{- end}}

Default values.yaml:

apisix:
  admin:
    allow:
      ipList:
        - 127.0.0.1/24  # Secure default - localhost only

⚠️ Edge Case Note:
Currently, an empty ipList: [] defaults to 0.0.0.0/0 via the {{- else }} branch. This is likely unintentional behavior, but exists in production. All proposed options below change this edge case.

Problem 1: Security Bypass

Steps to Reproduce

  1. Configure values.yaml with restricted allowlist:
    apisix:
      admin:
        allow:
          ipList:
            - 10.244.0.0/16  # Restrict to pod network only
    ingress-controller:
      enabled: true
  2. Deploy the chart: helm install apisix apisix/apisix -f values.yaml
  3. Check the generated ConfigMap: kubectl get configmap apisix -o yaml

Expected Behavior

Admin API should only be accessible from 10.244.0.0/16:

allow_admin:
  - 10.244.0.0/16

Actual Behavior

User's restriction is bypassed:

allow_admin:
  - 10.244.0.0/16
  - 0.0.0.0/0  # Unconditionally added - allows all traffic

Impact

Users cannot properly secure the admin API through the allowlist when using ingress-controller. Security hardening attempts are silently ignored.

Example with default values:

# User expects: localhost only
allow_admin:
  - 127.0.0.1/24    # Secure default
  - 0.0.0.0/0       # Auto-added - silently weakens security!

Problem 2: IPv6 Not Supported

Steps to Reproduce

  1. Deploy to an IPv6-only Kubernetes cluster
  2. Enable ingress-controller with default settings
  3. Check ingress-controller pod logs

Expected Behavior

Ingress-controller should be able to access the admin API using its IPv6 address.

Actual Behavior

Access is denied because the allowlist only includes 0.0.0.0/0 (IPv4):

[error] access forbidden by rule, client: [2001:db8::1]

Impact

Chart is non-functional in IPv6-only clusters without manual workarounds.


Proposed Solutions

I see three potential approaches, each with different tradeoffs:

Option A: Always Append (Fixes IPv6, Maintains Current Behavior)

allow_admin:
{{- range $ips := .Values.apisix.admin.allow.ipList }}
  - {{ $ips | quote }}
{{- end }}
{{- if (index .Values "ingress-controller" "enabled") }}
  - 0.0.0.0/0
  - "::/0"
{{- end }}

Pros:

  • Minimal change - most backward compatible
  • Fixes IPv6 support
  • Simple logic

Cons:

  • Still overrides user security (documents the issue but doesn't fix it)
  • Users with custom ipList still get wildcards appended

Breaking Changes:

  • Empty ipList: [] no longer defaults to allow-all (see edge case note above)
  • Otherwise maintains current behavior + adds IPv6

Option B: Respect User Configuration (Most Secure)

allow_admin:
{{- if .Values.apisix.admin.allow.ipList }}
  {{- range $ips := .Values.apisix.admin.allow.ipList }}
  - {{ $ips | quote }}
  {{- end }}
{{- else }}
{{- if (index .Values "ingress-controller" "enabled") }}
  - 0.0.0.0/0
  - "::/0"
{{- end }}
{{- end }}

Pros:

  • Respects user security choices
  • No more silent security bypass
  • IPv6 support in defaults

Cons:

  • Breaking change for users relying on the current buggy behavior
  • Users must explicitly allow ingress-controller access when using custom ipList

Breaking Changes:

  • Empty ipList: [] no longer defaults to allow-all (see edge case note above)
  • Users with custom ipList who relied on auto-added 0.0.0.0/0 must add it explicitly
apisix:
  admin:
    allow:
      ipList:
        - 10.244.0.0/16      # Custom restriction
        - 0.0.0.0/0          # Must add explicitly for ingress-controller (if cidr is unknown)
        - "::/0"             # IPv6 support

Option C: Make It Configurable (Future-Proof)

Add new value to control behavior:

apisix:
  admin:
    allow:
      ipList:
        - 127.0.0.1/24
      autoAddIngressControllerAccess: true  # New option (default: true)

Template logic:

allow_admin:
{{- range $ips := .Values.apisix.admin.allow.ipList }}
  - {{ $ips | quote }}
{{- end }}
{{- if and (index .Values "ingress-controller" "enabled") .Values.apisix.admin.allow.autoAddIngressControllerAccess }}
  - 0.0.0.0/0
  - "::/0"
{{- end }}

Pros:

  • Gives users explicit control
  • Backward compatible (default: true maintains current behavior)
  • Clear and self-documenting
  • Easy to disable for security-conscious users

Cons:

  • More complex (adds new configuration option)
  • Requires documentation updates

Breaking Changes:

  • Empty ipList: [] no longer defaults to allow-all (see edge case note above)

Recommendation

I'm inclined to suggest Option C as it balances all concerns:

  • Fixes IPv6 support
  • Maintains backward compatibility
  • Gives users control over security
  • Makes behavior explicit and documented

However, I'm happy to implement whichever approach is preferred.

I'm also happy to submit a PR for this fix, combined with my other issue (#920) about IPv6 quoting since both touch the same ConfigMap template file.


Note: AI helped format this issue. The issue identification and proposed solutions are my own work.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions