diff --git a/roles/keycloak/README.md b/roles/keycloak/README.md new file mode 100644 index 0000000..860a0b1 --- /dev/null +++ b/roles/keycloak/README.md @@ -0,0 +1,65 @@ +Keycloak +========= + +Ansible role to deploy Keycloak with PostgreSQL database using Docker Compose. + +Requirements +------------ + +- Docker and Docker Compose installed on the target host +- Ansible collection: `community.docker` +- Traefik reverse proxy (for external access) + +Role Variables +-------------- + +Key variables defined in `defaults/main.yml`: + +**Base Configuration:** +- `docker_compose_base_dir`: Base directory for Docker Compose files (default: `/etc/docker/compose`) +- `docker_volume_base_dir`: Base directory for Docker volumes (default: `/srv/data`) + +**Keycloak Configuration:** +- `keycloak_service_name`: Service name (default: `keycloak`) +- `keycloak_domain`: Domain name for Keycloak (default: `auth.digitalboard.ch`) +- `keycloak_image`: Keycloak Docker image (default: `quay.io/keycloak/keycloak:24.0.1`) +- `keycloak_port`: Internal Keycloak port (default: `8080`) +- `keycloak_admin_user`: Admin username (default: `admin`) +- `keycloak_admin_password`: Admin password (default: `changeme`) +- `keycloak_log_level`: Log level (default: `INFO`) +- `keycloak_proxy_mode`: Proxy mode (default: `edge`) + +**PostgreSQL Configuration:** +- `keycloak_postgres_image`: PostgreSQL Docker image (default: `postgres:15`) +- `keycloak_postgres_db`: Database name (default: `keycloak`) +- `keycloak_postgres_user`: Database user (default: `keycloak`) +- `keycloak_postgres_password`: Database password (default: `changeme`) + +**Traefik Configuration:** +- `keycloak_traefik_network`: Traefik network name (default: `proxy`) +- `keycloak_backend_network`: Backend network name (default: `backend`) +- `keycloak_use_ssl`: Enable SSL (default: `true`) +- `keycloak_cert_resolver`: Certificate resolver name (default: `dns`) + +Dependencies +------------ + +This role requires the Traefik reverse proxy to be configured and the `proxy` network to be created. + +Example Playbook +---------------- + +```yaml +- hosts: backend_servers + roles: + - role: keycloak + vars: + keycloak_domain: "auth.example.com" + keycloak_admin_password: "secure_password" + keycloak_postgres_password: "secure_db_password" +``` + +License +------- + +MIT-0 diff --git a/roles/keycloak/defaults/main.yml b/roles/keycloak/defaults/main.yml new file mode 100644 index 0000000..66d0a72 --- /dev/null +++ b/roles/keycloak/defaults/main.yml @@ -0,0 +1,35 @@ +#SPDX-License-Identifier: MIT-0 +--- +# defaults file for keycloak + +# Base directory configuration (inherited from base role or defined here) +docker_compose_base_dir: /etc/docker/compose +docker_volume_base_dir: /srv/data + +# Keycloak-specific configuration +keycloak_service_name: keycloak +keycloak_docker_compose_dir: "{{ docker_compose_base_dir }}/{{ keycloak_service_name }}" +keycloak_docker_volume_dir: "{{ docker_volume_base_dir }}/{{ keycloak_service_name }}" + +# Keycloak service configuration +keycloak_domain: "keycloak.local.test" +keycloak_image: "quay.io/keycloak/keycloak:24.0.1" +keycloak_port: 8080 +keycloak_admin_user: admin +keycloak_admin_password: "changeme" + +# PostgreSQL configuration +keycloak_postgres_image: "postgres:15" +keycloak_postgres_db: keycloak +keycloak_postgres_user: keycloak +keycloak_postgres_password: "changeme" + +# Traefik configuration +keycloak_traefik_network: "proxy" +keycloak_backend_network: "backend" +keycloak_use_ssl: true + +# Keycloak environment variables +keycloak_log_level: "INFO" +keycloak_proxy_mode: "edge" +keycloak_gzip_enabled: false # Disable GZIP encoding to avoid MIME type issues diff --git a/roles/keycloak/handlers/main.yml b/roles/keycloak/handlers/main.yml new file mode 100644 index 0000000..3eb65c6 --- /dev/null +++ b/roles/keycloak/handlers/main.yml @@ -0,0 +1,3 @@ +#SPDX-License-Identifier: MIT-0 +--- +# handlers file for keycloak diff --git a/roles/keycloak/meta/main.yml b/roles/keycloak/meta/main.yml new file mode 100644 index 0000000..36b9858 --- /dev/null +++ b/roles/keycloak/meta/main.yml @@ -0,0 +1,35 @@ +#SPDX-License-Identifier: MIT-0 +galaxy_info: + author: your name + description: your role description + company: your company (optional) + + # If the issue tracker for your role is not on github, uncomment the + # next line and provide a value + # issue_tracker_url: http://example.com/issue/tracker + + # Choose a valid license ID from https://spdx.org - some suggested licenses: + # - BSD-3-Clause (default) + # - MIT + # - GPL-2.0-or-later + # - GPL-3.0-only + # - Apache-2.0 + # - CC-BY-4.0 + license: license (GPL-2.0-or-later, MIT, etc) + + min_ansible_version: 2.1 + + # If this a Container Enabled role, provide the minimum Ansible Container version. + # min_ansible_container_version: + + galaxy_tags: [] + # List tags for your role here, one per line. A tag is a keyword that describes + # and categorizes the role. Users find roles by searching for tags. Be sure to + # remove the '[]' above, if you add tags to this list. + # + # NOTE: A tag is limited to a single word comprised of alphanumeric characters. + # Maximum 20 tags per role. + +dependencies: [] + # List your role dependencies here, one per line. Be sure to remove the '[]' above, + # if you add dependencies to this list. diff --git a/roles/keycloak/tasks/main.yml b/roles/keycloak/tasks/main.yml new file mode 100644 index 0000000..05db2ef --- /dev/null +++ b/roles/keycloak/tasks/main.yml @@ -0,0 +1,32 @@ +#SPDX-License-Identifier: MIT-0 +--- +# tasks file for keycloak + +- name: Create docker compose directory + file: + path: "{{ keycloak_docker_compose_dir }}" + state: directory + mode: '0755' + +- name: Create keycloak data directory + file: + path: "{{ keycloak_docker_volume_dir }}/data" + state: directory + mode: '0755' + +- name: Create postgres data directory + file: + path: "{{ keycloak_docker_volume_dir }}/postgresql" + state: directory + mode: '0755' + +- name: Create docker-compose file for keycloak + template: + src: docker-compose.yml.j2 + dest: "{{ keycloak_docker_compose_dir }}/docker-compose.yml" + mode: '0644' + +- name: Start keycloak container + community.docker.docker_compose_v2: + project_src: "{{ keycloak_docker_compose_dir }}" + state: present diff --git a/roles/keycloak/templates/docker-compose.yml.j2 b/roles/keycloak/templates/docker-compose.yml.j2 new file mode 100644 index 0000000..a91f746 --- /dev/null +++ b/roles/keycloak/templates/docker-compose.yml.j2 @@ -0,0 +1,64 @@ +services: + postgres: + image: {{ keycloak_postgres_image }} + restart: unless-stopped + environment: + POSTGRES_DB: {{ keycloak_postgres_db }} + POSTGRES_USER: {{ keycloak_postgres_user }} + POSTGRES_PASSWORD: {{ keycloak_postgres_password }} + volumes: + - {{ keycloak_docker_volume_dir }}/postgresql:/var/lib/postgresql/data + networks: + - {{ keycloak_backend_network }} + + {{ keycloak_service_name }}: + image: {{ keycloak_image }} + restart: unless-stopped + entrypoint: /bin/sh + command: + - -c + - > + /opt/keycloak/bin/kc.sh build && + /opt/keycloak/bin/kc.sh start --optimized + environment: + KC_DB: postgres + KC_DB_URL: jdbc:postgresql://postgres:5432/{{ keycloak_postgres_db }} + KC_DB_USERNAME: {{ keycloak_postgres_user }} + KC_DB_PASSWORD: {{ keycloak_postgres_password }} + KEYCLOAK_ADMIN: {{ keycloak_admin_user }} + KEYCLOAK_ADMIN_PASSWORD: {{ keycloak_admin_password }} + KC_LOG_LEVEL: {{ keycloak_log_level }} + KC_SPI_RESOURCE_ENCODING_GZIP_ENABLED: {{ keycloak_gzip_enabled | lower }} + KC_SPI_RESOURCE_ENCODING_GZIP_CACHE_DIR: /opt/keycloak/data/gzip-cache + KC_PROXY: {{ keycloak_proxy_mode }} + KC_HOSTNAME: {{ keycloak_domain }} + depends_on: + - postgres + volumes: + - {{ keycloak_docker_volume_dir }}/data:/opt/keycloak/data + networks: + - {{ keycloak_backend_network }} + - {{ keycloak_traefik_network }} + tmpfs: + - /opt/keycloak/data/tmp:size=1024m + labels: + - traefik.enable=true + - traefik.docker.network={{ keycloak_traefik_network }} + - traefik.http.routers.{{ keycloak_service_name }}.rule=Host(`{{ keycloak_domain }}`) +{% if keycloak_use_ssl %} + - traefik.http.routers.{{ keycloak_service_name }}.entrypoints=websecure + - traefik.http.routers.{{ keycloak_service_name }}.tls=true +{% else %} + - traefik.http.routers.{{ keycloak_service_name }}.entrypoints=web +{% endif %} + - traefik.http.services.{{ keycloak_service_name }}.loadbalancer.server.port={{ keycloak_port }} + # Middleware: Keycloak proxy headers + - traefik.http.routers.{{ keycloak_service_name }}.middlewares={{ keycloak_service_name }}-headers + - traefik.http.middlewares.{{ keycloak_service_name }}-headers.headers.customrequestheaders.X-Forwarded-Proto=https + - traefik.http.middlewares.{{ keycloak_service_name }}-headers.headers.customrequestheaders.X-Forwarded-Host={{ keycloak_domain }} + - traefik.http.middlewares.{{ keycloak_service_name }}-headers.headers.customrequestheaders.X-Forwarded-Port=443 + +networks: + {{ keycloak_backend_network }}: + {{ keycloak_traefik_network }}: + external: true \ No newline at end of file diff --git a/roles/keycloak/tests/inventory b/roles/keycloak/tests/inventory new file mode 100644 index 0000000..03ca42f --- /dev/null +++ b/roles/keycloak/tests/inventory @@ -0,0 +1,3 @@ +#SPDX-License-Identifier: MIT-0 +localhost + diff --git a/roles/keycloak/tests/test.yml b/roles/keycloak/tests/test.yml new file mode 100644 index 0000000..af5840f --- /dev/null +++ b/roles/keycloak/tests/test.yml @@ -0,0 +1,6 @@ +#SPDX-License-Identifier: MIT-0 +--- +- hosts: localhost + remote_user: root + roles: + - keycloak diff --git a/roles/keycloak/vars/main.yml b/roles/keycloak/vars/main.yml new file mode 100644 index 0000000..d1ec5fc --- /dev/null +++ b/roles/keycloak/vars/main.yml @@ -0,0 +1,3 @@ +#SPDX-License-Identifier: MIT-0 +--- +# vars file for keycloak