#SPDX-License-Identifier: MIT-0 --- # tasks file for roles/init-vps # @NOTE server deployment method is based on task tags compiled herein # @TODO review 'loop' task attribute return values and make compliant changes - name: Finding SSH public keys for root ansible.builtin.find: paths: "{{ cnode_homedir | default('/home/' ~ ansible_user ~ '/.ssh') }}" # @TODO define 'cnode_homedir' in playbook patterns: "{{ ['^'] | product(keys) | map('join') | list }}" file_type: file use_regex: true register: ssh_keypairs - name: Bootstrapping VPS block: - name: Creating VPS via Linode VPS service API block: - name: Creating the VPS linode.cloud.instance: api_token: "{{ token }}" label: "{{ instance }}" type: g6-standard-2 image: "{{ operating_system }}" disk_encryption: enabled region: "{{ origin }}" private_ip: true root_pass: "{{ password }}" authorized_keys: "{{ ssh_keypairs.files | selectattr('path', 'search', '\\.pub$') | map(attribute='path') | map('lookup', 'file') | list }}" state: present register: new_instance - name: Waiting for that VPS to come online delegate_to: "{{ new_instance.instance[ip_pref][0] }}" delegate_facts: true ansible.builtin.wait_for_connection: delay: 20 timeout: 300 vars: ansible_ssh_private_key_file: "{{ chosen_privkey | default(ssh_keypairs.files | rejectattr('path', 'search', '\\.pub$') | map(attribute='path') | list | random) }}" # @TODO define 'chosen_privkey'in playbook ansible_user: root - name: Checking if that VPS has required operating system delegate_to: "{{ new_instance.instance[ip_pref][0] }}" delegate_facts: true when: ansible_facts["system"] != "Linux" ansible.builtin.fail: msg: Unsupported operating system found vars: ansible_ssh_private_key_file: "{{ chosen_privkey | default(ssh_keypairs.files | rejectattr('path', 'search', '\\.pub$') | map(attribute='path') | list | random) }}" # @TODO define 'chosen_privkey'in playbook ansible_user: root - name: Checking if that VPS has required Linux distro delegate_to: "{{ new_instance.instance[ip_pref][0] }}" delegate_facts: true when: ansible_facts["system"] == "Linux" and ansible_facts["os_family"] != "Debian" ansible.builtin.fail: msg: Unsupported Linux distro found vars: ansible_ssh_private_key_file: "{{ chosen_privkey | default(ssh_keypairs.files | rejectattr('path', 'search', '\\.pub$') | map(attribute='path') | list | random) }}" # @TODO define 'chosen_privkey'in playbook ansible_user: root tags: - linode tags: - vps - name: Bootstrapping homeserver block: - name: Installing operating system or distro in server when: operating_system is defined block: - name: Creating a server block: [] tags: - unimplemented - name: Waiting for that VPS to come online delegate_to: "{{ hostvars[instance]['ansible_default_' ~ ip_pref].address }}" delegate_facts: true ansible.builtin.wait_for_connection: delay: 20 timeout: 300 vars: ansible_user: root - name: Checking if that server has required operating system delegate_to: "{{ hostvars[instance]['ansible_default_' ~ ip_pref].address }}" delegate_facts: true when: ansible_facts["system"] != "Linux" ansible.builtin.fail: msg: Unsupported operating system found vars: ansible_user: root - name: Checking if that server has required Linux distro delegate_to: "{{ hostvars[instance]['ansible_default_' ~ ip_pref].address }}" delegate_facts: true when: ansible_facts["system"] == "Linux" and ansible_facts["os_family"] != "Debian" ansible.builtin.fail: msg: Unsupported Linux distro found vars: ansible_user: root - name: Providing authorized keys for server root account delegate_to: "{{ hostvars[instance]['ansible_default_' ~ ip_pref].address }}" delegate_facts: true ansible.posix.authorized_key: user: "{{ ansible_facts['user_id'] }}" key: "{{ lookup('file', item) }}" state: present vars: ansible_root: root loop: "{{ ssh_keypairs.files | selectattr('path', 'search', '\\.pub$') | map(attribute='path') | list }}" tags: - lan - name: Starting SSH hardening when: harden and ansible_facts["system"] == "Linux" delegate_facts: true block: - name: Hardening SSH service for the Linode VPS delegate_to: "{{ new_instance.instance[ip_pref][0] }}" ansible.builtin.copy: src: sshd_config.d/harden.conf dest: /etc/ssh/sshd_config.d/harden.conf owner: root group: root mode: "644" force: true backup: true validate: "sshd -t %s" vars: ansible_ssh_private_key_file: "{{ chosen_privkey | default(ssh_keypairs.files | rejectattr('path', 'search', '\\.pub$') | map(attribute='path') | list | random) }}" # @TODO define 'chosen_privkey'in playbook ansible_user: root register: ssh_hardened tags: - linode - name: Hardening SSH service for the server delegate_to: "{{ hostvars[instance]['ansible_default_' ~ ip_pref].address }}" ansible.builtin.copy: src: sshd_config.d/harden.conf dest: /etc/ssh/sshd_config.d/harden.conf owner: root group: root mode: "644" force: true backup: true validate: "sshd -t %s" vars: ansible_ssh_private_key_file: "{{ chosen_privkey | default(ssh_keypairs.files | rejectattr('path', 'search', '\\.pub$') | map(attribute='path') | list | random) }}" # @TODO define 'chosen_privkey'in playbook ansible_user: root register: ssh_hardened tags: - lan - name: Starting user and group creation for SSH access block: - name: Creating group remote for managing SSH access delegate_facts: true block: - name: In the Linode VPS delegate_to: "{{ new_instance.instance[ip_pref][0] }}" ansible.builtin.group: name: remote system: true state: present vars: ansible_ssh_private_key_file: "{{ chosen_privkey | default(ssh_keypairs.files | rejectattr('path', 'search', '\\.pub$') | map(attribute='path') | list | random) }}" # @TODO define 'chosen_privkey'in playbook ansible_user: root register: remote_group tags: - linode - name: In the server delegate_to: "{{ hostvars[instance]['ansible_default_' ~ ip_pref].address }}" ansible.builtin.group: name: remote system: true state: present vars: ansible_ssh_private_key_file: "{{ chosen_privkey | default(ssh_keypairs.files | rejectattr('path', 'search', '\\.pub$') | map(attribute='path') | list | random) }}" # @TODO define 'chosen_privkey'in playbook ansible_user: root register: remote_group tags: - lan - name: Creating an administrative user delegate_facts: true block: - name: In the Linode VPS delegate_to: "{{ new_instance.instance[ip_pref][0] }}" ansible.builtin.user: name: "{{ item.username }}" comment: administrator group: "{{ item.username }}" groups: - "{{ remote_group.name }}" - sudo # @NOTE used by Debian append: true generate_ssh_key: true create_home: true password: "{{ item.password }}" shell: "/bin/bash" vars: ansible_ssh_private_key_file: "{{ chosen_privkey | default(ssh_keypairs.files | rejectattr('path', 'search', '\\.pub$') | map(attribute='path') | list | random) }}" # @TODO define 'chosen_privkey'in playbook ansible_user: root loop: "{{ admins }}" register: admin_users tags: - linode - name: In the server delegate_to: "{{ hostvars[instance]['ansible_default_' ~ ip_pref].address }}" ansible.builtin.user: name: "{{ item.username }}" comment: administrator group: "{{ item.username }}" groups: - "{{ remote_group.name }}" - sudo # @NOTE used by Debian append: true generate_ssh_key: true create_home: true password: "{{ item.password }}" shell: "/bin/bash" vars: ansible_ssh_private_key_file: "{{ chosen_privkey | default(ssh_keypairs.files | rejectattr('path', 'search', '\\.pub$') | map(attribute='path') | list | random) }}" # @TODO define 'chosen_privkey'in playbook ansible_user: root loop: "{{ admins }}" register: admin_users tags: - lan - name: Finding SSH public keys for an administrative user when: item.username in (admin_users.results | map(attribute="name") | list) ansible.builtin.find: paths: "{{ cnode_homedir | default('/home/' ~ ansible_user ~ '/.ssh') }}" # @TODO define 'cnode_homedir' in playbook patterns: "{{ ['^'] | product(item.keys) | map('join') | list }}" file_type: file use_regex: true loop: "{{ admins }}" register: admin_ssh_keypairs - name: Authorizing SSH public key for an administrative user delegate_facts: true block: - name: In the Linode VPS delegate_to: "{{ new_instance.instance[ip_pref][0] }}" ansible.posix.authorized_key: user: "{{ admin_users.results[idx] }}" key: "{{ admin_ssh_keypairs.results[idx].files | selectattr('path', 'search', '\\.pub$') | map(attribute='path') | map('lookup', 'file') | list | map('join','\n') }}" state: present vars: ansible_ssh_private_key_file: "{{ chosen_privkey | default(ssh_keypairs.files | rejectattr('path', 'search', '\\.pub$') | map(attribute='path') | list | random) }}" # @TODO define 'chosen_privkey'in playbook ansible_user: root loop: "{{ admin_users.results }}" loop_control: index_var: idx register: ssh_authorizations tags: - linode - name: In the server delegate_to: "{{ hostvars[instance]['ansible_default_' ~ ip_pref].address }}" ansible.posix.authorized_key: user: "{{ admin_users.results[idx] }}" key: "{{ admin_ssh_keypairs.results[idx].files | selectattr('path', 'search', '\\.pub$') | map(attribute='path') | map('lookup', 'file') | list | map('join','\n') }}" state: present vars: ansible_ssh_private_key_file: "{{ chosen_privkey | default(ssh_keypairs.files | rejectattr('path', 'search', '\\.pub$') | map(attribute='path') | list | random) }}" # @TODO define 'chosen_privkey'in playbook ansible_user: root loop: "{{ admin_users.results }}" loop_control: index_var: idx register: ssh_authorizations tags: - lan - name: Allowing sole SSH access to users in group remote when: ansible_facts["system"] == "Linux" delegate_facts: true block: - name: In Linode VPS delegate_to: "{{ new_instance.instance[ip_pref][0] }}" ansible.builtin.template: src: sshd_config.d/allowance.conf.j2 # @TODO create corresponding role template file dest: /etc/ssh/sshd_config.d/allowance.conf owner: root group: root mode: "644" force: true backup: true validate: "sshd -t %s" vars: ansible_ssh_private_key_file: "{{ chosen_privkey | default(ssh_keypairs.files | rejectattr('path', 'search', '\\.pub$') | map(attribute='path') | list | random) }}" # @TODO define 'chosen_privkey'in playbook ansible_user: root register: ssh_gatekept tags: - linode - name: In the server delegate_to: "{{ hostvars[instance]['ansible_default_' ~ ip_pref].address }}" ansible.builtin.template: src: sshd_config.d/allowance.conf.j2 # @TODO create corresponding role template file dest: /etc/ssh/sshd_config.d/allowance.conf owner: root group: root mode: "644" force: true backup: true validate: "sshd -t %s" vars: ansible_ssh_private_key_file: "{{ chosen_privkey | default(ssh_keypairs.files | rejectattr('path', 'search', '\\.pub$') | map(attribute='path') | list | random) }}" # @TODO define 'chosen_privkey'in playbook ansible_user: root register: ssh_gatekept tags: - lan - name: Setting approved SSH authentication procedures when: harden and ansible_facts["system"] == "Linux" delegate_facts: true block: - name: In the Linode VPS delegate_to: "{{ new_instance.instance[ip_pref][0] }}" ansible.builtin.copy: src: sshd_config.d/auth.conf dest: /etc/ssh/sshd_config.d/auth.conf owner: root group: root mode: "644" force: true backup: true validate: "sshd -t %s" vars: ansible_ssh_private_key_file: "{{ chosen_privkey | default(ssh_keypairs.files | rejectattr('path', 'search', '\\.pub$') | map(attribute='path') | list | random) }}" # @TODO define 'chosen_privkey'in playbook ansible_user: root register: ssh_authenticator tags: - linode - name: In the server delegate_to: "{{ hostvars[instance]['ansible_default_' ~ ip_pref].address }}" ansible.builtin.copy: src: sshd_config.d/auth.conf dest: /etc/ssh/sshd_config.d/auth.conf owner: root group: root mode: "644" force: true backup: true validate: "sshd -t %s" vars: ansible_ssh_private_key_file: "{{ chosen_privkey | default(ssh_keypairs.files | rejectattr('path', 'search', '\\.pub$') | map(attribute='path') | list | random) }}" # @TODO define 'chosen_privkey'in playbook ansible_user: root register: ssh_authenticator tags: - lan - ssh_secure_auth - name: Installing core packages delegate_facts: true block: - name: In the Linode VPS delegate_to: "{{ new_instance.instance[ip_pref][0] }}" block: - name: Registering a package signing key when: ansible_facts["os_family"] == "Debian" and item.key is defined and item.key_path is defined ansible.builtin.get_url: url: "{{ item.key }}" dest: "{{ item.key_path | default('/etc/apt/keyrings/') }}" owner: root group: root mode: "644" force: true backup: true loop: "{{ pkgs.mngr.core | rejectattr('key', 'search', '\\.deb$') }}" - name: Installing a package signing key when: ansible_facts["os_family"] == "Debian" and item.key is defined ansible.builtin.apt: deb: "{{ item.key }}" state: latest loop: "{{ pkgs.mngr.core | selectattr('key', 'search', '\\.deb$') }}" - name: Registering a package source when: ansible_facts["os_family"] == "Debian" and item.src_entry is defined and item.src_path is defined ansible.builtin.copy: content: "{{ item.src_entry }}" dest: "{{ item.src_path }}" owner: root group: root mode: "644" force: true backup: true loop: "{{ pkgs.mngr.core }}" - name: Installing a local package in managed node when: ansible_facts["os_family"] == "Debian" and item.uri is defined ansible.builtin.apt: deb: "{{ item.uri }}" update_cache: true state: latest loop: "{{ pkgs.mngr.core | selectattr('uri', 'search', '\\.deb$') }}" - name: Installing a package when: item.name is defined and item.uri is undefined ansible.builtin.package: name: "{{ item.name }}" update_cache: true state: latest notify: "{{ item.name }}" # @TODO create corresponding roles/init-vps handlers loop: "{{ pkgs.mngr.core| rejectattr('uri', 'search', '\\.deb$') }}" vars: ansible_ssh_private_key_file: "{{ chosen_privkey | default(ssh_keypairs.files | rejectattr('path', 'search', '\\.pub$') | map(attribute='path') | list | random) }}" # @TODO define 'chosen_privkey'in playbook ansible_user: root tags: - linode - name: In the server delegate_to: "{{ hostvars[instance]['ansible_default_' ~ ip_pref].address }}" block: - name: Registering a package signing key when: ansible_facts["os_family"] == "Debian" and item.key is defined and item.key_path is defined ansible.builtin.get_url: url: "{{ item.key }}" dest: "{{ item.key_path | default('/etc/apt/keyrings/') }}" owner: root group: root mode: "644" force: true backup: true loop: "{{ pkgs.mngr.core | rejectattr('key', 'search', '\\.deb$') }}" - name: Installing a package signing key when: ansible_facts["os_family"] == "Debian" and item.key is defined ansible.builtin.apt: deb: "{{ item.key }}" state: latest loop: "{{ pkgs.mngr.core | selectattr('key', 'search', '\\.deb$') }}" - name: Registering a package source when: ansible_facts["os_family"] == "Debian" and item.src_entry is defined and item.src_path is defined ansible.builtin.copy: content: "{{ item.src_entry }}" dest: "{{ item.src_path }}" owner: root group: root mode: "644" force: true backup: true loop: "{{ pkgs.mngr.core }}" - name: Installing a local package in managed node when: ansible_facts["os_family"] == "Debian" and item.uri is defined ansible.builtin.apt: deb: "{{ item.uri }}" update_cache: true state: latest notify: "{{ item.name }}" loop: "{{ pkgs.mngr.core | selectattr('uri', 'search', '\\.deb$') }}" - name: Installing a package when: item.name is defined and item.uri is undefined ansible.builtin.package: name: "{{ item.name }}" update_cache: true state: latest notify: "{{ item.name }}" # @TODO create corresponding roles/init-vps handlers loop: "{{ pkgs.mngr.core| rejectattr('uri', 'search', '\\.deb$') }}" vars: ansible_ssh_private_key_file: "{{ chosen_privkey | default(ssh_keypairs.files | rejectattr('path', 'search', '\\.pub$') | map(attribute='path') | list | random) }}" # @TODO define 'chosen_privkey'in playbook ansible_user: root tags: - lan tags: - get_pkgs