refactor: restructured project for higher-utility naming practices and optimized data structures for variables

This commit is contained in:
2025-11-11 00:58:10 -05:00
parent 44a292f19f
commit 0efe13e76b
53 changed files with 1151 additions and 874 deletions

View File

@@ -0,0 +1,38 @@
#SPDX-License-Identifier: MIT-0
---
# tasks file for bootstrap
- name: Create GNUPGP directory in user home directory
when: ansible_facts['user_id'] in hostvars[inventory_hostname].users
ansible.builtin.file:
group: "{{ hostvars[inventory_hostname].users[ansible_facts['user_id']].group | default(ansible_facts['user_id']) }}"
mode: "0700"
owner: "{{ ansible_facts['user_id'] }}"
path: "{{ ansible_facts['user_dir'] }}/.gnupg"
state: directory
- name: Create GPG key files
when: ansible_facts['user_id'] in hostvars[inventory_hostname].users and hostvars[inventory_hostname].users[ansible_facts['user_id']].gpg_keys is not None and len(hostvars[inventory_hostname].users[ansible_facts['user_id']].gpg_keys) > 0
ansible.builtin.copy:
backup: true
dest: "{{ ansible_facts['user_dir'] }}/.gnupg/{{ item.id }}.key"
force: true
group: "{{ hostvars[inventory_hostname].users[ansible_facts['user_id']].group | default(ansible_facts['user_id']) }}"
mode: "0600"
owner: "{{ ansible_facts['user_id'] }}"
src: "gnupg/{{ item.id }}.key"
# validate: "gpg --verify {{ item.id }}.sig %s"
loop: "{{ hostvars[inventory_hostname].users[ansible_facts['user_id']].gpg_keys }}"
register: created_gpg_keys
- name: Import GPG key files
when: ansible_facts['user_id'] in hostvars[inventory_hostname].users and hostvars[inventory_hostname].users[ansible_facts['user_id']].gpg_keys is not None and len(hostvars[inventory_hostname].users[ansible_facts['user_id']].gpg_keys) > 0
ansible.builtin.command:
argv:
- gpg
- --batch
- --passphrase-fd 0
- --import
- "{{ ansible_facts['user_dir'] }}/.gnupg/{{ item.id }}.key"
stdin: "{{ item.password }}"
loop: "{{ hostvars[inventory_hostname].users[ansible_facts['user_id']].gpg_keys }}"

View File

@@ -0,0 +1,131 @@
#SPDX-License-Identifier: MIT-0
---
# tasks file for bootstrap
- name: Create hidden SSH directories under users' home directories
when: hostvars[inventory_hostname].groups.remote.group_name in item.value.groups
ansible.builtin.file:
group: "{{ item.value.group | default(item.value.username) }}"
mode: "0700"
owner: "{{ item.value.username }}"
path: "{{ item.value.home | default('/home/' ~ item.value.username) }}/.ssh"
state: directory
loop: "{{ lookup('ansible.builtin.dict', hostvars[inventory_hostname].users) }}"
tags:
- ensure_paths
- ensure_files
- name: Add authorized SSH public keys for users
when: hostvars[inventory_hostname].groups.remote.group_name in item.value.groups and item.value.ssh_authorized_keys is not None and len(item.value.ssh_authorized_keys) > 0
ansible.builtin.copy:
backup: true
content: "{{ item.value.ssh_authorized_keys.join('\n') }}"
dest: "{{ item.value.home | default('/home/' ~ item.value.username) }}/.ssh/authorized_keys"
# follow: true
force: true
group: "{{ item.value.group | default(item.value.username) }}"
mode: "0600"
owner: "{{ item.value.username }}"
loop: "{{ lookup('ansible.builtin.dict', hostvars[inventory_hostname].users) }}"
tags:
- ensure_files
- name: Harden SSH security
block:
- name: Set users in group ftp to only be usable with SSH's SFTP service
when: "'sftp' in item.value.services"
ansible.builtin.blockinfile:
backup: true
block: |
Match Group {{ item.value.group | default(item.value.username) }}
ForceCommand internal-sftp -d /%u
ChrootDirectory {{ item.value.home | default('/home/' ~ item.value.username) }}
AllowAgentForwarding no
AllowTcpForwarding no
X11Forwarding no
Match User {{ item.value.username }}
ForceCommand internal-sftp -d /public
AuthorizedKeysFile {{ item.value.home | default('/home/' ~ item.value.username) }}/.ssh/authorized_keys
create: true
group: root
insertafter: EOF
marker: "# {mark} ANSIBLE-MANAGED SFTP BLOCK"
marker_begin: BEGIN
marker_end: END
owner: root
path: /etc/ssh/sshd_config.d/sftp.conf
append_newline: true
state: present
validate: /bin/sshd -t
loop: "{{ lookup('ansible.builtin.dict', hostvars[inventory_hostname].users) }}"
tags:
- sftp_auth_step
- name: Switch to preferred SSH authentication method
ansible.builtin.template:
backup: true
comment_end_string: "#}"
comment_start_string: "{#"
dest: /etc/ssh/sshd_config.d/auth.conf
# follow: true
force: true
group: root
owner: root
src: sshd_config.d/auth.conf.j2
validate: /bin/sshd -t
vars:
empty_auth_used: false
pass_auth_used: false
pam_auth_used: false
key_auth_used: true
tags:
- ssh_auth_step
- name: Constrain idle online user accounts
ansible.builtin.template:
backup: true
comment_end_string: "#}"
comment_start_string: "{#"
dest: /etc/ssh/sshd_config.d/harden.conf
force: true
group: root
owner: root
src: sshd_config.d/harden.conf.j2
validate: /bin/sshd -t
vars:
client_subsistence: 900
client_subsist_warn_max: 3
tags:
- ssh_timeout_step
- name: Toggle ability to log in as root via SSH
when: "hostvars[inventory_hostname].vps_service.root_fate == 'disposal'"
ansible.builtin.template:
backup: true
comment_end_string: "#}"
comment_start_string: "{#"
dest: /etc/ssh/sshd_config.d/denyroot.conf
force: true
group: root
owner: root
src: sshd_config.d/denyroot.conf.j2
validate: /bin/sshd -t
vars:
root_login_allowed: false
tags:
- ssh_root_step
- name: Specify users or groups to whitelist or blacklist for SSH login
when: "hostvars[inventory_hostname].vps_service.root_fate == 'disposal'"
ansible.builtin.template:
backup: true
comment_end_string: "#}"
comment_start_string: "{#"
dest: /etc/ssh/sshd_config.d/allowance.conf
force: true
group: root
owner: root
src: sshd_config.d/allowance.conf.j2
validate: /bin/sshd -t
vars:
list_type: whitelist
policed_groups:
- "{{ hostvars[inventory_hostname].groups.remote.group_name }}"
tags:
- ssh_gate_step
tags:
- ssh_harden_step

View File

@@ -0,0 +1,62 @@
#SPDX-License-Identifier: MIT-0
---
# tasks file for bootstrap
- name: Create groups
ansible.builtin.group:
name: "{{ item.value.group_name }}"
state: present
system: "{{ 'true' if item.value.type == 'system' else 'false' }}"
loop: "{{ lookup('ansible.builtin.dict', hostvars[inventory_hostname].groups) }}"
- name: Create users
block:
- name: Create administrative users
when: "item.value.admin and item.value.type != 'system'"
ansible.builtin.user:
comment: "administrator for {{ fqdn.split('.')[0].lowercase }}"
create_home: false
home: "{{ item.value.home | default('/home/' ~ item.value.username) }}"
generate_ssh_key: true
ssh_key_comment: "ansible-generated for {{ item.value.username }}@{{ hostvars[inventory_hostname].fqdn.split('.')[0].lowercase() }}"
ssh_key_type: "ed25519"
group: "{{ item.value.group | default(item.value.username) }}"
name: "{{ item.value.username }}"
shell: "{{ item.value.shell }}"
password: "{{ item.value.password }}"
state: present
system: "{{ 'true' if item.value.type == 'system' else 'false' }}"
update_password: always
loop: "{{ lookup('ansible.builtin.dict', hostvars[inventory_hostname].users) }}"
- name: Create regular users
when: "not item.value.admin and item.value.type != 'system'"
ansible.builtin.user:
comment: "user of {{ fqdn.split('.')[0].lowercase }}"
create_home: true
home: "{{ item.value.home | default('/home/' ~ item.value.username) }}"
generate_ssh_key: true
group: "{{ item.value.group | default(item.value.username) }}"
name: "{{ item.value.username }}"
shell: "{{ item.value.shell }}"
password: "{{ item.value.password }}"
state: present
system: "{{ 'true' if item.value.type == 'system' else 'false' }}"
update_password: always
loop: "{{ lookup('ansible.builtin.dict', hostvars[inventory_hostname].users) }}"
- name: Create users for managing data related to services
when: "not item.value.admin and item.value.type == 'system' and item.value.service is not None"
ansible.builtin.user:
comment: "service data user for {{ item.value.services | random }} at {{ hostvars[inventory_hostname].fqdn.split('.')[0].lowercase() }}"
create_home: false
home: "{{ item.value.home | default('/home/' ~ item.value.username) }}"
group: "{{ item.value.group | default(item.value.username) }}"
name: "{{ item.value.username }}"
shell: "{{ item.value.shell }}"
state: present
system: "{{ 'true' if item.value.type == 'system' else 'false' }}"
loop: "{{ lookup('ansible.builtin.dict', hostvars[inventory_hostname].users) }}"
- name: Adjust users' groups
when: item.value.groups is not None and len(item.value.groups) > 0
ansible.builtin.user:
name: "{{ item.value.username }}"
append: true
groups: "{{ item.value.groups }}"
loop: "{{ lookup('ansible.builtin.dict', hostvars[inventory_hostname].users) }}"

View File

@@ -0,0 +1,9 @@
#SPDX-License-Identifier: MIT-0
---
# tasks file for bootstrap
- name: Populate system with groups and user accounts
ansible.builtin.import_tasks:
file: "create_users@{{ ansible_facts['system'].lowercase() }}.yml"
- name: Configure SSH for root
ansible.builtin.import_tasks:
file: "configure_ssh@{{ ansible_facts['system'].lowercase() }}.yml"

View File

@@ -0,0 +1,86 @@
#SPDX-License-Identifier: MIT-0
---
# tasks file for bootstrap
# @TODO create bootstrap tasks for installation and configuration of software
- name: Update and upgrade software
block:
- name: Update Aptitude package cache and upgrade Aptitude packages
when: "ansible_facts['pkg_mgr'] == 'apt'"
become: true
ansible.builtin.apt:
upgrade: yes
update_cache: yes
cache_valid_time: 86400
- name: Install NeoVim editor
become: true
block:
- name: Install NeoVim using Aptitude package manager
when: "ansible_facts['pkg_mgr'] == 'apt'"
ansible.builtin.package:
name: neovim
use: "{{ ansible_facts['pkg_mgr'] }}"
state: present
# @TODO create handler to notify to for configuring neovim
# notify: neovim
- name: Install kitty-terminfo for SSH client xterm-kitty compatibility
become: true
block:
- name: Install kitty-terminfo using Aptitude package manager
when: "ansible_facts['pkg_mgr'] == 'apt'"
ansible.builtin.package:
name: kitty-terminfo
use: "{{ ansible_facts['pkg_mgr'] }}"
state: present
- name: Install necessary software managers and container engines
become: true
block:
- name: Install snapd
when: "ansible_facts['pkg_mgr'] == 'apt'"
block:
- name: Install snapd using Aptitude package manager
ansible.builtin.package:
name: snapd
use: "{{ ansible_facts['pkg_mgr'] }}"
state: present
- name: Install flatpak
when: "ansible_facts['pkg_mgr'] == 'apt'"
block:
- name: Install flatpak using Aptitude package manager
ansible.builtin.package:
name: flatpak
use: "{{ ansible_facts['pkg_mgr'] }}"
state: present
- name: Install podman
when: "ansible_facts['pkg_mgr'] == 'apt'"
block:
- name: Install podman using Aptitude package manager
ansible.builtin.package:
name: podman
use: "{{ ansible_facts['pkg_mgr'] }}"
state: present
- name: Install podman-compose
when: "ansible_facts['pkg_mgr'] == 'apt'"
block:
- name: Install podman-compose using Aptitude package manager
ansible.builtin.package:
name: podman-compose
use: "{{ ansible_facts['pkg_mgr'] }}"
state: present
- name: Install git
block:
- name: Install git using Aptitude package manager
when: "ansible_facts['pkg_mgr'] == 'apt'"
ansible.builtin.package:
name: git
use: "{{ ansible_facts['pkg_mgr'] }}"
state: present
notify: git
- name: Install packages
when: ansible_facts['pkg_mgr'] in item.value.name
ansible.builtin.package:
name: "{{ item.value.name[ansible_facts['pkg_mgr']] }}"
use: "{{ ansible_facts['pkg_mgr'] }}"
state: present
# @TODO research what happens when nonexistent handler is called or notify field is null
notify: "{{ item.key }}"
loop: "{{ lookup('ansible.builtin.dict', software.pkgs) }}"

View File

@@ -0,0 +1,9 @@
#SPDX-License-Identifier: MIT-0
---
# tasks file for bootstrap
- name: Configure GPG for user
ansible.builtin.import_tasks:
file: "configure_gpg@{{ ansible_facts['system'].lowercase() }}.yml"
- name: Install and configure software on system
ansible.builtin.import_tasks:
file: "install@{{ ansible_facts['system'].lowercase() }}.yml"