1. Output of caddy version
:
v2.6.1 h1:EDqo59TyYWhXQnfde93Mmv4FJfYe00dO60zMiEt+pzo=
2. How I run Caddy:
Official docker image: caddy:2.6.1-alpine
a. System environment:
Docker Desktop for Mac
4.3.1 (72247)
b. Command:
Paste command here.
c. Service/unit/compose file:
version: '3.4'
services:
caddy:
build:
context: .
dockerfile: Dockerfile
restart: unless-stopped
ports:
- "8080:8080"
volumes:
- caddy_data:/data
- caddy_config:/config
env_file:
- '.env'
networks:
default:
aliases:
- 'api.mycompany.com'
volumes:
caddy_data:
# external: true
caddy_config:
networks:
default:
external:
name: development
d. My complete Caddy config:
# global options
{
auto_https off
debug
log {
output stdout
format console
level DEBUG
}
servers {
metrics
}
}
# reusable snippets
(oauth2_protected) {
oauth2_token_introspection {
token_location bearer_token
introspection_endpoint {$INTROSPECTION_SERVICE_URI}/v1/oauth2/introspect
introspection_authentication_strategy client_credentials
introspection_client_id {$INTROSPECTION_CLIENT_ID}
introspection_client_secret {$INTROSPECTION_CLIENT_SECRET}
introspection_timeout 1000
}
}
http://{$API_HOST}:8080 {
@protected_paths {
path /v1/hello
path /v1/goodbye
}
route {
import oauth2_protected
reverse_proxy @protected_paths {$UPSTREAM_A_URI}
}
@public_paths {
path /v1/foo
path /v1/bar
}
route {
reverse_proxy @public_paths {$UPSTREAM_B_URI}
}
}
3. The problem I’m having:
I’m attempting to use Caddy as an API Gateway that sits in front of (e.g. reverse_proxy’s) a bunch of different backends. Some of the paths should be protected (I’m using an OAuth2 Token Introspection module here to make sure the request contains a valid OAuth2 token before reverse proxying to the backend, but this is sort of irrelevant for the issue I’m having) while others should be publicly available and don’t require this protection.
Since there are lots of different paths that get mapped to a specific backend, I assumed that I could put all of the paths that should get protected into a named matcher (e.g. @protected_paths
) to avoid having to duplicate the oauth2_protected
and reverse_proxy
directives over and over again for each path. In other words I’m trying avoid duplication like this:
route /v1/hello {
import oauth2_protected
reverse_proxy {$UPSTREAM_A_URI}
}
route /v1/goodbye {
import oauth2_protected
reverse_proxy {$UPSTREAM_A_URI}
}
And similarly set up named matchers for the public paths as well.
However, when set up this way, if I make a request to one of the public paths (say, /v1/foo) that should be served by upstream B (publicly available, not protected by OAuth), the OAuth2 module is being invoked on that path, so it seems to be getting matched to this route
for some reason:
route {
import oauth2_protected
reverse_proxy @protected_paths {$UPSTREAM_A_URI}
}
This leads me to believe that I’m simply doing something wrong, and either named matchers can’t be used this way, route
s can’t be used this way, or both?
How do I go about mapping a bunch of path
handlers to a specific reverse_proxy
backend?
4. Error messages and/or full log output:
N/A
5. What I already tried:
I tried changing the route
directives to handle
directives, as the docs mention that handle
directives are “mutually exclusive” from other handle
blocks, but it didn’t like the oauth2_token_introspection
inside the handle
directive:
caddy_1 | Error: adapting config using caddyfile: parsing caddyfile tokens for 'handle': directive 'oauth2_token_introspection' is not an ordered HTTP handler, so it cannot be used here