From f7ba34ec695b27fa760d4b3881f383d463a2b268 Mon Sep 17 00:00:00 2001 From: Alex Tavarez Date: Fri, 29 May 2026 07:32:28 -0400 Subject: [PATCH] due to refactor, shortened and renamed file to only take care of spawning the server and adding SSH public keys to root user account --- roles/init-server/tasks/main.yml | 444 ------------------------------ roles/init-server/tasks/spawn.yml | 91 ++++++ 2 files changed, 91 insertions(+), 444 deletions(-) delete mode 100644 roles/init-server/tasks/main.yml create mode 100644 roles/init-server/tasks/spawn.yml diff --git a/roles/init-server/tasks/main.yml b/roles/init-server/tasks/main.yml deleted file mode 100644 index 4f80d6e..0000000 --- a/roles/init-server/tasks/main.yml +++ /dev/null @@ -1,444 +0,0 @@ -#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 \ No newline at end of file diff --git a/roles/init-server/tasks/spawn.yml b/roles/init-server/tasks/spawn.yml new file mode 100644 index 0000000..a11c854 --- /dev/null +++ b/roles/init-server/tasks/spawn.yml @@ -0,0 +1,91 @@ +#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 + tags: + - linode + tags: + - vps +- name: Bootstrapping homeserver + block: + - name: Installing operating system or distro in server + when: operating_system != None + block: + - name: Creating a server + block: [] + tags: + - unimplemented + - name: Waiting for that server to come online + delegate_to: "{{ hostvars[instance]['ansible_default_' ~ ip_pref].address }}" + delegate_facts: true + remote_user: root + 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 + remote_user: root + 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 + remote_user: root + 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 + remote_user: root + ansible.posix.authorized_key: + user: "{{ ansible_user }}" + key: "{{ lookup('file', item) }}" + state: present + vars: + ansible_root: root + loop: "{{ ssh_keypairs.files | selectattr('path', 'search', '\\.pub$') | map(attribute='path') | list }}" + tags: + - lan