diff --git a/group_vars/all/keycloak.yml b/group_vars/all/keycloak.yml index c6a9eab..f502f90 100644 --- a/group_vars/all/keycloak.yml +++ b/group_vars/all/keycloak.yml @@ -3,3 +3,9 @@ keycloak_admin_username: "keycloak-admin" keycloak_admin_password: "{{ keycloak_admin_password_vault }}" keycloak_default_theme: "mpmx-theme" + +# Use these Realm ACLs to create custom Traefik labels for Keycloak to restrict admin access per realm +# Both variables are mandatory! +# name: +# admin_ips: +keycloak_admin_realm_acls: [] diff --git a/group_vars/stage_devnso/keycloak.yml b/group_vars/stage_devnso/keycloak.yml new file mode 100644 index 0000000..abd8686 --- /dev/null +++ b/group_vars/stage_devnso/keycloak.yml @@ -0,0 +1,3 @@ +# Used to authorize access to keaycloak via tcp/443 on the hcloud firewall +keycloak_https_whitelisted_ips: + - 0.0.0.0/0 # Public access to keycloak diff --git a/group_vars/stage_devnso/management.yml b/group_vars/stage_devnso/management.yml index e9adab1..388b7eb 100644 --- a/group_vars/stage_devnso/management.yml +++ b/group_vars/stage_devnso/management.yml @@ -1,6 +1,3 @@ --- stage_database_management_connect_name: "{{ stage }}_management_smardigo_connect" stage_database_management_connect_password: "connect-postgres-admin" - -management_oidc_realm: "management" -management_oidc_client_id: "smardigo" diff --git a/group_vars/stage_prodnso/keycloak.yml b/group_vars/stage_prodnso/keycloak.yml index 20ca6e9..6983432 100644 --- a/group_vars/stage_prodnso/keycloak.yml +++ b/group_vars/stage_prodnso/keycloak.yml @@ -1,21 +1,37 @@ +# Used to authorize access to keaycloak via tcp/443 on the hcloud firewall keycloak_https_whitelisted_ips: - - 195.200.47.243/32 # DEV-230 - sparda berlin - - 195.200.47.244/32 # DEV-230 - sparda berlin - - 92.42.192.128/25 # MOB-486 - mobene - - 195.140.123.0/24 # DEV-628 - spk bautzen - - 195.140.44.0/24 # DEV-628 - spk bautzen - - 62.181.145.0/24 # DEV-628 - spk bautzen - - 62.181.146.0/24 # DEV-628 - spk bautzen - - 167.235.150.201/32 # prodwork01-kube-cpl-01 ; DEV-786 mobene (nsodev) migration - - 167.235.150.198/32 # prodwork01-kube-cpl-02 ; DEV-786 mobene (nsodev) migration - - 167.235.150.195/32 # prodwork01-kube-cpl-03 ; DEV-786 mobene (nsodev) migration - - 167.235.150.133/32 # prodwork01-kube-node-01 ; DEV-786 mobene (nsodev) migration - - 167.235.150.197/32 # prodwork01-kube-node-02 DEV-786 mobene (nsodev) migration - - 23.88.53.161/32 # prodwork01-kube-node-03 ; DEV-786 mobene (nsodev) migration - - 195.201.113.110/32 # prodwork01-kube-node-04 ; DEV-786 mobene (nsodev) migration - - 5.75.184.216/32 # prodwork01-kube-node-05 ; DEV-786 mobene (nsodev) migration - - 91.107.228.133/32 # prodwork01-kube-node-06 ; => DEV-987 - - 167.235.25.0/32 # prodwork01-kube-node-07 ; => DEV-987 - - 145.225.17.1/32 # DEV-1142 - Linde - - 20.113.104.205/32 # DEV-1142 - Linde - - 20.113.10.224/32 # DEV-1142 - Linde + - 0.0.0.0/0 # Public access to keycloak + +# Use these Realm ACLs to create custom Traefik labels for Keycloak to restrict admin access per realm +# Both variables are mandatory! +# name: +# admin_ips: +keycloak_admin_realm_acls: + - name: spkbz + admin_ips: + - 195.140.123.0/24 # DEV-628 - spk bautzen + - 195.140.44.0/24 # DEV-628 - spk bautzen + - 62.181.145.0/24 # DEV-628 - spk bautzen + - 62.181.146.0/24 # DEV-628 - spk bautzen + - name: sbsma + admin_ips: + - 195.200.47.243/32 # DEV-230 - sparda berlin + - 195.200.47.244/32 # DEV-230 - sparda berlin + - name: mobene + admin_ips: + - 92.42.192.128/25 # MOB-486 - mobene + - 167.235.150.201/32 # prodwork01-kube-cpl-01 ; DEV-786 mobene (nsodev) migration + - 167.235.150.198/32 # prodwork01-kube-cpl-02 ; DEV-786 mobene (nsodev) migration + - 167.235.150.195/32 # prodwork01-kube-cpl-03 ; DEV-786 mobene (nsodev) migration + - 167.235.150.133/32 # prodwork01-kube-node-01 ; DEV-786 mobene (nsodev) migration + - 167.235.150.197/32 # prodwork01-kube-node-02 DEV-786 mobene (nsodev) migration + - 23.88.53.161/32 # prodwork01-kube-node-03 ; DEV-786 mobene (nsodev) migration + - 195.201.113.110/32 # prodwork01-kube-node-04 ; DEV-786 mobene (nsodev) migration + - 5.75.184.216/32 # prodwork01-kube-node-05 ; DEV-786 mobene (nsodev) migration + - 91.107.228.133/32 # prodwork01-kube-node-06 ; => DEV-987 + - 167.235.25.0/32 # prodwork01-kube-node-07 ; => DEV-987 + - name: linde + admin_ips: + - 145.225.17.1/32 # DEV-1142 - Linde + - 20.113.104.205/32 # DEV-1142 - Linde + - 20.113.10.224/32 # DEV-1142 - Linde diff --git a/group_vars/stage_qanso/keycloak.yml b/group_vars/stage_qanso/keycloak.yml new file mode 100644 index 0000000..5d10877 --- /dev/null +++ b/group_vars/stage_qanso/keycloak.yml @@ -0,0 +1,15 @@ +# Used to authorize access to keaycloak via tcp/443 on the hcloud firewall +keycloak_https_whitelisted_ips: + - 0.0.0.0/0 # Public access to keycloak + +# Use these Realm ACLs to create custom Traefik labels for Keycloak to restrict admin access per realm +# Both variables are mandatory! +# name: +# admin_ips: +keycloak_admin_realm_acls: + - name: management + admin_ips: + - 79.140.117.133/32 # mha + - name: mhel + admin_ips: + - 79.140.117.133/32 # mha diff --git a/host_vars/devnso-mhel-test01/plain.yml b/host_vars/devnso-mhel-test01/plain.yml new file mode 100644 index 0000000..1f2bbf0 --- /dev/null +++ b/host_vars/devnso-mhel-test01/plain.yml @@ -0,0 +1,15 @@ +--- +server_hcloud_firewall_objects: + - name: "customer-access-to-{{ inventory_hostname }}" + state: present + rules: + - direction: in + protocol: tcp + port: "443" + source_ips: "{{ additional_ip_adresses_vault }}" + destination_ips: [] + description: customer specific access to https services + apply_to: + - type: server + server: + id: "{{ stage_server_id }}" diff --git a/host_vars/devnso-mhel-test01/vault.yml b/host_vars/devnso-mhel-test01/vault.yml new file mode 100644 index 0000000..128c3a6 --- /dev/null +++ b/host_vars/devnso-mhel-test01/vault.yml @@ -0,0 +1,8 @@ +$ANSIBLE_VAULT;1.1;AES256 +31396666646138353139636535636563613531356430336362386265636465656638656661613135 +6331373138383964363266383331633532383537613837310a366531363137656566306565346263 +32653430646463356464653939363431363666373637633332323430303934316439326234663532 +6661373662663836660a663138613564623237666434353561366366353936363063313831333165 +64333464333061336337393762343362373362353462346236323965653666343264343438306132 +32653561656337636365663531333066666663623738643463653865663961303239376262306362 +373762363465613031666565383535313033 diff --git a/roles/keycloak/tasks/_configure_traefik.yml b/roles/keycloak/tasks/_configure_traefik.yml new file mode 100644 index 0000000..58757c2 --- /dev/null +++ b/roles/keycloak/tasks/_configure_traefik.yml @@ -0,0 +1,26 @@ +--- +- name: "Generate Traefik labels for custom admin access to specific realm(s)" + ansible.builtin.set_fact: + labels: + - '"traefik.http.routers.{{ keycloak_id }}-admin-{{ keycloak_accessible_realm.name }}.service={{ keycloak_id }}"' + - '"traefik.http.routers.{{ keycloak_id }}-admin-{{ keycloak_accessible_realm.name }}.rule=Host(`{{ stage_server_domain }}`) && Method(`POST`,`PUT`,`DELETE`, `PATCH`) && (PathPrefix(`/auth/realms/{{ keycloak_accessible_realm.name }}`) || PathPrefix(`/auth/admin/{{ keycloak_accessible_realm.name }}`) || PathPrefix(`/auth/admin/realms/{{ keycloak_accessible_realm.name }}`))"' + - '"traefik.http.routers.{{ keycloak_id }}-admin-{{ keycloak_accessible_realm.name }}.entrypoints=websecure"' + - '"traefik.http.routers.{{ keycloak_id }}-admin-{{ keycloak_accessible_realm.name }}.tls=true"' + - '"traefik.http.routers.{{ keycloak_id }}-admin-{{ keycloak_accessible_realm.name }}.tls.certresolver=letsencrypt-http"' + - '"traefik.http.routers.{{ keycloak_id }}-admin-{{ keycloak_accessible_realm.name }}.middlewares={{ keycloak_id }}-admin-{{ keycloak_accessible_realm.name }}-ipwhitelist"' + - '"traefik.http.middlewares.{{ keycloak_id }}-admin-{{ keycloak_accessible_realm.name }}-ipwhitelist.ipwhitelist.sourcerange={{ (ip_whitelist + (keycloak_accessible_realm.admin_ips) | default([])) | join(",") }}"' + loop: "{{ keycloak_admin_realm_acls }}" + register: keycloak_accessible_realms + loop_control: + loop_var: keycloak_accessible_realm + tags: + - update_deployment + +- name: "Extract keycloak_accessible_realms" + ansible.builtin.set_fact: + keycloak_labels_additional: "{{ keycloak_labels_additional | default([]) + keycloak_accessible_realm.ansible_facts.labels }}" + loop: "{{ keycloak_accessible_realms.results }}" + loop_control: + loop_var: keycloak_accessible_realm + tags: + - update_deployment diff --git a/roles/keycloak/tasks/main.yml b/roles/keycloak/tasks/main.yml index 91fbbc2..242747a 100644 --- a/roles/keycloak/tasks/main.yml +++ b/roles/keycloak/tasks/main.yml @@ -1,33 +1,37 @@ --- - ### tags: ### update_deployment ### update_realms - name: "Setup DNS configuration for {{ inventory_hostname }}" - include_role: + ansible.builtin.include_role: name: hetzner-ansible-dns vars: record_data: "{{ stage_server_ip }}" record_name: "{{ inventory_hostname }}" - name: "Check if {{ inventory_hostname }}/docker-compose.yml exists" - stat: - path: '{{ service_base_path }}/{{ inventory_hostname }}/docker-compose.yml' + ansible.builtin.stat: + path: "{{ service_base_path }}/{{ inventory_hostname }}/docker-compose.yml" register: check_docker_compose_file tags: - update_deployment +- name: "Configure Traefik labels to restrict access to admin console" + ansible.builtin.include_tasks: _configure_traefik.yml + tags: + - update_deployment + - name: "Stop {{ inventory_hostname }}" community.docker.docker_compose: - project_src: '{{ service_base_path }}/{{ inventory_hostname }}' + project_src: "{{ service_base_path }}/{{ inventory_hostname }}" state: absent when: check_docker_compose_file.stat.exists tags: - update_deployment - name: "Deploy docker templates for {{ inventory_hostname }}" - include_role: + ansible.builtin.include_role: name: hetzner-ansible-sma-deploy tasks_from: templates vars: @@ -41,7 +45,7 @@ - update_deployment - name: "Deploy service templates for {{ inventory_hostname }}" - include_role: + ansible.builtin.include_role: name: hetzner-ansible-sma-deploy tasks_from: templates vars: @@ -55,22 +59,22 @@ - name: "Start {{ inventory_hostname }}" community.docker.docker_compose: - project_src: '{{ service_base_path }}/{{ inventory_hostname }}' + project_src: "{{ service_base_path }}/{{ inventory_hostname }}" state: present - pull: yes + pull: true tags: - update_deployment - name: "Wait for " - wait_for: + ansible.builtin.wait_for: host: "localhost" - port: '{{ service_port_keycloak_external }}' + port: "{{ service_port_keycloak_external }}" delay: 60 tags: - update_deployment - name: "Authenticate on keycloak for {{ inventory_hostname }}" - include_role: + ansible.builtin.include_role: name: keycloak tasks_from: _authenticate tags: @@ -88,24 +92,24 @@ account_theme: "{{ keycloak_default_theme }}" admin_theme: "{{ keycloak_default_theme }}" login_theme: "{{ keycloak_default_theme }}" - registration_allowed: no - reset_password_allowed: no - login_with_email_allowed: no - duplicate_emails_allowed: yes - internationalization_enabled: yes + registration_allowed: false + reset_password_allowed: false + login_with_email_allowed: false + duplicate_emails_allowed: true + internationalization_enabled: true default_locale: "de" supported_locales: - - "de" - - "en" - events_enabled: yes + - "de" + - "en" + events_enabled: true events_expiration: 604800 - admin_events_enabled: yes + admin_events_enabled: true smtp_server: host: "{{ shared_service_mail_hostname }}" from: "{{ keycloak_id }}@smardigo.digital" events_listeners: - - "jboss-logging" - - "metrics-listener" + - "jboss-logging" + - "metrics-listener" state: present tags: - update_realms diff --git a/roles/keycloak/vars/main.yml b/roles/keycloak/vars/main.yml index 30ffc48..0522acb 100644 --- a/roles/keycloak/vars/main.yml +++ b/roles/keycloak/vars/main.yml @@ -11,6 +11,15 @@ keycloak_labels: [ '"traefik.http.routers.{{ keycloak_id }}.tls=true"', '"traefik.http.routers.{{ keycloak_id }}.tls.certresolver=letsencrypt"', '"traefik.http.services.{{ keycloak_id }}.loadbalancer.server.port={{ service_port }}"', + + '"traefik.http.routers.{{ keycloak_id }}-state-change.service={{ keycloak_id }}"', + '"traefik.http.routers.{{ keycloak_id }}-state-change.rule=Host(`{{ stage_server_domain }}`)&&Method(`POST`,`PUT`,`DELETE`, `PATCH`)"', + '"traefik.http.routers.{{ keycloak_id }}-state-change.entrypoints=websecure"', + '"traefik.http.routers.{{ keycloak_id }}-state-change.tls=true"', + '"traefik.http.routers.{{ keycloak_id }}-state-change.tls.certresolver=letsencrypt"', + '"traefik.http.services.{{ keycloak_id }}-state-change.loadbalancer.server.port={{ service_port }}"', + '"traefik.http.routers.{{ keycloak_id }}-state-change.middlewares={{ keycloak_id }}-state-change-ipwhitelist"', + '"traefik.http.middlewares.{{ keycloak_id }}-state-change-ipwhitelist.ipwhitelist.sourcerange={{ ip_whitelist | join(",") }}"', ] keycloak_docker: {