commit c72f2ab75cc95d8d885cd183a2e500a142a6aebf Author: Nicolas MASSE Date: Mon Mar 21 15:38:26 2022 +0100 initial commit diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..e823376 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,2 @@ +*.tar.gz filter=lfs diff=lfs merge=lfs -text +*.jar filter=lfs diff=lfs merge=lfs -text diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1a0cdd4 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +ansible.cfg +vault.yaml +rhsso*.zip +rh-sso*.zip +.ansible-vault-password diff --git a/files/postgresql-42.3.3.jar b/files/postgresql-42.3.3.jar new file mode 100644 index 0000000..0297cd1 --- /dev/null +++ b/files/postgresql-42.3.3.jar @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:eed0604f512ba44817954de99a07e2a5470aa4bfcb481d4e63a93e0ff0e0aede +size 1039047 diff --git a/files/traefik_v2.6.1_linux_amd64.tar.gz b/files/traefik_v2.6.1_linux_amd64.tar.gz new file mode 100644 index 0000000..a27ec5d --- /dev/null +++ b/files/traefik_v2.6.1_linux_amd64.tar.gz @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f6e10a6f2bf1844fe9e68406d7ed6e693ea9678184e6ead6a0f14a342d643dd5 +size 26843817 diff --git a/group_vars/all/all.yaml b/group_vars/all/all.yaml new file mode 100644 index 0000000..74c626f --- /dev/null +++ b/group_vars/all/all.yaml @@ -0,0 +1,6 @@ +db_username: keycloak +db_name: keycloak +db_hostname: db.itix.lab +keycloak_dir: /opt/rh-sso-7.5 +keycloak_url: http://lb.itix.lab +keycloak_admin_username: admin diff --git a/install-db.yaml b/install-db.yaml new file mode 100644 index 0000000..3c2bcbf --- /dev/null +++ b/install-db.yaml @@ -0,0 +1,94 @@ +- name: Install PostgreSQL + hosts: db + gather_facts: yes + become: yes + tasks: + - name: Install PostgreSQL + dnf: + name: + - postgresql-server + - postgresql-docs + - postgresql-upgrade + - postgresql-contrib + - python3-psycopg2 # Needed by the community.general.postgresql_* tasks + state: installed + + - name: Initialize the database + command: postgresql-setup --initdb + args: + creates: /var/lib/pgsql/data/log/ + + - name: Listen on all network interfaces + lineinfile: + insertbefore: '^ *#* *listen_addresses *=' + path: /var/lib/pgsql/data/postgresql.conf + regexp: '^ *listen_addresses *= *' + line: "listen_addresses = '0.0.0.0'" + register: postgresql_conf1 + + - name: Enable scram-sha-256 + lineinfile: + insertbefore: '^ *#* *password_encryption *=' + path: /var/lib/pgsql/data/postgresql.conf + regexp: '^ *password_encryption *= *' + line: "password_encryption = scram-sha-256" + register: postgresql_conf2 + + - name: Enable password authentication instead of ident + community.general.postgresql_pg_hba: + dest: /var/lib/pgsql/data/pg_hba.conf + contype: host + databases: all + users: all + address: '{{ item.address }}' + method: '{{ item.method }}' + state: '{{ item.state }}' + loop: + - address: 127.0.0.1/32 + method: scram-sha-256 + state: present + - address: ::1/128 + method: scram-sha-256 + state: present + - address: 0.0.0.0/0 + method: scram-sha-256 + state: present + register: pghba_conf + + - name: Reload PostgreSQL when needed + systemd: + name: postgresql + enabled: true + state: reloaded + when: postgresql_conf1.changed or postgresql_conf2.changed or pghba_conf.changed + + - name: Ensure the PostgreSQL service is started and enabled + systemd: + name: postgresql + enabled: true + state: started + + - name: Wait for PostgreSQL to be ready + community.general.postgresql_query: + db: template1 + query: SELECT version() + become_user: postgres + retries: 20 + delay: 5 + register: healthcheck + until: not healthcheck.failed + + - name: Create the PostgreSQL database for Keycloak + community.general.postgresql_db: + name: '{{ db_name }}' + become_user: postgres + + - name: Create the PostgreSQL user for Keycloak + community.general.postgresql_user: + name: '{{ db_username }}' + password: '{{ db_password }}' + login_db: '{{ db_name }}' + priv: ALL + become_user: postgres + environment: + PGOPTIONS: "-c password_encryption=scram-sha-256" diff --git a/install-kc.yaml b/install-kc.yaml new file mode 100644 index 0000000..4a27aed --- /dev/null +++ b/install-kc.yaml @@ -0,0 +1,105 @@ +- name: Install Keycloak + hosts: keycloak + gather_facts: yes + become: yes + tasks: + - name: Create the keycloak user + user: + name: keycloak + system: true + home: '{{ keycloak_dir }}' + create_home: false + state: present + + - name: Install pre-requisites + dnf: + name: + - unzip + - java-11-openjdk-headless + state: installed + + - name: Unpack Keycloak + unarchive: + src: 'rh-sso-7.5.0-server-dist.zip' + dest: /opt + owner: keycloak + creates: '{{ keycloak_dir }}' + + - name: Upload Keycloak patches + copy: + src: '{{ item }}' + dest: '/tmp/{{ item }}' + loop: + - rh-sso-7.5.1-patch.zip + - rhsso-1974.zip + - rhsso-2054.zip + + - name: Apply Keycloak patches + command: '{{ keycloak_dir }}/bin/jboss-cli.sh --command="patch apply /tmp/{{ item }}"' + loop: + - rh-sso-7.5.1-patch.zip + - rhsso-1974.zip + - rhsso-2054.zip + become_user: keycloak + + - name: Create modules/system/layers/keycloak/org/postgresql/jdbc/main + file: + state: directory + path: '{{ keycloak_dir }}/modules/system/layers/base/org/postgresql/jdbc/main' + owner: keycloak + + - name: Copy postgresql JDBC driver + copy: + src: postgresql-42.3.3.jar + dest: '{{ keycloak_dir }}/modules/system/layers/base/org/postgresql/jdbc/main' + owner: keycloak + + - name: Reference the jdbc driver in module.xml + template: + src: module.xml.j2 + dest: '{{ keycloak_dir }}/modules/system/layers/base/org/postgresql/jdbc/main/module.xml' + owner: keycloak + + # Sample cli scripts are here: https://github.com/keycloak/keycloak-containers/tree/15.0.2/server/tools/cli + - name: Upload the Keycloak configuration script + template: + src: keycloak-custom.cli + dest: '{{ keycloak_dir }}/custom.cli' + owner: keycloak + + - name: Configure keycloak + command: '{{ keycloak_dir }}/bin/jboss-cli.sh --file={{ keycloak_dir }}/custom.cli' + become_user: keycloak + + - name: Install the keycloak service unit + template: + src: keycloak.service + dest: /etc/systemd/system/keycloak.service + register: systemd_unit + tags: config + + - name: Configure the keycloak service unit + template: + src: keycloak.env + dest: '{{ keycloak_dir }}/keycloak.env' + register: unit_config + tags: config + + - name: Reload systemd + systemd: + daemon-reload: yes + when: systemd_unit.changed + tags: config + + - name: Create the initial admin + command: '{{ keycloak_dir }}/bin/add-user-keycloak.sh --user {{ keycloak_admin_username }} --password {{ keycloak_admin_password }}' + # only one node needs to execute this command + when: ansible_host == "sso1.itix.lab" + + - name: Start keycloak + systemd: + name: keycloak.service + state: restarted + when: systemd_unit.changed or unit_config.changed + tags: config + diff --git a/install-lb.yaml b/install-lb.yaml new file mode 100644 index 0000000..588c6e1 --- /dev/null +++ b/install-lb.yaml @@ -0,0 +1,68 @@ +- name: Install Traefik + hosts: lb + gather_facts: yes + become: yes + tasks: + - name: Create the traefik user + user: + name: traefik + system: true + home: '/opt/traefik' + create_home: false + state: present + + - name: Skaffold /opt/traefik + file: + path: '{{ item }}' + state: directory + owner: traefik + loop: + - /opt/traefik/etc + - /opt/traefik/etc/conf.d + - /opt/traefik/bin + + - name: Install traefik + unarchive: + src: traefik_v2.6.1_linux_amd64.tar.gz + dest: /opt/traefik/bin + owner: traefik + + - name: Install the systemd units + template: + src: traefik.service + dest: /etc/systemd/system/traefik.service + register: systemd_unit + tags: config + + - name: Install the systemd unit configuration + template: + src: traefik.env + dest: /opt/traefik/etc/traefik.env + register: unit_config + tags: config + + - name: Reload systemd + systemd: + daemon-reload: yes + when: systemd_unit.changed + tags: config + + - name: Configure traefik + template: + src: traefik.yaml + dest: /opt/traefik/etc/traefik.yaml + register: traefik_config + tags: config + + - name: Configure traefik + template: + src: traefik-keycloak.yaml + dest: /opt/traefik/etc/conf.d/keycloak.yaml + tags: config + + - name: Start traefik + systemd: + name: traefik.service + state: restarted + when: systemd_unit.changed or unit_config.changed or traefik_config.changed + tags: config diff --git a/install.yaml b/install.yaml new file mode 100644 index 0000000..50407d7 --- /dev/null +++ b/install.yaml @@ -0,0 +1,3 @@ +- import_playbook: install-db.yaml +- import_playbook: install-kc.yaml +- import_playbook: install-lb.yaml diff --git a/inventory.yaml b/inventory.yaml new file mode 100644 index 0000000..328da5c --- /dev/null +++ b/inventory.yaml @@ -0,0 +1,16 @@ +all: + vars: + ansible_user: nicolas + children: + db: + hosts: + db.itix.lab: + keycloak: + hosts: + sso1.itix.lab: + sso2.itix.lab: + sso3.itix.lab: + lb: + hosts: + lb.itix.lab: + diff --git a/requirements.yaml b/requirements.yaml new file mode 100644 index 0000000..8a8f78c --- /dev/null +++ b/requirements.yaml @@ -0,0 +1,3 @@ +collections: +- name: community.general + version: '>=2.2.0' # fix a bug with nmcli and bridge interfaces diff --git a/templates/keycloak-custom.cli b/templates/keycloak-custom.cli new file mode 100644 index 0000000..fd796a9 --- /dev/null +++ b/templates/keycloak-custom.cli @@ -0,0 +1,26 @@ +embed-server --server-config=standalone-ha.xml --std-out=echo + +/subsystem=undertow/server=default-server/http-listener=default: write-attribute(name=proxy-address-forwarding, value=true) +/subsystem=undertow/server=default-server/https-listener=https: write-attribute(name=proxy-address-forwarding, value=true) + +/subsystem=datasources/data-source=KeycloakDS: remove() +/subsystem=datasources/data-source=KeycloakDS: add(jndi-name=java:jboss/datasources/KeycloakDS,enabled=true,use-java-context=true,use-ccm=true, connection-url=jdbc:postgresql://{{ db_hostname }}/{{ db_name }}, driver-name=postgresql) +/subsystem=datasources/data-source=KeycloakDS: write-attribute(name=user-name, value={{ db_username }}) +/subsystem=datasources/data-source=KeycloakDS: write-attribute(name=password, value={{ db_password }}) +/subsystem=datasources/data-source=KeycloakDS: write-attribute(name=check-valid-connection-sql, value="SELECT 1") +/subsystem=datasources/data-source=KeycloakDS: write-attribute(name=background-validation, value=true) +/subsystem=datasources/data-source=KeycloakDS: write-attribute(name=background-validation-millis, value=60000) +/subsystem=datasources/data-source=KeycloakDS: write-attribute(name=flush-strategy, value=IdleConnections) +/subsystem=datasources/jdbc-driver=postgresql:add(driver-name=postgresql, driver-module-name=org.postgresql.jdbc, driver-xa-datasource-class-name=org.postgresql.xa.PGXADataSource) + +/subsystem=keycloak-server/spi=connectionsJpa/provider=default:write-attribute(name=properties.schema,value=public) + +/subsystem=infinispan/cache-container=keycloak/distributed-cache=sessions: write-attribute(name=owners, value=3) +/subsystem=infinispan/cache-container=keycloak/distributed-cache=offlineSessions: write-attribute(name=owners, value=3) +/subsystem=infinispan/cache-container=keycloak/distributed-cache=loginFailures: write-attribute(name=owners, value=3) +/subsystem=infinispan/cache-container=keycloak/distributed-cache=clientSessions: write-attribute(name=owners, value=3) +/subsystem=infinispan/cache-container=keycloak/distributed-cache=offlineClientSessions: write-attribute(name=owners, value=3) +/subsystem=infinispan/cache-container=keycloak/distributed-cache=actionTokens: write-attribute(name=owners, value=3) +/subsystem=infinispan/cache-container=keycloak/distributed-cache=authenticationSessions: write-attribute(name=owners, value=1) + +stop-embedded-server diff --git a/templates/keycloak.env b/templates/keycloak.env new file mode 100644 index 0000000..0999391 --- /dev/null +++ b/templates/keycloak.env @@ -0,0 +1 @@ +KEYCLOAK_OPTIONS="-b 0.0.0.0" diff --git a/templates/keycloak.service b/templates/keycloak.service new file mode 100644 index 0000000..e2bc71a --- /dev/null +++ b/templates/keycloak.service @@ -0,0 +1,13 @@ +[Unit] +Description=Red Hat SSO +After=network.target + +[Service] +ExecStart={{ keycloak_dir }}/bin/standalone.sh --server-config=standalone-ha.xml $KEYCLOAK_OPTIONS +WorkingDirectory={{ keycloak_dir }} +User=keycloak +EnvironmentFile={{ keycloak_dir }}/keycloak.env +Type=simple + +[Install] +WantedBy=multi-user.target default.target diff --git a/templates/module.xml.j2 b/templates/module.xml.j2 new file mode 100644 index 0000000..f41a1c3 --- /dev/null +++ b/templates/module.xml.j2 @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/templates/traefik-keycloak.yaml b/templates/traefik-keycloak.yaml new file mode 100644 index 0000000..e38a757 --- /dev/null +++ b/templates/traefik-keycloak.yaml @@ -0,0 +1,27 @@ +http: + routers: + keycloak-http: + rule: "Host(`lb.itix.lab`)" + entryPoints: + - http + - https + middlewares: + service: "keycloak" + keycloak-https: + rule: "Host(`lb.itix.lab`)" + entryPoints: + - http + - https + middlewares: + service: "keycloak" + tls: {} + services: + keycloak: + loadBalancer: + servers: + - url: "http://sso1.itix.lab:8080" + - url: "http://sso2.itix.lab:8080" + - url: "http://sso3.itix.lab:8080" + # Sticky session is required for users to login + sticky: + cookie: {} diff --git a/templates/traefik.env b/templates/traefik.env new file mode 100644 index 0000000..e69de29 diff --git a/templates/traefik.service b/templates/traefik.service new file mode 100644 index 0000000..d82901b --- /dev/null +++ b/templates/traefik.service @@ -0,0 +1,19 @@ +[Unit] +Description=The Cloud Native Application Proxy +Wants=network.target +After=network-online.target + +[Service] +Restart=always +Type=simple +EnvironmentFile=/opt/traefik/etc/traefik.env +ExecStart=/opt/traefik/bin/traefik +WorkingDirectory=/opt/traefik/etc +User=traefik +Group=traefik + +# Allow traefik to bind to <1024 ports +AmbientCapabilities=CAP_NET_BIND_SERVICE + +[Install] +WantedBy=multi-user.target default.target diff --git a/templates/traefik.yaml b/templates/traefik.yaml new file mode 100644 index 0000000..d6022b3 --- /dev/null +++ b/templates/traefik.yaml @@ -0,0 +1,19 @@ +log: + level: "INFO" + +accesslog: true + +providers: + file: + directory: /opt/traefik/etc/conf.d/ + watch: true + +global: + sendanonymoususage: false + checknewversion: false + +entryPoints: + http: + address: ":80" + https: + address: ":443"