75 Commits
Author SHA1 Message Date
Alex Tavarez 3208cd0b09 added package for adapting to remote kitty-based terminal sessions 2026-07-16 00:12:30 -04:00
Alex Tavarez e5f178e583 added a minidlna package for staging1.test host 2026-07-14 16:49:28 -04:00
Alex Tavarez 1accba1566 added more documentation links 2026-07-14 16:00:29 -04:00
Alex Tavarez 101c1cc0aa added a link 2026-07-14 15:17:07 -04:00
Alex Tavarez a367b3c5e4 fixed typo in documentation 2026-07-14 15:13:39 -04:00
Alex Tavarez 673e318824 changed shell script examples to reflect added fully qualified domain name optional argument for subcommands running playbooks 2026-07-14 15:05:51 -04:00
Alex Tavarez 71261e4017 added option to script subcommand for specifying a fully qualified domain name 2026-07-14 15:04:45 -04:00
Alex Tavarez 9fb64cef30 changed script to reflect change in Python envirohnment path 2026-07-14 14:45:24 -04:00
Alex Tavarez 29d4f33a46 inserted 'fqdn' variable so use of inventory hostname as a fully qualified domain name for managed node or target host services could be overridden 2026-07-14 14:43:49 -04:00
Alex Tavarez 80fd0ebb09 edited documentation so installation section reflects changes in dependency management package use 2026-07-14 14:41:57 -04:00
Alex Tavarez a4d46ada94 changed pip freeze method of dependency management to uv based method for Python 2026-07-14 14:41:17 -04:00
Alex Tavarez e04ed6f273 excluding uv Python dependency manager from version control 2026-07-14 14:39:48 -04:00
Alex Tavarez d44c67df42 added passlib library as a requirement for auto-generation of passwords by Ansible 2026-07-14 14:18:58 -04:00
Alex Tavarez 7574b8f9b4 added installation instructions; clarified how FQDN assignment for managed nodes works and its relevant for a given use-case 2026-07-14 14:15:03 -04:00
Alex Tavarez aa590effb2 added missing step or assurance 2026-07-14 12:54:34 -04:00
Alex Tavarez 5d1e073470 added clarity to a callout/alert in the documentation 2026-07-14 09:30:38 -04:00
Alex Tavarez c7b1d2d30b added more shell script examples, this time showing how to use the shell script in this repo 2026-07-14 09:28:38 -04:00
Alex Tavarez ba7283b583 added SSH-related auth details as defaults when no HTTP auth available 2026-07-14 09:00:08 -04:00
Alex Tavarez 1060724cf8 added some documentation for configuring and using this Ansible project 2026-07-14 08:57:49 -04:00
Alex Tavarez aae7b50c8d wrapped package names in quotes as convention 2026-07-13 13:13:16 -04:00
Alex Tavarez 36b293f923 added a soulseek-cli installation 2026-07-13 13:03:57 -04:00
Alex Tavarez 7e1c8c3f46 enabled julia installation and added virtualization packages for staging hosts 2026-07-13 10:21:22 -04:00
Alex Tavarez c9ceb8e9cf added port forwarding to Vagrant VMs and changed their SSH defaults 2026-07-13 07:13:16 -04:00
Alex Tavarez 26eb7b38b9 changed networking options in Vagrantfiles 2026-07-12 16:56:35 -04:00
Alex Tavarez 35a2f98fc0 added some Vagrantfiles to the repository 2026-07-12 16:37:24 -04:00
Alex Tavarez e53d6255e0 created major large-scale changes 2026-07-12 15:27:58 -04:00
Alex Tavarez e8b29bb8e8 added systemd unit for setting iptables rules using added script, added DSNet systemd unit 2026-06-19 19:22:21 -04:00
Alex Tavarez aa8d61aa09 added attribute to reboot task to ignore errors; included variables file for configuring VPN, i.e. DSNet, service configuration 2026-06-19 19:21:09 -04:00
Alex Tavarez 2d6dcd3dab added attribute to reboot task to ignore errors; included variables file needed for VPN, i.e. DSNet, service configuration 2026-06-19 19:18:54 -04:00
Alex Tavarez 2c66c3bd43 removed tasks that were instead migrated to a role task included by a role handler 2026-06-19 19:17:24 -04:00
Alex Tavarez 20d1cd21a3 removed an empty line 2026-06-19 19:16:29 -04:00
Alex Tavarez 83fa171341 added task for grabbng software binary as software installation 2026-06-19 19:15:38 -04:00
Alex Tavarez 240efbb713 changed hardlink or actual destnation paths for software binaries, and used relative path for fetched secrets 2026-06-19 19:10:39 -04:00
Alex Tavarez bcf9eaebc9 removed unnecessary copy task for a source file in managed node 2026-06-19 19:06:55 -04:00
Alex Tavarez 125ec09c8f migrated DSNet tasks to own task file in role, as opposed to having it in relevant playbook 2026-06-19 19:04:54 -04:00
Alex Tavarez b784b781d3 added a DSNet handler including tasks for post-installation 2026-06-19 19:03:52 -04:00
Alex Tavarez 909682d74e moved burp and rsync installations, and added a new package group for direct software binaries, with dsnet listed in it 2026-06-19 19:02:29 -04:00
Alex Tavarez bd3029b914 made sure that handlers run prior to hostname change as well as a reboot in which the server is rendered inaccessible 2026-06-18 19:20:56 -04:00
Alex Tavarez 57a3e876b8 added task informing user of needed actions to take advantage of now-avaialble vim plugins 2026-06-18 19:19:23 -04:00
Alex Tavarez 1ce6879abf fixed typo in a line substitution, added SystemD restart tasks for service to abide by configuration changes 2026-06-18 19:18:26 -04:00
Alex Tavarez 9ea7fb37b0 fixed missing loop variable for headscale user registration task 2026-06-18 19:17:16 -04:00
Alex Tavarez 7584027890 moved VIM and Crowdsec setup handler listener, former due to now being userspace package group installation, latter due to needing to precede headscale handler tasks 2026-06-18 19:16:26 -04:00
Alex Tavarez a78613920c moved vim to userspace package group, referenced crowdsec handler, added password hashing/encryption 2026-06-18 19:14:14 -04:00
Alex Tavarez 0dd0633166 hard-coded dedicated SSH keys for staging to automatically populate ssh-agent, added a subcommand for listing SSH keys in use by SSH agent 2026-06-17 14:42:16 -04:00
Alex Tavarez 9945330b82 added task block to prompt user for a fallback password if given root password is null 2026-06-17 14:40:35 -04:00
Alex Tavarez f4399a2c8a added task block to prompt user for a fallback password if given user password is null 2026-06-17 14:40:17 -04:00
Alex Tavarez 596b828e6f changed SSH key queries to dedicated SSH keys, and automated password creation, for staging; removed token to be prompted instead 2026-06-17 14:39:10 -04:00
Alex Tavarez 13ef8fa459 made ungrouped hosts be two machines for staging or more 2026-06-17 14:36:26 -04:00
Alex Tavarez 52f98c1d57 removed some information from example files for users to fill on their own 2026-06-16 15:02:11 -04:00
Alex Tavarez c0d0203406 excluded some very specific vars files from version control due to potential sensitive information 2026-06-16 14:57:56 -04:00
Alex Tavarez 889b06bf21 altered template variables for vim editor configuration file 2026-06-16 14:57:11 -04:00
Alex Tavarez 5bfde90be9 altered template variables for flexget configuration file 2026-06-16 14:56:30 -04:00
Alex Tavarez f592a56c60 added/altered template variables for headscale configuration file 2026-06-16 14:55:56 -04:00
Alex Tavarez fc95904327 added a default handler that performs a default action for software installation if none needed, as looped installations nonetheless will attempt to notify one 2026-06-16 14:55:06 -04:00
Alex Tavarez f43e420f66 allowed for change of crowdsec ports from their defaults 2026-06-16 14:53:58 -04:00
Alex Tavarez a6fc067eb1 added configuration of template files as top-level variable files in vars directory, to be referenced in relevant playbooks 2026-06-16 14:53:01 -04:00
Alex Tavarez 44c343dd7b added decision tree for how to retrieve and make use of output produced on remote machine for future purposes 2026-06-16 14:51:17 -04:00
Alex Tavarez 8894bd8925 ensured backups and forced changes for confguration files; fixed issue with failing to look up path on remote machine for remote machine copying operations 2026-06-16 14:50:02 -04:00
Alex Tavarez 7daf57da64 moved post-installation reboots to task blocks from each software's associated handler to reduce power cycle; fixed semantic errors with varables and fixed repository addition issues 2026-06-16 14:47:31 -04:00
Alex Tavarez dc8dbcc43e added handlers inclusive of tasks related to headscale, git, vim and tor, as well as those very tasks 2026-06-16 14:40:56 -04:00
Alex Tavarez a4c26fd9c3 created example files for the basic server groupings 2026-06-16 14:21:50 -04:00
Alex Tavarez cf7612365a finished new playbook 2026-06-16 14:20:57 -04:00
Alex Tavarez 3343f7ad69 finished new playbook 2026-06-16 14:20:40 -04:00
Alex Tavarez 7268245bb7 finished new playbook 2026-06-16 14:20:16 -04:00
Alex Tavarez 555c7d0a6a replaced example file with original, native playbook file 2026-06-16 14:16:01 -04:00
Alex Tavarez 5049210e25 added community.general to requirements, just in case 2026-06-16 14:14:26 -04:00
Alex Tavarez 0d7b0f0c66 created a new host grouping naming sheme, including a hypothetical ungrouped host for staging purposes 2026-06-16 14:12:30 -04:00
Alex Tavarez 691d85458e excluded old group_vars files from version control 2026-06-16 14:07:14 -04:00
Alex Tavarez 8e1f5c6743 replacing with what is mainly a testing file 2026-06-16 14:06:25 -04:00
Alex Tavarez 1fed9cf441 replaced with what is mainly a testing file 2026-06-16 14:04:30 -04:00
Alex Tavarez f5fa460e44 renamed bootstrap shell script 2026-06-16 14:03:07 -04:00
Alex Tavarez 9d80fbb567 removed example file, for original file 2026-06-16 14:02:17 -04:00
Alex Tavarez 3fe4d9d5a2 added new dictionary variable, that has groups of container engine images; removed two packages from package groups to prepare for containerization instead 2026-06-10 22:10:23 -04:00
Alex Tavarez 17a921b551 included an ansible-galaxy collecton requirement for containers.podman 2026-06-10 22:09:09 -04:00
Alex Tavarez ac4af5de3d added task that creates two new networks for podman 2026-06-10 22:08:17 -04:00
108 changed files with 6382 additions and 925 deletions
+4 -3
View File
@@ -4,11 +4,10 @@
/hosts.yml
/hosts.yaml
/hosts.json
*.bak
/init@homeserver.yml
/administrate@homeserver.yml
uv.lock
# Ansible runtime and backups
*.bak
*.original
*.tmp
/.tmp/
@@ -21,12 +20,14 @@
/.devcontainer/
.lock
/.cache/
/roles/**/files/user/wg/containerized/*.conf
# Try tyo avoid any plain-text passwords
*pwd*
*pass*
*password*
*secret*
*.key
roles/**/vars/*
roles/**/vars/**
+229
View File
@@ -0,0 +1,229 @@
# SKATO ANSIBLE
## Installation
Make sure you have `git`, `venv`, and `uv` installed. On GNU/Linux Debian-based distros with pre-installed `apt` package manager, the corresponding packages are:
- `git`
- `python3-venv`
- [Follow this guide](https://docs.astral.sh/uv/getting-started/installation/) for installing `uv`
Ansible must also be installed on your system (see [this guide](https://docs.ansible.com/projects/ansible/latest/installation_guide/index.html)). Then, in your interactive bash shell session, run the following in order:
```bash
git clone https://git.sukaato.moe/admin/skato-ansible.git
cd ./skato-ansible
uv venv .venv
uv sync
source .venv/bin/activate
ansible-galaxy collections install -r ./collections/requirements.yml
```
## Essential Background
### Software Management
The standard use-case, which we call the container routing case, of this Ansible library is to run its playbooks in such a way that one host ends up acting as a router (via VPN service) to services on another host.
In the container routing case these services are defined by a repository declared in `${ANSIBLE_PROJECT_ROOT}/vars/source_code.yml`, via setting `source_code.repos.compose.name` and `source_code.repos.compose.rpath`, respectively, to the name of a version-controlled source repository and to a desired path within that repository (that path written such that it treats the repository path as root, i.e. as `/`).
As can then be noted, the `source_code` dictionary holds an attribute `repos` with itself attributes of fixed name representing various services or utilities, each of whom must have a `name` attribute defined that represents the name of the corresponding version-controlled source repository for that service or utility.
The point of this set-up is to allow the repository name at its endpoint to change without requiring manual changes in the code using or deploying that repository as consequence. One unfortunate result of this is that adding a service or utiltiy from one's git bare repositories means making manual corresponding changes in `pkgs` software object groups in relevant [host variable files](https://docs.ansible.com/projects/ansible/latest/inventory_guide/intro_inventory.html#organizing-host-and-group-variables), in `${ANSIBLE_PROJECT_ROOT}/roles/init-server/handlers/{core,userspace}.yml` for calling installation or post-installation actions, and in `${ANSIBLE_PROJECT_ROOT}/roles/init-server/tasks/contingent/pkg/${SOFTWARE_NAME}.yml` for executing those actions. The convention is for [handler](https://docs.ansible.com/projects/ansible/latest/inventory_guide/intro_inventory.html#host-variables) [notifiers](https://docs.ansible.com/projects/ansible/latest/playbook_guide/playbooks_handlers.html#notifying-handlers), [handler](https://docs.ansible.com/projects/ansible/latest/inventory_guide/intro_inventory.html#host-variables) [listeners](https://docs.ansible.com/projects/ansible/latest/playbook_guide/playbooks_handlers.html#notifying-handlers) and software names to all be equivalent, though the software name can deviate from this relational pattern.
Importantly, the following top-level attributes of the `source_code` variable or dictionary are necessary if your version-controlled source repository source requires authentication:
- `host`
- `user`/`ssh_user`
- `http_password`/`ssh_password`
If you wish to be able to use both SSH and HTTP authentication methods for the version-controlled source repository or change the default preference for HTTP credential use, you may edit `${ANSIBLE_PROJECT_ROOT}/roles/init-server/templates/user/netrc.j2`. See [the CURL docuemntation for more information on the NetRC file format](https://everything.curl.dev/usingcurl/netrc.html).
The point of the `source_code` dictionary is that it allows plugging in and executing custom code from one's own source repositories.
All other software is simply handled through the aforementioned patttern of adding an entry to any of the following in a given [host variables file](https://docs.ansible.com/projects/ansible/latest/inventory_guide/intro_inventory.html#organizing-host-and-group-variables):
- `pkgs.mngr.core` or `pkgs.mngr.userspace` (software installed by package manager--in this case, `apt` as most tasks assume a Linux Debian system at Ansible's managed node)
- `pkgs.script.core` or `pkgs.script.userspace` (since different installation shell scripts allow for different parameters, entries for installation shell scripts here are downloaded--for actual installation, the script has to be ran in a set of tasks specified in a YAML file under `${ANSIBLE_PROJECT_ROOT}/roles/init-server/tasks/contingent/pkg/`)
- `pkgs.archive.core` or `pkgs.archive.userspace` (similar as before, except things like path movement or specification, building/compilation, etc., are what take place via the set of tasks specified under the YAML file under `${ANSIBLE_PROJECT_ROOT}/roles/init-server/tasks/contingent/pkg/`)
- `pkgs.git_repos.core` or `pkgs.git_repos.userspace` (similar as before, fulfilling any arbitrary set of specified tasks for the pulled git source repository--you get the point)
Each entry item must have a `handler` attribute whose value will be used to [notify](https://docs.ansible.com/projects/ansible/latest/playbook_guide/playbooks_handlers.html#notifying-handlers) a [handler](https://docs.ansible.com/projects/ansible/latest/inventory_guide/intro_inventory.html#host-variables) [listener](https://docs.ansible.com/projects/ansible/latest/playbook_guide/playbooks_handlers.html#notifying-handlers), whose `listen` attribute has that same value, that then includes tasks found in a YAML file under `${ANSIBLE_PROJECT_ROOT}/roles/init-server/tasks/contingent/pkg/` whose basename, conventionally, is the same as those `handler` and `listen` attribute values. All of which, again, also conventionally share the same value as the `name` attribute of the entry item.
The structure of entries within each `pkgs.${SOFTWARE_GROUP}.core`/`pkgs.${SOFTWARE_GROUP}.userspace` depends on `$SOFTWARE_GROUP` because it changes the semantics of installation. All entries are dictionary items.
> [!NOTE]
> A table will be added at a later date, covering the attributes of the dictionary items for each `$SOFTWARE_GROUP`. For now, the [host variable files](https://docs.ansible.com/projects/ansible/latest/inventory_guide/intro_inventory.html#organizing-host-and-group-variables) for the staging hosts found in the [YAML inventory file](https://docs.ansible.com/projects/ansible/latest/inventory_guide/intro_inventory.html) can be used as reference.
### Configuration Management
#### System Configuration
The most important configuration is that needed for each host. This involves software installation, though [that is already covered in the above section](#software-management). Other important [host variables](https://docs.ansible.com/projects/ansible/latest/inventory_guide/intro_inventory.html#host-variables) for any given [host variable file](https://docs.ansible.com/projects/ansible/latest/inventory_guide/intro_inventory.html#organizing-host-and-group-variables) are:
variable name | type | default | semantics
--- | --- | --- | ---
`password` | `str` | auto-generated | the hashed password for the root user if the managed node had not spawned prior to playbook run
`ssh_keys` | `list` | `["ed25519@staging", "ecdsa@staging"]` | the basenames of the SSH key pairs for the root user
`admins` | `list` | `[{"username": "senpai", "services": None, "ssh_keys": ["ed25519@staging", "ecdsa@staging"], "password": ""} for d in admins]` | the list of Linux superusers, i.e. administrators, to exist
`token` | `str` | `None` | API key for cloud hosting account to spawn a VPS
`origin` | `str` | `us-east` | region or location of the VPS to determine timezones and locale; useful for Akamai Linode cloud hosting
`operating_system` | `str` | `None` | name or path to operating system for cloud hosted VPS or a VM / bare metal system
#### Software Configuration
Other configuration involves Ansible role templates or files, e.g. those found in `${ANSIBLE_PROJECT_ROOT}/roles/init-server/{templates,files}`. Generally, these Ansible role templates or files are sent over to the Ansible managed node by tasks provided by that same role, and referred to by tasks in the [playbooks](https://docs.ansible.com/projects/ansible/latest/playbook_guide/playbooks_intro.html).
The convention for templates is to set some desired though non- software native "defaults" and leave some settings or values to variables, grouped by the software for which they are relevant into variable files in `${ANSIBLE_PROJECT_ROOT}/vars/`, or what we may call the [playbook variable files](https://docs.ansible.com/projects/ansible/latest/playbook_guide/playbooks_variables.html#defining-variables-in-included-files-and-roles). These YAML variable files by convention have as their basename `${SOFTWARE_NAME}_settings` (with some exceptions), and must be listed (as paths relative to `$ANSIBLE_PROJECT_ROOT`) under `vars_files` on the [playbook](https://docs.ansible.com/projects/ansible/latest/playbook_guide/playbooks_intro.html) that will be running the tasks/[handlers](https://docs.ansible.com/projects/ansible/latest/inventory_guide/intro_inventory.html#host-variables), native to itself or taken from a role, that send the respective files or (after processing) templates over to the managed Ansible node(s). These kinds of files shall be called "settings files."
Inside each such settings file, the convention is for there to be a top-level variable (i.e., a single variable that takes a dictionary value) with a name of `$SOFTWARE_NAME`, whose value has attributes representing configuration options for the given software.
Some common, shared attributes for settings files are:
attribute name | type | default value | required? | semantics
--- | --- | --- | --- | ---
`containerized` | `bool` | `True` | only for services or software that is service-capable | determines whether the configuration is for a containerized or base system install
`mode` | `str` | "prod" | no | usefully allows for setting the mode for an individual piece of software rather than having it automatically agree with [the playbook mode](#essential-usage) or having it assume production mode
Other common ones, especially for services, are `port` (which can be an integer or string) and--for web servers--`scheme` (which can be either string "http" or "https", serving to toggle SSL/TLS DNS authentication for HTTP connections). These can exist at any nesting level under the top-level dictionary, based on the feature scope of the given software.
## Essential Usage
Before running any Ansible [playbooks](https://docs.ansible.com/projects/ansible/latest/playbook_guide/playbooks_intro.html) in either development or production mode, make sure to specify the username and the home directory path of the user on the system you are currently using in `{ANSIBLE_PROJECT_ROOT}/vars/local_facts.yml`.
### Development Mode
For development purposes in the container routing case, either first manually set up two Debian virtual machines, or (**recommended**) change into the `${ANSIBLE_PROJECT_ROOT}/staging0.test/` directory then `${ANSIBLE_PROJECT_ROOT}/staging1.test/` directory, running `vagrant up` for each. Running the [playbooks](https://docs.ansible.com/projects/ansible/latest/playbook_guide/playbooks_intro.html) in development mode also requires supplying "dev" as an argument for the extra `mode` parameter.
> [!WARNING]
> Vagrantfiles for neither staging host has yet been tested. Proceed by your own discretion.
If you intend to run the [playbooks](https://docs.ansible.com/projects/ansible/latest/playbook_guide/playbooks_intro.html) in production, the argument for this extra parameter should be "prod" (skip to the [Production Mode](#production-mode) section). The additional extra parameter of `chosen_host` should be adjusted to reflect the name of the host enlisted in the [YAML inventory file](https://docs.ansible.com/projects/ansible/latest/inventory_guide/intro_inventory.html) for production purposes.
> [!IMPORTANT]
> The names of hosts should be equivalent to their intended final domain name for services on any given host for the [playbook](https://docs.ansible.com/projects/ansible/latest/playbook_guide/playbooks_intro.html) to work correctly.
Anyway, for *any* mode double-check that the correct values are set for `chosen_user`. The first playbook you run for each host or IP in each pair of `ansible-playbook` commands in the upcoming shell script should always have `chosen_user` as "root". The extra parameter `chosen_user` for the second `ansible-playbook` command of each pair of `ansible-playbook` commands shown below should take an argument whose value is the same as that of the `username` attribute of any of the items in the `admins` list in the [host variables file](https://docs.ansible.com/projects/ansible/latest/inventory_guide/intro_inventory.html#organizing-host-and-group-variables) for the given host. With that in mind, execute the below for development mode for container routing case while in the `$ANSIBLE_PROJECT_ROOT` directory:
```bash
#!/bin/bash
set -euo pipefail
SSH_KEYS=()
sudo ansible-playbook -Kk -i hosts.yml --extra-vars "chosen_host=staging0.test" --extra-vars "chosen_user=root" --extra-vars "mode=dev" init\@vps.yml
$SSH_KEYS | xargs -I %k ssh-add %k # ADD NEEDED SSH KEY FILEPATHS
ansible-playbook -Kk -i hosts.yml --extra-vars "chosen_host=staging0.test" --extra-vars "chosen_user=senpai" --extra-vars "mode=dev" administrate\@vps.yml
sudo ansible-playbook -Kk -i hosts.yml --extra-vars "chosen_host=staging1.test" --extra-vars "chosen_user=root" --extra-vars "mode=dev" init\@homeserver.yml
# UNCOMMENT BELOW TO REASSIGN ENVIRONMENT VARIABLE FOR NEW ARRAY OF SSH KEYPAIR FILEPATHS
# SSH_KEYS=()
# UNCOMMENT BELOW TO ADD UPDATED SSH KEY PAIR FILEPATHS TO SSH_AGENT
# $SSH_KEYS | xargs -I %k ssh-add %k
ansible-playbook -Kk -i hosts.yml --extra-vars "chosen_host=staging1.test" --extra-vars "chosen_user=senpai" --extra-vars "mode=dev" administrate\@homeserver.yml
```
> [!WARNING]
> In the container routing case, development mode may not supply an accurate test of the intended networking result if the virtual machines for staging were manually created, but the [playbook](https://docs.ansible.com/projects/ansible/latest/playbook_guide/playbooks_intro.html) should run successfully. This is because apppropriate networking must be set for the virtual machine itself for VPN client containers in one virtual machine to "speak" with the VPN server in another virtual machine. The supplied Vagrantfiles are a WIP.
### Production Mode
Its important to note that the above shell script example for development mode would have to be modified in production mode for the container routing case such that every `ansible-playbook` call instance has its extra parameter `chosen_host` share the same value throughout. This is due to the combination of how SSL/TLS and domain names are set up in the [playbook](https://docs.ansible.com/projects/ansible/latest/playbook_guide/playbooks_intro.html) and the requirements of the container routing case. Namely, the former treats the inventory hostname used for the [playbook](https://docs.ansible.com/projects/ansible/latest/playbook_guide/playbooks_intro.html) as the [fully qualified domain name](https://en.wikipedia.org/wiki/Fully_qualified_domain_name) of that server host, while the container routing case requires that both server hosts share that [fully qualified domain name](https://en.wikipedia.org/wiki/Fully_qualified_domain_name).
This means that `chosen_host`'s value being the same across all `ansible-playbook` commands does not mean that value should represent the same managed node or host [IP](https://en.wikipedia.org/wiki/IP_address) throughout. Whether it does so is of course determined by whatever is in the [YAML inventory file](https://docs.ansible.com/projects/ansible/latest/inventory_guide/intro_inventory.html), so that would need to be changed when running [playbooks](https://docs.ansible.com/projects/ansible/latest/playbook_guide/playbooks_intro.html) against the second chosen host in the container routing case.
To drive all that home, in production we may instead need something like the following (again, executed while in the `$ANSIBLE_PROJECT_ROOT` directory):
```bash
#!/bin/bash
set -euo pipefail
SSH_KEYS=()
HOST_FQDN=web.site # a fully qualified domain name you own
CURRENT_IP= # whatever the current IP of the host $HOST_FQDN is
UPDATED_IP= # the desired new IP for the host $HOST_FQDN
sudo ansible-playbook -Kk -i hosts.yml --extra-vars "chosen_host=${HOST_FQDN}" --extra-vars "chosen_user=root" --extra-vars "mode=prod" init\@vps.yml
$SSH_KEYS | xargs -I %k ssh-add %k # ADD NEEDED SSH KEY PAIR FILEPATHS TO SSH_AGENT
ansible-playbook -Kk -i hosts.yml --extra-vars "chosen_host=${HOST_FQDN}" --extra-vars "chosen_user=senpai" --extra-vars "mode=prod" administrate\@vps.yml
sed '/'"$HOST_FQDN"':{N;s/'"$HOST_FQDN"':\n {6}ansible_host: '"$CURRENT_IP"'/'"$HOST_FQDN"':\n ansible_host: '"$NEW_IP"'/g}' hosts.yml
sudo ansible-playbook -Kk -i hosts.yml --extra-vars "chosen_host=${HOST_FQDN}" --extra-vars "chosen_user=root" --extra-vars "mode=prod" init\@homeserver.yml
# UNCOMMENT BELOW TO REASSIGN ENVIRONMENT VARIABLE FOR NEW ARRAY OF SSH KEYPAIR FILEPATHS
# SSH_KEYS=()
# UNCOMMENT BELOW TO ADD UPDATED SSH KEY PAIR FILEPATHS TO SSH_AGENT
# $SSH_KEYS | xargs -I %k ssh-add %k
ansible-playbook -Kk -i hosts.yml --extra-vars "chosen_host=${HOST_FQDN}" --extra-vars "chosen_user=senpai" --extra-vars "mode=prod" administrate\@homeserver.yml
```
**There *is* a way to avoid reassigning the IP address of the shared inventory hostname for each host the pair of playbooks is being run against in the container routing case under production.** This involves defining `fqdn` as a playbook variable or as an extra parameter for all `ansible-playbook` commands in the shell script, resulting in a shell script that looks more similar to our [development mode shell script](#development-mode):
```bash
#!/bin/bash
set -euo pipefail
SSH_KEYS=()
sudo ansible-playbook -Kk -i hosts.yml --extra-vars "chosen_host=staging0.test" --extra-vars "chosen_user=root" --extra-vars "mode=dev" --extra-vars "fqdn=web.site" init\@vps.yml
$SSH_KEYS | xargs -I %k ssh-add %k # ADD NEEDED SSH KEY FILEPATHS
ansible-playbook -Kk -i hosts.yml --extra-vars "chosen_host=staging0.test" --extra-vars "chosen_user=senpai" --extra-vars "mode=dev" --extra-vars "fqdn=web.site" administrate\@vps.yml
sudo ansible-playbook -Kk -i hosts.yml --extra-vars "chosen_host=staging1.test" --extra-vars "chosen_user=root" --extra-vars "mode=dev" --extra-vars "fqdn=web.site" init\@homeserver.yml
# UNCOMMENT BELOW TO REASSIGN ENVIRONMENT VARIABLE FOR NEW ARRAY OF SSH KEYPAIR FILEPATHS
# SSH_KEYS=()
# UNCOMMENT BELOW TO ADD UPDATED SSH KEY PAIR FILEPATHS TO SSH_AGENT
# $SSH_KEYS | xargs -I %k ssh-add %k
ansible-playbook -Kk -i hosts.yml --extra-vars "chosen_host=staging1.test" --extra-vars "chosen_user=senpai" --extra-vars "mode=dev" --extra-vars "fqdn=web.site" administrate\@homeserver.yml
```
> [!TIP]
> Adding the extra parameter here to the `ansible-playbook` commands of the development mode execution for the container routing use-case may also improve the realism of its execution and final state, virtual machine network configurations notwithstanding. Consider doing so for more realistic networking tests under the container routing case.
### Custom Playbooks
As long as you are matching the appropriate set of [playbook variable files](https://docs.ansible.com/projects/ansible/latest/playbook_guide/playbooks_variables.html#defining-variables-in-included-files-and-roles) or settings files with the [playbook](https://docs.ansible.com/projects/ansible/latest/playbook_guide/playbooks_intro.html) according to its need of those variable definitions/declarations, mostly determined by [the intended targeted hosts' own variables](https://docs.ansible.com/projects/ansible/latest/inventory_guide/intro_inventory.html#host-variables) together with role [handlers](https://docs.ansible.com/projects/ansible/latest/inventory_guide/intro_inventory.html#host-variables), and as long as you have appropriately set up software installations, (see [Software Management](#software-management)) creating new/custom [playbooks](https://docs.ansible.com/projects/ansible/latest/playbook_guide/playbooks_intro.html) should be relatively easy.
Depending on what you are trying to do with a new [playbook](https://docs.ansible.com/projects/ansible/latest/playbook_guide/playbooks_intro.html), the best approach may be to just copy/duplicate the extant [playbook files](https://docs.ansible.com/projects/ansible/latest/playbook_guide/playbooks_intro.html) and then edit them, as this allows one to mitigate errors as well as have a reliable reference point for changes.
### CLI Tool
As an alternative, you can use the `skato-ansible` shell script in `$ANSIBLE_PROJECT_ROOT` as an abstraction of the `ansible-playbook` commands for the container routing case. I plan to replace it with a CLI app covering more intended use-cases.
When using the `skato-ansible.sh` script in development mode for the container routing use case:
```bash
#!/bin/bash
set -euo pipefail
./skato-ansible.sh init vps -v 3 -m dev -n staging0.test
./skato-ansible.sh populate-agent staging
./skato-ansible.sh bootstrap vps -s 1 -v 3 -m dev -n staging0.test
./skato-ansible.sh init homeserver -v 3 -m dev -n staging1.test
# BELOW ONLY IF $SKANSIBLE_SSH_KEY ALTERED IN THE SHELL SCRIPT
# ./skato-ansible.sh populate-agent staging
./skato-ansible.sh bootstrap vps -s 1 -v 3 -m dev -n staging1.test
```
Otherwise, in production mode:
```bash
#!/bin/bash
set -euo pipefail
INVENTORY_HOSTNAMES=(web1 web2)
FQDN=web.site
./skato-ansible.sh init vps -v 3 -n "${INVENTORY_HOSTNAMES[0]}" -d "$FQDN"
./skato-ansible.sh populate-agent staging
./skato-ansible.sh bootstrap vps -s 1 -v 3 -n "${INVENTORY_HOSTNAMES[0]}" -d "$FQDN"
./skato-ansible.sh init homeserver -v 3 -n "${INVENTORY_HOSTNAMES[1]}" -d "$FQDN"
# BELOW ONLY IF $SKANSIBLE_SSH_KEY ALTERED IN THE SHELL SCRIPT
# ./skato-ansible.sh populate-agent staging
./skato-ansible.sh bootstrap vps -s 1 -v 3 -n "${INVENTORY_HOSTNAMES[1]}" -d "$FQDN"
```
> [!IMPORTANT]
> If you have different SSH keypairs for staging, make sure to change the value of `SKANSIBLE_SSH_KEYS` environment variable in the `${ANSIBLE_PROJECT_ROOT}/skato-ansible.sh` shell script before running the above shell scripts from `$ANSIBLE_PROJECT_ROOT`.
+214
View File
@@ -0,0 +1,214 @@
- name: Initialize homeserver
hosts: "{{ chosen_host | default('staging1.test') }}"
remote_user: "{{ chosen_user | default('senpai') }}"
vars_files:
- vars/email_settings.yml
- vars/podpose_settings.yml # REQUIRED
- vars/source_code.yml # REQUIRED
- vars/caddy_settings.yml
- vars/certbot_settings@homeserver.yml # REQUIRED
- vars/mysql_settings.yml
- vars/redis_settings.yml
- vars/nextcloud_settings.yml
- vars/gitea_settings.yml
- vars/opengist_settings.yml
- vars/vpn_settings.yml
- vars/headscale_settings.yml
- vars/tailscale_settings.yml
- vars/glance_settings.yml
- vars/surge_settings.yml
- vars/aria_settings.yml
- vars/flexget_settings.yml # REQUIRED
- vars/git_aliases.yml # REQUIRED
- vars/vim_settings.yml # REQUIRED
- vars/tor_settings.yml # REQUIRED
- vars/config@{{ inventory_hostname | default('homeserver') }}.yml
tasks:
- name: Saving HTTP/(S)FTP credentials
ansible.builtin.include_role:
name: init-server
tasks_from: netrc.yml
handlers_from: userspace
- name: Installing requisite packages
ansible.builtin.include_role:
name: init-server
tasks_from: userspace@install-pkgs
handlers_from: userspace
- name: Acquiring home of current user
when: ansible_facts["system"] == "Linux"
ansible.builtin.shell:
cmd: "echo ~{{ ansible_user }}"
register: ansible_user_home
- name: Acquiring current user
when: ansible_facts["system"] == "Linux"
ansible.builtin.shell:
cmd: "echo {{ ansible_user }}"
register: current_user
- name: Disabling root user shell login
become: true
ansible.builtin.user:
name: root
shell: /sbin/nologin
tags:
- disable_root_shell
- name: Disable login for root user altogether
become: true
ansible.builtin.user:
name: root
password: "'*'"
tags:
- disable_root_login
- name: Reorganizing userspace package groups into single list
ansible.builtin.set_fact:
all_userspace_pkgs: "{{ pkgs | dict2items(key_name='pkg_group', value_name='pkgs') | map(attribute='pkgs') | list | map(attribute='userspace', default='no_userspace') | list | flatten | reject('search', 'no_userspace') | list }}"
- name: Reorganizing core package groups into single list
ansible.builtin.set_fact:
all_core_pkgs: "{{ pkgs | dict2items(key_name='pkg_group', value_name='pkgs') | map(attribute='pkgs') | list | map(attribute='core', default='no_core') | list | flatten | reject('search', 'no_core') | list }}"
- name: Configuring aliases for using git
when: "'git' in all_core_pkgs or 'git' in all_userspace_pkgs"
community.general.git_config:
name: "alias.{{ item[0] }}"
scope: global
value: "{{ item[1] }}"
loop: "{{ git_aliases }}"
- name: Checking presence of dependency for flexget
when: "'uv' in all_core_pkgs and flexget.enabled"
block:
- name: Installing flexget
become: true
become_user: "{{ current_user.stdout }}"
ansible.builtin.command:
argv:
- uv
- tool
- install
- "flexget[locked,ftp,sftp]"
- "--with"
- pysocks
- name: Creating requisite download paths
ansible.builtin.file:
path: "{{ ansible_user_home.stdout }}/downloads/flexget/vids/{{ item }}/torrents"
recurse: true
owner: "{{ ansible_user }}"
group: "{{ ansible_user }}"
state: directory
mode: "755"
loop:
- series
- name: Creating requisite download paths
ansible.builtin.file:
path: "{{ ansible_user_home.stdout }}/downloads/flexget/vids/torrents"
recurse: true
owner: "{{ ansible_user }}"
group: "{{ ansible_user }}"
state: directory
mode: "755"
- name: Creating requisite hidden download paths
ansible.builtin.file:
path: "{{ ansible_user_home.stdout }}/downloads/.xxx/flexget/vids/torrents"
recurse: true
owner: "{{ ansible_user }}"
group: "{{ ansible_user }}"
state: directory
mode: "755"
- name: Creating requisite hidden download paths
ansible.builtin.file:
path: "{{ ansible_user_home.stdout }}/downloads/media/vids/{{ item }}"
recurse: true
owner: "{{ ansible_user }}"
group: "{{ ansible_user }}"
state: directory
mode: "755"
loop:
- series
- features
- name: Creating requisite hidden file for film torrent links
ansible.builtin.file:
path: "{{ ansible_user_home.stdout }}/media/vids/{{ item }}.csv"
owner: "{{ ansible_user }}"
group: "{{ ansible_user }}"
state: touch
mode: "644"
loop:
- features/.films
- name: Creating requisite hidden download paths
ansible.builtin.file:
path: "{{ ansible_user_home.stdout }}/downloads/.xxx/media/vids"
recurse: true
owner: "{{ ansible_user }}"
group: "{{ ansible_user }}"
state: directory
mode: "755"
- name: Creating requisite hidden file for NSFW video torrent links
ansible.builtin.file:
path: "{{ ansible_user_home.stdout }}/.xxx/.{{ item }}.csv"
owner: "{{ ansible_user }}"
group: "{{ ansible_user }}"
state: touch
mode: "644"
loop:
- vids
- name: Configuring flexget
become: true
become_user: "{{ current_user.stdout }}"
ansible.builtin.template:
src: user/flexget/config.yml.j2
variable_start_string: "<<"
variable_end_string: ">>"
dest: "{{ ansible_user_home.stdout }}/.flexget/config.yml"
owner: "{{ ansible_user }}"
group: "{{ ansible_user }}"
force: true
backup: true
# validate: "flexget check"
- name: Creating a CRON job for flexget
ansible.builtin.cron:
name: Run flexget
minute: "*/60"
hour: "4-15"
weekday: "1-5"
job: "{{ ansible_user_home.stdout }}/.local/bin/flexget --cron execute"
- name: Checking presence of dependency
when: "'nvm' in all_core_pkgs or 'nvm' in all_userspace_pkgs"
block:
- name: Installing SoulSeek CLI
community.general.npm:
name: "soulseek-cli"
global: true
state: present
- name: Opening port 51820
become: true
ansible.builtin.iptables:
chain: INPUT
protocol: "{{ item }}"
destination_port: 51820
jump: ACCEPT
comment: Open up port 51820
loop:
- udp
- tcp
- name: Opening port 443
become: true
ansible.builtin.iptables:
chain: INPUT
protocol: "{{ item }}"
destination_port: 443
jump: ACCEPT
comment: Open up port 443
loop:
- udp
- tcp
- name: Opening ports
become: true
ansible.builtin.iptables:
chain: INPUT
protocol: tcp
destination_port: "{{ item }}"
jump: ACCEPT
comment: "Open up port {{ (item | string) }}"
loop:
- 80
- 465
- 587
- 995
- 993
-29
View File
@@ -1,29 +0,0 @@
- name: Initialize homeserver
hosts: armitage
remote_user: senpai
tasks:
- name: Installing requisite packages
ansible.builtin.include_role:
name: init-server
tasks_from: userspace@install-pkgs
handlers_from: userspace
- name: Disable root user shell login
become: true
ansible.builtin.user:
name: root
shell: /sbin/nologin
tags:
- disable_root_shell
- name: Disable login for root user altogether
become: true
ansible.builtin.user:
name: root
password: "'*'"
tags:
- disable_root_login
- name: Configuring aliases for using git
community.general.git_config:
name: "alias.{{ item[0] }}"
scope: global
value: "{{ item[1] }}"
loop: []
+84
View File
@@ -0,0 +1,84 @@
- name: Initialize VPS
hosts: "{{ chosen_host | default('staging0.test') }}"
remote_user: "{{ chosen_user | default('senpai') }}"
vars_files:
- vars/surge_settings.yml
- vars/git_aliases.yml # REQUIRED
- vars/vim_settings.yml # REQUIRED
- vars/podpose_settings.yml # REQUIRED
- vars/certbot_settings.yml # REQUIRED
- vars/config@{{ inventory_hostname | default('vps') }}.yml
tasks:
- name: Installing requisite packages
ansible.builtin.include_role:
name: init-server
tasks_from: userspace@install-pkgs
handlers_from: userspace
- name: Disabling root user shell login
become: true
ansible.builtin.user:
name: root
shell: /sbin/nologin
tags:
- disable_root_shell
- name: Disable login for root user altogether
become: true
ansible.builtin.user:
name: root
password: "'*'"
tags:
- disable_root_login
# - name: Debugging
# ansible.builtin.debug:
# msg: "{{ pkgs | dict2items(key_name='pkg_group', value_name='pkgs') | map(attribute='pkgs') | list | map(attribute='userspace', default='no_userspace') | list | flatten | reject('search', 'no_userspace') | list }}"
# - name: Prematurely ending play
# ansible.builtin.meta: end_play
- name: Reorganizing userspace package groups into single list
ansible.builtin.set_fact:
all_userspace_pkgs: "{{ pkgs | dict2items(key_name='pkg_group', value_name='pkgs') | map(attribute='pkgs') | list | map(attribute='userspace', default='no_userspace') | list | flatten | reject('search', 'no_userspace') | list }}"
- name: Reorganizing core package groups into single list
ansible.builtin.set_fact:
all_core_pkgs: "{{ pkgs | dict2items(key_name='pkg_group', value_name='pkgs') | map(attribute='pkgs') | list | map(attribute='core', default='no_core') | list | flatten | reject('search', 'no_core') | list }}"
- name: Configuring aliases for using git
when: "'git' in all_core_pkgs"
community.general.git_config:
name: "alias.{{ item[0] }}"
scope: global
value: "{{ item[1] }}"
loop: "{{ git_aliases }}"
- name: Opening port 51820
become: true
ansible.builtin.iptables:
chain: INPUT
protocol: "{{ item }}"
destination_port: 51820
jump: ACCEPT
comment: Open up port 51820
loop:
- udp
- tcp
- name: Opening port 443
become: true
ansible.builtin.iptables:
chain: INPUT
protocol: "{{ item }}"
destination_port: 443
jump: ACCEPT
comment: Open up port 443
loop:
- udp
- tcp
- name: Opening ports
become: true
ansible.builtin.iptables:
chain: INPUT
protocol: tcp
destination_port: "{{ item }}"
jump: ACCEPT
comment: "Open up port {{ (item | string) }}"
loop:
- 80
- 465
- 587
- 995
- 993
-5
View File
@@ -1,5 +0,0 @@
#!/bin/bash
set -euo pipefail
sudo ansible-playbook --ask-pass --ask-become-pass -i hosts.yml init@homeserver.yml
ansible-playbook --ask-become-pass -i hosts.test.yml administrate@homeserver.yml
+4
View File
@@ -1,4 +1,8 @@
---
collections:
- name: community.general
version: "12.6.1"
- name: linode.cloud
version: "0.46.0"
- name: containers.podman
version: "1.20.1"
-104
View File
@@ -1,104 +0,0 @@
# <str<vault>> representing password for Linux root user account of LAN server on PC
password: ""
# <str<vault>> administrative API token or PXE server authentication key/password
token: ~
# <str> representing hostname for LAN server; same as host or group variable name
instance: ""
# <str<enum>> representing Linux distro or OS image to be used for VPS; can be PXE server URI/URL link
# Example-- operating_system: "tftp://hikiki.local:69/debian.iso"
operating_system: ~
# <list[<str>]> of control node or local SSH key basenames
ssh_keys: []
# @TODO change 'key' attributes of package entres under 'mngr' section below to 'signkey'
# and edit 'roles/init-server/install-pks.yml' accordngly
# <dict[<str>:<dict>]> package groups
pkgs:
# <dict[<str>:<dict>]> representing package groups installed by package manager via repositories
mngr:
# <list[<dict>]> representing system-level or essential packages
core:
- name: ""
uri: ""
sigkey: ""
sources: ""
types: ""
suites: ""
comps: ""
# <list[<dict>]> representing user-level or supplemental packages
userspace:
- name: ""
uri: ""
sigkey: ""
sources: ""
types: ""
suites: ""
comps: ""
# <dict[<str>:<dict>]> representing package groups installed by shell scripts
script:
# <list[<dict>]> representing system-level or essential shell script software installations
core:
- name: "" # <str> arbitrary name, used by handler listener
src: "" # <str> URI/URL or path to software installation shell script
pre: "" # <str> URI/URL or path to shell script, or name of handler listener, for pre-installation actions
post: "" # <str> URI/URL or path to shell script, or name of handler listener, for post-installation actions
# <list[<dict>]> representing user-level or supplemental shell script software installations
userspace:
- name: ""
src: ""
pre: ""
post: ""
# <dict[<str>:<dict>]> representing package groups installed from source archives
archive:
# <list[<dict>]> representing system-level or essential source archives
core:
- name: "" # <str> arbitrary name, used by handler listener
src: "" # <str> URI/URL or path of archive file for software build
deploy: "" # <str> URI/URL or path to shell script, or handler listener name, to build software from archive
pre: "" # <str> URI/URL or path to shell script, or handler listener name, for actions to take before software build
post: "" # <str> URI/URL or path to shell script, or handler listener name, for actions to take after software build
# <list[<dict>]> representing user-level or supplemental source archives
userspace:
- name: ""
src: ""
deploy: ""
pre: ""
post: ""
# <dict[<str>:<dict>]> representing package groups installed from source git repositories
git_repos:
# <list[<dict>]> representing system-level or essential git repositories
core:
- name: "" # <str> arbitrary name, used by handler listener
src: "" # <str> URI/URL or path of git repository
src_path: "" # <str> path in which to place git repository clone
branch: "" # <str> specific branch to pull or otherwise to swtich into
deploy: "" # <str> URI/URL or path to shell script, or handler listener name, to build or run from source repository
pre: "" # <str> URI/URL or path to shell script, or handler listener name, for actions to take before building or running from source repository
post: "" # <str> URI/URL or path to shell script, or handler listener name, for actions to take after building or running from source repository
# <list[<dict>]> representing user-level or supplemental git repositories
userspace:
- name: "" # <str> arbitrary name, used by handler listener
src: "" # <str> URI/URL or path of git repository
src_path: "" # <str> path in which to place git repository clone
remote: "" # <str> the name of the remote source of the git repository
branch: "" # <str> specific branch to pull or otherwise to swtich into
deploy: "" # <str> URI/URL or path to shell script, or handler listener name, to build or run from source repository
pre: "" # <str> URI/URL or path to shell script, or handler listener name, for actions to take before building or running from source repository
post: "" # <str> URI/URL or path to shell script, or handler listener name, for actions to take after building or running from source repository
# <dict[<str>:<dict>]> representing package groups installed via container engine
containers:
# <list[<dict>]> representing system-level or essential container images
core: []
# <list[<dict>]> representing user-level or supplemental container images
userspace: []
# <dict[<str>:<dict>]> representing package groups installed via snap package
snaps:
# <list[<dict>]> representing system-level or essential packages
core: []
# <list[<dict>]> representing user-level or supplemental packages
userspace: []
# <dict[<str>:<dict>]> representing flatpak groups installed via flatpak package
flatpaks:
# <list[<dict>]> representing system-level or essential flatpaks
core: []
# <list[<dict>]> representing user-level or supplemental flatpaks
userspace: []
-105
View File
@@ -1,105 +0,0 @@
# <str<vault>> representing password for Linux root user account of VPS
password: ""
# <str<vault>> representing API token for VPS cloud service
token: ""
# <str> representing name and hostname of VPS to be made in VPS cloud service
instance: ""
# <str<enum>> representing region options from or for given VPS cloud service
origin: ""
# <str<enum>> representing Linux distro or OS image available in VPS service to be used for VPS
operating_system: ~
# <list[<str>]> of control node or local SSH key basenames
ssh_keys: []
# @TODO change 'key' attributes of package entres under 'mngr' section below to 'signkey'
# and edit 'roles/init-server/install-pks.yml' accordngly
# <dict[<str>:<dict>]> package groups
pkgs:
# <dict[<str>:<dict>]> representing package groups installed by package manager via repositories
mngr:
# <list[<dict>]> representing system-level or essential packages
core:
- name: ""
uri: ""
sigkey: ""
sources: ""
types: ""
suites: ""
comps: ""
# <list[<dict>]> representing user-level or supplemental packages
userspace:
- name: ""
uri: ""
sigkey: ""
sources: ""
types: ""
suites: ""
comps: ""
# <dict[<str>:<dict>]> representing package groups installed by shell scripts
script:
# <list[<dict>]> representing system-level or essential shell script software installations
core:
- name: "" # <str> arbitrary name, used by handler listener
src: "" # <str> URI/URL or path to software installation shell script
pre: "" # <str> URI/URL or path to shell script, or name of handler listener, for pre-installation actions
post: "" # <str> URI/URL or path to shell script, or name of handler listener, for post-installation actions
# <list[<dict>]> representing user-level or supplemental shell script software installations
userspace:
- name: ""
src: ""
pre: ""
post: ""
# <dict[<str>:<dict>]> representing package groups installed from source archives
archive:
# <list[<dict>]> representing system-level or essential source archives
core:
- name: "" # <str> arbitrary name, used by handler listener
src: "" # <str> URI/URL or path of archive file for software build
deploy: "" # <str> URI/URL or path to shell script, or handler listener name, to build software from archive
pre: "" # <str> URI/URL or path to shell script, or handler listener name, for actions to take before software build
post: "" # <str> URI/URL or path to shell script, or handler listener name, for actions to take after software build
# <list[<dict>]> representing user-level or supplemental source archives
userspace:
- name: ""
src: ""
deploy: ""
pre: ""
post: ""
# <dict[<str>:<dict>]> representing package groups installed from source git repositories
git_repos:
# <list[<dict>]> representing system-level or essential git repositories
core:
- name: "" # <str> arbitrary name, used by handler listener
src: "" # <str> URI/URL or path of git repository
src_path: "" # <str> path in which to place git repository clone
branch: "" # <str> specific branch to pull or otherwise to swtich into
deploy: "" # <str> URI/URL or path to shell script, or handler listener name, to build or run from source repository
pre: "" # <str> URI/URL or path to shell script, or handler listener name, for actions to take before building or running from source repository
post: "" # <str> URI/URL or path to shell script, or handler listener name, for actions to take after building or running from source repository
# <list[<dict>]> representing user-level or supplemental git repositories
userspace:
- name: "" # <str> arbitrary name, used by handler listener
src: "" # <str> URI/URL or path of git repository
src_path: "" # <str> path in which to place git repository clone
remote: "" # <str> the name of the remote source of the git repository
branch: "" # <str> specific branch to pull or otherwise to swtich into
deploy: "" # <str> URI/URL or path to shell script, or handler listener name, to build or run from source repository
pre: "" # <str> URI/URL or path to shell script, or handler listener name, for actions to take before building or running from source repository
post: "" # <str> URI/URL or path to shell script, or handler listener name, for actions to take after building or running from source repository
# <dict[<str>:<dict>]> representing container image groups installed via container engine
containers:
# <list[<dict>]> representing system-level or essential container images
core: []
# <list[<dict>]> representing user-level or supplemental container images
userspace: []
# <dict[<str>:<dict>]> representing package groups installed via snap package
snaps:
# <list[<dict>]> representing system-level or essential packages
core: []
# <list[<dict>]> representing user-level or supplemental packages
userspace: []
# <dict[<str>:<dict>]> representing flatpak groups installed via flatpak package
flatpaks:
# <list[<dict>]> representing system-level or essential flatpaks
core: []
# <list[<dict>]> representing user-level or supplemental flatpaks
userspace: []
@@ -1,79 +1,232 @@
# <str<vault>> representing password for Linux root user account of VPS
password: !vault |
$ANSIBLE_VAULT;1.1;AES256
66353462633933306537323461663665643234306166366663653163306436333037313032306338
3762653037396437633835356630656438623163656536310a306163663234383265386133396634
34363163343766623739646334643031373239373630663731376239333764346531396363636131
6163343335356337660a366337336632333236326532373032353332333636366638616265356562
66616534303035386134623535373935373065326539363065623230633034313433
password: "{{ lookup('password', './.tmp/' + inventory_hostname + '.pass', seed=inventory_hostname, encrypt='sha512_crypt') }}"
# <str<vault>> representing API token for VPS cloud service
token: !vault |
$ANSIBLE_VAULT;1.1;AES256
33333839333337323062326231626534616166646666343261343966636464346630363033653130
3035653864396363376633346362353239643939663462370a323935353061313563336435366331
30393463653661326539326234646438663133616634663439303932656137633839656533376433
3666643635613039390a323138393033623131326438616331386539666333613630316263613636
66663263373665343662393638623064356234646165343835623966643761333562323132396466
63363436333463653130323531343139316466316131313031343232343039396261616231376232
66383938333661363532303166306563396634663132396166646132663131373738396131626633
34393265343061356531
# <str> representing name and hostname of VPS to be made in VPS cloud service
instance: sukaato
token: ~
# <str<enum>> representing region options from or for given VPS cloud service
origin: us-east
# <str<enum>> representing Linux distro or OS image available in VPS service to be used for VPS
operating_system: linode/debian13
# <list[<str>]> list of control node or local SSH key basenames for root user
ssh_keys:
- ed25519@sukaato.hikiki
- ecdsa@sukaato.hikiki
- ed25519@staging
- ecdsa@staging
# <list<dict>> list of administrative users (in Linux, users that can use "sudo")
admins:
- username: senpai # <str> arbitrary valid user name
services: ~ # <list[<str>]> if linux system user, assocated servce
# <list[<str>]> list of control node or local SSH key basenames for this user
ssh_keys:
- ecdsa-37851076-sk@sukaato.hikiki
- ecdsa-37851072-sk@sukaato.hikiki
# @TODO add secondary and teriary Yubikeys
- ed25519@staging
- ecdsa@staging
# <str<vault?>> hashed (and maybe salted) password
password: !vault |
$ANSIBLE_VAULT;1.1;AES256
31663265653031323833373663653132653532646638316465393364613961643130653330393062
6165386239303965386261363565353137636164356130370a336465353931373564393339363561
37353162333331663833656631663165356134633961323337663439663733316231666334336539
6537373334326634610a623037613462663733343230306538386561363838316638623365636533
32313931666439363435663161663665346266653763343265376366383837376436643163376430
39393861613037333766386138376335653334363737626664383236303234653461313230383564
33393834636165386562383435666233313664656233326364616237636230303264363732376639
64396564366335366430303031323865333635306536346463386334303235386438663061343934
37376466373566396130366330383834323332626166316661336339346462343466
password: "{{ lookup('password', './.tmp/senpai@' + inventory_hostname + '.pass', seed='senpai@' + inventory_hostname, encrypt='sha512_crypt') }}"
# <dict[<str>:<dict>]> package groups
pkgs:
# <dict[<str>:<dict>]> representing package groups installed by package manager via repositories
mngr:
# <list[<dict>]> representing system-level or essential packages
core:
- name: neovim
- name: "zfsutils-linux"
uri: ~
sources: ~
sigkey: ~
sources: ~
types: ~
suites: ~
comps: ~
- name: flatpak
handler: default
- name: "zfs-dkms"
uri: ~
sources: ~
sigkey: ~
sources: ~
types: ~
suites: ~
comps: ~
- name: snapd
handler: default
- name: cron
uri: ~
sources: ~
sigkey: ~
sources: ~
types: ~
suites: ~
comps: ~
handler: default
- name: "cron-daemon-common"
uri: ~
sigkey: ~
sources: ~
types: ~
suites: ~
comps: ~
handler: default
- name: "python3-certbot"
uri: ~
sigkey: ~
sources: ~
types: ~
suites: ~
comps: ~
handler: default
- name: "python3-certbot-dns-standalone"
uri: ~
sigkey: ~
sources: ~
types: ~
suites: ~
comps: ~
handler: certbot
- name: sudo
uri: ~
sigkey: ~
sources: ~
types: ~
suites: ~
comps: ~
handler: default
- name: "xz-utils"
uri: ~
sigkey: ~
sources: ~
types: ~
suites: ~
comps: ~
handler: default
- name: wireguard
uri: ~
sigkey: ~
sources: ~
types: ~
suites: ~
comps: ~
handler: wireguard
- name: "wireguard-tools"
uri: ~
sigkey: ~
sources: ~
types: ~
suites: ~
comps: ~
handler: default
- name: vagrant
uri: ~
sigkey: ~
sources: ~
types: ~
suites: ~
comps: ~
handler: default
- name: "vagrant-libvirt"
uri: ~
sigkey: ~
sources: ~
types: ~
suites: ~
comps: ~
handler: default
- name: "qemu-system"
uri: ~
sigkey: ~
sources: ~
types: ~
suites: ~
comps: ~
handler: default
- name: "qemu-kvm"
uri: ~
sigkey: ~
sources: ~
types: ~
suites: ~
comps: ~
handler: default
- name: "bridge-utils"
uri: ~
sigkey: ~
sources: ~
types: ~
suites: ~
comps: ~
handler: default
- name: libvirt
uri: ~
sigkey: ~
sources: ~
types: ~
suites: ~
comps: ~
handler: default
- name: "libvirt-clients"
uri: ~
sigkey: ~
sources: ~
types: ~
suites: ~
comps: ~
handler: default
- name: "libvirt-daemon-system"
uri: ~
sigkey: ~
sources: ~
types: ~
suites: ~
comps: ~
handler: default
- name: "virt-install"
uri: ~
sigkey: ~
sources: ~
types: ~
suites: ~
comps: ~
handler: default
- name: "virt-viewer"
uri: ~
sigkey: ~
sources: ~
types: ~
suites: ~
comps: ~
handler: default
- name: gcc
uri: ~
sigkey: ~
sources: ~
types: ~
suites: ~
comps: ~
handler: default
- name: "kitty-terminfo"
uri: ~
sigkey: ~
sources: ~
types: ~
suites: ~
comps: ~
handler: default
- name: "git-doc"
uri: ~
sigkey: ~
sources: ~
types: ~
suites: ~
comps: ~
handler: default
- name: "git-delta"
uri: ~
sigkey: ~
sources: ~
types: ~
suites: ~
comps: ~
handler: default
- name: fastfetch
uri: ~
sigkey: ~
sources: ~
types: ~
suites: ~
comps: ~
handler: default
- name: git
uri: ~
sources: ~
@@ -81,6 +234,7 @@ pkgs:
types: ~
suites: ~
comps: ~
handler: git
- name: fail2ban
uri: ~
sources: ~
@@ -88,13 +242,7 @@ pkgs:
types: ~
suites: ~
comps: ~
- name: crowdsec
uri: ~
sources: ~
sigkey: ~
types: ~
suites: ~
comps: ~
handler: default
- name: glow
uri: ~
sigkey: "https://repo.charm.sh/apt/gpg.key"
@@ -102,92 +250,113 @@ pkgs:
types: deb
suites: "*"
comps: "*"
- name: vim-vimwiki
uri: ~
sigkey: ~
sources: ~
types: ~
suites: ~
comps: ~
- name: pandoc
uri: ~
sigkey: ~
sources: ~
types: ~
suites: ~
comps: ~
- name: tor
uri: ~
sigkey: ~
sources: ~
types: ~
suites: ~
comps: ~
- name: i2pd
handler: default
- name: whois
uri: ~
sigkey: ~
sources: ~
types: ~
suites: ~
comps: ~
handler: default
# @TODO make sure to create handler inclusion of tasks in file under/at `tasks/contingent/pkg` for below
- name: ufw
- name: iptables
uri: ~
sigkey: ~
sources: ~
types: ~
suites: ~
comps: ~
- name: avahi-utils
handler: default
- name: gnupg
uri: ~
sigkey: ~
sources: ~
types: ~
suites: ~
comps: ~
- name: libpam-google-authenticator
handler: default
- name: "gnupg-agent"
uri: ~
sigkey: ~
sources: ~
types: ~
suites: ~
comps: ~
- name: libpam-u2f
handler: default
- name: tmux
uri: ~
sigkey: ~
sources: ~
types: ~
suites: ~
comps: ~
- name: pamu2fcfg
handler: default
- name: "cpu-checker"
uri: ~
sigkey: ~
sources: ~
types: ~
suites: ~
comps: ~
- name: pcscd
handler: default
- name: libpam-doc
uri: ~
sigkey: ~
sources: ~
types: ~
suites: ~
comps: ~
handler: default
- name: headscale
uri: "https://github.com/juanfont/headscale/releases/download/v0.28.0/headscale_0.28.0_linux_amd64.deb"
sigkey: ~
sources: ~
types: ~
suites: ~
comps: ~
handler: headscale
userspace:
- name: podman
- name: "libpam-google-authenticator"
uri: ~
sigkey: ~
sources: ~
types: ~
suites: ~
comps: ~
- name: podman-compose
handler: "libpam-google-authenticator"
- name: vim
uri: ~
sources: ~
sigkey: ~
types: ~
suites: ~
comps: ~
handler: vim
- name: "vim-vimwiki"
uri: ~
sigkey: ~
sources: ~
types: ~
suites: ~
comps: ~
handler: default
- name: "vim-doc"
uri: ~
sigkey: ~
sources: ~
types: ~
suites: ~
comps: ~
handler: default
- name: neovim
uri: ~
sigkey: ~
sources: ~
types: ~
suites: ~
comps: ~
handler: neovim
- name: distrobox
uri: ~
sigkey: ~
@@ -195,83 +364,15 @@ pkgs:
types: ~
suites: ~
comps: ~
- name: proftpd-core
uri: ~
sigkey: ~
sources: ~
types: ~
suites: ~
comps: ~
- name: proftpd-doc
uri: ~
sigkey: ~
sources: ~
types: ~
suites: ~
comps: ~
- name: proftpd-mod-crypto
uri: ~
sigkey: ~
sources: ~
types: ~
suites: ~
comps: ~
- name: proftpd-mod-ldap
uri: ~
sigkey: ~
sources: ~
types: ~
suites: ~
comps: ~
- name: proftpd-mod-sqlite
uri: ~
sigkey: ~
sources: ~
types: ~
suites: ~
comps: ~
- name: aria2
uri: ~
sigkey: ~
sources: ~
types: ~
suites: ~
comps: ~
- name: syncplay-server
uri: ~
sigkey: ~
sources: ~
types: ~
suites: ~
comps: ~
- name: caddy
uri: ~
sigkey: ~
sources: ~
types: ~
suites: ~
comps: ~
- name: erlang
uri: ~
sigkey: ~
sources: ~
types: ~
suites: ~
comps: ~
- name: erlang-hex
uri: ~
sigkey: ~
sources: ~
types: ~
suites: ~
comps: ~
- name: elixir
handler: default
- name: smartmontools
uri: ~
sigkey: ~
sources: ~
types: ~
suites: ~
comps: ~
handler: default
- name: python3.13
uri: ~
sigkey: ~
@@ -279,20 +380,23 @@ pkgs:
types: ~
suites: ~
comps: ~
- name: python3-venv
handler: default
- name: "python3-venv"
uri: ~
sigkey: ~
sources: ~
types: ~
suites: ~
comps: ~
- name: python3-pip
handler: default
- name: "python3-pip"
uri: ~
sigkey: ~
sources: ~
types: ~
suites: ~
comps: ~
handler: default
- name: golang
uri: ~
sigkey: ~
@@ -300,34 +404,15 @@ pkgs:
types: ~
suites: ~
comps: ~
- name: hugo
uri: ~
sigkey: ~
sources: ~
types: ~
suites: ~
comps: ~
- name: yt-dlp
uri: ~
sigkey: ~
sources: ~
types: ~
suites: ~
comps: ~
- name: syncthing-discosrv
uri: ~
sigkey: ~
sources: ~
types: ~
suites: ~
comps: ~
- name: syncthing-relaysrv
handler: default
- name: "golang-doc"
uri: ~
sigkey: ~
sources: ~
types: ~
suites: ~
comps: ~
handler: default
# <dict[<str>:<dict>]> representing package groups installed by shell scripts
script:
# <list[<dict>]> representing user-level or supplemental shell script installations
@@ -336,38 +421,39 @@ pkgs:
src: "https://radicle.dev/install"
pre: ~
post: ~
handler: radicle
- name: rustup
src: "https://sh.rustup.rs"
pre: ~
post: ~
- name: julia
src: "https://install.julialang.org"
pre: ~
post: ~
handler: rustup
- name: uv
src: "https://astral.sh/uv/install.sh"
pre: ~
post: ~
handler: uv
# <dict[<str>:<dict>]> representing package groups installed from source archives
archive:
# <list[<dict>]> representing user-level or supplemental source archives
userspace:
- name: surge
src: "https://github.com/SurgeDM/Surge/releases/download/v0.8.6/Surge_0.8.6_linux_amd64.tar.gz"
deploy: ~
pre: ~
post: ~
- name: nvm
src: "https://nodejs.org/dist/v24.16.0/node-v24.16.0-linux-x64.tar.xz"
- name: difftastic
src: "https://github.com/Wilfred/difftastic/releases/download/0.69.0/difft-x86_64-unknown-linux-gnu.tar.gz"
deploy: ~
pre: ~
post: ~
handler: difftastic
# <dict[<str>:<dict>]> representing package groups installed from source git repositories
git_repos:
userspace:
- name: quartz
src: "https://github.com/jackyzha0/quartz.git"
branch: v5.0.0
deploy: ~
pre: ~
post: ~
# userspace:
# - name: quartz
# src: "https://github.com/jackyzha0/quartz.git"
# branch: v5.0.0
# deploy: ~
# pre: ~
# post: ~
# handler: default
binaries:
core:
- name: dsnet
src: "https://github.com/naggie/dsnet/releases/download/v0.8.1/dsnet-linux-amd64"
handler: dsnet
@@ -1,47 +1,166 @@
# @TODO create inventory group variables akin to structure of sukaato group's for homeserver
# <str<vault>> representing password for Linux root user account of VPS
password: !vault |
$ANSIBLE_VAULT;1.1;AES256
32333335343939653231313938666134306338356633393035363039373465386165313666383262
6465313738316635633332623765336563626165336330370a616634393266366430363663333066
63373165346236386632393866316164623133373761303262643734356433646661636533666266
3834643765613937300a326365643961626236386261303933643965333565623836313231346537
3030
# <str> representing hostname for LAN server; same as host or group variable name
instance: armitage
password: "{{ lookup('password', './.tmp/' + inventory_hostname + '.pass', seed=inventory_hostname, encrypt='sha512_crypt') }}"
# <str<enum>> representing Linux distro or OS image to be used for VPS
# operating_system: "tftp://hikiki.local:69/debian.iso"
operating_system: ~
# <list[<str>]> of control node or local SSH key basenames
ssh_keys:
- ed25519@sukaato.hikiki
- ecdsa@sukaato.hikiki
- ed25519@staging
- ecdsa@staging
# <list<dict>> list of administrative users (in Linux, users that can use "sudo")
admins:
- username: senpai # <str> arbitrary valid user name
services: ~ # <list[<str>]> if linux system user, assocated servce
# <list[<str>]> list of control node or local SSH key basenames for this user
ssh_keys:
- ecdsa-37851076-sk@sukaato.hikiki
- ecdsa-37851072-sk@sukaato.hikiki
# @TODO add secondary and teriary Yubikeys
- ed25519@staging
- ecdsa@staging
# <str<vault?>> hashed (and maybe salted) password
password: !vault |
$ANSIBLE_VAULT;1.1;AES256
34636132613365646330653431653236303563623464316638643439373761366564663264613738
3033343264373264333362616434333465323439653134340a643066663832353965313434386639
38366263646638353632656431366638393939623537326233306132306436363338373161643433
3439653833333164390a303430616561356464393030353433303738383730643330323031373432
62386231653339616436383837383966643539353036353034363132633539643332386131613537
31356230383561663735363530393562363237343166323635666665386165633130653864646238
39323735386161646531323335393639353630376136663063393930326434346435343937623336
33336132663238326662323536326638333139313535373166636363336366663962373936383536
62303536363939316563646630633064306364366331623665646533633065336236
password: "{{ lookup('password', './.tmp/senpai@' + inventory_hostname + '.pass', seed='senpai@' + inventory_hostname, encrypt='sha512_crypt') }}"
# <dict[<str>:<dict>]> package groups
pkgs:
# <dict[<str>:<dict>]> representing package groups installed by package manager via repositories
mngr:
# <list[<dict>]> representing system-level or essential packages
core:
- name: "zfsutils-linux"
uri: ~
sigkey: ~
sources: ~
types: ~
suites: ~
comps: ~
handler: default
- name: "zfs-dkms"
uri: ~
sigkey: ~
sources: ~
types: ~
suites: ~
comps: ~
handler: default
- name: cron
uri: ~
sigkey: ~
sources: ~
types: ~
suites: ~
comps: ~
handler: default
- name: "cron-daemon-common"
uri: ~
sigkey: ~
sources: ~
types: ~
suites: ~
comps: ~
handler: default
- name: "xz-utils"
uri: ~
sigkey: ~
sources: ~
types: ~
suites: ~
comps: ~
handler: default
- name: wireguard
uri: ~
sigkey: ~
sources: ~
types: ~
suites: ~
comps: ~
handler: wireguard
- name: "wireguard-tools"
uri: ~
sigkey: ~
sources: ~
types: ~
suites: ~
comps: ~
handler: default
- name: vagrant
uri: ~
sigkey: ~
sources: ~
types: ~
suites: ~
comps: ~
handler: default
- name: "vagrant-libvirt"
uri: ~
sigkey: ~
sources: ~
types: ~
suites: ~
comps: ~
handler: default
- name: "qemu-system"
uri: ~
sigkey: ~
sources: ~
types: ~
suites: ~
comps: ~
handler: default
- name: "qemu-kvm"
uri: ~
sigkey: ~
sources: ~
types: ~
suites: ~
comps: ~
handler: default
- name: "bridge-utils"
uri: ~
sigkey: ~
sources: ~
types: ~
suites: ~
comps: ~
handler: default
- name: libvirt
uri: ~
sigkey: ~
sources: ~
types: ~
suites: ~
comps: ~
handler: default
- name: "libvirt-clients"
uri: ~
sigkey: ~
sources: ~
types: ~
suites: ~
comps: ~
handler: default
- name: "libvirt-daemon-system"
uri: ~
sigkey: ~
sources: ~
types: ~
suites: ~
comps: ~
handler: default
- name: "virt-install"
uri: ~
sigkey: ~
sources: ~
types: ~
suites: ~
comps: ~
handler: default
- name: "virt-viewer"
uri: ~
sigkey: ~
sources: ~
types: ~
suites: ~
comps: ~
handler: default
- name: sudo
uri: ~
sigkey: ~
@@ -49,13 +168,15 @@ pkgs:
types: ~
suites: ~
comps: ~
- name: kitty-terminfo
handler: default
- name: "kitty-terminfo"
uri: ~
sigkey: ~
sources: ~
types: ~
suites: ~
comps: ~
handler: default
- name: whois
uri: ~
sigkey: ~
@@ -63,34 +184,15 @@ pkgs:
types: ~
suites: ~
comps: ~
- name: vim
uri: ~
sources: ~
sigkey: ~
types: ~
suites: ~
comps: ~
- name: vim-vimwiki
handler: default
- name: smartmontools
uri: ~
sigkey: ~
sources: ~
types: ~
suites: ~
comps: ~
- name: vim-doc
uri: ~
sigkey: ~
sources: ~
types: ~
suites: ~
comps: ~
# - name: flatpak
# uri: ~
# sources: ~
# sigkey: ~
# types: ~
# suites: ~
# comps: ~
handler: default
- name: snapd
uri: ~
sources: ~
@@ -98,6 +200,7 @@ pkgs:
types: ~
suites: ~
comps: ~
handler: default
- name: git
uri: ~
sources: ~
@@ -105,27 +208,31 @@ pkgs:
types: ~
suites: ~
comps: ~
- name: git-delta
handler: git
- name: "git-delta"
uri: ~
sources: ~
sigkey: ~
types: ~
suites: ~
comps: ~
- name: git-doc
handler: default
- name: "git-doc"
uri: ~
sources: ~
sigkey: ~
types: ~
suites: ~
comps: ~
- name: git-man
handler: default
- name: "git-man"
uri: ~
sources: ~
sigkey: ~
types: ~
suites: ~
comps: ~
handler: default
- name: fail2ban
uri: ~
sources: ~
@@ -133,13 +240,7 @@ pkgs:
types: ~
suites: ~
comps: ~
- name: crowdsec
uri: ~
sources: ~
sigkey: ~
types: ~
suites: ~
comps: ~
handler: default
- name: glow
uri: ~
sigkey: "https://repo.charm.sh/apt/gpg.key"
@@ -147,6 +248,7 @@ pkgs:
types: deb
suites: "*"
comps: "*"
handler: default
- name: pandoc
uri: ~
sigkey: ~
@@ -154,20 +256,7 @@ pkgs:
types: ~
suites: ~
comps: ~
- name: tor
uri: ~
sigkey: ~
sources: ~
types: ~
suites: ~
comps: ~
- name: i2pd
uri: ~
sigkey: ~
sources: ~
types: ~
suites: ~
comps: ~
handler: default
- name: curl
uri: ~
sigkey: ~
@@ -175,13 +264,15 @@ pkgs:
types: ~
suites: ~
comps: ~
- name: build-essential
handler: default
- name: "build-essential"
uri: ~
sigkey: ~
sources: ~
types: ~
suites: ~
comps: ~
handler: default
- name: fastfetch
uri: ~
sigkey: ~
@@ -189,6 +280,7 @@ pkgs:
types: ~
suites: ~
comps: ~
handler: default
- name: gcc
uri: ~
sigkey: ~
@@ -196,13 +288,7 @@ pkgs:
types: ~
suites: ~
comps: ~
- name: gcc-doc
uri: ~
sigkey: ~
sources: ~
types: ~
suites: ~
comps: ~
handler: default
- name: dpkg-dev
uri: ~
sigkey: ~
@@ -210,13 +296,7 @@ pkgs:
types: ~
suites: ~
comps: ~
- name: xz-utils
uri: ~
sigkey: ~
sources: ~
types: ~
suites: ~
comps: ~
handler: default
- name: ufw
uri: ~
sigkey: ~
@@ -224,6 +304,15 @@ pkgs:
types: ~
suites: ~
comps: ~
handler: default
- name: avahi-daemon
uri: ~
sigkey: ~
sources: ~
types: ~
suites: ~
comps: ~
handler: default
- name: avahi-utils
uri: ~
sigkey: ~
@@ -231,13 +320,7 @@ pkgs:
types: ~
suites: ~
comps: ~
- name: libpam-google-authenticator
uri: ~
sigkey: ~
sources: ~
types: ~
suites: ~
comps: ~
handler: default
- name: libpam-u2f
uri: ~
sigkey: ~
@@ -245,6 +328,7 @@ pkgs:
types: ~
suites: ~
comps: ~
handler: default
- name: pamu2fcfg
uri: ~
sigkey: ~
@@ -252,6 +336,7 @@ pkgs:
types: ~
suites: ~
comps: ~
handler: default
- name: pcscd
uri: ~
sigkey: ~
@@ -259,6 +344,7 @@ pkgs:
types: ~
suites: ~
comps: ~
handler: default
- name: tftpd-hpa
uri: ~
sigkey: ~
@@ -266,20 +352,7 @@ pkgs:
types: ~
suites: ~
comps: ~
- name: apache2
uri: ~
sigkey: ~
sources: ~
types: ~
suites: ~
comps: ~
- name: apache2-doc
uri: ~
sigkey: ~
sources: ~
types: ~
suites: ~
comps: ~
handler: default
- name: gocryptfs
uri: ~
sigkey: ~
@@ -287,6 +360,7 @@ pkgs:
types: ~
suites: ~
comps: ~
handler: default
- name: cryfs
uri: ~
sigkey: ~
@@ -294,6 +368,7 @@ pkgs:
types: ~
suites: ~
comps: ~
handler: default
- name: tmux
uri: ~
sigkey: ~
@@ -301,98 +376,48 @@ pkgs:
types: ~
suites: ~
comps: ~
- name: squid
handler: default
- name: "cpu-checker"
uri: ~
sigkey: ~
sources: ~
types: ~
suites: ~
comps: ~
- name: cpu-checker
handler: default
- name: "libpam-doc"
uri: ~
sigkey: ~
sources: ~
types: ~
suites: ~
comps: ~
- name: asciidoc
handler: default
- name: gnupg
uri: ~
sigkey: ~
sources: ~
types: ~
suites: ~
comps: ~
- name: libpam-doc
handler: default
- name: "gnupg-agent"
uri: ~
sigkey: ~
sources: ~
types: ~
suites: ~
comps: ~
handler: default
- name: minidlna
uri: ~
sigkey: ~
sources: ~
types: ~
suites: ~
comps: ~
handler: default
userspace:
- name: neovim
uri: ~
sources: ~
sigkey: ~
types: ~
suites: ~
comps: ~
- name: podman
uri: ~
sigkey: ~
sources: ~
types: ~
suites: ~
comps: ~
- name: podman-compose
uri: ~
sigkey: ~
sources: ~
types: ~
suites: ~
comps: ~
- name: distrobox
uri: ~
sigkey: ~
sources: ~
types: ~
suites: ~
comps: ~
- name: proftpd-core
uri: ~
sigkey: ~
sources: ~
types: ~
suites: ~
comps: ~
- name: proftpd-doc
uri: ~
sigkey: ~
sources: ~
types: ~
suites: ~
comps: ~
- name: proftpd-mod-crypto
uri: ~
sigkey: ~
sources: ~
types: ~
suites: ~
comps: ~
- name: proftpd-mod-ldap
uri: ~
sigkey: ~
sources: ~
types: ~
suites: ~
comps: ~
- name: proftpd-mod-sqlite
uri: ~
sigkey: ~
sources: ~
types: ~
suites: ~
comps: ~
- name: aria2
uri: ~
sigkey: ~
@@ -400,48 +425,87 @@ pkgs:
types: ~
suites: ~
comps: ~
- name: syncplay-server
handler: aria
- name: "libpam-google-authenticator"
uri: ~
sigkey: ~
sources: ~
types: ~
suites: ~
comps: ~
- name: caddy
handler: "libpam-google-authenticator"
- name: vim
uri: ~
sources: ~
sigkey: ~
types: ~
suites: ~
comps: ~
handler: vim
- name: "vim-vimwiki"
uri: ~
sigkey: ~
sources: ~
types: ~
suites: ~
comps: ~
- name: erlang
handler: default
- name: "vim-doc"
uri: ~
sigkey: ~
sources: ~
types: ~
suites: ~
comps: ~
- name: erlang-doc
handler: default
- name: neovim
uri: ~
sources: ~
sigkey: ~
types: ~
suites: ~
comps: ~
handler: neovim
- name: sqlite3
uri: ~
sources: ~
sigkey: ~
types: ~
suites: ~
comps: ~
handler: default
- name: podman
uri: ~
sigkey: ~
sources: ~
types: ~
suites: ~
comps: ~
- name: erlang-hex
handler: podman
- name: passt
uri: ~
sigkey: ~
sources: ~
types: ~
suites: ~
comps: ~
- name: elixir
handler: default
- name: "podman-compose"
uri: ~
sigkey: ~
sources: ~
types: ~
suites: ~
comps: ~
handler: default
- name: distrobox
uri: ~
sigkey: ~
sources: ~
types: ~
suites: ~
comps: ~
handler: default
- name: python3.13
uri: ~
sigkey: ~
@@ -449,27 +513,31 @@ pkgs:
types: ~
suites: ~
comps: ~
- name: python3-venv
handler: default
- name: "python3-venv"
uri: ~
sigkey: ~
sources: ~
types: ~
suites: ~
comps: ~
- name: python3-pip
handler: default
- name: "python3-pip"
uri: ~
sigkey: ~
sources: ~
types: ~
suites: ~
comps: ~
- name: python3-doc
handler: default
- name: "python3-doc"
uri: ~
sigkey: ~
sources: ~
types: ~
suites: ~
comps: ~
handler: default
- name: golang
uri: ~
sigkey: ~
@@ -477,13 +545,15 @@ pkgs:
types: ~
suites: ~
comps: ~
- name: golang-doc
handler: default
- name: "golang-doc"
uri: ~
sigkey: ~
sources: ~
types: ~
suites: ~
comps: ~
handler: default
- name: hugo
uri: ~
sigkey: ~
@@ -491,27 +561,111 @@ pkgs:
types: ~
suites: ~
comps: ~
- name: yt-dlp
handler: default
- name: "yt-dlp"
uri: ~
sigkey: ~
sources: ~
types: ~
suites: ~
comps: ~
- name: syncthing-discosrv
handler: default
- name: "syncthing-discosrv"
uri: ~
sigkey: ~
sources: ~
types: ~
suites: ~
comps: ~
- name: syncthing-relaysrv
handler: default
- name: "syncthing-relaysrv"
uri: ~
sigkey: ~
sources: ~
types: ~
suites: ~
comps: ~
handler: default
- name: burp
uri: ~
sigkey: ~
sources: ~
types: ~
suites: ~
comps: ~
handler: default
- name: rsync
uri: ~
sigkey: ~
sources: ~
types: ~
suites: ~
comps: ~
handler: default
- name: rclone
uri: ~
sigkey: ~
sources: ~
types: ~
suites: ~
comps: ~
handler: default
- name: proftpd
uri: ~
sigkey: ~
sources: ~
types: ~
suites: ~
comps: ~
handler: default
- name: reprepro
uri: ~
sigkey: ~
sources: ~
types: ~
suites: ~
comps: ~
handler: default
- name: erlang
uri: ~
sigkey: ~
sources: ~
types: ~
suites: ~
comps: ~
handler: default
- name: "erlang-hex"
uri: ~
sigkey: ~
sources: ~
types: ~
suites: ~
comps: ~
handler: default
- name: erlang-doc
uri: ~
sigkey: ~
sources: ~
types: ~
suites: ~
comps: ~
handler: default
- name: elixir
uri: ~
sigkey: ~
sources: ~
types: ~
suites: ~
comps: ~
handler: default
- name: "libsecret-1-dev"
uri: ~
sigkey: ~
sources: ~
types: ~
suites: ~
comps: ~
handler: default
# <dict[<str>:<dict>]> representing package groups installed by shell scripts
script:
# <list[<dict>]> representing user-level or supplemental shell script installations
@@ -520,18 +674,22 @@ pkgs:
src: "https://radicle.dev/install"
pre: ~
post: ~
handler: radicle
- name: rustup
src: "https://sh.rustup.rs"
pre: ~
post: ~
handler: rustup
- name: julia
src: "https://install.julialang.org"
pre: ~
post: ~
handler: julia
- name: uv
src: "https://astral.sh/uv/install.sh"
pre: ~
post: ~
handler: uv
# <dict[<str>:<dict>]> representing package groups installed from source archives
archive:
# <list[<dict>]> representing user-level or supplemental source archives
@@ -541,16 +699,19 @@ pkgs:
deploy: ~
pre: ~
post: ~
handler: surge
- name: nvm
src: "https://nodejs.org/dist/v24.16.0/node-v24.16.0-linux-x64.tar.xz"
deploy: ~
pre: ~
post: ~
handler: nvm
- name: difftastic
src: "https://github.com/Wilfred/difftastic/releases/download/0.69.0/difft-x86_64-unknown-linux-gnu.tar.gz"
deploy: ~
pre: ~
post: ~
handler: difftastic
# <dict[<str>:<dict>]> representing package groups installed from source git repositories
git_repos:
userspace:
@@ -562,9 +723,46 @@ pkgs:
# deploy: ~
# pre: ~
# post: ~
# handler: false
- name: "skato-compose"
src: "https://{{ source_code.host }}/{{ source_code.user }}/{{ source_code.repos.compose.name }}.git"
branch: main
deploy: ~
pre: ~
post: ~
handler: skato_compose
- name: skato-cli
src: "https://{{ source_code.host }}/{{ source_code.user }}/{{ source_code.repos.cli.name }}.git"
branch: main
deploy: ~
pre: ~
post: ~
handler: skato_cli
- name: "skato-blog-theme"
src: "https://{{ source_code.host }}/{{ source_code.user }}/{{ source_code.repos.blog_theme.name }}.git"
branch: main
deploy: ~
pre: ~
post: ~
handler: skato_blog_theme
- name: "skato-blog"
src: "https://{{ source_code.host }}/{{ source_code.user }}/{{ source_code.repos.blog.name }}.git"
branch: main
deploy: ~
pre: ~
post: ~
handler: skato_blog
- name: sukaato
src: "https://{{ source_code.host }}/{{ source_code.user }}/{{ source_code.repos.site.name }}.git"
branch: main
deploy: ~
pre: ~
post: ~
handler: sukaato
- name: quartz
src: "https://github.com/jackyzha0/quartz.git"
branch: v5.0.0
deploy: ~
pre: ~
post: ~
post: ~
handler: quartz
-11
View File
@@ -1,11 +0,0 @@
# @TODO use hosts and host groupings that refer or point to VM or containerized servers for testing
ungrouped:
hosts: ~
sukaato:
hosts: ~
armitage:
hosts: ~
vps:
children: ~
homeserver:
children: ~
+61
View File
@@ -0,0 +1,61 @@
# @NOTE run 'ansible-playbook' command on this using 'sudo'
- name: Initialize homeserver
hosts: "{{ chosen_host | default('staging1.test') }}"
remote_user: root
vars:
harden: true
vars_files:
# - vars/certbot_settings@homeserver.yml # REQUIRED
- vars/local_facts.yml # REQUIRED
- vars/wireguard_settings.yml # REQUIRED
- vars/podpose_settings.yml # REQUIRED
- vars/surge_settings.yml
- vars/users@{{ inventory_hostname | default('homeserver') }}.yml
- vars/config@{{ inventory_hostname | default('homeserver') }}.yml
tasks:
- name: Hardening SSH server
ansible.builtin.include_role:
name: init-server # required. The name of the role to be executed.
# apply: # not required. Accepts a hash of task keywords (e.g. C(tags), C(become)) that will be applied to all tasks within the included role.
tasks_from: harden # not required. File to load from a role's C(tasks/) directory.
# vars_from: main # not required. File to load from a role's C(vars/) directory.
# defaults_from: main # not required. File to load from a role's C(defaults/) directory.
# allow_duplicates: True # not required. Overrides the role's metadata setting to allow using a role more than once with the same parameters.
# handlers_from: main # not required. File to load from a role's C(handlers/) directory.
- name: Installing requisite packages
ansible.builtin.include_role:
name: init-server
tasks_from: core@install-pkgs
handlers_from: core
- name: Initializing groups and users
ansible.builtin.include_role:
name: init-server
tasks_from: ssh-users
- name: Updating hostname
become: true
ansible.builtin.hostname:
name: "{{ fqdn | default(inventory_hostname) }}"
- name: Updating hosts file
become: true
ansible.builtin.lineinfile:
path: /etc/hosts
regexp: "^127\\.0\\.1\\.1"
line: "127.0.1.1 {{ fqdn | default(inventory_hostname) }}"
insertbefore: BOF
state: present
- name: Updating host icon name
become: true
ansible.builtin.command:
cmd: "hostnamectl set-icon-name computer-server"
- name: Notifying user that all processes have finished
ansible.builtin.debug:
msg: All processes finished. Hit enter to reboot machine.
- name: Ensuring user has read prior message regarding upcoming reboot
ansible.builtin.pause:
- name: Rebooting machine for hostname change
become: true
ansible.builtin.reboot:
msg: "Rebooting machine.."
connect_timeout: 0
test_command: ~
ignore_errors: true
-31
View File
@@ -1,31 +0,0 @@
# @NOTE run 'ansible-playbook' command on this using 'sudo'
- name: Initialize homeserver
hosts: armitage
remote_user: root
vars:
harden: true
local_facts:
user_id: ~ # REQUIRED
user_dir: ~ # REQUIRED
tasks:
- name: Hardening SSH server
ansible.builtin.include_role:
name: init-server # required. The name of the role to be executed.
# apply: # not required. Accepts a hash of task keywords (e.g. C(tags), C(become)) that will be applied to all tasks within the included role.
tasks_from: harden # not required. File to load from a role's C(tasks/) directory.
# vars_from: main # not required. File to load from a role's C(vars/) directory.
# defaults_from: main # not required. File to load from a role's C(defaults/) directory.
# allow_duplicates: True # not required. Overrides the role's metadata setting to allow using a role more than once with the same parameters.
# handlers_from: main # not required. File to load from a role's C(handlers/) directory.
- name: Installing requisite packages
ansible.builtin.include_role:
name: init-server
tasks_from: core@install-pkgs
handlers_from: core
# - name: Reboot machine for shell environment change
# ansible.builtin.reboot:
# msg: Rebooting machine
- name: Initializing groups and users
ansible.builtin.include_role:
name: init-server
tasks_from: ssh-users
+99
View File
@@ -0,0 +1,99 @@
# @NOTE run 'ansible-playbook' command on this using 'sudo'
- name: Initialize VPS
hosts: "{{ chosen_host | default('staging0.test') }}"
remote_user: "{{ chosen_user | default('root') }}"
vars:
harden: true
vars_files:
- vars/local_facts.yml # REQUIRED
- vars/vpn_settings.yml # REQUIRED
- vars/wireguard_settings.yml # REQUIRED
- vars/headscale_settings.yml # REQUIRED
- vars/podpose_settings.yml # REQUIRED
- vars/certbot_settings@vps.yml # REQUIRED
- vars/users@{{ inventory_hostname | default('vps') }}.yml
- vars/config@{{ inventory_hostname | default('vps') }}.yml
tasks:
- name: Hardening SSH server
ansible.builtin.include_role:
name: init-server # required. The name of the role to be executed.
# apply: # not required. Accepts a hash of task keywords (e.g. C(tags), C(become)) that will be applied to all tasks within the included role.
tasks_from: harden # not required. File to load from a role's C(tasks/) directory.
# vars_from: main # not required. File to load from a role's C(vars/) directory.
# defaults_from: main # not required. File to load from a role's C(defaults/) directory.
# allow_duplicates: True # not required. Overrides the role's metadata setting to allow using a role more than once with the same parameters.
# handlers_from: main # not required. File to load from a role's C(handlers/) directory.
- name: Installing requisite packages
ansible.builtin.include_role:
name: init-server
tasks_from: core@install-pkgs
handlers_from: core
- name: Initializing groups and users
ansible.builtin.include_role:
name: init-server
tasks_from: ssh-users
- name: Flushing handlers
ansible.builtin.meta: flush_handlers
- name: Updating hostname
become: true
ansible.builtin.hostname:
name: "{{ fqdn | default(inventory_hostname) }}"
- name: Updating hosts file
become: true
ansible.builtin.lineinfile:
path: /etc/hosts
regexp: "^127\\.0\\.1\\.1"
line: "127.0.1.1 {{ fqdn | default(inventory_hostname) }}"
insertbefore: BOF
state: present
- name: Updating host icon name
become: true
ansible.builtin.command:
cmd: "hostnamectl set-icon-name computer-server"
- name: Opening port 51820
become: true
ansible.builtin.iptables:
chain: INPUT
protocol: "{{ item }}"
destination_port: 51820
jump: ACCEPT
comment: Open up port 51820
loop:
- udp
- tcp
- name: Opening port 443
become: true
ansible.builtin.iptables:
chain: INPUT
protocol: "{{ item }}"
destination_port: 443
jump: ACCEPT
comment: Open up port 443
loop:
- udp
- tcp
- name: Opening ports
become: true
ansible.builtin.iptables:
chain: INPUT
protocol: tcp
destination_port: "{{ item }}"
jump: ACCEPT
comment: "Open up port {{ (item | string) }}"
loop:
- 80
- 465
- 587
- 995
- 993
- name: Notifying user that all processes have finished
ansible.builtin.debug:
msg: All processes finished. Hit enter to reboot machine.
- name: Ensuring user has read prior message regarding upcoming reboot
ansible.builtin.pause:
- name: Rebooting machine for hostname change
become: true
ansible.builtin.reboot:
msg: "Rebooting machine.."
connect_timeout: 0
test_command: ~
+73
View File
@@ -0,0 +1,73 @@
[project]
name = "skato-ansible"
version = "0.1.0"
requires-python = ">=3.13"
dependencies = [
"ansible==13.7.0",
"ansible-builder==3.1.1",
"ansible-compat==26.3.0",
"ansible-core==2.20.6",
"ansible-lint==26.4.0",
"ansible-navigator==26.4.0",
"ansible-runner==2.4.3",
"ansible-specdoc==0.0.20",
"appdirs==1.4.4",
"attrs==26.1.0",
"baron==0.10.1",
"bindep==2.14.0",
"black==26.5.1",
"bracex==2.6",
"certifi==2026.5.20",
"cffi==2.0.0",
"charset-normalizer==3.4.7",
"click==8.4.1",
"cryptography==48.0.0",
"deprecated==1.3.1",
"distro==1.9.0",
"enrich==1.2.7",
"filelock==3.29.0",
"idna==3.16",
"jinja2==3.1.6",
"jsonschema==4.26.0",
"jsonschema-specifications==2025.9.1",
"linode-api4==5.44.0",
"lockfile==0.12.2",
"markdown-it-py==4.2.0",
"markupsafe==3.0.3",
"mdurl==0.1.2",
"molecule==26.4.0",
"mypy-extensions==1.1.0",
"onigurumacffi==1.5.0",
"packaging==26.2",
"parsley==1.3",
"passlib==1.7.4",
"pathspec==1.0.4",
"pbr==7.0.3",
"pexpect==4.9.0",
"pip==25.1.1",
"platformdirs==4.9.6",
"pluggy==1.6.0",
"polling==0.3.2",
"ptyprocess==0.7.0",
"pycparser==3.0",
"pygments==2.20.0",
"python-daemon==3.1.2",
"pytokens==0.4.1",
"pyyaml==6.0.3",
"redbaron==0.9.2",
"referencing==0.37.0",
"requests==2.34.2",
"resolvelib==1.2.1",
"rich==15.0.0",
"rpds-py==0.30.0",
"rply==0.7.8",
"ruamel-yaml==0.19.1",
"ruamel-yaml-clib==0.2.15",
"setuptools==82.0.1",
"subprocess-tee==0.4.2",
"tzdata==2026.2",
"urllib3==2.7.0",
"wcmatch==10.1",
"wrapt==2.2.1",
"yamllint==1.38.0",
]
-65
View File
@@ -1,65 +0,0 @@
ansible==13.7.0
ansible-builder==3.1.1
ansible-compat==26.3.0
ansible-core==2.20.6
ansible-lint==26.4.0
ansible-navigator==26.4.0
ansible-runner==2.4.3
ansible-specdoc==0.0.20
appdirs==1.4.4
attrs==26.1.0
baron==0.10.1
bindep==2.14.0
black==26.5.1
bracex==2.6
certifi==2026.5.20
cffi==2.0.0
charset-normalizer==3.4.7
click==8.4.1
cryptography==48.0.0
deprecated==1.3.1
distro==1.9.0
enrich==1.2.7
filelock==3.29.0
idna==3.16
jinja2==3.1.6
jsonschema==4.26.0
jsonschema-specifications==2025.9.1
linode-api4==5.44.0
lockfile==0.12.2
markdown-it-py==4.2.0
markupsafe==3.0.3
mdurl==0.1.2
molecule==26.4.0
mypy-extensions==1.1.0
onigurumacffi==1.5.0
packaging==26.2
parsley==1.3
pathspec==1.0.4
pbr==7.0.3
pexpect==4.9.0
platformdirs==4.9.6
pluggy==1.6.0
polling==0.3.2
ptyprocess==0.7.0
pycparser==3.0
pygments==2.20.0
python-daemon==3.1.2
pytokens==0.4.1
pyyaml==6.0.3
redbaron==0.9.2
referencing==0.37.0
requests==2.34.2
resolvelib==1.2.1
rich==15.0.0
rpds-py==0.30.0
rply==0.7.8
ruamel-yaml==0.19.1
ruamel-yaml-clib==0.2.15
setuptools==82.0.1
subprocess-tee==0.4.2
tzdata==2026.2
urllib3==2.7.0
wcmatch==10.1
wrapt==2.2.1
yamllint==1.38.0
@@ -0,0 +1,17 @@
#!/bin/bash
set -euo pipefail
HTTP_SERVERS=()
HTTP_SERVERS_LEN="${#HTTP_SERVERS[@]}"
if command -v systemctl > /dev/null 2>&1; then
if (( HTTP_SERVERS_LEN > 0 )); then
for htserv in "${HTTP_SERVERS[@]}"
do
sudo systemctl start "$htserv"
done
fi
fi
if command -v podman > /dev/null 2>&1; then
podman start -a
fi
@@ -0,0 +1,17 @@
#!/bin/bash
set -euo pipefail
HTTP_SERVERS=()
HTTP_SERVERS_LEN="${#HTTP_SERVERS[@]}"
if command -v systemctl > /dev/null 2>&1; then
if (( HTTP_SERVERS_LEN > 0 )); then
for htserv in "${HTTP_SERVERS[@]}"
do
sudo systemctl stop "$htserv"
done
fi
fi
if command -v podman > /dev/null 2>&1; then
podman stop -a
fi
@@ -0,0 +1,14 @@
[Unit]
Description=dsnet
After=network-online.target
Wants=network-online.target
[Service]
Type=oneshot
ExecStart=/usr/bin/dsnet up
ExecStop=/usr/bin/dsnet down
RemainAfterExit=yes
ExecReload=/usr/bin/dsnet sync
[Install]
WantedBy=default.target
@@ -0,0 +1,13 @@
[Unit]
Description=surge
After=network-online.target
Wants=network-online.target
[Service]
Type=oneshot
ExecStart=surge service start
ExecStop=surge service stop
RemainAfterExit=yes
[Install]
WantedBy=default.target
@@ -0,0 +1,14 @@
[Unit]
Description=thrunet
After=network-online.target
Wants=network-online.target
[Service]
Type=oneshot
ExecStart=/usr/local/bin/dsnet-forward.sh start
ExecStop=/usr/local/bin/dsnet-forward.sh stop
RemainAfterExit=yes
# ExecReload=/usr/bin/dsnet sync
[Install]
WantedBy=default.target
@@ -0,0 +1,9 @@
# Search these registries when pulling images without full path
unqualified-search-registries = ["docker.io", "quay.io", "ghcr.io"]
# Registry-specific configuration
# [[registry]]
# location = "docker.io"
# [[registry.mirror]]
# location = "mirror.gcr.io"
@@ -0,0 +1,14 @@
[Unit]
Description=aria2 Daemon
Requires=network.target
After=network.target
[Service]
Type=forking
ExecStart=/usr/bin/aria2c -D
ExecReload=/usr/bin/kill -HUP $MAINPID
RestartSec=1min
Restart=on-failure
[Install]
WantedBy=default.target
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

@@ -0,0 +1,139 @@
<!DOCTYPE html>
<html>
<head>
<title>{{.Host}}: {{ph "http.request.uri.path"}}</title>
<style>
body {
background-image: url(./dark-matter.png);
animation: 7s infinite ease-in-out day-cycle-bg;
}
@keyframes day-cycle-bg {
0% {
background-color: #000;
}
15% {
background-color: #121212;
}
75% {
background-color: #121212;
}
100% {
background-color: #000;
}
}
img {
display: block;
margin: 3vh auto;
max-width: 25vw;
animation: 7s infinite alternate day-cycle-filter;
}
@keyframes day-cycle-filter {
0% {
filter: brightness(1.00) contrast(0.66) grayscale(0.75);
}
15% {
filter: brightness(1.33) contrast(1.00) grayscale(0.66);
}
75% {
filter: brightness(1.33) contrast(1.10) grayscale(0.66);
}
100% {
filter: brightness(1.00) contrast(0.66) grayscale(0.75);
}
}
#sun {
display: block;
content: "";
margin: 0 auto;
height: 10vw;
max-width: 100vw;
background-color: #993333;
box-shadow: 0 0 10px 5px #000 inset;
position: relative;
top: 3vh;
transform: rotateX(180deg) rotateY(180deg);
animation: 7s infinite linear day-cycle-celestial;
}
@keyframes day-cycle-celestial {
0% {
clip-path: circle(33px at 100% 0%);
}
25% {
clip-path: circle(33px at 75% 25%);
}
50% {
clip-path: circle(33px at center 66%);
}
75% {
clip-path: circle(33px at 25% 25%);
}
100% {
clip-path: circle(33px at 0% 0%);
}
}
#err-message, #err-trace {
display: block;
margin: 50px auto;
}
#err {
margin: 2vh auto;
max-width: 75vw;
color: #fff;
background-color: #000;
border-radius: 10px;
padding: 3%;
position: relative;
top: -50px;
z-index: -1;
animation: 7s infinite ease-in-out day-cycle-bxsh;
}
@keyframes day-cycle-bxsh {
0% {
box-shadow: 0 0 25px 15px #000 inset;
}
15% {
box-shadow: 0 0 25px 15px #121212 inset;
}
75% {
box-shadow: 0 0 25px 15px #121212 inset;
}
100% {
box-shadow: 0 0 25px 15px #000 inset;
}
}
#err-message {
max-width: 66%;
text-align: justify;
}
#err-trace {
max-width: 50%;
}
#err-summary {
text-align: center;
color: #993333;
}
</style>
</head>
<body>
<div id="sun"></div>
<img src="mythe-sisyphus-klein.png" alt="Sisyphus carrying a boulder" srcset="https://kariannekirsten.com/wp-content/uploads/2022/12/mythe-klein.png"/>
<main>
<article id="err">
<section id="err-summary" class="post frontmatter">
<h1>Under Construction</h1>
<h2>Err. {{ph "http.error.status_code"}}: {{ph "http.error.status_text"}}</h2>
</section>
<section id="err-body" class="post body">
<p id="err-message" class="message">{{ph "http.error.message"}}</p>
<code id="err-trace" class="trace http">
{{ph "http.error.trace"}}
</code>
</section>
</article>
</main>
</body>
</html>
Binary file not shown.

After

Width:  |  Height:  |  Size: 121 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

+30 -6
View File
@@ -1,11 +1,35 @@
# SPDX-License-Identifier: MIT-0
---
# handlers file for roles/init-vps
- name: Setting up Radicle
- name: Skipping to next installaton
ansible.builtin.debug:
msg: "No post-installaton or additional installation steps needed--continuing..."
listen: default
- name: Setting up Git
ansible.builtin.include_tasks:
file: tasks/contingent/pkg/radicle.yml
listen: radicle
- name: Setting up ViM
file: tasks/contingent/pkg/git.yml
listen: git
- name: Setting up Certbot
ansible.builtin.include_tasks:
file: tasks/contingent/pkg/vim.yml
listen: vim
file: tasks/contingent/pkg/certbot.yml
listen: certbot
- name: Setting up TOR
ansible.builtin.include_tasks:
file: tasks/contingent/pkg/tor.yml
listen: tor
- name: Setting up Wireguard
ansible.builtin.include_tasks:
file: tasks/contingent/pkg/wireguard.yml
listen: wireguard
- name: Setting up Crowdsec
ansible.builtin.include_tasks:
file: tasks/contingent/pkg/crowdsec.yml
listen: crowdsec
- name: Setting up Headscale
ansible.builtin.include_tasks:
file: tasks/contingent/pkg/headscale.yml
listen: headscale
- name: Setting up DSNet
ansible.builtin.include_tasks:
file: tasks/contingent/pkg/dsnet.yml
listen: dsnet
+46 -6
View File
@@ -1,6 +1,14 @@
# SPDX-License-Identifier: MIT-0
---
# handlers file for roles/init-vps
- name: Skipping to next installaton
ansible.builtin.debug:
msg: "No post-installaton or additional installation steps needed--continuing..."
listen: default
- name: Setting up ViM
ansible.builtin.include_tasks:
file: tasks/contingent/pkg/vim.yml
listen: vim
- name: Settng up NeoViM
ansible.builtin.include_tasks:
file: tasks/contingent/pkg/neovim.yml
@@ -29,15 +37,47 @@
ansible.builtin.include_tasks:
file: tasks/contingent/pkg/uv.yml
listen: uv
- name: Setting up Radicle
- name: Setting up Podman
ansible.builtin.include_tasks:
file: tasks/contingent/pkg/radicle.yml
listen: radicle
- name: Setting up Surge
file: tasks/contingent/pkg/podman.yml
listen: podman
- name: Setting up Podman Compose sources
ansible.builtin.include_tasks:
file: tasks/contingent/pkg/surge.yml
listen: surge
file: tasks/contingent/pkg/skato_compose.yml
listen: skato_compose
- name: Setting up Quartz
ansible.builtin.include_tasks:
file: tasks/contingent/pkg/quartz.yml
listen: quartz
- name: Setting up native CLI tools sources
ansible.builtin.include_tasks:
file: tasks/contingent/pkg/skato_cli.yml
listen: skato_cli
- name: Setting up blog theme
ansible.builtin.include_tasks:
file: tasks/contingent/pkg/skato_blog_theme.yml
listen: skato_blog_theme
- name: Setting up blog
ansible.builtin.include_tasks:
file: tasks/contingent/pkg/skato_blog.yml
listen: skato_blog
- name: Setting up website sources
ansible.builtin.include_tasks:
file: tasks/contingent/pkg/sukaato.yml
listen: sukaato
- name: Setting up Surge
ansible.builtin.include_tasks:
file: tasks/contingent/pkg/surge.yml
listen: surge
- name: Setting up Aria2
ansible.builtin.include_tasks:
file: tasks/contingent/pkg/aria.yml
listen: aria
- name: Setting up Radicle
ansible.builtin.include_tasks:
file: tasks/contingent/pkg/radicle.yml
listen: radicle
- name: Setting up TOTP
ansible.builtin.include_tasks:
file: tasks/contingent/pkg/libpam-google-authenticator.yml
listen: libpam-google-authenticator
@@ -0,0 +1,70 @@
---
- name: Acquiring home of current user
when: ansible_facts["system"] == "Linux"
ansible.builtin.shell:
cmd: "echo ~{{ ansible_user }}"
register: ansible_user_home
- name: Acquiring current user
when: ansible_facts["system"] == "Linux"
ansible.builtin.shell:
cmd: "echo {{ ansible_user }}"
register: current_user
- name: Informing user of requirement of two main domains
when: (certbot.domains | length) < 2 or (certbot.domains | length) > 2
ansible.builtin.fail:
msg: Only two domains allowed and required
- name: Informing user of requirement at least one wildcard
when: (certbot.domains | select("regex", "^\\*\\.") | list | length) == 0
ansible.builtin.fail:
msg: At least one of the FQDNs must have a wildcard
# - name: Setting the FQDN for development
# when: compose.mode == "dev"
# ansible.builtin.set_fact:
# web_fqdn: "{{ (certbot.domains | map('regex_replace', '\\.([^\\.]*)$', '.test') | reject('regex', '^\\*\\.') | list)[0] }}"
- name: Setting the FQDN
# when: compose.mode == "prod"
ansible.builtin.set_fact:
web_fqdn: "{{ (certbot.domains | reject('regex', '^\\*\\.') | list)[0] }}"
- name: Creating directory to store configuration file
ansible.builtin.file:
path: "{{ ansible_user_home.stdout }}/.aria2"
recurse: true
owner: "{{ ansible_user }}"
group: "{{ ansible_user }}"
mode: "755"
state: directory
- name: Configuring Aria2
become: true
become_user: "{{ current_user.stdout }}"
ansible.builtin.template:
src: user/aria2/aria2.conf.j2
dest: "{{ ansible_user_home.stdout }}/.aria2/aria2.conf"
owner: "{{ ansible_user }}"
group: "{{ ansible_user }}"
mode: "644"
force: true
backup: true
# validate: "aria2c --check"
- name: Setting up Aria2 as a service
when: aria.rpc.enabled and not aria.containerized
block:
- name: Creating a user SystemD service unit for Aria2
become: true
become_user: "{{ current_user.stdout }}"
ansible.builtin.copy:
src: user/config/systemd/user/aria2cd.service
dest: "{{ ansible_user_home.stdout }}/.config/systemd/user/aria2cd.service"
owner: "{{ ansible_user }}"
group: "{{ ansible_user }}"
mode: "644"
force: true
backup: true
- name: Starting and enabling user SystemD service unit for Aria2
become: true
become_user: "{{ current_user.stdout }}"
ansible.builtin.systemd_service:
name: aria2cd
scope: user
enabled: true
state: started
daemon_reload: true
@@ -0,0 +1,166 @@
---
- name: Informing user of requirement of two main domains
when: (certbot.domains | length) < 2 or (certbot.domains | length) > 2
ansible.builtin.fail:
msg: Only two domains allowed and required
- name: Informing user of requirement at least one wildcard
when: (certbot.domains | select("regex", "^\\*\\.") | list | length) == 0
ansible.builtin.fail:
msg: At least one of the FQDNs must have a wildcard
# - name: Modifying FQDN list for development
# when: "certbot.mode == 'dev'"
# ansible.builtin.set_fact:
# web_fqdns: "{{ certbot.domains | map('regex_replace', '\\.([^\\.]*)$', '.test') | list }}"
- name: Modifying FQDN list for development
# when: "certbot.mode == 'prod'"
ansible.builtin.set_fact:
web_fqdns: "{{ certbot.domains }}"
- name: Creating domain arguments for certbot
ansible.builtin.set_fact:
certbot_domains: "{{ ['-d'] | product(web_fqdns) | map('join', '=') | list }}"
- name: Getting public IP
community.general.ipify_facts:
validate_certs: false
timeout: 20
- name: Starting domain name registration with standalone DNS
when: "certbot.auth_method == 'dns' and not certbot.containerized"
ignore_errors: true
block:
- name: Opening port 53
become: true
ansible.builtin.iptables:
chain: INPUT
protocol: tcp
destination_port: 53
jump: ACCEPT
comment: Opening up port 53
# @NOTE https://github.com/siilike/certbot-dns-standalone
- name: Exemplifying needed ACME record
ansible.builtin.set_fact:
acme_record: |
{{ fqdn | default(inventory_hostname) }} IN A {{ ipify_public_ip }}
_acme-challenge.{{ fqdn | default(inventory_hostname) }} IN CNAME {{ fqdn | default(inventory_hostname) }}.acme.{{ fqdn | default(inventory_hostname) }}.
acme.{{ fqdn | default(inventory_hostname) }} IN NS ns.acme.{{ fqdn | default(inventory_hostname) }}.
ns.acme.{{ fqdn | default(inventory_hostname) }} IN A {{ ipify_public_ip }}
- name: Informing user of need to set up ACME record
ansible.builtin.debug:
msg: "Please set ACME record in domain name provider:\n {{ acme_record }}"
- name: Waiting for user to set up ACME records
ansible.builtin.pause:
- name: Running certbot to authenticate and acquire domain name certificates
become: true
when: "certbot.mode == 'dev'"
ansible.builtin.command:
argv:
- certbot
- certonly
- "--staging"
- "--debug"
- "--authenticator=dns-standalone"
- "--email={{ certbot.email }}"
- "--agree-tos"
- "--non-interactive"
- "--dns-standalone-address={{ ipify_public_ip }}"
# - "--dns-standalone-ipv6-address={{ ansible_default_ipv6.address | default(ansible_all_ipv6_addresses[0]) }}"
- "--dns-standalone-port={{ certbot.port }}"
- "{{ certbot_domains[0] }}"
- "{{ certbot_domains[1] }}"
- name: Running certbot to authenticate and acquire domain name certificates
become: true
when: "certbot.mode == 'prod'"
ansible.builtin.command:
argv:
- certbot
- certonly
- "--authenticator=dns-standalone"
- "--email={{ certbot.email }}"
- "--agree-tos"
- "--non-interactive"
- "--dns-standalone-address={{ ipify_public_ip }}"
# - "--dns-standalone-ipv6-address={{ ansible_default_ipv6.address | default(ansible_all_ipv6_addresses[0]) }}"
- "--dns-standalone-port={{ certbot.port }}"
- "{{ certbot_domains[0] }}"
- "{{ certbot_domains[1] }}"
- name: Starting domain name registration with standalone option
when: "certbot.auth_method == 'standalone' and not certbot.containerized"
ignore_errors: true
block:
- name: Opening port 80
become: true
ansible.builtin.iptables:
chain: INPUT
protocol: tcp
destination_port: 80
jump: ACCEPT
comment: Open up port 80
- name: Acquiring domain certificates
become: true
when: "certbot.mode == 'dev'"
ansible.builtin.command:
argv:
- certbot
- certonly
- "--staging"
- "--debug"
- "--standalone"
- "--preferred-challenges=http-01"
- "--email={{ certbot.email }}"
- "--agree-tos"
- "--non-interactive"
- "{{ certbot_domains[0] }}"
- "{{ certbot_domains[1] }}"
- name: Acquiring domain certificates
become: true
when: "certbot.mode == 'dev'"
ansible.builtin.command:
argv:
- certbot
- certonly
- "--standalone"
- "--email {{ certbot.email }}"
- "--agree-tos"
- "--non-interactive"
- "--preferred-challenges http-01"
- "{{ certbot_domains[0] }}"
- "{{ certbot_domains[1] }}"
- name: Creating needed directory for renewal pre- hooks
become: true
ansible.builtin.file:
path: /etc/letsencrypt/renewal-hooks/pre
recurse: true
owner: root
group: root
mode: "755"
state: directory
- name: Copying renewal pre- hook to renewal pre- hook path
become: true
ansible.builtin.copy:
src: letsencrypt/renewal-hooks/pre/down-dependents.sh
dest: /etc/letsencrypt/renewal-hooks/pre/
owner: root
group: root
mode: "755"
force: true
backup: true
- name: Creating needed directory for renewal post- hooks
become: true
ansible.builtin.file:
path: /etc/letsencrypt/renewal-hooks/post
recurse: true
owner: root
group: root
mode: "755"
state: directory
- name: Copying renewal post- hook to renewal post- hook path
become: true
ansible.builtin.copy:
src: letsencrypt/renewal-hooks/post/up-dependents.sh
dest: /etc/letsencrypt/renewal-hooks/post/
owner: root
group: root
mode: "755"
force: true
backup: true
- name: Starting domain name registration with webroot option
when: "certbot.auth_method == 'webroot' and not certbot.containerized"
block: []
@@ -0,0 +1,65 @@
- name: Preparing non-containerized Crowdsec
when: not crowdsec.containerized
block:
- name: Changing the address and port of the Crowdsec server
become: true
ansible.builtin.lineinfile:
path: /etc/crowdsec/config.yaml
insertafter: EOF
regexp: "^ {4}listen_uri"
line: " listen_uri: localhost:{{ crowdsec.port }}"
owner: root
group: root
mode: "644"
- name: Changing the address of the Crowdsec Prometheus server
become: true
ansible.builtin.lineinfile:
path: /etc/crowdsec/config.yaml
regexp: "^ {2}listen_addr"
insertafter: EOF
line: " listen_addr: localhost"
owner: root
group: root
mode: "644"
- name: Changing target or expected address for credentials of the Crowdsec local API
become: true
ansible.builtin.lineinfile:
path: /etc/crowdsec/local_api_credentials.yaml
insertafter: EOF
regexp: "^url"
line: "url: http://localhost:{{ crowdsec.port }}"
owner: root
group: root
mode: "644"
- name: Restarting SystemD service
become: true
ansible.builtin.systemd_service:
name: crowdsec
scope: system
state: restarted
- name: Adding remediation component or bouncer
ansible.builtin.command:
cmd: "cscli bouncers add {{ item }}"
loop: "{{ crowdsec.bouncers }}"
- name: Installing Crowdsec collections
ansible.builtin.command:
cmd: "cscli collections install {{ item }}"
loop: "{{ crowdsec.colls }}"
- name: Installing Crowdsec parsers
ansible.builtin.command:
cmd: "cscli parsers install {{ item }}"
loop: "{{ crowdsec.parsers }}"
- name: Installing Crowdsec scenarios
ansible.builtin.command:
cmd: "cscli scenarios install {{ item }}"
loop: "{{ crowdsec.scenarios }}"
- name: Installing Crowdsec postoverflows
ansible.builtin.command:
cmd: "cscli postoverflows install {{ item }}"
loop: "{{ crowdsec.postoverflows }}"
- name: Restarting SystemD service
become: true
ansible.builtin.systemd_service:
name: crowdsec
scope: system
state: restarted
@@ -5,6 +5,7 @@
cmd: "echo ~{{ ansible_user }}"
register: ansible_user_home
- name: Linking binaries to directories already in PATH environment variable
ignore_errors: true
ansible.builtin.file:
src: "{{ ansible_user_home.stdout }}/downloads/archives/released/difftastic/{{ item }}"
dest: "{{ ansible_user_home.stdout }}/.local/bin/{{ item }}"
@@ -13,9 +14,10 @@
- difft
- name: Linking binaries to directories already in PATH environment variable
become: true
ignore_errors: true
ansible.builtin.file:
src: "{{ ansible_user_home.stdout }}/downloads/archives/released/difftastic/{{ item }}"
dest: "/usr/local/bin/{{ item }}"
dest: "/usr/bin/{{ item }}"
state: link
loop:
- difft
@@ -0,0 +1,159 @@
- name: Acquiring home of current user
when: ansible_facts["system"] == "Linux"
ansible.builtin.shell:
cmd: "echo ~{{ ansible_user }}"
register: ansible_user_home
- name: Acquiring current user
when: ansible_facts["system"] == "Linux"
ansible.builtin.shell:
cmd: "echo {{ ansible_user }}"
register: current_user
- name: Initializing DSNet
become: true
ansible.builtin.command:
cmd: dsnet init
- name: Running DSNet VPN service interface
become: true
ansible.builtin.command:
cmd: dsnet up
- name: Creating a directory to house VPN service client configurations
ansible.builtin.file:
path: "{{ ansible_user_home.stdout }}/.wg/authorized_clients.d"
recurse: true
owner: "{{ ansible_user }}"
group: "{{ ansible_user }}"
mode: "755"
state: directory
- name: Adding peer device for DSNet VPN service interface
become: true
ansible.builtin.shell:
cmd: "dsnet add {{ item.name }}{{ idx }} --owner {{ current_user.stdout }} --description 'For {{ item.name }}--{{ item.desc }}' --confirm > {{ ansible_user_home.stdout }}/.wg/authorized_clients.d/{{ item.name }}{{ (idx | string) }}.conf"
creates: "{{ ansible_user_home.stdout }}/.wg/authorized_clients.d/{{ item.name }}{{ idx }}.conf"
loop: "{{ vpn.clients }}"
loop_control:
index_var: idx
- name: Changing ownership of consequent DSNet VPN service client configurations
ansible.builtin.file:
path: "{{ ansible_user_home.stdout }}/.wg/authorized_clients.d/{{ item.name }}{{ (idx | string) }}.conf"
owner: "{{ ansible_user }}"
group: "{{ ansible_user }}"
mode: "600"
state: file
loop: "{{ vpn.clients }}"
loop_control:
index_var: idx
- name: Pausing to inquire about how to proceed
ansible.builtin.pause:
prompt: "Type \"fetch\" to get the DSNet VPN service client configuration files, or \"show\" to see their contents for manual copying instead"
echo: true
register: data_method
- name: Presenting DSNet VPN service client configuration files to control node for copying
when: data_method.user_input == "show"
block:
- name: Acquiring contents of DSNet VPN service client configuration files
ansible.builtin.slurp:
src: "{{ ansible_user_home.stdout }}/.wg/authorized_clients.d/{{ item.name }}{{ (idx | string) }}.conf"
loop: "{{ vpn.clients }}"
loop_control:
index_var: idx
register: vpn_client_configs
- name: Presenting contents of DSNet VPN service client configurations to control node
ansible.builtin.debug:
msg: "Copy this client configuration of the DSNet VPN service:\n {{ item.content | b64decode }}"
loop: "{{ vpn_client_configs.results }}"
- name: Giving opportunity to manually copy contents of DSNet VPN service client configuration files
ansible.builtin.pause:
- name: Providing DSNet VPN service client configuration files to control node machine
when: data_method.user_input == "fetch"
block:
- name: Informing user of inventory requirements for VPN clients
when: ((fqdn is undefined or fqdn == None) and item.name != fqdn) or not item.name in groups.homeserver
ansible.builtin.fail:
msg: The VPN client must be the name of an inventory host in a homeserver group
loop: "{{ vpn.clients }}"
- name: Dupliciating DSNet VPN service client configuration files to control node
when: ((fqdn is defined and fqdn != None) and item.name == fqdn) or item.name in groups.homeserver
ansible.builtin.fetch:
src: "{{ ansible_user_home.stdout }}/.wg/authorized_clients.d/{{ item.name }}{{ (idx | string) }}.conf"
dest: "./.tmp/{{ inventory_hostname }}-dsnet/"
flat: true
loop: "{{ vpn.clients }}"
loop_control:
index_var: idx
- name: Dupliciating DSNet VPN service client configuration files to control node
ansible.builtin.fetch:
src: "{{ ansible_user_home.stdout }}/.wg/authorized_clients.d/{{ item.name }}{{ (idx | string) }}.conf"
dest: "./roles/init-server/files/user/wg/containerized/{{ item.name }}{{ (idx | string) }}.conf"
flat: true
loop: "{{ vpn.clients }}"
loop_control:
index_var: idx
- name: Informing control node of acquired files
ansible.builtin.debug:
msg: "The DSNet VPS service client configuration files have been duplicated to \"{{ item }}\" at the control node."
loop:
- "./.tmp/{{ inventory_hostname }}-dsnet/"
- "./roles/init-server/files/user/wg/"
- name: Giving control node user time to read the aforementiioned message
ansible.builtin.pause:
seconds: 30
- name: Ensuring IP forwarding is allowed
become: true
ansible.posix.sysctl:
name: "net.{{ item }}.ip_forward"
value: "1"
sysctl_set: true
state: present
loop:
- ipv4
# - ipv6
- name: Creating SystemD unit for placing up DSNet interface
become: true
ansible.builtin.copy:
src: systemd/system/dsnet.service
dest: /etc/systemd/system/dsnet.service
owner: root
group: root
force: true
backup: true
- name: Reloading SystemD and enabling DSNet interface
become: true
ansible.builtin.systemd_service:
name: dsnet
enabled: true
daemon_reload: true
- name: Copying script for DSNet iptables rules
become: true
ansible.builtin.template:
src: usr/local/bin/dsnet-forward.sh.j2
dest: /usr/local/bin/dsnet-forward.sh
owner: root
group: root
mode: "755"
force: true
backup: true
- name: Creating SystemD unit for DSNet iptables rules
become: true
ansible.builtin.copy:
src: systemd/system/thrunet.service
dest: /etc/systemd/system/thrunet.service
owner: root
group: root
force: true
backup: true
- name: Reloading SystemD
become: true
ansible.builtin.systemd_service:
name: thrunet
daemon_reload: true
- name: Opening port 51820
become: true
ansible.builtin.iptables:
chain: INPUT
protocol: "{{ item }}"
destination_port: 51820
jump: ACCEPT
comment: Open up port 51820
loop:
- udp
- tcp
@@ -0,0 +1,100 @@
---
- name: Acquiring home of current user
when: ansible_facts["system"] == "Linux"
ansible.builtin.shell:
cmd: "echo ~{{ ansible_user }}"
register: ansible_user_home
- name: Acquiring current user
when: ansible_facts["system"] == "Linux"
ansible.builtin.shell:
cmd: "echo {{ ansible_user }}"
register: current_user
- name: Informing user of requirement of two main domains
when: (certbot.domains | length) < 2 or (certbot.domains | length) > 2
ansible.builtin.fail:
msg: Only two domains allowed and required
- name: Informing user of requirement at least one wildcard
when: (certbot.domains | select("regex", "^\\*\\.") | list | length) == 0
ansible.builtin.fail:
msg: At least one of the FQDNs must have a wildcard
# - name: Setting the FQDN for development
# when: compose.mode == "dev"
# ansible.builtin.set_fact:
# web_fqdn: "{{ (certbot.domains | map('regex_replace', '\\.([^\\.]*)$', '.test') | reject('regex', '^\\*\\.') | list)[0] }}"
- name: Setting the FQDN
# when: compose.mode == "prod"
ansible.builtin.set_fact:
web_fqdn: "{{ (certbot.domains | reject('regex', '^\\*\\.') | list)[0] }}"
- name: Configuring Headscale
become: true
ansible.builtin.template:
src: headscale/config.yaml.j2
dest: /etc/headscale/config.yaml
owner: root
group: root
mode: "644"
force: true
backup: true
# validate: "headscale configtest"
- name: Starting SystemD service
become: true
ansible.builtin.systemd_service:
name: headscale
scope: system
enabled: true
state: started
- name: Registering a headscale user
become: true
ansible.builtin.command:
cmd: "headscale users create {{ headscale.users.admin.username }} -d '{{ headscale.users.admin.dname }}' -e '{{ headscale.users.admin.email }}'"
register: headscale_registration
changed_when:
- "'User created' in headscale_registration.stdout"
- name: Creating an authentication key for this registered headscale user
become: true
ansible.builtin.command:
cmd: "headscale preauthkeys create -e 24h -u 1"
register: tailscale_admin_authkey
- name: Pausing to inquire about how to proceed
ansible.builtin.pause:
prompt: "Type \"fetch\" to get the DSNet VPN service client configuration files, or \"show\" to see their contents for manual copying instead"
echo: true
register: data_method
- name: Choosing Headscale authentication key to control node for copying
when: data_method.user_input == "show"
block:
- name: Presenting Headscale authentication key to Control Node
ansible.builtin.debug:
msg: "Copy this client configuration of the headscale service:\n {{ tailscale_admin_authkey.stdout }}"
- name: Giving opportunity to manually copy Headscale authentication key
ansible.builtin.pause:
- name: Choosing Headscale service client configuration files to control node machine
when: data_method.user_input == "fetch"
block:
- name: Creating temporary file on managed node that stores Headscale authentication key
ansible.builtin.copy:
content: "{{ tailscale_admin_authkey.stdout }}"
dest: "/tmp/headscale.key"
owner: "{{ ansible_user }}"
group: "{{ ansible_user }}"
mode: "644"
register: tailscale_admin_authkey_file
- name: Placing Headscale authentication key into file on control node
ansible.builtin.fetch:
src: "{{ tailscale_admin_authkey_file.dest }}"
dest: "./.tmp/{{ inventory_hostname }}-{{ headscale.users.admin.username }}@headscale/headscale.key"
flat: true
- name: Placing Headscale authentication key into file on control node
ansible.builtin.fetch:
src: "{{ tailscale_admin_authkey_file.dest }}"
dest: "./roles/init-server/files/{{ item.name }}-{{ headscale.users.admin.username }}@headscale/headscale{{ (idx | string) }}.key"
flat: true
loop: "{{ headscale.clients }}"
loop_control:
index_var: idx
- name: Informing control node of acquired files
ansible.builtin.debug:
msg: "The Headscale authentication key files have been duplicated to './.tmp/{{ inventory_hostname }}-{{ headscale.users.admin.username }}@headscale/headscale.key' at the control node."
- name: Giving control node user time to read the aforementiioned message
ansible.builtin.pause:
seconds: 30
@@ -15,18 +15,19 @@
ansible.builtin.shell:
cmd: "{{ ansible_user_home.stdout }}/.local/bin/julia-install.sh --yes"
creates: "{{ ansible_user_home.stdout }}/.juliaup/bin"
async: 900
poll: 5
- name: Linking binaries to directories already in PATH environment variable
become: true
ignore_errors: true
ansible.builtin.file:
src: "{{ ansible_user_home.stdout }}/.juliaup/bin/{{ item }}"
dest: "/usr/local/bin/{{ item }}"
owner: root
group: root
dest: "/usr/bin/{{ item }}"
state: link
loop:
- julia
- julialauncher
- juliaup
- name: Reboot machine for shell environment change
ansible.builtin.reboot:
msg: Rebooting machine
# - name: Reboot machine for shell environment change
# ansible.builtin.reboot:
# msg: Rebooting machine
@@ -0,0 +1,110 @@
---
- name: Acquiring home of current user
when: ansible_facts["system"] == "Linux"
ansible.builtin.shell:
cmd: "echo ~{{ ansible_user }}"
register: ansible_user_home
- name: Acquiring home of current user
when: ansible_facts["system"] == "Linux"
ansible.builtin.shell:
cmd: "echo {{ ansible_user }}"
register: current_user
- name: Setting up TOTP using Google Authenticator
become: true
become_user: "{{ current_user.stdout }}"
ansible.builtin.command:
argv:
- "google-authenticator"
- "-t"
- "-d"
- "-f"
- "-C"
- "-q"
- "-l {{ ansible_user }}@{{ inventory_hostname }}"
- "-i {{ inventory_hostname }}"
- "--qr-mode=NONE"
- "-r 7"
- "-R 300"
- "-w 9"
creates: "{{ ansible_user_home.stdout }}/.google_authenticator"
- name: Pausing to inquire about how to proceed
ansible.builtin.pause:
prompt: "Type \"fetch\" to get the TOTP secret and backup codes, or \"show\" to see it for manual copying instead"
echo: true
register: data_method
- name: Choosing to present TOTP secret and backup codes to Control Node
when: data_method.user_input == "show"
block:
- name: Acquiring contents of file containing TOTP secret and backup codes
ansible.builtin.slurp:
src: "{{ ansible_user_home.stdout }}/.google_authenticator"
register: totp_token
- name: Presenting TOTP secret and backup codes to Control Node
ansible.builtin.debug:
msg: "Make sure to store the following TOTP secret and backup codes for Google Authenticator:\n {{ totp_token.content | b64decode }}"
- name: Giving opportunity to manually copy TOTP secret and backup codes
ansible.builtin.pause:
- name: Choosing to provide file on control node containing TOTP secret and backup codes
when: data_method.user_input == "fetch"
block:
- name: Placing TOTP secret and backup codes into file on control node
ansible.builtin.fetch:
src: "{{ ansible_user_home.stdout }}/.google_authenticator"
dest: "./.tmp/{{ inventory_hostname }}-google-auth/google_auth.secret"
flat: true
- name: Informing control node of acquired files
ansible.builtin.debug:
msg: "The TOTP secret and backup codes file has been duplicated to './.tmp/{{ inventory_hostname }}-google-auth/google-auth.secret' at the control node."
- name: Giving control node user time to read the aforementiioned message
ansible.builtin.pause:
seconds: 30
- name: Adding Google Authenticator OTP module PAM authentication line for SSH
become: true
ansible.builtin.lineinfile:
path: /etc/pam.d/sshd
line: "auth sufficient pam_google_authenticator.so nullok"
insertafter: "include common-auth$"
owner: root
group: root
mode: "644"
- name: Prioritizing authentication methods in SSH
become: true
ansible.builtin.lineinfile:
path: /etc/ssh/sshd_config.d/auth.conf
line: "KbdInteractiveAuthentication yes # enable if implementing TOTP 2FA"
regexp: "^KbdInteractiveAuthentication"
owner: root
group: root
mode: "644"
- name: Prioritizing authentication methods in SSH
become: true
ansible.builtin.lineinfile:
path: /etc/ssh/sshd_config.d/auth.conf
line: "AuthenticationMethods publickey keyboard-interactive:pam"
insertafter: "^KbdInteractiveAuthentication"
owner: root
group: root
mode: "644"
# - name: Adding Google Authenticator OTP module PAM authentication line for SSH
# become: true
# ansible.builtin.lineinfile:
# path: /etc/pam.d/sudo
# line: "auth required pam_google_authenticator.so nullok"
# insertafter: "include common-auth$"
# owner: root
# group: root
# mode: "644"
# - name: Adding Google Authenticator OTP module PAM authentication line for SSH
# become: true
# ansible.builtin.lineinfile:
# path: /etc/pam.d/su
# line: "auth required pam_google_authenticator.so nullok"
# insertafter: "include common-auth$"
# owner: root
# group: root
# mode: "644"
- name: Restarting SystemD SSH service
become: true
ansible.builtin.systemd_service:
name: sshd
state: restarted
@@ -8,15 +8,7 @@
when: ansible_facts["system"] == "Linux"
ansible.builtin.shell:
cmd: "echo {{ ansible_user }}"
- name: Copy system ViM configuration to home directory
become: true
ansible.builtin.copy:
remote_src: /root/.vimrc
dest: "{{ ansible_user_home.stdout }}/.vimrc"
owner: "{{ ansible_user }}"
group: "{{ ansible_user }}"
mode: "644"
state: present
register: current_user
- name: Creating directory tree for NeoViM configuration files
ansible.builtin.file:
path: "{{ ansible_user_home.stdout }}/.config/nvim"
@@ -33,4 +25,5 @@
owner: "{{ ansible_user }}"
group: "{{ ansible_user }}"
mode: "644"
state: present
force: true
backup: true
+19 -26
View File
@@ -18,78 +18,71 @@
ansible.builtin.set_fact:
prebuilt_nodepaths: "{{ prebuilt_nodes.files | map(attribute='path') }}"
- name: Linking binaries to directories already in PATH environment variable
ignore_errors: true
ansible.builtin.file:
src: "{{ item[0] }}/bin/{{ item[1] }}"
dest: "/usr/local/bin/{{ item[1] }}"
owner: root
group: root
dest: "/usr/bin/{{ item[1] }}"
state: link
loop: "{{ prebuilt_nodepaths | product(['node']) }}"
- name: Linking binaries to directories already in PATH environment variable
ignore_errors: true
ansible.builtin.file:
src: "{{ item[0] }}/lib/node_modules/corepack/dist/{{ item[1] }}.js"
dest: "/usr/local/bin/{{ item[1] }}"
owner: root
group: root
dest: "/usr/bin/{{ item[1] }}"
state: link
loop: "{{ prebuilt_nodepaths | product(['corepack']) }}"
- name: Linking binaries to directories already in PATH environment variable
ignore_errors: true
ansible.builtin.file:
src: "{{ item[0] }}/lib/node_modules/npm/bin/{{ item[1] }}-cli.js"
dest: "/usr/local/bin/{{ item[1] }}"
owner: root
group: root
dest: "/usr/bin/{{ item[1] }}"
state: link
loop: "{{ prebuilt_nodepaths | product(['npm','npx']) }}"
- name: Link includes to directories already recognized by system
ignore_errors: true
ansible.builtin.file:
src: "{{ item[0] }}/include/{{ item[1] }}"
dest: "/usr/local/include/{{ item[1] }}"
owner: root
group: root
dest: "/usr/include/{{ item[1] }}"
state: link
loop: "{{ prebuilt_nodepaths | product(['node']) }}"
- name: Link requisite libraries to directories already recognized by system
ignore_errors: true
ansible.builtin.file:
src: "{{ item[0] }}/lib/{{ item[1] }}"
dest: "/usr/local/lib/{{ item[1] }}"
owner: root
group: root
dest: "/usr/lib/{{ item[1] }}"
state: link
loop: "{{ prebuilt_nodepaths | product(['node_modules']) }}"
- name: Create man1 subdirectory for man pages
ansible.builtin.file:
path: /usr/local/share/man/man1
path: /usr/share/man/man1
recurse: true
owner: root
group: root
mode: "644"
state: directory
- name: Link shared resources to directories already recognized by system
ignore_errors: true
ansible.builtin.file:
src: "{{ item[0] }}/share/man/man1/{{ item[1] }}.1"
dest: "/usr/local/share/man/man1/{{ item[1] }}.1"
owner: root
group: root
dest: "/usr/share/man/man1/{{ item[1] }}.1"
state: link
loop: "{{ prebuilt_nodepaths | product(['node']) }}"
- name: Create man1 subdirectory for man pages
ansible.builtin.file:
path: /usr/local/share/doc
path: /usr/share/doc
recurse: true
owner: root
group: root
mode: "644"
state: directory
- name: Link shared resources to directories already recognized by system
ignore_errors: true
ansible.builtin.file:
src: "{{ item[0] }}/share/doc/{{ item[1] }}"
dest: "/usr/local/share/doc/{{ item[1] }}"
owner: root
group: root
dest: "/usr/share/doc/{{ item[1] }}"
state: link
loop: "{{ prebuilt_nodepaths | product(['node']) }}"
- name: Reboot machine for shell environment change
ansible.builtin.reboot:
msg: Rebooting machine
# - name: Reboot machine for shell environment change
# ansible.builtin.reboot:
# msg: Rebooting machine
@@ -0,0 +1,65 @@
---
- name: Acquiring home of current user
when: ansible_facts["system"] == "Linux"
ansible.builtin.shell:
cmd: "echo ~{{ ansible_user }}"
register: ansible_user_home
- name: Acquiring current user
when: ansible_facts["system"] == "Linux"
ansible.builtin.shell:
cmd: "echo {{ ansible_user }}"
register: current_user
- name: Ensuring user namespace support is enabled
become: true
ansible.posix.sysctl:
name: kernel.unprivileged_userns_clone
value: "1"
sysctl_set: true
state: present
- name: Ensuring user namespace support is enabled
become: true
ansible.posix.sysctl:
name: net.ipv4.ip_unprivileged_port_start
value: "0"
sysctl_set: true
state: present
- name: Creating container directory
ansible.builtin.file:
path: "{{ ansible_user_home.stdout }}/.config/containers"
recurse: true
owner: "{{ ansible_user }}"
group: "{{ ansible_user }}"
mode: "755"
state: directory
- name: Configuring container storage
become: true
become_user: "{{ current_user.stdout }}"
ansible.builtin.template:
src: user/config/containers/storage.conf.j2
dest: "{{ ansible_user_home.stdout }}/.config/containers/storage.conf"
owner: "{{ ansible_user }}"
group: "{{ ansible_user }}"
mode: "644"
force: true
backup: true
- name: Configuring container image registries
become: true
become_user: "{{ current_user.stdout }}"
ansible.builtin.copy:
src: user/config/containers/registries.conf
dest: "{{ ansible_user_home.stdout }}/.config/containers/"
owner: "{{ ansible_user }}"
group: "{{ ansible_user }}"
mode: "644"
force: true
backup: true
# @NOTE https://oneuptime.com/blog/post/2026-01-27-podman-rootless/view#configuring-registries
# @NOTE https://github.com/podman-container-tools/podman/blob/main/docs/tutorials/rootless_tutorial.md#using-volumes
# @NOTE https://github.com/containers/podman-compose/issues/166#issuecomment-1550515230
- name: Enabling and starting SystemD unit service for automatic restart of containers/pods
become: true
ansible.builtin.systemd_service:
name: podman-restart
scope: system
enabled: true
state: started
@@ -4,23 +4,180 @@
ansible.builtin.shell:
cmd: "echo ~{{ ansible_user }}"
register: ansible_user_home
- name: Acquiring home of current user
when: ansible_facts["system"] == "Linux"
ansible.builtin.shell:
cmd: "echo {{ ansible_user }}"
register: current_user
- name: Informing user of requirement of two main domains
when: (certbot.domains | length) < 2 or (certbot.domains | length) > 2
ansible.builtin.fail:
msg: Only two domains allowed and required
- name: Informing user of requirement at least one wildcard
when: (certbot.domains | select("regex", "^\\*\\.") | list | length) == 0
ansible.builtin.fail:
msg: At least one of the FQDNs must have a wildcard
# - name: Setting the FQDN for development
# when: compose.mode == "dev"
# ansible.builtin.set_fact:
# web_fqdn: "{{ (certbot.domains | map('regex_replace', '\\.([^\\.]*)$', '.test') | reject('regex', '^\\*\\.') | list)[0] }}"
- name: Setting the FQDN for production
# when: compose.mode == "prod"
ansible.builtin.set_fact:
web_fqdn: "{{ (certbot.domains | reject('regex', '^\\*\\.') | list)[0] }}"
- name: Linking repository to another path
ignore_errors: true
ansible.builtin.file:
src: "{{ ansible_user_home.stdout }}/repos/.foreign/quartz"
dest: "{{ ansible_user_home.stdout }}/repos/skato-quartz"
state: link
- name: Installing NodeJS dependencies of quartz software
become: true
become_user: "{{ current_user.stdout }}"
community.general.npm:
path: "{{ ansible_user_home.stdout }}/repos/.foreign/quartz"
state: latest
- name: Configuring quartz software
block:
- name: Creating path for Quartz content files (path for Obsidian vaults)
ansible.builtin.file:
path: "{{ ansible_user_home.stdout }}/journal/notes"
recurse: true
owner: "{{ ansible_user }}"
group: "{{ ansible_user }}"
mode: "755"
state: directory
- name: Creating some initial text content for Quartz
become: true
become_user: "{{ current_user.stdout }}"
ansible.builtin.template:
src: "user/journal/notes/index.md.j2"
dest: "{{ ansible_user_home.stdout }}/journal/notes/index.md"
owner: "{{ ansible_user }}"
group: "{{ ansible_user }}"
mode: "644"
backup: true
- name: Creating some initial image content for Quartz
become: true
become_user: "{{ current_user.stdout }}"
ansible.builtin.copy:
src: user/journal/mythe-sisyphus-klein.png
dest: "{{ ansible_user_home.stdout }}/journal/notes/mythe-sisyphus-klein.png"
owner: "{{ ansible_user }}"
group: "{{ ansible_user }}"
mode: "644"
backup: true
- name: Initializing quartz website
become: true
become_user: "{{ current_user.stdout }}"
ansible.builtin.command:
argv:
- npx
- "-y"
- quartz
- create
- "-b"
- notes.{{ web_fqdn }}"
- "-t"
- obsidian
- "-s"
- "{{ ansible_user_home.stdout }}/journal/notes"
- "-X"
- symlink
chdir: "{{ ansible_user_home.stdout }}/repos/.foreign/quartz"
cmd: npx quartz create
creates: "{{ ansible_user_home.stdout }}/repos/.foreign/quartz/content/index.md"
- name: Installing quartz plugins referenced in website template
become: true
become_user: "{{ current_user.stdout }}"
ansible.builtin.command:
argv:
- npx
- "-y"
- quartz
- plugin
- install
chdir: "{{ ansible_user_home.stdout }}/repos/.foreign/quartz"
cmd: npx quartz plugin install --from-config
# - name: Starting quartz site web server
# ansible.builtin.command:
# chdir: "{{ ansible_user_home.stdout }}/repos/.foreign/quartz"
# cmd: npx quartz build --serve
# register: stdout
# changed_when: stdout
- name: Starting quartz site web server
become: true
become_user: "{{ current_user.stdout }}"
ansible.builtin.command:
argv:
- npx
- "-y"
- quartz
- build
- "-o"
- "{{ ansible_user_home.stdout }}/srv/notes.{{ web_fqdn }}"
- "--concurrency"
- "3"
chdir: "{{ ansible_user_home.stdout }}/repos/.foreign/quartz"
creates: "{{ ansible_user_home.stdout }}/srv/notes.{{ web_fqdn }}/index.html"
- name: Creating directory for caddy configuration
ansible.builtin.file:
path: "{{ ansible_user_home.stdout }}/.config/caddy"
recurse: true
owner: "{{ ansible_user }}"
group: "{{ ansible_user }}"
state: directory
- name: Adding Caddyfile subdomain entry
when: "(caddy is defined and caddy != None) and caddy.containerized and mode == 'prod'"
ansible.builtin.blockinfile:
path: "{{ ansible_user_home.stdout }}/.config/caddy/Caddyfile"
block: |
notes.{{ web_fqdn }} {
respond 503
# root /srv/notes.{{ web_fqdn }}
file_server
encode gzip
handle_errors {
root /srv/{{ web_fqdn }}
rewrite /error/{err.status_code}.html
templates
}
}
prepend_newline: true
marker: "# <-- {mark} ANSIBLE MANAGED NOTES DOMAIN -->"
owner: "{{ ansible_user }}"
group: "{{ ansible_user }}"
mode: "644"
create: true
state: present
- name: Adding Caddyfile subdomain entry
when: "(caddy is defined and caddy != None) and caddy.containerized and (mode == 'dev' or caddy.scheme == 'http')"
ansible.builtin.blockinfile:
path: "{{ ansible_user_home.stdout }}/.config/caddy/Caddyfile"
block: |
http://notes.{{ web_fqdn }} {
respond 503
# root /srv/notes.{{ web_fqdn }}
file_server
encode gzip
handle_errors {
root /srv/{{ web_fqdn }}
rewrite /error/{err.status_code}.html
templates
}
}
prepend_newline: true
marker: "# <-- {mark} ANSIBLE MANAGED NOTES DOMAIN -->"
owner: "{{ ansible_user }}"
group: "{{ ansible_user }}"
mode: "644"
create: true
state: present
- name: Checking the status of podman containers
ansible.builtin.command:
argv:
- podman
- ps
register: podman_status
- name: Restarting webserver / reverse proxy container
become: true
become_user: "{{ current_user.stdout }}"
when: "'revproxy0' in podman_status.stdout and (caddy is defined and caddy != None) and caddy.containerized"
containers.podman.podman_container:
name: "{{ compose.containers.webserver.name }}"
state: started
force_restart: true
@@ -15,13 +15,16 @@
ansible.builtin.shell:
cmd: "{{ ansible_user_home.stdout }}/.local/bin/radicle-install.sh"
creates: "{{ ansible_user_home.stdout }}/.radicle"
async: 600
poll: 5
- name: Bootstrapping Radicle
become: true
block:
- name: Linking binaries to directories already in PATH environment variable
ignore_errors: true
ansible.builtin.file:
src: "{{ ansible_user_home.stdout }}/.radicle/bin/{{ item }}"
dest: "/usr/local/bin/{{ item }}"
dest: "/usr/bin/{{ item }}"
state: link
loop:
- rad
@@ -29,16 +32,17 @@
- git-remote-rad
- name: Create man1 subdirectory for man pages
ansible.builtin.file:
path: /usr/local/share/man/man1
path: /usr/share/man/man1
recurse: true
owner: root
group: root
mode: "644"
state: directory
- name: Link manpages to Linux manpage directories
ignore_errors: true
ansible.builtin.file:
src: "{{ ansible_user_home.stdout }}/.radicle/man/man1/{{ item }}.1"
dest: "/usr/local/share/man/man1/{{ item }}.1"
dest: "/usr/share/man/man1/{{ item }}.1"
state: link
loop:
- rad
@@ -46,6 +50,6 @@
- git-remote-rad
- rad-id
- rad-patch
- name: Reboot machine for shell environment change
ansible.builtin.reboot:
msg: Rebooting machine
# - name: Reboot machine for shell environment change
# ansible.builtin.reboot:
# msg: Rebooting machine
@@ -15,14 +15,17 @@
ansible.builtin.shell:
cmd: "{{ ansible_user_home.stdout }}/.local/bin/rustup-install.sh -yq"
creates: "{{ ansible_user_home.stdout }}/.cargo/bin"
async: 600
poll: 5
- name: Linking binaries to directories already in PATH environment variable
become: true
ignore_errors: true
ansible.builtin.file:
src: "{{ ansible_user_home.stdout }}/.cargo/bin/{{ item }}"
dest: "/usr/local/bin/{{ item }}"
dest: "/usr/bin/{{ item }}"
state: link
loop:
- rustup
- name: Reboot machine for shell environment change
ansible.builtin.reboot:
msg: Rebooting machine
# - name: Reboot machine for shell environment change
# ansible.builtin.reboot:
# msg: Rebooting machine
@@ -0,0 +1,171 @@
---
- name: Acquiring home of current user
when: ansible_facts["system"] == "Linux"
ansible.builtin.shell:
cmd: "echo ~{{ ansible_user }}"
register: ansible_user_home
- name: Acquiring home of current user
when: ansible_facts["system"] == "Linux"
ansible.builtin.shell:
cmd: "echo {{ ansible_user }}"
register: current_user
- name: Informing user of requirement of two main domains
when: (certbot.domains | length) < 2 or (certbot.domains | length) > 2
ansible.builtin.fail:
msg: Only two domains allowed and required
- name: Informing user of requirement at least one wildcard
when: (certbot.domains | select("regex", "^\\*\\.") | list | length) == 0
ansible.builtin.fail:
msg: At least one of the FQDNs must have a wildcard
# - name: Setting the FQDN for development
# when: compose.mode == "dev"
# ansible.builtin.set_fact:
# web_fqdn: "{{ (certbot.domains | map('regex_replace', '\\.([^\\.]*)$', '.test') | reject('regex', '^\\*\\.') | list)[0] }}"
- name: Setting the FQDN
# when: compose.mode == "prod"
ansible.builtin.set_fact:
web_fqdn: "{{ (certbot.domains | reject('regex', '^\\*\\.') | list)[0] }}"
- name: Moving git repository from initial path
block:
- name: Recursively copying directory and its contents to elsewhere
ansible.builtin.copy:
src: "{{ ansible_user_home.stdout }}/repos/.foreign/{{ source_code.repos.blog.name }}"
remote_src: true
dest: "{{ ansible_user_home.stdout }}/repos/"
owner: "{{ ansible_user }}"
group: "{{ ansible_user }}"
register: new_blog_path
- name: Deleting directory at previous path
ansible.builtin.file:
path: "{{ ansible_user_home.stdout }}/repos/.foreign/{{ source_code.repos.blog.name }}"
state: absent
- name: Creating directory for new bare repository
ansible.builtin.file:
path: "{{ ansible_user_home.stdout }}/src"
recurse: true
owner: "{{ ansible_user }}"
group: "{{ ansible_user }}"
state: directory
- name: Creating bare repository
ansible.builtin.command:
cmd: "git init --bare {{ source_code.repos.blog.name }}.git"
chdir: "{{ ansible_user_home.stdout }}/src"
creates: "{{ ansible_user_home.stdout }}/src/{{ source_code.repos.blog.name }}.git"
- name: Running Hugo blog
when: source_code.repos.blog.run
block:
- name: Creating path for Hugo content files
when: caddy.containerized and (compose.containers.webserver is defined or compose.containers.webserver != None)
ansible.builtin.file:
path: "{{ ansible_user_home.stdout }}/journal/blog"
recurse: true
owner: "{{ ansible_user }}"
group: "{{ ansible_user }}"
mode: "755"
state: directory
- name: Creating some initial text content for Hugo
become: true
become_user: "{{ current_user.stdout }}"
when: caddy.containerized and (compose.containers.webserver is defined or compose.containers.webserver != None)
ansible.builtin.template:
src: "user/journal/blog/_index.md.j2"
dest: "{{ ansible_user_home.stdout }}/journal/blog/index.md"
owner: "{{ ansible_user }}"
group: "{{ ansible_user }}"
mode: "644"
backup: true
- name: Creating some initial image content for Hugo
become: true
become_user: "{{ current_user.stdout }}"
when: caddy.containerized and (compose.containers.webserver is defined or compose.containers.webserver != None)
ansible.builtin.copy:
src: "user/journal/mythe-sisyphus-klein.png"
dest: "{{ ansible_user_home.stdout }}/journal/blog/mythe-sisyphus-klein.png"
owner: "{{ ansible_user }}"
group: "{{ ansible_user }}"
mode: "644"
backup: true
- name: Creating subdirectory for blog website root
when: caddy.containerized and (compose.containers.webserver is defined or compose.containers.webserver != None)
ansible.builtin.file:
path: "{{ ansible_user_home.stdout }}/srv/blog.{{ web_fqdn }}"
owner: "{{ ansible_user }}"
group: "{{ ansible_user }}"
mode: "755"
state: directory
- name: Build Hugo blog at additional webroot
when: caddy.containerized and (compose.containers.webserver is defined or compose.containers.webserver != None)
ansible.builtin.command:
argv:
- hugo
- "--quiet"
- "-b"
- "https://blog.{{ web_fqdn }}/"
- "-c"
- "{{ ansible_user_home.stdout }}/journal/blog"
- "-d"
- "{{ ansible_user_home.stdout }}/srv/blog.{{ web_fqdn }}"
- "--cleanDestinationDir"
chdir: "{{ new_blog_path.dest }}"
creates: "{{ ansible_user_home.stdout }}/srv/blog.{{ web_fqdn }}/index.html"
# @TODO write error Caddy template HTML file at the blog's web root
- name: Adding Caddyfile subdomain entry
when: caddy.containerized and mode == 'prod' and (compose.containers.webserver is defined or compose.containers.webserver != None)
ansible.builtin.blockinfile:
path: "{{ ansible_user_home.stdout }}/.config/caddy/Caddyfile"
block: |
blog.{{ web_fqdn }} {
respond 503
# root /srv/blog.{{ web_fqdn }}
file_server
encode gzip
handle_errors {
root /srv/{{ web_fqdn }}
rewrite /error/{err.status_code}.html
templates
}
}
prepend_newline: true
marker: "# <-- {mark} ANSIBLE MANAGED BLOG DOMAIN -->"
owner: "{{ ansible_user }}"
group: "{{ ansible_user }}"
mode: "644"
state: present
- name: Adding Caddyfile subdomain entry
when: caddy.containerized and (mode == 'dev' or caddy.scheme == 'http') and (compose.containers.webserver is defined or compose.containers.webserver != None)
ansible.builtin.blockinfile:
path: "{{ ansible_user_home.stdout }}/.config/caddy/Caddyfile"
block: |
http://blog.{{ web_fqdn }} {
respond 503
# root /srv/blog.{{ web_fqdn }}
file_server
encode gzip
handle_errors {
root /srv/{{ web_fqdn }}
rewrite /error/{err.status_code}.html
templates
}
}
prepend_newline: true
marker: "# <-- {mark} ANSIBLE MANAGED BLOG DOMAIN -->"
owner: "{{ ansible_user }}"
group: "{{ ansible_user }}"
mode: "644"
state: present
- name: Checking the status of podman containers
ansible.builtin.command:
argv:
- podman
- ps
register: podman_status
- name: Restarting webserver / reverse proxy container
become: true
become_user: "{{ current_user.stdout }}"
when: "'revproxy0' in podman_status.stdout and (caddy is defined and caddy != None) and caddy.containerized"
containers.podman.podman_container:
name: "{{ compose.containers.webserver.name }}"
state: started
force_restart: true
@@ -0,0 +1,45 @@
---
- name: Acquiring home of current user
when: ansible_facts["system"] == "Linux"
ansible.builtin.shell:
cmd: "echo ~{{ ansible_user }}"
register: ansible_user_home
- name: Acquiring home of current user
when: ansible_facts["system"] == "Linux"
ansible.builtin.shell:
cmd: "echo {{ ansible_user }}"
register: current_user
- name: Moving git repository from initial path
block:
- name: Recursively copying directory and its contents to elsewhere
ansible.builtin.copy:
src: "{{ ansible_user_home.stdout }}/repos/.foreign/{{ source_code.repos.blog_theme.name }}"
remote_src: true
dest: "{{ ansible_user_home.stdout }}/repos/"
owner: "{{ ansible_user }}"
group: "{{ ansible_user }}"
register: new_compose_path
- name: Deleting directory at previous path
ansible.builtin.file:
path: "{{ ansible_user_home.stdout }}/repos/.foreign/{{ source_code.repos.blog_theme.name }}"
state: absent
- name: Creating directory for new bare repository
ansible.builtin.file:
path: "{{ ansible_user_home.stdout }}/src"
recurse: true
owner: "{{ ansible_user }}"
group: "{{ ansible_user }}"
state: directory
- name: Creating bare repository
ansible.builtin.command:
cmd: "git init --bare {{ source_code.repos.blog_theme.name }}.git"
chdir: "{{ ansible_user_home.stdout }}/src"
creates: "{{ ansible_user_home.stdout }}/src/{{ source_code.repos.blog_theme.name }}.git"
- name: Running Hugo blog theme
when: source_code.repos.blog_theme.run
# become: true
# become_user: "{{ current_user.stdout }}"
block:
- name: Warning about lack of implementation
ansible.builtin.debug:
msg: Not yet implemented
@@ -0,0 +1,45 @@
---
- name: Acquiring home of current user
when: ansible_facts["system"] == "Linux"
ansible.builtin.shell:
cmd: "echo ~{{ ansible_user }}"
register: ansible_user_home
- name: Acquiring home of current user
when: ansible_facts["system"] == "Linux"
ansible.builtin.shell:
cmd: "echo {{ ansible_user }}"
register: current_user
- name: Moving git repository from initial path
block:
- name: Recursively copying directory and its contents to elsewhere
ansible.builtin.copy:
src: "{{ ansible_user_home.stdout }}/repos/.foreign/{{ source_code.repos.cli.name }}"
remote_src: true
dest: "{{ ansible_user_home.stdout }}/repos/"
owner: "{{ ansible_user }}"
group: "{{ ansible_user }}"
register: new_compose_path
- name: Deleting directory at previous path
ansible.builtin.file:
path: "{{ ansible_user_home.stdout }}/repos/.foreign/{{ source_code.repos.cli.name }}"
state: absent
- name: Creating directory for new bare repository
ansible.builtin.file:
path: "{{ ansible_user_home.stdout }}/src"
recurse: true
owner: "{{ ansible_user }}"
group: "{{ ansible_user }}"
state: directory
- name: Creating bare repository
ansible.builtin.command:
cmd: "git init --bare {{ source_code.repos.cli.name }}.git"
chdir: "{{ ansible_user_home.stdout }}/src"
creates: "{{ ansible_user_home.stdout }}/src/{{ source_code.repos.cli.name }}.git"
- name: Running CLI utility
when: source_code.repos.cli.run
# become: true
# become_user: "{{ current_user.stdout }}"
block:
- name: Warning about lack of implementation
ansible.builtin.debug:
msg: Not yet implemented
@@ -0,0 +1,836 @@
---
- name: Acquiring home of current user
when: ansible_facts["system"] == "Linux"
ansible.builtin.shell:
cmd: "echo ~{{ ansible_user }}"
register: ansible_user_home
- name: Acquiring home of current user
when: ansible_facts["system"] == "Linux"
ansible.builtin.shell:
cmd: "echo {{ ansible_user }}"
register: current_user
- name: Informing user of requirement of two main domains
when: (certbot.domains | length) < 2 or (certbot.domains | length) > 2
ansible.builtin.fail:
msg: Only two domains allowed and required
- name: Informing user of requirement at least one wildcard
when: (certbot.domains | select("regex", "^\\*\\.") | list | length) == 0
ansible.builtin.fail:
msg: At least one of the FQDNs must have a wildcard
# - name: Setting the FQDN for development
# when: compose.mode == "dev"
# ansible.builtin.set_fact:
# web_fqdn: "{{ (certbot.domains | map('regex_replace', '\\.([^\\.]*)$', '.test') | reject('regex', '^\\*\\.') | list)[0] }}"
- name: Setting the FQDN for production
# when: compose.mode == "prod"
ansible.builtin.set_fact:
web_fqdn: "{{ (certbot.domains | reject('regex', '^\\*\\.') | list)[0] }}"
- name: Moving git repository from initial path
block:
- name: Recursively copying directory and its contents to elsewhere
become: true
become_user: "{{ current_user.stdout }}"
ansible.builtin.copy:
src: "{{ ansible_user_home.stdout }}/repos/.foreign/{{ source_code.repos.compose.name }}"
remote_src: true
dest: "{{ ansible_user_home.stdout }}/repos/"
owner: "{{ ansible_user }}"
group: "{{ ansible_user }}"
register: new_compose_path
- name: Deleting directory at previous path
ansible.builtin.file:
path: "{{ ansible_user_home.stdout }}/repos/.foreign/{{ source_code.repos.compose.name }}"
state: absent
- name: Creating directory for new bare repository
ansible.builtin.file:
path: "{{ ansible_user_home.stdout }}/src"
recurse: true
owner: "{{ ansible_user }}"
group: "{{ ansible_user }}"
state: directory
- name: Creating bare repository
ansible.builtin.command:
cmd: "git init --bare {{ source_code.repos.compose.name }}.git"
chdir: "{{ ansible_user_home.stdout }}/src"
creates: "{{ ansible_user_home.stdout }}/src/{{ source_code.repos.compose.name }}.git"
- name: Creating required SMTP-related container secret
become: true
# become_user: "{{ current_user.stdout }}"
ansible.builtin.lineinfile:
line: "{{ email.smtp.password }}"
path: "{{ ansible_user_home.stdout }}/.podsecrets/email.pass"
insertafter: EOF
owner: "{{ ansible_user }}"
group: "{{ ansible_user }}"
mode: "644"
create: true
backup: true
- name: Creating environment file
become: true
become_user: "{{ current_user.stdout }}"
ansible.builtin.template:
src: user/all.env.j2
dest: "{{ ansible_user_home.stdout }}/.all.env"
owner: "{{ ansible_user }}"
group: "{{ ansible_user }}"
mode: "644"
force: true
backup: true
register: environ
- name: Creating environment file for email
become: true
become_user: "{{ current_user.stdout }}"
ansible.builtin.template:
src: user/email.env.j2
dest: "{{ ansible_user_home.stdout }}/.email.env"
owner: "{{ ansible_user }}"
group: "{{ ansible_user }}"
mode: "644"
force: true
backup: true
register: email_environ
- name: Creating a directory for container secrets
ansible.builtin.file:
path: "{{ ansible_user_home.stdout }}/.podsecrets"
recurse: true
owner: "{{ ansible_user }}"
group: "{{ ansible_user }}"
mode: "644"
state: directory
- name: Creating directory for DBMS server configuration
ansible.builtin.file:
path: "{{ ansible_user_home.stdout }}/.config/{{ item }}"
recurse: true
owner: "{{ ansible_user }}"
group: "{{ ansible_user }}"
mode: "755"
state: directory
loop:
- caddy
- letsencrypt
- mysql
- redis
- gitea
- opengist
- tailscale
- glance
- name: Creating directory for main website root
ansible.builtin.file:
path: "{{ ansible_user_home.stdout }}/srv/{{ web_fqdn }}"
recurse: true
owner: "{{ ansible_user }}"
group: "{{ ansible_user }}"
mode: "755"
state: directory
- name: Creating directory for caddy configuration
ansible.builtin.file:
path: "{{ ansible_user_home.stdout }}/.config/caddy"
recurse: true
owner: "{{ ansible_user }}"
group: "{{ ansible_user }}"
state: directory
# - name: Pulling all needed container images for Compose services
# become: true
# become_user: "{{ current_user.stdout }}"
# ansible.builtin.command:
# cmd: podman-compose pull
# chdir: "{{ new_compose_path.dest }}{{ source_code.repos.compose.rpath | default('/independent') }}"
- name: Calculating desired container states
block:
- name: Calculating desired container state for VPN client
ansible.builtin.set_fact:
# @TODO write case of at least one vpn client having a boolean attribute declaring whether it is a container
vpn_run: "{{ source_code.repos.compose.run and (vpn.clients | selectattr('name', 'in', web_fqdn) | length) == 1 and (compose.containers.vpn is defined and compose.containers.vpn != None) }}"
- name: Calculating desired container state for web server or reverse proxy server
ansible.builtin.set_fact:
server_run: "{{ source_code.repos.compose.run and caddy.containerized and (compose.containers.webserver is defined and compose.containers.webserver != None) }}"
- name: Calculatng desired container state for ACME challenge
ansible.builtin.set_fact:
ssl_run: "{{ server_run and certbot.containerized and (compose.containers.ssl is defined and compose.containers.ssl != None) }}"
- name: Calculating desired container state for database management
ansible.builtin.set_fact:
dbms_run: "{{ source_code.repos.compose.run and mysql.containerized and (compose.containers.db is defined and compose.containers.db != None) }}"
- name: Calculating desired container state for caching server
ansible.builtin.set_fact:
cache_run: "{{ source_code.repos.compose.run and redis.containerized and (compose.containers.cache is defined and compose.containers.cache != None) }}"
- name: Calculating desired container state for cloud server
ansible.builtin.set_fact:
cloud_run: "{{ source_code.repos.compose.run and nextcloud.containerized and (compose.containers.cloud is defined and compose.containers.cloud != None) }}"
- name: Calculating desired container state for forge server
ansible.builtin.set_fact:
forge_run: "{{ source_code.repos.compose.run and gitea.containerized and (compose.containers.forge is defined and compose.containers.forge != None ) }}"
- name: Calculating desired container state for pastebin server
ansible.builtin.set_fact:
pbin_run: "{{ source_code.repos.compose.run and gist.containerized and (compose.containers.pastebin is defined and compose.containers.pastebin != None) }}"
- name: Calculating desired container state for tailnet client
ansible.builtin.set_fact:
tail_run: "{{ source_code.repos.compose.run and tailscale.containerized and (compose.containers.tail is defined and compose.containers.tail != None) }}"
- name: Calculating desired container state for tailnet dashboard
ansible.builtin.set_fact:
dash_run: "{{ source_code.repos.compose.run and glance.containerized and (compose.containers.dash is defined and compose.containers.dash != None) }}"
- name: Running VPN client Compose files
when: vpn_run
block:
- name: Creating subdirectory for VPN client container specified by Compose file
ansible.builtin.file:
path: "{{ ansible_user_home.stdout }}/.wg/containerized"
recurse: true
owner: "{{ ansible_user }}"
group: "{{ ansible_user }}"
state: directory
- name: Copying VPN client configuration files
become: true
become_user: "{{ current_user.stdout }}"
when: ((fqdn is defined and fqdn != None) and item.name == fqdn) or item.name == inventory_hostname
ansible.builtin.copy:
src: "user/wg/containerized/{{ item.name }}{{ (idx | string) }}.conf"
dest: "{{ ansible_user_home.stdout }}/.wg/containerized/"
owner: "{{ ansible_user }}"
group: "{{ ansible_user }}"
mode: "644"
force: true
backup: true
loop: "{{ vpn.clients }}"
loop_control:
index_var: idx
- name: Creating and deploying VPN client container specified by Compose file
become: true
become_user: "{{ current_user.stdout }}"
when: source_code.repos.compose.run and (vpn.clients | selectattr("name", "in", web_fqdn) | length) == 1 and (compose.containers.vpn is defined and compose.containers.vpn != None)
ansible.builtin.command:
cmd: "podman-compose up -d {{ compose.containers.vpn.name }}"
chdir: "{{ new_compose_path.dest }}{{ source_code.repos.compose.rpath | default('/independent') }}"
- name: Opening port 51820
become: true
ansible.builtin.iptables:
chain: INPUT
protocol: "{{ item }}"
destination_port: 51820
jump: ACCEPT
comment: Open up port 51820
loop:
- udp
- tcp
- name: Spinning up database management container specified by Compose file
when: dbms_run
block:
- name: Creating required container secret for DBMS server container's root account
become: true
# become_user: "{{ current_user.stdout }}"
ansible.builtin.lineinfile:
line: "{{ mysql.password }}"
path: "{{ ansible_user_home.stdout }}/.podsecrets/root-mysql.pass"
insertafter: EOF
owner: "{{ ansible_user }}"
group: "{{ ansible_user }}"
mode: "644"
create: true
backup: true
- name: Creatng required container secret for DBMS server container's primary non-root account
become: true
# become_user: "{{ current_user.stdout }}"
ansible.builtin.lineinfile:
line: "{{ mysql.users.admin.password }}"
path: "{{ ansible_user_home.stdout }}/.podsecrets/user-mysql.pass"
insertafter: EOF
owner: "{{ ansible_user }}"
group: "{{ ansible_user }}"
mode: "644"
create: true
backup: true
- name: Creating environment file for DBMS server container specified in Compose file
become: true
become_user: "{{ current_user.stdout }}"
ansible.builtin.template:
src: user/mysql.env.j2
dest: "{{ ansible_user_home.stdout }}/.mysql.env"
owner: "{{ ansible_user }}"
group: "{{ ansible_user }}"
mode: "644"
force: true
backup: true
register: mysql_environ
- name: Creating and deploying DBMS server container specified by Compose file
become: true
become_user: "{{ current_user.stdout }}"
ansible.builtin.command:
cmd: "podman-compose --env-file {{ mysql_environ.dest }} --env-file {{ environ.dest }} up -d {{ compose.containers.db.name }}"
chdir: "{{ new_compose_path.dest }}{{ source_code.repos.compose.rpath | default('/independent') }}"
# @TODO see if manual creation of additional databases is necessary for the DBMS server container
# REDIS
- name: Spinning up caching container specified by Compose file
when: cache_run
block:
- name: Creating environment file for DBMS server container specified in Compose file
become: true
become_user: "{{ current_user.stdout }}"
ansible.builtin.template:
src: user/redis.env.j2
dest: "{{ ansible_user_home.stdout }}/.redis.env"
owner: "{{ ansible_user }}"
group: "{{ ansible_user }}"
mode: "644"
force: true
backup: true
register: redis_environ
- name: Creating and deploying cache server container specified by Compose file
become: true
become_user: "{{ current_user.stdout }}"
ansible.builtin.command:
cmd: "podman-compose --env-file {{ environ.dest }} up -d {{ compose.containers.cache.name }}"
chdir: "{{ new_compose_path.dest }}{{ source_code.repos.compose.rpath | default('/independent') }}"
- name: Spinning up cloud container specified by Compose file
when: cloud_run
block:
- name: Creating database necessary for cloud server container specified by Compose file
become: true
become_user: "{{ current_user.stdout }}"
when: dbms_run
containers.podman.podman_container_exec:
name: "{{ compose.containers.db.name }}"
argv:
- mysql
- "-u"
- "{{ mysql.users.admin.username }}"
- "-p{{ mysql.users.admin.password }}"
- "-e"
- "'CREATE DATABASE IF NOT EXISTS {{ mysql.users.admin.databases.nextcloud.name }};'"
detach: true
- name: Creating required cache-related container secret for cloud server container specified by Compose file
become: true
# become_user: "{{ current_user.stdout }}"
when: cache_run
ansible.builtin.lineinfile:
line: "{{ redis.password }}"
dest: "{{ ansible_user_home.stdout }}/.podsecrets/redis.pass"
insertafter: EOF
owner: "{{ ansible_user }}"
group: "{{ ansible_user }}"
mode: "644"
create: true
backup: true
- name: Creating environment file for cloud server container specified by Compose file
become: true
become_user: "{{ current_user.stdout }}"
ansible.builtin.template:
src: user/nextcloud.env.j2
dest: "{{ ansible_user_home.stdout }}/.nextcloud.env"
owner: "{{ ansible_user }}"
group: "{{ ansible_user }}"
mode: "644"
force: true
backup: true
register: nextcloud_environ
- name: Creating and deploying DBMS server container specified by Compose file
become: true
become_user: "{{ current_user.stdout }}"
ansible.builtin.command:
cmd: "podman-compose --env-file {{ mysql_environ.dest }} --env-file {{ email_environ.dest }} --env-file {{ nextcloud_environ.dest }} --env-file {{ environ.dest }} up -d {{ compose.containers.cloud.name }}"
chdir: "{{ new_compose_path.dest }}{{ source_code.repos.compose.rpath | default('/independent') }}"
- name: Adding Caddyfile subdomain entry
when: "server_run and mode == 'prod'"
ansible.builtin.blockinfile:
path: "{{ ansible_user_home.stdout }}/.config/caddy/Caddyfile"
block: |
{{ nextcloud.subdomain }}.{{ web_fqdn }} {
root /srv/{{ nextcloud.subdomain }}.{{ web_fqdn }}
file_server
php_fastcgi localhost:9000
handle_errors {
root /srv/{{ web_fqdn }}
rewrite /error/{err.status_code}.html
templates
encode gzip
}
}
prepend_newline: true
marker: "# <-- {mark} ANSIBLE MANAGED CLOUD DOMAIN -->"
owner: "{{ ansible_user }}"
group: "{{ ansible_user }}"
create: true
mode: "644"
state: present
- name: Adding Caddyfile subdomain entry
when: "server_run and (mode == 'dev' or caddy.scheme == 'http')"
ansible.builtin.blockinfile:
path: "{{ ansible_user_home.stdout }}/.config/caddy/Caddyfile"
block: |
http://{{ nextcloud.subdomain }}.{{ web_fqdn }} {
root /srv/{{ nextcloud.subdomain }}.{{ web_fqdn }}
file_server
php_fastcgi localhost:9000
handle_errors {
root /srv/{{ web_fqdn }}
rewrite /error/{err.status_code}.html
templates
encode gzip
}
}
prepend_newline: true
marker: "# <-- {mark} ANSIBLE MANAGED CLOUD DOMAIN -->"
owner: "{{ ansible_user }}"
group: "{{ ansible_user }}"
create: true
mode: "644"
state: present
# - name: Restarting webserver / reverse proxy container
# become: true
# become_user: "{{ current_user.stdout }}"
# when: server_run
# containers.podman.podman_container:
# name: "{{ compose.containers.webserver.name }}"
# state: started
# force_restart: true
- name: Spinning up forge container specified by Compose file
when: forge_run
block:
- name: Creating database necessary for forge server container specified by Compose file
become: true
become_user: "{{ current_user.stdout }}"
when: dbms_run
containers.podman.podman_container_exec:
name: "{{ compose.containers.db.name }}"
argv:
- mysql
- "-u"
- "{{ mysql.users.admin.username }}"
- "-p{{ mysql.users.admin.password }}"
- "-e"
- "'CREATE DATABASE IF NOT EXISTS {{ mysql.users.admin.databases.gitea.name }};'"
detach: true
- name: Creating environment file for forge server container specified by Compose file
become: true
become_user: "{{ current_user.stdout }}"
ansible.builtin.template:
src: user/gitea.env.j2
dest: "{{ ansible_user_home.stdout }}/.gitea.env"
owner: "{{ ansible_user }}"
group: "{{ ansible_user }}"
mode: "644"
force: true
backup: true
register: gitea_environ
- name: Creating and deploying forge server container specified by Compose file
become: true
become_user: "{{ current_user.stdout }}"
ansible.builtin.command:
cmd: "podman-compose --env-file {{ mysql_environ.dest }} --env-file {{ email_environ.dest }} --env-file {{ gitea_environ.dest }} --env-file {{ environ.dest }} up -d {{ compose.containers.forge.name }}"
chdir: "{{ new_compose_path.dest }}{{ source_code.repos.compose.rpath | default('/independent') }}"
- name: Adding Caddyfile subdomain entry
when: "server_run and mode == 'prod'"
ansible.builtin.blockinfile:
path: "{{ ansible_user_home.stdout }}/.config/caddy/Caddyfile"
block: |
{{ gitea.subdomain }}.{{ web_fqdn }} {
reverse_proxy localhost:3000
handle_errors {
root /srv/{{ web_fqdn }}
rewrite /error/{err.status_code}.html
file_server
templates
encode gzip
}
}
prepend_newline: true
marker: "# <-- {mark} ANSIBLE MANAGED CLOUD DOMAIN -->"
owner: "{{ ansible_user }}"
group: "{{ ansible_user }}"
create: true
mode: "644"
state: present
- name: Adding Caddyfile subdomain entry
when: "server_run and (mode == 'dev' or caddy.scheme == 'http')"
ansible.builtin.blockinfile:
path: "{{ ansible_user_home.stdout }}/.config/caddy/Caddyfile"
block: |
http://{{ gitea.subdomain }}.{{ web_fqdn }} {
reverse_proxy localhost:3000
handle_errors {
root /srv/{{ web_fqdn }}
rewrite /error/{err.status_code}.html
file_server
templates
encode gzip
}
}
prepend_newline: true
marker: "# <-- {mark} ANSIBLE MANAGED CLOUD DOMAIN -->"
owner: "{{ ansible_user }}"
group: "{{ ansible_user }}"
create: true
mode: "644"
state: present
# - name: Restarting webserver / reverse proxy container
# become: true
# become_user: "{{ current_user.stdout }}"
# when: server_run
# containers.podman.podman_container:
# name: "{{ compose.containers.webserver.name }}"
# state: started
# force_restart: true
- name: Spinning up pastebin container specified by Compose file
when: pbin_run
block:
- name: Creating database necessary for pastebin server container specified by Compose file
become: true
become_user: "{{ current_user.stdout }}"
when: dbms_run
containers.podman.podman_container_exec:
name: "{{ compose.containers.db.name }}"
argv:
- mysql
- "-u"
- "{{ mysql.users.admin.username }}"
- "-p{{ mysql.users.admin.password }}"
- "-e"
- "'CREATE DATABASE IF NOT EXISTS {{ mysql.users.admin.databases.opengist.name }};'"
detach: true
- name: Creating environment file for pastebin server container specified by Compose file
become: true
become_user: "{{ current_user.stdout }}"
when: pbin_run
ansible.builtin.template:
src: user/opengist.env.j2
dest: "{{ ansible_user_home.stdout }}/.opengist.env"
owner: "{{ ansible_user }}"
group: "{{ ansible_user }}"
mode: "644"
force: true
backup: true
register: opengist_environ
- name: Creating gist configuration file
become: true
become_user: "{{ current_user.stdout }}"
when: pbin_run
ansible.builtin.template:
src: user/config/opengist/config.yml.j2
dest: "{{ ansible_user_home.stdout }}/.config/opengist/config.yml"
owner: "{{ ansible_user }}"
group: "{{ ansible_user }}"
mode: "644"
force: true
backup: true
- name: Creating and deploying pastebin server container specified by Compose file
become: true
become_user: "{{ current_user.stdout }}"
when: pbin_run
ansible.builtin.command:
cmd: "podman-compose --env-file {{ opengist_environ.dest }} --env-file {{ mysql_environ.dest }} --env-file {{ environ.dest }} up -d {{ compose.containers.pastebin.name }}"
chdir: "{{ new_compose_path.dest }}{{ source_code.repos.compose.rpath | default('/independent') }}"
- name: Adding Caddyfile subdomain entry
when: "server_run and mode == 'prod'"
ansible.builtin.blockinfile:
path: "{{ ansible_user_home.stdout }}/.config/caddy/Caddyfile"
block: |
{{ gist.subdomain }}.{{ web_fqdn }} {
reverse_proxy localhost:6157
handle_errors {
root /srv/{{ web_fqdn }}
rewrite /error/{err.status_code}.html
file_server
templates
encode gzip
}
}
prepend_newline: true
marker: "# <-- {mark} ANSIBLE MANAGED CLOUD DOMAIN -->"
owner: "{{ ansible_user }}"
group: "{{ ansible_user }}"
create: true
mode: "644"
state: present
- name: Adding Caddyfile subdomain entry
when: "server_run and (mode == 'dev' or caddy.scheme == 'http')"
ansible.builtin.blockinfile:
path: "{{ ansible_user_home.stdout }}/.config/caddy/Caddyfile"
block: |
http://{{ gist.subdomain }}.{{ web_fqdn }} {
reverse_proxy localhost:6157
handle_errors {
root /srv/{{ web_fqdn }}
rewrite /error/{err.status_code}.html
file_server
templates
encode gzip
}
}
prepend_newline: true
marker: "# <-- {mark} ANSIBLE MANAGED CLOUD DOMAIN -->"
owner: "{{ ansible_user }}"
group: "{{ ansible_user }}"
create: true
mode: "644"
state: present
# - name: Restarting webserver / reverse proxy container
# become: true
# become_user: "{{ current_user.stdout }}"
# when: server_run
# containers.podman.podman_container:
# name: "{{ compose.containers.webserver.name }}"
# state: started
# force_restart: true
- name: Spinning up web or reverse proxy server container specified by Compose file
when: server_run
block:
- name: Creating directory for website subdomains
ansible.builtin.file:
path: "{{ ansible_user_home.stdout }}/srv/{{ item }}.{{ web_fqdn }}"
recurse: true
owner: "{{ ansible_user }}"
group: "{{ ansible_user }}"
state: directory
loop:
- certbot
- notes
- blog
- name: Creating directory for Caddy configuration
ansible.builtin.file:
path: "{{ ansible_user_home.stdout }}/.caddy"
recurse: true
owner: "{{ ansible_user }}"
group: "{{ ansible_user }}"
state: directory
- name: Creating and deploying webserver / reverse proxy server container specified by Compose file
become: true
become_user: "{{ current_user.stdout }}"
ansible.builtin.command:
cmd: "podman-compose --env-file {{ environ.dest }} up -d {{ compose.containers.webserver.name }}"
chdir: "{{ new_compose_path.dest }}{{ source_code.repos.compose.rpath | default('/independent') }}"
- name: Creating volume subdirectory for main website root's error pages
ansible.builtin.file:
path: "{{ ansible_user_home.stdout }}/srv/{{ web_fqdn }}/error"
recurse: true
owner: "{{ ansible_user }}"
group: "{{ ansible_user }}"
state: directory
- name: Creating image files for main website root's error path
become: true
become_user: "{{ current_user.stdout }}"
ansible.builtin.copy:
src: "user/srv/domain-root/error/{{ item }}"
dest: "{{ ansible_user_home.stdout }}/srv/{{ web_fqdn }}/error/"
owner: "{{ ansible_user }}"
group: "{{ ansible_user }}"
backup: true
mode: "644"
loop:
- 503.html
- "mythe-sisyphus-klein.png"
- "dark-matter.png"
- name: Creating or updating Caddyfile at directory
when: "mode == 'prod' and ssl_run"
ansible.builtin.blockinfile:
path: "{{ ansible_user_home.stdout }}/.config/caddy/Caddyfile"
block: |
http://{{ web_fqdn }} {
handle /.well-known/acme-challenge/* {
reverse_proxy localhost:80
}
handle {
redir https://{host}{uri} 308
}
}
:80 {
root /srv/certbot.{{ web_fqdn }}
browse
}
{{ web_fqdn }} {
respond 503
# root /srv/{{ web_fqdn }}
file_server
header /.well-known/openpgpkey/* {
Content-Type application/octet-stream
Access-Control-Allow-Origin *
}
handle_errors {
rewrite /error/{err.status_code}.html
templates
}
}
prepend_newline: true
marker: "# <-- {mark} ANSIBLE MANAGED ROOT DOMAIN -->"
create: true
owner: "{{ ansible_user }}"
group: "{{ ansible_user }}"
mode: "644"
state: present
- name: Creating or updating Caddyfile at directory
when: "mode == 'prod' and not ssl_run"
ansible.builtin.blockinfile:
path: "{{ ansible_user_home.stdout }}/.config/caddy/Caddyfile"
block: |
{{ web_fqdn }} {
respond 503
# root /srv/{{ web_fqdn }}
file_server
header /.well-known/openpgpkey/* {
Content-Type application/octet-stream
Access-Control-Allow-Origin *
}
handle_errors {
rewrite /error/{err.status_code}.html
templates
}
}
prepend_newline: true
marker: "# <-- {mark} ANSIBLE MANAGED ROOT DOMAIN -->"
create: true
owner: "{{ ansible_user }}"
group: "{{ ansible_user }}"
mode: "644"
state: present
- name: Creating or updating Caddyfile at directory
when: "mode == 'dev' or caddy.scheme == 'http'"
ansible.builtin.blockinfile:
path: "{{ ansible_user_home.stdout }}/.config/caddy/Caddyfile"
block: |
http://{{ web_fqdn }} {
respond 503
# root /srv/{{ web_fqdn }}
file_server
header /.well-known/openpgpkey/* {
Content-Type application/octet-stream
Access-Control-Allow-Origin *
}
handle_errors {
rewrite /error/{err.status_code}.html
templates
}
}
prepend_newline: true
marker: "# <-- {mark} ANSIBLE MANAGED ROOT DOMAIN -->"
create: true
owner: "{{ ansible_user }}"
group: "{{ ansible_user }}"
mode: "644"
state: present
- name: Opening port 443
become: true
ansible.builtin.iptables:
chain: INPUT
protocol: "{{ item }}"
destination_port: 443
jump: ACCEPT
comment: Open up port 443
loop:
- udp
- tcp
- name: Opening ports
become: true
ansible.builtin.iptables:
chain: INPUT
protocol: tcp
destination_port: "{{ item }}"
jump: ACCEPT
comment: "Open up port {{ (item | string) }}"
loop:
- 80
# - name: Restarting webserver / reverse proxy container
# become: true
# become_user: "{{ current_user.stdout }}"
# containers.podman.podman_container:
# name: "{{ compose.containers.webserver.name }}"
# state: started
# force_restart: true
- name: Spinning up ACME challenge container specified by Compose file
when: ssl_run
ignore_errors: true
block:
- name: Creating environment file for SSL/TLS certificate acquisition container specified in Compose file
become: true
become_user: "{{ current_user.stdout }}"
ansible.builtin.template:
src: user/certbot.env.j2
dest: "{{ ansible_user_home.stdout }}/.certbot.env"
owner: "{{ ansible_user }}"
group: "{{ ansible_user }}"
mode: "644"
force: true
backup: true
register: certbot_environ
- name: Creating and deploying SSL/TLS certificate acquisition container specified by Compose file
become: true
become_user: "{{ current_user.stdout }}"
ansible.builtin.command:
cmd: "podman-compose --env-file {{ certbot_environ.dest }} --env-file {{ environ.dest }} up -d {{ compose.containers.ssl.name }}"
chdir: "{{ new_compose_path.dest }}{{ source_code.repos.compose.rpath | default('/independent') }}"
- name: Spinning up tailnet container specified by Compose file
when: tail_run
block:
- name: Creating required tailnet container secret for tailnet container specified by Compose file
become: true
# become_user: "{{ current_user.stdout }}"
when: ((fqdn is defined and fqdn != None) and item.name == fqdn) or item.name == inventory_hostname
ansible.builtin.copy:
src: "{{ item.name }}-{{ headscale.users.admin.username }}@headscale/headscale{{ (idx | string) }}.key"
dest: "{{ ansible_user_home.stdout }}/.podsecrets/headscale.key"
owner: "{{ ansible_user }}"
group: "{{ ansible_user }}"
mode: "644"
force: true
backup: true
loop: "{{ headscale.clients }}"
loop_control:
index_var: idx
- name: Creating tailnet configuration file
become: true
become_user: "{{ current_user.stdout }}"
ansible.builtin.template:
src: user/config/tailscale/conf.json.j2
dest: "{{ ansible_user_home.stdout }}/.config/tailscale/conf.json"
owner: "{{ ansible_user }}"
group: "{{ ansible_user }}"
mode: "644"
force: true
backup: true
- name: Creating and deploying tailnet container specified by Compose file
become: true
become_user: "{{ current_user.stdout }}"
ansible.builtin.command:
cmd: "podman-compose --env-file {{ environ.dest }} up -d {{ compose.containers.tail.name }}"
chdir: "{{ new_compose_path.dest }}{{ source_code.repos.compose.rpath | default('/independent') }}"
- name: Spinning up dashboard container specified by Compose file
when: tail_run and dash_run
block:
- name: Creating glance configuration file
become: true
become_user: "{{ current_user.stdout }}"
ansible.builtin.template:
src: user/config/glance/glance.yml.j2
dest: "{{ ansible_user_home.stdout }}/.config/glance/glance.yml"
owner: "{{ ansible_user }}"
group: "{{ ansible_user }}"
mode: "644"
force: true
backup: true
- name: Creating and deploying dashboard container specified by Compose file
become: true
become_user: "{{ current_user.stdout }}"
ansible.builtin.command:
cmd: "podman-compose up -d {{ compose.containers.dash.name }}"
chdir: "{{ new_compose_path.dest }}{{ source_code.repos.compose.rpath | default('/independent') }}"
- name: Restarting webserver / reverse proxy container
become: true
become_user: "{{ current_user.stdout }}"
when: server_run
containers.podman.podman_container:
name: "{{ compose.containers.webserver.name }}"
state: started
force_restart: true
- name: Making running containers persist on user logout
become: true
ansible.builtin.shell:
cmd: loginctl enable-linger $(whoami)
@@ -0,0 +1,45 @@
---
- name: Acquiring home of current user
when: ansible_facts["system"] == "Linux"
ansible.builtin.shell:
cmd: "echo ~{{ ansible_user }}"
register: ansible_user_home
- name: Acquiring home of current user
when: ansible_facts["system"] == "Linux"
ansible.builtin.shell:
cmd: "echo {{ ansible_user }}"
register: current_user
- name: Moving git repository from initial path
block:
- name: Recursively copying directory and its contents to elsewhere
ansible.builtin.copy:
src: "{{ ansible_user_home.stdout }}/repos/.foreign/{{ source_code.repos.site.name }}"
remote_src: true
dest: "{{ ansible_user_home.stdout }}/repos/"
owner: "{{ ansible_user }}"
group: "{{ ansible_user }}"
register: new_compose_path
- name: Deleting directory at previous path
ansible.builtin.file:
path: "{{ ansible_user_home.stdout }}/repos/.foreign/{{ source_code.repos.site.name }}"
state: absent
- name: Creating directory for new bare repository
ansible.builtin.file:
path: "{{ ansible_user_home.stdout }}/src"
recurse: true
owner: "{{ ansible_user }}"
group: "{{ ansible_user }}"
state: directory
- name: Creating bare repository
ansible.builtin.command:
cmd: "git init --bare {{ source_code.repos.site.name }}.git"
chdir: "{{ ansible_user_home.stdout }}/src"
creates: "{{ ansible_user_home.stdout }}/src/{{ source_code.repos.site.name }}.git"
- name: Running website
when: source_code.repos.site.run
# become: true
# become_user: "{{ current_user.stdout }}"
block:
- name: Warning about lack of implementation
ansible.builtin.debug:
msg: Not yet implemented
@@ -6,21 +6,81 @@
register: ansible_user_home
- name: Linking binaries to directories already in PATH environment variable
become: true
ignore_errors: true
ansible.builtin.file:
src: "{{ ansible_user_home.stdout }}/downloads/archives/released/surge/{{ item }}"
dest: "/usr/local/bin/{{ item }}"
dest: "/usr/bin/{{ item }}"
state: link
loop:
- surge
- name: Installing accompanying complementary Surge system service
ansible.builtin.command:
cmd: surge service install
- name: Acquiring API token for remote Surge service control
ansible.builtin.command:
cmd: surge token
register: surge_token
- name: Presenting Surge API token to Control Node
ansible.builtin.debug:
msg: "Make sure to store the following API token for Surge:\n {{ surge_token.stdout }}"
- name: Pausing to ensure completion of manual act
ansible.builtin.pause:
- name: Starting to spin up the Surge service
when: surge.service.enabled and not surge.containerized
block:
- name: Installing accompanying complementary Surge system service
become: true
ansible.builtin.command:
cmd: surge service install
- name: Creating corresponding SystemD service unit
become: true
ansible.builtin.copy:
src: systemd/system/surge.service
dest: /etc/systemd/system/surge.service
owner: root
group: root
force: true
backup: true
- name: Acquiring API token for remote Surge service control
become: true
ansible.builtin.command:
cmd: surge token
register: surge_token
- name: Pausing to inquire about how to proceed
ansible.builtin.pause:
prompt: "Type \"fetch\" to get the Surge API token, or \"show\" to see it for manual copying instead"
echo: true
register: data_method
- name: Presenting Surge API token to Control Node
when: data_method.user_input == "show"
ansible.builtin.debug:
msg: "Make sure to store the following API token for Surge:\n {{ surge_token.stdout }}"
- name: Giving opportunity to manually copy Surge API token
when: data_method.user_input == "show"
ansible.builtin.pause:
- name: Creating temporary file on managed node that stores Surge API token
become: true
when: data_method.user_input == "fetch"
ansible.builtin.copy:
content: "{{ surge_token.stdout }}"
dest: /tmp/surge.secret
owner: "{{ ansible_user }}"
group: "{{ ansible_user }}"
mode: "644"
register: surge_token
- name: Placing Surge API token into file on control node
when: data_method.user_input == "fetch"
ansible.builtin.fetch:
src: "{{ surge_token.dest }}"
dest: "./.tmp/{{ inventory_hostname }}-surge/surge.secret"
flat: true
- name: Placing Surge API token into file on control node
when: data_method.user_input == "fetch"
ansible.builtin.fetch:
src: "{{ surge_token.dest }}"
dest: "./roles/init-server/files/{{ inventory_hostname }}-surge/surge.secret"
flat: true
- name: Informing control node of acquired files
when: data_method.user_input == "fetch"
ansible.builtin.debug:
msg: "The Surge API token file have been duplicated to './.tmp/{{ inventory_hostname }}-surge/surge.secret' at the control node."
- name: Giving control node user time to read the aforementiioned message
when: data_method.user_input == "fetch"
ansible.builtin.pause:
seconds: 30
- name: Starting and enabling Surge SystemD service unit
become: true
ansible.builtin.systemd_service:
name: surge
scope: system
state: started
enabled: true
daemon_reload: true
@@ -0,0 +1,8 @@
---
- name: Starting and enabling TOR daemon service
become: true
ansible.builtin.systemd_service:
name: tor
scope: system
enabled: true
state: started
@@ -15,15 +15,18 @@
ansible.builtin.shell:
cmd: "{{ ansible_user_home.stdout }}/.local/bin/uv-install.sh"
creates: "{{ ansible_user_home.stdout }}/.local/bin/uv"
async: 600
poll: 5
- name: Linking binaries to directories already in PATH environment variable
become: true
ignore_errors: true
ansible.builtin.file:
src: "{{ ansible_user_home.stdout }}/.local/bin/{{ item }}"
dest: "/usr/local/bin/{{ item }}"
dest: "/usr/bin/{{ item }}"
state: link
loop:
- uv
- uvx
- name: Reboot machine for shell environment change
ansible.builtin.reboot:
msg: Rebooting machine
# - name: Reboot machine for shell environment change
# ansible.builtin.reboot:
# msg: Rebooting machine
+10 -25
View File
@@ -19,6 +19,7 @@
recurse: true
owner: "{{ ansible_user }}"
group: "{{ ansible_user }}"
mode: "755"
state: directory
loop:
- autoload
@@ -30,42 +31,26 @@
become_user: "{{ current_user.stdout }}"
ansible.builtin.uri:
url: "https://raw.githubusercontent.com/junegunn/vim-plug/master/plug.vim"
dest: "{{ ansible_user_home.stdout }}/.vim/autoload/"
dest: "{{ ansible_user_home.stdout }}/.vim/autoload/plug.vim"
owner: "{{ ansible_user }}"
group: "{{ ansible_user }}"
force: true
mode: "644"
follow_redirects: safe
timeout: 300
- name: Configuring ViM
become: true
become_user: "{{ current_user.stdout }}"
ansible.builtin.template:
src: vimrc.j2
src: user/vimrc.j2
dest: "{{ ansible_user_home.stdout }}/.vimrc"
owner: "{{ ansible_user }}"
group: "{{ ansible_user }}"
force: true
backup: true
vars:
vim_spatialize: true
vim_tabgap: 2
vim_blockedcursor: true
vim_historyspan: 500
vim_origami: true
vim_eddelimiters: true
vim_gitspice: true
vim_statusline: true
vim_fancycomment: true
vim_idelangs: true
vim_pathcompletion: true
vimignore:
- "*.docx"
- "*.jpg"
- "*.png"
- "*.gif"
- "*.pdf"
- "*.pyc"
- "*.exe"
- "*.flv"
- "*.img"
- "*.xlsx"
- name: Informing user of need to manually run PlugInstall in ViM
ansible.builtin.debug:
msg: "Make sure to run \":PlugInstall\" the first time you open/use ViM"
- name: Pausing to ensure user has read message about needed manual PlugInstall execution for ViM
ansible.builtin.pause:
seconds: 30
@@ -0,0 +1,57 @@
---
- name: Enabling Wireguard kernel module
become: true
community.general.modprobe:
name: wireguard
persistent: present
state: present
- name: Enable IP forwarding
become: true
when: wireguard.ip_forward
block:
- name: Enabling IPv4 forwarding
ansible.posix.sysctl:
name: net.ipv4.ip_forward
value: "1"
sysctl_set: true
state: present
- name: Marking IPv4 forwarded traffic as valid
ansible.posix.sysctl:
name: net.ipv4.conf.all.src_valid_mark
value: "1"
sysctl_set: true
state: present
- name: Enabling IPv6 forwarding
ansible.posix.sysctl:
name: net.ipv6.conf.all.forwarding
value: "1"
sysctl_set: true
state: present
- name: Marking IPv6 traffic as valid
ansible.posix.sysctl:
name: net.ipv6.conf.all.src_valid_mark
value: "1"
sysctl_set: true
state: present
- name: Preparing to allow Wireguard logging
when: "wireguard.debug"
block:
- name: Creating subdirectory for system kernel debugging
become: true
ansible.builtin.file:
path: /sys/kernel/debug/dynamic_debug
recurse: true
owner: root
group: root
mode: "755"
state: directory
- name: Enabling Wireguard system logging
become: true
ansible.builtin.copy:
content: "module wireguard +p"
dest: /sys/kernel/debug/dynamic_debug/control
owner: root
group: root
mode: "644"
force: true
+89 -19
View File
@@ -45,9 +45,48 @@
when: ansible_facts["os_family"] == "Debian"
become: true
block:
- name: Updating package cache
ansible.builtin.apt:
update_cache: true
- name: Creating core OS repository sources for package manager
when: ansible_facts["os_family"] == "Debian"
ansible.builtin.deb822_repository:
name: "{{ item.name }}"
uris: "{{ item.sources }}"
types: "{{ item.types }}"
suites: "{{ item.suites }}"
components: "{{ item.comps }}"
signed_by: "{{ item.sigkey }}"
state: present
loop:
- name: debian-trixie
sources: "http://deb.debian.org/debian/"
sigkey: /usr/share/keyrings/debian-archive-keyring.gpg
types:
- deb-src
- deb
suites:
- trixie
- trixie-updates
comps:
- main
- non-free-firmware
- contrib
- name: debian-trixie-security
sources: "http://security.debian.org/debian-security"
sigkey: /usr/share/keyrings/debian-archive-keyring.gpg
types:
- deb-src
- deb
suites: trixie-security
comps:
- main
- non-free-firmware
- contrib
- name: Remove previous core OS repository sources for package manager
ansible.builtin.file:
path: /etc/apt/sources.list
state: absent
# @TODO uncomment below before continuing with testing previous task
# - name: Premature end of play
# ansible.builtin.meta: end_play
- name: Registering a package source
when: item.sources != None
ansible.builtin.deb822_repository:
@@ -59,24 +98,28 @@
signed_by: "{{ item.sigkey }}"
state: present
loop: "{{ ((pkgs.mngr.core | default([]))) }}"
- name: Updating package cache
ansible.builtin.apt:
update_cache: true
- name: Upgrading
ansible.builtin.apt:
upgrade: dist
autoremove: true
- name: Installing a local package in managed node
when: item.uri != None
ansible.builtin.apt:
deb: "{{ item.uri }}"
state: present
# @TODO add a default value for notify
# notify: "{{ item.name }}"
notify: "{{ item.handler | default('default') }}"
loop: "{{ (pkgs.mngr.core | default([])) | selectattr('uri', 'search', '\\.deb$') }}"
- name: Updating package cache
ansible.builtin.apt:
update_cache: true
- name: Installing a package
when: item.name != None and item.uri == None
ansible.builtin.package:
name: "{{ item.name }}"
state: latest
# @TODO add a default value for notify
# notify: "{{ item.name }}"
state: present
notify: "{{ item.handler | default('default') }}"
async: 600
poll: 5
loop: "{{ ((pkgs.mngr.core | default([]))) | rejectattr('uri', 'search', '\\.deb$') }}"
tags:
- get_mngr_pkgs
@@ -93,13 +136,17 @@
owner: "{{ ansible_user }}"
group: "{{ ansible_user }}"
force: true
mode: "744"
# @TODO add a default value for notify
notify: "{{ ((pkgs.script.core | default([])))[idx].name }}"
mode: "755"
timeout: 300
notify: "{{ ((pkgs.script.core | default([])))[idx].handler | default('default') }}"
loop: "{{ (pkgs.script.core | default([])) }}"
loop_control:
index_var: idx
register: install_scripts
- name: Reboot machine for shell environment change
become: true
ansible.builtin.reboot:
msg: Rebooting machine
tags:
- get_script_pkgs
- name: Installing software by building it from source archives
@@ -131,16 +178,21 @@
loop_control:
index_var: idx
- name: Unarchiving software build archive
become: true
become_user: "{{ current_user.stdout }}"
when: item.dest != None and (((pkgs.script.core | default([]))) | length) > 0
ansible.builtin.unarchive:
src: "{{ item.dest }}"
remote_src: true
dest: "{{ ansible_user_home.stdout }}/downloads/archives/released/{{ ((pkgs.archive.core | default([])))[idx].name }}/"
# @TODO add a default value for notify
notify: "{{ ((pkgs.archive.core | default([])))[idx].name }}"
notify: "{{ ((pkgs.archive.core | default([])))[idx].handler | default('default') }}"
loop: "{{ archived_builds.results }}"
loop_control:
index_var: idx
- name: Reboot machine for shell environment change
become: true
ansible.builtin.reboot:
msg: Rebooting machine
tags:
- get_archive_pkgs
- name: Installing software from source git repositories
@@ -154,10 +206,28 @@
dest: "{{ ansible_user_home.stdout }}/repos/.foreign/{{ item.name }}"
version: "{{ item.branch }}"
clone: true
single_branch: true
# @TODO add a default value for notify
notify: "{{ item.name }}"
notify: "{{ item.handler | default('default') }}"
loop: "{{ (pkgs.git_repos.core | default([])) }}"
register: installation_repos
- name: Reboot machine for shell environment change
become: true
ansible.builtin.reboot:
msg: Rebooting machine
tags:
- get_git_pkgs
- name: Installing software as pre-compiled binary
block:
- name: Grabbing software binary
become: true
ansible.builtin.get_url:
url: "{{ item.src }}"
dest: "/usr/bin/{{ item.name }}"
owner: root
group: root
mode: "755"
force: true
backup: true
notify: "{{ item.handler }}"
timeout: 300
loop: "{{ (pkgs.binaries.core | default([])) }}"
+20
View File
@@ -0,0 +1,20 @@
---
- name: Acquiring home of current user
when: ansible_facts["system"] == "Linux"
ansible.builtin.shell:
cmd: "echo ~{{ ansible_user }}"
register: ansible_user_home
- name: Acquiring current user
when: ansible_facts["system"] == "Linux"
ansible.builtin.shell:
cmd: "echo {{ ansible_user }}"
register: current_user
- name: Copying NetRC file
become: true
become_user: "{{ current_user.stdout }}"
ansible.builtin.template:
src: user/netrc.j2
dest: "{{ ansible_user_home.stdout }}/.netrc"
owner: "{{ ansible_user }}"
group: "{{ ansible_user }}"
mode: "600"
+45 -16
View File
@@ -2,6 +2,22 @@
---
# tasks file for roles/init-vps
# @NOTE server deployment method is based on task tags compiled herein
- name: Informing user of requirement of two main domains
when: (certbot.domains | length) < 2 or (certbot.domains | length) > 2
ansible.builtin.fail:
msg: Only two domains allowed and required
- name: Informing user of requirement at least one wildcard
when: (certbot.domains | select("regex", "^\\*\\.") | list | length) == 0
ansible.builtin.fail:
msg: At least one of the FQDNs must have a wildcard
# - name: Setting the FQDN for development
# when: mode == "dev"
# ansible.builtin.set_fact:
# fqdn: "{{ (certbot.domains | map('regex_replace', '\\.([^\\.]*)$', '.test') | reject('regex', '^\\*\\.') | list)[0] }}"
- name: Setting the FQDN
# when: mode == "prod"
ansible.builtin.set_fact:
fqdn: "{{ (certbot.domains | reject('regex', '^\\*\\.') | list)[0] }}"
- name: Finding SSH public keys for root
delegate_facts: true
delegate_to: localhost
@@ -18,20 +34,41 @@
ansible.builtin.set_fact:
root_pubkeys: "{{ root_pubkeys | default([]) + [lookup('file', item)] }}"
loop: "{{ root_pubkey_paths }}"
- name: Ensuring password is defined for root user
when: prehashed_password is undefined or prehashed_password == None
block:
- name: Prompting for password for or of root user
when: password is undefined or password == None
ansible.builtin.pause:
prompt: "Provide a password for the root user"
echo: false
register: prompted_password
- name: Getting the inputted password for root user
when: prompted_password is defined or prompted_password != None
ansible.builtin.set_fact:
prehashed_password: "{{ prompted_password.user_input }}"
- name: Bootstrapping VPS
block:
- name: Ensuring token is available for VPS service API
when: token is undefined or token == None
ansible.builtin.pause:
prompt: "Provide the API token for the given VPS service"
echo: false
register: prompted_token
- name: Creating VPS via Linode VPS service API
block:
- name: Creating the VPS
linode.cloud.instance:
api_token: "{{ token }}"
label: "{{ instance }}"
api_token: "{{ token | prompted_token.user_input }}"
label: "{{ fqdn }}"
type: g6-standard-2
image: "{{ operating_system }}"
disk_encryption: enabled
region: "{{ origin }}"
private_ip: true
root_pass: "{{ password }}"
# @TODO find out if 'root_pass' attribute takes in hashed or plaintext password
# root_pass: "{{ password | default((prehashed_password | lookup('password_hash', hashtype='sha512'))) }}" # IF HASHED
root_pass: "{{ password | default(prehashed_password) }}" # IF PLAINTEXT
authorized_keys: "{{ root_pubkeys }}"
state: present
register: new_instance
@@ -43,7 +80,6 @@
timeout: 300
vars:
ansible_ssh_private_key_file: "{{ chosen_privkey | default(ssh_keypairs.files | rejectattr('path', 'search', '\\.pub$') | map(attribute='path') | list | random) }}" # @TODO define 'chosen_privkey'in playbook
ansible_user: root
loop: "{{ new_instance.instance[ip_pref] }}"
tags:
- linode
@@ -66,9 +102,7 @@
ansible.builtin.wait_for_connection:
delay: 20
timeout: 300
vars:
ansible_user: root
loop: "{{ groups[instance] | default(hostvars[instance]) }}"
loop: "{{ groups[fqdn] | default(hostvars[fqdn]) }}"
- name: Checking if that server has required operating system
delegate_to: "{{ item }}"
delegate_facts: true
@@ -76,9 +110,7 @@
when: ansible_facts["system"] != "Linux" and item is ansible.utils['ip_pref']
ansible.builtin.fail:
msg: Unsupported operating system found
vars:
ansible_user: root
loop: "{{ groups[instance] | default(hostvars[instance]) }}"
loop: "{{ groups[fqdn] | default(hostvars[fqdn]) }}"
- name: Checking if that server has required Linux distro
delegate_to: "{{ item }}"
delegate_facts: true
@@ -86,19 +118,16 @@
when: ansible_facts["system"] == "Linux" and ansible_facts["os_family"] != "Debian" and item is ansible.utils['ip_pref']
ansible.builtin.fail:
msg: Unsupported Linux distro found
vars:
ansible_user: root
loop: "{{ groups[instance] | default(hostvars[instance]) }}"
loop: "{{ groups[fqdn] | default(hostvars[fqdn]) }}"
- name: Providing authorized keys for server root account
delegate_to: "{{ item[0] }}"
delegate_facts: true
become: true
remote_user: root
ansible.posix.authorized_key:
user: "{{ ansible_user }}"
key: "{{ lookup('file', item[1]) }}"
state: present
vars:
ansible_user: root
loop: "{{ (groups[instance] | default(hostvars[instance])) | product(root_pubkey_paths) }}"
loop: "{{ (groups[fqdn] | default(hostvars[fqdn])) | product(root_pubkey_paths) }}"
tags:
- lan
+22 -1
View File
@@ -16,6 +16,27 @@
register: remote_group
tags:
- lan
- name: Managing passwords
when: prehashed_passwords is undefined or prehashed_passwords == None
block:
- name: Acquiring users lacking passwords
ansible.builtin.set_fact:
passwordless_admins: "{{ admins | selectattr('password', '==', 'null') | list }}"
- name: Pausing to acquire password for a user
when: item.password is undefined or item.password == None
ansible.builtin.pause:
prompt: "Provide a password for the administrative user, {{ item.username }}"
echo: false
loop: "{{ passwordless_admins }}"
register: prompted_passwords
- name: Processing inputted password per user
when: prompted_passwords is defined and prompted_passwords != None
ansible.builtin.set_fact:
prehashed_passwords: "{{ (prompted_passwords.results | default([])) | map(attribute='user_input') | list }}"
- name: Pairing inputted passwords with associated user
when: prehashed_passwords is defined or prehashed_passwords != None
ansible.builtin.set_fact:
prehashed_passwords: "{{ dict(passwordless_admins | map(attribute='username') | zip(prehashed_passwords) | list) }}"
- name: Creating an administrative user
become: true
ansible.builtin.user:
@@ -27,7 +48,7 @@
append: true
generate_ssh_key: true
create_home: true
password: "{{ item.password }}"
password: "{{ item.password | default((prehashed_passwords[item.username] | password_hash(hashtype='sha512'))) }}"
shell: "/bin/bash"
loop: "{{ admins }}"
register: admin_users
@@ -46,9 +46,48 @@
when: ansible_facts["os_family"] == "Debian"
become: true
block:
- name: Updating package cache
ansible.builtin.apt:
update_cache: true
- name: Creating core OS repository sources for package manager
when: ansible_facts["os_family"] == "Debian"
ansible.builtin.deb822_repository:
name: "{{ item.name }}"
uris: "{{ item.sources }}"
types: "{{ item.types }}"
suites: "{{ item.suites }}"
components: "{{ item.comps }}"
signed_by: "{{ item.sigkey }}"
state: present
loop:
- name: debian-trixie
sources: "http://deb.debian.org/debian/"
sigkey: /usr/share/keyrings/debian-archive-keyring.gpg
types:
- deb-src
- deb
suites:
- trixie
- trixie-updates
comps:
- main
- non-free-firmware
- contrib
- name: debian-trixie-security
sources: "http://security.debian.org/debian-security"
sigkey: /usr/share/keyrings/debian-archive-keyring.gpg
types:
- deb-src
- deb
suites: trixie-security
comps:
- main
- non-free-firmware
- contrib
- name: Remove previous core OS repository sources for package manager
ansible.builtin.file:
path: /etc/apt/sources.list
state: absent
# @TODO uncomment below before continuing with testing previous task
# - name: Premature end of play
# ansible.builtin.meta: end_play
- name: Registering a package source
when: item.sources != None
ansible.builtin.deb822_repository:
@@ -60,32 +99,36 @@
signed_by: "{{ item.sigkey }}"
state: present
loop: "{{ (pkgs.mngr.userspace | default([])) }}"
- name: Updating package cache
ansible.builtin.apt:
update_cache: true
- name: Upgrading
ansible.builtin.apt:
upgrade: dist
autoremove: true
- name: Installing a local package in managed node
when: item.uri != None
ansible.builtin.apt:
deb: "{{ item.uri }}"
state: present
# @TODO add a default value for notify
# notify: "{{ item.name }}"
notify: "{{ item.handler | default('default') }}"
loop: "{{ (pkgs.mngr.userspace | default([])) | selectattr('uri', 'search', '\\.deb$') }}"
- name: Updating package cache
ansible.builtin.apt:
update_cache: true
- name: Installing a package
when: item.name != None and item.uri == None
ansible.builtin.package:
name: "{{ item.name }}"
state: latest
# @TODO add a default value for notify
# notify: "{{ item.name }}" # @TODO create corresponding roles/init-vps handlers
notify: "{{ item.handler | default('default') }}" # @TODO create corresponding roles/init-vps handlers
async: 600
poll: 5
loop: "{{ (pkgs.mngr.userspace | default([])) | rejectattr('uri', 'search', '\\.deb$') }}"
tags:
- get_mngr_pkgs
- name: Installing software by executing installation shell scripts
become: true
become_user: "{{ current_user.stdout }}"
block:
- name: Acquiring installation shell script
become_user: "{{ current_user.stdout }}"
when: item.src != None and ((pkgs.script.userspace | default([])) | length) > 0
ansible.builtin.uri:
url: "{{ item.src }}"
@@ -94,13 +137,16 @@
owner: "{{ ansible_user }}"
group: "{{ ansible_user }}"
force: true
mode: "744"
# @TODO add a default value for notify
notify: "{{ (pkgs.script.userspace | default([]))[idx].name }}"
mode: "755"
timeout: 300
notify: "{{ (pkgs.script.userspace | default([]))[idx].handler | default('default') }}"
loop: "{{ (pkgs.script.userspace | default([])) }}"
loop_control:
index_var: idx
register: install_scripts
- name: Reboot machine for shell environment change
ansible.builtin.reboot:
msg: Rebooting machine
tags:
- get_script_pkgs
- name: Installing software by building it from source archives
@@ -139,38 +185,48 @@
dest: "{{ ansible_user_home.stdout }}/downloads/archives/released/{{ (pkgs.archive.userspace | default([]))[idx].name }}/"
owner: "{{ ansible_user }}"
group: "{{ ansible_user }}"
# @TODO add a default value for notify
notify: "{{ (pkgs.archive.userspace | default([]))[idx].name }}"
notify: "{{ (pkgs.archive.userspace | default([]))[idx].handler | default('default') }}"
loop: "{{ archived_builds.results }}"
loop_control:
index_var: idx
- name: Reboot machine for shell environment change
become: true
ansible.builtin.reboot:
msg: Rebooting machine
tags:
- get_archive_pkgs
- name: Installing software from source git repositories
block:
- name: Clone git bare repository
when: item.src != None
become: true
become_user: "{{ current_user.stdout }}"
when: item.src != None
ansible.builtin.git:
repo: "{{ item.src }}"
dest: "{{ ansible_user_home.stdout }}/repos/.foreign/{{ item.name }}"
version: "{{ item.branch }}"
clone: true
single_branch: true
notify: "{{ item.handler | default('default') }}"
loop: "{{ (pkgs.git_repos.userspace | default([])) }}"
register: installation_repos
# - name: Changing ownership of specific repo subdirectory
# become: true
# become_user: "{{ current_user.stdout }}"
# ansible.builtin.file:
# path: "{{ ansible_user_home.stdout }}/repos/.foreign/{{ item.name }}"
# recurse: true
# owner: "{{ ansible_user }}"
# group: "{{ ansible_user }}"
# notify: "{{ item.name }}"
# loop: "{{ (pkgs.git_repos.userspace | default([])) }}"
- name: Reboot machine for shell environment change
become: true
ansible.builtin.reboot:
msg: Rebooting machine
tags:
- get_git_pkgs
# @TODO add a reboot either here or in any of the handlers potentially notified from here in
# order to update environment (unless found better solution)
- name: Installing software as pre-compiled binary
block:
- name: Grabbing software binary
become: true
ansible.builtin.get_url:
url: "{{ item.src }}"
dest: "/usr/bin/{{ item.name }}"
owner: root
group: root
mode: "755"
force: true
backup: true
notify: "{{ item.handler }}"
timeout: 300
loop: "{{ (pkgs.binaries.userspace | default([])) }}"
@@ -0,0 +1,525 @@
---
# headscale will look for a configuration file named `config.yaml` (or `config.json`) in the following order:
#
# - `/etc/headscale`
# - `~/.headscale`
# - current working directory
# The url clients will connect to.
# Typically this will be a domain like:
#
# https://myheadscale.example.com:443
#
{% if mode == 'prod' %}
{% if headscale.port is defined and headscale.port != None %}
server_url: {{ headscale.scheme + '://' + web_fqdn + ':' + (headscale.port | string) }}
{% else %}
server_url: {{ headscale.scheme + '://' + web_fqdn + ':80' }}
{% endif %}
{% else %}
{% if headscale.port is defined and headscale.port != None %}
server_url: {{ 'http://' + web_fqdn + ':' + (headscale.port | string) }}
{% else %}
server_url: {{ 'http://' + web_fqdn + ':443' }}
{% endif %}
{% endif %}
# Address to listen to / bind to on the server
#
# For production:
# listen_addr: 0.0.0.0:8080
{% if headscale.port is defined and headscale.port != None %}
listen_addr: {{ '0.0.0.0' + ':' + (headscale.port | string) }}
{% else %}
listen_addr: 0.0.0.0:80
{% endif %}
# Address to listen to /metrics and /debug, you may want
# to keep this endpoint private to your internal network
# Use an empty value to disable the metrics listener.
metrics_listen_addr: 127.0.0.1:9090
# Address to listen for gRPC.
# gRPC is used for controlling a headscale server
# remotely with the CLI
# Note: Remote access _only_ works if you have
# valid certificates.
#
# For production:
# grpc_listen_addr: 0.0.0.0:50443
{% if headscale.grpc.expose %}
grpc_listen_addr: {{ '0.0.0.0' + ':' + (headscale.grpc.port | string) }}
{% else %}
grpc_listen_addr: 127.0.0.1:50443
{% endif %}
# Allow the gRPC admin interface to run in INSECURE
# mode. This is not recommended as the traffic will
# be unencrypted. Only enable if you know what you
# are doing.
{% if headscale.grpc.secure %}
grpc_allow_insecure: false
{% else %}
grpc_allow_insecure: true
{% endif %}
# CIDR(s) of reverse proxies (e.g. 127.0.0.1/32) whose
# True-Client-IP, X-Real-IP and X-Forwarded-For headers should
# be honoured. Empty (default) ignores those headers; setting
# this without a proxy in front lets clients spoof their logged
# source IP.
trusted_proxies: []
# The Noise section includes specific configuration for the
# TS2021 Noise protocol
noise:
# The Noise private key is used to encrypt the traffic between headscale and
# Tailscale clients when using the new Noise-based protocol. A missing key
# will be automatically generated.
private_key_path: /var/lib/headscale/noise_private.key
# List of IP prefixes to allocate tailaddresses from.
# Each prefix consists of either an IPv4 or IPv6 address,
# and the associated prefix length, delimited by a slash.
#
# WARNING: These prefixes MUST be subsets of the standard Tailscale ranges:
# - IPv4: 100.64.0.0/10 (CGNAT range)
# - IPv6: fd7a:115c:a1e0::/48 (Tailscale ULA range)
#
# Using a SUBSET of these ranges is supported and useful if you want to
# limit IP allocation to a smaller block (e.g., 100.64.0.0/24).
#
# Using ranges OUTSIDE of CGNAT/ULA is NOT supported and will cause
# undefined behaviour. The Tailscale client has hard-coded assumptions
# about these ranges and will break in subtle, hard-to-debug ways.
#
# See:
# IPv4: https://github.com/tailscale/tailscale/blob/22ebb25e833264f58d7c3f534a8b166894a89536/net/tsaddr/tsaddr.go#L33
# IPv6: https://github.com/tailscale/tailscale/blob/22ebb25e833264f58d7c3f534a8b166894a89536/net/tsaddr/tsaddr.go#LL81C52-L81C71
prefixes:
v4: 100.64.0.0/10
v6: fd7a:115c:a1e0::/48
# Strategy used for allocation of IPs to nodes, available options:
# - sequential (default): assigns the next free IP from the previous given
# IP. A best-effort approach is used and Headscale might leave holes in the
# IP range or fill up existing holes in the IP range.
# - random: assigns the next free IP from a pseudo-random IP generator (crypto/rand).
allocation: sequential
# DERP is a relay system that Tailscale uses when a direct
# connection cannot be established.
# https://tailscale.com/blog/how-tailscale-works/#encrypted-tcp-relays-derp
#
# Headscale needs a list of DERP servers that can be presented to the clients.
derp:
server:
# If enabled, runs the embedded DERP server and merges it into the rest of the DERP config
# The Headscale server_url defined above MUST be using https, DERP requires TLS to be in place
enabled: false
# Region ID to use for the embedded DERP server.
# The local DERP prevails if the region ID collides with other region ID coming from
# the regular DERP config.
region_id: 999
# Region code and name are displayed in the Tailscale UI to identify a DERP region
region_code: "headscale"
region_name: "Headscale Embedded DERP"
# Only allow clients associated with this server access
verify_clients: true
# Listens over UDP at the configured address for STUN connections - to help with NAT traversal.
# When the embedded DERP server is enabled stun_listen_addr MUST be defined.
#
# For more details on how this works, check this great article: https://tailscale.com/blog/how-tailscale-works/
stun_listen_addr: "0.0.0.0:3478"
# Private key used to encrypt the traffic between headscale DERP and
# Tailscale clients. A missing key will be automatically generated.
private_key_path: /var/lib/headscale/derp_server_private.key
# This flag can be used, so the DERP map entry for the embedded DERP server is not written automatically,
# it enables the creation of your very own DERP map entry using a locally available file with the parameter DERP.paths
# If you enable the DERP server and set this to false, it is required to add the DERP server to the DERP map using DERP.paths
automatically_add_embedded_derp_region: true
# For better connection stability (especially when using an Exit-Node and DNS is not working),
# it is possible to optionally add the public IPv4 and IPv6 address to the Derp-Map using:
ipv4: 198.51.100.1
ipv6: 2001:db8::1
# List of externally available DERP maps encoded in JSON
urls:
- https://controlplane.tailscale.com/derpmap/default
# Locally available DERP map files encoded in YAML
#
# This option is mostly interesting for people hosting their own DERP servers:
# https://tailscale.com/docs/reference/derp-servers/custom-derp-servers
# https://headscale.net/stable/ref/derp/
#
# paths:
# - /etc/headscale/derp-example.yaml
paths: []
# If enabled, a worker will be set up to periodically
# refresh the given sources and update the derpmap
# will be set up.
auto_update_enabled: true
# How often should we check for DERP updates?
update_frequency: 3h
# Disables the automatic check for headscale updates on startup
disable_check_updates: false
# Node lifecycle configuration.
node:
# Default key expiry for non-tagged nodes, regardless of registration method
# (auth key, CLI, web auth). Tagged nodes are exempt and never expire.
#
# This is the base default. OIDC can override this via oidc.expiry.
# If a client explicitly requests a specific expiry, the client value is used.
#
# Setting the value to "0" means no default expiry (nodes never expire unless
# explicitly expired via `headscale nodes expire`).
#
# Tailscale SaaS uses 180d; set to a positive duration to match that behaviour.
#
# Default: 0 (no default expiry)
expiry: 0
ephemeral:
# Time before an inactive ephemeral node is deleted.
inactivity_timeout: 30m
# HA subnet router health probing.
#
# When HA routes exist (2+ nodes advertising the same prefix), headscale
# pings each HA node every probe_interval via the Noise channel. If a node
# fails to respond within probe_timeout it is marked unhealthy and the
# primary role moves to the next healthy node. A node that later responds
# is marked healthy again but does NOT reclaim primary (avoids flapping).
#
# Worst-case detection time is probe_interval + probe_timeout (15s default).
# No-op when no HA routes exist. Set probe_interval to 0 to disable.
routes:
ha:
# How often to ping HA subnet routers. Set to 0 to disable probing.
# Must be >= 2s when enabled.
probe_interval: 10s
# How long to wait for a ping response before marking a node unhealthy.
# Must be >= 1s and less than probe_interval.
probe_timeout: 5s
database:
# Database type. Available options: sqlite, postgres
# Please note that using Postgres is highly discouraged as it is only supported for legacy reasons.
# All new development, testing and optimisations are done with SQLite in mind.
type: sqlite
# Enable debug mode. This setting requires the log.level to be set to "debug" or "trace".
debug: false
# GORM configuration settings.
gorm:
# Enable prepared statements.
prepare_stmt: true
# Enable parameterized queries.
parameterized_queries: true
# Skip logging "record not found" errors.
skip_err_record_not_found: true
# Threshold for slow queries in milliseconds.
slow_threshold: 1000
# SQLite config
sqlite:
path: /var/lib/headscale/db.sqlite
# Enable WAL mode for SQLite. This is recommended for production environments.
# https://www.sqlite.org/wal.html
write_ahead_log: true
# Maximum number of WAL file frames before the WAL file is automatically checkpointed.
# https://www.sqlite.org/c3ref/wal_autocheckpoint.html
# Set to 0 to disable automatic checkpointing.
wal_autocheckpoint: 1000
# # Postgres config
# Please note that using Postgres is highly discouraged as it is only supported for legacy reasons.
# See database.type for more information.
# postgres:
# # If using a Unix socket to connect to Postgres, set the socket path in the 'host' field and leave 'port' blank.
# host: localhost
# port: 5432
# name: headscale
# user: foo
# pass: bar
# max_open_conns: 10
# max_idle_conns: 10
# conn_max_idle_time_secs: 3600
# # If other 'sslmode' is required instead of 'require(true)' and 'disabled(false)', set the 'sslmode' you need
# # in the 'ssl' field. Refers to https://www.postgresql.org/docs/current/libpq-ssl.html Table 34.1.
# ssl: false
{% if mode == 'prod' or headscale.scheme == 'https' %}
### TLS configuration
#
## Let's encrypt / ACME
#
# headscale supports automatically requesting and setting up
# TLS for a domain with Let's Encrypt.
#
{% if certbot is undefined or certbot == None %}
# URL to ACME directory
# acme_url: https://acme-v02.api.letsencrypt.org/directory
# Email to register with ACME provider
# acme_email: ""
# Domain name to request a TLS certificate for:
# tls_letsencrypt_hostname: ""
# Path to store certificates and metadata needed by
# letsencrypt
# For production:
# tls_letsencrypt_cache_dir: /var/lib/headscale/cache
# Type of ACME challenge to use, currently supported types:
# HTTP-01 or TLS-ALPN-01
# See: https://headscale.net/stable/ref/tls/
# tls_letsencrypt_challenge_type: HTTP-01
# When HTTP-01 challenge is chosen, letsencrypt must set up a
# verification endpoint, and it will be listening on:
# :http = port 80
# tls_letsencrypt_listen: ":http"
{% else %}
{% if not certbot.containerized %}
## Use already defined certificates:
tls_cert_path: {{ '/etc/letsencrypt/live/' + web_fqdn + '/fullchain.pem' }}
tls_key_path: {{ '/etc/letsencrypt/live/' + web_fqdn + '/privkey.pem' }}
{% else %}
tls_cert_path: {{ ansible_user_home.stdout + '/.config/letsencrypt/live/' + web_fqdn + '/fullchain.pem' }}
tls_key_path: {{ ansible_user_home.stdout + '/.config/letsencrypt/live/' + web_fqdn + '/privkey.pem' }}
## Use already defined certificates:
{% endif %}
{% endif %}
{% endif %}
log:
# Valid log levels: panic, fatal, error, warn, info, debug, trace
level: info
# Output formatting for logs: text or json
format: text
## Policy
# Headscale supports a wide range of Tailscale policy features such as ACLs and
# Grants. Please have a look at their docs to better understand the concepts:
# ACLs: https://tailscale.com/docs/features/access-control/acls
# Grants: https://tailscale.com/docs/features/access-control/grants
policy:
# The mode can be "file" or "database" that defines
# where the policies are stored and read from.
mode: file
# If the mode is set to "file", the path to a HuJSON file containing policies.
path: ""
## DNS
#
# headscale supports Tailscale's DNS configuration and MagicDNS.
# Please have a look to their docs to better understand the concepts:
#
# - https://tailscale.com/docs/features/magicdns
# - https://tailscale.com/blog/2021-09-private-dns-with-magicdns
#
# Please note that for the DNS configuration to have any effect,
# clients must have the `--accept-dns=true` option enabled. This is the
# default for the Tailscale client. This option is enabled by default
# in the Tailscale client.
#
# Setting _any_ of the configuration and `--accept-dns=true` on the
# clients will integrate with the DNS manager on the client or
# overwrite /etc/resolv.conf.
# https://tailscale.com/docs/reference/faq/dns-resolv-conf
#
# If you want stop Headscale from managing the DNS configuration
# all the fields under `dns` should be set to empty values.
dns:
# Whether to use MagicDNS
magic_dns: true
# Defines the base domain to create the hostnames for MagicDNS.
# This domain _must_ be different from the server_url domain.
# `base_domain` must be a FQDN, without the trailing dot.
# The FQDN of the hosts will be
# `hostname.base_domain` (e.g., _myhost.example.com_).
base_domain: {{ headscale.magic_dns.domain }}
# Whether to use the local DNS settings of a node or override the local DNS
# settings (default) and force the use of Headscale's DNS configuration.
override_local_dns: true
# List of DNS servers to expose to clients.
nameservers:
global: {{ headscale.magic_dns.nameservers }}
# NextDNS (see https://tailscale.com/docs/integrations/nextdns).
# "abc123" is example NextDNS ID, replace with yours.
# - https://dns.nextdns.io/abc123
# Split DNS (see https://tailscale.com/docs/reference/dns-in-tailscale#restricted-nameservers),
# a map of domains and which DNS server to use for each.
split: {}
# foo.bar.com:
# - 1.1.1.1
# darp.headscale.net:
# - 1.1.1.1
# - 8.8.8.8
# Set custom DNS search domains. With MagicDNS enabled,
# your tailnet base_domain is always the first search domain.
search_domains: []
# Extra DNS records
# so far only A and AAAA records are supported (on the tailscale side)
# See: https://headscale.net/stable/ref/dns/
extra_records: []
# - name: "grafana.myvpn.example.com"
# type: "A"
# value: "100.64.0.3"
#
# # you can also put it in one line
# - { name: "prometheus.myvpn.example.com", type: "A", value: "100.64.0.3" }
#
# Alternatively, extra DNS records can be loaded from a JSON file.
# Headscale processes this file on each change.
# extra_records_path: /var/lib/headscale/extra-records.json
# Unix socket used for the CLI to connect without authentication
# Note: for production you will want to set this to something like:
unix_socket: /var/run/headscale/headscale.sock
unix_socket_permission: "0770"
# OpenID Connect
# https://headscale.net/stable/ref/oidc/
# oidc:
# # Block startup until the identity provider is available and healthy.
# only_start_if_oidc_is_available: true
#
# # OpenID Connect Issuer URL from the identity provider
# issuer: "https://your-oidc.issuer.com/path"
#
# # Client ID from the identity provider
# client_id: "your-oidc-client-id"
#
# # Client secret generated by the identity provider
# # Note: client_secret and client_secret_path are mutually exclusive.
# client_secret: "your-oidc-client-secret"
# # Alternatively, set `client_secret_path` to read the secret from the file.
# # It resolves environment variables, making integration to systemd's
# # `LoadCredential` straightforward:
# client_secret_path: "${CREDENTIALS_DIRECTORY}/oidc_client_secret"
#
# # Use the expiry from the token received from OpenID when the user logged
# # in. This will typically lead to frequent need to reauthenticate and should
# # only be enabled if you know what you are doing.
# # Note: enabling this will cause `node.expiry` to be ignored for
# # OIDC-authenticated nodes.
# use_expiry_from_token: false
#
# # The OIDC scopes to use, defaults to "openid", "profile" and "email".
# # Custom scopes can be configured as needed, be sure to always include the
# # required "openid" scope.
# scope: ["openid", "profile", "email"]
#
# # Only verified email addresses are synchronized to the user profile by
# # default. Unverified emails may be allowed in case an identity provider
# # does not send the "email_verified: true" claim or email verification is
# # not required.
# email_verified_required: true
#
# # Provide custom key/value pairs which get sent to the identity provider's
# # authorization endpoint.
# extra_params:
# domain_hint: example.com
#
# # Only accept users whose email domain is part of the allowed_domains list.
# allowed_domains:
# - example.com
#
# # Only accept users whose email address is part of the allowed_users list.
# allowed_users:
# - alice@example.com
#
# # Only accept users which are members of at least one group in the
# # allowed_groups list.
# allowed_groups:
# - /headscale
#
# # Optional: PKCE (Proof Key for Code Exchange) configuration
# # PKCE adds an additional layer of security to the OAuth 2.0 authorization code flow
# # by preventing authorization code interception attacks
# # See https://datatracker.ietf.org/doc/html/rfc7636
# pkce:
# # Enable or disable PKCE support (default: false)
# enabled: false
#
# # PKCE method to use:
# # - plain: Use plain code verifier
# # - S256: Use SHA256 hashed code verifier (default, recommended)
# method: S256
# Logtail configuration
# Logtail is Tailscales logging and auditing infrastructure, it allows the
# control panel to instruct tailscale nodes to log their activity to a remote
# server. To disable logging on the client side, please refer to:
# https://tailscale.com/docs/features/logging#opt-out-of-client-logging
logtail:
# Enable logtail for tailscale nodes of this Headscale instance.
# As there is currently no support for overriding the log server in Headscale, this is
# disabled by default. Enabling this will make your clients send logs to Tailscale Inc.
enabled: false
# Taildrop configuration
# Taildrop is the file sharing feature of Tailscale, allowing nodes to
# send files to each other.
# https://tailscale.com/docs/features/taildrop
taildrop:
# Enable or disable Taildrop tailnet-wide. When disabled, headscale
# withholds `https://tailscale.com/cap/file-sharing` from every
# node's CapMap.
enabled: true
# Default node auto-update behaviour. When enabled, every node's
# CapMap carries `default-auto-update: [true]` so clients that have
# not made a local opt-in / opt-out choice run auto-updates by
# default. Setting it back to false flips the default for future
# clients; clients that already stored the value locally keep their
# choice.
auto_update:
enabled: false
# Advanced performance tuning parameters.
# The defaults are carefully chosen and should rarely need adjustment.
# Only modify these if you have identified a specific performance issue.
#
# tuning:
# # Maximum number of pending registration entries in the auth cache.
# # Oldest entries are evicted when the cap is reached.
# #
# # register_cache_max_entries: 1024
#
# # NodeStore write batching configuration.
# # The NodeStore batches write operations before rebuilding peer relationships,
# # which is computationally expensive. Batching reduces rebuild frequency.
# #
# # node_store_batch_size: 100
# # node_store_batch_timeout: 500ms
@@ -0,0 +1,17 @@
{% if compose.mode == "dev" %}
SERVER_FQDN_SCHEME=http
SERVER_FQDN={{ (certbot.domains | map("regex_replace", "\\.([^\\.]*)$", ".test") | reject("regex", "^\\*\\.") | list)[0] }}
{% elif compose.mode == "prod" %}
SERVER_FQDN_SCHEME=https
SERVER_FQDN={{ (certbot.domains | reject("regex", "^\\*\\.") | list)[0] }}
{% endif %}
{% if nextcloud.subdomain is defined and nextcloud != None %}
NEXTCLOUD_SUBDOMAIN={{ nextcloud.subdomain }}
{% endif %}
{% if gitea is defined and gitea != None %}
GITEA_SUBDOMAIN={{ gitea.subdomain }}
{% endif %}
{% if gist is defined and gist != None %}
OG_SUBDOMAIN={{ gist.subdomain }}
{% endif %}
@@ -0,0 +1,196 @@
# GENERAL
max-concurrent-downloads={{ aria.dl.max.concurrent | string }}
dir={{ ansible_user_home.stdout + "/downloads/aria2" }}
log={{ ansible_user_home.stdout + "/.aria2/aria2.log" }}
log-level={{ aria.log }}
console-log-level=notice
continue=true
{% if aria.dl.resume %}
always-resume=true
{% else %}
always-resume=false
{% endif %}
{% if aria.dl.overwriting %}
allow-overwrite=true
{% else %}
allow-overwrite=false
{% endif %}
{% if aria.dl.autorenaming %}
auto-file-renaming=true
{% else %}
auto-file-renaming=false
{% endif %}
file-allocation={{ aria.alloc }}
disk-cache={{ aria.dcache | string }}
enable-mmap=true
enable-color=true
human-readable=true
keep-unfinished-download-result=true
max-download-result=500
max-resume-failure-tries=0
# RPC
{% if aria.rpc.enabled %}
enable-rpc=true
{% if mode == "prod" or aria.rpc.scheme == "https" %}
rpc-secure=true
{% if not certbot.containerized %}
rpc-certificate={{ "/etc/letsencrypt/live/" + web_fqdn + "/fullchain.pem" }}
rpc-private-key={{ "/etc/letsencrypt/live/" + web_fqdn + "/privkey.pem" }}
{% else %}
rpc-certificate={{ ansible_user_home.stdout + "/.config/letsencrypt/live/" + web_fqdn + "/fullchain.pem" }}
rpc-private-key={{ ansible_user_home.stdout + "/.config/letsencrypt/live/" + web_fqdn + "/privkey.pem" }}
{% endif %}
{% endif %}
{% if aria.rpc.listen_all %}
rpc-listen-all=true
{% else %}
rpc-listen-all=false
{% endif %}
rpc-listen-port={{ aria.rpc.port | string }}
{% if aria.rpc.credentials is undefined or aria.rpc.credentials == None %}
rpc-secret={{ aria.rpc.secret | default(lookup('password', './.tmp/' + inventory_hostname + '-aria/aria.secret', chars=['ascii_lowercase', 'digits'], length=32)) }}
{% else %}
rpc-user={{ aria.rpc.credentials.username }}
rpc-password={{ aria.rpc.credentials.password }}
{% endif %}
{% else %}
enable-rpc=false
{% endif %}
# (HT/SF/F)TP
{% if aria.conn.proxy.enabled %}
all-proxy={{ aria.conn.proxy.uri }}
no-proxy={{ aria.conn.proxy.excluded | join(",") }}
{% endif %}
max-connection-per-server={{ aria.conn.max.per_server | string }}
split={{ aria.conn.split | string }}
max-tries={{ aria.conn.max.attempts | string }}
retry-wait=15
netrc-path={{ ansible_user_home.stdout + "/.netrc" }}
server-stat-if={{ ansible_user_home.stdout + "/.aria2/dl.log" }}
server-stat-of={{ ansible_user_home.stdout + "/.aria2/dl.log" }}
uri-selector={{ aria.dl.algorithm }}
# HTTP
{% if aria.http.gzip %}
http-accept-gzip=true
{% else %}
http-accept-gzip=false
{% endif %}
{% if aria.http.cache %}
http-no-cache=true
{% else %}
http-no-cache=false
{% endif %}
{% if aria.http.sustain %}
enable-http-keep-alive=true
{% else %}
enable-http-keep-alive=false
{% endif %}
{% if aria.http.agent is defined and aria.http.agent != None %}
user-agent={{ aria.http.agent }}
{% endif %}
# (S)FTP
{% if aria.ftp.mode == "passive" %}
ftp-pasv=true
{% elif aria.ftp.mode == "active" %}
ftp-pasv=false
{% endif %}
ftp-type={{ aria.ftp.data_type }}
# METALINKS/TORRENTS
# TORRENTS
bt-detach-seed-only=true
{% if aria.trnt.local_discovery %}
bt-enable-lpd=true
{% else %}
bt-enable-lpd=false
{% endif %}
{% if aria.trnt.encrypt %}
bt-force-encryption=true
{% else %}
bt-force-encryption=false
{% endif %}
bt-max-peers={{ aria.trnt.peers.max }}
{% if aria.trnt.trackers is defined and aria.trnt.trackers != None and (aria.trnt.trackers | length) > 0 %}
bt-tracker={{ aria.trnt.trackers | join(",") }}
{% endif %}
{% if aria.trnt.dht.enabled %}
enable-dht=true
enable-dht6=true
dht-file-path={{ ansible_user_home.stdout + "/.aria2/dht.dat" }}
dht-file-path6={{ ansible_user_home.stdout + "/.aria2/dht6.dat" }}
{% if aria.trnt.dht.entrypoint is defined and aria.trnt.dht.entrypoint != None %}
dht-entry-point={{ aria.trnt.dht.entrypoint }}
dht-entry-point6={{ aria.trnt.dht.entrypoint }}
{% endif %}
dht-listen-port={{ aria.trnt.dht.port | string }}
{% endif %}
{% if aria.trnt.peers.agent is defined and aria.trnt.peers.agent != None %}
peer-agent={{ aria.trnt.peers.agent }}
{% endif %}
{% if aria.trnt.peers.prefix is defined and aria.trnt.peers.prefix != None %}
peer-id-prefix={{ aria.trnt.peers.prefix }}
{% endif %}
{% if aria.trnt.peers.exchange %}
enable-peer-exchange=true
{% else %}
enable-peer-exchange=false
{% endif %}
seed-ratio={{ aria.trnt.seeding.ratio | string }}
seed-time={{ aria.trnt.seeding.time | string }}
max-overall-upload-limit={{ aria.ul.max.overall | string }}
max-upload-limit={{ aria.ul.max.per_entry | string }}
# METALINK
{% if aria.meta.follow == "mem" %}
follow-metalink={{ aria.meta.follow }}
{% elif aria.meta.follow %}
follow-metalink=true
{% else %}
follow-metalink=false
{% endif %}
metalink-language={{ aria.meta.lang }}
{% if aria.meta.locs is defined and aria.meta.locs != None and (aria.meta.locs | length) > 0 %}
metalink-location={{ aria.meta.locs | join(",") }}
{% endif %}
metalink-preferred-protocol={{ aria.meta.protocol_pref }}
@@ -0,0 +1,14 @@
CERTBOT_EMAIL="{{ certbot.email }}"
CERTBOT_AUTHENTICATOR={{ certbot.auth_method }}
SERVER_WEBROOT_PATH={{ ansible_user_home.stdout + "/srv/certbot." + (fqdn | default(inventory_hostname)) }}
{% if compose.mode == "dev" %}
# @TODO find a better way to have processed the below in Ansible
CERTBOT_CHALLENGE_DOMAINS={{ certbot.domains | map("regex_replace", "\\.([^\\.]*)$", ".test") | list | join(",") }}
STAGING=1
DEBUG=1
VERBOSE=1
RUN_ONCE=1
{% elif compose.mode == "prod" %}
CERTBOT_CHALLENGE_DOMAINS={{ certbot.domains | join(",") }}
CRON={{ certbot.cron }}
{% endif %}
@@ -0,0 +1,27 @@
http://<< web_fqdn >> {
handle /.well-known/acme-challenge/* {
reverse_proxy localhost:80
}
handle {
redir https://{host}{uri} 308
}
}
:80 {
root /srv/certbot.<< web_fqdn >>
browse
}
<< web_fqdn >> {
respond 503
# root /srv/<< web_fqdn >>
file_server
header /.well-known/openpgpkey/* {
Content-Type application/octet-stream
Access-Control-Allow-Origin *
}
handle_errors {
rewrite /error/{err.status_code}.html
templates
}
}
@@ -0,0 +1,15 @@
[storage]
# Storage driver - use overlay with fuse-overlayfs for rootless
driver = "overlay"
# Root directory for storage (default: $HOME/.local/share/containers/storage)
# Uncomment to customize:
graphroot = "{{ ansible_user_home.stdout }}/volumes"
# Run directory for transient data
runroot = "/run/user/1000/containers"
[storage.options]
[storage.options.overlay]
# Use fuse-overlayfs for rootless overlay support on older kernels
mount_program = "/usr/bin/fuse-overlayfs"
@@ -0,0 +1,339 @@
server:
port: {{ glance.port }}
{% if glance.proxied %}
proxied: true
{% endif %}
base-url: /
pages:
- name: home
head-widgets:
- type: search
search-engine: {{ glance.search.engine }}
bangs:
- title: Linux Kernel
shortcut: "@linuxkernel"
url: "https://www.kernel.org/doc/html/latest/search.html?q={QUERY}"
- title: Linux
shortcut: "@linux"
url: https://cse.google.com/cse?cx=017644269519104757279%3Agm62gtzaoky&q={QUERY}&sa=go
- title: Debian
shortcut: "@deb"
url: https://search.debian.org/cgi-bin/omega?DB=en&P={QUERY}
- title: C++
shortcut: "@cpp"
url: https://learn.microsoft.com/en-us/search/?scope=C%2B%2B&view=msvc-170&terms={QUERY}
- title: Rust
shortcut: "@rust"
url: https://doc.rust-lang.org/book/?search={QUERY}
- title: Python
shortcut: "@python"
url: https://docs.python.org/3/search.html?check_keywords=yes&area=default&q={QUERY}
- title: Python Click
shortcut: "@pyclick"
url: https://click.palletsprojects.com/en/stable/search/?q={QUERY}
- title: Ansible
shortcut: "@ansible"
url: https://docs.ansible.com/projects/ansible/latest/search.html?q={QUERY}&check_keywords=yes&area=default
- title: Podman
shortcut: "@podman"
url: https://docs.podman.io/en/latest/search.html?q={QUERY}
- title: Podman Compose
shortcut: "@podcompose"
url: https://docs.podman.io/en/latest/search.html?q={QUERY}
- title: Elixir
shortcut: "@elixir"
url: https://hexdocs.pm/?packages=elixir%3A1.20.2%2Ceex%3A1.20.2%2Cex_unit%3A1.20.2%2Ciex%3A1.20.2%2Clogger%3A1.20.2%2Cmix%3A1.20.2&q={QUERY}
- title: Elixir Phoenix
shortcut: "@exphoenix"
url: https://hexdocs.pm/?q={QUERY}&packages=plug%3Alatest%2Cphoenix%3Alatest%2Cphoenix_html%3Alatest%2Cphoenix_live_view%3Alatest%2Cphoenix_pubsub%3Alatest%2Cphoenix_template%3Alatest
- title: Javascript
shortcut: "@js"
shortcut: https://javascript.info/search?query={QUERY}
- title: MDN
shortcut: "@mdn"
url: https://developer.mozilla.org/en-US/search?q={QUERY}
- title: PHP
shortcut: "@php"
url: https://www.php.net/search.php#gsc.tab=0&gsc.q={QUERY}&gsc.sort=
- title: PostgreSQL
shortcut: "@pgsql"
url: https://www.postgresql.org/search/?q={QUERY}
- title: MySQL
shortcut: "@mysql"
url: https://dev.mysql.com/doc/search/?q={QUERY}
- title: Julia
shortcut: "@julia"
url: https://docs.julialang.org/en/v1/?q={QUERY}
- title: Gitea
shortcut: "@gitea"
url: https://docs.gitea.com/search/?q={QUERY}
- title: Git
shortcut: "@git"
url: https://git-scm.com/search/results?search={QUERY}&language=en
- title: Elixir Phoenix
shortcut: "@exphoenix"
url: https://hexdocs.pm/?q={QUERY}&packages=plug%3Alatest%2Cphoenix%3Alatest%2Cphoenix_html%3Alatest%2Cphoenix_live_view%3Alatest%2Cphoenix_pubsub%3Alatest%2Cphoenix_template%3Alatest
# @TODO add more documentation searches
- type: group
widgets:
- type: clock
hour-format: 24h
timezones:
- timezone: {{ glance.timezone.id}}
label: {{ glance.timezone.name }}
- type: weather
units: metric
hour-format: 24h
location: {{ glance.weather_loc }}
columns:
- size: small
widgets:
- type: calendar
first-day-of-week: sunday
- type: bookmarks
groups:
- title: strat
links:
- title: The New Oil
url: https://thenewoil.org/en/
- title: Resilient by Design
url: https://theanarchistlibrary.org/library/the-techno-anarchist-resilient-by-design
- title: complang dox
links:
- name: Raspberry Pi
url: https://www.raspberrypi.com/documentation/
- name: Arduino
url: https://docs.arduino.cc/
- name: NASM x86 Assembly
url: https://www.tutorialspoint.com/assembly_programming/index.htm
- name: WASM
url: https://webassembly.org/docs/faq/
- name: Linux Kernel
url: https://www.kernel.org/doc/html/latest/index.html
- name: Linux
url: https://tldp.org/guides.html
- name: Debian
url: https://www.debian.org/doc/
- name: Zig
url: https://ziglang.org/documentation/
- name: Rust
url: https://doc.rust-lang.org/
- name: Cargo
url: https://doc.rust-lang.org/cargo/
- name: C++
url: https://learn.microsoft.com/en-us/cpp/
- name: vcpkg
url: https://learn.microsoft.com/en-us/vcpkg/
- name: C#
url: https://learn.microsoft.com/en-us/dotnet/csharp/
- name: nuget
url: https://learn.microsoft.com/en-us/nuget/
- name: Python
url: https://docs.python.org/
- name: Python uv
url: https://docs.astral.sh/uv/
- name: Ansible
url: https://docs.ansible.com/projects/ansible/latest/
- name: Podman
url: https://docs.podman.io/en/latest/
- name: Podman Compose
url: https://docs.podman.io/en/latest/markdown/podman-compose.1.html
- name: Erlang
url: https://www.erlang.org/docs.html
- name: Elixir
url: https://elixir-lang.org/docs.html
- name: Hex
url: https://hex.pm/docs/usage
- name: Lua
url: https://www.lua.org/docs.html
- name: LuaRocks
url: https://luarocks.org/docs
- name: GDScript
url: https://docs.godotengine.org/en/latest/tutorials/scripting/gdscript/index.html
- name: Julia
url: https://docs.julialang.org/
- name: NodeJS
url: https://nodejs.org/docs/latest/api/
- name: NPMJS
url: https://docs.npmjs.com/
- name: Go
url: https://go.dev/doc/
- name: CSS
url: https://www.w3.org/Style/CSS/Overview.en.html
- name: HTML
url: https://html.spec.whatwg.org/multipage/
- name: Javascript
url: https://javascript.info/
- name: Javascript DOM
url: https://dom.spec.whatwg.org/
- name: JSON
url: https://www.json.org/json-en.html
- name: Client-Side Web Languages
url: https://developer.mozilla.org/en-US/
- name: PHP
url: https://www.php.net/docs.php
- name: PHP Composer
url: https://getcomposer.org/doc/
- name: SQL
url: https://www.postgresql.org/docs/current/sql.html
- name: SASS
url: https://sass-lang.com/documentation/
- name: YAML
url: https://www.yaml.info/
- name: TOML
url: https://toml.io/en/
- name: ActivityPub
url: https://activitypub.rocks/
- name: SuperCollider
url: https://doc.sccode.org/
- name: CSound
url: https://csound.com/docs/manual/index.html
- name: FAUST
url: https://faustdoc.grame.fr/
- name: regexp
url: https://regexbox.com/cheatsheet
- title: warez dox
links:
- name: glance
url: https://github.com/glanceapp/glance/tree/main/docs
- name: Nextcloud
url: https://docs.nextcloud.com/
- name: Wireguard
url: https://www.wireguard.com/#conceptual-overview
- name: Headscale
url: https://docs.headscale.org/
- name: Tailscale
url: https://tailscale.com/docs
- name: Gitea
url: https://docs.gitea.com/
- name: OpenGist
url: https://opengist.io/docs/
- name: Caddy
url: https://caddyserver.com/docs/
- name: MySQL
url: https://dev.mysql.com/doc/
- name: Certbot
url: https://eff-certbot.readthedocs.io/en/stable/
- name: git
url: https://git-scm.dev/doc
# - title: api
# - title: src
# - title: warez
- size: full
widgets:
- type: split-column
widgets:
- type: rss
title: technoscience
style: detailed-list
collapse-after: 5
feeds:
- url: https://feeds.arstechnica.com/arstechnica/features
title: arstechnica
- url: https://rss.slashdot.org/Slashdot/slashdotMain
title: slashdot
- url: https://torrentfreak.com/feed/
title: torrentfreak
- url: https://blog.p2pfoundation.net/feed/
title: p2p foundation
- url: https://www.wired.com/feed/category/backchannel/latest/rss
title: wired
- url: https://www.quantamagazine.org/feed/
title: quanta
- url: https://www.sciencedaily.com/rss/matter_energy/telecommunications.xml
title: scidaily - telecomms
- url: https://www.sciencedaily.com/rss/computers_math/quantum_computers.xml
title: scidaily - quancomp
- url: https://www.sciencedaily.com/rss/matter_energy/engineering_and_construction.xml
title: scidaily - engi
- url: https://www.sciencedaily.com/rss/matter_energy/energy_and_resources.xml
title: scidaily - energy & fuel
- url: https://www.sciencedaily.com/rss/earth_climate/energy.xml
title: scidaily - climate change
- url: https://www.sciencedaily.com/rss/earth_climate/global_warming.xml
title: scidaily - global warming
- url: https://www.sciencedaily.com/rss/earth_climate/sustainability.xml
title: scidaily - sustainability
- url: https://www.sciencedaily.com/rss/computers_math/computer_programming.xml
title: scidaily - compprog
- url: https://www.sciencedaily.com/rss/computers_math/information_technology.xml
title: scidaily - infotech
- url: https://phys.org/rss-feed/biology-news/ecology/
title: phys - ecology
- type: rss
title: politics
style: detailed-list
collapse-after: 5
feeds:
- url: https://www.democracynow.org/democracynow.rss
title: democracy now
- url: https://www.nakedcapitalism.com/feed
title: naked capitalism
- url: https://www.thenews.coop/feed/
title: co-operative news
- url: http://feeds.propublica.org/propublica/main
title: propublica
- url: https://unicornriot.ninja/feed/rss/
title: unicorn riot
- url: https://crimethinc.com/feed
title: crimethinc
- url: https://anarchistnews.org/rss.xml
title: anarchist news
- type: rss
title: misc
style: detailed-list
collapse-after: 5
feeds:
- url: https://www.radicalphilosophy.com/feed
title: radical philosophy
- url: http://spectrejournal.com/feed/rss
title: spectre
- url: https://nautil.us/feed
title: nautilus
- url: https://theconversation.com/us/home-page/articles.atom
title: the conversation
- url: https://thebaffler.com/latest/feed
title: the baffler
- type: videos
style: horizontal-cards
channels:
- UCjEDZ_R_ypSc-MUBRkiW1lw
- UCIZ5ZOeiXYbmKTl_85ghNPw
- UCdcemy56JtVTrsFIOoqvV8g
- UCSkzHxIcfoEr69MWBdo0ppg
- UCW6TXMZ5Pq6yL6_k5NZ2e0Q
- UC4a-Gbdw7vOaccHmFo40b9g
- UCwbyKKmjVdCpWzZZY-WnajA
- UCMMBpWfWUd3xlcOxrot_neA
- UCJXa3_WNNmIpewOtCHf3B0g
- UCmfF7JZv26UUKyRedViGIlw
- UCEmQRq5bxIUNGvAWj41AoaA
- UCHkYOD-3fZbuGhwsADBd9ZQ
- UCHa8J-xnRYOg5VuudfWpBgg
- UCJZv4d5rbIKd4QHMPkcABCw
- UCZUyPT9DkJWmS_DzdOi7RIA
- UChbS_z6KHQiIu9et38O37eQ
- UCxX9wt5FWQUAAz4UrysqK9A
- UCX6b17PVsYBQ0ip5gyeme-Q
- UCk0fGHsCEzGig-rSzkfCjMw
- UCYO_jab_esuFRV4b17AJtAw
- UChKIQpndVpX1ung-7IkGhzA
- UCaM7SQvF5q9sz4NgL16PNRA
- UC7pdnrWVj8eDfCI0bRe_0kQ
- UCtuXekfqj-paqsxtqVNCC2A
- UCgkjg1UbcQZHW40IO1BEt5A
- UCGKEMK3s-ZPbjVOIuAV8clQ
- UCbiGcwDWZjz05njNPrJU7jA
- UC6biysICWOJ-C3P4Tyeggzg
- UCbi3ei431gvBpgZ3R-_Wk5Q
- UCA5yXa0rDsGaJFOuj6pxw1w
- UCDG73pGqESS1XcEVY_0xwWw
- UCR2uRTQ53V_egXKFflMMaaw
- UCzGUT9PjV3SMBwjWXUYh4HA
- name: services
columns:
- size: full
widgets:
- type: docker-containers
title: status
hide-by-default: false
@@ -0,0 +1,9 @@
db-uri: {{ gist.db.type + "://" + mysql.users.admin.username + ":" + mysql.users.admin.password + "@localhost:3306/" + mysql.users.admin.databases.opengist.name }}
{% if mode == "dev" %}
log-level: debug
{% else %}
log-level: info
{% endif %}
custom.static-links:
- name: Playbooks
path: {{ "https://" + gitea.subdomain + "." + web_fqdn + "/admin/skato-ansible" }}
@@ -0,0 +1,39 @@
{
"TCP": {
<% if mode == "prod" %>
"443": {
"HTTPS": true
}
<% elif mode == "dev" or tailscale.scheme == "http" %>
"80": {
"HTTPS": false
}
<% endif %>
},
"Web": {
<% if mode == "prod" %>
"${TS_CERT_DOMAIN}:443": {
"Handlers": {
"/": {
"Proxy": "http://localhost:8080"
}
}
}
<% elif mode == "dev" or tailscale.scheme == "http" %>
"${TS_CERT_DOMAIN}:80": {
"Handlers": {
"/": {
"Proxy": "http://localhost:8080"
}
}
}
<% endif %>
},
"AllowFunnel": {
<% if mode == "prod" %>
"${TS_CERT_DOMAIN}:443": false
<% elif mode == "dev" or tailscale.scheme == "http" %>
"${TS_CERT_DOMAIN}:80": false
<% endif %>
}
}
@@ -0,0 +1,10 @@
{% if email.smtp is defined or email.smtp != None %}
EMAIL_SEND_PROTOCOL=smtp
EMAIL_SMTP_HOST={{ email.smtp.host }}
EMAIL_SMTP_PORT={{ email.smtp.port }}
EMAIL_SMTP_SECURE={{ email.smtp.conn_mode }}
EMAIL_SMTP_USERNAME={{ email.smtp.username }}
EMAIL_SMTP_AUTHTYPE={{ email.smtp.auth_meth.upper() }}
EMAIL_SMTP_USER_ALIAS={{ (email.smtp.alias | split("@"))[0] }}
EMAIL_SMTP_EMAIL_ALIAS={{ (email.smtp.alias | split("@"))[1] }}
{% endif %}
@@ -0,0 +1,148 @@
templates:
limited_series:
configure_series:
from:
filesystem:
path:
- ~/media/vids/series
# - ~/media/vids/features
recursive: false
retrieve: dirs
settings:
identified_by: ep
path: '~/media/vids/series/{{ series_name }}'
target: 1080p
timeframe: 2 weeks
parse_only: true
exists_series:
path: '~/media/vids/series/{{ series_name }}'
allow_different_qualities: better
feature_films:
csv:
url: 'file://%7e/media/vids/features/.films.csv'
values:
title: 1
url: 2
list_add:
- entry_list: films
porn_vids:
csv:
url: 'file://%7e/.xxx/.vids.csv'
values:
title: 1
url: 2
list_add:
- entry_list: pornos
tasks:
# @NOTE uncommenting aria2 block requires commenting out download and proxy blocks
populate_folders:
{% if download_mode == 'direct' %}
proxy:
https: 'socks5://<< tor.proxy.hostname >>:<< tor.proxy.port >>'
{% endif %}
if:
- "'batch' in title.lower()": reject
discover:
what:
- next_series_episodes:
from_start: true
backfill: false
from:
- nyaa:
category: anime eng
filter: trusted only
- search_rss:
url: 'https://nyaa.si/?f=2&c=1_2&q={{ search_term }}'
link:
- magneturi
- link
all_entries: false
- eztv: true
interval: 1 week
limit: 80
template: limited_series
{% if flexget.download_mode == 'aria2' %}
aria2:
server: << aria.rpc.hostname >>
port: << aria.rpc.port >>
{% if aria.rpc.secret != None and aria.rpc.secret is defined %}
secret: << aria.rpc.secret >>
{% elif aria.rpc.credentials != None and aria.rpc.credentials is defined %}
username: << aria.rpc.credentials.username >>
password: << aria.rpc.credentials.password >>
{% endif %}
{% if mode == 'prod' %}
scheme: << aria.rpc.scheme >>
{% else %}
scheme: http
{% endif %}
rpc_mode: json
rpc_path: jsonrpc
path: '~/downloads/media/vids/{{ series_name }}'
{% else %}
download:
temp: ~/.tmp
path: '~/downloads/flexget/vids/series/torrents/{{ series_name }}'
{% endif %}
# @NOTE uncommenting aria2 block requires commenting out download and proxy blocks
fap_queue:
{% if download_mode == 'direct' %}
proxy:
https: 'socks5://<< tor.proxy.hostname >>:<< tor.proxy.port >>'
{% endif %}
template: porn_vids
{% if flexget.download_mode == 'aria2' %}
aria2:
server: << aria.rpc.hostname >>
port: << aria.rpc.port >>
{% if aria.rpc.secret != None and aria.rpc.secret is defined %}
secret: << aria.rpc.secret >>
{% endif %}
{% if aria.rpc.credentials != None and aria.rpc.credentials is defined %}
username: << aria.rpc.credentials.username >>
password: << aria.rpc.credentials.password >>
{% endif %}
{% if mode == 'prod' %}
scheme: << aria.rpc.scheme >>
{% else %}
scheme: http
{% endif %}
rpc_mode: json
rpc_path: jsonrpc
path: ~/downloads/.xxx/media/vids
{% elif flexget.download_mode == 'direct' %}
download:
temp: ~/.tmp
path: ~/downloads/.xxx/flexget/vids/torrents
{% endif %}
# @NOTE uncommenting aria2 block requires commenting out download and proxy blocks
film_queue:
{% if download_mode == 'direct' %}
proxy:
https: 'socks5://<< tor.proxy.hostname >>:<< tor.proxy.port >>'
{% endif %}
template: feature_films
{% if flexget.download_mode == 'aria2' %}
aria2:
server: << aria.rpc.hostname >>
port: << aria.rpc.port >>
{% if aria.rpc.secret != None or aria.rpc.secret is defined %}
secret: << aria.rpc.secret >>
{% endif %}
{% if aria.rpc.credentials != None or aria.rpc.credentials is defined %}
username: << aria.rpc.credentials.username >>
password: << aria.rpc.credentials.password >>
{% endif %}
{% if mode == 'prod' %}
scheme: << aria.rpc.scheme >>
{% else %}
scheme: http
{% endif %}
rpc_mode: json
rpc_path: jsonrpc
path: ~/downloads/media/vids
{% elif flexget.download_mode == 'direct' %}
download:
temp: ~/.tmp
path: ~/downloads/flexget/vids/torrents
{% endif %}
@@ -0,0 +1,17 @@
GITEA_MODE={{ mode | default("dev") }}
GITEA_NAME={{ (web_fqdn | split("."))[0].upper() + " Nous" }}
GITEA_SSH_PORT={{ gitea.ssh.port }}
GITEA_LANDING={{ gitea.site.landing }}
GITEA_TRUSTED_PROXIES={{ gitea.trusted.proxies | join(",") }}
{% if gitea.site.registration.enabled %}
GITEA_PROHIBIT_REGISTRATION=false
{% else %}
GITEA_PROHIBIT_REGISTRATION=true
{% endif %}
GITEA_DB_TYPE={{ gitea.db.type }}
{% if gitea.db.type == "mysql" %}
GITEA_DB_NAME={{ gitea.db.name }}
{% endif %}
@@ -0,0 +1,12 @@
+++
title = '{{ web_fqdn }}'
draft = false
+++
![sisyphus-with-boulder](./mythe-sisyphus-klein.png)
## Under Construction
### Err. 503: Service Unavailable
Working on building or migrating the content, layout or theme for this blog.
@@ -0,0 +1,11 @@
---
title: {{ web_fqdn }}
---
![sisyphus-with-boulder](./mythe-sisyphus-klein.png)
## Under Construction
### Err. 503: Service Unavailable
Working on building or migrating Obsidian vault entries for this server.
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

@@ -0,0 +1,3 @@
MYSQL_DB_HOST=localhost
MYSQL_DB_USER={{ mysql.users.admin.username }}
MYSQL_DB_NAME={{ mysql.users.admin.databases.init.name }}
@@ -0,0 +1,3 @@
machine {{ source_code.host }}
login {{ source_code.user | default(source_code.ssh_user) }}
password {{ source_code.http_password | default(source_code.ssh_password) }}
@@ -0,0 +1,19 @@
{% if nextcloud.db.type == "mysql" or nextcloud.db.type == "mariadb" %}
NEXTCLOUD_DB_NAME={{ nextcloud.db.name | default("nextcloud") }}
{% elif nextcloud.db.type == "sqlite" %}
SQLITE_DATABASE={{ nextcloud.db.name + ".db" }}
{% endif %}
{% if not nextcloud.rewrite_ip %}
NEXTCLOUD_APACHE_DISABLE_REWRITE_IP=1
{% endif %}
NEXTCLOUD_TRUSTED_PROXIES={{ nextcloud.trusted.proxies | join(" ") }}
NEXTCLOUD_TRUSTED_FQDNS={{ "cloud." + web_fqdn }}
{% if compose.mode == "prod" %}
NEXTCLOUD_URL_REWRITE={{ "https://cloud." + web_fqdn }}
NEXTCLOUD_PROTOCOL_REWRITE=https
{% elif compose.mode == "dev" %}
NEXTCLOUD_URL_REWRITE={{ "http://cloud." + web_fqdn }}
NEXTCLOUD_PROTOCOL_REWRITE=http
{% endif %}
@@ -0,0 +1,18 @@
OG_DB_TYPE={{ gist.db.type }}
{% if gist.db.type == "mysql" %}
OG_DB_NAME={{ gist.db.name }}
{% endif %}
OG_SEARCH_DEFAULT={{ gist.search.priority | join(",") }}
{% if ssh is defined and ssh != None %}
OG_SSH_PORT={{ gist.ssh.port }}
OG_SSH_GIT_ENABLED=true
{% else %}
OG_SSH_GIT_ENABLED=false
{% endif %}
OG_NAME={{ (web_fqdn | split("."))[0].upper() + " Grimoire" }}
OG_LOGO=logo.svg
OG_FAVICON=logo.ico
@@ -0,0 +1 @@
REDIS_PARAMS={{ "--requirepass " + redis.password + " --appendonly yes" }}
@@ -18,7 +18,7 @@ set number
" Jump to line by relative number
set relativenumber
{% if (vim_blockedcursor | default(True)) %}
{% if (vimrc.cursor.blocky | default(True)) %}
" Highlight cursor line underneath the cursor horizontally.
set cursorline
@@ -26,13 +26,13 @@ set cursorline
" Highlight cursor line underneath the cursor vertically.
set cursorcolumn
{% endif %}
{% if (vim_spatialize | default(True)) %}
{% if (vim.tabs.spatialize | default(True)) %}
" Use space characters instead of tabs.
set expandtab
" Set tab width to 4 columns.
set tabstop={{ vim_tabgap | default(2) }}
set tabstop={{ vimrc.tabs.gap | default(2) }}
{% endif %}
" While searching though a file incrementally highlight matching characters as you type.
@@ -48,19 +48,19 @@ set showmatch
set hlsearch
" Set the commands to save in history default number is 20.
set history={{ vim_historyspan | default(20) }}
{% if (vim_pathcompletion | default(True)) %}
set history={{ vimrc.history_span | default(20) }}
{% if (vimrc.typed_paths.autocomplete | default(True)) %}
" Enable auto completion menu after pressing TAB.
set wildmenu
" Make wildmenu behave like similar to Bash completion.
set wildmode=list:longest
{% if vimignore is defined or vimignore != None %}
{% if vimrc.typed_paths.ignore is defined and vimrc.typed_paths.ignore != None %}
" There are certain files that we would never want to edit with Vim.
" Wildmenu will ignore files with these extensions.
set wildignore={{ vimignore | join(',') }}
set wildignore={{ vimrc.typed_paths.ignore | join(',') }}
{% endif %}
{% endif %}
@@ -71,20 +71,20 @@ set wildignore={{ vimignore | join(',') }}
call plug#begin('~/.vim/plugged')
Plug 'flazz/vim-colorschemes'
{% if (vim_eddelimiters | default(True)) %}
{% if (vimrc.autodelimit | default(True)) %}
Plug 'tpope/vim-surround'
{% endif %}
{% if (vim_gitspice | default(True)) %}
{% if (vimrc.git_spice | default(True)) %}
Plug 'airblade/vim-gitgutter'
Plug 'tpope/vim-fugitive'
{% endif %}
{% if (vim_statusline | default(True)) %}
{% if (vimrc.status_line | default(True)) %}
Plug 'vim-airline/vim-airline'
{% endif %}
{% if (vim_fancycomment | default(True)) %}
{% if (vimrc.fancy_commenting | default(True)) %}
Plug 'scrooloose/nerdcommenter'
{% endif %}
{% if (vim_idelangs | default(True)) %}
{% if (vimrc.ide | default(True)) %}
Plug 'dense-analysis/ale'
{% endif %}
@@ -95,7 +95,7 @@ call plug#end()
" Mappings code goes here.
" VIMSCRIPT --------------------------------------------------------------
{% if (vim_origami | default(True)) %}
{% if (vimrc.origami | default(True)) %}
" This will enable code folding.
" Use the marker method of folding.
@@ -0,0 +1,22 @@
#!/bin/bash
set -euo pipefail
if [[ "$1" == "start" ]]; then
/usr/sbin/iptables -A FORWARD -i dsnet -p tcp --sport 80 -j ACCEPT # HTTP
/usr/sbin/iptables -A FORWARD -i dsnet -p tcp --sport 443 -j ACCEPT # HTTPS (HTTP + SSL/TLS)
/usr/sbin/iptables -A FORWARD -i dsnet -p udp --sport 443 -j ACCEPT # HTTPS (HTTP + SSL/TLS)
/usr/sbin/iptables -A FORWARD -i dsnet -p tcp --sport 465 -j ACCEPT # SMTP (send)
/usr/sbin/iptables -A FORWARD -i dsnet -p tcp --sport 587 -j ACCEPT # SMTP (send)
/usr/sbin/iptables -A FORWARD -i dsnet -p tcp --sport 995 -j ACCEPT # POP3 (receive)
/usr/sbin/iptables -A FORWARD -i dsnet -p tcp --sport 993 -j ACCEPT # IMAP (receive)
/usr/sbin/iptables -t nat -A POSTROUTING -o dsnet -j MASQUERADE
elif [[ "$1" == "stop" ]]; then
/usr/sbin/iptables -D FORWARD -i dsnet -p tcp --sport 80 -j ACCEPT # HTTP
/usr/sbin/iptables -D FORWARD -i dsnet -p tcp --sport 443 -j ACCEPT # HTTPS (HTTP + SSL/TLS)
/usr/sbin/iptables -D FORWARD -i dsnet -p udp --sport 443 -j ACCEPT # HTTPS (HTTP + SSL/TLS)
/usr/sbin/iptables -D FORWARD -i dsnet -p tcp --sport 465 -j ACCEPT # SMTP (send)
/usr/sbin/iptables -D FORWARD -i dsnet -p tcp --sport 587 -j ACCEPT # SMTP (send)
/usr/sbin/iptables -D FORWARD -i dsnet -p tcp --sport 995 -j ACCEPT # POP3 (receive)
/usr/sbin/iptables -D FORWARD -i dsnet -p tcp --sport 993 -j ACCEPT # IMAP (receive)
/usr/sbin/iptables -t nat -D POSTROUTING -o dsnet -j MASQUERADE
fi
+181
View File
@@ -0,0 +1,181 @@
#!/bin/bash
set -euo pipefail
# @TODO long-term, turn this simple bash script into more complex Python Click project
SKANSIBLE_SCRIPT_PATH=$(dirname "$0")
export ANSIBLE_FORCE_COLOR=True
SKANSIBLE_STAGE=0
ANSIBLE_VERBOSITY=0
SKANSIBLE_UNITTEST=False
SKANSIBLE_MODE=prod
if [[ "$1" == "version" ]]; then
echo "26.07"
fi
if [[ "$1" == "show-defaults" ]]; then
source "${SKANSIBLE_SCRIPT_PATH}/.venv/bin/activate"
printf "Private SSH keys available throufh SSH agent: |\n%s\n" "$(ssh-add -l)"
fi
if [[ "$1" == "start-agent" ]]; then
source "${SKANSIBLE_SCRIPT_PATH}/.venv/bin/activate"
eval "$(ssh-agent -s)"
fi
if [[ "$1" == "list-agent" ]]; then
source "${SKANSIBLE_SCRIPT_PATH}/.venv/bin/activate"
ssh-add -l
fi
if [[ "$1" == "populate-agent" ]]; then
shift 1
source "${SKANSIBLE_SCRIPT_PATH}/.venv/bin/activate"
if [[ "$1" == "all" ]]; then
for SKANSIBLE_SSH_KEY in ~/.ssh/*; do
case $SKANSIBLE_SSH_KEY in
*.pub);;
*.old);;
*.bak);;
*/config);;
*/authorized_keys);;
*_hosts);;
*/agent);;
*) ssh-add "${SKANSIBLE_SSH_KEY}";;
esac
done
elif [[ "$1" == "staging" ]]; then
SKANSIBLE_SSH_KEY_COLLECTION=(~/.ssh/ed25519\@staging ~/.ssh/ecdsa\@staging ~/.ssh/ed25519-37851076-sk\@staging ~/.ssh/ecdsa-37851076-sk\@staging)
shift 1
elif [[ "$1" == "prod" ]]; then
SKANSIBLE_SSH_KEY_COLLECTION=(~/.ssh/ed25519\@staging ~/.ssh/ecdsa\@staging ~/.ssh/ed25519-37851076-sk\@staging ~/.ssh/ecdsa-37851076-sk\@staging)
shift 1
elif [[ -n "$1" ]]; then
# @TODO improve by adding fuzzy querying or file finding pror
ssh-add "$1"
exit 0
fi
for key in "${SKANSIBLE_SSH_KEY_COLLECTION[@]}"; do
ssh-add "$key"
done
exit 0
fi
if [[ "$1" == "init" ]]; then
shift 1
source "${SKANSIBLE_SCRIPT_PATH}/.venv/bin/activate"
if [[ "$1" == "vps" ]]; then
SKANSIBLE_PLAY_HOST="$1"
shift 1
elif [[ "$1" == "homeserver" ]]; then
SKANSIBLE_PLAY_HOST="$1"
shift 1
else
echo "A playbook type must be specified"
exit 1
fi
while getopts "htd:m:s:n:v:u:" opt; do
case $opt in
h) Help "init";;
s) SKANSIBLE_STAGE="$OPTARG";;
n) SKANSIBLE_CONN_HOST="$OPTARG";;
v) export ANSIBLE_VERBOSITY="$OPTARG";;
t) SKANSIBLE_UNITTEST=True;;
u) export ANSIBLE_REMOTE_USER="$OPTARG";;
m) SKANSIBLE_MODE="$OPTARG";;
d) SKANSIBLE_FQDN="$OPTARG";;
*) echo "Err: Invalid option set"; exit 1;;
esac
done
if [ -n "$SKANSIBLE_STAGE" ]; then
# SKANSIBLE_CONN_HOST=staging${SKANSIBLE_STAGE}.test
if [[ "$SKANSIBLE_STAGE" == 0 ]]; then
if [[ "$SKANSIBLE_UNITTEST" == "True" ]]; then
sudo ansible-playbook -CKk -i hosts.yml --extra-vars "chosen_host=${SKANSIBLE_CONN_HOST}" --extra-vars "chosen_user=${ANSIBLE_REMOTE_USER:-root}" --extra-vars "mode=${SKANSIBLE_MODE}" --extra-vars "fqdn=${SKANSIBLE_FQDN:-staging.test}" "init@${SKANSIBLE_PLAY_HOST}.yml"
else
sudo ansible-playbook -Kk -i hosts.yml --extra-vars "chosen_host=${SKANSIBLE_CONN_HOST}" --extra-vars "chosen_user=${ANSIBLE_REMOTE_USER:-root}" --extra-vars "mode=${SKANSIBLE_MODE}" --extra-vars "fqdn=${SKANSIBLE_FQDN:-staging.test}" "init@${SKANSIBLE_PLAY_HOST}.yml"
fi
elif [[ "$SKANSIBLE_STAGE" == 1 ]]; then
if [[ "$SKANSIBLE_UNITTEST" == "True" ]]; then
sudo ansible-playbook -CK -i hosts.yml --extra-vars "chosen_host=${SKANSIBLE_CONN_HOST}" --extra-vars "chosen_user=${ANSIBLE_REMOTE_USER:-root}" --extra-vars "mode=${SKANSIBLE_MODE}" --extra-vars "fqdn=${SKANSIBLE_FQDN:-staging.test}" "init@${SKANSIBLE_PLAY_HOST}.yml"
else
sudo ansible-playbook -K -i hosts.yml --extra-vars "chosen_host=${SKANSIBLE_CONN_HOST}" --extra-vars "chosen_user=${ANSIBLE_REMOTE_USER:-root}" --extra-vars "mode=${SKANSIBLE_MODE}" --extra-vars "fqdn=${SKANSIBLE_FQDN:-staging.test}" "init@${SKANSIBLE_PLAY_HOST}.yml"
fi
fi
else
if [[ -z "$SKANSIBLE_CONN_HOST" ]] && [[ -z "$SKANSIBLE_FQDN" ]]; then
echo "When not doing staging, chosen or preferred host must be made explicit as well as desired FQDN"
exit 1
else
sudo ansible-playbook -Kk -i hosts.yml --extra-vars "chosen_host=${SKANSIBLE_CONN_HOST}" --extra-vars "chosen_user=${ANSIBLE_REMOTE_USER:-root}" --extra-vars "mode=prod" --extra-vars "fqdn=${SKANSIBLE_FQDN}" "init@${SKANSIBLE_PLAY_HOST}.yml"
fi
fi
exit 0
fi
if [[ "$1" == "bootstrap" ]]; then
shift 1
source "${SKANSIBLE_SCRIPT_PATH}/.venv/bin/activate"
if [[ "$1" == "vps" ]]; then
SKANSIBLE_PLAY_HOST="$1"
shift 1
elif [[ "$1" == "homeserver" ]]; then
SKANSIBLE_PLAY_HOST="$1"
shift 1
else
echo "A playbook type must be specified"
exit 1
fi
while getopts "htd:m:s:n:v:u:" opt; do
case $opt in
h) Help "bootstrap";;
s) SKANSIBLE_STAGE="$OPTARG";;
n) SKANSIBLE_CONN_HOST="$OPTARG";;
v) export ANSIBLE_VERBOSITY=4;;
t) SKANSIBLE_UNITTEST=True;;
u) export ANSIBLE_REMOTE_USER="$OPTARG";;
m) SKANSIBLE_MODE="$OPTARG";;
d) SKANSIBLE_FQDN="$OPTARG";;
*) echo "Err: Invalid option set"; exit 1;;
esac
done
if [ -n "$SKANSIBLE_STAGE" ]; then
# SKANSIBLE_CONN_HOST=staging${SKANSIBLE_STAGE}.test
if [[ "$SKANSIBLE_STAGE" == 0 ]]; then
if [[ "$SKANSIBLE_UNITTEST" == "True" ]]; then
ansible-playbook -CKk -i hosts.yml --extra-vars "chosen_host=${SKANSIBLE_CONN_HOST}" --extra-vars "chosen_user=${ANSIBLE_REMOTE_USER:-senpai}" --extra-vars "mode=${SKANSIBLE_MODE}" --extra-vars "fqdn=${SKANSIBLE_FQDN:-staging.test}" "administrate@${SKANSIBLE_PLAY_HOST}.yml"
else
ansible-playbook -Kk -i hosts.yml --extra-vars "chosen_host=${SKANSIBLE_CONN_HOST}" --extra-vars "chosen_user=${ANSIBLE_REMOTE_USER:-senpai}" --extra-vars "mode=${SKANSIBLE_MODE}" --extra-vars "fqdn=${SKANSIBLE_FQDN:-staging.test}" "administrate@${SKANSIBLE_PLAY_HOST}.yml"
fi
elif [[ "$SKANSIBLE_STAGE" == 1 ]]; then
if [[ "$SKANSIBLE_UNITTEST" == "True" ]]; then
ansible-playbook -CK -i hosts.yml --extra-vars "chosen_host=${SKANSIBLE_CONN_HOST}" --extra-vars "chosen_user=${ANSIBLE_REMOTE_USER:-senpai}" --extra-vars "mode=${SKANSIBLE_MODE}" --extra-vars "fqdn=${SKANSIBLE_FQDN:-staging.test}" "administrate@${SKANSIBLE_PLAY_HOST}.yml"
else
ansible-playbook -K -i hosts.yml --extra-vars "chosen_host=${SKANSIBLE_CONN_HOST}" --extra-vars "chosen_user=${ANSIBLE_REMOTE_USER:-senpai}" --extra-vars "mode=${SKANSIBLE_MODE}" --extra-vars "fqdn=${SKANSIBLE_FQDN:-staging.test}" "administrate@${SKANSIBLE_PLAY_HOST}.yml"
fi
fi
else
if [[ -z "$SKANSIBLE_CONN_HOST" ]]; then
echo "When not doing staging, chosen or preferred host must be made explicit as well as desired FQDN"
exit 1
else
ansible-playbook -Kk -i hosts.yml --extra-vars "chosen_host=${SKANSIBLE_CONN_HOST}" --extra-vars "chosen_user=${ANSIBLE_REMOTE_USER:-senpai}" --extra-vars "mode=prod" --extra-vars "fqdn=${SKANSIBLE_FQDN}" "administrate@${SKANSIBLE_PLAY_HOST}.yml"
fi
fi
exit 0
fi
+16
View File
@@ -0,0 +1,16 @@
Vagrant.configure("2") do |config|
config.vm.box = "debian/trixie64"
config.vm.hostname = "staging0.test"
config.vm.network "private_network", ip: "192.168.122.234", bridge: "virbr0", ipv6: true
config.vm.network "forwarded_port", guest: 51820, host: 51820
config.vm.network "forwarded_port", guest: 80, host: 80
config.vm.network "forwarded_port", guest: 443, host: 443
config.ssh.username = "root"
config.ssh.password = "vagrant"
config.vm.provider "libvirt" do |lv|
lv.memory = 2048
lv.cpus = 3
lv.driver = "kvm"
end
config.vm.box_version = "13.20260519.1"
end
+16
View File
@@ -0,0 +1,16 @@
Vagrant.configure("2") do |config|
config.vm.box = "debian/trixie64"
config.vm.hostname = "staging0.test"
config.vm.network "private_network", ip: "192.168.200.201", bridge: "virbr0", ipv6: true
config.vm.network "forwarded_port", guest: 51820, host: 51820
# config.vm.network "forwarded_port", guest: 80, host: 80
# config.vm.network "forwarded_port", guest: 443, host: 443
config.ssh.username = "root"
config.ssh.password = "vagrant"
config.vm.provider "libvirt" do |lv|
lv.memory = 2048
lv.cpus = 3
lv.driver = "kvm"
end
config.vm.box_version = "13.20260519.1"
end
+146
View File
@@ -0,0 +1,146 @@
# @TODO use Ansible vault for secret and some credentials values
aria:
containerized: true
rpc:
enabled: false
listen_all: true
scheme: http
hostname: localhost
port: 6800
secret: ~
credentials: ~
# dest: "{{ ansible_user_home.stdout }}/downloads/aria2"
log: warn
alloc: falloc # choices are "falloc", "prealloc", "trunc", or "none"
dcache: 64M
conn:
# netrc:
# path: "{{ ansible_user_home.stdout }}/.netrc"
max:
attempts: 3
per_server: 3
split: 6
proxy:
enabled: true
uri: "localhost:9050"
excluded:
- "127.0.0.1"
- "::1"
dl:
max:
concurrent: 30
resume: true
overwriting: true
autorenaming: true
# stats:
# path: "{{ ansible_user_home.stdout }}/.aria2/dl.log"
algorithm: adaptive # choices are "inorder", "feedback", "adaptive"
ul:
max:
overall: 5M
per_entry: 0
http:
gzip: true
cache: false
sustain: true
agent: "Mozilla/5.0 (X11; Linux x86_64; rv:140.0) Gecko/20100101 Firefox/140.0"
ftp:
mode: passive # choices are "passive" or "active"
data_type: binary # choices are "binary" or "ascii"
trnt:
dht:
enabled: true
port: 6881-6999
entrypoint: ~
# path: "{{ ansible_user_home.stdout }}/.aria2/dht.dat"
# path6: "{{ ansible_user_home.stdout }}/.aria2/dht6.dat"
local_discovery: true
encrypt: true
peers:
agent: "Deluge 1.3.15"
prefix: "-DE13F0-"
max: 100
exchange: true
seeding:
ratio: 2.0
time: 0
trackers:
- http://1337.abcvg.info:80/announce
- http://bt1.archive.org:6969/announce
- http://ipv4announce.sktorrent.eu:6969/announce
- http://nyaa.tracker.wf:7777/announce
- http://torrentsmd.com:8080/announce
- http://tracker.bt4g.com:2095/announce
- http://tracker.dhitechnical.com:6969/announce
- http://tracker.mywaifu.best:6969/announce
- http://tracker.renfei.net:8080/announce
- http://tracker.waaa.moe:6969/announce
- http://tracker.xn--djrq4gl4hvoi.top:80/announce
- http://www.wareztorrent.com:80/announce
- https://021912.xyz:443/announce
- https://1337.abcvg.info:443/announce
- https://banananetwork.qzz.io:443/announce
- https://orgtgju.org:443/announce
- https://t.213891.xyz:443/announce
- https://torrents.tmtime.dev:443/announce
- https://tr.abiir.top:443/announce
- https://tr.nyacat.pw:443/announce
- https://tr.zukizuki.org:443/announce
- https://tracker.7471.top:443/announce
- https://tracker.anibt.net:443/announce
- https://tracker.gcrenwp.top:443/announce
- https://tracker.kuroy.me:443/announce
- https://tracker.leechshield.link:443/announce
- https://tracker.manager.v6.navy:443/announce
- https://tracker.nekomi.cn:443/announce
- https://tracker.pmman.tech:443/announce
- https://tracker.zhuqiy.com:443/announce
- https://tracker1.520.jp:443/announce
- udp://anime-tracker.aruku.kro.kr:8081/announce
- udp://bittorrent-tracker.e-n-c-r-y-p-t.net:1337/announce
- udp://coeus.torrentonline.cc:42069/announce
- udp://evan.im:6969/announce
- udp://mail.segso.net:6969/announce
- udp://martin-gebhardt.eu:25/announce
- udp://ns575949.ip-51-222-82.net:6969/announce
- udp://open.demonii.com:1337/announce
- udp://open.ftorrent.com:443/announce
- udp://open.stealth.si:80/announce
- udp://open.tracker.ink:6969/announce
- udp://opentor.org:2710/announce
- udp://p4p.arenabg.com:1337/announce
- udp://seedpeer.net:6969/announce
- udp://t.overflow.biz:6969/announce
- udp://torrentclub.online:1984/announce
- udp://tracker-udp.gbitt.info:80/announce
- udp://tracker.004430.xyz:1337/announce
- udp://tracker.aruku.ovh:8081/announce
- udp://tracker.auctor.tv:6969/announce
- udp://tracker.bittor.pw:1337/announce
- udp://tracker.bluefrog.pw:2710/announce
- udp://tracker.breizh.pm:6969/announce
- udp://tracker.corpscorp.online:80/announce
- udp://tracker.dler.com:6969/announce
- udp://tracker.ducks.party:1984/announce
- udp://tracker.gmi.gd:6969/announce
- udp://tracker.hismz.cn:6969/announce
- udp://tracker.opentorrent.top:6969/announce
- udp://tracker.opentrackr.org:1337/announce
- udp://tracker.peerfect.org:6969/announce
- udp://tracker.publictracker.xyz:6969/announce
- udp://tracker.qu.ax:6969/announce
- udp://tracker.skyts.net:6969/announce
- udp://tracker.t-1.org:6969/announce
- udp://tracker.teambelgium.net:6969/announce
- udp://tracker.torrent.eu.org:451/announce
- udp://tracker.trackarr.org:6969/announce
- udp://tracker.tryhackx.org:6969/announce
- udp://tracker.wildkat.net:6969/announce
- udp://yuptracker-eu.gaijinent.com:27022/announce
- udp://zer0day.ch:1337/announce
- wss://tracker.openwebtorrent.com:443/announce
meta:
follow: mem
lang: en
protocol_pref: ftp # choices are "ftp", "http", "https"
locs: []
+3
View File
@@ -0,0 +1,3 @@
caddy:
containerized: true
scheme: http
+10
View File
@@ -0,0 +1,10 @@
certbot:
mode: "{{ mode | default('prod') }}"
containerized: true
auth_method: webroot
port: ~
email: ajt95@prole.biz
domains:
- "{{ fqdn | default(inventory_hostname) }}"
- "*.{{ fqdn | default(inventory_hostname) }}"
cron: "2 6 24 1 *"
+10
View File
@@ -0,0 +1,10 @@
certbot:
mode: "{{ mode | default('prod') }}"
containerized: false
auth_method: dns
port: 53
email: ajt95@prole.biz
domains:
- "{{ fqdn | default(inventory_hostname) }}"
- "*.{{ fqdn | default(inventory_hostname) }}"
cron: ~
+9
View File
@@ -0,0 +1,9 @@
crowdsec:
containerized: false
port: 6050
colls:
- "crowdsecurity/base-http-scenarios"
- "crowdsecurity/proftpd"
parsers: []
scenarios: []
postoverflows: []
+9
View File
@@ -0,0 +1,9 @@
email:
smtp:
auth_meth: login
host: smtp.startmail.com
port: 465
conn_mode: ssl
username: rika@sukaato.moe
password: "{{ lookup('password', './.tmp/' + inventory_hostname + '-user@email/email.pass', chars=['ascii_lowercase', 'digits'], length=8) }}"
alias: admin@sukaato.moe
+3
View File
@@ -0,0 +1,3 @@
flexget:
enabled: true
download_mode: direct
+57
View File
@@ -0,0 +1,57 @@
git_aliases:
- [ckot, checkout]
- [chbr, checkout]
- [ch_br, checkout]
- [tch_br, "'checkout -b'"]
- [tchbr, "'checkout -b'"]
- [tchbr, "'checkout -b'"]
- [add_br, "'checkout -b'"]
- [addbr, "'checkout -b'"]
- [rm_br, "'branch -D'"]
- [rmbr, "'branch -D'"]
- [del_br, "'branch -D'"]
- [delbr, "'branch -D'"]
- [tch_rmt, "'remote add'"]
- [tchrmt, "'remote add'"]
- [add_rmt, "'remote add'"]
- [addrmt, "'remote add'"]
- [rm_rmt, "'remote remove'"]
- [rmrmt, "'remote remove'"]
- [del_rmt, "'remote remove'"]
- [delrmt, "'remote remove'"]
- [cfg, config]
- [psh, push]
- [snd, push]
- [send, push]
- [post, push]
- [pl, pull]
- [receive, pull]
- [get, pull]
- [fch, fetch]
- [hist, log]
- [scenes, log]
- [br, branch]
- [lsbr, "'branch -v'"]
- [ls_br, "'branch -v'"]
- [rmt, remote]
- [ls_rmt, "'remote -v'"]
- [lsrmt, "'remote -v'"]
- [cmmt, commit]
- [register, commit]
- [st, status]
- [stat, status]
- [state, status]
- [current, status]
- [restart, "reset --hard"]
- [undo, "'reset HEAD~1 --mixed'"]
- [unstage, "'reset HEAD --'"]
- [stage, add]
- [touch, add]
- [tch, add]
- [del, rm]
- [prev, "'log -1 HEAD'"]
- [last, "'log -1 HEAD'"]
- [finito, "'!git push origin HEAD'"]
- [chkpnt, "'!git add -A && git commit -m'"]
- [sgn, "'commit --amend --no-edit -S'"]
- [rvrt, revert]
+16
View File
@@ -0,0 +1,16 @@
gitea:
containerized: true
subdomain: git
trusted:
proxies:
- "127.0.0.1"
- "::1"
db:
type: mysql
name: "{{ mysql.users.admin.databases.gitea.name }}"
ssh:
port: 2323
site:
registration:
enabled: true
landing: explore
+10
View File
@@ -0,0 +1,10 @@
glance:
containerized: true
port: 8080
proxied: true
timezone:
id: America/New_York
name: New York, United States of America
weather_loc: Hancock, MD
search:
engine: duckduckgo
+23
View File
@@ -0,0 +1,23 @@
headscale:
users:
admin:
username: admin
dname: Administrator
email: rika@sukaato.moe
pfp: ~
scheme: http
port: 9191
# @NOTE https://headscale.net/stable/ref/tls/
grpc:
expose: false
secure: true
port: 50443
magic_dns:
domain: "cyberia.net"
nameservers:
- "https://dns.nextdns.io/5a5ac6"
- 9.9.9.9
clients:
- name: staging1.test
description: "my homelab server"
+3
View File
@@ -0,0 +1,3 @@
local_facts:
user_id: rika
user_dir: /home/rika
+17
View File
@@ -0,0 +1,17 @@
mysql:
containerized: true
password: "{{ lookup('password', './.tmp/' + inventory_hostname + '-root@mysql/mysql.pass', chars=['ascii_lowercase', 'digits'], length=8) }}"
users:
admin:
username: admin
# @TODO see if manual creation of additional databases is necessary for the DBMS server
databases:
init:
name: testdb
nextcloud:
name: nextcloud
gitea:
name: gitea
opengist:
name: opengist
password: "{{ lookup('password', './.tmp/' + inventory_hostname + '-user@mysql/mysql.pass', chars=['ascii_lowercase', 'digits'], length=8) }}"
+12
View File
@@ -0,0 +1,12 @@
nextcloud:
containerized: true
subdomain: cloud
rewrite_ip: false
trusted:
proxies:
- "127.0.0.1"
- "::1"
db:
type: mysql
name: "{{ mysql.users.admin.databases.nextcloud.name }}"
+15
View File
@@ -0,0 +1,15 @@
gist:
containerized: true
subdomain: gist
ssh:
port: 2323
search:
priority:
- content
- description
- filename
- topic
- title
db:
type: mysql
name: "{{ mysql.users.admin.databases.opengist.name }}"
+24
View File
@@ -0,0 +1,24 @@
compose:
mode: "{{ mode | default('prod') }}"
containers:
vpn:
name: wgclient
webserver:
name: revproxy0
ssl:
name: sslcerts
cache:
name: cache0
db:
name: db0
cloud:
name: cloud
forge:
name: forge
pastebin:
name: gist
tail:
name: tailclient
dash:
name: dash

Some files were not shown because too many files have changed in this diff Show More