Specific URL pattern for forward_auth path_regexp not getting matched

1. The problem I’m having:

The problem I’m having is that Caddy matches only some URL paths, but not all, for the following regex:

path_regexp('(?:[^.][?])|(?:/$)')

That regex should match the following paths (anything with a question mark in the URL and anything ending with a slash):

/?h
/u/?h
/u/user/?h
/u/user/file.mp3?delete
/u/user/file.part.mp3?delete
/u/user/

and NOT match the following paths:

/u/user/file.mp3
/u/user/folder/file.mp3
/u/user/folder/filename_noextension
/u/user/folder/filename-with-dashes

2. Error messages and/or full log output:

The problem is that the /u/user/file.mp3?delete request (i.e. containing a question mark) skips the auth flow, when it SHOULD match the regex in my caddyfile and get routed to authentik.

3. Caddy version:

docker, v2.10.2

4. How I installed and ran Caddy:

kubernetes, deployment, 1 pod

a. System environment:

ubuntu 64bit, kubernetes, rke2

b. Command:

no command used. just kubernetes deployment, 1 pod

c. Service/unit/compose file:

spec:
  template:
    spec:
      containers:
        - image: caddy:latest
          imagePullPolicy: Always
          name: caddy
          ports:
            - containerPort: 80
              name: http
              protocol: TCP
            - containerPort: 443
              name: https
              protocol: TCP
          resources: {}
          securityContext:
            allowPrivilegeEscalation: false
            privileged: false
            readOnlyRootFilesystem: false
            runAsNonRoot: false
          terminationMessagePath: /dev/termination-log
          terminationMessagePolicy: File
          volumeMounts:
            - mountPath: /etc/caddy
              name: caddyfile-vol
            - mountPath: /tls
              name: caddy-tls-vol
      dnsPolicy: ClusterFirst
      restartPolicy: Always
      schedulerName: default-scheduler
      securityContext: {}
      terminationGracePeriodSeconds: 30
      volumes:
        - configMap:
            defaultMode: 420
            name: caddyfile
            optional: false
          name: caddyfile-vol
        - name: caddy-tls-vol
          secret:
            defaultMode: 420
            optional: false
            secretName: caddy-tls

d. My complete Caddy config:

http://copy.my.site {

    #@nologinreq not path_regexp (?:[^.][?])|(?:[/]$)|(?:outpost\.goauthentik\.io)

    #handle @nologinreq {

    #    reverse_proxy http://copyparty.copyparty.svc.cluster.local:3923 {

    #        header_up X-Forwarded-Proto https

    #    }

    #}

    #tls /tls/tls.crt /tls/tls.key

    # directive execution order is only as stated if enclosed with route.

    route {

        # always forward outpost path to actual outpost

        reverse_proxy /outpost.goauthentik.io/* http://authentik-server.authentik.svc.cluster.local:80



        # forward authentication to outpost

        # except file serving for public https://caddy.community/t/how-to-exclude-a-path-from-forward-auth/20051

        @loginrequired `path_regexp('(?:[^.][?])|(?:/$)')`

        #@loginreq `(method('GET') || method('POST')) && path_regexp('(?:[^.][?])|(?:/$)')`

        #@example `method('GET') && path_regexp('^/v\d\.\d+/*') && path_regexp('^/asdf/*')`

        # not path_regexp (?:\.)[a-zA-Z0-9_\-]*[^?][a-zA-Z0-9=&:,./\-]*$

        # path /u/*

        forward_auth @loginrequired http://authentik-server.authentik.svc.cluster.local:80 {

            uri /outpost.goauthentik.io/auth/caddy



            # capitalization of the headers is important, otherwise they will be empty

            copy_headers X-Authentik-Username X-Authentik-Groups X-Authentik-Entitlements X-Authentik-Email X-Authentik-Name X-Authentik-Uid X-Authentik-Jwt X-Authentik-Meta-Jwks X-Authentik-Meta-Outpost X-Authentik-Meta-Provider X-Authentik-Meta-App X-Authentik-Meta-Version



            # optional, in this config trust all private ranges, should probably be set to the outposts IP

            trusted_proxies private_ranges

        }



        # actual site configuration below, for example

        reverse_proxy http://copyparty.copyparty.svc.cluster.local:3923 {

            header_up X-Forwarded-Proto https

        }

    }

}

5. Links to relevant resources:

using authentik for forward_auth - following this: Caddy | authentik

because i need to forward headers once a user is logged in (x-authentik-username etc)

also these are the URL params I’m trying to put behind auth - mainly ?h and the PUT ones copyparty/docs/devnotes.md at hovudstraum · 9001/copyparty · GitHub

The question mark isn’t part of the HTTP path. It’s part of the URI.

In other words:

uri = path ? query

So your regex will never match a question mark in the path, because it isn’t there.

In your case, I’d look at using a named matcher by combining request matchers like path or path_regexp with query. Or you could use an expression matcher to check whether the uri contains a question mark, etc.

1 Like

Sorry, I typed my previous message on my phone. I’ve got a bit more time now, so here’s a quick matcher for your use case. Feel free to adjust it as needed.

Caddyfile

:80 {
	@question_slash `{uri}.contains('?') || {path}.endsWith('/')`

	respond @question_slash "Matched!"
	respond "Not Matched!"
}

Test

$ curl http://localhost/?h
Matched!

$ curl http://localhost/u/?h
Matched!

$ curl http://localhost/u/user/?h
Matched!

$ curl http://localhost/u/user/file.mp3?delete
Matched!

$ curl http://localhost/u/user/file.part.mp3?delete
Matched!

$ curl http://localhost/u/user/
Matched!
$ curl http://localhost/u/user/file.mp3
Not Matched!

$ curl http://localhost/u/user/folder/file.mp3
Not Matched!

$ curl http://localhost/u/user/folder/filename_noextension
Not Matched!

$ curl http://localhost/u/user/folder/filename-with-dashes
Not Matched!
5 Likes