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

38
roles/bootstrap/README.md Normal file
View File

@@ -0,0 +1,38 @@
Role Name
=========
A brief description of the role goes here.
Requirements
------------
Any pre-requisites that may not be covered by Ansible itself or the role should be mentioned here. For instance, if the role uses the EC2 module, it may be a good idea to mention in this section that the boto package is required.
Role Variables
--------------
A description of the settable variables for this role should go here, including any variables that are in defaults/main.yml, vars/main.yml, and any variables that can/should be set via parameters to the role. Any variables that are read from other roles and/or the global scope (ie. hostvars, group vars, etc.) should be mentioned here as well.
Dependencies
------------
A list of other roles hosted on Galaxy should go here, plus any details in regards to parameters that may need to be set for other roles, or variables that are used from other roles.
Example Playbook
----------------
Including an example of how to use your role (for instance, with variables passed in as parameters) is always nice for users too:
- hosts: servers
roles:
- { role: username.rolename, x: 42 }
License
-------
BSD
Author Information
------------------
An optional section for the role authors to include contact information, or a website (HTML is not allowed).

View File

@@ -0,0 +1,3 @@
#SPDX-License-Identifier: MIT-0
---
# defaults file for bootstrap

View File

@@ -0,0 +1,9 @@
# @NOTE possible commit types: https://github.com/qoomon/git-conventional-commits?tab=readme-ov-file#config-file
# @NOTE for description or body consider: motivation for or cause of change, impact and domain of change
<type>[optional scope]: <description{<50char}>
[optional body]
# @NOTE footer should almost be treated as metadata of/for commit, or alerts of significant impact
# @NOTE for footer purpose, see: https://dev.to/mochafreddo/a-comprehensive-guide-to-using-footers-in-conventional-commit-messages-37g6
[optional footer(s)]

View File

@@ -0,0 +1,18 @@
Match Group ftp
ForceCommand internal-sftp -d /%u
ChrootDirectory /srv/ftp
AllowAgentForwarding no
AllowTcpForwarding no
X11Forwarding no
Match User ftp
ForceCommand internal-sftp -d /public
AuthorizedKeysFile /srv/ftp/.ssh/authorized_keys
Match User caddy,www-data
ForceCommand internal-sftp -d /domain1.tld
AuthorizedKeysFile /srv/www/.ssh/authorized_keys
ChrootDirectory /srv/www
AllowAgentForwarding no
AllowTcpForwarding no
X11Forwarding no

View File

@@ -0,0 +1,3 @@
# SPDX-License-Identifier: MIT-0
---
# handlers file for bootstrap

View File

@@ -0,0 +1,306 @@
# SPDX-License-Identifier: MIT-0
---
# handlers file for bootstrap
- name: Configure git
listen: git
block:
# @NOTE below are system git configuration
- name: Configure git aliases
become: true
block:
- name: Configure non-coding alias for merge subcommand
community.general.git_config:
name: alias.assimilate
add_mode: replace-all
state: present
value: merge
- name: Configure alias for merge subcommand
community.general.git_config:
name: alias.mrg
add_mode: replace-all
state: present
value: merge
- name: Configure non-coding alias for add subcommand
community.general.git_config:
name: alias.approve
add_mode: replace-all
state: present
value: add
- name: Configure non-coding alias for status subcommand
community.general.git_config:
name: alias.revisions
add_mode: replace-all
state: present
value: status
- name: Configure alias for status subcommand
community.general.git_config:
name: alias.stat
add_mode: replace-all
state: present
value: status
- name: Configure non-coding alias for commit subcommand
community.general.git_config:
name: alias.finalize
add_mode: replace-all
state: present
value: commit
- name: Configure alias for commit subcommand
community.general.git_config:
name: alias.cit
add_mode: replace-all
state: present
value: commit
- name: Configure alias for config subcommand
community.general.git_config:
name: alias.cfg
add_mode: replace-all
state: present
value: config
- name: Configure non-coding alias for commit subcommand with signing flag
community.general.git_config:
name: alias.claim
add_mode: replace-all
state: present
value: commit -S
- name: Configure alias for commit subcommand with signing flag
community.general.git_config:
name: alias.sig
add_mode: replace-all
state: present
value: commit -S
- name: Configure non-coding alias for show subcommand
community.general.git_config:
name: alias.review
add_mode: replace-all
state: present
value: show
- name: Configure alias for show subcommand
community.general.git_config:
name: alias.peek
add_mode: replace-all
state: present
value: show
- name: Configure alias for add subcommand with universal staging flag
community.general.git_config:
name: alias.badd
add_mode: replace-all
state: present
value: add -A
- name: Configure non-coding alias for add subcommand with universal staging flag
community.general.git_config:
name: alias.approve-all
add_mode: replace-all
state: present
value: add -A
- name: Configure non-coding alias for rm subcommand
community.general.git_config:
name: alias.redact
add_mode: replace-all
state: present
value: rm
- name: Configure non-coding alias for init subcommand
community.general.git_config:
name: alias.author
add_mode: replace-all
state: present
value: init
- name: Configure non-coding alias for mv subcommand
community.general.git_config:
name: alias.revise
add_mode: replace-all
state: present
value: mv
- name: Configure non-coding alias for rebase subcommand
community.general.git_config:
name: alias.retroact
add_mode: replace-all
state: present
value: rebase
- name: Configure alias for rebase subcommand
community.general.git_config:
name: alias.rb
add_mode: replace-all
state: present
value: rebase
- name: Configure non-coding alias for push subcommand
community.general.git_config:
name: alias.publish
add_mode: replace-all
state: present
value: push
- name: Configure non-coding alias for pull subcommand
community.general.git_config:
name: alias.publication
add_mode: replace-all
state: present
value: pull
- name: Configure non-coding alias for fetch subcommand
community.general.git_config:
name: alias.manuscript
add_mode: replace-all
state: present
value: fetch
- name: Configure alias for fetch subcommand
community.general.git_config:
name: alias.get
add_mode: replace-all
state: present
value: fetch
- name: Configure non-coding alias for clone subcommand
community.general.git_config:
name: alias.copy
add_mode: replace-all
state: present
value: clone
- name: Configure alias for clone subcommand
community.general.git_config:
name: alias.cp
add_mode: replace-all
state: present
value: clone
- name: Configure non-coding alias for branch subcommand
community.general.git_config:
name: alias.draft
add_mode: replace-all
state: present
value: branch
- name: Configure alias for branch subcommand
community.general.git_config:
name: alias.br
add_mode: replace-all
state: present
value: branch
- name: Configure non-coding alias for switch subcommand
community.general.git_config:
name: alias.edit
add_mode: replace-all
state: present
value: switch
- name: Configure alias for switch subcommand
community.general.git_config:
name: alias.cd
add_mode: replace-all
state: present
value: switch
- name: Configure non-coding alias for restore subcommand
community.general.git_config:
name: alias.revert
add_mode: replace-all
state: present
value: restore
- name: Configure alias for restore subcommand
community.general.git_config:
name: alias.rs
add_mode: replace-all
state: present
value: restore
- name: Set default editor for git
become: true
community.general.git_config:
add_mode: replace-all
name: core.editor
state: present
value: "{{ config.git.sys.editor }}"
- name: Create a directory for storing system-level templates for git
become: true
ansible.builtin.file:
group: root
owner: root
path: /etc/gitconfig.d
state: directory
- name: Create a commit message template file for git
become: true
ansible.builtin.copy:
owner: root
group: root
backup: true
dest: /etc/gitconfig.d/commit.msg
force: true
src: gitconfig.d/commit.msg
- name: Set system-level commit message template file path for git
become: true
community.general.git_config:
add_mode: replace-all
name: commit.template
state: present
value: /etc/gitconfig.d/commit.msg
- name: Set UI to have color for git at system-level
become: true
community.general.git_config:
add_mode: replace-all
name: color.ui
state: present
value: "true"
- name: Set line-end conversion behavior for git
become: true
community.general.git_config:
add_mode: replace-all
name: core.autocrlf
state: present
value: input
# @NOTE below are user git configuration
- name: Create a user directory for a user gitignore file for git
when: ansible_facts['user_id'] in hostvars[inventory_hostname].users
ansible.builtin.file:
owner: "{{ ansible_facts['user_id'] }}"
group: "{{ hostvars[inventory_hostname].users[ansible_facts['user_id']].group | default(ansible_facts['user_id']) }}"
path: "{{ ansible_facts['user_dir'] }}/.config/git"
state: directory
- name: Create a user gitignore file for git
when: ansible_facts['user_id'] in hostvars[inventory_hostname].users
ansible.builtin.copy:
owner: "{{ ansible_facts['user_id'] }}"
group: "{{ hostvars[inventory_hostname].users[ansible_facts['user_id']].group | default(ansible_facts['user_id']) }}"
backup: true
dest: "{{ ansible_facts['user_dir'] }}/.config/git/gitignore"
force: true
src: gitconfig.d/exclude.rules
- name: Set user gitignore file path for git
when: ansible_facts['user_id'] in hostvars[inventory_hostname].users
community.general.git_config:
add_mode: replace-all
name: core.excludesfile
scope: global
state: present
value: "{{ ansible_facts['user_dir'] }}/.config/git/gitignore"
- name: Create link from user config directory git config file to user home directory git config file
when: ansible_facts['user_id'] in hostvars[inventory_hostname].users
community.general.file:
src: "{{ ansible_facts['user_dir'] }}/.gitconfig"
dest: "{{ ansible_facts['user_dir'] }}/.config/git/config"
# @TODO check whether below two attributes make sense for links
owner: "{{ ansible_facts['user_id'] }}"
group: "{{ hostvars[inventory_hostname].users[ansible_facts['user_id']].group | default(ansible_facts['user_id']) }}"
state: hard
- name: Set format for keys used by git
when: ansible_facts['user_id'] in hostvars[inventory_hostname].users
community.general.git_config:
add_mode: replace-all
name: gpg.format
scope: global
state: present
value: openpgp
- name: Set signing key to be used by git
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
community.general.git_config:
add_mode: replace-all
name: user.signingkey
scope: global
state: present
value: "{{ hostvars[inventory_hostname].users[ansible_facts['user_id']].gpg_keys[hostvars[inventory_hostname].users[ansible_facts['user_id']].gpg_keyid_pref].id | default((hostvars[inventory_hostname].users[ansible_facts['user_id']].gpg_keys | random).id) }}"
- name: Set name of user of git
when: ansible_facts['user_id'] in hostvars[inventory_hostname].users
community.general.git_config:
add_mode: replace-all
name: user.name
scope: global
state: present
value: "{{ hostvars[inventory_hostname].users[ansible_facts['user_id']].git_profile.name }}"
- name: Set email of user of git
when: ansible_facts['user_id'] in hostvars[inventory_hostname].users
community.general.git_config:
add_mode: replace-all
name: user.email
scope: global
state: present
value: "{{ hostvars[inventory_hostname].users[ansible_facts['user_id']].git_profile.email }}"

View File

@@ -0,0 +1,6 @@
# SPDX-License-Identifier: MIT-0
---
# handlers file for bootstrap
- name: Postinstall set-up of git
ansible.builtin.import_tasks:
file: git.yml

View File

@@ -0,0 +1,35 @@
#SPDX-License-Identifier: MIT-0
galaxy_info:
author: your name
description: your role description
company: your company (optional)
# If the issue tracker for your role is not on github, uncomment the
# next line and provide a value
# issue_tracker_url: http://example.com/issue/tracker
# Choose a valid license ID from https://spdx.org - some suggested licenses:
# - BSD-3-Clause (default)
# - MIT
# - GPL-2.0-or-later
# - GPL-3.0-only
# - Apache-2.0
# - CC-BY-4.0
license: license (GPL-2.0-or-later, MIT, etc)
min_ansible_version: 2.1
# If this a Container Enabled role, provide the minimum Ansible Container version.
# min_ansible_container_version:
galaxy_tags: []
# List tags for your role here, one per line. A tag is a keyword that describes
# and categorizes the role. Users find roles by searching for tags. Be sure to
# remove the '[]' above, if you add tags to this list.
#
# NOTE: A tag is limited to a single word comprised of alphanumeric characters.
# Maximum 20 tags per role.
dependencies: []
# List your role dependencies here, one per line. Be sure to remove the '[]' above,
# if you add dependencies to this list.

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"

View File

@@ -0,0 +1,15 @@
{% if list_type == 'whitelist' %}
{% if policed_groups is not None and len(policed_groups) > 0 %}
AllowGroups {{ policed_groups.join(' ') }}
{% endif %}
{% if policed_users is not None and len(policed_users) > 0 %}
AllowUsers {{ policed_users.join(' ') }}
{% endif %}
{% else %}
{% if policed_groups is not None and len(policed_groups) > 0 %}
DenyGroups {{ policed_groups.join(' ') }}
{% endif %}
{% if policed_users is not None and len(policed_users) > 0 %}
DenyGroups {{ policed_users.join(' ') }}
{% endif %}
{% endif %}

View File

@@ -0,0 +1,27 @@
{% if empty_auth_used %}
PermitEmptyPasswords yes
{% else %}
PermitEmptyPasswords no
{% endif %}
{% if pass_auth_used %}
PasswordAuthentication yes
{% else %}
PasswordAuthentication no
{% endif %}
{% if kbd_auth_used is not None %}
{% if kbd_auth_used %}
KbdInteractiveAuthentication yes
{% else %}
KbdInteractiveAuthentication no # enable if implementing TOTP 2FA
{% endif %}
{% endif %}
{% if pam_auth_used %}
UsePAM yes
{% else %}
UsePAM no # enable if implementing TOTP 2FA
{% endif %}
{% if key_auth_used %}
PubkeyAuthentication yes
{% else %}
PubkeyAuthentication no
{% endif %}

View File

@@ -0,0 +1,5 @@
{% if root_login_allowed %}
PermitRootLogin yes
{% else %}
PermitRootLogin no
{% endif %}

View File

@@ -0,0 +1,2 @@
ClientAliveInterval {{ client_subsistence }}
ClientAliveCountMax {{ client_subsist_warn_max }}

View File

@@ -0,0 +1,3 @@
#SPDX-License-Identifier: MIT-0
localhost

View File

@@ -0,0 +1,6 @@
#SPDX-License-Identifier: MIT-0
---
- hosts: localhost
remote_user: root
roles:
- bootstrap

View File

@@ -0,0 +1,208 @@
#SPDX-License-Identifier: MIT-0
---
# vars file for bootstrap
# @TODO make list or dictionary of software to be installed in bootstrap task
software:
pkgs:
# @NOTE keep fields or keys constant; otherwise will have to edit handler notifiers and listeners elsewhere
gocryptfs:
name:
apt: gocryptfs
lua-lang:
name:
apt: lua5.4
lua-docs:
name:
apt: luadoc
lua-pkg:
name:
apt: luarocks
python-lang:
name:
apt: python3
python-pkg:
name:
apt: python3-pip
python-linter:
name:
apt: python3-doc8
python-docs:
name:
apt: python3-doc
rust-lang:
name:
apt: rustc # @NOTE alternative: rustup
rust-pkg:
name:
apt: cargo
rust-debugger:
name:
apt: rust-analyzer
rust-linter:
name:
apt: rust-clippy
rust-docs:
name:
apt: rust-doc
java-lang:
name:
apt: default-jdk-headless
java-docs:
name:
apt: default-jdk-doc
java-runtime:
name:
apt: default-jre-headless
kotlin-lang:
name:
apt: kotlin
swift-lang:
name:
apt: swiftlang
swift-docs:
name:
apt: swiftlang-doc
erlang-lang:
name:
apt: erlang
erlang-pkg:
name:
apt: erlang-hex
erlang-docs:
name:
apt: erlang-doc
elixir-lang:
name:
apt: elixir
crystal-lang:
name:
apt: crystal
crystal-docs:
name:
apt: crystal-doc
javascript-lang:
name:
apt: nodejs
javascript-docs:
name:
apt: nodejs-doc
javascript-pkg:
name:
apt: npm
javascript-linter:
name:
apt: eslint
php-lang:
name:
apt: php
php-docs:
name:
apt: php-common
php-debugger:
name:
apt: php-xdebug
php-pkg:
name:
apt: composer
html-linter:
name:
apt: tidy
json-linter:
name:
apt: jsonlint
yaml-linter:
name:
apt: yamllint
pandoc:
name:
apt: pandoc
distrobox:
name:
apt: distrobox
# @TODO manually install the commented below on current active new VPS, then uncomment
# duplicity:
# name:
# apt: duplicity
# pass:
# name:
# apt: pass
# sonicpi:
# name:
# apt: sonic-pi-server
# sonicpi-docs:
# name:
# apt: sonic-pi-server-doc
# supercollider:
# name:
# apt: supercollider
# supercollider-docs:
# name:
# apt: supercollider-common
# supercollider-plugins:
# name:
# apt: sc3-plugins-language
qrencode:
name:
apt: qrencode
ffmpeg:
name:
apt: ffmpeg
ffmpeg-docs:
name:
apt: ffmpeg-doc
graphicsmagick:
name:
apt: graphicsmagick
graphicsmagick-compatibility:
name:
apt: graphicsmagick-imagemagick-compat
timg:
name:
apt: timg
tmux:
name:
apt: tmux
# @TODO add glow apt repository in install@linux bootstrap role play before uncommenting the below
# glow:
# name:
# apt: glow
# @TODO add ZFS apt repository in install@linux bootstrap role play before uncommenting the below
# zfs:
# name:
# apt: zfsutils-linux
# @TODO manually install the commented below on current active new VPS, then uncomment
# dpkg-dev:
# name:
# apt: dpkg-dev
# ldap-utils:
# name:
# apt: ldap-utils
# slapd:
# name:
# apt: slapd
# proftpd:
# name:
# apt: proftpd
# rsync:
# name:
# apt: rsync
# rclone:
# name:
# apt: rsync
# aria:
# name:
# apt: aria2
# mopidy:
# name:
# apt: mopidy
# mopidy-mpd:
# name:
# apt: mopidy-mpd
# caddy:
# name:
# apt: caddy
config:
git:
sys:
editor: nvim