thomseddon / traefik-forward-auth

Minimal forward authentication service that provides Google/OpenID oauth based login and authentication for the traefik reverse proxy
MIT License
2.17k stars 410 forks source link

Google OAuth Redirection to 404 #208

Closed typkrft closed 4 years ago

typkrft commented 4 years ago

I seem to be having an issue with Google's OAuth. No Auth and Basic Auth are working as expected but OAuth is not. When I go to a url for example log.example.com I am redirected to sign in with google. Once I am signed in it just sends me to a 404 page. Any help would be greatly appreciated!

I've looked at the Traefik Dash it reports no errors with oauth.example.com I've looked in OAtuh Container it has no logs. I've looked in Traefik logs, but I am not sure exactly what to look for here.

Configs:

Note: all instances of example.com are replaced by my domain The OAuth Container has no logs, but is running.

Google App

Project = Treafik Authentication
OAuth 2.0 client Name = Traefik
URI = https://oauth.example.com/_oauth
Authorized Domain = example.com

Cloudflare

A  = example.com => Public IP
CNAME = oauth.example.com => example.com
CNAME=  *.example.com => example.com

systemStack.yml

version: "3.7"
########################### NETWORKS
networks:
  t2_proxy:
    external:
      name: t2_proxy
  default:
    driver: bridge
########################### SERVICES
services:
# All services / apps go below this line
# Traefik 2 - Reverse Proxy
  traefik:
    container_name: traefik
    image: traefik:2.2.1 # the chevrotin tag refers to v2.2.x but introduced a breaking change in 2.2.2
    restart: unless-stopped
    command: # CLI arguments
      - --global.checkNewVersion=true
      - --global.sendAnonymousUsage=true
      - --entryPoints.http.address=:80
      - --entryPoints.https.address=:443
        # Allow these IPs to set the X-Forwarded-* headers - Cloudflare IPs: https://www.cloudflare.com/ips/
      - --entrypoints.https.forwardedHeaders.trustedIPs=173.245.48.0/20,103.21.244.0/22,103.22.200.0/22,103.31.4.0/22,141.101.64.0/18,108.162.192.0/18,190.93.240.0/20,188.114.96.0/20,197.234.240.0/22,198.41.128.0/17,162.158.0.0/15,104.16.0.0/12,172.64.0.0/13,131.0.72.0/22
      - --entryPoints.traefik.address=:8080
      - --api=true
#      - --api.insecure=true
#      - --serversTransport.insecureSkipVerify=true
      - --log=true
      - --log.level=DEBUG # (Default: error) DEBUG, INFO, WARN, ERROR, FATAL, PANIC
      - --accessLog=true
      - --accessLog.filePath=/traefik.log
      - --accessLog.bufferingSize=100 # Configuring a buffer of 100 lines
      - --accessLog.filters.statusCodes=400-499
      - --providers.docker=true
      - --providers.docker.endpoint=unix:///var/run/docker.sock
      - --providers.docker.defaultrule=Host(`{{ index .Labels "com.docker.compose.service" }}.$DOMAINNAME`)
      - --providers.docker.exposedByDefault=false
      - --providers.docker.network=t2_proxy
      - --providers.docker.swarmMode=false
      - --providers.file.directory=/rules # Load dynamic configuration from one or more .toml or .yml files in a directory.
#      - --providers.file.filename=/path/to/file # Load dynamic configuration from a file.
      - --providers.file.watch=true # Only works on top level files in the rules folder
#      - --certificatesResolvers.dns-cloudflare.acme.caServer=https://acme-staging-v02.api.letsencrypt.org/directory # LetsEncrypt Staging Server - uncomment when testing
      - --certificatesResolvers.dns-cloudflare.acme.email=$CLOUDFLARE_EMAIL
      - --certificatesResolvers.dns-cloudflare.acme.storage=/acme.json
      - --certificatesResolvers.dns-cloudflare.acme.dnsChallenge.provider=cloudflare
    networks:
      t2_proxy:
        ipv4_address: 192.168.90.254 # You can specify a static IP
#    networks:
#      - t2_proxy
    security_opt:
      - no-new-privileges:true
    ports:
      - target: 80
        published: 80
        protocol: tcp
        mode: host
      - target: 443
        published: 443
        protocol: tcp
        mode: host
      - target: 8080
        published: 8080
        protocol: tcp
        mode: host
    volumes:
      - $DOCKERDIR/System/traefik2/rules:/rules 
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - $DOCKERDIR/System/traefik2/acme/acme.json:/acme.json 
      - $DOCKERDIR/System/traefik2/traefik.log:/traefik.log 
      - $DOCKERDIR/System/traefik2/shared:/shared
    environment:
      - CF_API_EMAIL=$CLOUDFLARE_EMAIL
      - CF_API_KEY=$CLOUDFLARE_API_KEY
    labels:
    # SUI
      - sui.enabled=true
      - sui.name=Traefik
      - sui.icon=traffic-light
      - sui.url=https://traefik.example.com
    # Traefik
      - "traefik.enable=true"
      # HTTP-to-HTTPS Redirect
      - "traefik.http.routers.http-catchall.entrypoints=http"
      - "traefik.http.routers.http-catchall.rule=HostRegexp(`{host:.+}`)"
      - "traefik.http.routers.http-catchall.middlewares=redirect-to-https"
      - "traefik.http.middlewares.redirect-to-https.redirectscheme.scheme=https"
      # HTTP Routers
      - "traefik.http.routers.traefik-rtr.entrypoints=https"
      - "traefik.http.routers.traefik-rtr.rule=Host(`traefik.$DOMAINNAME`)"
      - "traefik.http.routers.traefik-rtr.tls=true"
#      - "traefik.http.routers.traefik-rtr.tls.certresolver=dns-cloudflare" # Comment out this line after first run of traefik to force the use of wildcard certs
      - "traefik.http.routers.traefik-rtr.tls.domains[0].main=$DOMAINNAME"
      - "traefik.http.routers.traefik-rtr.tls.domains[0].sans=*.$DOMAINNAME"
#      - "traefik.http.routers.traefik-rtr.tls.domains[1].main=$SECONDDOMAINNAME" # Pulls main cert for second domain
#      - "traefik.http.routers.traefik-rtr.tls.domains[1].sans=*.$SECONDDOMAINNAME" # Pulls wildcard cert for second domain
      ## Services - API
      - "traefik.http.routers.traefik-rtr.service=api@internal"  
      ## Middlewares
#      - "traefik.http.routers.traefik-rtr.middlewares=chain-oauth@file" 

# Portainer - WebUI for Containers
  portainer:
    container_name: portainer
    image: portainer/portainer-ce:latest
    restart: unless-stopped
    command: -H unix:///var/run/docker.sock
    networks:
      - t2_proxy
    security_opt:
      - no-new-privileges:true
    ports:
      - "$PORTAINER_PORT:9000"
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - $DOCKERDIR/System/portainer/data:/data 
    environment:
      - TZ=$TZ
    labels:
    # SUI
      - sui.enabled=true
      - sui.name=Portainer
      - sui.icon=docker
      - sui.url=https://portainer.example.com
      # Watchtower
      - "com.centurylinklabs.watchtower.enable=true"
      # Traefik
      - "traefik.enable=true"
      ## HTTP Routers
      - "traefik.http.routers.portainer-rtr.entrypoints=https"
      - "traefik.http.routers.portainer-rtr.rule=Host(`portainer.$DOMAINNAME`)"
      - "traefik.http.routers.portainer-rtr.tls=true"
      ## Middlewares
      - "traefik.http.routers.portainer-rtr.middlewares=chain-no-auth@file" # No Authentication
#      - "traefik.http.routers.portainer-rtr.middlewares=chain-basic-auth@file" # Basic Authentication
#      - "traefik.http.routers.portainer-rtr.middlewares=chain-oauth@file" # Google OAuth 2.0
      ## HTTP Services
      - "traefik.http.routers.portainer-rtr.service=portainer-svc"
      - "traefik.http.services.portainer-svc.loadbalancer.server.port=9000"

# Dozzle 
  dozzle:
    container_name: dozzle
    image: amir20/dozzle:latest
    networks:
      - t2_proxy
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
    ports:
      - $DOZZLE_PORT:8080
    labels:    
    # SUI
      - sui.enabled=true
      - sui.name=Dozzle
      - sui.icon=clipboard-list-outline
      - sui.url=https://log.example.com
      # Watchtower
      - "com.centurylinklabs.watchtower.enable=true"
      # Traefik
      - "traefik.enable=true"
      ## HTTP Routers
      - "traefik.http.routers.dozzle-rtr.entrypoints=https"
      - "traefik.http.routers.dozzle-rtr.rule=Host(`log.$DOMAINNAME`)"
      - "traefik.http.routers.dozzle-rtr.tls=true"
      ## Middlewares
#      - "traefik.http.routers.dozzle-rtr.middlewares=chain-oauth@file"
      - "traefik.http.routers.dozzle-rtr.middlewares=chain-oauth@file"
#      - "traefik.http.routers.dozzle-rtr.middlewares=chain-basic-auth@file" # Basic Authentication
      ## HTTP Services
      - "traefik.http.routers.dozzle-rtr.service=dozzle-svc"
      - "traefik.http.services.dozzle-svc.loadbalancer.server.port=8080"

# Watchtower      
  watchtower:
    container_name: watchtower
    command: --label-enable --schedule "0 2 * * 0"
    image: containrrr/watchtower
    labels:
      - "com.centurylinklabs.watchtower.enable=true"
    network_mode: none
    restart: always
    environment:
      WATCHTOWER_NOTIFICATIONS: email
      WATCHTOWER_NOTIFICATION_EMAIL_SERVER_USER: user@example.com
      WATCHTOWER_NOTIFICATION_EMAIL_SERVER_PASSWORD: SECRET
      WATCHTOWER_NOTIFICATION_EMAIL_FROM: notifications@example.com
      WATCHTOWER_NOTIFICATION_EMAIL_TO: user@example.com
      # you have to use a network alias here, if you use your own certificate
      WATCHTOWER_NOTIFICATION_EMAIL_SERVER: smtp.example.com
      WATCHTOWER_NOTIFICATION_EMAIL_SERVER_PORT: 587
      WATCHTOWER_NOTIFICATION_EMAIL_DELAY: 2    
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock

  apprise:
    container_name: apprise
    image: caronc/apprise:latest
    networks:
      - t2_proxy
    security_opt:
      - no-new-privileges:true  
    volumes:
      - $DOCKERDIR/System/apprise:/config
    ports:
      - $APPRISE_PORT:8000
    labels:    
    # SUI
      - sui.enabled=true
      - sui.name=Apprise
      - sui.icon=bell-ring
      - sui.url=https://notify.example.com
      # Watchtower
      - "com.centurylinklabs.watchtower.enable=true"
      # Traefik
      - "traefik.enable=true"
      ## HTTP Routers
      - "traefik.http.routers.apprise-rtr.entrypoints=https"
      - "traefik.http.routers.apprise-rtr.rule=Host(`notify.$DOMAINNAME`)"
      - "traefik.http.routers.apprise-rtr.tls=true"
      ## Middlewares
#      - "traefik.http.routers.apprise-rtr.middlewares=chain-oauth@file"
      - "traefik.http.routers.apprise-rtr.middlewares=chain-basic-auth@file" # Basic Authentication
      ## HTTP Services
      - "traefik.http.routers.apprise-rtr.service=apprise-svc"
      - "traefik.http.services.apprise-svc.loadbalancer.server.port=8000"

  # Google OAuth - Single Sign On using OAuth 2.0 for Traefik 2.2
  oauth:
    container_name: oauth
    image: thomseddon/traefik-forward-auth:latest
    # image: thomseddon/traefik-forward-auth:2.1-arm # Use this image with Raspberry Pi
    restart: unless-stopped
    networks:
      - t2_proxy
    security_opt:
      - no-new-privileges:true
    environment:
      - CLIENT_ID=$GOOGLE_CLIENT_ID
      - CLIENT_SECRET=$GOOGLE_CLIENT_SECRET
      - SECRET=$OAUTH_SECRET
      - COOKIE_DOMAIN=$DOMAINNAME
      - INSECURE_COOKIE=false
      - AUTH_HOST=oauth.$DOMAINNAME
      - URL_PATH=/_oauth
      - WHITELIST=$MY_EMAIL
      - LOG_LEVEL=warn
      - LOG_FORMAT=text
      - LIFETIME=2592000 # 30 days
      - DEFAULT_ACTION=auth
      - DEFAULT_PROVIDER=google
    labels:
      - "traefik.enable=true"
      ## HTTP Routers
      - "traefik.http.routers.oauth-rtr.entrypoints=https"
      - "traefik.http.routers.oauth-rtr.rule=Host(`oauth.$DOMAINNAME`)"
      ## Middlewares
      - "traefik.http.routers.oauth-rtr.middlewares=chain-oauth@file"
      ## HTTP Services
      - "traefik.http.routers.oauth-rtr.service=oauth-svc"
      - "traefik.http.services.oauth-svc.loadbalancer.server.port=4181"

# Can't Get to Work / Unused 

# Wireshark
#  wireshark:
#    image: ghcr.io/linuxserver/wireshark
#    container_name: wireshark
#    cap_add:
#      - NET_ADMIN
#    network_mode: host
#    environment:
#      - PUID=$PUID
#      - PGID=$PGID
#      - TZ=$TZ
#    volumes:
#      - $DOCKERDIR/System/wireshark:/config
#    ports:
#      - $WIRESHARK_PORT:3000 #optional
#    restart: unless-stopped
#    labels:
#      # Watchtower
#      - "com.centurylinklabs.watchtower.enable=true"
#      # Traefik
#      - "traefik.enable=true"
#      ## HTTP Routers
#      - "traefik.http.routers.wireshark-rtr.entrypoints=https"
#      - "traefik.http.routers.wireshark-rtr.rule=Host(`ws.$DOMAINNAME`)"
#      - "traefik.http.routers.wireshark-rtr.tls=true"
#      ## Middlewares
##      - "traefik.http.routers.wireshark-rtr.middlewares=chain-oauth@file"
#      - "traefik.http.routers.wireshark-rtr.middlewares=chain-basic-auth@file" # Basic Authentication
#      ## HTTP Services
#      - "traefik.http.routers.wireshark-rtr.service=wireshark-svc"
#      - "traefik.http.services.wireshark-svc.loadbalancer.server.port=3000"

midllewares.toml

[http.middlewares]
  [http.middlewares.middlewares-basic-auth]
    [http.middlewares.middlewares-basic-auth.basicAuth]
#      users = [
#        "user:$apsdfswWvC/6.$E3FtsfTntPC0wVJ7IUVtX1",
#      ]
      realm = "Traefik2 Basic Auth"
      usersFile = "/shared/.htpasswd" #be sure to mount the volume through docker-compose.yml

  [http.middlewares.middlewares-rate-limit]
    [http.middlewares.middlewares-rate-limit.rateLimit]
      average = 100
      burst = 50

# Available Header Options: 
#####https://github.com/unrolled/secure#available-options
#####https://docs.traefik.io/middlewares/headers/
# A great resource for these headers is your preferred browser's docs. Firefox: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers
# https://developers.google.com/search/reference/robots_meta_tag
# https://github.com/OWASP/CheatSheetSeries/blob/master/cheatsheets/Clickjacking_Defense_Cheat_Sheet.md
# CSP for VNC: https://github.com/cockpit-project/cockpit/pull/5932
# Check headers here, don't include OAuth when checking headers, otherwise you are checking google's headers: https://securityheaders.com
# or check them here: https://observatory.mozilla.org/

# CAUTION: Any headers defined in docker-compose (yml) will OVERWRITE ALL of the headers defined below.

  [http.middlewares.middlewares-secure-headers]
    [http.middlewares.middlewares-secure-headers.headers]
      accessControlAllowMethods= ["GET", "OPTIONS", "PUT"]
      accessControlMaxAge = 100
      hostsProxyHeaders = ["X-Forwarded-Host"]
      sslRedirect = true
      stsSeconds = 63072000
      stsIncludeSubdomains = true
      stsPreload = true
      forceSTSHeader = true
#      frameDeny = true #overwritten by customFrameOptionsValue
      customFrameOptionsValue = "allow-from https:example.com" #CSP takes care of this but may be needed for organizr. 
      contentTypeNosniff = true 
      browserXssFilter = true 
#      sslForceHost = true # add sslHost and all of the 
#      sslHost = "example.com"
      referrerPolicy = "same-origin" 
#      Setting contentSecurityPolicy is more secure but it can break things. Proper auth will reduce the risk.
#      the below line also breaks some apps due to 'none' - sonarr, radarr, etc.
#      contentSecurityPolicy = "frame-ancestors '*.example.com:*';object-src 'none';script-src 'none';"
      featurePolicy = "camera 'none'; geolocation 'none'; microphone 'none'; payment 'none'; usb 'none'; vr 'none';" 
      [http.middlewares.middlewares-secure-headers.headers.customResponseHeaders]
        X-Robots-Tag = "none,noarchive,nosnippet,notranslate,noimageindex,"
        server = ""

  [http.middlewares.middlewares-oauth]
    [http.middlewares.middlewares-oauth.forwardAuth]
      address = "http://oauth:4181" # Make sure you have the OAuth service in docker-compose.yml
      trustForwardHeader = true
      authResponseHeaders = ["X-Forwarded-User"]

  [http.middlewares.middlewares-authelia]
    [http.middlewares.middlewares-authelia.forwardAuth]
      address = "http://authelia:9091/api/verify?rd=https://authelia.example.com"
      trustForwardHeader = true
      authResponseHeaders = ["Remote-User", "Remote-Groups"]

middlewares-chain.toml

[http.middlewares]
  [http.middlewares.chain-no-auth]
    [http.middlewares.chain-no-auth.chain]
      middlewares = [ "middlewares-rate-limit", "middlewares-secure-headers"]

  [http.middlewares.chain-basic-auth]
    [http.middlewares.chain-basic-auth.chain]
      middlewares = [ "middlewares-rate-limit", "middlewares-secure-headers", "middlewares-basic-auth"]

  [http.middlewares.chain-oauth]
    [http.middlewares.chain-oauth.chain]
      middlewares = [ "middlewares-rate-limit", "middlewares-secure-headers", "middlewares-oauth"]

  [http.middlewares.chain-authelia]
    [http.middlewares.chain-authelia.chain]
      middlewares = [ "middlewares-rate-limit", "middlewares-secure-headers", "middlewares-authelia"]

.env Note: SECRET is used to obsfucate it is explicit in my file.

# Universal Variables
  # System
    PUID=1026
    PGID=100
    TZ=America/New_York
  # Directories
    USERDIR=/var/services/homes/SECRET
    DOCKERDIR=/volume1/docker
    DL_DIR=/volume1/Downloads
    MEDIA_DIR=/volume1/Media
  # Docker
    COMPOSE_HTTP_TIMEOUT=200
  # Networking
    DOMAINNAME=example.com

# System Apps
  CLOUDFLARE_EMAIL=SECRET
  CLOUDFLARE_API_KEY=SECRET
  PORTAINER_PORT=9000
  WIRESHARK_PORT=9019
  DOZZLE_PORT=9018
  APPRISE_PORT=9025
  # AUTHELIA_JWT=SECRET
  # AUTHELIA_SESSION_SECRET=SECRET
  # AUTHELIA_REDIS_PASSWORD=SECRET
  # AUTHELIA_DB_USERNAME=SECRET
  GOOGLE_SMTP_PASSWORD:SECRET
  GOOGLE_CLIENT_ID=SECRET
  GOOGLE_CLIENT_SECRET=SECRET
  OAUTH_SECRET=SECRET
  MY_EMAIL=SECRET # Use MY_WHITELIST for multiple emails
  # URL_PATH=https://oauth.example.com/_oauth
  MY_WHITELIST=user@example.com
typkrft commented 4 years ago

Apparently Adding - "traefik.http.routers.oauth-rtr.tls=true" resolved the issue. I noticed there was no TLS listed in Traefik. I'm not sure if this has any security implications, but I will close this.