Skip to content

mTLS / basic auth behaviour change due to go-openapi/runtime bump (v3.2.13 -> 3.2.14) #414

Description

@Nezisi

The bump from dataplane-api 3.2.13 to 3.2.14 led to an regression - the mTLS auth isn't working anymore.

I've used Claude to backtrace the issue, but can validate that 3.2.13 works, while 3.2.14 completely broke our setup as auth via mTLS isn't working anymore.

Claude backtrace:

This is the exact root cause — a two-line diff in go-openapi/runtime that silently flipped behavior with zero change to dataplaneapi's own code:
     
  middleware/context.go, v0.29.0 → v0.33.0:
  - if !applies || err != nil || !route.Authenticators.AllowsAnonymous() && usr == nil {
  + if !applies || err != nil || !route.Authenticators.AllowsAnonymous() && typeutils.IsZero(usr) {

  The mTLS-skip stub in dataplaneapi's main.go (unchanged across every 3.2.x release) signals "auth bypassed, allow it" by returning an empty-string principal: return true, "", nil. That's a deliberate, harmless-looking choice — as long as the framework only checks "is there a principal at all" (usr == nil), "" counts as present.

  v0.33.0 replaced that nil-check with typeutils.IsZero(usr) — a stricter check that also treats an empty string as "no real value." So the exact same stub that used to mean "let it through" now gets read as "nobody authenticated," and every request past the mTLS listener gets rejected with the generic errors.Unauthenticated("invalid credentials") — independent of whatever credentials were actually sent.

  The chain, fully confirmed by reproduction:
  1. dataplaneapi v3.2.13 → go-openapi/runtime v0.29.0 → stub's "" principal passes the nil check → mTLS path bypasses auth entirely (any/no credentials succeed) → worked, silently, because it never actually checked anything.
  2. dataplaneapi v3.2.14 bumped client-native (routine dependency update, nothing touched in dataplaneapi's own auth code) → transitively pulled go-openapi/runtime v0.33.0 → same "" principal now fails typeutils.IsZero → mTLS path now rejects everything.

If you need anything else, give a ping - I've reverted our version to 3.2.13 for now.

The basic setup is two containers, one running dataplane-api, other running HAProxy 3.2 latest - both have shared volume mounts, can access each other.

Dataplane API uses the master_worker_mode with master_runtime / master socket, haproxy defines the userlist, dataplaneapi has userlist + mtls auth (as userlist is required).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions