From a29efe00e03d8caf94e48605885cad011f2590ff Mon Sep 17 00:00:00 2001 From: Albert Stefanov Date: Sat, 17 Feb 2024 11:00:06 +0200 Subject: [PATCH] Keycloak works, add example yml --- ansible/.gitignore | 1 + ansible/group_vars/all/ssh_users.yml | 6 ++ .../host_vars/infrahost/keycloak.yml.example | 23 +++++++ .../infrahost/postgresql.yml.example | 2 + ansible/roles/auth-server/tasks/keycloak.yml | 65 +++++++++++++------ ansible/roles/auth-server/tasks/main.yml | 4 +- .../templates/units/sso-keycloak.container.j2 | 6 +- ansible/roles/container-user/tasks/main.yml | 18 ++--- .../roles/postgresql-server/tasks/main.yml | 11 +++- ansible/roles/reverse-proxy/files/Caddyfile | 12 ++++ ansible/roles/reverse-proxy/handlers/main.yml | 4 ++ ansible/roles/reverse-proxy/tasks/main.yml | 25 +++++++ ansible/tasks/create_postgres_db.yml | 35 ++++++---- ansible/tasks/create_vhost.yml | 27 ++++++++ ansible/templates/vhost.caddy.j2 | 17 +++++ 15 files changed, 207 insertions(+), 49 deletions(-) create mode 100644 ansible/.gitignore create mode 100644 ansible/group_vars/all/ssh_users.yml create mode 100644 ansible/host_vars/infrahost/keycloak.yml.example create mode 100644 ansible/host_vars/infrahost/postgresql.yml.example create mode 100644 ansible/roles/reverse-proxy/files/Caddyfile create mode 100644 ansible/roles/reverse-proxy/handlers/main.yml create mode 100644 ansible/roles/reverse-proxy/tasks/main.yml create mode 100644 ansible/tasks/create_vhost.yml create mode 100644 ansible/templates/vhost.caddy.j2 diff --git a/ansible/.gitignore b/ansible/.gitignore new file mode 100644 index 0000000..8180076 --- /dev/null +++ b/ansible/.gitignore @@ -0,0 +1 @@ +*.secret.yml diff --git a/ansible/group_vars/all/ssh_users.yml b/ansible/group_vars/all/ssh_users.yml new file mode 100644 index 0000000..8520069 --- /dev/null +++ b/ansible/group_vars/all/ssh_users.yml @@ -0,0 +1,6 @@ +global_ssh_keys: + - albert_rsa + - ignisf + - zeridon + - shteryana + - dexxter00 diff --git a/ansible/host_vars/infrahost/keycloak.yml.example b/ansible/host_vars/infrahost/keycloak.yml.example new file mode 100644 index 0000000..1f1f360 --- /dev/null +++ b/ansible/host_vars/infrahost/keycloak.yml.example @@ -0,0 +1,23 @@ +keycloak_config: + hostname: auth.openfest.org + db: + host: host.containers.internal + user: keycloak + database: keycloak + password: slojnaparola + access_host: all + ansible_host: infrahost + podman: + user: auth + home: /home/auth + datadir: /home/auth/keycloak + listen_address: 127.0.0.1:9091 + reverse_proxy: + external_url: auth.openfest.org + proxy_url: 127.0.0.1:9091 + ansible_host: infrahost + tls: + type: internal + admin: + user: admin + password: slojnaparola diff --git a/ansible/host_vars/infrahost/postgresql.yml.example b/ansible/host_vars/infrahost/postgresql.yml.example new file mode 100644 index 0000000..62e883f --- /dev/null +++ b/ansible/host_vars/infrahost/postgresql.yml.example @@ -0,0 +1,2 @@ +postgresql: + listen_addresses: "*" diff --git a/ansible/roles/auth-server/tasks/keycloak.yml b/ansible/roles/auth-server/tasks/keycloak.yml index 4c65dba..07e127a 100644 --- a/ansible/roles/auth-server/tasks/keycloak.yml +++ b/ansible/roles/auth-server/tasks/keycloak.yml @@ -3,46 +3,62 @@ - name: Check parameters ansible.builtin.assert: that: - - keycloak_podman_user_name is defined - - keycloak_db_password is defined + - keycloak.podman.user is defined + - keycloak.db.password is defined - name: Create PostgreSQL database - include_tasks: create_postgres_db.yml + ansible.builtin.include_tasks: create_postgres_db.yml vars: - postgres_username: keycloak - postgres_database: keycloak - postgres_password: "{{ keycloak_db_password }}" #TODO: change for a password manager + user: "{{ keycloak.db.user }}" + database: "{{ keycloak.db.database }}" + password: "{{ keycloak.db.password }}" + access_host: "{{ keycloak.db.access_host | default(omit) }}" args: apply: - delegate_to: "{{ keycloak_db_ansible_host | default(omit) }}" + delegate_to: "{{ keycloak.db.ansible_host | default(omit) }}" - name: Set up container user - include_role: + ansible.builtin.include_role: name: container-user vars: - podman_user: "{{ keycloak_podman_user_name }}" - podman_home: "{{ keycloak_podman_user_home | default(omit) }}" - podman_uid: "{{ keycloak_podman_user_uid | default(omit) }}" - - #- name: Create secrets - # containers.podman.podman_secret: - # become: true - # become_user: "{{ keycloak_podman_user_name }}" + user: "{{ keycloak.podman.user }}" + home: "{{ keycloak.podman.home | default(omit) }}" + uid: "{{ keycloak.podman.uid | default(omit) }}" - name: Create data directories ansible.builtin.file: state: directory path: "{{ item }}" with_items: - - "{{ keycloak_data_dir }}/keystore/" + - "{{ keycloak.datadir }}/keystore/" - name: Upload unit files ansible.builtin.template: src: units/sso-keycloak.container.j2 dest: ~/.config/containers/systemd/sso-keycloak.container become: true - become_user: "{{ keycloak_podman_user_name }}" + become_user: "{{ keycloak.podman.user }}" +- name: Set up podman secrets + containers.podman.podman_secret: + name: "{{ item.key }}" + data: "{{ item.value }}" + state: present + skip_existing: false + force: true + vars: + secrets: + keycloak-admin-user: "{{ keycloak.admin.user }}" + keycloak-admin-password: "{{ keycloak.admin.password }}" + keycloak-db-host: "{{ keycloak.db.host }}" + keycloak-db-name: "{{ keycloak.db.database }}" + keycloak-db-user: "{{ keycloak.db.user }}" + keycloak-db-password: "{{ keycloak.db.password }}" + with_dict: "{{ secrets }}" + no_log: true # Secret values + + become: true + become_user: "{{ keycloak.podman.user }}" # Note: enabled in the unit file - name: Start Keycloak @@ -52,4 +68,15 @@ daemon_reload: true state: started become: true - become_user: "{{ keycloak_podman_user_name }}" + become_user: "{{ keycloak.podman.user }}" + +- name: Set up reverse proxy + ansible.builtin.include_tasks: create_vhost.yml + vars: + external_url: "{{ keycloak.reverse_proxy.external_url }}" + proxy_url: "{{ keycloak.reverse_proxy.proxy_url }}" + app_name: "{{ keycloak.reverse_proxy.app_name | default('keycloak') }}" + tls: "{{ keycloak.reverse_proxy.tls | default(omit) }}" + args: + apply: + delegate_to: "{{ keycloak.reverse_proxy.ansible_host | default(omit) }}" diff --git a/ansible/roles/auth-server/tasks/main.yml b/ansible/roles/auth-server/tasks/main.yml index 611df3b..f679f30 100644 --- a/ansible/roles/auth-server/tasks/main.yml +++ b/ansible/roles/auth-server/tasks/main.yml @@ -6,6 +6,4 @@ - name: Set up Keycloak include_tasks: keycloak.yml vars: - podman_user_name: "{{ keycloak_podman_user_name }}" - podman_user_home: "{{ keycloak_podman_user_home | default(omit) }}" - podman_user_uid: "{{ keycloak_podman_user_uid | default(omit) }}" + keycloak: "{{ keycloak_config }}" diff --git a/ansible/roles/auth-server/templates/units/sso-keycloak.container.j2 b/ansible/roles/auth-server/templates/units/sso-keycloak.container.j2 index f2f7bd2..e4d1265 100644 --- a/ansible/roles/auth-server/templates/units/sso-keycloak.container.j2 +++ b/ansible/roles/auth-server/templates/units/sso-keycloak.container.j2 @@ -6,7 +6,7 @@ ContainerName=sso-keycloak Image=quay.io/keycloak/keycloak:latest Environment=JAVA_OPTS="-Xms64m -Xmx512m -XX:MetaspaceSize=96M -XX:MaxMetaspaceSize=256m -Dfile.encoding=UTF-8 -Dsun.stdout.encoding=UTF-8 -Dsun.err.encoding=UTF-8 -Dstdout.encoding=UTF-8 -Dstderr.encoding=UTF-8 -XX:+ExitOnOutOfMemoryError -Djava.security.egd=file:/dev/urandom -XX:+UseG1GC -XX:MinHeapFreeRatio=10 -XX:MaxHeapFreeRatio=80 -XX:GCTimeRatio=4 -XX:AdaptiveSizePolicyWeight=90 -XX:FlightRecorderOptions=stackdepth=512" -Volume={{ keycloak_data_dir }}/keystore/:/keystore/ +Volume={{ keycloak.datadir }}/keystore/:/keystore/ Secret=keycloak-admin-user,type=env,target=KEYCLOAK_ADMIN Secret=keycloak-admin-password,type=env,target=KEYCLOAK_ADMIN_PASSWORD @@ -18,9 +18,9 @@ Secret=keycloak-db-password,type=env,target=KC_DB_PASSWORD Environment=KC_DB=postgres Environment=KC_HEALTH_ENABLED=true -Exec=start --features=preview --hostname {{ keycloak_hostname }} --proxy edge +Exec=start --features=preview --hostname {{ keycloak.hostname }} --proxy edge -PublishPort={{ keycloak_listen_address }}:8080 +PublishPort={{ keycloak.listen_address }}:8080 AutoUpdate=registry [Install] WantedBy=default.target diff --git a/ansible/roles/container-user/tasks/main.yml b/ansible/roles/container-user/tasks/main.yml index 5cd38e5..12495a7 100644 --- a/ansible/roles/container-user/tasks/main.yml +++ b/ansible/roles/container-user/tasks/main.yml @@ -3,7 +3,7 @@ - name: Check if required parameters are set ansible.builtin.assert: that: - - podman_user_name is defined + - user is defined - name: Set up as container host ansible.builtin.include_role: @@ -11,17 +11,17 @@ - name: Create user ansible.builtin.user: - name: "{{ podman_user_name }}" - home: "{{ podman_user_home | default(omit) }}" - uid: "{{ podman_user_uid | default(omit) }}" + name: "{{ user }}" + home: "{{ home | default(omit) }}" + uid: "{{ uid | default(omit) }}" state: present - name: Add public keys for user '{{ podman_user_name }}' ansible.posix.authorized_key: - user: "{{ podman_user_name }}" + user: "{{ user }}" key: "{{ lookup('file', '../../access/keys/' + item + '.pub') }}" state: present # Note: we don't remove other/existing keys - with_items: "{{ global_ssh_keys + (ssh_keys[podman_user_name] | default([])) + (ssh_keys['*'] | default([])) }}" + with_items: "{{ global_ssh_keys + (ssh_keys[user] | default([])) + (ssh_keys['*'] | default([])) }}" - name: Create unit files dir @@ -29,15 +29,15 @@ path: ~/.config/containers/systemd state: directory become: true - become_user: "{{ podman_user_name }}" + become_user: "{{ user }}" # Note: We check whether lingering is already enabled so we show as OK/skipped instead of changed - name: Check if user is lingering ansible.builtin.stat: - path: "/var/lib/systemd/linger/{{ podman_user_name }}" + path: "/var/lib/systemd/linger/{{ user }}" register: user_lingering - name: Enable session lingering - ansible.builtin.command: "loginctl enable-linger {{ podman_user_name }}" + ansible.builtin.command: "loginctl enable-linger {{ user }}" when: - not user_lingering.stat.exists diff --git a/ansible/roles/postgresql-server/tasks/main.yml b/ansible/roles/postgresql-server/tasks/main.yml index e1845d9..8b5ff85 100644 --- a/ansible/roles/postgresql-server/tasks/main.yml +++ b/ansible/roles/postgresql-server/tasks/main.yml @@ -27,4 +27,13 @@ become_user: postgres community.postgresql.postgresql_user: name: root - role_attr_flags: SUPERUSER \ No newline at end of file + role_attr_flags: SUPERUSER + +- name: Change listen addresses + community.postgresql.postgresql_set: + name: listen_addresses + value: "{{ postgresql.listen_addresses }}" + become: true + become_user: postgres + when: postgresql.listen_addresses is defined + notify: Restart PostgreSQL diff --git a/ansible/roles/reverse-proxy/files/Caddyfile b/ansible/roles/reverse-proxy/files/Caddyfile new file mode 100644 index 0000000..cc7db48 --- /dev/null +++ b/ansible/roles/reverse-proxy/files/Caddyfile @@ -0,0 +1,12 @@ +(vhost-access-log) { + log { + output file /var/log/caddy/{args[0]}-access.log + } +} + +# Note: Requires a new caddy version +#{ +# persist_config off +#} + +import /etc/caddy/sites-enabled/*.caddy diff --git a/ansible/roles/reverse-proxy/handlers/main.yml b/ansible/roles/reverse-proxy/handlers/main.yml new file mode 100644 index 0000000..73bfca2 --- /dev/null +++ b/ansible/roles/reverse-proxy/handlers/main.yml @@ -0,0 +1,4 @@ +- name: Reload Caddy + ansible.builtin.service: + name: caddy + state: reloaded diff --git a/ansible/roles/reverse-proxy/tasks/main.yml b/ansible/roles/reverse-proxy/tasks/main.yml new file mode 100644 index 0000000..c6965a7 --- /dev/null +++ b/ansible/roles/reverse-proxy/tasks/main.yml @@ -0,0 +1,25 @@ +--- + +- name: Install Caddy + ansible.builtin.package: + name: caddy + state: present + +- name: Update Caddyfile + ansible.builtin.copy: + src: Caddyfile + dest: /etc/caddy/Caddyfile + +- name: Create site config directories + ansible.builtin.file: + path: "{{ item }}" + state: directory + with_items: + - /etc/caddy/sites-available + - /etc/caddy/sites-enabled + +- name: Enable and start the Caddy server + ansible.builtin.service: + name: caddy.service + enabled: true + state: started diff --git a/ansible/tasks/create_postgres_db.yml b/ansible/tasks/create_postgres_db.yml index 6e6b23c..0fa7b7b 100644 --- a/ansible/tasks/create_postgres_db.yml +++ b/ansible/tasks/create_postgres_db.yml @@ -3,9 +3,9 @@ - name: Check params ansible.builtin.assert: that: - - postgres_username is defined - - postgres_database is defined - - not(postgres_access_host is defined and postgres_password is defined) + - user is defined + - database is defined + - not(access_host is defined and password is not defined) - name: Set up PostgreSQL ansible.builtin.include_role: @@ -13,25 +13,32 @@ - name: Create user community.postgresql.postgresql_user: - name: "{{ postgres_username }}" - password: "{{ postgres_password | default(omit) }}" + name: "{{ user }}" + password: "{{ password | default(omit) }}" become: true become_user: postgres -- name: Create postgres_database +- name: Create database community.postgresql.postgresql_db: - name: "{{ postgres_database }}" - owner: "{{ postgres_username }}" + name: "{{ database }}" + owner: "{{ user }}" become: true become_user: postgres - -- name: Update pg_hba scram +- name: Get pg_hba.conf location + community.postgresql.postgresql_query: + query: SHOW hba_file + become: true + become_user: postgres + register: postgres_hba_file_query + +- name: Update pg_hba scram authentication community.postgresql.postgresql_pg_hba: contype: host - users: "{{ postgres_username }}" - source: "{{ postgres_access_host }}" - databases: "{{ postgres_database }}" + users: "{{ user }}" + source: "{{ access_host }}" + databases: "{{ database }}" method: "scram-sha-256" - when: postgres_access_host is defined + dest: "{{ postgres_hba_file_query.query_result[0].hba_file }}" + when: access_host is defined notify: Restart PostgreSQL diff --git a/ansible/tasks/create_vhost.yml b/ansible/tasks/create_vhost.yml new file mode 100644 index 0000000..9ea16ff --- /dev/null +++ b/ansible/tasks/create_vhost.yml @@ -0,0 +1,27 @@ +--- + +- name: Check params + ansible.builtin.assert: + that: + - app_name is defined + - external_url is defined + - proxy_url is defined + - not(tls.type == "cloudflare" and tls.cloudflare_token is undefined) + - not(tls.type == "file" and (tls.cert is undefined or tls.key is undefined)) + - tls.type is not defined or (tls.type in ['auto', 'internal', 'cloudflare', 'file'] ) + +- name: Set up Caddy + ansible.builtin.include_role: + name: reverse-proxy + +- name: Template vhost file + ansible.builtin.template: + src: vhost.caddy.j2 + dest: "/etc/caddy/sites-available/{{ app_name }}.caddy" + +- name: Symlink vhost + ansible.builtin.file: + src: "/etc/caddy/sites-available/{{ app_name }}.caddy" + dest: "/etc/caddy/sites-enabled/{{ app_name }}.caddy" + state: link + notify: Reload Caddy diff --git a/ansible/templates/vhost.caddy.j2 b/ansible/templates/vhost.caddy.j2 new file mode 100644 index 0000000..58b2cd9 --- /dev/null +++ b/ansible/templates/vhost.caddy.j2 @@ -0,0 +1,17 @@ +{{ external_url }} { + + {% if tls is defined and tls.type != 'auto' %} + {% if tls.type == 'internal' %} + tls internal + {% elif tls.type == 'cloudflare' %} + tls { + dns cloudflare {{ tls.cloudflare_token }} + } + {% elif tls.type == 'file' %} + tls {{ tls.cert }} {{ tls.key }} + {% endif %} + {% endif %} + + reverse_proxy {{ proxy_url }} + import vhost-access-log {{ app_name }} +}