Compare commits

...

43 Commits

Author SHA1 Message Date
Alex Tavarez
d149361e60 set up or initialized Python project 2025-12-21 16:58:39 -05:00
Alex Tavarez
1851b0bb04 added Python lock file to version control exclusion rules 2025-12-21 16:57:41 -05:00
Alex Tavarez
88aac6b598 created environment variables file 2025-12-21 16:56:35 -05:00
Alex Tavarez
ed06773cac changed a version control exclude rule due to change in Python environment dir change 2025-12-21 16:55:59 -05:00
Alex Tavarez
0dc3edc0bc added next few tasks 2025-12-21 16:54:39 -05:00
Alex Tavarez
ea7d9f7caf changed some fields/keys/attributes and changed references to reflect those changes 2025-12-21 16:53:52 -05:00
Alex Tavarez
ffa5408e9c further clarified STDOUT message guiding user 2025-12-21 14:44:47 -05:00
Alex Tavarez
2d7f783bb9 added some more guiding STDOUT statements for commands and removed useless function 2025-12-21 14:35:56 -05:00
Alex Tavarez
eb437ce2e9 refactor: script file no longer necessary after refactor 2025-12-21 13:58:11 -05:00
Alex Tavarez
07ff003870 refactor: changed name and location of file setting environment variables, made it take shell arguments with native functions as well 2025-12-21 13:57:09 -05:00
Alex Tavarez
d88a9d6176 fix: playbook task already has access to inventory-level variables 2025-12-16 13:48:33 -05:00
Alex Tavarez
c39463f4a7 feature: added a set of default path environment variables to be used as part of execution environment 2025-12-16 13:47:47 -05:00
Alex Tavarez
474574860a fix: changed from dot notation to bracket notation for attr/meth access 2025-12-16 13:44:10 -05:00
Alex Tavarez
645d815413 fix: removed some unnecessary escape characters, added title for installation section, added links to Gitea Wiki 2025-12-16 12:52:05 -05:00
Alex Tavarez
3c3f7cbf1d fix: edited typos and added more exposition for the role scope section 2025-12-15 10:22:58 -05:00
Alex Tavarez
e175e41810 Filled in role metadata and placed in dependencies 2025-12-15 10:12:08 -05:00
Alex Tavarez
3040a6203d Added task for adding essential documentation on Ansible inventory file naming conventions 2025-12-15 10:10:45 -05:00
Alex Tavarez
29bd8090be Rewrote README to reflect refactored and rewritten Ansible project, and essential information 2025-12-15 10:08:23 -05:00
Alex Tavarez
40bbe62203 removed Podman post-installation handlers as containers will no longer be managed via Ansible 2025-12-07 21:35:53 -05:00
Alex Tavarez
cb69a9b430 removed a list of container software due to separate repo handling this via Compose files 2025-12-07 21:34:43 -05:00
Alex Tavarez
19f1681473 Added containers to software dictionary list 2025-12-01 21:11:39 -05:00
Alex Tavarez
5e5dabff1a Added the creation of symbolic links to Nextcloud snap binaries for Nextcloud 2025-12-01 19:20:06 -05:00
Alex Tavarez
d4aa128e4c Added configuration value related to setting default phone region for Nextcloud 2025-12-01 18:59:31 -05:00
Alex Tavarez
512c7a82e5 Added default phone region for accepting phone numbers, and overwrote URLs for Nextcloud 2025-12-01 18:58:07 -05:00
Alex Tavarez
6c4c3d0794 Added ClamAV and ClamAV SystemD daemon package installations 2025-11-30 14:54:25 -05:00
Alex Tavarez
5a75942336 Created handler for podman installation that installs containers 2025-11-30 14:53:27 -05:00
Alex Tavarez
dd463297dd Added TODOs for this handler 2025-11-30 14:52:09 -05:00
Alex Tavarez
a1bc0ae727 Created systemd user unit service file for Aria2 2025-11-18 21:47:11 -05:00
Alex Tavarez
4dd524a5f2 Added RSyncD configuration file 2025-11-18 21:45:43 -05:00
Alex Tavarez
5901f69c29 Added handler for installing containers after podman installation 2025-11-18 21:45:14 -05:00
Alex Tavarez
73ef748c95 Added Jinja variables 2025-11-18 21:44:28 -05:00
Alex Tavarez
065e93d7c7 Added configuration fields for Aria2 2025-11-18 21:43:48 -05:00
Alex Tavarez
3f11b7c78d Added handler for RSyncD configuration 2025-11-18 21:38:39 -05:00
Alex Tavarez
64873bb846 Added a handler for Aria2 setting up daemon and configuration 2025-11-18 21:37:40 -05:00
Alex Tavarez
bdc6905491 Added Fail2Ban jails for ProFTPd and SSHD 2025-11-18 21:36:59 -05:00
Alex Tavarez
272c1342c1 Excluded Ansible Galaxy cache data and token file from version control tracking 2025-11-18 21:36:09 -05:00
Alex Tavarez
db18e6ddf6 Commented LDAP and NodeJS packages, uncommented aria and rsync packages, from software list--added container, misc and snap software 2025-11-18 21:34:30 -05:00
Alex Tavarez
ee3cf37d2f Imported new additional tasks 2025-11-18 21:31:56 -05:00
Alex Tavarez
7d2d9081e2 Created set of additional tasks for custom filters for new Fail2Ban ProFTPd jail 2025-11-18 21:30:47 -05:00
Alex Tavarez
870b16582c Added set of tasks configuring Nextcloud after installation 2025-11-18 21:29:25 -05:00
Alex Tavarez
de70d3bdd3 Added task to modify default branch name 2025-11-18 21:28:33 -05:00
Alex Tavarez
72395382d3 Changed order of preferred paths for Ansible 2025-11-18 21:27:01 -05:00
Alex Tavarez
fd5cdca0e9 Excluded collections from version control tracking 2025-11-18 21:25:57 -05:00
26 changed files with 789 additions and 149 deletions

6
.bin/ansible_aliases Executable file
View File

@@ -0,0 +1,6 @@
#!/bin/bash
set -euo pipefail
alias ansible-galaxy="/usr/bin/ansible-galaxy"
alias ansible-vault="/usr/bin/ansible-vault"
alias ansible-playbook="/usr/bin/ansible-playbook"

187
.bin/skansible.sh Executable file
View File

@@ -0,0 +1,187 @@
#!/bin/bash
set -euo pipefail
SKATO_ANSIBLE_ROOT=$(dirname "$0")
SKATO_ANSIBLE_ROOT=$(dirname "$SKATO_ANSIBLE_ROOT")
export SKATO_ANSIBLE_ROOT
printf "root=%s\n" "$SKATO_ANSIBLE_ROOT" > "./config" # INI format
export SKATO_BOOTSTRAP_ROLE="${SKATO_ANSIBLE_ROOT}/roles/bootstrap"
export SKANSIBLE_SECRETS="${SKATO_ANSIBLE_ROOT}/.secrets"
if [[ -f "./ansible_aliases" ]]; then
source ./ansible_aliases
fi
# Relative directory paths for role templates/files
export SKANSIBLE_ARIA="aria2"
export SKANSIBLE_PROFTPD="proftpd"
export SKANSIBLE_PROFTPD_CONFS="${SKANSIBLE_PROFTPD}/conf.d"
# @NOTE below 4 filepaths have filenames that must correspond to
# the filenames in role ProFTPd templates'/files' Display settings
export SKANSIBLE_PROFTPD_CONFS_WELCOME="${SKANSIBLE_PROFTPD}/conf.d/WELCOME.txt"
export SKANSIBLE_PROFTPD_CONFS_BANNER="${SKANSIBLE_PROFTPD}/conf.d/BANNER.txt"
export SKANSIBLE_PROFTPD_CONFS_SUCCESS="${SKANSIBLE_PROFTPD}/conf.d/SUCCESS.txt"
export SKANSIBLE_PROFTPD_CONFS_EXIT="${SKANSIBLE_PROFTPD}/conf.d/BYE.txt"
export SKANSIBLE_SSHD_CONFS="sshd_config.d"
export SKANSIBLE_SYSTEMD="systemd"
export SKANSIBLE_SYSTEMD_USER_UNITS="${SKANSIBLE_SYSTEMD}/user"
export SKANSIBLE_FAIL2BAN="fail2ban"
export SKANSIBLE_FAIL2BAN_JAILS="${SKANSIBLE_FAIL2BAN}/jail.d"
export SKANSIBLE_FAIL2BAN_FILTERS="${SKANSIBLE_FAIL2BAN}/filter.d"
export SKANSIBLE_GITCONFIG_CONFS="gitconfig.d"
# @NOTE files in here must have extension "key" with IDs in
# "gpg_keys" inventory variable list as basenames.
export SKANSIBLE_GPG="gnupg"
# @NOTE files in path below must have extensions "key" (private),
# "crt" (signed), or "pem" (public) with inventory host FQDN as basename
export SKANSIBLE_SSL="ca-certificates"
set-root () {
if [[ $# -eq 0 ]]; then
SKATO_ANSIBLE_ROOT=$(awk -F "=" '/root/ {print $2}' "./config")
export SKATO_ANSIBLE_ROOT
elif [[ -z "$1" ]]; then
SKATO_ANSIBLE_ROOT="$1"
export SKATO_ANSIBLE_ROOT
sed -i 's|^(root=).*||g' "./config"
sed -i "1 i\root=${SKATO_ANSIBLE_ROOT}" "./config"
fi
}
gxy () {
ansible-galaxy "$@"
}
vult () {
ansible-vault "$@"
}
play () {
ansible-playbook "$@"
}
import-gpg () {
for id in "$@";
do
gpg --export-secret-keys "$id" > "${SKATO_BOOTSTRAP_ROLE}/files/${SKANSIBLE_GPG}/${id}.key"
printf "Please manually add GPG key with 'id' of '%s' in 'users.\$username.gpg_keys' list of inventory file." "$id"
done
printf "Please manually change ID attribute of GPG keys in 'users.\$username.gpg_keys' list of inventory file."
}
import-ssl () {
for domain in "$@";
do
cp "/usr/local/share/ca-certificates/${domain}.key" "${SKATO_BOOTSTRAP_ROLE}/files/${SKANSIBLE_SSL}/${domain}.key"
cp "/usr/local/share/ca-certificates/${domain}.pem" "${SKATO_BOOTSTRAP_ROLE}/files/${SKANSIBLE_SSL}/${domain}.pem"
cp "/usr/local/share/ca-certificates/${domain}.crt" "${SKATO_BOOTSTRAP_ROLE}/files/${SKANSIBLE_SSL}/${domain}.crt"
printf "Please manually change 'fqdn' attribute in inventory group or host variable file to '%s'." "$domain"
done
}
import () {
case "$1" in
ssl) shift; import-ssl "$@";;
gpg) shift; import-gpg "$@";;
*) exit 1;;
esac
}
decrypt () {
while getopts "mv:i:d:" flag; do
case "$flag" in
m) METHOD=$OPTARG;;
v) VAULT_ID=$OPTARG;;
i) INPUT_FILE=$OPTARG;;
d) OUTPUT_PATH=$OPTARG;;
*) exit 1;;
esac
done
if ! [[ "$VAULT_ID" == *"@"* ]]; then
ID_TAG="$VAULT_ID"
if [[ "$METHOD" == "prompt" ]]; then
VAULT_ID="${VAULT_ID}@prompt"
elif [[ "$METHOD" == "file" ]]; then
if [[ -z "$INPUT_FILE" ]]; then
exit 1
else
VAULT_ID="${VAULT_ID}@${INPUT_FILE}"
fi
else
exit 1
fi
fi
if [[ -z "$OUTPUT_PATH" ]]; then
OUTPUT_FILE="${SKANSIBLE_SECRETS}/${ID_TAG}.txt"
else
mkdir -p "${SKANSIBLE_SECRETS}/${OUTPUT_PATH}"
OUTPUT_FILE="${SKANSIBLE_SECRETS}/${OUTPUT_PATH}/${ID_TAG}.txt"
fi
ansible-vault decrypt --vault-id "$VAULT_ID" --output "$OUTPUT_FILE" "$INPUT_FILE"
}
encrypt () {
while getopts "mv:d:pn:" flag; do
case "$flag" in
m) METHOD="$OPTARG";;
v) VAULT_ID="$OPTARG";;
d) PASS_PATH="$OPTARG";;
p) read -rp "Provide intended password: " PASSWORD;;
n) VAR_NAME="$OPTARG";;
*) exit 1;;
esac
done
while [[ -z "$PASSWORD" ]]; do
printf "Password missing. \nPlease specify a password. \n"
read -rp "Provide intended password: " PASSWORD
done
if ! [[ "$VAULT_ID" == *"@"* ]]; then
ID_TAG="${VAULT_ID}"
if [[ "$METHOD" == "prompt" ]]; then
VAULT_ID="${VAULT_ID}@prompt"
elif [[ "$METHOD" == "file" ]]; then
if [[ -z "$PASS_PATH" ]]; then
PASS_FILE="${SKANSIBLE_SECRETS}/${VAULT_ID}.txt"
else
mkdir -p "${SKANSIBLE_SECRETS}/${PASS_PATH}"
PASS_FILE="${SKANSIBLE_SECRETS}/${PASS_PATH}/${VAULT_ID}.txt"
fi
printf "%s\n" "$PASSWORD" > "$PASS_FILE"
VAULT_ID="${VAULT_ID}@${PASS_FILE}"
fi
fi
printf "Make sure to copy following to appropriate location in appropriate YAML file under %s: \n" "$SKATO_ANSIBLE_ROOT"
if [[ -z "$VAR_NAME" ]]; then
ansible-vault encrypt_string --name "$VAR_NAME" --stdin-name "$VAR_NAME" --vault-id "$VAULT_ID" --output - "$PASSWORD"
else
ansible-vault encrypt_string --stdin-name "$ID_TAG" --vault-id "$VAULT_ID" --output - "$PASSWORD"
fi
YAMLS_WITH_PASSWORDS=("${SKATO_BOOTSTRAP_ROLE}/vars/main/software.yml" "${SKATO_BOOTSTRAP_ROLE}/defaults/main/software.yml")
printf "Examples of common YAML files passwords may be in: \n"
printf " 1. any YAML file in %s \n" "${SKATO_ANSIBLE_ROOT}/hostvars"
printf " 2. any YAML file in %s \n" "${SKATO_ANSIBLE_ROOT}/groupvars"
for i in "${!YAMLS_WITH_PASSWORDS[@]}"; do
printf " %u. %s \n" "$(( i + 3 ))" "${YAMLS_WITH_PASSWORDS[$i]}"
done
}
# source ./extensions.d/edit.sh
case "$1" in
set-root) shift; set-root "$1";;
gxy) shift; gxy "$@";;
vult) shift; vult "$@";;
play) shift; play "$@";;
import) shift; import "$@";;
decrypt) shift; decrypt "$@";;
encrypt) shift; encrypt "$@";;
*) exit 1;;
esac

28
.env Normal file
View File

@@ -0,0 +1,28 @@
SKATO_ANSIBLE_ROOT=$(dirname "$0")
SKATO_ANSIBLE_ROOT=$(dirname "$SKATO_ANSIBLE_ROOT")
SKATO_BOOTSTRAP_ROLE="${SKATO_ANSIBLE_ROOT}/roles/bootstrap"
SKANSIBLE_SECRETS="${SKATO_ANSIBLE_ROOT}/.secrets"
# Relative directory paths for role templates/files
SKANSIBLE_ARIA="aria2"
SKANSIBLE_PROFTPD="proftpd"
SKANSIBLE_PROFTPD_CONFS="${SKANSIBLE_PROFTPD}/conf.d"
# @NOTE below 4 filepaths have filenames that must correspond to
# the filenames in role ProFTPd templates'/files' Display settings
SKANSIBLE_PROFTPD_CONFS_WELCOME="${SKANSIBLE_PROFTPD}/conf.d/WELCOME.txt"
SKANSIBLE_PROFTPD_CONFS_BANNER="${SKANSIBLE_PROFTPD}/conf.d/BANNER.txt"
SKANSIBLE_PROFTPD_CONFS_SUCCESS="${SKANSIBLE_PROFTPD}/conf.d/SUCCESS.txt"
SKANSIBLE_PROFTPD_CONFS_EXIT="${SKANSIBLE_PROFTPD}/conf.d/BYE.txt"
SKANSIBLE_SSHD_CONFS="sshd_config.d"
SKANSIBLE_SYSTEMD="systemd"
SKANSIBLE_SYSTEMD_USER_UNITS="${SKANSIBLE_SYSTEMD}/user"
SKANSIBLE_FAIL2BAN="fail2ban"
SKANSIBLE_FAIL2BAN_JAILS="${SKANSIBLE_FAIL2BAN}/jail.d"
SKANSIBLE_FAIL2BAN_FILTERS="${SKANSIBLE_FAIL2BAN}/filter.d"
SKANSIBLE_GITCONFIG_CONFS="gitconfig.d"
# @NOTE files in here must have extension "key" with IDs in
# "gpg_keys" inventory variable list as basenames.
SKANSIBLE_GPG="gnupg"
# @NOTE files in path below must have extensions "key" (private),
# "crt" (signed), or "pem" (public) with inventory host FQDN as basename
SKANSIBLE_SSL="ca-certificates"

6
.gitignore vendored
View File

@@ -1,4 +1,4 @@
.env/
.venv/
*.bak
hosts.yml
.secrets/*
@@ -12,5 +12,9 @@ hosts.yml
log.txt
**/update-motd.d/*.sh
**/vhost@vps1-sukaato.moe.conf.j2
collections/
motd
banner
.galaxy_cache/
galaxy_token
uv.lock

1
.python-version Normal file
View File

@@ -0,0 +1 @@
3.13

View File

@@ -1,25 +1,39 @@
# SUKAATO Ansible
This repository is for automating the management of the configuration of, and the provisioning of software for, my virtual private servers using [Ansible](https://www.redhat.com/en/ansible-collaborative?intcmp=7015Y000003t7aWQAQ). This repository is especially useful for setting up the virtual private server(s) that is(/are) to host and serve my website(s). It is also meant to be useful for provisioning of software and the configuration of that software for personal or household LAN computers.
This repository is for automating the management of the configuration of, and the provisioning of software for, my virtual private servers using [Ansible](https://www.redhat.com/en/ansible-collaborative?intcmp=7015Y000003t7aWQAQ). It's main purpose is to spin up the VPSs, create initial users and groups, import SSH or GPG keys, lock down SSH access or harden SSH, and then install and configure packages available to the given package manager of the operating system. The `bootstrap` role in here serves to abstract some of these tasks for our main playbook files.
## Installation and Use
## Variable Names and Their Scopes
All files with file extension `.example` must be converted to [YAML](https://yaml.org/) files that follow their semantics and naming (or follow the minimum bare "namespace" nesting for dictionaries or lists thereof) *prior* to executing any given [play or task](https://docs.ansible.com/ansible/latest/playbook_guide/playbooks_intro.html). For more on semantics and naming conventions see the [mini-documentation](#mini-documentation).
To be able to make use of the Ansible playbooks, it is necessary to specify some variables in or at relevant scopes, though some may have some defaults. The relevant scopes variables are defined in, for our purposes, are:
> [!IMPORTANT]
> Keep in mind files with the `.example` extension may also be present recursively under given [role](https://docs.ansible.com/ansible/latest/playbook_guide/playbooks_reuse_roles.html) directories (i.e., under path `${SUKAATO_ANSIBLE_PROJECT}/.ansible/roles/**/**/`).
- Ansible **inventory scope**: corresponds to variables inside per-hostname files in `group_vars` or `host_vars` directories, or the inventory file itself, i.e. `hosts.ini` or `hosts.yml`. The inventory file has some enforced naming conventions to be covered later or elsewhere.
- Ansible **role scope**: corresponds to variables found in files inside the `defaults` / `vars` directory in a role directory, or variables found in files inside subdirectory `main` in either `defaults` or `vars` directory of that role directory. There are favored conventional directory structures within which these variables are specified in the aforementioned directories, to be covered later or elsewhere.
## Mini-Documentation
Other variables that tend to have default definitions as is but that may be of interest are those found in Jinja templates of roles, in this case of the role `bootstrap`. Look through the `bootstrap` role's `templates` directory and you will discover them--most of them defined in role tasks or handlers that make reference to them. However, more information may be found elsewhere.
### Available Roles
### Inventory Scope
To surmise, the available or planned [roles](https://docs.ansible.com/ansible/latest/playbook_guide/playbooks_reuse_roles.html) are as follows (and are all found under `${SUKAATO_ANSIBLE_PROJECT}/.ansible/roles`):
Herein are listed the relevant variables at or in the *inventory* scope. These must be specified for a specific inventory host or group, typically in their corresponding files under `group_vars` or `host_vars`. Some variables must take in a dictionary type to be valid. To save space, there will be more detail on what keys are required or optional for such dictionaries [elsewhere](https://git.sukaato.moe/admin/skato-ansible/wiki/Inventory-Scope) and not here.
role name | purpose
---|---
lockdown | creating initial `sudo`-capable user, disabling system/SSH root login, setting up key-based SSH authentication, transferring GPG keys, configuring environment, hardening system
bootstrap | installing programming language and server/container packages, installing extra system managers and essential utilities, configuring and running servers/services/containers
postinstall | installing and configuring custom sets of packages, largely non-server related and not essential
name | type | value validity rule
---|---|---
`fqdn` | `<str>` | fully qualified domain name
`vps_service` | `<dict{<str>:<str\|bool\|list>}>` | valid fields providing data for spinning up new VPS
`groups` | `<dict{$group_name:<dict>}>` | fields/keys that are group names with data configuring that group
`users` | `<dict{$user_name:<dict>}>` | fields/keys that are user names with data configuring that user
`keywords` | `<list[<str>]>` | strings that describe the VPS, useful for applying tags if allowed by API
`custom_vars` | `<dict{<str>:<any>}>` | your own custom variables, though there are some reserved variable names for this namespace
### Role Scope
Herein are listed the relevant variables at or in the *role* scope. These must be specified for a set of role tasks expected to run in a playbook for the host specified for its play. Some variables must take in a dictionary type to be valid. To save space, there will be more detail on what keys are required or optional for such dictionaries [elsewhere](https://git.sukaato.moe/admin/skato-ansible/wiki/Role-Scope) and not here.
name | type | value validity rule
---|---|---
`software` | `<dict{<str>:<dict>}>` | valid fields providing data for software installations
`config` | `<dict{$software_name:<dict>}>` | software name fields providing data for configuring that software
## Installation
> **TBC**
> This README is yet unfinished. Check back later.
> This README is yet unfinished and unverified. Check back later.

View File

@@ -16,7 +16,7 @@
;force_color=False
# (path) The default root path for Ansible config files on the controller.
home=.ansible
home=.
# (boolean) This setting allows suppressing colorizing output, which is used to give a better indication of failure and status information.
;nocolor=False
@@ -38,7 +38,7 @@ become_plugins=plugins/become:.ansible/plugins/become:/usr/share/ansible/plugins
fact_caching=jsonfile
# (string) Defines connection or path information for the cache plugin
fact_caching_connection=.ansible/facts
fact_caching_connection=.facts
# (string) Prefix to use for cache plugin files/tables
;fact_caching_prefix=ansible_facts
@@ -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=.ansible/collections:collections:/usr/share/collections:/etc/ansible/collections
collections_path=collections:.ansible/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
@@ -169,7 +169,7 @@ local_tmp=.tmp
;log_filter=
# (path) File to which Ansible will log on the controller. When empty logging is disabled.
log_path=.ansible/log.txt
log_path=log.txt
# (pathspec) Colon separated paths in which Ansible will search for Lookup Plugins.
lookup_plugins=plugins/lookup:.ansible/lookup:/usr/share/ansible/plugins/lookup
@@ -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=.ansible/roles:roles:/usr/share/ansible/roles:/etc/ansible/roles
roles_path=roles:.ansible/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.

View File

@@ -2,7 +2,7 @@
---
# vars file
custom_vars:
generality:
shared:
ssh_authorized_keys:
- sk-ssh-ed25519@openssh.com AAAAGnNrLXNzaC1lZDI1NTE5QG9wZW5zc2guY29tAAAAIIO0sbFLwfgSWpWwn4cy4cddKvV74efUMZVYTTjX2vnjAAAABHNzaDo= rika@hikiki
- sk-ssh-ed25519@openssh.com AAAAGnNrLXNzaC1lZDI1NTE5QG9wZW5zc2guY29tAAAAIHJqHHMplgqm8yiq4Qwisk67p9+f9sLM8tIAzuw2qkwpAAAABHNzaDo= rika@hikiki
@@ -56,6 +56,7 @@ groups:
remote:
group_name: remote
type: system
id: ~
users:
# @NOTE key/field names MUST match value of 'username' key or field of its object
senpai:
@@ -81,8 +82,8 @@ users:
- sudo
- "{{ groups.remote.group_name }}"
services: [sshd]
ssh_authorized_keys: "{{ custom_vars.generality.ssh_authorized_keys }}"
ssh_private_key_paths: "{{ custom_vars.generality.ssh_private_key_paths }}"
ssh_authorized_keys: "{{ custom_vars['shared']['ssh_authorized_keys'] }}"
ssh_private_key_paths: "{{ custom_vars['shared']['ssh_private_key_paths'] }}"
ssh_private_key_path_pref: 0
gpg_keys:
- id: 558041D5CF2AB23B # @NOTE professional
@@ -128,8 +129,8 @@ users:
groups:
- "{{ groups.remote.group_name }}"
services: [proftpd,sftp,ftps]
ssh_authorized_keys: "{{ custom_vars.generality.ssh_authorized_keys }}"
ssh_private_key_paths: "{{ custom_vars.generality.ssh_private_key_paths }}"
ssh_authorized_keys: "{{ custom_vars['shared']['ssh_authorized_keys'] }}"
ssh_private_key_paths: "{{ custom_vars['shared']['ssh_private_key_paths'] }}"
ssh_private_key_path_pref: 0
gpg_keys: []
gpg_keyid_pref: 0

View File

@@ -13,7 +13,7 @@
private_ip: true
region: "{{ vps_service.region }}"
root_pass: "{{ vps_service.password }}"
tags: "{{ hostvars[inventory_hostname].keywords }}"
tags: "{{ keywords }}"
state: "{{ 'present' if vps_service.exists else 'absent' }}"
tags:
- vps_step

12
pyproject.toml Normal file
View File

@@ -0,0 +1,12 @@
[project]
name = "skansible"
version = "0.1.0"
description = "Add your description here"
readme = "README.md"
requires-python = ">=3.13"
dependencies = [
"ansible>=13.1.0",
"ansible-lint>=25.12.1",
"ansible-navigator>=25.12.0",
"click>=8.3.1",
]

View File

@@ -0,0 +1,16 @@
# fail2ban filter for the ProFTPD FTP daemon
[INCLUDES]
before = common.conf
[Definition]
_daemon = proftpd
failregex = \(\S+\[<HOST>\]\)[: -]+ USER \S+: no such user found from \S+ \[[0-9.]+\] to \S+:\S+\s*$
\(\S+\[<HOST>\]\)[: -]+ USER \S+ \(Login failed\):.*\s+$
\(\S+\[<HOST>\]\)[: -]+ Maximum login attempts \([0-9]+\) exceeded, connection refused.*\s+$
\(\S+\[<HOST>\]\)[: -]+ SECURITY VIOLATION: \S+ login attempted\.\s+$
\(\S+\[<HOST>\]\)[: -]+ Maximum login attempts \(\d+\) exceeded\s+$
ignoreregex =

View File

@@ -0,0 +1,7 @@
[proftpd]
enabled = true
port = 990
filter = custom_proftpd
logpath = /var/log/proftpd.log
maxretry = 6

View File

@@ -0,0 +1,35 @@
[sshd]
# ==========================
# SSH Jail Configuration
# ==========================
# Enable the SSH jail to monitor and protect against brute-force attacks.
enabled = true
# Port Fail2Ban should monitor for SSH connections.
# If you run SSH on a custom port, replace 'ssh' with the actual port number (e.g., 2222).
port = ssh
# Filter definition to use.
# 'sshd' refers to the default filter that matches common SSH authentication failures.
filter = sshd
# Log file location.
# '%(sshd_log)s' uses the default value set by the system, typically /var/log/auth.log or journalctl.
logpath = %(sshd_log)s
# Backend for reading logs.
# 'systemd' is recommended if your system uses journalctl for logging.
backend = systemd
# ==========================
# SSH-Specific Overrides
# ==========================
# Time window to evaluate failed login attempts.
# If 'maxretry' failures occur within this time, the IP will be banned.
findtime = 5m
# Number of failed attempts allowed before triggering a ban.
maxretry = 4

View File

@@ -0,0 +1,49 @@
# SPDX-License-Identifier: MIT-0
---
# handlers file for bootstrap
- name: Configure Aria2
listen: aria
when: ansible_facts['user_id'] in hostvars[inventory_hostname].users.keys()
block:
- name: Create Aria2 user configuration directory
block:
- name: Create configuration directory
ansible.builtin.file:
force: false
group: "{{ hostvars[inventory_hostname].users[ansible_facts['user_id']].group | default(ansible_facts['user_id']) }}"
owner: "{{ ansible_facts['user_id'] }}"
path: "{{ ansible_facts['user_dir'] }}/.config"
state: directory
- name: Create configuration directory
when: ansible_facts['user_id'] in hostvars[inventory_hostname].users.keys()
ansible.builtin.file:
force: false
group: "{{ hostvars[inventory_hostname].users[ansible_facts['user_id']].group | default(ansible_facts['user_id']) }}"
owner: "{{ ansible_facts['user_id'] }}"
path: "{{ ansible_facts['user_dir'] }}/.config/aria2"
state: directory
- name: Create Aria2 configuration
ansible.builtin.template:
backup: true
dest: "{{ ansible_facts['user_dir'] }}/.config/aria2/aria2.conf"
force: true
group: "{{ hostvars[inventory_hostname].users[ansible_facts['user_id']].group | default(ansible_facts['user_id']) }}"
owner: "{{ ansible_facts['user_id'] }}"
src: aria2/aria2.conf.j2
# validate: string
- name: Create Aria2 SystemD user unit service
ansible.builtin.copy:
backup: true
dest: "{{ ansible_facts['user_dir'] }}/.config/systemd/user/aria2cd.service"
force: true
group: "{{ hostvars[inventory_hostname].users[ansible_facts['user_id']].group | default(ansible_facts['user_id']) }}"
owner: "{{ ansible_facts['user_id'] }}"
src: systemd/user/aria2cd.service.j2
# validate: string
- name: Start Aria2 SystemD user unit service
ansible.builtin.systemd_service:
daemon_reload: true
enabled: true
name: aria2cd
scope: user
state: started

View File

@@ -304,3 +304,11 @@
scope: global
state: present
value: "{{ hostvars[inventory_hostname].users[ansible_facts['user_id']].git_profile.email }}"
- name: Set default initial branch name
become: true
community.general.git_config:
add_mode: replace-all
name: init.defaultBranch
scope: system
state: present
value: main

View File

@@ -1,9 +1,20 @@
# SPDX-License-Identifier: MIT-0
---
# handlers file for bootstrap
# @NOTE below for packages
- name: Postinstall set-up of git
ansible.builtin.import_tasks:
file: git.yml
- name: Postinstall set-up of ProFTPd
ansible.builtin.import_tasks:
file: proftpd.yml
- name: Postinstall set-up of ProFTPd
ansible.builtin.import_tasks:
file: proftpd.yml
# @NOTE below for snaps
- name: Postinstall set-up of snapd
ansible.builtin.import_tasks:
file: snapd.yml
- name: Postinstall set-up of Nextcloud snap
ansible.builtin.import_tasks:
file: nextcloud.yml

View File

@@ -8,19 +8,27 @@
- name: Enable monitoring of network hardware
ansible.builtin.command:
cmd: "snap connect nextcloud:network-observe"
- name: Enable access to removable media
- name: Begin manual installation
ansible.builtin.command:
cmd: "snap connect nextcloud:removable-media"
argv:
- /snap/bin/nextcloud.manual-install
- "{{ config.nextcloud.users.admin.username }}"
- "{{ config.nextcloud.users.admin.password }}"
# @TODO see if setting below is necessary given use of reverse proxy
# - name: Set trusted domains
# ansible.builtin.command:
# cmd: "nextcloud.occ config:system:set trusted_domains 0 --value='cloud.{{ hostvars[inventory_hostname].fqdn }}'"
- name: Set trusted domains
block:
- name: Set FQDN as trusted domain
ansible.builtin.command:
cmd: "/snap/bin//snap/bin/nextcloud.occ config:system:set trusted_domains 1 --value='cloud.{{ hostvars[inventory_hostname].fqdn }}'"
# @TODO configure perhaps for trusted (reverse) proxy instead of above
- name: Set trusted reverse proxy IPv4 address
- name: Set trusted reverse proxy addresses
block:
- name: Set trusted reverse proxy IPv4 address based on hostname
# @TODO create config.trusted_revproxy_ips data structure in bootstrap role's vars dir--may include loopback addresses
when: config.trusted_revproxy_ips.ipv4 is None or len(config.trusted_revproxy_ips.ipv4) < 1
ansible.builtin.command:
argv:
- nextcloud.occ
- /snap/bin/nextcloud.occ
- "config:system:set"
- trusted_proxies 0
- "--value=$(hostname -I | awk -F ' ' '{ print $1 }')"
@@ -28,37 +36,70 @@
when: config.trusted_revproxy_ips.ipv4 is not None and len(config.trusted_revproxy_ips.ipv4) > 0
ansible.builtin.command:
argv:
- nextcloud.occ
- /snap/bin/nextcloud.occ
- "config:system:set"
- "trusted_proxies {{ idx }}"
- "--value={{ item }}"
loop: "{{ config.trusted_revproxy_ips.ipv4 }}"
loop_control:
index_var: idx
- name: Set trusted reverse proxy IPv6 address
- name: Set trusted reverse proxy IPv6 address based on hostname
when: config.trusted_revproxy_ips.ipv6 is None or len(config.trusted_revproxy_ips.ipv6) < 1
ansible.builtin.command:
argv:
- nextcloud.occ
- /snap/bin/nextcloud.occ
- "config:system:set"
- trusted_proxies 0
- "--value=$(hostname -I | awk -F ' ' '{ print $2 }')"
- trusted_proxies 1
- --value=$(hostname -I | awk -F ' ' '{ print $2 }')
- name: Set trusted reverse proxy IPv6 address
when: config.trusted_revproxy_ips.ipv6 is not None and len(config.trusted_revproxy_ips.ipv6) > 0
ansible.builtin.command:
argv:
- nextcloud.occ
- /snap/bin/nextcloud.occ
- "config:system:set"
- "trusted_proxies {{ idx }}"
- "--value={{ item }}"
loop: "{{ config.trusted_revproxy_ips.ipv6 }}"
loop_control:
index_var: idx
- name: Enable LDAP integration app
# @TODO create task based on shell command `sudo /snap/bin/nextcloud.occ config:system:set default_phone_region --value="US"`
- name: Set default phone region
ansible.builtin.command:
cmd: "nextcloud.occ app:enable user_ldap"
# @TODO see if can further configure LDAP in particular: https://docs.nextcloud.com/server/stable/admin_manual/occ_command.html#ldap-commands
- name: Enable contacts' birthday calendar app
argv:
- /snap/bin/nextcloud.occ
- "config:system:set"
- default_phone_region
- "--value={{ config.nextcloud.phone_region }}"
# @TODO create task based on shell command:
# `sudo /snap/bin/nextcloud.occ config:system:set overwrite.cli.url --value="https://cloud.{{ fqdn }}"` for Caddy task
- name: Set overwrite CLI URL
ansible.builtin.command:
cmd: "nextcloud.occ dav:sync-birthday-calendar"
# @TODO create system-level bash alias for `nextcloud.occ` command
argv:
- /snap/bin/nextcloud.occ
- "config:system:set"
- overwrite.cli.url
- "--value=cloud.{{ hostvars[inventory_hostname].fqdn }}"
# @TODO create task based on shell command `sudo /snap/bin/nextcloud.occ config:system:set overwriteprotocol --value="https"` for Caddy task
- name: Overwrite protocol
ansible.builtin.command:
argv:
- /snap/bin/nextcloud.occ
- "config:system:set"
- overwriteprotocol
- --value="https"
# @TODO create system-level bash alias for `/snap/bin/nextcloud.occ` command
- name: Get Nextcloud snap binaries
ansible.builtin.find:
paths:
- /snap/bin
patterns:
- nextcloud\..*
recurse: false
use_regex: true
register: nextcloud_snap_binaries
- name: Create symbolic links for Nextcloud snap binaries
ansible.builtin.file:
dest: "/usr/sbin/{{ item.path | basename }}"
src: "{{ item.path }}"
state: link
loop: "{{ nextcloud_snap_binaries.files }}"

View File

@@ -14,7 +14,7 @@
group: "{{ item[0]['group'] | default(item[0]['username']) }}"
path: "{{ item[0]['home'] | default('/home/' ~ item[0]['username']) }}/{{ item[1]['username'] }}"
state: directory
loop: "{{ hostvars[inventory_hostname]['users'].values() | product(config['proftpd']['vusers'].values()) }}"
loop: "{{ hostvars[inventory_hostname]['users'].values() | product(config['proftpd']['users'].values()) }}"
- name: Create ProFTPd FTP public directory for anonymous logins
when: "'ftps' in item.value['services']"
ansible.builtin.file:
@@ -65,7 +65,7 @@
owner: root
path: "{{ item.value }}"
state: touch
loop: "{{ lookup('ansible.builtin.dict', config['proftpd']['auth_filepaths']) }}"
loop: "{{ lookup('ansible.builtin.dict', config['proftpd']['auth_paths']) }}"
- name: Create the virtual users
when: "not 'caddy' in item.value['services'] and not 'httpd' in item.value['services'] and not 'www-data' in item.value['services'] and not 'http' in item.value['services'] and not 'https' in item.value['services']"
ansible.builtin.command:
@@ -73,14 +73,14 @@
- ftpasswd
- --passwd
- "--name={{ item.value['username'] }}"
- "--uid=$(id -u {{ item.value['id_of'] }})"
- "--gid=$(id -g {{ item.value['gid_of'] }})"
- "--uid=$(id -u {{ item.value['id'] }})"
- "--gid=$(id -g {{ item.value['gid'] }})"
- "--home={{ hostvars[inventory_hostname]['users']['ftp']['home'] | default('/srv/ftp') }}/{{ item.value['username'] }}"
- --shell=/sbin/nologin
- --file={{ config['proftpd']['auth_filepaths']['users_path'] }}
- --file={{ config['proftpd']['auth_paths']['users'] }}
- --stdin
stdin: "{{ item.value['password'] }}"
loop: "{{ lookup('ansible.builtin.dict', config['proftpd']['vusers']) }}"
loop: "{{ lookup('ansible.builtin.dict', config['proftpd']['users']) }}"
- name: Create the virtual groups of virtual users
when: "not 'caddy' in item.value['services'] and not 'httpd' in item.value['services'] and not 'www-data' in item.value['services'] and not 'http' in item.value['services'] and not 'https' in item.value['services']"
ansible.builtin.command:
@@ -88,10 +88,10 @@
- ftpasswd
- --group
- "--name={{ item.value['username'] }}"
- "--gid=$(id -g {{ item.value['gid_of'] }})"
- "--gid=$(id -g {{ item.value['gid'] }})"
- "--member={{ item.value['username'] }}"
- --file={{ config['proftpd']['auth_filepaths']['groups_path'] }}
loop: "{{ lookup('ansible.builtin.dict', config['proftpd']['vusers']) }}"
- --file={{ config['proftpd']['auth_paths']['groups'] }}
loop: "{{ lookup('ansible.builtin.dict', config['proftpd']['users']) }}"
# @TODO create tasks in block integrating LDAP users to ProFTPd
# - name: Integrate LDAP users into ProFTPd
- name: Create ProFTPd FTPS virtual host
@@ -108,6 +108,50 @@
validate: proftpd --configtest
vars:
ftp_server_name: "{{ config['proftpd']['name'].uppercase() }}'s Archive'"
allowed_users: "{{ ','.join(list(map(lambda u: u['username'], filter(lambda u: not 'http' in u['services'] and not 'https' in u['services'] and not 'httpd' in u['services'] and not 'caddy' in u['services'] and not 'www-data' in u['services'], config['proftpd']['vusers'].values())))) }}"
allowed_users: "{{ ','.join(list(map(lambda u: u['username'], filter(lambda u: not 'http' in u['services'] and not 'https' in u['services'] and not 'httpd' in u['services'] and not 'caddy' in u['services'] and not 'www-data' in u['services'], config['proftpd']['users'].values())))) }}"
anon_root: "{{ map(lambda u: u['home'], filter(lambda u: 'ftps' in u['services'] or 'proftpd' in u['services'], hostvars[inventory_hostname]['users'].values())) | list | random }}/public"
anon_user: "{{ config['proftpd']['vusers']['smuggler']['username'] }}"
anon_user: "{{ config['proftpd']['users']['smuggler']['username'] }}"
- name: Set ProFTPd jail in fail2ban
block:
- name: Create fail2ban system configuration directory
ansible.builtin.file:
force: false
group: root
mode: "0755"
owner: root
path: /etc/fail2ban
state: directory
- name: Create fail2ban filters system configuration directory
ansible.builtin.file:
force: false
group: root
mode: "0755"
owner: root
path: /etc/fail2ban/filter.d
state: directory
- name: Create fail2ban filter system configuration
ansible.builtin.copy:
backup: true
dest: /etc/fail2ban/filter.d/custom_proftpd.conf
force: true
group: root
owner: root
src: fail2ban/filter.d/custom_proftpd.conf
# validate: string
- name: Create fail2ban jails system configuration directory
ansible.builtin.file:
force: false
group: root
mode: "0755"
owner: root
path: /etc/fail2ban/jail.d
state: directory
- name: Create fail2ban jail system configuration
ansible.builtin.copy:
backup: true
dest: /etc/fail2ban/jail.d/proftpd.local
force: true
group: root
owner: root
src: fail2ban/jail.d/proftpd.local
# validate: string

View File

@@ -0,0 +1,24 @@
# SPDX-License-Identifier: MIT-0
---
# handlers file for bootstrap
- name: Configure RSyncD
listen: rsync
become: true
block:
# @TODO further construct the following commented task
# - name: Add directories to be published by RSyncD
- name: Create RSyncD configuration
ansible.builtin.template:
backup: true
dest: /etc/rsyncd.conf
force: true
group: root
owner: root
src: rsyncd.conf.j2
# validate: string
- name: Start and enable RSyncD SystemD system unit service
ansible.builtin.systemd_service:
enabled: true
name: rsync
scope: system
state: started

View File

@@ -1,12 +1,9 @@
# 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
author: Alex Tavarez
description: A role that aids in the deployment and bootstrapping of a new VPS.
company: SUKAATO
issue_tracker_url: https://git.sukaato.moe/admin/skato-ansible/issues
# Choose a valid license ID from https://spdx.org - some suggested licenses:
# - BSD-3-Clause (default)
@@ -16,20 +13,13 @@ galaxy_info:
# - Apache-2.0
# - CC-BY-4.0
license: license (GPL-2.0-or-later, MIT, etc)
min_ansible_version: "2.1"
galaxy_tags:
- sukaato
- vps
- server
- web
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.
dependencies:
- community.general
# - containers.podman

View File

@@ -0,0 +1,83 @@
# Global settings
continue=true
# check-integrity=true
daemon=true
human-readable=true
dir=~/downloads
file-allocation=falloc
log-level=warn
max-concurrent-downloads=3
max-overall-download-limit=0
# RPC settings
enable-rpc=true
rpc-allow-origin-all=true
rpc-max-request-size=10M
rpc-listen-all=true
rpc-listen-port=6800
rpc-secret={{ config['aria']['api_key'] }}
# rpc-certificate=
# rpc-private-key=
# rpc-secure=true
# HTTP/FTP/SFTP settings
connect-timeout=120
timeout=90
server-stat-of=~/.config/aria2/dl.log
server-stat-if=~/.config/aria2/dl.log
server-stat-timeout=86400
# checksum=sha-256={{ config.aria.checksum }}
max-connection-per-server=5
max-tries=10
max-file-not-found=7
min-split-size=100M
split=7
retry-wait=10
netrc-path=~/.netrc
reuse-uri=true
uri-selector=feedback
seed-ratio=1.5
seed-time=75
# HTTP settings
http-accept-gzip=true
http-auth-challenge=true
http-no-cache=true
enable-http-pipelining=true
enable-http-keep-alive=true
save-cookies=~/.config/aria2/cookie.txt
load-cookies=~/.config/aria2/cookie.txt
# user-agent=Mozilla/5.0
# FTP/SFTP settings
ftp-pasv=true
ftp-type=binary
ftp-reuse-connection=true
# ssh-host-key-md=sha-256=
# Bittorrent settings
listen-port=6881-6999
# bt-hash-check-seed=true
# bt-force-encryption=true
bt-save-metadata=true
bt-load-saved-metadata=true
bt-max-open-files=50
bt-max-peers=150
bt-stop-timeout=7200
bt-tracker=udp://93.158.213.92:1337/announce,udp://23.134.88.9:6969/announce,udp://23.134.88.9:1337/announce,udp://185.243.218.213:80/announce,udp://89.234.156.205:451/announce,udp://44.30.4.4:6969/announce,udp://23.175.184.30:23333/announce,udp://51.222.82.36:6969/announce,udp://211.75.205.189:80/announce,udp://77.91.85.95:6969/announce,udp://45.13.119.213:6969/announce,udp://43.154.112.29:17272/announce,udp://209.141.59.25:6969/announce,udp://5.255.124.190:6969/announce,udp://152.53.152.105:1984/announce,udp://109.201.134.183:80/announce,udp://111.90.151.241:6969/announce,udp://152.53.152.105:54123/announce,udp://189.69.171.209:6969/announce,udp://151.243.109.110:6969/announce
bt-tracker-connect-timeout=120
bt-tracker-timeout=333
bt-tracker-interval=0
enable-dht=true
dht-listen-port=6881-6999
dht-message-timeout=15
enable-peer-exchange=true
follow-torrent=mem
# Metalink settings
follow-metalink=mem
metalink-language=en,es,ja
metalink-location=jp,us,ch
# metalink-os=linux

View File

@@ -20,8 +20,8 @@
# AuthOrder mod_auth_pam.c mod_auth_unix.c*
AuthOrder mod_auth_file.c
AuthUserFile {{ config.proftpd.auth_filepaths.users_path }}
AuthGroupFile {{ config.proftpd.auth_filepaths.groups_path }}
AuthUserFile {{ config.proftpd.auth_paths.users }}
AuthGroupFile {{ config.proftpd.auth_paths.groups }}
AuthFileOptions SyntaxCheck
TLSEngine on

View File

@@ -0,0 +1,41 @@
port = 873
use chroot = true
max connections = 17
ignore nonreadable = true
pid file = /var/run/rsyncd.pid
[{{ ansible_facts['user_id'] }}-dl]
path = {{ ansible_facts['user_dir'] }}/downloads/public
uid = rika
gid = rika
timeout = 90
comment = Personal download inventory
read only = true
exclude = .nextcloudsync.log .calnotes .caltrash .stfolder .stignore .directory .ssh *.pub
[{{ ansible_facts['user_id'] }}-public]
path = {{ ansible_facts['user_dir'] }}/public/rsync
uid = rika
gid = rika
timeout = 90
comment = Public share point
read only = true
exclude = .nextcloudsync.log .calnotes .caltrash .stfolder .stignore .directory .ssh *.pub
[{{ ansible_facts['user_id'] }}-soulseek]
path = {{ ansible_facts['user_dir'] }}/public/soulseek
uid = rika
gid = rika
timeout = 90
comment = Personal SoulSeek inventory
read only = true
exclude = .nextcloudsync.log .calnotes .caltrash .stfolder .stignore .directory .ssh *.pub
[{{ ansible_facts['user_id'] }}-portfolio]
path = {{ ansible_facts['user_dir'] }}/portfolio
uid = rika
gid = rika
timeout = 90
comment = Personal portfolio
read only = true
exclude = .nextcloudsync.log .calnotes .caltrash .stfolder .stignore .directory .ssh *.pub

View File

@@ -0,0 +1,13 @@
[Unit]
Description=aria2 Daemon
After=network.target
[Service]
Type=forking
ExecStart=/usr/bin/aria2c --conf-path={{ ansible_facts['user_dir'] }}/.config/aria2/aria2.conf
ExecReload=/usr/bin/kill -HUP $MAINPID
RestartSec=1min
Restart=on-failure
[Install]
WantedBy=default.target

View File

@@ -5,6 +5,9 @@
software:
pkgs:
# @NOTE keep fields or keys constant; otherwise will have to edit handler notifiers and listeners elsewhere
failtwoban:
name:
apt: fail2ban
gocryptfs:
name:
apt: gocryptfs
@@ -80,18 +83,19 @@ software:
crystal-docs:
name:
apt: crystal-doc
javascript-lang:
name:
apt: nodejs
# @TODO replace below commented with an NVM-style installation (v22): https://nodejs.org/en/download
# javascript-lang:
# name:
# apt: nodejs
# javascript-pkg:
# name:
# apt: npm
# javascript-linter:
# name:
# apt: eslint
javascript-docs:
name:
apt: nodejs-doc
javascript-pkg:
name:
apt: npm
javascript-linter:
name:
apt: eslint
php-lang:
name:
apt: php
@@ -104,9 +108,9 @@ software:
php-pkg:
name:
apt: composer
php-ldap:
name:
apt: php-ldap
# php-ldap:
# name:
# apt: php-ldap
html-linter:
name:
apt: tidy
@@ -180,37 +184,38 @@ software:
# dpkg-dev:
# name:
# apt: dpkg-dev
ldap-utils:
name:
apt: ldap-utils
slapd:
name:
apt: slapd
# ldap-utils:
# name:
# apt: ldap-utils
# slapd:
# name:
# apt: slapd
proftpd-mod-crypto:
name:
apt: proftpd-mod-crypto
proftpd-mod-ldap:
# @TODO write configuration files and handler for below two package installations
# based on:
clamav:
name:
apt: proftpd-mod-ldap
# @TODO manually install the commented below on current active new VPS, then uncomment
# proftpd-mod-clamav:
# name:
# apt: proftpd-mod-clamav
apt: clamav
clamd:
name:
apt: clamav-daemon
proftpd:
name:
apt: proftpd
proftpd-docs:
name:
apt: proftpd-doc
# rsync:
# name:
# apt: rsync
rsync:
name:
apt: rsync
# rclone:
# name:
# apt: rsync
# aria:
# name:
# apt: aria2
# apt: rclone
aria:
name:
apt: aria2
# mopidy:
# name:
# apt: mopidy
@@ -228,27 +233,32 @@ software:
- "nextcloud:php.memory-limit=512M"
- "nextcloud:nextcloud.cron-interval=10m"
- "nextcloud:http.compression=true"
# @TODO commented opts use 'connect' snapd subcommand--look into how to use in Ansible
# - "nextcloud:removable-media"
# - "nextcloud:network-observe"
- "nextcloud:ports.http=81"
# @TODO see how to set these options: https://help.nextcloud.com/t/how-to-configure-nextcloud-snap/216036#p-649442-trusted-domains-configuration-8
# @TODO see how to set these options: https://help.nextcloud.com/t/how-to-configure-nextcloud-snap/216036#p-649442-trusted-proxy-configuration-9
links:
quartz:
name: quartz
src: https://github.com/jackyzha0/quartz.git
branch: v4
version: ~
output: ~
config:
git:
sys:
editor: nvim
proftpd:
name: "{{ hostvars[inventory_hostname].fqdn.split('.')[0] }}"
auth_filepaths:
users_path: /etc/proftpd/ftpd.passwd
groups_path: /etc/proftpd/ftpd.group
auth_paths:
users: /etc/proftpd/ftpd.passwd
groups: /etc/proftpd/ftpd.group
msg:
welcome: "Our head librarians Furcas and Marbas welcome you!"
vusers:
users:
webmaster:
username: webmaster
id_of: "{{ ['caddy', 'www-data'][0] }}"
gid_of: "{{ ['caddy', 'www-data'][0] }}"
id: "{{ ['caddy', 'www-data'][0] }}"
gid: "{{ ['caddy', 'www-data'][0] }}"
# @TODO create vaulted password for this ProFTPd virtual user
password: !vault |
$ANSIBLE_VAULT;1.2;AES256;vps1-webmaster
@@ -260,8 +270,8 @@ config:
services: [http,https]
smuggler:
username: smuggler
id_of: "{{ hostvars[inventory_hostname].users.ftp.username }}"
gid_of: "{{ hostvars[inventory_hostname].users.ftp.group | default(hostvars[inventory_hostname].users.ftp.username) }}"
id: "{{ hostvars[inventory_hostname].users.ftp.username }}"
gid: "{{ hostvars[inventory_hostname].users.ftp.group | default(hostvars[inventory_hostname].users.ftp.username) }}"
# @TODO create vaulted password for this ProFTPd virtual user
password: !vault |
$ANSIBLE_VAULT;1.2;AES256;vps1-smuggler
@@ -274,4 +284,14 @@ config:
tls_paths:
cert: "/usr/local/share/ca-certificates/{{ hostvars[inventory_hostname].fqdn }}.crt"
privkey: "/usr/local/share/ca-certificates/{{ hostvars[inventory_hostname].fqdn }}.key"
nextcloud:
users:
admin:
username: admin
# @TODO change this password to ansible-vaulted actual choice password later
password: password123 # @NOTE placeholder
phone_region: US
aria:
checksum: ~
api_key: ~

View File

@@ -3,6 +3,11 @@
#+language: en
* PLANNED
** DONE [#A] Write documentation on the expected conventional names to be used in the inventory file
** DONE [#A] Write documentation on the expected conventional paths to be used in the inventory file
** TODO [#A] Create Python Click library/package- based CLI
** TODO [#A] Soft-code relative paths for role files/templates in Ansible tasks/plays
** TODO [#A] Soft-code project root and paths to passwords/secrets files for Ansible tasks/plays
** TODO [#A] Rewrite dot notation usage of keys for accessing values in custom dictionary variables to bracket notation usage of keys across whole project
* IN PROGRESS