refactor: restructured project for higher-utility naming practices and optimized data structures for variables
This commit is contained in:
@@ -1,5 +0,0 @@
|
||||
---
|
||||
collections:
|
||||
- name: community.general
|
||||
version: "11.2.1"
|
||||
source: "https://galaxy.ansible.com"
|
||||
@@ -1,8 +0,0 @@
|
||||
Match Group ftp
|
||||
ForceCommand internal-sftp -u 003 -d /%u
|
||||
AuthorizedKeysFile /srv/.ftp/authorized_keys
|
||||
ChrootDirectory /srv/ftp
|
||||
PasswordAuthentication no
|
||||
AllowAgentForwarding no
|
||||
AllowTcpForwarding no
|
||||
X11Forwarding no
|
||||
@@ -1,16 +0,0 @@
|
||||
#SPDX-License-Identifier: MIT-0
|
||||
---
|
||||
# defaults file for lockdown
|
||||
files_mode: no
|
||||
create_users:
|
||||
- username: "{{ hostvars[inventory_hostname]['passwords'][0].username }}"
|
||||
password: "{{ hostvars[inventory_hostname]['passwords'][0].password }}"
|
||||
ssh_pubkey_filename_pattern: '.*\.pub'
|
||||
include_root_lock: yes
|
||||
gpg_private_keys_origin_host: localhost
|
||||
ssh_keypairs_origin_host: localhost
|
||||
gpg_origin_private_keyids: [] # @NOTE list of gpg key ids from origin or source server
|
||||
gpg_origin_private_key_passwords: "{{ vaulted_gpg_origin_private_key_passwords }}" # @NOTE list of gpg key passwords from origin or source server
|
||||
ssh_origin_keypairs_filenames: [] # @NOTE list of basenames (filename sans extension) of SSH keypairs
|
||||
git_config_name: ~ # @NOTE: has equivalent field under lockdown role vars example YAML file, but different value
|
||||
git_config_email: ~ # @NOTE: has equivalent field under lockdown role vars example YAML file, but different value
|
||||
@@ -1,12 +0,0 @@
|
||||
#!/usr/bin/bash
|
||||
# ssh aliases
|
||||
alias ssh_send="scp -C"
|
||||
# @NOTE consider adding aliases for scp source or destination hosts
|
||||
|
||||
# flatpak aliases
|
||||
alias clone="rsync -pogAXtlHrDx --stats --info=progress2"
|
||||
alias flatshell="flatpak run --user --command=sh"
|
||||
alias codium="flatpak run --user com.vscodium.codium"
|
||||
|
||||
# podman aliases
|
||||
alias docker="podman"
|
||||
@@ -1,13 +0,0 @@
|
||||
#!/usr/bin/bash
|
||||
# podman bash function
|
||||
conman () {
|
||||
if command -v podman &> /dev/null; then
|
||||
CONTAINER_MANAGER="podman"
|
||||
elif command -v docker &> /dev/null; then
|
||||
CONTAINER_MANAGER="docker"
|
||||
else
|
||||
exit 1
|
||||
fi
|
||||
|
||||
$CONTAINER_MANAGER
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
Host local
|
||||
HostName localhost
|
||||
Port 22
|
||||
# IdentitiesOnly yes
|
||||
# IdentityFile ~/.ssh/*.ppk
|
||||
# AddKeysToAgent yes
|
||||
|
||||
Host local.me
|
||||
HostName localhost
|
||||
Port 22
|
||||
# User admin
|
||||
# IdentitiesOnly yes
|
||||
# IdentityFile ~/.ssh/*.ppk
|
||||
# AddKeysToAgent yes
|
||||
|
||||
Host *.local.local
|
||||
HostName localhost
|
||||
# IdentitiesOnly yes
|
||||
# IdentityFile ~/.ssh/*.ppk
|
||||
# AddKeysToAgent yes
|
||||
|
||||
Host ip4.local.local
|
||||
# HostName 127.0.0.1
|
||||
Port 22
|
||||
AddressFamily inet
|
||||
|
||||
Host ip6.local.local
|
||||
# HostName ::1
|
||||
Port 22
|
||||
AddressFamily inet6
|
||||
@@ -1,2 +0,0 @@
|
||||
PasswordAuthentication no
|
||||
PermitEmptyPasswords no
|
||||
@@ -1 +0,0 @@
|
||||
PermitRootLogin no
|
||||
@@ -1,15 +0,0 @@
|
||||
# This file is written by xdg-user-dirs-update
|
||||
# If you want to change or add directories, just edit the line you're
|
||||
# interested in. All local changes will be retained on the next run.
|
||||
# Format is XDG_xxx_DIR="$HOME/yyy", where yyy is a shell-escaped
|
||||
# homedir-relative path, or XDG_xxx_DIR="/yyy", where /yyy is an
|
||||
# absolute path. No other format is supported.
|
||||
#
|
||||
XDG_DESKTOP_DIR="$HOME/Desktop"
|
||||
XDG_DOWNLOAD_DIR="$HOME/Downloads"
|
||||
XDG_TEMPLATES_DIR="$HOME/Templates"
|
||||
XDG_PUBLICSHARE_DIR="$HOME/Public"
|
||||
XDG_DOCUMENTS_DIR="$HOME/Documents"
|
||||
XDG_MUSIC_DIR="$HOME/Music"
|
||||
XDG_PICTURES_DIR="$HOME/Pictures"
|
||||
XDG_VIDEOS_DIR="$HOME/Videos"
|
||||
@@ -1,13 +0,0 @@
|
||||
# SPDX-License-Identifier: MIT-0
|
||||
---
|
||||
# handlers file for lockdown
|
||||
- name: Restart SSH server
|
||||
when: ansible_facts["user_id"] == "root"
|
||||
ansible.builtin.service:
|
||||
name: ssh
|
||||
state: restarted
|
||||
tags:
|
||||
- default
|
||||
- finalize
|
||||
register: restarted_ssh
|
||||
listen: "restart ssh"
|
||||
@@ -1,12 +0,0 @@
|
||||
# SPDX-License-Identifier: MIT-0
|
||||
---
|
||||
# tasks file for lockdown
|
||||
- name: Disable shell for root user
|
||||
when: ansible_facts["user_id"] != "root"
|
||||
become: true
|
||||
ansible.builtin.user:
|
||||
name: root
|
||||
shell: /sbin/nologin
|
||||
tags:
|
||||
- deshell_root
|
||||
register: root_shell_disabled
|
||||
@@ -1,128 +0,0 @@
|
||||
# 'preferred_signing_key' -> 'gpg_preferred_signing'
|
||||
# 'gpg_or_ssh_git_signing' -> 'git_signing_key_type'
|
||||
- name: Install git package
|
||||
ansible.builtin.package:
|
||||
name: git
|
||||
state: latest
|
||||
- name: Configure git name and email
|
||||
block:
|
||||
- name: Configure git name
|
||||
community.general.git_config:
|
||||
name: user.name
|
||||
scope: global
|
||||
state: present
|
||||
value: "{{ git_config_name }}"
|
||||
- name: Configure git email
|
||||
community.general.git_config:
|
||||
name: user.email
|
||||
scope: global
|
||||
state: present
|
||||
value: "{{ git_config_email }}"
|
||||
- name: Configure git signing GPG key
|
||||
when: git_signing_key_type == "gpg"
|
||||
block:
|
||||
- name: Configure specified git signing GPG key
|
||||
when: preferred_signing_key > -1
|
||||
community.general.git_config:
|
||||
name: user.signingkey
|
||||
scope: global
|
||||
state: present
|
||||
value: "{{ gpg_origin_private_keyids[preferred_signing_key] }}"
|
||||
register: selected_signing_key
|
||||
- name: Configure random git signing GPG key
|
||||
when: preferred_signing_key <= -1
|
||||
community.general.git_config:
|
||||
name: user.signingkey
|
||||
scope: global
|
||||
state: present
|
||||
value: "{{ gpg_origin_private_keyids | random }}"
|
||||
register: selected_signing_key
|
||||
- name: Configure git signing SSH key
|
||||
when: git_signing_key_type == "ssh"
|
||||
block:
|
||||
- name: Acquire SSH key-pairs from other system
|
||||
when: not files_mode
|
||||
block:
|
||||
- name: Acquire private SSH keys from other system
|
||||
delegate_to: "{{ ssh_keypairs_origin_host }}"
|
||||
ansible.builtin.command:
|
||||
argv:
|
||||
- cat
|
||||
- "~/.ssh/{{ item }}.ppk"
|
||||
loop: "{{ ssh_origin_keypairs_filenames }}"
|
||||
register: ssh_secrets
|
||||
- name: Find SSH public keys in other system
|
||||
delegate_to: "{{ ssh_keypairs_origin_host }}"
|
||||
ansible.builtin.command:
|
||||
argv:
|
||||
- cat
|
||||
- "~/.ssh/{{ item }}.pub"
|
||||
loop: "{{ ssh_origin_keypairs_filenames }}"
|
||||
register: ssh_nonsecrets
|
||||
- name: Create private SSH keys
|
||||
ansible.builtin.copy:
|
||||
content: "{{ item }}"
|
||||
dest: "{{ ansible_facts['user_dir'] }}/.ssh/{{ ssh_origin_keypairs_filenames[idx] }}.ppk"
|
||||
force: yes
|
||||
backup: yes
|
||||
mode: "0600"
|
||||
state: present
|
||||
loop: "{{ ssh_secrets.results }}"
|
||||
loop_control:
|
||||
index_var: idx
|
||||
register: created_ssh_private_keys
|
||||
- name: Create public SSH keys
|
||||
ansible.builtin.copy:
|
||||
content: "{{ item }}"
|
||||
dest: "{{ ansible_facts['user_dir'] }}/.ssh/{{ ssh_origin_keypairs_filenames[idx] }}.pub"
|
||||
force: yes
|
||||
backup: yes
|
||||
mode: "0644"
|
||||
state: present
|
||||
loop: "{{ ssh_nonsecrets.results }}"
|
||||
loop_control:
|
||||
index_var: idx
|
||||
register: created_ssh_public_keys
|
||||
- name: Acquire SSH key-pairs
|
||||
when: files_mode
|
||||
block:
|
||||
- name: Transfer private SSH keys
|
||||
ansible.builtin.copy:
|
||||
src: ssh/{{ ansible_facts['user_id'] }}/{{ item }}.ppk
|
||||
dest: "{{ ansible_facts['user_dir'] }}/.ssh/{{ item }}.ppk"
|
||||
force: yes
|
||||
backup: yes
|
||||
mode: "0600"
|
||||
state: present
|
||||
loop: "{{ ssh_origin_keypairs_filenames }}"
|
||||
loop_control:
|
||||
index_var: idx
|
||||
register: created_ssh_private_keys
|
||||
- name: Transfer public SSH keys
|
||||
ansible.builtin.copy:
|
||||
src: ssh/{{ ansible_facts['user_id'] }}/{{ item }}.pub
|
||||
dest: "{{ ansible_facts['user_dir'] }}/.ssh/{{ item }}.pub"
|
||||
force: yes
|
||||
backup: yes
|
||||
mode: "0644"
|
||||
state: present
|
||||
loop: "{{ ssh_origin_keypairs_filenames }}"
|
||||
loop_control:
|
||||
index_var: idx
|
||||
register: created_ssh_public_keys
|
||||
- name: Configure acquired, specified SSH public key as git signing key
|
||||
when: preferred_signing_key > -1
|
||||
community.general.git_config:
|
||||
name: user.signingkey
|
||||
scope: global
|
||||
state: present
|
||||
value: "{{ created_ssh_public_keys.results[preferred_signing_key] }}"
|
||||
register: selected_signing_key
|
||||
- name: Configure acquired, random SSH public key as git signing key
|
||||
when: preferred_signing_key <= -1
|
||||
community.general.git_config:
|
||||
name: user.signingkey
|
||||
scope: global
|
||||
state: present
|
||||
value: "{{ created_ssh_public_keys.results | random }}"
|
||||
register: selected_signing_key
|
||||
@@ -1,54 +0,0 @@
|
||||
---
|
||||
- name: Acquire GPG private keys from other system
|
||||
when: not files_mode
|
||||
block:
|
||||
- name: Acquire GPG private keys' contents from other system
|
||||
delegate_to: "{{ gpg_private_keys_origin_host }}"
|
||||
ansible.builtin.command:
|
||||
argv:
|
||||
- gpg
|
||||
- -a
|
||||
- --export-secret-key
|
||||
- "{{ item }}"
|
||||
loop: "{{ gpg_origin_private_keyids }}"
|
||||
register: gpg_secrets
|
||||
- name: Create GPG private keys using acquired GPG private keys' contents
|
||||
ansible.builtin.copy:
|
||||
content: "{{ item }}"
|
||||
dest: "{{ ansible_facts['user_dir'] }}/.gnupg/{{ gpg_origin_private_keyids[idx] }}.priv.asc"
|
||||
force: yes
|
||||
backup: yes
|
||||
mode: "0600"
|
||||
state: present
|
||||
loop: "{{ gpg_secrets.results }}"
|
||||
loop_control:
|
||||
index_var: idx
|
||||
register: created_gpg_private_keys
|
||||
- name: Acquire GPG private keys
|
||||
when: files_mode
|
||||
ansible.builtin.copy:
|
||||
src: gnupg/{{ ansible_facts['user_id'] }}/{{ item }}.asc
|
||||
dest: "{{ ansible_facts['user_dir'] }}/.gnupg/{{ item }}.priv.asc"
|
||||
force: yes
|
||||
backup: yes
|
||||
mode: "0600"
|
||||
state: present
|
||||
loop: "{{ gpg_origin_private_keyids }}"
|
||||
loop_control:
|
||||
index_var: idx
|
||||
register: created_gpg_private_keys
|
||||
- name: Import GPG private keys
|
||||
when: (gpg_origin_private_key_passwords | length) == (gpg_origin_private_keyids | length)
|
||||
ansible.builtin.command:
|
||||
argv:
|
||||
- gpg
|
||||
- --batch
|
||||
- --import
|
||||
- --yes
|
||||
- --passphrase-fd
|
||||
- 0
|
||||
- "{{ item.dest }}"
|
||||
stdin: "{{ gpg_origin_private_key_passwords[idx] }}"
|
||||
loop: "{{ created_gpg_private_keys.results }}"
|
||||
loop_control:
|
||||
index_var: idx
|
||||
@@ -1,172 +0,0 @@
|
||||
# SPDX-License-Identifier: MIT-0
|
||||
---
|
||||
# tasks file for lockdown
|
||||
# @NOTE: assumes one logged in to SSH server as root to begin with, hence no need for privilege escalation
|
||||
- name: Create users
|
||||
when: ansible_facts["user_id"] == "root"
|
||||
block:
|
||||
- name: Create sys-admin user
|
||||
ansible.builtin.user:
|
||||
name: "{{ create_users[0].username }}"
|
||||
uid: 1000
|
||||
password: "{{ create_users[0].password }}"
|
||||
append: yes
|
||||
groups:
|
||||
- sudo
|
||||
shell: /bin/bash
|
||||
generate_ssh_key: yes
|
||||
password_expire_min: 93
|
||||
password_expire_max: 186
|
||||
password_expire_warn: 45
|
||||
comment: sysadmin
|
||||
# ssh_key_passphrase: "{{ item.password }}"
|
||||
state: present
|
||||
tags:
|
||||
- default
|
||||
- administrative_user
|
||||
register: created_admin
|
||||
- name: Create new user
|
||||
ansible.builtin.user:
|
||||
name: "{{ item.username }}"
|
||||
uid: 1000
|
||||
password: "{{ item.password }}"
|
||||
append: yes
|
||||
shell: /bin/bash
|
||||
generate_ssh_key: yes
|
||||
password_expire_min: 93
|
||||
password_expire_max: 186
|
||||
password_expire_warn: 45
|
||||
comment: administrator
|
||||
# ssh_key_passphrase: "{{ item.password }}"
|
||||
state: present
|
||||
loop: "{{ create_users[1:] }}"
|
||||
tags:
|
||||
- other_users
|
||||
register: created_users
|
||||
- name: Specify authorized SSH keys for users based on local public keys
|
||||
when: not files_mode and ansible_facts["user_id"] == "root"
|
||||
block:
|
||||
- name: Acquire list of SSH public keys for sys-admin user
|
||||
delegate_to: "{{ ssh_keypairs_origin_host }}"
|
||||
ansible.builtin.find:
|
||||
paths: "{{ lookup('env', 'HOME') }}/.ssh"
|
||||
patterns:
|
||||
- '{{ ssh_pubkey_filename_pattern }}'
|
||||
use_regex: yes
|
||||
recurse: no
|
||||
tags:
|
||||
- default
|
||||
- administrative_user
|
||||
- admin_ssh
|
||||
register: ssh_public_keys
|
||||
- name: Acquire contents of SSH public keys for sys-admin user
|
||||
delegate_to: "{{ ssh_keypairs_origin_host }}"
|
||||
ansible.builtin.command:
|
||||
argv:
|
||||
- cat
|
||||
- "{{ item.path }}"
|
||||
loop: "{{ ssh_public_keys.files }}"
|
||||
register: ssh_public_keys_contents
|
||||
- name: Register SSH public keys as sys-admin user's authorized keys
|
||||
ansible.builtin.lineinfile:
|
||||
path: "{{ created_admin.home }}/.ssh/authorized_keys"
|
||||
line: "{{ item }}"
|
||||
owner: "{{ created_admin.name }}"
|
||||
group: "{{ created_admin.name }}"
|
||||
mode: "0600"
|
||||
create: yes
|
||||
insertafter: EOF
|
||||
state: present
|
||||
tags:
|
||||
- default
|
||||
- administrative_user
|
||||
- admin_ssh
|
||||
loop: "{{ ssh_public_keys_contents.results }}"
|
||||
- name: Register SSH public keys as other users' authorized keys
|
||||
ansible.builtin.copy:
|
||||
src: "ssh/{{ item.name }}/authorized_keys"
|
||||
dest: "{{ item.home }}/.ssh/authorized_keys"
|
||||
force: yes
|
||||
backup: yes
|
||||
owner: "{{ item.name }}"
|
||||
group: "{{ item.name }}"
|
||||
mode: "0600"
|
||||
state: present
|
||||
tags:
|
||||
- other_users
|
||||
- others_ssh
|
||||
loop: "{{ created_users.results }}"
|
||||
register: authorized_ssh_pubkeys
|
||||
- name: Specify authorized SSH keys for users
|
||||
when: files_mode and ansible_facts["user_id"] == "root"
|
||||
block:
|
||||
- name: Specify authorized keys file for sys-admin user
|
||||
ansible.builtin.copy:
|
||||
src: ssh/authorized_keys
|
||||
dest: "{{ created_admin.home }}/.ssh/authorized_keys"
|
||||
force: yes
|
||||
backup: yes
|
||||
owner: "{{ created_admin.name }}"
|
||||
group: "{{ created_admin.name }}"
|
||||
mode: "0600"
|
||||
state: present
|
||||
tags:
|
||||
- default
|
||||
- administrative_user
|
||||
- admin_ssh
|
||||
register: authorized_admin_ssh_pubkeys
|
||||
- name: Specify authorized keys file for other users
|
||||
ansible.builtin.copy:
|
||||
src: "ssh/{{ item.name }}/authorized_keys"
|
||||
dest: "{{ item.home }}/.ssh/authorized_keys"
|
||||
force: yes
|
||||
backup: yes
|
||||
owner: "{{ item.name }}"
|
||||
group: "{{ item.name }}"
|
||||
mode: "0600"
|
||||
tags:
|
||||
- other_users
|
||||
- others_ssh
|
||||
loop: "{{ created_users.results }}"
|
||||
register: authorized_ssh_pubkeys
|
||||
- name: Lock down root SSH access
|
||||
when: ansible_facts["user_id"] == "root"
|
||||
block:
|
||||
- name: Constrain SSH authentication methods to using SSH key
|
||||
ansible.builtin.copy:
|
||||
src: sshd_config.d/auth.conf
|
||||
dest: /etc/ssh/sshd_config.d/auth.conf
|
||||
force: yes
|
||||
backup: yes
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0644"
|
||||
state: present
|
||||
tags:
|
||||
- depass_root
|
||||
register: constrained_auth
|
||||
- name: Prohibit access to root via SSH
|
||||
ansible.builtin.copy:
|
||||
src: sshd_config.d/denyroot.conf
|
||||
dest: /etc/ssh/sshd_config.d/denyroot.conf
|
||||
force: yes
|
||||
backup: yes
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0644"
|
||||
state: present
|
||||
tags:
|
||||
- prohib_root_ssh
|
||||
register: prohibited_root_ssh_login
|
||||
- name: Lock the root account
|
||||
when: include_root_lock
|
||||
ansible.builtin.user:
|
||||
name: root
|
||||
password_lock: yes
|
||||
tags:
|
||||
- delog_root
|
||||
register: prohibited_root_login
|
||||
tags:
|
||||
- default
|
||||
- deroot
|
||||
notify: "restart ssh"
|
||||
71
.gitignore
vendored
71
.gitignore
vendored
@@ -1,65 +1,10 @@
|
||||
/.env/
|
||||
/.tmp/
|
||||
rika/
|
||||
senpai/
|
||||
.galaxy_cache/
|
||||
/galaxy_token
|
||||
.ansible/log.txt
|
||||
.ansible/facts/
|
||||
.ansible/collections/ansible_collections/
|
||||
.env/
|
||||
*.bak
|
||||
|
||||
group_vars/**/main.yml
|
||||
host_vars/**/main.yml
|
||||
**/group_vars/**/main.yml
|
||||
**/host_vars/**/main.yml
|
||||
group_vars/**/vault.yml
|
||||
host_vars/**/vault.yml
|
||||
**/group_vars/**/vault.yml
|
||||
**/host_vars/**/vault.yml
|
||||
|
||||
.ansible/roles/**/vars/*
|
||||
playbooks/vars/ssh_keys_vault.yml
|
||||
playbooks/vars/ssh_keys.yml
|
||||
**/playbooks/vars/ssh_keys_vault.yml
|
||||
**/playbooks/vars/ssh_keys.yml
|
||||
playbooks/vars/main.yml
|
||||
playbooks/vars/vault.yml
|
||||
**/playbooks/vars/main.yml
|
||||
**/playbooks/vars/vault.yml
|
||||
|
||||
files/**/**/config
|
||||
**/files/**/**/config
|
||||
files/**/**/authorized_keys
|
||||
**/files/**/**/authorized_keys
|
||||
files/**/**/*.conf
|
||||
**/files/**/**/*.conf
|
||||
files/**/**/*.dirs
|
||||
**/files/**/**/*.dirs
|
||||
files/**/**/*.defaults
|
||||
**/files/**/**/*.defaults
|
||||
files/**/**/bash_aliases
|
||||
**/files/**/**/bash_aliases
|
||||
files/**/**/bash_functions
|
||||
**/files/**/**/bash_functions
|
||||
|
||||
templates/**/**/config
|
||||
**/templates/**/**/config
|
||||
templates/**/**/authorized_keys
|
||||
**/templates/**/**/authorized_keys
|
||||
templates/**/**/*.conf
|
||||
**/templates/**/**/*.conf
|
||||
templates/**/**/*.dirs
|
||||
**/templates/**/**/*.dirs
|
||||
templates/**/**/*.defaults
|
||||
**/templates/**/**/*.defaults
|
||||
templates/**/**/bash_aliases
|
||||
**/templates/**/**/bash_aliases
|
||||
templates/**/**/bash_functions
|
||||
**/templates/**/**/bash_functions
|
||||
|
||||
hosts.ini
|
||||
hosts.yml
|
||||
hosts.yaml
|
||||
hosts.json
|
||||
vault.yml
|
||||
.secrets/*
|
||||
**/*.key
|
||||
**/*.gpg
|
||||
**/*.asc
|
||||
**/*.pem
|
||||
**/*.ppk
|
||||
log.txt
|
||||
@@ -54,7 +54,7 @@ fact_caching_connection=.ansible/facts
|
||||
|
||||
# (pathspec) Colon separated paths in which Ansible will search for collections content. Collections must be in nested *subdirectories*, not directly in these directories. For example, if ``COLLECTIONS_PATHS`` includes ``'{{ ANSIBLE_HOME ~ "/collections" }}'``, and you want to add ``my.collection`` to that directory, it must be saved as ``'{{ ANSIBLE_HOME} ~ "/collections/ansible_collections/my/collection" }}'``.
|
||||
|
||||
collections_path=collections:.ansible/collections:/usr/share/ansible/collections
|
||||
collections_path=.ansible/collections:collections:/usr/share/collections:/etc/ansible/collections
|
||||
|
||||
# (boolean) A boolean to enable or disable scanning the sys.path for installed collections
|
||||
;collections_scan_sys_path=True
|
||||
@@ -223,7 +223,7 @@ netconf_plugins=plugins/netconf:.ansible/netconf:/usr/share/ansible/plugins/netc
|
||||
;remote_user=
|
||||
|
||||
# (pathspec) Colon separated paths in which Ansible will search for Roles.
|
||||
roles_path=roles:.ansible/roles:/usr/share/ansible/roles:/etc/ansible/roles
|
||||
roles_path=.ansible/roles:roles:/usr/share/ansible/roles:/etc/ansible/roles
|
||||
|
||||
# (string) Set the main callback used to display Ansible output. You can only have one at a time.
|
||||
# You can have many other callbacks, but just one can be in charge of stdout.
|
||||
@@ -357,7 +357,7 @@ host_key_checking=False
|
||||
;old_plugin_cache_clear=False
|
||||
|
||||
# (path) A number of non-playbook CLIs have a ``--playbook-dir`` argument; this sets the default value for it.
|
||||
playbook_dir=playbooks
|
||||
playbook_dir=playbooks:/etc/ansible/playbooks
|
||||
|
||||
# (string) This sets which playbook dirs will be used as a root to process vars plugins, which includes finding host_vars/group_vars
|
||||
;playbook_vars_root=top
|
||||
|
||||
37
group_vars/all
Normal file
37
group_vars/all
Normal file
@@ -0,0 +1,37 @@
|
||||
#SPDX-License-Identifier: MIT-0
|
||||
---
|
||||
# vars file
|
||||
custom_vars: ~
|
||||
fqdn: ~
|
||||
vps_service:
|
||||
# @DOC <bool>
|
||||
exists: true
|
||||
# @DOC <vault<str>>
|
||||
password: ~
|
||||
# @DOC <vault<str>>
|
||||
api_key: ~
|
||||
# @DOC <str>
|
||||
type: "linode"
|
||||
# @DOC <str>
|
||||
region: "us-east"
|
||||
# @DOC <list<str>>
|
||||
ssh_authorized_keys: []
|
||||
# @DOC <str>
|
||||
root_fate: disposal
|
||||
# @DOC <list<str>>
|
||||
keywords: []
|
||||
groups:
|
||||
sample_group:
|
||||
group_name: ~
|
||||
type: ~
|
||||
users:
|
||||
admin:
|
||||
username: admin
|
||||
password: "password123"
|
||||
shell: /bin/bash
|
||||
home: ~
|
||||
admin: true
|
||||
type: regular
|
||||
group: ~
|
||||
groups: [sudo]
|
||||
ssh_authorized_keys:
|
||||
129
host_vars/vps1
Normal file
129
host_vars/vps1
Normal file
@@ -0,0 +1,129 @@
|
||||
#SPDX-License-Identifier: MIT-0
|
||||
---
|
||||
# vars file
|
||||
custom_vars:
|
||||
generality:
|
||||
ssh_authorized_keys:
|
||||
- sk-ssh-ed25519@openssh.com AAAAGnNrLXNzaC1lZDI1NTE5QG9wZW5zc2guY29tAAAAIIO0sbFLwfgSWpWwn4cy4cddKvV74efUMZVYTTjX2vnjAAAABHNzaDo= rika@hikiki
|
||||
- sk-ssh-ed25519@openssh.com AAAAGnNrLXNzaC1lZDI1NTE5QG9wZW5zc2guY29tAAAAIHJqHHMplgqm8yiq4Qwisk67p9+f9sLM8tIAzuw2qkwpAAAABHNzaDo= rika@hikiki
|
||||
ssh_private_key_paths:
|
||||
- ~/.ssh/id_ed25519_sukaato_yubikey.ppk
|
||||
- ~/.ssh/id_ed25519_sukaato_miniyubikey.ppk
|
||||
fqdn: sukaato.moe
|
||||
vps_service:
|
||||
exists: true
|
||||
password: !vault |
|
||||
$ANSIBLE_VAULT;1.2;AES256;vps1-root
|
||||
39303536373434346134346536653462623164373265646430636330616666323437363365366364
|
||||
3030303736323432636631306361313031376238356335350a653032376432333562663361623236
|
||||
30313766633662656637623033313461633662303763306361313337623965396130616531323061
|
||||
6538316265376536630a616330666430323631393035313933346332353939313833623666636164
|
||||
61653264643933636666613665633461646336656337383730396262633239376439
|
||||
api_key: !vault |
|
||||
$ANSIBLE_VAULT;1.2;AES256;vps1-api
|
||||
36353161313366323930643037643637636664373266356433616632313766386161666663336366
|
||||
3462666366646338616561643939333134666162616465320a376364363833653136366434633264
|
||||
63643364626235666333363335656536396239646562393837343138653737346537316536303739
|
||||
6565633730326234350a366435653637373061336162343134643431613034623761666264393134
|
||||
61343062323933366235356132376366636534343530316432336265316632393531303161316632
|
||||
64323431666361303137313937316631393266643961613863643035333237613931343533303537
|
||||
66643166303733333761313566343030343762306633613733613762386339653663323730666637
|
||||
38663634383531633838
|
||||
type: "linode"
|
||||
region: "us-east"
|
||||
ssh_authorized_keys:
|
||||
- sk-ecdsa-sha2-nistp256@openssh.com AAAAInNrLWVjZHNhLXNoYTItbmlzdHAyNTZAb3BlbnNzaC5jb20AAAAIbmlzdHAyNTYAAABBBNoC2Z4oLDEEeX7SmRpUlyXVQ+uJg3ZdjMaDONzJtMuZa9/bVzAByiNTXM0yYzas/lFLpOKh3tUw8NCS+3QMjkIAAAAEc3NoOg== rika@hikiki
|
||||
- sk-ecdsa-sha2-nistp256@openssh.com AAAAInNrLWVjZHNhLXNoYTItbmlzdHAyNTZAb3BlbnNzaC5jb20AAAAIbmlzdHAyNTYAAABBBDJjW/BGw3Rkr7pB69hwGGCD3poBWMRLPdUlrTjYqP/Lam5FZATRlzpDbCyub0tgBZwWIiGGvS88XWosESk2lToAAAAEc3NoOg== rika@hikiki
|
||||
root_fate: disposal
|
||||
ssh_private_key_paths:
|
||||
- ~/.ssh/id_ecdsa-sha2_sukaato_yubikey.ppk
|
||||
- ~/.ssh/id_ecdsa-sha2_sukaato_miniyubikey.ppk
|
||||
ssh_private_key_path_pref: 0
|
||||
keywords:
|
||||
- social media
|
||||
- internet
|
||||
- chat
|
||||
- web
|
||||
- cloud
|
||||
- "file-share"
|
||||
- stream
|
||||
groups:
|
||||
# @NOTE key/field names SHOULD match value of 'group_name' key or field of its object
|
||||
remote:
|
||||
group_name: remote
|
||||
type: system
|
||||
users:
|
||||
# @NOTE key/field names MUST match value of 'username' key or field of its object
|
||||
senpai:
|
||||
username: senpai
|
||||
password: !vault |
|
||||
$ANSIBLE_VAULT;1.2;AES256;vps1-senpai
|
||||
62626662666239376237616464626630393562373130623934653764333139346337313539613863
|
||||
3163623834636235323433323066373435393432303234320a343433343334386131613062353761
|
||||
30323832666333366330306261386435303066626664336332393263366262626430386230356161
|
||||
3863383530616135390a383361383136366565363066326332306631323730663533623163666133
|
||||
62646339613864356264656362326562636336376136656336323962616236396562623833313531
|
||||
38633938386435656437383033656630373238366663323265326533333035376233646465626363
|
||||
33316364356533616437343439653635626637393137633034613432383530376132656138333636
|
||||
66376535346164393630383532373963663439366339646666336264393731313135343962613932
|
||||
33316433656236353230643332333231623730323262363831396437656331626539
|
||||
shell: /bin/bash
|
||||
home: ~
|
||||
admin: true
|
||||
type: regular
|
||||
group: ~
|
||||
groups:
|
||||
- sudo
|
||||
- "{{ groups.remote.group_name }}"
|
||||
services: []
|
||||
ssh_authorized_keys: "{{ custom_vars.generality.ssh_authorized_keys }}"
|
||||
ssh_private_key_paths: "{{ custom_vars.generality.ssh_private_key_paths }}"
|
||||
ssh_private_key_path_pref: 0
|
||||
gpg_keys:
|
||||
- id: 558041D5CF2AB23B # @NOTE professional
|
||||
name: professional
|
||||
password: !vault |
|
||||
$ANSIBLE_VAULT;1.2;AES256;vps1-senpai
|
||||
30326232323038346232663635343439393130376666616165626339646461663165393539353733
|
||||
3666346333366237643964653633306263373365373731660a663361633030613630623434353332
|
||||
35363939356339623732623061323866353739623936353234636133303534363863666462633133
|
||||
3462653139366138330a336433343566633066643834613836353331316163653739656230663164
|
||||
6131
|
||||
- id: F0CA546935C02C76 # @NOTE personal
|
||||
name: personal
|
||||
password: !vault |
|
||||
$ANSIBLE_VAULT;1.2;AES256;vps1-senpai
|
||||
62373636643365623161643266313734633632633066373863666661306433393464396565363636
|
||||
3638353234393838623133633839316130393539356464370a346262313262623164623637323066
|
||||
37333432313438343761636330663332383035306131643339326261386231643231353930373961
|
||||
3466643062396465330a656362316336376338653963376137663632646266343335333036656461
|
||||
3964
|
||||
- id: CE245A7D7CEE3639 # @NOTE undercover
|
||||
name: undercover
|
||||
password: !vault |
|
||||
$ANSIBLE_VAULT;1.2;AES256;vps1-senpai
|
||||
38343338373839336436396431366665383437646233613036393666613339363062616134383631
|
||||
3938333066323838623938353231623034643635663031620a646631346233653535643337623737
|
||||
63373437653665623361663131346137336435623862396262353764323161323338663731613266
|
||||
6466323838306131390a383962616461616237343261666630393166303932623765633239353631
|
||||
3230
|
||||
gpg_keyid_pref: 0
|
||||
git_profile:
|
||||
name: Alex Tavarez
|
||||
email: ajt95@prole.biz
|
||||
ftp:
|
||||
username: ftp
|
||||
password: ~
|
||||
shell: /sbin/nologin
|
||||
home: /srv/ftp
|
||||
admin: false
|
||||
type: system
|
||||
group: ~
|
||||
groups:
|
||||
- "{{ groups.remote.group_name }}"
|
||||
services: [proftpd,sftp]
|
||||
ssh_authorized_keys: "{{ custom_vars.generality.ssh_authorized_keys }}"
|
||||
ssh_private_key_paths: "{{ custom_vars.generality.ssh_private_key_paths }}"
|
||||
ssh_private_key_path_pref: 0
|
||||
gpg_keys: []
|
||||
gpg_keyid_pref: 0
|
||||
@@ -1,37 +0,0 @@
|
||||
---
|
||||
ungrouped:
|
||||
hosts:
|
||||
localhost:
|
||||
ansible_host: "localhost"
|
||||
localhost4:
|
||||
ansible_host: "127.0.0.1"
|
||||
localhost6:
|
||||
ansible_host: "::1"
|
||||
sukaato:
|
||||
ansible_host: ~
|
||||
sukaato4:
|
||||
ansible_host: ~
|
||||
sukaato6:
|
||||
ansible_host: ~
|
||||
localhost_hosts:
|
||||
hosts:
|
||||
localhost:
|
||||
localhost4:
|
||||
localhost6:
|
||||
sukaato_hosts:
|
||||
hosts:
|
||||
sukaato:
|
||||
sukaato4:
|
||||
sukaato6:
|
||||
locals:
|
||||
children:
|
||||
localhost_hosts:
|
||||
vars:
|
||||
ansible_connection: local
|
||||
name_surname:
|
||||
surname_household:
|
||||
servers:
|
||||
children:
|
||||
sukaato_hosts:
|
||||
vars:
|
||||
ansible_port: 22
|
||||
19
playbooks/deroot.yml
Normal file
19
playbooks/deroot.yml
Normal file
@@ -0,0 +1,19 @@
|
||||
---
|
||||
- name: Create new users and lock down VPS
|
||||
hosts: vps1
|
||||
remote_user: root
|
||||
vars:
|
||||
ansible_user: root
|
||||
ansible_ssh_private_key_file: "{{ vps_service.ssh_private_key_paths[vps_service.ssh_private_key_path_pref] }}"
|
||||
tasks:
|
||||
- name: Engage in SSH hardening and user creation
|
||||
ansible.builtin.include_role:
|
||||
# allow_duplicates: true
|
||||
defaults_from: main
|
||||
handlers_from: main
|
||||
name: bootstrap
|
||||
# public: false
|
||||
# rolespec_validate: true
|
||||
tasks_from: "init@{{ ansible_facts['system'].lowercase() }}"
|
||||
vars_from: main
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
# Default settings for user directories
|
||||
#
|
||||
# The values are relative pathnames from the home directory and
|
||||
# will be translated on a per-path-element basis into the users locale
|
||||
DESKTOP=Desktop
|
||||
DOWNLOAD=Downloads
|
||||
TEMPLATES=Templates
|
||||
PUBLICSHARE=Public
|
||||
DOCUMENTS=Documents
|
||||
MUSIC=Music
|
||||
PICTURES=Pictures
|
||||
VIDEOS=Videos
|
||||
# Another alternative is:
|
||||
#MUSIC=Documents/Music
|
||||
#PICTURES=Documents/Pictures
|
||||
@@ -1,15 +0,0 @@
|
||||
# Default settings for user directories
|
||||
#
|
||||
# The values are relative pathnames from the home directory and
|
||||
# will be translated on a per-path-element basis into the users locale
|
||||
DESKTOP=Desktop
|
||||
DOWNLOAD=Downloads
|
||||
TEMPLATES=Templates
|
||||
PUBLICSHARE=Public
|
||||
DOCUMENTS=Documents
|
||||
MUSIC=Music
|
||||
PICTURES=Pictures
|
||||
VIDEOS=Videos
|
||||
# Another alternative is:
|
||||
#MUSIC=Documents/Music
|
||||
#PICTURES=Documents/Pictures
|
||||
@@ -1,6 +0,0 @@
|
||||
---
|
||||
passwords:
|
||||
- username: admin
|
||||
password: "{{ vaulted_passwords.admin.password }}"
|
||||
local_ssh_private_key_files: [] # @NOTE list paths to SSH private keys
|
||||
chosen_local_ssh_private_key_file: "{{ local_ssh_private_key_files | random }}"
|
||||
@@ -1,7 +0,0 @@
|
||||
---
|
||||
# @TODO encrypt as vault
|
||||
# @NOTE see https://docs.ansible.com/ansible/latest/reference_appendices/faq.html#how-do-i-generate-encrypted-passwords-for-the-user-module
|
||||
# Specifically, section for hashing using python passlib library
|
||||
vaulted_passwords:
|
||||
admin:
|
||||
password: ~
|
||||
@@ -1,6 +0,0 @@
|
||||
---
|
||||
passwords:
|
||||
- username: admin
|
||||
password: "{{ vaulted_passwords.admin.password }}"
|
||||
local_ssh_private_key_files: [] # @NOTE list paths to SSH private keys
|
||||
chosen_local_ssh_private_key_file: "{{ local_ssh_private_key_files | random }}"
|
||||
@@ -1,7 +0,0 @@
|
||||
---
|
||||
# @TODO encrypt as vault
|
||||
# @NOTE see https://docs.ansible.com/ansible/latest/reference_appendices/faq.html#how-do-i-generate-encrypted-passwords-for-the-user-module
|
||||
# Specifically, section for hashing using python passlib library
|
||||
vaulted_passwords:
|
||||
admin:
|
||||
password: ~
|
||||
20
playbooks/init.yml
Normal file
20
playbooks/init.yml
Normal file
@@ -0,0 +1,20 @@
|
||||
---
|
||||
- name: Initialize VPS
|
||||
hosts: localhost
|
||||
connection: local
|
||||
tasks:
|
||||
- name: Create a VPS using Linode
|
||||
when: vps_service.type == "linode"
|
||||
community.general.linode_v4:
|
||||
access_token: "{{ vps_service.api_key }}"
|
||||
authorized_keys: "{{ vps_service.ssh_authorized_keys }}"
|
||||
image: linode/debian13
|
||||
label: sukaato
|
||||
private_ip: true
|
||||
region: "{{ vps_service.region }}"
|
||||
root_pass: "{{ vps_service.password }}"
|
||||
tags: "{{ hostvars[inventory_hostname].keywords }}"
|
||||
state: "{{ 'present' if vps_service.exists else 'absent' }}"
|
||||
tags:
|
||||
- vps_step
|
||||
- linode_step
|
||||
@@ -1,197 +0,0 @@
|
||||
---
|
||||
- name: init_login
|
||||
hosts: servers # @NOTE for IPv6, switch to 'servers6' instead of 'servers4'--for both, 'servers'
|
||||
vars_files:
|
||||
# @NOTE if second line is uncommented with its variables actively in use, first line should too be uncommented
|
||||
# - vars/ssh_keys_vault.yml
|
||||
- vars/ssh_keys.yml
|
||||
vars:
|
||||
ansible_user: "{{ passwords[0].username }}"
|
||||
# @NOTE one of below two lines should be commented/uncommented in a mutually exclusive fashion
|
||||
# ansible_ssh_private_key_file: "{{ chosen_native_ssh_private_key_file | default(chosen_local_ssh_private_key_file, true) }}" # @NOTE only works with soft-coded SSH key list building
|
||||
ansible_ssh_private_key_file: "{{ chosen_local_ssh_private_key_file }}" # @NOTE references an inventory / group variable
|
||||
# @NOTE below three lines should only be uncommented when above two are commented and vice versa; key-based authentication should have already been enabled prior to running this playbook
|
||||
# ansible_password: "{{ passwords[0].password }}"
|
||||
ansible_python_interpreter: “{{ ansible_playbook_python }}”
|
||||
personal_computers: locals # @NOTE can change to *_households group or {{ name }}_{{ surname }} group name
|
||||
vars_prompt:
|
||||
- name: gpg_or_ssh_git_signing
|
||||
prompt: Enter preferred signing key type (e.g., ssh or gpg)
|
||||
unsafe: yes
|
||||
private: no
|
||||
default: "ssh"
|
||||
- name: git_preferred_signing
|
||||
prompt: Enter index or number of preferred signing key (negative number for random)
|
||||
unsafe: yes
|
||||
private: no
|
||||
default: -1
|
||||
tasks:
|
||||
- name: Disable shell access for root
|
||||
ansible.builtin.include_role:
|
||||
name: lockdown
|
||||
defaults_from: main
|
||||
vars_from: main
|
||||
handlers_from: main
|
||||
tasks_from: deshell
|
||||
apply:
|
||||
become: yes
|
||||
tags:
|
||||
- default
|
||||
- name: Create global bash aliases
|
||||
become: yes
|
||||
ansible.builtin.copy:
|
||||
src: bash/bash_aliases
|
||||
dest: /etc/bash_aliases
|
||||
owner: root
|
||||
group: root
|
||||
follow: yes
|
||||
force: yes
|
||||
backup: yes
|
||||
mode: "0644"
|
||||
state: present
|
||||
tags:
|
||||
- default
|
||||
- source_sys_bashrc
|
||||
- name: Create global bash functions
|
||||
become: yes
|
||||
ansible.builtin.copy:
|
||||
src: bash/bash_functions
|
||||
dest: /etc/bash_functions
|
||||
owner: root
|
||||
group: root
|
||||
follow: yes
|
||||
force: yes
|
||||
backup: yes
|
||||
mode: "0644"
|
||||
state: present
|
||||
tags:
|
||||
- default
|
||||
- source_sys_bashrc
|
||||
- name: Register bash aliases and functions to global bashrc
|
||||
become: yes
|
||||
ansible.builtin.blockinfile:
|
||||
block: |
|
||||
if [ -f /etc/bash_aliases ]; then
|
||||
. /etc/bash_aliases
|
||||
fi
|
||||
|
||||
if [ -f /etc/bash_functions ]; then
|
||||
. /etc/bash_functions
|
||||
fi
|
||||
path: /etc/bash.bashrc
|
||||
prepend_newline: yes
|
||||
marker: "# {mark} ANSIBLE MANAGED SYSTEM-WIDE BASH ALIASES AND FUNCTIONS BLOCK"
|
||||
insertafter: EOF
|
||||
create: yes
|
||||
owner: root
|
||||
group: root
|
||||
backup: yes
|
||||
state: present
|
||||
tags:
|
||||
- default
|
||||
- source_sys_bashrc
|
||||
- name: Start XDG configuration tasks if current host in servers group
|
||||
when: "'servers' in group_names and ansible_connection != 'local'"
|
||||
become: yes
|
||||
block:
|
||||
- name: Create XDG user home directory environment variables
|
||||
ansible.builtin.copy:
|
||||
src: files/servers/xdg/user-dirs.defaults
|
||||
dest: /etc/xdg/user-dirs.defaults
|
||||
owner: root
|
||||
group: root
|
||||
follow: yes
|
||||
force: yes
|
||||
backup: yes
|
||||
mode: "0644"
|
||||
state: present
|
||||
- name: Create XDG user home directory environment variables
|
||||
ansible.builtin.copy:
|
||||
src: "xdg/{{ ansible_facts['user_id'] }}/user-dirs.dirs"
|
||||
dest: "{{ ansible_facts['user_dir'] }}/.config/user-dirs.dirs"
|
||||
owner: root
|
||||
group: root
|
||||
follow: yes
|
||||
force: yes
|
||||
backup: yes
|
||||
mode: "0644"
|
||||
state: present
|
||||
tags:
|
||||
- default
|
||||
- create_xdg_config
|
||||
- servers_exclusive
|
||||
- name: Start XDG configuration tasks if current host is local or personal
|
||||
when: "personal_computers in group_names or ansible_connection == 'local'"
|
||||
become: yes
|
||||
block:
|
||||
- name: Create XDG user home directory environment variables
|
||||
ansible.builtin.copy:
|
||||
src: files/locals/xdg/user-dirs.defaults
|
||||
dest: /etc/xdg/user-dirs.defaults
|
||||
owner: root
|
||||
group: root
|
||||
follow: yes
|
||||
force: yes
|
||||
backup: yes
|
||||
mode: "0644"
|
||||
state: present
|
||||
- name: Create XDG user home directory environment variables
|
||||
ansible.builtin.copy:
|
||||
src: "xdg/{{ ansible_facts['user_id'] }}/user-dirs.dirs"
|
||||
dest: "{{ ansible_facts['user_dir'] }}/.config/user-dirs.dirs"
|
||||
owner: root
|
||||
group: root
|
||||
follow: yes
|
||||
force: yes
|
||||
backup: yes
|
||||
mode: "0644"
|
||||
state: present
|
||||
tags:
|
||||
- default
|
||||
- create_xdg_config
|
||||
- locals_exclusive
|
||||
- name: Start SSH configuration tasks if current host is local or personal
|
||||
when: "personal_computers in group_names or ansible_connection == 'local'"
|
||||
become: yes
|
||||
block:
|
||||
- name: Create user SSH configuration
|
||||
ansible.builtin.copy:
|
||||
src: "ssh/{{ ansible_facts['user_id'] }}/config"
|
||||
dest: "{{ ansible_facts['user_dir'] }}/.ssh/config"
|
||||
follow: yes
|
||||
force: yes
|
||||
backup: yes
|
||||
owner: "{{ ansible_facts['user_id'] }}"
|
||||
group: "{{ ansible_facts['user_id'] }}"
|
||||
mode: "0600"
|
||||
state: present
|
||||
tags:
|
||||
- default
|
||||
- create_ssh_config
|
||||
- locals_exclusive
|
||||
- name: Import GPG private keys
|
||||
ansible.builtin.include_role:
|
||||
name: lockdown
|
||||
defaults_from: main
|
||||
vars_from: main
|
||||
handlers_from: main
|
||||
tasks_from: gpg
|
||||
tags:
|
||||
- default
|
||||
- import_gpg_privkeys
|
||||
- name: Set up git
|
||||
ansible.builtin.include_role:
|
||||
name: lockdown
|
||||
defaults_from: main
|
||||
vars_from: main
|
||||
handlers_from: main
|
||||
tasks_from: git
|
||||
vars:
|
||||
git_signing_key_type: gpg_or_ssh_git_signing
|
||||
preferred_signing_key: git_preferred_signing
|
||||
tags:
|
||||
- default
|
||||
- configure_git
|
||||
|
||||
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
---
|
||||
- name: manage_root
|
||||
hosts: servers # @NOTE for IPv6, switch to 'servers6' instead of 'servers4'--for both, 'servers'
|
||||
remote_user: root # MUST be run as root
|
||||
vars:
|
||||
ansible_user: root
|
||||
# ansible_ssh_user: root
|
||||
vars_prompt:
|
||||
- name: ansible_password
|
||||
prompt: Enter pasword for root user of VPS
|
||||
unsafe: yes
|
||||
private: yes
|
||||
# - name: ansible_ssh_pass
|
||||
# prompt: Enter pasword for root user of VPS
|
||||
# unsafe: yes
|
||||
# private: yes
|
||||
tasks:
|
||||
- name: Set up sys-admin account on VPS and secure VPS
|
||||
ansible.builtin.include_role:
|
||||
name: lockdown
|
||||
defaults_from: main
|
||||
vars_from: main
|
||||
handlers_from: main
|
||||
tasks_from: main
|
||||
tags:
|
||||
- init
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
- name: Lock down VPS
|
||||
ansible.builtin.import_playbook: manage_root.yml
|
||||
- name: Bootstrap VPS
|
||||
ansible.builtin.import_playbook: init_login.yml
|
||||
@@ -1,4 +0,0 @@
|
||||
---
|
||||
native_ssh_private_keys: "{{ vaulted_native_ssh_private_keys }}"
|
||||
native_ssh_private_key_files: []
|
||||
chosen_native_ssh_private_key_file: "{{ native_ssh_private_key_files | random }}"
|
||||
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
|
||||
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,4 +3,4 @@
|
||||
- hosts: localhost
|
||||
remote_user: root
|
||||
roles:
|
||||
- lockdown
|
||||
- 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