Hello, that’s been now more than two week that I’m stuck with two error in my config.
I’m transitioning my infrastructure to use docker container and Traefik as a reverse proxy. But I’m stuck with two problem
Here is the infra
- Traefik (reverse proxy)
- Keycloak (auth server)
- React.js (frontend) => main website
- Laravel (backend) => API for main website + admin UI
The laravel backend provide :
- an API used by the react frontend using Keycloak auth with
GitHub - robsontenorio/laravel-keycloak-guard: 🔑 Simple Keycloak Guard for Laravel.
-an admin UI using laravel Socialite to connect to another keycloak realm.
I’ve two problem
- My login theme template is totally broken with error “incorrect (X-Content-Type-Options: nosniff)” concerning index.js and index.css
- When accessing the laravel admin and trying to connect I’m redirected to
https://LARAVEL_APP/auth/callback?session_state=… But I get a 504 Gateway Time-out
= From the main website in react you can connect using keycloak and the api side of the backend is working properly. The problem really reside in the backend laravel socialite config or traefik config
Here is the config
Traefik - docker-compose
version: "3.8"
services:
traefik:
image: ${TRAEFIK_IMAGE_TAG}
container_name: traefik
command:
- --log.level=WARNING
- --accesslog=true
- --api.dashboard=true
- --api.insecure=true
- --ping=true
- --ping.entrypoint=ping
- --entryPoints.ping.address=:8082
- --entryPoints.web.address=:80
- --entryPoints.websecure.address=:443
- --providers.docker=true
- --providers.docker.endpoint=unix:///var/run/docker.sock
- --providers.docker.exposedbydefault=false
- --certificatesresolvers.myresolver.acme.tlschallenge=true
- --certificatesresolvers.myresolver.acme.caserver=https://acme-v02.api.letsencrypt.org/directory
- --certificatesresolvers.myresolver.acme.email=${TRAEFIK_ACME_EMAIL}
- --certificatesresolvers.myresolver.acme.storage=/etc/traefik/acme/acme.json
- "--metrics.prometheus=true"
- "--metrics.prometheus.buckets=0.1,0.3,1.2,5.0"
- "--global.checkNewVersion=true"
- "--global.sendAnonymousUsage=false"
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- "/etc/traefik/acme:/etc/traefik/acme"
networks:
- traefik-network
ports:
- "80:80"
- "443:443"
labels:
traefik.enable: true
traefik.http.routers.dashboard.rule: Host(`${TRAEFIK_HOSTNAME}`)
traefik.http.routers.dashboard.tls.domains[0].main: ${TRAEFIK_HOSTNAME}
traefik.http.routers.dashboard.tls.domains[0].sans: ${TRAEFIK_WWW_HOSTNAME}
traefik.http.routers.dashboard.entrypoints: websecure
traefik.http.routers.dashboard.service: dashboard@internal
traefik.http.services.dashboard.loadbalancer.server.port: 8080
traefik.http.routers.dashboard.tls: true
traefik.http.routers.dashboard.tls.certresolver: myresolver
traefik.http.services.dashboard.loadbalancer.passhostheader: true
traefik.http.routers.dashboard.middlewares: authtraefik
traefik.http.middlewares.authtraefik.basicauth.users: ${TRAEFIK_BASIC_AUTH}
traefik.http.routers.api.rule: Host(`${TRAEFIK_HOSTNAME}`) && PathPrefix(`/api`)
traefik.http.routers.api.service: api@internal
traefik.http.routers.api.entrypoints: websecure
traefik.http.routers.api.tls: true
traefik.http.routers.api.tls.certresolver: myresolver
traefik.http.routers.httpsonly.rule: HostRegexp(`{any:.*}`)
traefik.http.middlewares.httpsonly.redirectscheme.scheme: https
traefik.http.middlewares.httpsonly.redirectscheme.permanent: true
traefik.http.routers.httpsonly.middlewares: httpsonly
traefik.http.middlewares.sslheader.headers.customrequestheaders.X-Forwarded-Proto: https
traefik.docker.network: traefik-network
restart: unless-stopped
logging:
driver: gelf
options:
gelf-address: ${TRAEFIK_LOG_ADDRESS}
tag: ${TRAEFIK_LOG_TAG}
networks:
traefik-network:
external: true
Keycloak - docker-compose
version: "3.8"
services:
postgres-keycloak:
image: ${KEYCLOAK_POSTGRES_IMAGE_TAG}
container_name: postgres-keycloak
environment:
POSTGRES_DB: ${KEYCLOAK_DB_NAME}
POSTGRES_USER: ${KEYCLOAK_DB_USER}
POSTGRES_PASSWORD: ${KEYCLOAK_DB_PASSWORD}
volumes:
- keycloak-data:/var/lib/postgresql/data
- /var/log/keycloak/postgres:/var/lib/postgresql/logs
networks:
- keycloak-network
ports:
- ":5432"
healthcheck:
test: [ "CMD", "pg_isready", "-q", "-d", "${KEYCLOAK_DB_NAME}", "-U", "${KEYCLOAK_DB_USER}" ]
interval: 10s
timeout: 5s
retries: 3
start_period: 60s
restart: unless-stopped
logging:
driver: gelf
options:
gelf-address: ${KEYCLOAK_LOG_ADDRESS}
tag: ${KEYCLOAK_POSTGRES_LOG_TAG}
keycloak:
build:
context: .
args:
KEYCLOAK_VERSION: ${KEYCLOAK_VERSION}
command: ['start','--optimized']
container_name: keycloak
environment:
JAVA_OPTS_APPEND: -Dkeycloak.profile.feature.upload_scripts=enabled
KC_DB_USERNAME: ${KEYCLOAK_DB_USER}
KC_DB_PASSWORD: ${KEYCLOAK_DB_PASSWORD}
KC_DB_URL: jdbc:postgresql://postgres-keycloak:5432/${KEYCLOAK_DB_NAME}
KC_HEALTH_ENABLED: 'true'
KC_HTTP_ENABLED: 'true'
KC_METRICS_ENABLED: 'true'
KC_HOSTNAME_STRICT: 'false'
KC_HOSTNAME: ${KEYCLOAK_HOSTNAME}
KC_PROXY: reencrypt
KEYCLOAK_ADMIN: ${KEYCLOAK_USER}
KEYCLOAK_ADMIN_PASSWORD: ${KEYCLOAK_PASSWORD}
# KC_LOG_LEVEL: debug
networks:
- keycloak-network
- traefik-network
ports:
- ":8080"
healthcheck:
test: timeout 10s bash -c ':> /dev/tcp/127.0.0.1/8080' || exit 1
interval: 10s
timeout: 5s
retries: 3
start_period: 90s
labels:
traefik.enable: true
traefik.http.routers.keycloak.rule: Host(`${KEYCLOAK_HOSTNAME}`)
traefik.http.routers.keycloak.tls.domains[0].main: ${KEYCLOAK_HOSTNAME}
traefik.http.routers.keycloak.tls.domains[0].sans: ${KEYCLOAK_WWW_HOSTNAME}
traefik.http.routers.keycloak.service: keycloak
traefik.http.routers.keycloak.entrypoints: websecure
traefik.http.services.keycloak.loadbalancer.server.port: 8080
traefik.http.services.keycloak.loadbalancer.passhostheader: true
traefik.http.routers.keycloak.tls: true
traefik.http.routers.keycloak.tls.certresolver: myresolver
# Middelwares
traefik.http.middlewares.keycloak-headers.headers.customrequestheaders.X-Forwarded-Proto: https
traefik.http.middlewares.keycloak-headers.headers.customrequestheaders.X-Forwarded-For: 127.0.0.1
traefik.http.middlewares.keycloak-headers.headers.customrequestheaders.X-Forwarded-Host: ${KEYCLOAK_HOSTNAME}
traefik.http.routers.keycloak.middlewares: keycloak-headers
traefik.docker.network: traefik-network
restart: unless-stopped
depends_on:
postgres-keycloak:
condition: service_healthy
logging:
driver: gelf
options:
gelf-address: ${KEYCLOAK_LOG_ADDRESS}
tag: ${KEYCLOAK_LOG_TAG}
keycloak-backups:
image: ${KEYCLOAK_POSTGRES_IMAGE_TAG}
container_name: keycloak-backups
command: >-
sh -c 'sleep $KEYCLOAK_BACKUP_INIT_SLEEP &&
while true; do
pg_dump -h postgres-keycloak -p 5432 -d $KEYCLOAK_DB_NAME -U $KEYCLOAK_DB_USER | gzip > $KEYCLOAK_POSTGRES_BACKUPS_PATH/$KEYCLOAK_POSTGRES_BACKUP_NAME-$(date "+%Y-%m-%d_%H-%M").gz &&
find $KEYCLOAK_POSTGRES_BACKUPS_PATH -type f -mtime +$KEYCLOAK_POSTGRES_BACKUP_PRUNE_DAYS | xargs rm -f &&
sleep $KEYCLOAK_BACKUP_INTERVAL; done'
volumes:
- keycloak-postgres-backup:/var/lib/postgresql/data
- keycloak-database-backups:${KEYCLOAK_POSTGRES_BACKUPS_PATH}
- /var/log/keycloak/backups:/var/lib/postgresql/logs # Added volume for backup logs
environment:
KEYCLOAK_DB_NAME: ${KEYCLOAK_DB_NAME}
KEYCLOAK_DB_USER: ${KEYCLOAK_DB_USER}
PGPASSWORD: ${KEYCLOAK_DB_PASSWORD}
KEYCLOAK_BACKUP_INIT_SLEEP: ${KEYCLOAK_BACKUP_INIT_SLEEP}
KEYCLOAK_BACKUP_INTERVAL: ${KEYCLOAK_BACKUP_INTERVAL}
KEYCLOAK_POSTGRES_BACKUP_PRUNE_DAYS: ${KEYCLOAK_POSTGRES_BACKUP_PRUNE_DAYS}
KEYCLOAK_POSTGRES_BACKUPS_PATH: ${KEYCLOAK_POSTGRES_BACKUPS_PATH}
KEYCLOAK_POSTGRES_BACKUP_NAME: ${KEYCLOAK_POSTGRES_BACKUP_NAME}
KEYCLOAK_LOG: console,gelf
KEYCLOAK_LOG_GELF_HOST: localhost
KEYCLOAK_LOG_GELF_PORT: 12201
networks:
- keycloak-network
restart: unless-stopped
depends_on:
postgres-keycloak:
condition: service_healthy
logging:
driver: gelf
options:
gelf-address: ${KEYCLOAK_LOG_ADDRESS}
tag: ${KEYCLOAK_BACKUPS_LOG_TAG}
volumes:
keycloak-data:
driver: local
keycloak-postgres-backup:
driver: local
keycloak-database-backups:
driver: local
networks:
traefik-network:
external: true
keycloak-network:
external: true
Keycloak - Dockerfile
FROM quay.io/keycloak/keycloak:22.0.5 as builder
# Enable health and metrics support
ENV KC_HEALTH_ENABLED=true
ENV KC_METRICS_ENABLED=true
# Configure a database vendor
ENV KC_DB=postgres
WORKDIR /opt/keycloak
# for demonstration purposes only, please make sure to use proper certificates in production instead
# RUN keytool -genkeypair -storepass password -storetype PKCS12 -keyalg RSA -keysize 2048 -dname "CN=server" -alias server -ext "SAN:c=DNS:localhost,IP:127.0.0.1" -keystore conf/server.keystore
RUN /opt/keycloak/bin/kc.sh build
FROM quay.io/keycloak/keycloak:22.0.5
COPY --from=builder /opt/keycloak/ /opt/keycloak/
# change these values to point to a running postgres instance
ENV KC_HOSTNAME=***********
ENV JAVA_OPTS_APPEND: -Dkeycloak.profile.feature.upload_scripts=enabled
ENV KC_DB_USERNAME: ${KEYCLOAK_DB_USER}
ENV KC_DB_PASSWORD: ${KEYCLOAK_DB_PASSWORD}
ENV KC_DB_URL: jdbc:postgresql://postgres-keycloak:5432/${KEYCLOAK_DB_NAME}
ENV KC_HTTP_ENABLED: 'true'
ENV KC_HOSTNAME_STRICT: 'false'
ENV KC_HOSTNAME: ${KEYCLOAK_HOSTNAME}
ENV KC_PROXY: reencrypt
ENV KEYCLOAK_ADMIN: ${KEYCLOAK_USER}
ENV KEYCLOAK_ADMIN_PASSWORD: ${KEYCLOAK_PASSWORD}
# ENV KC_LOG_LEVEL: debug
COPY ./themes/*****-keycloak-theme-keywind /opt/keycloak/themes/*******-keycloak-theme-keywind
ENTRYPOINT ["/opt/keycloak/bin/kc.sh"]
Laravel - docker-compose
version: "3.8"
services:
postgres-web-backend:
image: ${WEBBACKEND_POSTGRES_IMAGE_TAG}
container_name: postgres-web-backend
environment:
POSTGRES_DB: ${WEBBACKEND_DB_NAME}
POSTGRES_USER: ${WEBBACKEND_DB_USER}
POSTGRES_PASSWORD: ${WEBBACKEND_DB_PASSWORD}
volumes:
- web-backend-data:/var/lib/postgresql/data
networks:
- web-backend-network
ports:
- ":5432"
healthcheck:
test: [ "CMD", "pg_isready", "-q", "-d", "${WEBBACKEND_DB_NAME}", "-U", "${WEBBACKEND_DB_USER}" ]
interval: 10s
timeout: 5s
retries: 3
start_period: 60s
restart: unless-stopped
logging:
driver: gelf
options:
gelf-address: ${WEBBACKEND_LOG_ADDRESS}
tag: ${WEBBACKEND_POSTGRES_LOG_TAG}
web-backend:
build:
args:
user: www
uid: 1000
context: ./
dockerfile: Dockerfile
image: web-backend
container_name: web-backend
working_dir: /var/www/
volumes:
- ./:/var/www/
networks:
- web-backend-network
- traefik-network
restart: unless-stopped
depends_on:
postgres-web-backend:
condition: service_healthy
logging:
driver: gelf
options:
gelf-address: ${WEBBACKEND_LOG_ADDRESS}
tag: ${WEBBACKEND_LOG_TAG}
web-backend-nginx:
image: nginx:alpine
container_name: web-backend-nginx
volumes:
- ./:/var/www/
- ./nginx/:/etc/nginx/conf.d/
networks:
- traefik-network
ports:
- ":80"
labels:
traefik.enable: true
traefik.http.routers.web-backend.rule: Host(`${WEBBACKEND_HOSTNAME}`)
traefik.http.routers.web-backend.tls.domains[0].main: ${WEBBACKEND_HOSTNAME}
traefik.http.routers.web-backend.tls.domains[0].sans: ${WEBBACKEND_WWW_HOSTNAME}
traefik.http.routers.web-backend.service: web-backend
traefik.http.routers.web-backend.entrypoints: websecure
traefik.http.services.web-backend.loadbalancer.server.port: 80
traefik.http.services.web-backend.loadbalancer.passhostheader: true
traefik.http.routers.web-backend.tls: true
traefik.http.routers.web-backend.tls.certresolver: myresolver
traefik.docker.network: traefik-network
logging:
driver: gelf
options:
gelf-address: ${WEBBACKEND_LOG_ADDRESS}
tag: ${WEBBACKEND_NGINX_LOG_TAG}
depends_on:
- web-backend
web-backend-backups:
image: ${WEBBACKEND_POSTGRES_IMAGE_TAG}
container_name: web-backend-backups
command: >-
sh -c 'sleep $WEBBACKEND_BACKUP_INIT_SLEEP &&
while true; do
pg_dump -h postgres-web-backend -p 5432 -d $WEBBACKEND_DB_NAME -U $WEBBACKEND_DB_USER | gzip > $WEBBACKEND_POSTGRES_BACKUPS_PATH/$WEBBACKEND_POSTGRES_BACKUP_NAME-$(date "+%Y-%m-%d_%H-%M").gz &&
find $WEBBACKEND_POSTGRES_BACKUPS_PATH -type f -mtime +$WEBBACKEND_POSTGRES_BACKUP_PRUNE_DAYS | xargs rm -f &&
sleep $WEBBACKEND_BACKUP_INTERVAL; done'
volumes:
- web-backend-postgres-backup:/var/lib/postgresql/data
- web-backend-database-backups:${WEBBACKEND_POSTGRES_BACKUPS_PATH}
environment:
ZABBIX_DB_NAME: ${WEBBACKEND_DB_NAME}
ZABBIX_DB_USER: ${WEBBACKEND_DB_USER}
PGPASSWORD: ${WEBBACKEND_DB_PASSWORD}
ZABBIX_BACKUP_INIT_SLEEP: ${WEBBACKEND_BACKUP_INIT_SLEEP}
ZABBIX_BACKUP_INTERVAL: ${WEBBACKEND_BACKUP_INTERVAL}
ZABBIX_POSTGRES_BACKUP_PRUNE_DAYS: ${WEBBACKEND_POSTGRES_BACKUP_PRUNE_DAYS}
ZABBIX_POSTGRES_BACKUPS_PATH: ${WEBBACKEND_POSTGRES_BACKUPS_PATH}
ZABBIX_POSTGRES_BACKUP_NAME: ${WEBBACKEND_POSTGRES_BACKUP_NAME}
networks:
- web-backend-network
restart: unless-stopped
depends_on:
postgres-web-backend:
condition: service_healthy
logging:
driver: gelf
options:
gelf-address: ${WEBBACKEND_LOG_ADDRESS}
tag: ${WEBBACKEND_BACKUPS_LOG_TAG}
volumes:
web-backend-data:
web-backend-postgres-backup:
web-backend-database-backups:
networks:
traefik-network:
external: true
web-backend-network:
external: true
Laravel - Dockerfile
FROM php:8.1-fpm
# Copy composer.lock and composer.json
COPY composer.lock composer.json /var/www/
# Set working directory
WORKDIR /var/www
# Install dependencies
RUN apt-get update && apt-get install -y \
git \
curl \
libpng-dev \
libonig-dev \
libxml2-dev \
libpq-dev \
zip \
unzip \
nano
# Clear cache
RUN apt-get clean && rm -rf /var/lib/apt/lists/*
# Install extensions
RUN docker-php-ext-configure pgsql -with-pgsql=/usr/local/pgsql
RUN docker-php-ext-install pdo pdo_pgsql pgsql mbstring exif pcntl bcmath gd
# Install composer
RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer
# Add user for laravel application
RUN groupadd -g 1000 www
RUN useradd -u 1000 -ms /bin/bash -g www www
# Copy existing application directory contents
COPY . /var/www
# Copy existing application directory permissions
COPY --chown=www:www . /var/www
# Change current user to www
USER www
# Expose port 9000 and start php-fpm server
EXPOSE 9000
CMD ["php-fpm"]
Thanks all in advance
Feel free to ask question if you need more informations.