refactor: restructured project for higher-utility naming practices and optimized data structures for variables
This commit is contained in:
38
roles/bootstrap/README.md
Normal file
38
roles/bootstrap/README.md
Normal 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).
|
||||
3
roles/bootstrap/defaults/main/software.yml
Normal file
3
roles/bootstrap/defaults/main/software.yml
Normal file
@@ -0,0 +1,3 @@
|
||||
#SPDX-License-Identifier: MIT-0
|
||||
---
|
||||
# defaults file for bootstrap
|
||||
9
roles/bootstrap/files/gitconfig.d/commit.msg
Normal file
9
roles/bootstrap/files/gitconfig.d/commit.msg
Normal 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)]
|
||||
0
roles/bootstrap/files/gitconfig.d/exclude.rules
Normal file
0
roles/bootstrap/files/gitconfig.d/exclude.rules
Normal file
18
roles/bootstrap/files/sshd_config.d/sftp.example.conf
Normal file
18
roles/bootstrap/files/sshd_config.d/sftp.example.conf
Normal 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
|
||||
3
roles/bootstrap/handlers/caddy.yml
Normal file
3
roles/bootstrap/handlers/caddy.yml
Normal file
@@ -0,0 +1,3 @@
|
||||
# SPDX-License-Identifier: MIT-0
|
||||
---
|
||||
# handlers file for bootstrap
|
||||
306
roles/bootstrap/handlers/git.yml
Normal file
306
roles/bootstrap/handlers/git.yml
Normal 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 }}"
|
||||
6
roles/bootstrap/handlers/main.yml
Normal file
6
roles/bootstrap/handlers/main.yml
Normal 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
|
||||
35
roles/bootstrap/meta/main.yml
Normal file
35
roles/bootstrap/meta/main.yml
Normal 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.
|
||||
38
roles/bootstrap/tasks/configure_gpg@linux.yml
Normal file
38
roles/bootstrap/tasks/configure_gpg@linux.yml
Normal 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 }}"
|
||||
|
||||
|
||||
|
||||
131
roles/bootstrap/tasks/configure_ssh@linux.yml
Normal file
131
roles/bootstrap/tasks/configure_ssh@linux.yml
Normal 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
|
||||
62
roles/bootstrap/tasks/create_users@linux.yml
Normal file
62
roles/bootstrap/tasks/create_users@linux.yml
Normal 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) }}"
|
||||
9
roles/bootstrap/tasks/init@linux.yml
Normal file
9
roles/bootstrap/tasks/init@linux.yml
Normal 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"
|
||||
86
roles/bootstrap/tasks/install@linux.yml
Normal file
86
roles/bootstrap/tasks/install@linux.yml
Normal 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) }}"
|
||||
9
roles/bootstrap/tasks/main.yml
Normal file
9
roles/bootstrap/tasks/main.yml
Normal 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"
|
||||
15
roles/bootstrap/templates/sshd_config.d/allowance.conf.j2
Normal file
15
roles/bootstrap/templates/sshd_config.d/allowance.conf.j2
Normal 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 %}
|
||||
27
roles/bootstrap/templates/sshd_config.d/auth.conf.j2
Normal file
27
roles/bootstrap/templates/sshd_config.d/auth.conf.j2
Normal 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 %}
|
||||
5
roles/bootstrap/templates/sshd_config.d/denyroot.conf.j2
Normal file
5
roles/bootstrap/templates/sshd_config.d/denyroot.conf.j2
Normal file
@@ -0,0 +1,5 @@
|
||||
{% if root_login_allowed %}
|
||||
PermitRootLogin yes
|
||||
{% else %}
|
||||
PermitRootLogin no
|
||||
{% endif %}
|
||||
2
roles/bootstrap/templates/sshd_config.d/harden.conf.j2
Normal file
2
roles/bootstrap/templates/sshd_config.d/harden.conf.j2
Normal file
@@ -0,0 +1,2 @@
|
||||
ClientAliveInterval {{ client_subsistence }}
|
||||
ClientAliveCountMax {{ client_subsist_warn_max }}
|
||||
3
roles/bootstrap/tests/inventory
Normal file
3
roles/bootstrap/tests/inventory
Normal file
@@ -0,0 +1,3 @@
|
||||
#SPDX-License-Identifier: MIT-0
|
||||
localhost
|
||||
|
||||
6
roles/bootstrap/tests/test.yml
Normal file
6
roles/bootstrap/tests/test.yml
Normal file
@@ -0,0 +1,6 @@
|
||||
#SPDX-License-Identifier: MIT-0
|
||||
---
|
||||
- hosts: localhost
|
||||
remote_user: root
|
||||
roles:
|
||||
- bootstrap
|
||||
208
roles/bootstrap/vars/main/software.yml
Normal file
208
roles/bootstrap/vars/main/software.yml
Normal 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
|
||||
|
||||
Reference in New Issue
Block a user