Docker Caddy v2 and Google oAuth

1. Caddy version (caddy version): caddy:2-alpine@sha256:2c73dc9258a8ebf0244d67701a655c1fc655cbfda642ab615e06b1a6039d5b2e

2. How I run Caddy:

a. System environment:

Docker

b. Command:

paste command here

c. Service/unit/compose file:

  caddy:
    image: caddy:2-alpine
    container_name: caddy
    restart: unless-stopped
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - /data/caddy/Caddyfile:/etc/caddy/Caddyfile
      - /data/caddy/data:/data
      - /data/caddy/config:/config
    networks:
      - web
    env_file:
      - ~/.docker/compose/.env

d. My complete Caddyfile or JSON config:

{
    email noreply@{$MY_DOMAIN}
    http_port 80 #changed from 8080 as that didn't work, even for basic auth
    https_port 443 #changed from 8443 as that didn't work, even for basic auth
    debug
}
(basic-auth) {
  basicauth {
    admin {$CADDYPASS}
  }
}
a.{$MY_DOMAIN} {
    reverse_proxy a:{$A_PORT}
    import basic-auth
}
b.{$MY_DOMAIN} {
    reverse_proxy b:{$B_PORT}
    import basic-auth
}
127.0.0.1, localhost {
  route /auth* {
    auth_portal {
      backends {
        google_oauth2_backend {
          method oauth2
          realm google
          provider google
          client_id {$GOOGLE_CLIENT_ID}
          client_secret {$GOOGLE_CLIENT_SECRET}
          scopes openid email profile
        }
      }
    }
  }
  route /sso/oauth2/google* {
    jwt {
      auth_url /auth/oauth2/google
    }
    respond * "google oauth2 sso" 200
  }
  route /tautulli* {
    jwt {
      primary yes
      trusted_tokens {
        static_secret {
          token_name access_token
          token_secret {$JWT_SECRET}
        }
      }
      auth_url /auth
      allow roles verified
    }
    reverse_proxy localhost:8181
  }
}

3. The problem I’m having:

Caddy working great for basic authentication. My challenge is adding Google oAuth to protect all my sites… every time I try to add I break something and need to fall back.

I am a complete noob to linux and docker.

4. Error messages and/or full log output:

My containers that worked using basic auth will not load and continuously refresh trying to work

5. What I already tried:

Various configs found online, read a bunch of posts… please be kind as I’m just trying to learn linux, docker and caddy.

6. Links to relevant resources:

Tried following this post but wasn’t successful: Google oAuth on V2

You can just remove these lines, 80 and 443 are the defaults.

FYI you can run Caddy from the docker image like this (no need to install it outside of docker for one-offs like that):

docker run -it --rm caddy caddy hash-password

For the JWT auth stuff, I’ll have to ask @greenpau to take a look :+1:

thanks @francislavoie for the tips.

I just took a look at the caddy logs and see the following errors:

run: adapting config using caddyfile: parsing caddyfile tokens for 'route': /etc/caddy/Caddyfile:30 - Error during parsing: unrecognized directive: auth_portal
{"level":"info","ts":1611525980.1044445,"msg":"using provided configuration","config_file":"/etc/caddy/Caddyfile","config_adapter":"caddyfile"}

Right, that would happen if you didn’t build Caddy with the plugins you wanted, which seems like the case, because you’re just using the vanilla Caddy image according to this:

Please follow the instructions on Docker Hub for writing a Dockerfile to build with plugins, i.e. the " Adding custom Caddy modules" section.

thanks again @francislavoie . I’m trying to follow the “Adding custom Caddy modules” section but not sure what to do. I navigated to Download Caddy and selected 3 packages:

I then downloaded the package… and now not sure what to do? Since I installed caddy through a docker-compose, do I just update the image and point to the newly downloaded package? Is there another way to include these packages through compose?

You need to make a file called Dockerfile on your computer, which has the contents as described there, with the --with arguments to the xcaddy command, which tell xcaddy which plugins to build with, then configure your docker-compose.yml to use that Dockerfile, like so: Compose file version 3 reference | Docker Documentation

this is starting to get more complex then I thought it would be :slight_smile: I was hoping for a simpler solution via ready made compose image option with some minor configuration needed if any. I’ll need to reconsider my options (i.e. Caddy vs. Traefik) to see which I can successfully install/configure. Caddy basic auth works for now.

It’s Docker which is making this complicated. If you’re not very familiar with Docker or Caddy, trying to do customizations with both at the same time is a big bite to chew all at once. Using Traefik won’t make things easier.

Are you sure you need Docker at all? Caddy is a static binary with no dependencies.

I don’t see what’s complex about building from a Dockerfile. That’s very typical.

Just make a Dockerfile like this:

FROM caddy:2.3.0-builder AS builder

RUN xcaddy build \
    --with github.com/greenpau/caddy-auth-jwt \
    --with github.com/greenpau/caddy-auth-portal \
    --with github.com/greenpau/caddy-trace

FROM caddy:2.3.0

COPY --from=builder /usr/bin/caddy /usr/bin/caddy

Save it beside your docker-compose.yml, then update your docker-compose.yml to this:

  caddy:
    build: .
    container_name: caddy
    restart: unless-stopped
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - /data/caddy/Caddyfile:/etc/caddy/Caddyfile
      - /data/caddy/data:/data
      - /data/caddy/config:/config
    networks:
      - web
    env_file:
      - ~/.docker/compose/.env

Then run docker-compose up -d, as normal. That’s it.

1 Like

thanks for the details @francislavoie . I tried your suggestion and got the following error:

Building caddy
Step 1/4 : FROM caddy:v2.3.0-builder AS builder
ERROR: Service 'caddy' failed to build : manifest for caddy:v2.3.0-builder not found: manifest unknown: manifest unknown

Did I miss a step?

Ah my bad, there’s no v in the tag, so just caddy:2.3.0 in both spots in the Dockerfile.

the build worked! thanks @francislavoie … truly appreciate all your help.

Logs are now clean of errors. Non Tautulli apps working with basic auth, but get the following error on the Tautulli app:

# This site can’t provide a secure connection

**tautulli.my.domain** sent an invalid response.

ERR_SSL_PROTOCOL_ERROR

I suspect this may be related to JWT as I don’t know how to create the token_secret . Appreciate any help and pointers to resolve.

Caddy logs below… not sure what’s wrong but still getting the same error as above.

{"level":"info","ts":1611714876.83874,"msg":"shutting down apps then terminating","signal":"SIGTERM"}
{"level":"info","ts":1611714877.8394282,"logger":"tls.cache.maintenance","msg":"stopped background certificate maintenance","cache":"0xc0000eda40"}
{"level":"info","ts":1611714878.3400848,"logger":"admin","msg":"stopped previous server"}
{"level":"info","ts":1611714878.3402386,"msg":"shutdown done","signal":"SIGTERM"}
{"level":"info","ts":1611714879.7864664,"msg":"using provided configuration","config_file":"/etc/caddy/Caddyfile","config_adapter":"caddyfile"}
{"level":"info","ts":1611714879.7932012,"logger":"admin","msg":"admin endpoint started","address":"tcp/localhost:2019","enforce_origin":false,"origins":["[::1]:2019","127.0.0.1:2019","localhost:2019"]}
{"level":"info","ts":1611714879.793753,"logger":"http","msg":"server is listening only on the HTTPS port but has no TLS connection policies; adding one to enable TLS","server_name":"srv0","https_port":443}
{"level":"info","ts":1611714879.7937832,"logger":"http","msg":"enabling automatic HTTP->HTTPS redirects","server_name":"srv0"}
{"level":"info","ts":1611714879.794786,"logger":"http.authentication.providers.jwt","msg":"provisioned plugin instance","instance_name":"jwt-2","started_at":1611714879.7947223}
{"level":"info","ts":1611714879.7954743,"logger":"http.handlers.auth_portal","msg":"JWT token name found","instance_name":"portal-1","token_name":"access_token"}
{"level":"warn","ts":1611714879.7954943,"logger":"http.handlers.auth_portal","msg":"JWT token origin not found, using default","instance_name":"portal-1"}
{"level":"info","ts":1611714879.796129,"logger":"tls.cache.maintenance","msg":"started background certificate maintenance","cache":"0xc000152000"}
{"level":"info","ts":1611714880.1549819,"logger":"http.handlers.auth_portal","msg":"successfully configured OAuth 2.0 backend","provider":"google","client_id":"maskingid.apps.googleusercontent.com","server_id":"","domain_name":"","metadata":{"authorization_endpoint":"https://accounts.google.com/o/oauth2/v2/auth","claims_supported":["aud","email","email_verified","exp","family_name","given_name","iat","iss","locale","name","picture","sub"],"code_challenge_methods_supported":["plain","S256"],"device_authorization_endpoint":"https://oauth2.googleapis.com/device/code","grant_types_supported":["authorization_code","refresh_token","urn:ietf:params:oauth:grant-type:device_code","urn:ietf:params:oauth:grant-type:jwt-bearer"],"id_token_signing_alg_values_supported":["RS256"],"issuer":"https://accounts.google.com","jwks_uri":"https://www.googleapis.com/oauth2/v3/certs","response_types_supported":["code","token","id_token","code token","code id_token","token id_token","code token id_token","none"],"revocation_endpoint":"https://oauth2.googleapis.com/revoke","scopes_supported":["openid","email","profile"],"subject_types_supported":["public"],"token_endpoint":"https://oauth2.googleapis.com/token","token_endpoint_auth_methods_supported":["client_secret_post","client_secret_basic"],"userinfo_endpoint":"https://openidconnect.googleapis.com/v1/userinfo"},"jwks_keys":{"03b2d22c2fecf873ed19e5b8cf704afb7e2ed4be":{"alg":"RS256","e":"AQAB","kid":"03b2d22c2fecf873ed19e5b8cf704afb7e2ed4be","kty":"RSA","n":"rKZ-1zdz_CoLekSynOtyWv6cPSSkV28Kb9kZZHyYL-yhkKnH_bHl8OpWiGxQiKP0ulLRIaq1IhSMetkZ8FfXH-iptIDu4lPb8gt0HQYkjcy3HoaKRXBw2F8fJQO4jQ-ufR4l-E0HRqwLywzdtAImNWmju3A4kx8s0iSGHGSHyE4EUdh5WKt-NMtfUPfB5v9_2bC-w6wH7zAEsI5nscMXnvz1u8w7g2_agyhKSK0D9OkJ02w3I4xLMlrtKEv2naoBGerWckKcQ1kBYUh6WASPdvTqX4pcAJi7Tg6jwQXIP1aEq0JU8C0zE3d33kaMoCN3SenIxpRczRzUHpbZ-gk5PQ==","use":"sig"},"783ec031c59e11f257d0ec15714ef607ce6a2a6f":{"alg":"RS256","e":"AQAB","kid":"783ec031c59e11f257d0ec15714ef607ce6a2a6f","kty":"RSA","n":"8Yb9hQAJroV6VKCsZZ6ylhVJqo0gsFa0Ca8ytzanKKWsCjo6RaqLjej7QKniTKwhUheCvbfLUqY9Mc6iMbA3gI-6_2lLQbbxExt6WUpf-CAEv1oUcnH_jA6X5Bdu4TdUX29s3D8J95d0eR8z8J1pe-7CjTBClx7lZd5xSRcoDXHDhzkwvc-EehYV46FsJyZCthLpAXvj81gpfycveavNFBMj-nlHKopZvhMcwbsK5JZ37wn2SxFigpfmIojheFVShJsNmLErHVC9HoHTC0iMibsKdyo7mk5QNM_rdBK-KjJhlQr8l7CktAqUJIQzkW8qC7tV7Hl0xicp6ylWZ-pj-Q==","use":"sig"},"eea1b1f42807a8cc136a03a3c16d29db8296daf0":{"alg":"RS256","e":"AQAB","kid":"eea1b1f42807a8cc136a03a3c16d29db8296daf0","kty":"RSA","n":"0zNdxOgV5VIpoeAfj8TMEGRBFg-gaZWz94ePR1yxTKzScHakH4F4wcMEyL0vNE-yW_u4pOl9E-hAalPa2tFv4fCVNMMkmKwcf0gm9wNFWXGakVQ8wER4iUg33MyUGOWj2RGX1zlZxCdFoZRtshLx8xcpL3F5Hlh6m8MqIAowWtusTf5TtYMXFlPaWLQgRXvoOlLZ-muzEuutsZRu-agdOptnUiAZ74e8BgaKN8KNEZ2SqP6vE4w16mgGHQjEPUKz9exxcsnbLru6hZdTDvXbX9IduabyvHy8vQRZsqlE9lTiOOOC9jwh27TXsD05HAXmNYiR6voekzEvfS88vnot2Q==","use":"sig"}}}
{"level":"info","ts":1611714880.1555655,"logger":"http.handlers.auth_portal","msg":"successfully validated OAuth 2.0 backend"}
{"level":"info","ts":1611714880.1607108,"logger":"http.handlers.auth_portal","msg":"JWT token validator provisioned","instance_name":"portal-1","access_list":[{"action":"allow","values":["anonymous","guest","*"],"claim":"roles"}]}
{"level":"info","ts":1611714880.160774,"logger":"http.handlers.auth_portal","msg":"provisioned plugin instance","instance_name":"portal-1","started_at":1611714879.7954555}
{"level":"info","ts":1611714880.161408,"logger":"http","msg":"enabling automatic TLS certificate management","domains":["uwish.cyou"]}
{"level":"info","ts":1611714880.163998,"msg":"autosaved config","file":"/config/caddy/autosave.json"}
{"level":"info","ts":1611714880.1640303,"msg":"serving initial configuration"}
{"level":"info","ts":1611714880.1774333,"logger":"tls","msg":"cleaned up storage units"}

and here’s my Caddyfile:

{
    email noreply@{$MY_DOMAIN}
}
{$MY_DOMAIN} {
  route /auth* {
    auth_portal {
      path /auth
      backends {
#        local_backend {
#          method local
#          path assets/conf/local/auth/user_db.json
#          realm local
#        }
        google_oauth2_backend {
          method oauth2
          realm google
          provider google
          client_id {$GOOGLE_CLIENT_ID}
          client_secret {$GOOGLE_CLIENT_SECRET}
          scopes openid email profile
          user myname@gmail.com add role verified
        }
      }
      jwt {
        token_name access_token
        token_secret 0e2fdcf8-6868-41a7-884b-7308795fc286
#        token_issuer e1008f2d-ccfa-4e62-bbe6-c202ec2988cc
        token_lifetime 3600
        token_sign_method HS256
      }
    }
  }
  route /sso/oauth2/google* {
    jwt {
      auth_url /auth/oauth2/google
    }
    respond * "google oauth2 sso" 200
  }
  route /tautulli* {
    jwt {
      primary yes
      trusted_tokens {
        static_secret {
          token_name access_token
          token_secret 0e2fdcf8-6868-41a7-884b-7308795fc286
        }
      }
      auth_url /auth
      allow roles verified
    }
    reverse_proxy {$MY_DOMAIN}:8181
  }
}

I’ll have to defer to @greenpau for the auth stuff, I don’t have any experience with it.

@noob do you still need help on this?

Thanks for following up. I gave up after struggling for over a week and ended up deploying traffic instead. I’d be open to trying again with a little help and guidance.

This topic was automatically closed after 30 days. New replies are no longer allowed.