From 21cdd70dd6479a280c8c57d5578eb9dd7ead25e4 Mon Sep 17 00:00:00 2001 From: Anatoli Tsikhamirau Date: Tue, 15 Mar 2022 18:25:00 +0100 Subject: [PATCH] Passwordless SSH access for PostgreSQL used in cloning standby nodes (#2978) --- .../_shared/scripts/add-repos-debian.sh | 1 + .../extensions/replication/extension.yml | 6 +- .../tasks/helpers/configure-ssh-access.yml | 116 ++++++++++++++++++ .../playbooks/roles/postgresql/tasks/main.yml | 6 + .../replication/pg-new/standby-clone.yml | 4 +- .../roles/postgresql/tasks/upgrade/main.yml | 8 ++ docs/changelogs/CHANGELOG-2.0.md | 4 +- 7 files changed, 141 insertions(+), 4 deletions(-) create mode 100644 ansible/playbooks/roles/postgresql/tasks/helpers/configure-ssh-access.yml diff --git a/ansible/playbooks/roles/postgresql/molecule/_shared/scripts/add-repos-debian.sh b/ansible/playbooks/roles/postgresql/molecule/_shared/scripts/add-repos-debian.sh index ff536fab8b..f403d6c6fc 100644 --- a/ansible/playbooks/roles/postgresql/molecule/_shared/scripts/add-repos-debian.sh +++ b/ansible/playbooks/roles/postgresql/molecule/_shared/scripts/add-repos-debian.sh @@ -4,6 +4,7 @@ apt update && apt -y install wget gpg-agent wget -qO - https://www.postgresql.org/media/keys/ACCC4CF8.asc | sudo apt-key add - echo "deb http://apt.postgresql.org/pub/repos/apt focal-pgdg main" | tee /etc/apt/sources.list.d/pgdg.list +echo "deb http://apt-archive.postgresql.org/pub/repos/apt focal-pgdg-archive main" | tee -a /etc/apt/sources.list.d/pgdg.list wget -qO - https://dl.2ndquadrant.com/gpg-key.asc | apt-key add - echo "deb https://dl.2ndquadrant.com/default/release/apt focal-2ndquadrant main" | tee /etc/apt/sources.list.d/2ndquadrant-dl-default-release.list diff --git a/ansible/playbooks/roles/postgresql/tasks/extensions/replication/extension.yml b/ansible/playbooks/roles/postgresql/tasks/extensions/replication/extension.yml index 5276d7a329..b7e0ae6cca 100644 --- a/ansible/playbooks/roles/postgresql/tasks/extensions/replication/extension.yml +++ b/ansible/playbooks/roles/postgresql/tasks/extensions/replication/extension.yml @@ -163,9 +163,11 @@ become_user: postgres command: >- {{ repmgr.bin_dir[ansible_os_family] }}/repmgr standby clone - -h {{ hostvars[groups.postgresql[0]].ansible_default_ipv4.address }} + -h {{ hostvars[_pg_primary_node].ansible_default_ipv4.address }} -U {{ specification.extensions.replication.privileged_user_name }} -d {{ specification.extensions.replication.repmgr_database }} -p 5432 --force + --copy-external-config-files=samepath + --remote-user=postgres - name: Extensions | repmgr | Start PostgreSQL service systemd: @@ -176,7 +178,7 @@ become_user: postgres command: >- {{ repmgr.bin_dir[ansible_os_family] }}/repmgr standby register --force - --upstream-conninfo='host={{ hostvars[groups['postgresql'][0]]['ansible_default_ipv4']['address'] }}, + --upstream-conninfo='host={{ hostvars[_pg_primary_node]['ansible_default_ipv4']['address'] }}, user={{ specification.extensions.replication.replication_user_name }}, dbname={{ specification.extensions.replication.repmgr_database }}, connect_timeout=2' diff --git a/ansible/playbooks/roles/postgresql/tasks/helpers/configure-ssh-access.yml b/ansible/playbooks/roles/postgresql/tasks/helpers/configure-ssh-access.yml new file mode 100644 index 0000000000..24bb845290 --- /dev/null +++ b/ansible/playbooks/roles/postgresql/tasks/helpers/configure-ssh-access.yml @@ -0,0 +1,116 @@ +--- +- name: Ensure openssh packages are installed + package: + name: "{{ _packages[ansible_os_family] }}" + state: present + vars: + _packages: + Debian: + - openssh-client + - openssh-server + RedHat: + - openssh-clients + - openssh-server + +- name: Ensure sshd is started and enabled + systemd: + name: sshd + enabled: true + state: started + +- name: Set getent_passwd fact + getent: + database: passwd + key: "{{ username }}" + split: ":" + +- become_user: "{{ username }}" + vars: + home_dir: "{{ ansible_facts.getent_passwd[username][4] }}" + ssh_key_type: ed25519 + ssh_key_path: "{{ home_dir }}/.ssh/id_{{ ssh_key_type }}" + ssh_key_backup_path: "{{ home_dir }}/.ssh/id_{{ ssh_key_type }}.bkp" + block: + - name: Ensure {{ home_dir }}/.ssh directory exists + file: + path: "{{ home_dir }}/.ssh/" + state: directory + mode: u=rwx,go= + + # Backup is recommended for full_idempotence option: + # https://docs.ansible.com/ansible/latest/collections/community/crypto/openssh_keypair_module.html#parameter-regenerate + - name: Backup ssh key + block: + - name: Check if file {{ ssh_key_path }} exists + stat: + path: "{{ ssh_key_path }}" + get_attributes: false + get_checksum: true + get_mime: false + register: ssh_key + + - name: Check if backup file {{ ssh_key_backup_path }} exists + stat: + path: "{{ ssh_key_backup_path }}" + get_attributes: false + get_checksum: true + get_mime: false + register: ssh_key_backup + + - name: Backup {{ ssh_key_path }} + when: + - ssh_key.stat.exists + - not ssh_key_backup.stat.exists or ssh_key.stat.checksum != ssh_key_backup.stat.checksum + copy: + src: "{{ ssh_key_path }}" + dest: "{{ ssh_key_backup_path }}" + remote_src: true + force: true + register: copy_result + # set status to changed only when file is overwritten not to fail Molecule idempotence test for the first run + changed_when: + - copy_result.changed + - ssh_key_backup.stat.exists + + - name: Generate openssh keypair + openssh_keypair: + path: "{{ ssh_key_path }}" + type: "{{ ssh_key_type }}" + regenerate: full_idempotence + register: openssh_keypair + + - name: Set pg_authorized_keys fact + set_fact: + pg_authorized_keys: "{{ groups['postgresql'] | map('extract', hostvars) + | map(attribute='openssh_keypair.public_key') + | list + | difference([openssh_keypair.public_key]) + | join('\n') }}" + + # There was an attempt to add each key in a loop, + # but from time to time not all keys are in place and that leads to connection issues + - name: Add public keys to authorized_keys + authorized_key: + user: "{{ username }}" + state: present + key: "{{ pg_authorized_keys }}" + + - name: Run ssh-keyscan + when: host != inventory_hostname + command: "ssh-keyscan {{ hostvars[host]['ansible_default_ipv4']['address'] }}" + changed_when: false + loop: "{{ groups['postgresql'] }}" + loop_control: + loop_var: host + register: ssh_known_host_results + + - name: Ensure public keys are in {{ home_dir }}/.ssh/known_host + when: host_info.host != inventory_hostname + known_hosts: + name: "{{ hostvars[host_info.host]['ansible_default_ipv4']['address'] }}" + key: "{{ host_info.stdout }}" + path: "{{ home_dir }}/.ssh/known_hosts" + loop: "{{ ssh_known_host_results.results }}" + loop_control: + loop_var: host_info + label: "{{ host_info.host }}" diff --git a/ansible/playbooks/roles/postgresql/tasks/main.yml b/ansible/playbooks/roles/postgresql/tasks/main.yml index 4c322da3e8..4420aa25c9 100644 --- a/ansible/playbooks/roles/postgresql/tasks/main.yml +++ b/ansible/playbooks/roles/postgresql/tasks/main.yml @@ -75,6 +75,12 @@ - /var/log/postgresql - "{{ pg.data_dir[ansible_os_family] }}" # Permissions should be u=rwx (0700) or u=rwx,g=rx (0750) +- name: Configure ssh for 'postgres' user + when: groups['postgresql']|length > 1 + vars: + username: postgres + include_tasks: helpers/configure-ssh-access.yml + - name: RedHat | Initialize database when: ansible_os_family == 'RedHat' block: diff --git a/ansible/playbooks/roles/postgresql/tasks/upgrade/extensions/replication/pg-new/standby-clone.yml b/ansible/playbooks/roles/postgresql/tasks/upgrade/extensions/replication/pg-new/standby-clone.yml index e3b5cb526a..cb71e9d933 100644 --- a/ansible/playbooks/roles/postgresql/tasks/upgrade/extensions/replication/pg-new/standby-clone.yml +++ b/ansible/playbooks/roles/postgresql/tasks/upgrade/extensions/replication/pg-new/standby-clone.yml @@ -25,9 +25,11 @@ become_user: postgres command: >- {{ new_version.repmgr.bin_dir[ansible_os_family] }}/repmgr standby clone - -h {{ pg_primary_node }} + -h {{ hostvars[pg_primary_node].ansible_default_ipv4.address }} -U {{ manifest.specification.extensions.replication.privileged_user_name }} -d {{ manifest.specification.extensions.replication.repmgr_database }} -p 5432 --force + --copy-external-config-files=samepath + --remote-user=postgres register: repmgr_standby_clone changed_when: repmgr_standby_clone.rc == 0 diff --git a/ansible/playbooks/roles/postgresql/tasks/upgrade/main.yml b/ansible/playbooks/roles/postgresql/tasks/upgrade/main.yml index 9e0e522758..b2431dcc1a 100644 --- a/ansible/playbooks/roles/postgresql/tasks/upgrade/main.yml +++ b/ansible/playbooks/roles/postgresql/tasks/upgrade/main.yml @@ -68,6 +68,14 @@ completed: Host already upgraded, nothing to do failed: Re-run upgrade after a failure +# Passwordless access for 'postgres' user was added in Epiphany v2.0 +# When it's not required to support upgrade from previous versions, this task can be removed +- name: Configure ssh for 'postgres' user + when: groups['postgresql']|length > 1 + vars: + username: postgres + include_tasks: helpers/configure-ssh-access.yml + - name: Include upgrade tasks when: postgresql_upgrade_status != 'completed' include_tasks: upgrade/run-upgrade.yml diff --git a/docs/changelogs/CHANGELOG-2.0.md b/docs/changelogs/CHANGELOG-2.0.md index 8ff9ebefb4..0ea5758ac5 100644 --- a/docs/changelogs/CHANGELOG-2.0.md +++ b/docs/changelogs/CHANGELOG-2.0.md @@ -7,7 +7,7 @@ - [#959](https://github.com/epiphany-platform/epiphany/issues/959) - Add usage of use_network_security_groups to disable NSG on AWS - [#2701](https://github.com/epiphany-platform/epiphany/issues/2701) - Epicli prepare - generate files in separate directory - [#2812](https://github.com/epiphany-platform/epiphany/issues/2812) - Extend K8s config validation -- [#2950](https://github.com/epiphany-platform/epiphany/issues/2950) - CLI refactor to make it more consistant +- [#2950](https://github.com/epiphany-platform/epiphany/issues/2950) - CLI refactor to make it more consistent - [#2844](https://github.com/epiphany-platform/epiphany/issues/2844) - Refactor K8s upgrade task in order to simplify its flow - [#2985](https://github.com/epiphany-platform/epiphany/issues/2985) - Make RabbitMQ Plugins configurable - [#2974](https://github.com/epiphany-platform/epiphany/issues/2974) - Refactor Apply command @@ -17,6 +17,7 @@ - [#2858](https://github.com/epiphany-platform/epiphany/issues/2858) - Make Ruby spec tests code compliant with rubocop lint rules - [#2975](https://github.com/epiphany-platform/epiphany/issues/2975) - Copy only required files - [#2991](https://github.com/epiphany-platform/epiphany/issues/2991) - Add automatic backup creation for download requirements +- [#2448](https://github.com/epiphany-platform/epiphany/issues/2448) - Passwordless SSH communication for postgres user between DB nodes ### Fixed @@ -37,6 +38,7 @@ - [#2989](https://github.com/epiphany-platform/epiphany/issues/2989) - Task `Remove swap from /etc/fstab` does not remove swap entry from file - [#2907](https://github.com/epiphany-platform/epiphany/issues/2907) - Backup/recovery commands fail when executed directly after upgrade - [#3025](https://github.com/epiphany-platform/epiphany/issues/3025) - Running yum commands may hang waiting for user input +- [#2728](https://github.com/epiphany-platform/epiphany/issues/2728) - PostgreSQL's configuration files located outside the data directory are not copied by repmgr ### Updated