decided to start over

This commit is contained in:
2026-05-23 07:07:26 -04:00
parent ffcf6b2596
commit 71f57d3122
62 changed files with 0 additions and 5058 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,157 +0,0 @@
# SPDX-License-Identifier: MIT-0
---
# handlers file for bootstrap
- name: Configure ProFTPd server
listen: proftpd
become: true
block:
- name: Create ProFTPd non-web user subdirectories
when: "'ftps' in item[0]['services'] and not 'caddy' in item[1]['services'] and not 'httpd' in item[1]['services'] and not 'www-data' in item[1]['services'] and not 'http' in item[1]['services'] and not 'https' in item[1]['services']"
ansible.builtin.file:
# follow: true
force: true
owner: "{{ item[0]['username'] }}"
group: "{{ item[0]['group'] | default(item[0]['username']) }}"
path: "{{ item[0]['home'] | default('/home/' ~ item[0]['username']) }}/{{ item[1]['username'] }}"
state: directory
loop: "{{ hostvars[inventory_hostname]['users'].values() | product(config['proftpd']['users'].values()) }}"
- name: Create ProFTPd FTP public directory for anonymous logins
when: "'ftps' in item.value['services']"
ansible.builtin.file:
# follow: true
force: true
owner: "{{ item.value['username'] }}"
group: "{{ item.value['group'] | default(item.value['username']) }}"
path: "{{ item.value['home'] | default('/home/' ~ item.value['username']) }}/public"
state: directory
loop: "{{ lookup('ansible.builtin.dict', hostvars[inventory_hostname]['users']) }}"
- name: Configure ProFTPd main control server
ansible.builtin.template:
backup: true
comment_end_string: "#}"
comment_start_string: "{#"
dest: /etc/proftpd/proftpd.conf
follow: true
force: true
group: root
owner: root
src: proftpd/proftpd.conf.j2
validate: proftpd --configtest
vars:
ftp_server_name: init
max_conns: 30
- name: Configure ProFTPd global settings
ansible.builtin.template:
backup: true
comment_end_string: "#}"
comment_start_string: "{#"
dest: /etc/proftpd/conf.d/global.conf
follow: true
force: true
group: root
owner: root
src: proftpd/conf.d/global.conf.j2
validate: proftpd --configtest
vars:
pasv_ports: "49152 65534"
allow_symlinks: false
- name: Add virtual users to ProFTPd
block:
- name: Create virtual user authentication files
ansible.builtin.file:
force: true
group: root
mode: "0640"
owner: root
path: "{{ item.value }}"
state: touch
loop: "{{ lookup('ansible.builtin.dict', config['proftpd']['auth_paths']) }}"
- name: Create the virtual users
when: "not 'caddy' in item.value['services'] and not 'httpd' in item.value['services'] and not 'www-data' in item.value['services'] and not 'http' in item.value['services'] and not 'https' in item.value['services']"
ansible.builtin.command:
argv:
- ftpasswd
- --passwd
- "--name={{ item.value['username'] }}"
- "--uid=$(id -u {{ item.value['id'] }})"
- "--gid=$(id -g {{ item.value['gid'] }})"
- "--home={{ hostvars[inventory_hostname]['users']['ftp']['home'] | default('/srv/ftp') }}/{{ item.value['username'] }}"
- --shell=/sbin/nologin
- --file={{ config['proftpd']['auth_paths']['users'] }}
- --stdin
stdin: "{{ item.value['password'] }}"
loop: "{{ lookup('ansible.builtin.dict', config['proftpd']['users']) }}"
- name: Create the virtual groups of virtual users
when: "not 'caddy' in item.value['services'] and not 'httpd' in item.value['services'] and not 'www-data' in item.value['services'] and not 'http' in item.value['services'] and not 'https' in item.value['services']"
ansible.builtin.command:
argv:
- ftpasswd
- --group
- "--name={{ item.value['username'] }}"
- "--gid=$(id -g {{ item.value['gid'] }})"
- "--member={{ item.value['username'] }}"
- --file={{ config['proftpd']['auth_paths']['groups'] }}
loop: "{{ lookup('ansible.builtin.dict', config['proftpd']['users']) }}"
# @TODO create tasks in block integrating LDAP users to ProFTPd
# - name: Integrate LDAP users into ProFTPd
- name: Create ProFTPd FTPS virtual host
ansible.builtin.template:
backup: true
comment_end_string: "#}"
comment_start_string: "{#"
dest: "/etc/proftpd/conf.d/{{ config['proftpd']['name'].lowercase() }}.conf"
follow: true
force: true
group: root
owner: root
src: "proftpd/conf.d/vhost@vps1-{{ hostvars[inventory_hostname].fqdn }}.conf.j2"
validate: proftpd --configtest
vars:
ftp_server_name: "{{ config['proftpd']['name'].uppercase() }}'s Archive'"
allowed_users: "{{ ','.join(list(map(lambda u: u['username'], filter(lambda u: not 'http' in u['services'] and not 'https' in u['services'] and not 'httpd' in u['services'] and not 'caddy' in u['services'] and not 'www-data' in u['services'], config['proftpd']['users'].values())))) }}"
anon_root: "{{ map(lambda u: u['home'], filter(lambda u: 'ftps' in u['services'] or 'proftpd' in u['services'], hostvars[inventory_hostname]['users'].values())) | list | random }}/public"
anon_user: "{{ config['proftpd']['users']['smuggler']['username'] }}"
- name: Set ProFTPd jail in fail2ban
block:
- name: Create fail2ban system configuration directory
ansible.builtin.file:
force: false
group: root
mode: "0755"
owner: root
path: /etc/fail2ban
state: directory
- name: Create fail2ban filters system configuration directory
ansible.builtin.file:
force: false
group: root
mode: "0755"
owner: root
path: /etc/fail2ban/filter.d
state: directory
- name: Create fail2ban filter system configuration
ansible.builtin.copy:
backup: true
dest: /etc/fail2ban/filter.d/custom_proftpd.conf
force: true
group: root
owner: root
src: fail2ban/filter.d/custom_proftpd.conf
# validate: string
- name: Create fail2ban jails system configuration directory
ansible.builtin.file:
force: false
group: root
mode: "0755"
owner: root
path: /etc/fail2ban/jail.d
state: directory
- name: Create fail2ban jail system configuration
ansible.builtin.copy:
backup: true
dest: /etc/fail2ban/jail.d/proftpd.local
force: true
group: root
owner: root
src: fail2ban/jail.d/proftpd.local
# validate: string

View File

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

View File

@@ -1,17 +0,0 @@
# SPDX-License-Identifier: MIT-0
---
# handlers file for bootstrap
- name: Install all snapd applications
become: true
listen: snapd
block:
- name: Install snaps
community.general.snap:
channel: "{{ item.value['channel'] | default('latest/stable') }}"
name:
- "{{ item.value['name'] }}"
# @TODO test the below list extend method for list of lists
options: "{{ item.value['opts'] }}"
state: present
notify: "{{ item.key }}"
loop: "{{ lookup('ansible.builtin.dict', software.snaps) }}"

View File

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

View File

@@ -1,38 +0,0 @@
#SPDX-License-Identifier: MIT-0
---
# tasks file for bootstrap
- name: Create GNUPGP directory in user home directory
when: ansible_facts['user_id'] in hostvars[inventory_hostname].users
ansible.builtin.file:
group: "{{ hostvars[inventory_hostname].users[ansible_facts['user_id']].group | default(ansible_facts['user_id']) }}"
mode: "0700"
owner: "{{ ansible_facts['user_id'] }}"
path: "{{ ansible_facts['user_dir'] }}/.gnupg"
state: directory
- name: Create GPG key files
when: ansible_facts['user_id'] in hostvars[inventory_hostname].users and hostvars[inventory_hostname].users[ansible_facts['user_id']].gpg_keys is not None and len(hostvars[inventory_hostname].users[ansible_facts['user_id']].gpg_keys) > 0
ansible.builtin.copy:
backup: true
dest: "{{ ansible_facts['user_dir'] }}/.gnupg/{{ item.id }}.key"
force: true
group: "{{ hostvars[inventory_hostname].users[ansible_facts['user_id']].group | default(ansible_facts['user_id']) }}"
mode: "0600"
owner: "{{ ansible_facts['user_id'] }}"
src: "gnupg/{{ item.id }}.key"
# validate: "gpg --verify {{ item.id }}.sig %s"
loop: "{{ hostvars[inventory_hostname].users[ansible_facts['user_id']].gpg_keys }}"
register: created_gpg_keys
- name: Import GPG key files
when: ansible_facts['user_id'] in hostvars[inventory_hostname].users and hostvars[inventory_hostname].users[ansible_facts['user_id']].gpg_keys is not None and len(hostvars[inventory_hostname].users[ansible_facts['user_id']].gpg_keys) > 0
ansible.builtin.command:
argv:
- gpg
- --batch
- --passphrase-fd 0
- --import
- "{{ ansible_facts['user_dir'] }}/.gnupg/{{ item.id }}.key"
stdin: "{{ item.password }}"
loop: "{{ hostvars[inventory_hostname].users[ansible_facts['user_id']].gpg_keys }}"

View File

@@ -1,157 +0,0 @@
#SPDX-License-Identifier: MIT-0
---
# tasks file for bootstrap
- name: Create directory for MOTD update scripts
ansible.builtin.file:
force: true
group: root
owner: root
path: /etc/update-motd.d
state: directory
- name: Create MOTD update scripts
ansible.builtin.copy:
force: true
backup: true
group: root
mode: "0744"
owner: root
dest: "/etc/update-motd.d/{{ item }}"
src: "update-motd.d/{{ item }}"
state: present
loop: "{{ hostvars[inventory_hostname].vps_service.ssh_motd_script_basenames }}"
- name: Create hidden SSH directories under users' home directories
when: hostvars[inventory_hostname].groups.remote.group_name in item.value.groups
ansible.builtin.file:
group: "{{ item.value.group | default(item.value.username) }}"
mode: "0700"
owner: "{{ item.value.username }}"
path: "{{ item.value.home | default('/home/' ~ item.value.username) }}/.ssh"
state: directory
loop: "{{ lookup('ansible.builtin.dict', hostvars[inventory_hostname].users) }}"
tags:
- ensure_paths
- ensure_files
- name: Add authorized SSH public keys for users
when: hostvars[inventory_hostname].groups.remote.group_name in item.value.groups and item.value.ssh_authorized_keys is not None and len(item.value.ssh_authorized_keys) > 0
ansible.builtin.copy:
backup: true
content: "{{ '\n'.join(item.value.ssh_authorized_keys) }}"
dest: "{{ item.value.home | default('/home/' ~ item.value.username) }}/.ssh/authorized_keys"
# follow: true
force: true
group: "{{ item.value.group | default(item.value.username) }}"
mode: "0600"
owner: "{{ item.value.username }}"
loop: "{{ lookup('ansible.builtin.dict', hostvars[inventory_hostname].users) }}"
tags:
- ensure_files
- name: Harden SSH security
block:
- name: Create public subdirectory for SSH's SFTP-exclusive user's chroot
when: "'sftp' in item.value.services"
ansible.builtin.file:
group: "{{ item.value.group | default(item.value.username) }}"
owner: "{{ item.value.username }}"
path: "{{ item.value.home | default('/home/' ~ item.value.username) }}/public"
state: directory
loop: "{{ lookup('ansible.builtin.dict', hostvars[inventory_hostname].users) }}"
- name: Set users in group ftp to only be usable with SSH's SFTP service
when: "'sftp' in item.value.services"
ansible.builtin.blockinfile:
backup: true
block: |2
Match User {{ item.value.username }}
ForceCommand internal-sftp -d /public
AuthorizedKeysFile {{ item.value.home | default('/home/' ~ item.value.username) }}/.ssh/authorized_keys
Match Group {{ item.value.group | default(item.value.username) }}
ForceCommand internal-sftp -d /%u
ChrootDirectory {{ item.value.home | default('/home/' ~ item.value.username) }}
AllowAgentForwarding no
AllowTcpForwarding no
X11Forwarding no
create: true
group: root
insertafter: EOF
marker: "# {mark} ANSIBLE-MANAGED SFTP BLOCK"
marker_begin: BEGIN
marker_end: END
owner: root
path: /etc/ssh/sshd_config.d/sftp.conf
append_newline: true
state: present
validate: /bin/sshd -t
loop: "{{ lookup('ansible.builtin.dict', hostvars[inventory_hostname].users) }}"
tags:
- sftp_auth_step
- name: Switch to preferred SSH authentication method
ansible.builtin.template:
backup: true
comment_end_string: "#}"
comment_start_string: "{#"
dest: /etc/ssh/sshd_config.d/auth.conf
# follow: true
force: true
group: root
owner: root
src: sshd_config.d/auth.conf.j2
validate: /bin/sshd -t
vars:
empty_auth_used: false
pass_auth_used: false
pam_auth_used: true
key_auth_used: true
tags:
- ssh_auth_step
- name: Constrain idle online user accounts
ansible.builtin.template:
backup: true
comment_end_string: "#}"
comment_start_string: "{#"
dest: /etc/ssh/sshd_config.d/harden.conf
force: true
group: root
owner: root
src: sshd_config.d/harden.conf.j2
validate: /bin/sshd -t
vars:
client_subsistence: 900
client_subsist_warn_max: 3
tags:
- ssh_timeout_step
- name: Toggle ability to log in as root via SSH
when: "hostvars[inventory_hostname].vps_service.root_fate == 'disposal'"
ansible.builtin.template:
backup: true
comment_end_string: "#}"
comment_start_string: "{#"
dest: /etc/ssh/sshd_config.d/denyroot.conf
force: true
group: root
owner: root
src: sshd_config.d/denyroot.conf.j2
validate: /bin/sshd -t
vars:
root_login_allowed: false
tags:
- ssh_root_step
- name: Specify users or groups to whitelist or blacklist for SSH login
when: "hostvars[inventory_hostname].vps_service.root_fate == 'disposal'"
ansible.builtin.template:
backup: true
comment_end_string: "#}"
comment_start_string: "{#"
dest: /etc/ssh/sshd_config.d/allowance.conf
force: true
group: root
owner: root
src: sshd_config.d/allowance.conf.j2
validate: /bin/sshd -t
vars:
list_type: whitelist
policed_groups:
- "{{ hostvars[inventory_hostname].groups.remote.group_name }}"
tags:
- ssh_gate_step
tags:
- ssh_harden_step

View File

@@ -1,39 +0,0 @@
#SPDX-License-Identifier: MIT-0
---
# tasks file for bootstrap
- name: Provide requisite SSL signed certificate for FQDN
ansible.builtin.copy:
backup: true
checksum: string
dest: "/usr/local/share/ca-certificates/{{ hostvars[inventory_hostname].fqdn }}.crt"
force: true
group: root
owner: root
src: "ca-certificates/{{ hostvars[inventory_hostname].fqdn }}.crt"
# validate: string
- name: Provide requisite SSL private key for FQDN
ansible.builtin.copy:
backup: true
dest: "/usr/local/share/ca-certificates/{{ hostvars[inventory_hostname].fqdn }}.key"
force: true
group: root
mode: "0600"
owner: root
src: "ca-certificates/{{ hostvars[inventory_hostname].fqdn }}.key"
# validate: string
- name: Provide requisite SSL public key for FQDN
ansible.builtin.copy:
backup: true
checksum: string
dest: "/usr/local/share/ca-certificates/{{ hostvars[inventory_hostname].fqdn }}.pem"
force: true
group: root
owner: root
src: "ca-certificates/{{ hostvars[inventory_hostname].fqdn }}.pem"
# validate: string
- name: Update system registration of SSL certificates
ansible.builtin.command:
cmd: update-ca-certificates
creates: "/etc/ssl/certs/{{ hostvars[inventory_hostname].fqdn }}.pem"

View File

@@ -1,65 +0,0 @@
#SPDX-License-Identifier: MIT-0
---
# tasks file for bootstrap
- name: Create groups
ansible.builtin.group:
name: "{{ item.value.group_name }}"
state: present
system: "{{ item.value.type == 'system' }}"
loop: "{{ lookup('ansible.builtin.dict', hostvars[inventory_hostname].groups) }}"
- name: Create users
block:
- name: Create administrative users
when: "item.value.admin and item.value.type != 'system'"
ansible.builtin.user:
comment: "administrator for {{ fqdn.split('.')[0].lowercase }}"
create_home: false
home: "{{ item.value.home | default('/home/' ~ item.value.username) }}"
generate_ssh_key: true
ssh_key_comment: "ansible-generated for {{ item.value.username }}@{{ hostvars[inventory_hostname].fqdn.split('.')[0].lowercase() }}"
ssh_key_type: "ed25519"
group: "{{ item.value.group | default(item.value.username) }}"
name: "{{ item.value.username }}"
uid: "{{ item.value.id }}"
shell: "{{ item.value.shell }}"
password: "{{ item.value.password }}"
state: present
system: "{{ item.value.type == 'system' }}"
update_password: always
loop: "{{ lookup('ansible.builtin.dict', hostvars[inventory_hostname].users) }}"
- name: Create regular users
when: "not item.value.admin and item.value.type != 'system'"
ansible.builtin.user:
comment: "user of {{ fqdn.split('.')[0].lowercase }}"
create_home: true
home: "{{ item.value.home | default('/home/' ~ item.value.username) }}"
generate_ssh_key: true
group: "{{ item.value.group | default(item.value.username) }}"
name: "{{ item.value.username }}"
uid: "{{ item.value.id }}"
shell: "{{ item.value.shell }}"
password: "{{ item.value.password }}"
state: present
system: "{{ item.value.type == 'system' }}"
update_password: always
loop: "{{ lookup('ansible.builtin.dict', hostvars[inventory_hostname].users) }}"
- name: Create users for managing data related to services
when: "not item.value.admin and item.value.type == 'system' and item.value.service is not None"
ansible.builtin.user:
comment: "service data user for {{ item.value.services | random }} at {{ hostvars[inventory_hostname].fqdn.split('.')[0].lowercase() }}"
create_home: false
home: "{{ item.value.home | default('/home/' ~ item.value.username) }}"
group: "{{ item.value.group | default(item.value.username) }}"
name: "{{ item.value.username }}"
uid: "{{ item.value.id }}"
shell: "{{ item.value.shell }}"
state: present
system: "{{ item.value.type == 'system' }}"
loop: "{{ lookup('ansible.builtin.dict', hostvars[inventory_hostname].users) }}"
- name: Adjust users' groups
when: item.value.groups is not None and len(item.value.groups) > 0
ansible.builtin.user:
name: "{{ item.value.username }}"
append: true
groups: "{{ item.value.groups }}"
loop: "{{ lookup('ansible.builtin.dict', hostvars[inventory_hostname].users) }}"

View File

@@ -1,12 +0,0 @@
#SPDX-License-Identifier: MIT-0
---
# tasks file for bootstrap
- name: Populate system with groups and user accounts
ansible.builtin.import_tasks:
file: "create_users@{{ ansible_facts['system'].lowercase() }}.yml"
- name: Provide SSL certificate files
ansible.builtin.import_tasks:
file: "configure_ssl@{{ ansible_facts['system'].lowercase() }}.yml"
- name: Configure SSH for root
ansible.builtin.import_tasks:
file: "configure_ssh@{{ ansible_facts['system'].lowercase() }}.yml"

View File

@@ -1,87 +0,0 @@
#SPDX-License-Identifier: MIT-0
---
# tasks file for bootstrap
# @TODO create bootstrap tasks for installation and configuration of software
- name: Update and upgrade software
block:
- name: Update Aptitude package cache and upgrade Aptitude packages
when: "ansible_facts['pkg_mgr'] == 'apt'"
become: true
ansible.builtin.apt:
upgrade: yes
update_cache: yes
cache_valid_time: 86400
- name: Install NeoVim editor
become: true
block:
- name: Install NeoVim using Aptitude package manager
when: "ansible_facts['pkg_mgr'] == 'apt'"
ansible.builtin.package:
name: neovim
use: "{{ ansible_facts['pkg_mgr'] }}"
state: present
# @TODO create handler to notify to for configuring neovim
# notify: neovim
- name: Install kitty-terminfo for SSH client xterm-kitty compatibility
become: true
block:
- name: Install kitty-terminfo using Aptitude package manager
when: "ansible_facts['pkg_mgr'] == 'apt'"
ansible.builtin.package:
name: kitty-terminfo
use: "{{ ansible_facts['pkg_mgr'] }}"
state: present
- name: Install necessary software managers and container engines
become: true
block:
- name: Install snapd
when: "ansible_facts['pkg_mgr'] == 'apt'"
block:
- name: Install snapd using Aptitude package manager
ansible.builtin.package:
name: snapd
use: "{{ ansible_facts['pkg_mgr'] }}"
state: present
notify: snapd
- name: Install flatpak
when: "ansible_facts['pkg_mgr'] == 'apt'"
block:
- name: Install flatpak using Aptitude package manager
ansible.builtin.package:
name: flatpak
use: "{{ ansible_facts['pkg_mgr'] }}"
state: present
- name: Install podman
when: "ansible_facts['pkg_mgr'] == 'apt'"
block:
- name: Install podman using Aptitude package manager
ansible.builtin.package:
name: podman
use: "{{ ansible_facts['pkg_mgr'] }}"
state: present
- name: Install podman-compose
when: "ansible_facts['pkg_mgr'] == 'apt'"
block:
- name: Install podman-compose using Aptitude package manager
ansible.builtin.package:
name: podman-compose
use: "{{ ansible_facts['pkg_mgr'] }}"
state: present
- name: Install git
block:
- name: Install git using Aptitude package manager
when: "ansible_facts['pkg_mgr'] == 'apt'"
ansible.builtin.package:
name: git
use: "{{ ansible_facts['pkg_mgr'] }}"
state: present
notify: git
- name: Install packages
when: ansible_facts['pkg_mgr'] in item.value.name
ansible.builtin.package:
name: "{{ item.value.name[ansible_facts['pkg_mgr']] }}"
use: "{{ ansible_facts['pkg_mgr'] }}"
state: present
# @TODO research what happens when nonexistent handler is called or notify field is null
notify: "{{ item.key }}"
loop: "{{ lookup('ansible.builtin.dict', software.pkgs) }}"

View File

@@ -1,9 +0,0 @@
#SPDX-License-Identifier: MIT-0
---
# tasks file for bootstrap
- name: Configure GPG for user
ansible.builtin.import_tasks:
file: "configure_gpg@{{ ansible_facts['system'].lowercase() }}.yml"
- name: Install and configure software on system
ansible.builtin.import_tasks:
file: "install@{{ ansible_facts['system'].lowercase() }}.yml"

View File

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

View File

@@ -1,31 +0,0 @@
<Global>
# PassivePorts {{ pasv_ports }}
RequireValidShell off
{% if allow_symlinks %}
ShowSymlinks on
{% else %}
ShowSymlinks off
{% endif %}
AllowRetrieveRestart on
HiddenStores .%P- .frag
DisplayLogin /etc/proftpd/WELCOME.txt
DisplayChdir .README.md true
DisplayConnect /etc/proftpd/BANNER.txt
DisplayFileTransfer /etc/proftpd/SUCCESS.txt
DisplayReadme /etc/proftpd/ANNOUNCE.md
DisplayQuit /etc/proftpd/BYE.txt
TimeoutNoTransfer 3600
TimeoutStalled 210
TimeoutIdle 1400
Umask 0022 0022
AllowOverwrite on
<Directory />
<Limit ALL>
DenyAll
</Limit>
</Directory>
</Global>

View File

@@ -1,93 +0,0 @@
<IfModule !mod_tls.c>
LoadModule mod_tls.c
</IfModule>
<IfModule mod_tls.c>
<VirtualHost 0.0.0.0>
ServerName "{{ ftp_server_name }}"
ServerIdent on "Our head librarians Furcas and Marbas welcome you!"
ServerAlias {{ hostvars[inventory_hostname].fqdn }} ftp.{{ hostvars[inventory_hostname].fqdn }} {{ hostvars[inventory_hostname].fqdn.split('.')[0] }}
ServerLog /var/log/proftpd/{{ hostvars[inventory_hostname].fqdn }}.log
Protocols ftps
Port 990
DefaultRoot ~
# AllowStoreRestart on
MaxStoreFileSize 10 Gb
MaxTransfersPerUser STOR,RETR 9
MaxTransfersPerHost STOR,RETR 36
DirFakeUser on ~
DirFakeGroup on ~
# AuthOrder mod_auth_pam.c mod_auth_unix.c*
AuthOrder mod_auth_file.c
AuthUserFile {{ config.proftpd.auth_paths.users }}
AuthGroupFile {{ config.proftpd.auth_paths.groups }}
AuthFileOptions SyntaxCheck
TLSEngine on
TLSLog /var/log/proftpd/tls.log
# @NOTE: "SSLv23" means all SSL versions
TLSProtocol SSLv23
TLSOptions AllowClientRenegotiations
TLSVerifyClient off
TLSRequired on
TLSRenegotiate required off
TLSECCertificateFile {{ config.proftpd.tls_paths.cert }}
TLSECCertificateKeyFile {{ config.proftpd.tls_paths.privkey }}
TLSCACertificateFile {{ config.proftpd.tls_paths.cert }}
<Limit LOGIN>
AllowUser OR {{ allowed_users}}
</Limit>
<Directory ~>
<Limit READ DIRS>
AllowAll
</Limit>
</Directory>
<Directory ~/*>
UserOwner ftp
GroupOwner ftp
HideUser !~
HideFiles ^\.(.+)?
HideNoAccess on
<Limit ALL>
AllowAll
</Limit>
</Directory>
<Anonymous {{ anon_root }}>
User ftp
Group ftp
RequireValidShell off
DirFakeUser on anon
DirFakeGroup on anon
DirFakeMode 0444
UserAlias anon {{ anon_user }}
AllowStoreRestart off
MaxStoreFileSize 4 Gb
MaxTransfersPerUser STOR,RETR 3
MaxTransfersPerHost STOR,RETR 10
HideUser !~
HideNoAccess on
<Directory {{ anon_root }}>
<Limit READ DIRS>
AllowAll
</Limit>
</Directory>
<Directory {{ anon_root }}/*>
# <Limit READ DIRS MKD RMD XMKD XRMD>
<Limit READ DIRS>
AllowAll
</Limit>
HideFiles ^\.(.+)?
</Directory>
</Anonymous>
</VirtualHost>
</IfModule>

View File

@@ -1,185 +0,0 @@
#
# /etc/proftpd/proftpd.conf -- This is a basic ProFTPD configuration file.
# To really apply changes, reload proftpd after modifications, if
# it runs in daemon mode. It is not required in inetd/xinetd mode.
#
# Includes DSO modules
Include /etc/proftpd/modules.conf
# Set off to disable IPv6 support which is annoying on IPv4 only boxes.
UseIPv6 on
# If set on you can experience a longer connection delay in many cases.
<IfModule mod_ident.c>
IdentLookups on
</IfModule>
ServerName "{{ ftp_server_name }}"
# Set to inetd only if you would run proftpd by inetd/xinetd/socket.
# Read README.Debian for more information on proper configuration.
ServerType standalone
DeferWelcome off
MaxInstances {{ max_conns }}
# Disable MultilineRFC2228 per https://github.com/proftpd/proftpd/issues/1085
# MultilineRFC2228on
DefaultServer on
DefaultRoot ~
DenyFilter \*.*/
# Users require a valid shell listed in /etc/shells to login.
# Use this directive to release that constrain.
# RequireValidShell off
# Port 21 is the standard FTP port.
Port 21
# If your host was NATted, this option is useful in order to
# allow passive tranfers to work. You have to use your public
# address and opening the passive ports used on your firewall as well.
# MasqueradeAddress 1.2.3.4
# This is useful for masquerading address with dynamic IPs:
# refresh any configured MasqueradeAddress directives every 8 hours
# <IfModule mod_dynmasq.c>
# DynMasqRefresh 28800
# </IfModule>
# Set the user and group that the server normally runs at.
User proftpd
Group nogroup
# Uncomment this if you are using NIS or LDAP via NSS to retrieve passwords:
# PersistentPasswd off
# This is required to use both PAM-based authentication and local passwords
# AuthOrder mod_auth_pam.c* mod_auth_unix.c
# Be warned: use of this directive impacts CPU average load!
# Uncomment this if you like to see progress and transfer rate with ftpwho
# in downloads. That is not needed for uploads rates.
#
# UseSendFile off
TransferLog /var/log/proftpd/transfer.log
SystemLog /var/log/proftpd/connection.log
# Logging onto /var/log/lastlog is enabled but set to off by default
#UseLastlog on
# In order to keep log file dates consistent after chroot, use timezone info
# from /etc/localtime. If this is not set, and proftpd is configured to
# chroot (e.g. DefaultRoot or <Anonymous>), it will use the non-daylight
# savings timezone regardless of whether DST is in effect.
#SetEnv TZ :/etc/localtime
<IfModule mod_quotatab.c>
QuotaEngine off
</IfModule>
<IfModule mod_ratio.c>
Ratios off
</IfModule>
# Delay engine reduces impact of the so-called Timing Attack described in
# http://www.securityfocus.com/bid/11430/discuss
# It is on by default.
<IfModule mod_delay.c>
DelayEngine on
</IfModule>
<IfModule mod_ctrls.c>
ControlsEngine off
ControlsMaxClients 2
ControlsLog /var/log/proftpd/controls.log
ControlsInterval 5
ControlsSocket /var/run/proftpd/proftpd.sock
</IfModule>
<IfModule mod_ctrls_admin.c>
AdminControlsEngine off
</IfModule>
#
# Alternative authentication frameworks
#
#Include /etc/proftpd/ldap.conf
#Include /etc/proftpd/sql.conf
#
# This is used for FTPS connections
#
#Include /etc/proftpd/tls.conf
#
# This is used for SFTP connections
#
#Include /etc/proftpd/sftp.conf
#
# This is used for other add-on modules
#
#Include /etc/proftpd/dnsbl.conf
#Include /etc/proftpd/geoip.conf
#Include /etc/proftpd/snmp.conf
#
# Useful to keep VirtualHost/VirtualRoot directives separated
#
#Include /etc/proftpd/virtuals.conf
# A basic anonymous configuration, no upload directories.
# <Anonymous ~ftp>
# User ftp
# Group nogroup
# # We want clients to be able to login with "anonymous" as well as "ftp"
# UserAlias anonymous ftp
# # Cosmetic changes, all files belongs to ftp user
# DirFakeUser on ftp
# DirFakeGroup on ftp
#
# RequireValidShell off
#
# # Limit the maximum number of anonymous logins
# MaxClients 10
#
# # We want 'welcome.msg' displayed at login, and '.message' displayed
# # in each newly chdired directory.
# DisplayLogin welcome.msg
# DisplayChdir .message
#
# # Limit WRITE everywhere in the anonymous chroot
# <Directory *>
# <Limit WRITE>
# DenyAll
# </Limit>
# </Directory>
#
# # Uncomment this if you're brave.
# # <Directory incoming>
# # # Umask 022 is a good standard umask to prevent new files and dirs
# # # (second parm) from being group and world writable.
# # Umask 022 022
# # <Limit READ WRITE>
# # DenyAll
# # </Limit>
# # <Limit STOR>
# # AllowAll
# # </Limit>
# # </Directory>
#
# </Anonymous>
<Limit LOGIN>
DenyAll
</Limit>
# Include other custom configuration files
# !! Please note, that this statement will read /all/ file from this subdir,
# i.e. backup files created by your editor, too !!!
# Eventually create file patterns like this: /etc/proftpd/conf.d/*.conf
#
Include /etc/proftpd/conf.d/

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,297 +0,0 @@
#SPDX-License-Identifier: MIT-0
---
# vars file for bootstrap
# @TODO make list or dictionary of software to be installed in bootstrap task
software:
pkgs:
# @NOTE keep fields or keys constant; otherwise will have to edit handler notifiers and listeners elsewhere
failtwoban:
name:
apt: fail2ban
gocryptfs:
name:
apt: gocryptfs
lua-lang:
name:
apt: lua5.4
lua-docs:
name:
apt: luadoc
lua-pkg:
name:
apt: luarocks
python-lang:
name:
apt: python3
python-pkg:
name:
apt: python3-pip
python-linter:
name:
apt: python3-doc8
python-docs:
name:
apt: python3-doc
rust-lang:
name:
apt: rustc # @NOTE alternative: rustup
rust-pkg:
name:
apt: cargo
rust-debugger:
name:
apt: rust-analyzer
rust-linter:
name:
apt: rust-clippy
rust-docs:
name:
apt: rust-doc
java-lang:
name:
apt: default-jdk-headless
java-docs:
name:
apt: default-jdk-doc
java-runtime:
name:
apt: default-jre-headless
kotlin-lang:
name:
apt: kotlin
swift-lang:
name:
apt: swiftlang
swift-docs:
name:
apt: swiftlang-doc
erlang-lang:
name:
apt: erlang
erlang-pkg:
name:
apt: erlang-hex
erlang-docs:
name:
apt: erlang-doc
elixir-lang:
name:
apt: elixir
crystal-lang:
name:
apt: crystal
crystal-docs:
name:
apt: crystal-doc
# @TODO replace below commented with an NVM-style installation (v22): https://nodejs.org/en/download
# javascript-lang:
# name:
# apt: nodejs
# javascript-pkg:
# name:
# apt: npm
# javascript-linter:
# name:
# apt: eslint
javascript-docs:
name:
apt: nodejs-doc
php-lang:
name:
apt: php
php-docs:
name:
apt: php-common
php-debugger:
name:
apt: php-xdebug
php-pkg:
name:
apt: composer
# php-ldap:
# name:
# apt: php-ldap
html-linter:
name:
apt: tidy
json-linter:
name:
apt: jsonlint
yaml-linter:
name:
apt: yamllint
pandoc:
name:
apt: pandoc
distrobox:
name:
apt: distrobox
fastfetch:
name:
apt: fastfetch
# @TODO manually install the commented below on current active new VPS, then uncomment
# duplicity:
# name:
# apt: duplicity
# pass:
# name:
# apt: pass
# sonicpi:
# name:
# apt: sonic-pi-server
# sonicpi-docs:
# name:
# apt: sonic-pi-server-doc
# supercollider:
# name:
# apt: supercollider
# supercollider-docs:
# name:
# apt: supercollider-common
# supercollider-plugins:
# name:
# apt: sc3-plugins-language
qrencode:
name:
apt: qrencode
ffmpeg:
name:
apt: ffmpeg
ffmpeg-docs:
name:
apt: ffmpeg-doc
graphicsmagick:
name:
apt: graphicsmagick
graphicsmagick-compatibility:
name:
apt: graphicsmagick-imagemagick-compat
timg:
name:
apt: timg
tmux:
name:
apt: tmux
# @TODO add glow apt repository in install@linux bootstrap role play before uncommenting the below
# glow:
# name:
# apt: glow
# @TODO add ZFS apt repository in install@linux bootstrap role play before uncommenting the below
# zfs:
# name:
# apt: zfsutils-linux
# @TODO manually install the commented below on current active new VPS, then uncomment
# dpkg-dev:
# name:
# apt: dpkg-dev
# ldap-utils:
# name:
# apt: ldap-utils
# slapd:
# name:
# apt: slapd
proftpd-mod-crypto:
name:
apt: proftpd-mod-crypto
# @TODO write configuration files and handler for below two package installations
# based on:
clamav:
name:
apt: clamav
clamd:
name:
apt: clamav-daemon
proftpd:
name:
apt: proftpd
proftpd-docs:
name:
apt: proftpd-doc
rsync:
name:
apt: rsync
# rclone:
# name:
# apt: rclone
aria:
name:
apt: aria2
# mopidy:
# name:
# apt: mopidy
# mopidy-mpd:
# name:
# apt: mopidy-mpd
# caddy:
# name:
# apt: caddy
snaps:
nextcloud:
name: nextcloud
channel: ~
opts:
- "nextcloud:php.memory-limit=512M"
- "nextcloud:nextcloud.cron-interval=10m"
- "nextcloud:http.compression=true"
- "nextcloud:ports.http=81"
# @TODO see how to set these options: https://help.nextcloud.com/t/how-to-configure-nextcloud-snap/216036#p-649442-trusted-domains-configuration-8
# @TODO see how to set these options: https://help.nextcloud.com/t/how-to-configure-nextcloud-snap/216036#p-649442-trusted-proxy-configuration-9
links:
quartz:
name: quartz
src: https://github.com/jackyzha0/quartz.git
branch: v4
version: ~
output: ~
config:
git:
sys:
editor: nvim
proftpd:
name: "{{ hostvars[inventory_hostname].fqdn.split('.')[0] }}"
auth_paths:
users: /etc/proftpd/ftpd.passwd
groups: /etc/proftpd/ftpd.group
msg:
welcome: "Our head librarians Furcas and Marbas welcome you!"
users:
webmaster:
username: webmaster
id: "{{ ['caddy', 'www-data'][0] }}"
gid: "{{ ['caddy', 'www-data'][0] }}"
# @TODO create vaulted password for this ProFTPd virtual user
password: !vault |
$ANSIBLE_VAULT;1.2;AES256;vps1-webmaster
63633938633139636663623166343836643839306538373762393834393230336334383334303163
3465323831366163386265353664313932383664373838660a363463303364373963353638396462
65356135623030653533333766623865643065303739386538636662303537376466333039613363
3932313334643163650a303336623031613964356433363536373236303266663735343939383930
3636
services: [http,https]
smuggler:
username: smuggler
id: "{{ hostvars[inventory_hostname].users.ftp.username }}"
gid: "{{ hostvars[inventory_hostname].users.ftp.group | default(hostvars[inventory_hostname].users.ftp.username) }}"
# @TODO create vaulted password for this ProFTPd virtual user
password: !vault |
$ANSIBLE_VAULT;1.2;AES256;vps1-smuggler
38396565313866383761303137343431613830643436666431316434393362623035623031656263
6537313630393433336133643166363564383163616232320a623034636664353864613862353366
38303663363665663366336131663431383936306131616262376162653837326163393561323465
3734333031323330300a353562353035323731303732323534613938353935393433646235356137
62336333666362383665623466353337303134623966663061366235303261653333
services: []
tls_paths:
cert: "/usr/local/share/ca-certificates/{{ hostvars[inventory_hostname].fqdn }}.crt"
privkey: "/usr/local/share/ca-certificates/{{ hostvars[inventory_hostname].fqdn }}.key"
nextcloud:
users:
admin:
username: admin
# @TODO change this password to ansible-vaulted actual choice password later
password: password123 # @NOTE placeholder
phone_region: US
aria:
checksum: ~
api_key: ~