# dclint disable top-level-properties-order services-alphabetical-order service-image-require-explicit-tag
name: keycloak

x-image:
  nginx_image: &nginx_image ${NGINX_IMAGE:-nginx}:${NGINX_TAG:-1.27.4}
  postgres_image: &postgres_image ${POSTGRES_IMAGE:-postgres}:${POSTGRES_TAG:-14.12}
  keycloak_server_image: &keycloak_server_image ${KEYCLOAK_SERVER_IMAGE:-quay.io/eggplantsoftware/keycloak-server}:${KEYCLOAK_SERVER_TAG:-6.3.331}

x-tls:
  tls_cert: &tls_cert ${TLS_CERT-/etc/ssl/certs/tls.crt}
  tls_key: &tls_key ${TLS_KEY-/etc/ssl/private/tls.key}

services:
  postgres:
    image: *postgres_image
    container_name: postgres
    volumes:
      - kc-db-data:/var/lib/postgresql/data
    environment:
      - POSTGRES_USER=${POSTGRES_USER:-postgres}
      - POSTGRES_PASSWORD=${POSTGRES_PASSWORD:-postgres}
      - POSTGRES_DB=${POSTGRES_DB:-keycloak}
    ports:
      - "127.0.0.1:5432:5432"
    networks:
      - backend_network
    restart: always
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres"]
      interval: 10s
      timeout: 3s
      retries: 3
      start_period: 5s
    user: "999:999"
    cap_drop:
      - ALL
    profiles:
      - default
      - notls
    security_opt:
      - no-new-privileges:true

  keycloak-server:
    image: *keycloak_server_image
    container_name: keycloak-server
    depends_on:
      postgres:
        condition: service_healthy
    environment:
      - KEYCLOAK_DATABASE_HOST=${POSTGRES_HOSTNAME:-postgres}
      - KEYCLOAK_DATABASE_PORT=${POSTGRES_PORT:-5432}
      - KEYCLOAK_DATABASE_NAME=${POSTGRES_DB:-keycloak}
      - KEYCLOAK_DATABASE_USER=${POSTGRES_USER:-postgres}
      - KEYCLOAK_DATABASE_PASSWORD=${POSTGRES_PASSWORD:-postgres}
      - KC_HOSTNAME=${KEYCLOAK_URL:?}/auth
      - KEYCLOAK_ADMIN=${KC_ADMIN_USER:?}
      - KEYCLOAK_ADMIN_PASSWORD=${KC_ADMIN_PASSWORD:?}
      - KEYCLOAK_HTTP_RELATIVE_PATH=/auth/
      - KC_SPI_ADMIN_REALM=master
      - KC_SPI_LOGIN_PROTOCOL_OPENID_CONNECT_LEGACY_LOGOUT_REDIRECT_URI=true
      - KC_SPI_GROUP_JPA_ESCAPE_SLASHES_IN_GROUP_PATH=true
      - KC_HTTP_ENABLED=true
      - KC_HEALTH_ENABLED=true
      - KC_METRICS_ENABLED=true
    ports:
      - "127.0.0.1:8080:8080"
      - "127.0.0.1:9000:9000"
    networks:
      - frontend_network
      - backend_network
    restart: always
    healthcheck:
      test: ["CMD", "bash", "-c", "echo > /dev/tcp/localhost/8080"]
      interval: 10s
      timeout: 3s
      retries: 3
      start_period: 5s
    user: "2000:2000"
    cap_drop:
      - ALL
    profiles:
      - default
      - notls
    security_opt:
      - no-new-privileges:true

  nginx: &nginx-base
    image: *nginx_image
    container_name: nginx
    depends_on:
      keycloak-server:
        condition: service_healthy
    configs:
      - source: nginx_config
        target: /etc/nginx/nginx.conf
        uid: "101"
        gid: "101"
        mode: 644
    secrets:
      - source: tls_cert
        target: /etc/nginx/certs/tls.crt
      - source: tls_key
        target: /etc/nginx/certs/tls.key
    ports:
      - "0.0.0.0:${KEYCLOAK_PORT:-443}:443"
    networks:
      - frontend_network
    restart: always
    healthcheck:
      test: ["CMD-SHELL", "curl --fail http://localhost || exit 1"]
      interval: 10s
      timeout: 3s
      retries: 3
      start_period: 5s
    cap_add:
      - CAP_CHOWN
      - CAP_SETGID
      - CAP_SETUID
    cap_drop:
      - ALL
    profiles:
      - default
    security_opt:
      - no-new-privileges:true

  nginx-notls:
    <<: *nginx-base
    container_name: nginx-notls
    configs:
      - source: nginx_config_notls
        target: /etc/nginx/nginx.conf
        uid: "101"
        gid: "101"
        mode: 644
    ports:
      - "0.0.0.0:${KEYCLOAK_PORT:-8000}:8000"
    secrets: []
    profiles:
      - notls

networks:
  frontend_network:
  backend_network:

volumes:
  kc-db-data:
    driver: local

secrets:
  tls_cert:
    file: *tls_cert
  tls_key:
    file: *tls_key

configs:
  nginx_config:
    content: |
      worker_processes auto;
      error_log /var/log/nginx/error.log warn;
      pid /var/run/nginx.pid;

      events {}

      http {
          server_tokens off;                 # Hide Nginx version
          ssl_protocols TLSv1.2 TLSv1.3;     # Secure protocols only
          ssl_prefer_server_ciphers on;
          ssl_ciphers "HIGH:!aNULL:!MD5:!RC4:!3DES";

          server {
              listen 80;
              server_name localhost;

              return 301 https://$$host$$request_uri;
          }

          server {
              listen 443 ssl;
              server_name localhost;

              ssl_certificate /etc/nginx/certs/tls.crt;
              ssl_certificate_key /etc/nginx/certs/tls.key;

              # HTTP Strict Transport Security
              add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
              add_header X-Content-Type-Options nosniff;
              add_header X-XSS-Protection "1; mode=block";

              location / {
                  proxy_pass http://keycloak-server:8080;
                  proxy_set_header Host $$host;
                  proxy_set_header X-Real-IP $$remote_addr;
                  proxy_set_header X-Forwarded-For $$proxy_add_x_forwarded_for;
                  proxy_set_header X-Forwarded-Proto $$scheme;
              }
          }
      }

  nginx_config_notls:
    content: |
      worker_processes auto;
      error_log /var/log/nginx/error.log warn;
      pid /var/run/nginx.pid;

      events {}

      http {
          server_tokens off;                 # Hide Nginx version

          server {
              listen 8000;
              server_name localhost;

              location / {
                  proxy_pass http://keycloak-server:8080;
                  proxy_set_header Host $$host;
              }
          }
      }
