Compare commits

...

101 Commits

Author SHA1 Message Date
Felix Stupp 2efb214edd
misc/ssh_tg_notify: Ignore messages from trusted VPN subnet 2 years ago
Felix Stupp c842c40c89
misc/ssh_tg_notify: Lookup & inform user which logged in as well 2 years ago
Felix Stupp 10a3e8aa9f
misc/ssh_tg_notify: Lookup user id of root dynamically 2 years ago
Felix Stupp 10e2dea52a
misc/ssh_tg_notify: Add comments for sections 2 years ago
Felix Stupp a98b250c31
misc/ssh_tg_notify: Check that PAM_RHOST is defined 2 years ago
Felix Stupp b50f457d99
misc/ssh_tg_notify: Add Content-Type to bot requests 2 years ago
Felix Stupp 4da0a72682
misc/ssh_tg_notify: Extract sendMessage method 2 years ago
Felix Stupp 07eeba447e
misc/ssh_tg_notify: Replace link to ipinfo.io with link to stat.ripe.net 2 years ago
Felix Stupp 8b7d4f1688
misc/ssh_tg_notify: strip expected stat error message 2 years ago
Felix Stupp 27bd516581
misc/ssh_tg_notify: Include username into cache key
- So if multiple users log in from the same ip, the cache does not prevent a notification
2 years ago
Felix Stupp a2091da6a9
Added playbooks/facts folder with gitignore 2 years ago
Felix Stupp 9731affd79
Renamed host_var files to add .yml ending 2 years ago
Felix Stupp 224b302e2d
Implement virtual env for pip and ansible collections 3 years ago
Felix Stupp 8b6b7e57ad
Move credentials.tar.gpg into misc directory 3 years ago
Felix Stupp dadee8a49d
ansible.cfg: Add comments for explaining every option / group of options 3 years ago
Felix Stupp 3a5cf551e7
makefile: Group targets with different headlines 3 years ago
Felix Stupp 8e6a96c5fa
makefile: Split up phony entries 3 years ago
Felix Stupp 734091b4fb
Add tag deploy-auto-update-script to some tasks 3 years ago
Felix Stupp f9912f950f
Change default GPG keyserver to keys.openpgp.org
- sks-keyservers not available anymore, so fallback to new one
3 years ago
Felix Stupp b5df9a1225
Extract GPG keyserver for backups into global var 3 years ago
Felix Stupp 644164eac5
Added tags to included roles 3 years ago
Felix Stupp 08e91883b3
misc/ssh_tg_notify: Added cache to send fewer notifications
- per IP only once in the next 4 hours
3 years ago
Felix Stupp 2e71da368b
nginx/{php,static}: Remove legacy "ssl on" directive 3 years ago
Felix Stupp 0781b4c175
nginx/forward: Redeveloped using nginx/server template 3 years ago
Felix Stupp cc8513ddf4
nginx/forward: correctly documented vars as required by removing default value 3 years ago
Felix Stupp 6c48b7360e
Update mitogen to version compatible to Ansible 2.10 3 years ago
Felix Stupp 0c6d7161ed
nginx/server: Added tag nginx-server-config to deployment of site config 3 years ago
Felix Stupp 05c0d5fa7b
added intention to change global admin mail to own domain mail 3 years ago
Felix Stupp 16a72f6014
group_vars/all: extracted os_defaults from general vars.yml 3 years ago
Felix Stupp 88b904abc7
deprecate global "project" variables 3 years ago
Felix Stupp 72e3fda3ff
added role misc/deb_backports_prio 3 years ago
Felix Stupp e267e345da
server/nextcloud: Enable apc on using occ commands
Required since Nextcloud 21
3 years ago
Felix Stupp 1bf40022a9
server/nextcloud: Allow configuring files_chunk_size and setup linked limits
- linked settings are (configured to same value appending some overheads):
   - nginx_max_size
   - php_post_max_size
   - php_upload_max_size
3 years ago
Felix Stupp 5ae646abdd
server/spotme: service_environment_file variable fixed to use correct service_name var 3 years ago
Felix Stupp ae39585006
server/spotme: Increased java version to 11 3 years ago
Felix Stupp 79e1423947
server/tt-rss: increased lifetime of session cookies 3 years ago
Felix Stupp 25616aa118
nginx/php-pool: Added tag nginx-php-pool-config 3 years ago
Felix Stupp 416289a367
server/firefox-sync: added dependency libmariadb-dev 3 years ago
Felix Stupp b4eef0e28b
configure drone-ci on hatoria for git.banananet.work 3 years ago
Felix Stupp b666ffb439
Removed temporary transfer tag 3 years ago
Felix Stupp 6301ed65fc
playbooks/dns: Moved main dns server from nvak to hatoria 3 years ago
Felix Stupp 0126d2f8ae
server/nextcloud: Add support for configuring default phone region 3 years ago
Felix Stupp 6919a6f7dc
dns/application: Added dependency python3-dnspython
Required for Ansible to configure dns records
3 years ago
Felix Stupp f2dac5ca1f
nginx/php-pool: Allow configuring arbitary php_admin_values
- removing support for explicit configuration key for memory_limit
- replaced usage of memory_limit key with usage of admin_values key
3 years ago
Felix Stupp 98b7b55a53
Whitelist multiple services of proc's hidepid feature
Not only required for systemd-logind, but also for user@.service
3 years ago
Felix Stupp 15ad953131
common: Validate sshd_config before applying 3 years ago
Felix Stupp e1a612966c
Hide running processes from users other than root 3 years ago
Felix Stupp d0e9962d04
common: Add tag journald to certain tasks 3 years ago
Felix Stupp edc4ccd4c3
Fix journald path from /systmed/ to /systemd/ 3 years ago
Felix Stupp 065408fd9d
moved git.bnet from nvak to hatoria 3 years ago
Felix Stupp c48c32f786
moved {firefox,rss,spotme}.bnet from nvak to hatoria 3 years ago
Felix Stupp 4c4c6529ad
server/spotme: Change default database user equal to system user 3 years ago
Felix Stupp fe34e6111e
mysql/database: Added tag mysql_database to all tasks 3 years ago
Felix Stupp 9fd183bbac
mysql/database: rewrote usage of include_tasks to be better compatible with tags 3 years ago
Felix Stupp 288c4175d6
dns/entries: Add timeout of 8s after changing dns entries
so futher roles do not fail because external dns servers have not
received the change yet
3 years ago
Felix Stupp 6ae690aac3
transfered forumderschan.de from nvak to hatoria 3 years ago
Felix Stupp 933a094916
moved Stadtpiraten comments from nvak to hatoria 3 years ago
Felix Stupp 53d051824f
nvak: Remove turnips.banananet.work 3 years ago
Felix Stupp 455ca2ce09
nvak: transfered future projects phpmyadmin and banananetwork keys to hatoria
- Comments are already stored in hatorias playbook because this change
was committed by accident at b86d856b
3 years ago
Felix Stupp abdc53c317
ansible.cfg: Always enable diff on playbook executions 3 years ago
Felix Stupp 18f374103a
hatoria hst20 nextcloud: updated app list 3 years ago
Felix Stupp a734f6ef42
hatoria: Monitor public-known nameservers of forumderschan.de 3 years ago
Felix Stupp 0a5b3fc26f
Added new role misc/tg_monitor_cmd 3 years ago
Felix Stupp 7f9980903f
dns: Explicit defined main_nameserver and added to entries 3 years ago
Felix Stupp 6d061088a2
misc/ssh_tg_notify: Quoted TIMEOUT variable 3 years ago
Felix Stupp 6c547434b9
Renamed global_ssh_notify_telegram_bot_key to global_telegram_server_bot_key 3 years ago
Felix Stupp 5aa78edc12
dns: Assigned MailJet mail service to wg.banananet.work 3 years ago
Felix Stupp 03bc38ff78
playbooks/dns: Added section for managing arbitary entries 3 years ago
Felix Stupp 8413cbd9cc
Added role ext_mail/mailjet 3 years ago
Felix Stupp 52c03dc9d2
Added python3-yaml to common packages 3 years ago
Felix Stupp 43cb8f0a5e
hatoria: Removed ransomware_detection because of current problems
See https://github.com/undo-ransomware/ransomware_detection/issues/48#issue-763599989
3 years ago
Felix Stupp f2c5aedc52
server/nextcloud: Add extract_app_list helper script 3 years ago
Felix Stupp 10dab39328
Transfered WG and HST21 Nextcloud instances from nvak to hatoria 3 years ago
Felix Stupp 0164e4810b
server/nextcloud: Remove not required comment on lost_password_link
Feature already explained in another comment above
3 years ago
Felix Stupp a23e80abc1
server/nextcloud: Delete no longer required install_nextcloud.sh script 3 years ago
Felix Stupp 09cd9782a9
Added roles server/drone.io/{runner,server} 3 years ago
Felix Stupp 398ed9084c
Added role docker/compose-git 3 years ago
Felix Stupp c8fdc4fae9
account: Configure authorized_keys using authorized_key module
- also restricts .ssh directory only to user
- restriction was automatically applied by authorized_keys module before
- more restriction is not harmful
- this restriction ensures indempotency while using the authorized_keys module
3 years ago
Felix Stupp 0a8ee3983d
group_vars/all: Changed default dns servers to normal Quad9
- in distinction to Quad9 servers supporting/using EDNS
- the default servers have EDNS disabled
3 years ago
Felix Stupp f825787dd6
docker/application: Configure dns and log-driver of daemon 3 years ago
Felix Stupp e9651f3b09
role misc/docker renamed to docker/application 3 years ago
Felix Stupp 7d240539c8
misc/docker: Reworked role to use docker package from distro repo 3 years ago
Felix Stupp 8725e65dfb
server/nextcloud: Fix re-importing config.json after changes 3 years ago
Felix Stupp d281b238e7
Added group_vars for group hetzner_server 3 years ago
Felix Stupp 78dbfe3c8e
hosts.py: Added support for "&" operator in groups pattern 3 years ago
Felix Stupp 0b0135dd57
hosts.py: Generalized supported operations of group pattern 3 years ago
Felix Stupp 7d858342e2
Updated hosts.yml to support new inventory syntax
While reworked group structure to be more useful and powerful
3 years ago
Felix Stupp 827865b44c
hosts.py: Reworked inventory interpreter to support more powerful syntaxes 3 years ago
Felix Stupp 8e4cae43b5
site: Add name to playbook for common roles 3 years ago
Felix Stupp 00dde619e8
Rename group surface3 to dev_surface3 3 years ago
Felix Stupp 92b13e90ed
nginx/application: Fixed getting nameserver ips using ansible facts, not custom script 3 years ago
Felix Stupp bf21d8727f
playbooks/group_bwcloud: Added hint about manual change 3 years ago
Felix Stupp 149dadd393
Added symlink for library in playbooks/ 3 years ago
Felix Stupp eee29f48b3
Integrated mitogen boost for ansible 3 years ago
Felix Stupp 74fa987e2d
Edit vault, add my minecraft name 3 years ago
Felix Stupp 47cc7a0706
site: Added tag common to "common" role execution 3 years ago
Felix Stupp 25ca7ef895
server/spotme: Define some conversion tasks to run also in check mode
- required to check later tasks
- do not change anything on the system on their own
3 years ago
Felix Stupp 01ed71353c
playbooks/wireguard: Remove not required strategy directives 3 years ago
Felix Stupp e56498dc78
hatoria: Define creating custom_archive_directory for cloud-bnet 3 years ago
Felix Stupp 9d62d35564
hatoria: Moved domain/system_user for cloud-bnet to variables 3 years ago
Felix Stupp e13c86fae1
mc-wg-bnet on hatoria: upgraded MC to version 1.16.4 3 years ago

3
.gitignore vendored

@ -1,5 +1,7 @@
/ansible_collections
credentials/**
facts/**
/venv/**
public_keys/**
__pycache__/
!README.md
@ -10,3 +12,4 @@ __pycache__/
/*.yml
!/site.yml
!/hosts.yml
!/collection_requirements.yml

3
.gitmodules vendored

@ -0,0 +1,3 @@
[submodule "misc/mitogen"]
path = misc/mitogen
url = https://git.banananet.work/archive/mitogen.git

@ -11,5 +11,6 @@
},
"files.exclude": {
"playbooks/{credentials,filter_plugins,group_vars,helpers,host_vars,public_keys,roles}/": true
}
},
"python.pythonPath": "/home/zocker/Repositories/ansible2/venv/bin/python",
}

@ -1,8 +1,37 @@
[defaults]
# always ask for vault pass instead of expecting user to make it available unasked
ask_vault_pass = True
# force handlers to be executed always, especially after a normal task failed to execute
# without this option, it might happen that a handler might be missed to be executed after the role was completed in multiple tries (e.g. reload certain services)
force_handlers = True
# select custom inventory parser for setting up inventory
inventory = ./hosts.py
# install & use ansible collections locally (similar to venv) instead of globally
# helps to prevent differences on developer machines to be disturbing
# collections will be automatically setup from the dependency list "collection-requirements.yml" using "make ansible_collections"
# requires dev's to documentate each external dependency inside the repository
collections_path = ./ # ansible then searches for the subdirectory "ansible_collections" for itself
# disable usage of cowsay for ansible-playbook's logging (increases readability drastically, only matters if cowsay is installed)
nocows = True
# disable storing retry files after fail because of no usage
retry_files_enabled = False
# automatically select python interpreter, should be sufficient
interpreter_python = auto
# add mitogen strategies and select mitogen as default strategy
# mitogen, see https://mitogen.networkgenomics.com/ansible_detailed.html
strategy_plugins = ./misc/mitogen/ansible_mitogen/plugins/strategy
strategy = mitogen_linear
[diff]
# always enable --diff option
always = True

12
enter

@ -0,0 +1,12 @@
#!/bin/echo You need to source this script! Use something like: source
# (re-)create env if required (e.g. requirements.txt changed)
make setup
# enable coloring on these tools
export ANSIBLE_FORCE_COLORS=1
export PY_COLORS=1
# enter venv
. ./venv/bin/activate

@ -0,0 +1,92 @@
---
# === Constants defined by OS packages / applications
# seperated in arbitary system/kernel and applications/packages
# each group is sorted alphabetically
# general system/kernel constants
global_fstab_file: "/etc/fstab"
global_resolv_conf: "/etc/resolv.conf"
global_pamd: "/etc/pam.d"
global_proc_hidepid_service_whitelist:
- "{{ global_systemd_login_service_name }}"
- "{{ global_systemd_user_service_name }}"
global_users_directory: "/home"
# application constants
global_ansible_facts_directory: "/etc/ansible/facts.d"
global_apparmor_profiles_directory: "/etc/apparmor.d"
global_apparmor_profiles_local_directory: "{{ global_apparmor_profiles_directory }}/local"
global_apt_sources_directory: "/etc/apt/sources.list.d"
global_bind_service_name: "named.service"
global_bind_configuration_directory: "/etc/bind"
global_bind_data_directory: "/var/lib/bind"
global_certbot_configuration_directory: "/etc/letsencrypt"
global_certbot_configuration_file: "{{ global_certbot_configuration_directory }}/cli.ini"
global_certbot_certificates_directory: "/etc/letsencrypt/live"
global_chromium_configuration_directory: "/etc/chromium"
global_chromium_managed_policies_file: "{{ global_chromium_configuration_directory }}/policies/managed/managed_policies.json"
global_dnsmasq_configuration_file: "/etc/dnsmasq.conf"
global_dnsmasq_configuration_directory: "/etc/dnsmasq.d"
global_docker_service_name: "docker.service"
global_docker_configuration_directory: "/etc/docker"
global_docker_daemon_configuration_file: "{{ global_docker_configuration_directory }}/daemon.json"
global_fail2ban_service_name: "fail2ban.service"
global_fail2ban_system_directory: "/etc/fail2ban"
global_fail2ban_configuration_directory: "{{ global_fail2ban_system_directory }}/fail2ban.d"
global_fail2ban_actions_directory: "{{ global_fail2ban_system_directory }}/action.d"
global_fail2ban_filters_directory: "{{ global_fail2ban_system_directory }}/filter.d"
global_fail2ban_jails_directory: "{{ global_fail2ban_system_directory }}/jail.d"
global_interfaces_directory: "/etc/network/interfaces.d"
global_lightdm_configuration_directory: "/etc/lightdm"
global_log_directory: "/var/log"
global_mysql_socket_path: "/var/run/mysqld/mysqld.sock"
global_nfs_port: "2049" # for version 4
global_nfs_directory: "{{ global_webservers_directory }}/nfs"
global_nginx_system_user: www-data
global_nginx_service_name: "nginx.service"
global_nginx_installation_directory: "/etc/nginx"
global_plymouth_themes_directory: "/usr/share/plymouth/themes"
global_redis_configuration_directory: "/etc/redis"
global_redis_service_name: "redis-server.service"
global_ssh_service_name: "sshd.service"
global_ssh_configuration_directory: "/etc/ssh/"
global_ssh_configuration_environment_directory: "{{ global_configuration_environment_directory }}/ssh"
global_ssh_configuration_link_name: "config"
global_ssh_configuration_link: "{{ global_ssh_configuration_environment_directory }}/{{ global_ssh_configuration_link_name }}"
global_sudoers_directory: "/etc/sudoers.d"
global_wireguard_configuration_directory: "/etc/wireguard"
global_systemd_preset_directory: "/lib/systemd/system"
global_systemd_configuration_directory: "/etc/systemd/system"
global_systemd_journal_configuration_directory: "/etc/systemd/journald.conf.d"
global_systemd_login_service_name: "systemd-logind.service"
global_systemd_network_directory: "/etc/systemd/network"
global_systemd_network_service_name: "systemd-networkd.service"
global_systemd_network_system_user: "systemd-network"
global_systemd_user_service_name: "user@.service"
global_zsh_antigen_source: "/usr/share/zsh-antigen/antigen.zsh"

@ -5,17 +5,23 @@ TIMEZONE: "Europe/Berlin"
local_user: "{{ lookup('env','USER') }}"
global_username: zocker
global_admin_mail: felix.stupp@outlook.com
global_admin_mail: felix.stupp@outlook.com # TODO change to felix.stupp@banananet.work, verify if all usages will apply change (e.g. lets encrypt)
ansible_user: "{{ global_username }}"
ansible_become: yes
ansible_become_pass: "{{ zocker_password }}"
default_gpg_keyserver_hostname: "keys.openpgp.org"
default_tg_monitor_recipient_id: "{{ zocker_telegram_id }}"
zocker_authorized_keys_url: "https://git.banananet.work/zocker.keys"
update_scripts_directory: "/root/update"
tailscale_vpn_subnet: "100.64.0.0/10"
backup_gpg_fingerprint: "73D09948B2392D688A45DC8393E1BD26F6B02FB7"
backups_to_keep: 1
backups_directory: "/backups"
@ -58,7 +64,9 @@ global_dns_debug_ttl: "{{ 60 }}" # mostly used if has_debug_instance to allow sh
global_ssh_key_directory: "{{ global_public_key_directory }}/ssh"
global_ssh_host_key_directory: "{{ global_ssh_key_directory }}/hosts"
global_validate_python_script: "/usr/bin/python3 -m pylint --disable=C0114 %s"
global_validate_shell_script: "/usr/bin/shellcheck %s" # TODO add "--format="
global_validate_sshd_config: "/usr/sbin/sshd -t -f %s"
global_validate_sudoers_file: "/usr/sbin/visudo -c -f %s"
global_wireguard_private_directory: "{{ global_credentials_directory }}/wireguard"
@ -93,102 +101,25 @@ raspbian_repository_mirror: "http://raspbian.raspberrypi.org/raspbian/"
raspbian_archive_repository_mirror: "http://archive.raspberrypi.org/debian/"
raspbian_repository_use_sources: yes
# System configuration
global_users_directory: "/home"
# Application configurations
global_ansible_facts_directory: "/etc/ansible/facts.d"
global_apparmor_profiles_directory: "/etc/apparmor.d"
global_apparmor_profiles_local_directory: "{{ global_apparmor_profiles_directory }}/local"
global_apt_sources_directory: "/etc/apt/sources.list.d"
global_bind_service_name: "named.service"
global_bind_configuration_directory: "/etc/bind"
global_bind_data_directory: "/var/lib/bind"
global_certbot_configuration_directory: "/etc/letsencrypt"
global_certbot_configuration_file: "{{ global_certbot_configuration_directory }}/cli.ini"
global_certbot_certificates_directory: "/etc/letsencrypt/live"
global_chromium_configuration_directory: "/etc/chromium"
global_chromium_managed_policies_file: "{{ global_chromium_configuration_directory }}/policies/managed/managed_policies.json"
global_dns_upstream_servers:
- "9.9.9.11"
- "149.112.112.11"
- "2620:fe::11"
- "2620:fe::fe:11"
global_dnsmasq_configuration_file: "/etc/dnsmasq.conf"
global_dnsmasq_configuration_directory: "/etc/dnsmasq.d"
global_fail2ban_service_name: "fail2ban.service"
global_fail2ban_system_directory: "/etc/fail2ban"
global_fail2ban_configuration_directory: "{{ global_fail2ban_system_directory }}/fail2ban.d"
global_fail2ban_actions_directory: "{{ global_fail2ban_system_directory }}/action.d"
global_fail2ban_filters_directory: "{{ global_fail2ban_system_directory }}/filter.d"
global_fail2ban_jails_directory: "{{ global_fail2ban_system_directory }}/jail.d"
# Quad9 DNS with DNSSEC support, without EDNS
- "9.9.9.9"
- "149.112.112.112"
- "2620:fe::fe"
- "2620:fe::9"
global_ip_discover_url: "https://keys.banananet.work/ping"
global_ip_discover_register_pass: "{{ lookup('password', 'credentials/ip_discover/register_pass chars=digits,ascii_letters length=256') }}"
global_interfaces_directory: "/etc/network/interfaces.d"
global_lightdm_configuration_directory: "/etc/lightdm"
global_log_directory: "/var/log"
global_mysql_socket_path: "/var/run/mysqld/mysqld.sock"
global_nfs_port: "2049" # for version 4
global_nfs_directory: "{{ global_webservers_directory }}/nfs"
global_nginx_system_user: www-data
global_nginx_service_name: "nginx.service"
global_nginx_installation_directory: "/etc/nginx"
global_pamd: "/etc/pam.d"
global_plymouth_themes_directory: "/usr/share/plymouth/themes"
global_redis_configuration_directory: "/etc/redis"
global_redis_service_name: "redis-server.service"
global_resolv_conf: "/etc/resolv.conf"
global_ssh_service_name: "sshd.service"
global_ssh_configuration_directory: "/etc/ssh/"
global_ssh_configuration_environment_directory: "{{ global_configuration_environment_directory }}/ssh"
global_ssh_configuration_link_name: "config"
global_ssh_configuration_link: "{{ global_ssh_configuration_environment_directory }}/{{ global_ssh_configuration_link_name }}"
global_sudoers_directory: "/etc/sudoers.d"
global_wireguard_configuration_directory: "/etc/wireguard"
global_wireguard_port: 51820
global_wireguard_ipv4_subnet: 22
global_wireguard_ipv4_netmask: "{{ ('0.0.0.0/' + (global_wireguard_ipv4_subnet | string)) | ipaddr('netmask') }}"
global_wireguard_ipv4_range: "10.162.4.0/{{ global_wireguard_ipv4_subnet }}"
# TODO Wireguard IPv6 Support
global_systemd_preset_directory: "/lib/systemd/system"
global_systemd_configuration_directory: "/etc/systemd/system"
global_systemd_journal_configuration_directory: "/etc/systmed/journald.conf.d"
global_systemd_journal_max_storage: 1G
global_systemd_network_directory: "/etc/systemd/network"
global_systemd_network_service_name: "systemd-networkd.service"
global_systemd_network_system_user: "systemd-network"
global_zsh_antigen_source: "/usr/share/zsh-antigen/antigen.zsh"
# Projects
# WG Minecraft
project_wg_minecraft_port: 25566
# Miscellaneous

@ -1,24 +1,38 @@
$ANSIBLE_VAULT;1.1;AES256
63343063643236623632373437303138303636643862323961633739653032376333386666626162
3738366330393339303030373430653162616138383261370a393738326638663064323963366338
31303332353439666363653839353932333338313830366566653534343739613036306465656137
6366353730656230320a633334306135653163313435303037343138326137383765363666376262
63353237396637386663663535646363366639313961343037656162336664343832656331393535
33353534653738346331313034666237656630613439656164343234333161353939356435656634
63396134356138323064313365366537336137646432636131353734343130653066383862346461
66383364656233393839666462336661643730646633633135626331643366666135353437346633
37633838373339363332633134386637303561366238353538643837386332636439383034333434
31363866373161636431383862326137306466613361356337646133643630373332666434666133
66366564383161376234343135616531613238613131363834313764363366326163333562303061
31333734333336663037313333383632373130313631626533623139666265646530386464616135
30363462623136393730616337306163663763616430303530306361393834303661613864313830
33616161323535323865626639323132333131626662626161623234613136663961393063303739
61353632373265363761636235313430383237363938396534666663353336383234663561373833
63666364313539393831353833393763326432303035343830386663633534356362316130353866
64383564666431343333626332356666633231653239363130386265363164356664326633623065
61393636613162376334646661663232626534326562613235633434656466303435393233613233
36666463316331366365643861633362386466663863316564656439633364616566373062306633
66326464326138306130666631313830643236663134363166383264366139643861393565623537
33376165396531323863626635323237363665363539613963376537373635323365616234313762
66313934623631386432633861383136386464353932316534363836613038313934356331363737
333931356137336563653162316563306636
66386430666466343732636663313264663933613563643231323066383261616361353234366534
3337323862636537663538343062333064383838653138340a343662326139396634343261396230
65666533626263386465616466663431333339613162373766363937333564323233353930303836
6332366434333437370a666636656534653031303237633863356630393836386137353837303039
33323433343065313135323462316163343364656562303962373634656666353235363537366361
35383031343138376439316365306337636264346434363863623765356161663133653363633533
30613430613333666561303935663833396265363931653133373934363263323362333839366662
62373533643535323430353032386431346462363566323637613736313336373665666631326633
34343830653535623262333730356164636131623735333839663336623735353138313962656564
35643231303461653236373665613339313332386535376665623130646637626531306366316266
36613961653162633639333536333434383332363061653062396163623664316363303561636634
63353263313730313133613537386536616338323533303666653131656262323763616432343664
65626130383432326663303238383233633265393936633934623634366663333862643562383736
38313265306138303431363634656334656530393539636232613962386238613963643161306234
35646136613764353138666431363337393765343233303332663530336261316331383665643536
30663831656566663239656565613535316438666632663236666636383762333432303964333833
33353661623965633630383536613633313437666430623565636635633634646338633666356234
66323966396638316236626234326364633366666266643832333066383735306330366234383533
63386563626264303234303832356662363732356438306234656561373637376137346565653966
65373465303032393939383833386333353461633732623232393761353236306331626164386238
35353464373732346537626464663532653434386564636532623838383937363463633332366534
35613137613933636434336432653964353536303366353832356161653535353165613964333339
30646139316661656363383832313765326234316134393732636262373730386562626233633439
39643862393336653533373731333938343164363233323638353265656139333465363831333431
62323332396537656432343235633735636631646334306265376566343364646566396563386537
32366335313335666436613531356535623364336135636665623233363763663537393538666233
35643431396430336533396137303763333332626439316265383138663639343061656631626463
39386461303866373862626361373836643437346365343531323264386631313834613166393833
34656537326531643962636436393236393537373935346135663335656666343430313335373633
65393066636233653262623031383564393038353730393363356561363936356366636330386264
37383064636433646265396365373330613833623338666638653532363061316261343639323937
33623665316161353035366438663337346532653262366434366138306364343966653235383636
38666263623633356463373963636135656637613164353265613635353733316138626637623364
34386338633363653231643334323161653933613864636338626638323035323233643137353964
35666332346264613136343039336261303964343237373136393139376234363833376164643839
33316566353033363333633966643366303537653766623935643933373062313830316166303961
37393638653064623935356564303236343766393939323561356461656636626534

@ -0,0 +1,14 @@
---
bootstrap_user: "root"
global_dns_upstream_servers:
- 213.133.100.100
- 213.133.99.99
- 213.133.98.98
- "2a01:4f8:0:1::add:1010"
- "2a01:4f8:0:1::add:9898"
- "2a01:4f8:0:1::add:9999"
debian_repository_mirror: "http://mirror.hetzner.de/debian/packages"
debian_repository_use_sources: no # Not supported by Hetzner mirrors, but also not required

@ -1,27 +1,212 @@
#!/usr/bin/env python3
import json
import re
import sys
import yaml
class LoopPrevention:
def __init__(self, obj):
self.__obj = obj
self.__entered = False
def __enter__(self):
if self.__entered:
raise Exception("detected and prevented infinite loop")
self.__entered = True
return self
def __exit__(self, *args):
self.__entered = False
return False # forward exception
class Group:
def __init__(self, inv):
self.__inv = inv
self.__hosts = set()
self.__children = set()
def add_host(self, host):
if not host in self.__hosts:
self.__hosts.add(host)
def add_hosts(self, hosts):
self.__hosts |= hosts
@property
def direct_hosts(self):
return set(self.__hosts)
@property
def all_hosts(self):
with LoopPrevention(self):
hosts = self.direct_hosts
for child in self.children:
hosts |= self.__inv._group(child).all_hosts
return hosts
def add_child(self, group_name):
if not group_name in self.__children:
self.__children.add(group_name)
@property
def children(self):
return set(self.__children)
def export(self):
return { "hosts": list(self.__hosts), "vars": dict(), "children": list(self.__children) }
class Inventory:
def __init__(self):
self.__groups = dict()
self.add_group("all")
def __group(self, group_name):
if group_name not in self.__groups:
self.__groups[group_name] = Group(self)
return self.__groups[group_name]
def _group(self, group_name):
if group_name not in self.__groups:
raise Exception(f'Unknown group "{group_name}"')
return self.__groups[group_name]
def add_host(self, host):
self.__group("all").add_host(host)
def add_hosts(self, hosts):
self.__group("all").add_hosts(hosts)
def add_group(self, group_name):
self.__group(group_name)
def add_host_to_group(self, host, group_name):
self.add_host(host)
self.__group(group_name).add_host(host)
def add_hosts_to_group(self, hosts, group_name):
self.add_hosts(hosts)
self.__group(group_name).add_hosts(hosts)
def add_child_to_group(self, child_name, parent_name):
self.__group(child_name)
self.__group(parent_name).add_child(child_name)
def all_hosts_of_group(self, group_name):
return self._group(group_name).all_hosts
def export(self):
meta_dict = {
"_meta": {
"hostvars": {},
},
}
group_dict = { group_name: group.export() for group_name, group in self.__groups.items() }
return { **meta_dict , **group_dict }
def _read_yaml(path):
with open(path, 'r') as stream:
try:
return yaml.safe_load(stream)
except yaml.YAMLError as e:
return AnsibleError(e)
GROUPS_PATTERN_OPS = {
"": lambda old, add: old | add,
"&": lambda old, add: old & add,
"!": lambda old, add: old - add,
}
GROUPS_PATTERN_OPS_NAMES = "".join(GROUPS_PATTERN_OPS.keys())
GROUPS_PATTERN = re.compile(r'^(?P<operation>[' + GROUPS_PATTERN_OPS_NAMES + r']?)(?P<group_name>[^' + GROUPS_PATTERN_OPS_NAMES + r'].*)$')
def _parse_group_aliasses(inv, data):
for group, syntax in data.items():
if isinstance(syntax, str):
group_list = syntax.split(':')
elif isinstance(syntax, list):
group_list = syntax
else:
raise Exception(f'Unknown syntax for alias "{group}": {syntax}')
if len(syntax) <= 0 or len(group_list) <= 0:
raise Exception(f'Empty syntax for alias "{group}": {syntax}')
if group_list[0][0] == '!': # if first entry is an inversion
group_list.insert(0, 'all') # remove group from all for inversion
hosts = set()
for group_name in group_list:
group_matched = GROUPS_PATTERN.match(group_name)
add = inv.all_hosts_of_group(group_matched.group('group_name'))
op = GROUPS_PATTERN_OPS[group_matched.group('operation')]
hosts = op(hosts, add)
inv.add_hosts_to_group(hosts, group)
def _parse_groups(inv, data):
for group, children in data.items():
inv.add_group(group)
if children is None:
continue # as if no children are given
for child in children:
inv.add_child_to_group(child, group)
if isinstance(children, dict):
_parse_groups(inv, children)
def _parse_host_groups(inv, data):
GROUPS_KEY = "_all"
for host_group, hosts in data.items():
inv.add_group(host_group)
if hosts is None:
continue
for host in hosts:
if host != GROUPS_KEY:
inv.add_host_to_group(host, host_group)
if isinstance(hosts, dict):
hosts = dict(hosts) # copy dict for further edits
parents = hosts.pop(GROUPS_KEY, None)
if parents is not None:
for parent in parents:
inv.add_child_to_group(host_group, parent)
_parse_single_hosts(inv, hosts)
def _parse_single_hosts(inv, data):
for host, groups in data.items():
inv.add_host(host)
if groups is not None:
for group in groups:
inv.add_host_to_group(host, group)
def _parse_version_0(inv, data):
return _parse_single_hosts(inv, data)
parser_mapping_v1 = { "groups": _parse_groups, "host_groups": _parse_host_groups, "single_hosts": _parse_single_hosts }
def _parse_version_1(inv, data):
for key_name, parser in parser_mapping_v1.items():
if key_name in data:
parser(inv, data[key_name])
def _parse_version_2(inv, data):
_parse_version_1(inv, data)
_parse_group_aliasses(inv, data["group_aliasses"])
parser_version_mapping = {
None: _parse_version_0, # legacy version without version number, only hosts list with tags
1: _parse_version_1, # adds support for default, inversed group dependencies and host_groups aside single_hosts (ignores aliases supported with version 2)
2: _parse_version_2, # adds support for aliases (thus destroying the common graph structures where aliasses were used)
}
def parse(path):
with open(path, 'r') as stream:
try:
data = yaml.safe_load(stream)
except yaml.YAMLError as e:
return AnsibleError(e)
ret = { "all": { "hosts": list(), "vars": dict(), "children": list() } , "_meta": { "hostvars": {} } }
for host, groups in data.items():
ret["all"]["hosts"].append(host)
if groups is not None:
for group in groups:
if not group in ret:
ret[group] = dict()
ret[group]["hosts"] = list()
ret[group]["vars"] = dict()
ret[group]["children"] = list()
if not host in ret[group]["hosts"]:
ret[group]["hosts"].append(host)
return ret
data = _read_yaml(path)
inv = Inventory()
version = data.get("version", None)
# detect that version was used as hostname
if not isinstance(version, (int, float, complex)):
version = None
if version not in parser_version_mapping:
raise AnsibleError(Exception("Version not supported"))
parser_version_mapping[version](inv, data)
return inv.export()
print(json.dumps(parse("hosts.yml")))

@ -1,41 +1,85 @@
# Public Servers
hatoria.banananet.work:
- hetzner_server
- os_debian
- bootstrap
- public_available
- wireguard_backbones
nvak.banananet.work:
- contabo_vserver
- os_debian
- bootstrap
- public_available
- wireguard_backbones
morska.banananet.work:
- bwcloud_vserver
- os_debian
- bootstrap
- public_available
- wireguard_backbones
rurapenthe.banananet.work:
- bwcloud_vserver
- os_debian
- bootstrap
- public_available
- wireguard_backbones
# Location Eridon
## Local Servers
hardie.eridon.banananet.work:
- bootstrap
## Embedded Devices
wgpanel.eridon.banananet.work:
- surface3
- os_debian
- bootstrap
version: 2
groups: # a:b meaning b is a, can be nested
# hardware structure
dev_known:
barebones:
- rented_barebones # sub group
# list of all known barebone device groups
- dev_surface3 # Microsoft Surface 3
virtual:
- rented_vserver # sub group
dev_unknown: # for unknown device kinds
# structure of rented servers
rented:
rented_barebones:
- hetzner_server # https://robot.your-server.de/server
rented_vserver:
- bwcloud_vserver # https://portal.bw-cloud.org/
- contabo_vserver # https://my.contabo.com/vps
# OS structure
os_known: # list of all known OS derivates
- os_debian
- os_raspbian
# applications
bootstrapable: # which OSes/hosts can be bootstraped
- os_debian
- os_raspbian
group_aliasses: # a:b meaning a equals b, should only depend on groups not defined here
# unknown groups
dev_unknown: "!dev_known"
os_unknown: "!os_known"
# applications
bootstrap: "bootstrapable:!no_bootstrap" # which hosts should be bootstraped
common_roles: "!no_common_roles"
wireguard_backbones: "public_available:!no_wireguard_automatic"
wireguard_clients: "!public_available:!no_wireguard_automatic"
host_groups: # group: host: [*groups]
no_defaults: # do not include in all default playbooks / roles
_all:
- no_bootstrap # do not setup sudo bootstrap
- no_common_roles # do not include in common roles
- no_wireguard_automatic # do not assign wireguard role automatic, hosts may be excluded from wireguard or assigned to their wireguard role manually
rented:
_all:
- public_available # rented are public available
# to group similar devices together
common_server: # public common servers
_all:
- os_debian
hatoria.banananet.work:
- hetzner_server
nvak.banananet.work:
- contabo_vserver
morska.banananet.work:
- bwcloud_vserver
rurapenthe.banananet.work:
- bwcloud_vserver
single_hosts: # a:b meaning a is b, cannot be nested
# Local Servers
hardie.eridon.banananet.work:
- os_debian
# Embedded Devices
wgpanel.eridon.banananet.work:
- dev_surface3
- os_debian
- no_wireguard_automatic # no wireguard

@ -2,25 +2,52 @@ vault:=group_vars/all/vault.yml
playbooks_dir:=playbooks
playbooks:=$(wildcard ${playbooks_dir}/*.yml)
credentials_dir:=credentials
credentials_file:=misc/credentials.tar.gpg
venv_dir:=venv
.PHONY: main list vault ${playbooks} store-credentials load-credentials
# Default Target (must be first target)
.PHONY: main
main:
ansible-playbook site.yml
# Virtual Environment's Setup
.PHONY: setup
setup: ansible_collections ${venv_dir}
ansible_collections: collection-requirements.yml ${venv_dir}
mkdir --parent $@
. ./${venv_dir}/bin/activate && ansible-galaxy install -r $<
${venv_dir}: pip-requirements.txt
python3 -m venv $@
. ./$@/bin/activate && python3 -m pip install -r $<
# Playbook Execution
.PHONY: list
list:
@echo ${playbooks}
.PHONY: ${playbooks}
${playbooks}:
ansible-playbook ${playbooks_dir}/$@.yml
# Vault Handling
.PHONY: vault
vault:
ansible-vault edit ${vault}
${playbooks}:
ansible-playbook ${playbooks_dir}/$@.yml
# Credential Handling
store-credentials: credentials.tar.gpg
.PHONY: store-credentials
store-credentials: ${credentials_file}
credentials.tar.gpg: $(shell find "${credentials_dir}")
${credentials_file}: $(shell find "${credentials_dir}")
tar -cf - "${credentials_dir}" | gpg --encrypt --recipient 73D09948B2392D688A45DC8393E1BD26F6B02FB7 > "$@"
.PHONY: load-credentials
load-credentials:
< credentials.tar.gpg gpg --decrypt | tar -xf -
< "${credentials_file}" gpg --decrypt | tar -xf -

@ -0,0 +1 @@
Subproject commit 36f3e3b28c82611c72a867cdc1f5ddc8bd9325e9

@ -0,0 +1,27 @@
#### Python / PiP Requirements ####
# each group either sorted by alphabet or, if applicable, sorted by hierachy
### Main Runtime Dependencies ###
# Ansible itself
ansible ~= 2.10.0 # pinned to 2.10 because upgrade may bring issues
### Test Frameworks ###
ansible-lint # simple linter
yamllint # linter for YAML files in general
## molecule ##
# role based test framework for Ansible
molecule
# enable docker for test environments, requires Docker to be installed on host and usuable without additional permissions
molecule-docker
# allows using Vagrant (VMs) for creating test environments, requires Vagrant and any hypervisor (e.g. VirtualBox) to be installed
molecule-vagrant
python-vagrant # extra module required as not always installed with vagrant

@ -1,5 +1,5 @@
- name: Configure nvak as dns server
hosts: nvak.banananet.work
- name: Configure hatoria as dns server
hosts: hatoria.banananet.work
vars:
# Source: https://docs.hetzner.com/dns-console/dns/general/authoritative-name-servers
hetzner_authoritatives:
@ -46,9 +46,13 @@
roles:
- role: dns/master
domain: banananet.work
main_nameserver_domain: "ns1.banananet.work" # required glue entry already configured
responsible_mail_name: hostmaster.banananet.work
slaves_ip: "{{ hetzner_authoritatives_ip }}"
entries:
# main NS entry
- type: NS
data: ns1.banananet.work.
# Hetzner NS entries
- type: NS
data: "{{ hetzner_authoritatives }}"
@ -93,10 +97,11 @@
data: "10 10 10110 mc.wg.{{ domain }}."
- role: dns/master
domain: forumderschan.de
main_nameserver_domain: "ns1.banananet.work"
responsible_mail_name: hostmaster.banananet.work
slaves_ip: "{{ hetzner_authoritatives_ip }}"
entries:
# Glue record
# main NS entry
- type: NS
data: ns1.banananet.work.
# Hetzner NS entries
@ -107,9 +112,10 @@
data: 0 issue "letsencrypt.org"
- role: dns/master
domain: stadtpiraten-karlsruhe.de
main_nameserver_domain: "ns1.banananet.work"
responsible_mail_name: hostmaster.banananet.work
entries:
# Glue record
# main NS entry
- type: NS
data: ns1.banananet.work.
# limit CA
@ -121,3 +127,17 @@
roles:
- role: dns/server_entries
domain: "{{ inventory_hostname }}"
- name: Arbitary entries
# all tasks/roles here must be local only
hosts: all # select any host as not important
run_once: yes # run only once "for first host"
gather_facts: no # do not gather facts from host as these may not be used
roles:
- role: ext_mail/mailjet
tags:
- mailjet
- wg.banananet.work
domain: wg.banananet.work
verification_name: 5803f0f5
verification_data: 5803f0f5f4278d66327350f7a8141b70

@ -0,0 +1 @@
*.yml

@ -16,3 +16,7 @@
owner: root
group: root
mode: u=rw,g=r,o=r
# If something goes wrong with mouting or /etc/hosts, add this back to cloud.cfg using directory:
#mount_default_fields: [~, ~, 'auto', 'defaults,nofail', '0', '2']
#manage_etc_hosts: true

@ -1,7 +1,7 @@
---
- name: Configure Surface 3 device
hosts: surface3
hosts: dev_surface3
tasks:
- name: Install packages for hardware
apt:

@ -1,7 +1,27 @@
- name: Configure hatoria.banananet.work
hosts: hatoria.banananet.work
vars:
bnet_cloud_domain: "cloud.banananet.work"
bnet_cloud_username: "{{ bnet_cloud_domain | domain_to_username }}"
roles:
- role: nginx/default_server # Would not be configurable otherwise
tags:
- default_server
# Git Server
- role: server/gitea
tags:
- git.banananet.work
domain: git.banananet.work
gitea_system_user: git
database_user: gitea
- role: server/drone.io/server
domain: ci.git.banananet.work
bind_port: 12824
gitea_server_url: https://git.banananet.work
gitea_client_id: "{{ drone_ci_gitea_main_oauth2_client_id }}"
gitea_client_secret: "{{ drone_ci_gitea_main_oauth2_client_secret }}"
- role: server/drone.io/runner
drone_server_host: ci.git.banananet.work
# Banananet.work
- role: server/static
tags:
@ -13,6 +33,23 @@
- banananet.work
domain: www.banananet.work
dest: banananet.work
# SpotMe Server
- role: server/spotme
tags:
- spotme.banananet.work
domain: spotme.banananet.work
bind_port: 12820
# Firefox Sync Server
- role: server/firefox-sync
tags:
- firefox.banananet.work
domain: firefox.banananet.work
# RSS Server
# TODO Manual initialization of database required
- role: server/tt-rss
tags:
- rss.banananet.work
domain: rss.banananet.work
# Linx Server
- role: server/linx
tags:
@ -35,7 +72,8 @@
- role: server/nextcloud
tags:
- cloud.banananet.work
domain: cloud.banananet.work
domain: "{{ bnet_cloud_domain }}"
system_user: "{{ bnet_cloud_username }}"
nextcloud_admin_user: "{{ global_username }}"
enabled_apps_list:
- accessibility
@ -115,7 +153,6 @@
tags:
- forumderschan.de
domain: forumderschan.de
is_debug_instance: yes
repo: git@git.banananet.work:strichliste/strichliste-php.git
root: html
installation_includes:
@ -125,15 +162,225 @@
- forumderschan.de
domain: www.forumderschan.de
dest: forumderschan.de
# Monitors
- role: misc/tg_monitor_cmd
tags: tg-monitor-cmd
monitor_name: forumderschan.de-NS
description: "NS entries of forumderschan.de"
command_str: >-
/usr/bin/dig
@a.nic.de.
forumderschan.de. NS
| grep --only-matching --perl-regexp '(?<=\s)(\S+\.)+(?=$)'
| sort
use_shell: yes
# WG Nextcloud
- role: server/nextcloud
tags:
- wg.banananet.work
domain: wg.banananet.work
nextcloud_admin_user: felix
enabled_apps_list:
- accessibility
- activity
- apporder
- bruteforcesettings
- calendar
- checksum
- cloud_federation_api
- comments
- contacts
- cookbook
- cospend
- dav
- deck
- encryption
- external
- federatedfilesharing
- federation
- files
- files_automatedtagging
- files_external
- files_pdfviewer
- files_rightclick
- files_sharing
- files_trashbin
- files_versions
- files_videoplayer
- firstrunwizard
- logreader
- lookup_server_connector
- metadata
- nextcloud_announcements
- notes
- notifications
- oauth2
- ocdownloader
- password_policy
- photos
- polls
- privacy
- provisioning_api
- quota_warning
- ransomware_protection
- serverinfo
- settings
- sharebymail
- side_menu
- sociallogin
- socialsharing_email
- support
- suspicious_login
- systemtags
- tasks
- text
- theming
- twofactor_admin
- twofactor_backupcodes
- twofactor_gateway
- twofactor_nextcloud_notification
- twofactor_totp
- twofactor_u2f
- updatenotification
- viewer
- workflowengine
disabled_apps_list:
- admin_audit
- recommendations
- spreed
- survey_client
- user_ldap
# WG Minecraft
- role: server/minecraft
tags:
- mc.wg.banananet.work
domain: mc.wg.banananet.work
minecraft_version: "1.16.1"
minecraft_version: "1.16.4"
minecraft_ram: "16G"
minecraft_port: 25566
config:
difficulty: normal
motd: ChaosCraft
view-distance: 16
# # Stadtpiraten
# - role: server/typo3
# domain: piraten.dev.banananet.work
# - role: server/php
# domain: forum.piraten.dev.banananet.work
# repo: PHPBB # TODO
# version: master
# # Stadtpiraten (prod)
# - role: nginx/forward
# domain: www.stadtpiraten-karlsruhe.de
# dest: stadtpiraten-karlsruhe.de
# SMD/SFC HST 2020
- role: nginx/forward
tags:
- proj-hst
- hst21.banananet.work
domain: hst20.banananet.work
dest: hst21.banananet.work
- role: server/nextcloud
tags:
- proj-hst
- hst21.banananet.work
domain: hst21.banananet.work
system_user: nc-hst21
nextcloud_admin_user: felix
enabled_apps_list:
- accessibility
- activity
- apporder
- bruteforcesettings
- calendar
- checksum
- cloud_federation_api
- comments
- contacts
- contactsinteraction
- cospend
- dav
- deck
- encryption
- external
- federatedfilesharing
- federation
- files
- files_automatedtagging
- files_linkeditor
- files_mindmap
- files_pdfviewer
- files_rightclick
- files_sharing
- files_trashbin
- files_versions
- files_videoplayer
- firstrunwizard
- forms
- logreader
- lookup_server_connector
- mail
- maps
- metadata
- nextcloud_announcements
- notes
- notifications
- oauth2
- password_policy
- photos
- polls
- privacy
- provisioning_api
- quota_warning
- ransomware_protection
- serverinfo
- settings
- sharebymail
- socialsharing_email
- spreed
- support
- suspicious_login
- systemtags
- tasks
- text
- theming
- twofactor_admin
- twofactor_backupcodes
- twofactor_gateway
- twofactor_totp
- twofactor_u2f
- updatenotification
- viewer
- whiteboard
- workflowengine
disabled_apps_list:
- admin_audit
- dashboard
- files_external
- recommendations
- sociallogin
- survey_client
- user_ldap
- user_status
- weather_status
tasks:
- name: Configure custom archive Nextcloud directory on hdd for personal usages
tags:
- cloud.banananet.work
- custom_archive_directory
vars:
archive_directory: "{{ global_hdd_directory }}/{{ bnet_cloud_domain }}~personal-archive"
block:
- name: Create archive directory
file:
state: directory
path: "{{ archive_directory }}"
owner: "{{ bnet_cloud_username }}"
group: "{{ bnet_cloud_username }}"
mode: "u=rwx,g=rx,o="
register: archive_directory_task
- name: Show message to user about path on changes
debug:
msg: >-
Changed custom archive directory: Please ensure you (re-)configure this directory properly on your Nextcloud instance: {{ archive_directory | quote }}
when: archive_directory_task.changed

@ -2,229 +2,9 @@
hosts: nvak.banananet.work
roles:
- role: nginx/default_server # Would not be configurable otherwise
# Git Server
- role: server/gitea
tags:
- git.banananet.work
domain: git.banananet.work
gitea_system_user: git
database_user: gitea
# SpotMe Server
- role: server/spotme
tags:
- spotme.banananet.work
domain: spotme.banananet.work
bind_port: 12820
spotme_system_user: spotme
# # Admin Panel
# - role: server/php
# domain: nvak.banananet.work
# repo: PHPMYADMIN # TODO
# BananaNetwork Keys
# - role: server/node
# domain: keys.banananet.work
# repo: https://git.banananet.work/banananetwork/keys.git
# bind_port: 12822
# system_user: keys-banananet-work
# Firefox Sync Server
- role: server/firefox-sync
tags:
- firefox.banananet.work
domain: firefox.banananet.work
system_user: firefox-banananet-work
# RSS Server
# TODO Manual initialization of database required
- role: server/tt-rss
tags:
- rss.banananet.work
domain: rss.banananet.work
system_user: rss-banananet-work
# DSA Seite
# - role: server/node
# domain: dsa.banananet.work
# repo: git@git.banananet.work:dsaGroup/dsaPage.git
# bind_port: 12821
# system_user: dsaPage
# Forum der Schande
- role: server/php
tags:
- forumderschan.de
domain: forumderschan.de
has_debug_instance: yes
system_user: forumderschan-de
repo: git@git.banananet.work:strichliste/strichliste-php.git
root: html
installation_includes:
- includes
# WG Nextcloud
- role: server/nextcloud
tags:
- wg.banananet.work
domain: wg.banananet.work
system_user: wg-banananet-work
nextcloud_admin_user: felix
enabled_apps_list:
- accessibility
- activity
- apporder
- bruteforcesettings
- calendar
- checksum
- cloud_federation_api
- comments
- contacts
- cookbook
- cospend
- dav
- deck
- encryption
- external
- federatedfilesharing
- federation
- files
- files_automatedtagging
- files_external
- files_pdfviewer
- files_rightclick
- files_sharing
- files_trashbin
- files_versions
- files_videoplayer
- firstrunwizard
- logreader
- lookup_server_connector
- metadata
- nextcloud_announcements
- notes
- notifications
- oauth2
- ocdownloader
- password_policy
- photos
- polls
- privacy
- provisioning_api
- quota_warning
- ransomware_detection
- ransomware_protection
- serverinfo
- settings
- sharebymail
- side_menu
- sociallogin
- socialsharing_email
- support
- suspicious_login
- systemtags
- tasks
- text
- theming
- twofactor_admin
- twofactor_backupcodes
- twofactor_gateway
- twofactor_nextcloud_notification
- twofactor_totp
- twofactor_u2f
- updatenotification
- viewer
- workflowengine
disabled_apps_list:
- admin_audit
- recommendations
- spreed
- survey_client
- user_ldap
- role: server/static
domain: turnips.banananet.work
repo: https://git.banananet.work/banananetwork/ac-nh-turnip-prices.git
# SMD/SFC HST 2020
- role: server/nextcloud
tags:
- hst20.banananet.work
domain: hst20.banananet.work
system_user: nc-hst20
nextcloud_admin_user: felix
enabled_apps_list:
- accessibility
- activity
- apporder
- bruteforcesettings
- calendar
#- checksum # currently not supported
- cloud_federation_api
- comments
- contacts
- cospend
- dav
- deck
- encryption
- external
- federatedfilesharing
- federation
- files
- files_automatedtagging
- files_mindmap
- files_pdfviewer
- files_rightclick
- files_sharing
- files_trashbin
- files_versions
- files_videoplayer
- firstrunwizard
- forms
- logreader
- lookup_server_connector
- metadata
- nextcloud_announcements
- notes
- notifications
- oauth2
- password_policy
- photos
- polls
- privacy
- provisioning_api
- quota_warning
#- ransomware_detection # currently not supported
- ransomware_protection
- serverinfo
- settings
- sharebymail
- socialsharing_email
- spreed
- support
- suspicious_login
- systemtags
- tasks
- text
- theming
- twofactor_admin
- twofactor_backupcodes
- twofactor_gateway
- twofactor_totp
- twofactor_u2f
- updatenotification
- viewer
- whiteboard
- workflowengine
disabled_apps_list:
- admin_audit
- dashboard
- files_external
- recommendations
- sociallogin
- survey_client
- user_ldap
- user_status
- weather_status
# # Stadtpiraten
# - role: server/typo3
# domain: piraten.dev.banananet.work
# - role: server/php
# domain: forum.piraten.dev.banananet.work
# repo: PHPBB # TODO
# version: master
# # Stadtpiraten (prod)
# - role: nginx/forward
# domain: www.stadtpiraten-karlsruhe.de
# dest: stadtpiraten-karlsruhe.de

@ -0,0 +1 @@
../library

@ -2,7 +2,6 @@
- name: Configure wireguard backbones
hosts: wireguard_backbones
strategy: linear
tags:
- wireguard
- wireguard_backbones
@ -11,7 +10,6 @@
- name: Configure wireguard clients
hosts: wireguard_clients
strategy: linear
tags:
- wireguard
- wireguard_clients

@ -52,23 +52,11 @@
group: "{{ username }}"
mode: "u=rwx,g=rx,o="
- name: Configure ssh configration directory
file:
path: "{{ user_directory }}/.ssh"
state: directory
owner: "{{ username }}"
group: "{{ username }}"
mode: "u=rwx,g=rx,o="
- name: Configure authorized_keys
get_url:
url: "{{ authorized_keys }}"
dest: "{{ user_directory }}/.ssh/authorized_keys"
force: yes
owner: "{{ username }}"
group: "{{ username }}"
mode: "u=rwx,g=rx,o="
ignore_errors: yes
authorized_key:
state: present
user: "{{ username }}"
key: "{{ authorized_keys }}"
- name: Configure zsh
become_user: "{{ username }}"

@ -24,6 +24,17 @@
tags:
- backups
- name: Upload python helper scripts
copy:
src: "{{ item }}"
dest: "{{ global_helper_directory }}/{{ item }}"
owner: root
group: root
mode: "u=rwx,g=rx,o=rx"
validate: "{{ global_validate_python_script }}"
loop:
- check_subnet.py
- name: Build and upload template helper scripts
template:
src: "{{ item }}"

@ -0,0 +1,39 @@
---
# protecting process list of users different than root
# Source: https://wiki.archlinux.org/index.php/Security#hidepid
- name: Configure group for reading other processes
group:
state: present
name: proc
system: yes
- name: Configure proc mounting in fstab
lineinfile:
path: "{{ global_fstab_file }}"
regexp: '^\S+\s+/proc\s+proc\s+'
line: >-
proc /proc proc
nosuid,nodev,noexec,hidepid=2,gid=proc
0 0
- name: Ensure configuration directory for whitelisted services exist
file:
state: directory
path: "{{ global_systemd_configuration_directory }}/{{ item }}.d"
owner: root
group: root
mode: u=rwx,g=rx,o=rx
loop: "{{ global_proc_hidepid_service_whitelist }}"
- name: Configure whitelisted services to adapt to hidepid setting
copy:
content: |
[Service]
SupplementaryGroups=proc
dest: "{{ global_systemd_configuration_directory }}/{{ item }}.d/proc_hidepid_whitelist.conf"
owner: root
group: root
mode: u=rw,g=r,o=r
loop: "{{ global_proc_hidepid_service_whitelist }}"

@ -9,11 +9,18 @@
- name: Configure ufw
import_tasks: ufw.yml
- name: Enforce kernel security
import_tasks: kernel_hidepid.yml
tags:
- kernel_hidepid
- name: Configure locales
import_tasks: locales.yml
- name: Configure journald
import_tasks: journald.yml
tags:
- journald
- name: Configure custom facts
import_tasks: custom_facts.yml

@ -24,7 +24,9 @@
- pv # Required for scripting
- python3
- python3-apt # required for Ansible
- python3-ipy # required for helper check_subnet.py
- python3-pip
- python3-yaml # required for scripting
- sed # required for scripting
- shellcheck
- software-properties-common

@ -35,6 +35,7 @@
owner: root
group: root
mode: "u=rw,g=r,o=r"
validate: "{{ global_validate_sshd_config }}"
notify: reassemble sshd config
- name: Upload main ssh_config

@ -18,6 +18,7 @@
state: present
name:
- bind9
- python3-dnspython
- name: Create directories for zone databases
file:

@ -44,5 +44,11 @@
loop_control:
label: "{{ item.domain | default('@') | domain_relative_to(effective_domain) }}. {{ item.type }}"
delegate_to: "{{ dns_system_domain }}"
register: dns_entries_task
tags:
- dns_entries
- name: Wait for entries to become announced
wait_for:
timeout: 8
when: dns_entries_task.changed

@ -0,0 +1,5 @@
---
docker_configuration:
dns: "{{ ansible_dns.nameservers | ipv4 }}" # use only ipv4 dns servers TODO: check if docker supports ipv6
log-driver: journald # send container logs also to journald

@ -0,0 +1,6 @@
---
- name: restart docker
systemd:
name: "{{ global_docker_service_name }}"
state: restarted

@ -0,0 +1,18 @@
---
- name: Install Docker-CE
apt:
state: present
name:
- docker.io
- docker-compose
- python3-docker
- name: Configure docker daemon
copy:
content: "{{ docker_configuration | to_nice_json }}\n"
dest: "{{ global_docker_daemon_configuration_file }}"
owner: root
group: root
mode: u=rw,g=r,o=r
notify: restart docker

@ -0,0 +1,18 @@
---
# Required arguments
#domain: example.com # to derive instance_name
instance_name: "{{ domain }}" # required if domain is not set
#repo_url: https://git.example.com/app/docker.git
# Optional arguments
repo_version: master # branch/tag of git repository to use
compose_overrides: "" # syntax of common docker-compose file
main_compose_name: docker-compose.yml # to derive main_compose_path
#user_directory # to derive docker_directory
docker_directory: "{{ user_directory }}/docker:{{ instance_name }}"
repository_directory: "{{ docker_directory }}/repository"
main_compose_path: "{{ repository_directory }}/{{{ main_compose_name }}"
compose_override_path: "{{ docker_directory }}/compose.override.yml"

@ -0,0 +1,6 @@
---
allow_duplicates: yes
dependencies:
- role: docker/application

@ -0,0 +1,32 @@
---
- name: Ensure directory for repository exists
file:
state: directory
path: "{{ docker_directory }}"
owner: root
group: root
mode: u=rwx,g=rx,o=rx
- name: Clone git repository
git:
repo: "{{ repo_url }}"
dest: "{{ repository_directory }}"
version: "{{ repo_version }}"
- name: Configure docker-compose overrides
copy:
content: "{{ compose_overrides }}"
dest: "{{ compose_override_path }}"
validate: "/usr/bin/docker-compose -f {{ main_compose_path | quote }} -f %s config" # requires original compose file because override is not (always) valid only
- name: Build and start docker containers
docker_compose:
state: present
project_name: "{{ instance_name }}"
project_src: "{{ repository_directory }}"
files:
- "{{ main_compose_name }}"
- "{{ compose_override_path }}"
build: yes
recreate: smart

@ -0,0 +1,33 @@
---
# domain: example.com
# MailJet will assign a unique verification record which name and data must be given in these variables
# e.g. the record 'mailjet._12345678.example.com TXT "abcdefghijklmnopqrstuvwxyz123456"'
# resolves to name="12345678" and data="abcdefghijklmnopqrstuvwxyz123456"
# verification_name: 12345678
# verification_data: abcdefghijklmnopqrstuvwxyz123456
spf_redirect_domain: "spf.mailjet.com"
dkim_key_name: mailjet
dkim_key_data: >-
"v=DKIM1; k=rsa; "
"p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDrA+r6R11vE4/FMWpgOzNWn5"
"JIzn7/y88DTla9DZvfpbcBFGEKDqhArsa+t9V34TdFzpIss4T80F1C7BrGWnwo46"
"I7rk2y5ee9Ga3iwG5EyilrXF10hw+qk2EsTKdAHld0x24vnzW/tFWfF47eu4ricY"
"/KuIrjXQ4Xs23eCNw6vQIDAQAB"
dmarc_policy: "v=DMARC1;p=none"
# derived DNS record data
# names are relative to domain
verification_record_name: "mailjet._{{ verification_name }}"
verification_record_data: "{{ verification_data }}"
spf_record_name: "@"
spf_record_data: "v=spf1 include:{{ spf_redirect_domain }} -all"
dkim_record_name: "{{ dkim_key_name }}._domainkey"
dkim_record_data: "{{ dkim_key_data }}"
dmarc_record_name: "_dmarc"
dmarc_record_data: "{{ dmarc_policy }}"

@ -0,0 +1,20 @@
---
allow_duplicates: yes
dependencies:
- role: dns/entries
# domain
entries:
- domain: "{{ verification_record_name }}"
type: TXT
data: "{{ verification_record_data }}"
- domain: "{{ spf_record_name }}"
type: TXT
data: "{{ spf_record_data }}"
- domain: "{{ dkim_record_name }}"
type: TXT
data: "{{ dkim_record_data }}"
- domain: "{{ dmarc_record_name }}"
type: TXT
data: "{{ dmarc_record_data }}"

@ -8,6 +8,8 @@
group: root
mode: "u=rwx,g=rx,o=r"
validate: "{{ global_validate_shell_script }}"
tags:
- deploy-auto-update-script
- name: Create repository directory for {{ repo_name }}
file:

@ -21,7 +21,7 @@ fi
git remote set-url origin "$REPO";
[ -z "$GPG_FINGERPRINT" ] ||
gpg --quiet --keyserver eu.pool.sks-keyservers.net --recv "$GPG_FINGERPRINT";
gpg --quiet --keyserver {{ default_gpg_keyserver_hostname | quote }} --recv "$GPG_FINGERPRINT";
git fetch --recurse-submodules --tags > /dev/null;
TAG=$(git tag --list | grep "^$PREFIX" | sort -r | head -n 1);

@ -0,0 +1,5 @@
---
# packages: []
configuration_name: "{{ packages[0] }}"
priority: 990

@ -0,0 +1,6 @@
---
allow_duplicates: yes
dependencies:
- role: misc/deb_backports

@ -0,0 +1,16 @@
---
- name: Restrict backports for apt
copy:
dest: "/etc/apt/preferences.d/backports-{{ configuration_name }}"
owner: root
group: root
mode: "u=rw,g=r,o=r"
content: |
Package: {{ packages | join(" ") }}
Pin: release a={{ debian_backports_name }}
Pin-Priority: {{ priority }}
notify: update apt cache
- name: Flush handlers for backports priority configuration
meta: flush_handlers

@ -1,3 +0,0 @@
---
docker_version: "stable"

@ -1,29 +0,0 @@
---
- name: Add key for source for docker
apt_key:
state: present
id: 9DC858229FC7DD38854AE2D88D81803C0EBFCD88
url: https://download.docker.com/linux/debian/gpg
- name: Add source for docker
apt_repository:
state: present
repo: "deb [arch={{ ansible_local.dpkg.architecture }}] https://download.docker.com/linux/debian {{ ansible_distribution_release }} {{ docker_version }}"
filename: docker
update_cache: yes
- name: Install Docker-CE
apt:
state: present
name:
- docker-ce
- docker-ce-cli
- containerd.io
- python3-docker
install_recommends: no # To fix https://github.com/raspberrypi/linux/issues/3021
- name: Docker Module Python3 Dependencies
pip:
name:
- docker-compose

@ -1,7 +1,12 @@
---
notify_script: "{{ global_deployment_directory }}/ssh_notify/telegram.sh"
notify_directory: "{{ global_deployment_directory }}/ssh_notify"
notify_script: "{{ notify_directory }}/telegram.sh"
notify_cache_directory: "{{ notify_directory }}/cache"
notify_users_directory: "{{ notify_directory }}/users"
trusted_vpn_subnet: "{{ tailscale_vpn_subnet }}"
# recipient_id
bot_key: "{{ global_ssh_notify_telegram_bot_key }}"
bot_key: "{{ global_telegram_server_bot_key }}"
timeout: 10

@ -7,13 +7,25 @@
- curl
- gawk
- name: Create directory for notify script
- name: Create directories for notify script
file:
state: directory
path: "{{ notify_script | dirname }}"
path: "{{ item }}"
owner: root
group: root
mode: u=rwx,g=rx,o=
loop:
- "{{ notify_directory }}"
- "{{ notify_cache_directory }}"
- "{{ notify_users_directory }}"
- name: Configure recipient id for root user
ansible.builtin.template:
src: root_id.j2
dest: "{{ notify_users_directory }}/root"
owner: root
group: root
mode: u=rw,g=r,o=
- name: Install notify script
template:

@ -1,14 +1,41 @@
#!/bin/bash
# Modified version, original source: https://gitlab.com/snippets/1871482#note_188602535
USERID={{ recipient_id | quote }}
USER_ID_DIR={{ notify_users_directory | quote }}
CACHE_DIR={{ notify_cache_directory | quote }}
KEY={{ bot_key | quote }}
VPN_SUBNET={{ trusted_vpn_subnet | quote }}
TIMEOUT={{ timeout | quote }}
getUserId() {
USER_CONF="${USER_ID_DIR}/$1"
[[ -r "$USER_CONF" ]] && head -n 1 "$USER_CONF"
}
URL="https://api.telegram.org/bot$KEY/sendMessage"
if [[ "$PAM_SERVICE" == "sshd" && "$PAM_TYPE" == "open_session" && "$PAM_USER" != "git" && -z "$TMUX" ]]; then
sendMessage() {
curl -s --max-time "$TIMEOUT" -H "Content-Type: application/x-www-form-urlencoded" -d "chat_id=$1" -d "disable_web_page_preview=1" -d "parse_mode=Markdown" -d "text=$2" "$URL" >/dev/null
}
if [[ "$PAM_SERVICE" == "sshd" && "$PAM_TYPE" == "open_session" && "$PAM_USER" != "git" && -z "$TMUX" && -n "$PAM_RHOST" ]] && ! /ansible/helpers/check_subnet.py "$PAM_RHOST" "$VPN_SUBNET"; then
IP="$PAM_RHOST"
cache_file="${CACHE_DIR}/${IP}-${PAM_USER}"
cache_mtime=$(stat --format="%Y" "$cache_file" 2>/dev/null)
current_time=$(date +%s)
touch "$cache_file"
if (( cache_mtime > (current_time - 4*60*60) )); then
exit 0
fi
# define message text
HOSTNAME=$(hostname --fqdn)
TEXT="Successful login from [$IP](https://ipinfo.io/$IP) for ${PAM_USER} @ ${HOSTNAME} ($(date "+%Y-%m-%d %H:%M"))"
curl -s --max-time $TIMEOUT -d "chat_id=$USERID" -d "disable_web_page_preview=1" -d "parse_mode=Markdown" -d "text=$TEXT" "$URL" > /dev/null
TEXT="Successful login from [$IP](https://stat.ripe.net/app/$IP) for ${PAM_USER} @ ${HOSTNAME} ($(date "+%Y-%m-%d %H:%M"))"
# send to root
ROOT_USER_ID="$(getUserId root)"
sendMessage "$ROOT_USER_ID" "$TEXT (This message was sent to you because you are the admin.)"
# send to user if id is known
USER_ID="$(getUserId "$PAM_USER")"
if [[ -n "$USER_ID" ]]; then
sendMessage "$USER_ID" "$TEXT"
fi
fi

@ -0,0 +1,26 @@
---
# monitor_name: "echo-output-check"
instance_name: "tg-monitor-cmd-{{ monitor_name }}"
description: "{{ monitor_name }}" # should be human fancy
# command: "/bin/echo Hello" # or command_str
command_str: "{{ command | map('quote') | join(' ') }}"
use_shell: no # read https://docs.python.org/3/library/subprocess.html#security-considerations before using use_shell=yes
system_user: tg-monitor-cmd
recipient_id: "{{ default_tg_monitor_recipient_id }}"
bot_key: "{{ global_telegram_server_bot_key }}"
telegram_timeout: 10
calendar_spec: "*:0:0" # once every hour
service_description: |
Telegram Monitor Command of {{ description }}
# paths
monitoring_directory: "{{ global_deployment_directory }}/tg-monitor-cmd"
instance_directory: "{{ monitoring_directory }}/{{ monitor_name }}"
script_path: "{{ instance_directory }}/script.py"
data_path: "{{ instance_directory }}/data"

@ -0,0 +1,8 @@
---
allow_duplicates: yes
dependencies:
- role: misc/system_user
# system_user
user_directory: "{{ monitoring_directory }}"

@ -0,0 +1,69 @@
---
- name: Create directory for monitoring scripts and data
file:
state: directory
path: "{{ instance_directory }}"
owner: root
group: "{{ system_user }}"
mode: u=rwx,g=rx,o=
- name: Deploy script
template:
src: monitor.py
dest: "{{ script_path }}"
owner: root
group: "{{ system_user }}"
mode: u=rwx,g=rx,o=
register: script_task
- name: Create empty data file
copy:
content: ""
dest: "{{ data_path }}"
force: no # do not overwrite
- name: Ensure permissions on data file
file:
state: file
path: "{{ data_path }}"
owner: root
group: "{{ system_user }}"
mode: u=rw,g=rw,o=
- name: Register service for monitor
template:
src: monitor.service
dest: "{{ global_systemd_configuration_directory }}/{{ instance_name }}.service"
owner: root
group: root
mode: u=rw,g=r,o=
register: service_task
- name: Run service for initial test
systemd:
state: started
daemon_reload: yes
name: "{{ instance_name }}.service"
when: script_task.changed or service_task.changed
- name: Register timer for monitor service
template:
src: monitor.timer
dest: "{{ global_systemd_configuration_directory }}/{{ instance_name }}.timer"
owner: root
group: root
mode: u=rw,g=r,o=
register: timer_task
- name: Restart timer for monitor
systemd:
state: restarted
daemon_reload: yes
name: "{{ instance_name }}.timer"
when: timer_task.changed
- name: Enable timer for monitor
systemd:
name: "{{ instance_name }}.timer"
enabled: yes

@ -0,0 +1,64 @@
#!/usr/bin/env python3
# imports
from pathlib import Path
from hashlib import sha256
import shlex
import subprocess
import sys
import requests
# config
MONITOR_DESC = """{{ description }}"""
MONITOR_COMMAND = """{{ command_str }}"""
USE_SHELL = {{ use_shell | ternary('True', 'False') }}
DATA_PATH = Path("""{{ data_path }}""")
TG_ENDPOINT = "https://api.telegram.org"
TG_KEY = """{{ bot_key }}"""
TG_RECIPIENT = """{{ recipient_id }}"""
# helpers
def print_error(*args, **kwargs):
print(*args, file=sys.stderr, **kwargs)
def tg_msg(msg: str) -> None:
print(f"Sending message using telegram:\n{msg}")
ret = requests.post(f"{TG_ENDPOINT}/bot{TG_KEY}/sendMessage", data={
"chat_id": TG_RECIPIENT,
"disable_web_page_preview": 1,
"parse_mode": "Markdown",
"text": msg,
})
if 400 <= ret.status_code:
raise Exception(f"Sending telegram message failed: {ret.status_code} {ret.text}")
def run_cmd(cmd: list, **kwargs) -> str:
return subprocess.run(cmd, capture_output=True, check=True, text=True, **kwargs).stdout
def hash_data(data: str) -> bool:
return sha256(data.encode("utf-8")).hexdigest()
def check_cmd(cmd_str: str, use_shell: bool, data_file: Path) -> str:
cmd = shlex.split(cmd_str) if not use_shell else cmd_str
old_hash = data_file.read_text() if data_file.exists() else None
new_data = run_cmd(cmd, shell=use_shell)
new_hash = hash_data(new_data)
if old_hash == new_hash:
return None
data_file.write_text(new_hash)
return new_data
if __name__ == "__main__":
try:
data = check_cmd(MONITOR_COMMAND, USE_SHELL, DATA_PATH)
if data:
tg_msg(f"{MONITOR_DESC} changed to:\n```\n{data}\n```")
except Exception as e:
tg_msg(f"Got exception while running command of {MONITOR_DESC}: {str(e)}")
raise e

@ -0,0 +1,23 @@
[Unit]
Description={{ service_description }}
[Service]
Type=simple
ExecStart={{ script_path | quote }}
User={{ system_user }}
Group={{ system_user }}
UMask=007
PrivateTmp=yes
PrivateDevices=yes
ProtectHome=yes
ReadOnlyPaths=/
ReadWritePaths=-{{ data_path }}
ProtectKernelModules=true
ProtectKernelTunables=true
ProtectControlGroups=true
RestrictRealtime=true
RestrictNamespaces=true
ProtectSystem=full

@ -0,0 +1,8 @@
[Unit]
Description=Timer of {{ service_description }}
[Timer]
OnCalendar={{ calendar_spec }}
[Install]
WantedBy=multi-user.target

@ -1,6 +1,8 @@
---
- meta: flush_handlers
tags:
- mysql_database
- name: Create SQL user {{ database_user }}
mysql_user:
@ -13,6 +15,8 @@
login_unix_socket: "{{ global_mysql_socket_path }}"
login_user: root
login_password: "{{ mysql_root_password }}"
tags:
- mysql_database
- name: Create SQL database {{ database_name }}
mysql_db:
@ -21,7 +25,18 @@
login_user: root
login_password: "{{ mysql_root_password }}"
register: create_database
tags:
- mysql_database
- name: Import SQL database template on creation
include_tasks: import.yml
when: create_database.changed and database_template is defined
include_tasks:
file: import.yml
apply:
tags:
- mysql_database
when:
- create_database is defined
- create_database.changed
- database_template is defined
tags:
- always

@ -36,12 +36,14 @@
with_items: "{{ nginx_snippets }}"
notify: reload nginx
- name: Retrieve dns resolver addresses
shell: >-
echo resolver $(awk 'BEGIN{ORS=" "} $1=="nameserver" {print $2}' /etc/resolv.conf) ';'
> {{ nginx_snippets_directory | quote }}/resolver.conf
args:
creates: "{{ nginx_snippets_directory }}/resolver.conf"
- name: Configure dns resolver addresses for nginx
copy:
content: |
resolver {{ ansible_dns.nameservers | ipwrap | join(' ') }};
dest: "{{ nginx_snippets_directory }}/resolver.conf"
owner: root
group: root
mode: u=rwx,g=rx,o=rx
notify: reload nginx
- name: Configure validation directory

@ -1,4 +1,4 @@
---
domain: example.com
dest: example.com
# domain: old.example.com
# dest: new.example.com

@ -3,5 +3,7 @@
allow_duplicates: yes
dependencies:
- role: acme/certificate
- role: nginx/application
- role: nginx/server
# domain
directives: |
return 301 https://{{ dest }}$request_uri;

@ -1,12 +0,0 @@
---
- name: Enable forwarding {{ domain }} to {{ dest }}
template:
src: forward.conf
dest: "{{ nginx_sites_directory }}/{{ domain }}"
owner: root
group: root
mode: "u=rw,g=r,o=r"
notify: reload nginx
tags:
- certificate

@ -1,14 +0,0 @@
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name {{ effective_domain }};
ssl on;
ssl_certificate {{ acme_fullchain_location }};
ssl_certificate_key {{ acme_key_location }};
include {{ nginx_snippets_directory }}/https;
include {{ nginx_snippets_directory }}/global;
return 301 https://{{ dest }}$request_uri;
}

@ -9,6 +9,13 @@ socket: "{{ socket_directory }}/socket"
allow_overwrite_includes: no
includes: []
env_vars: {}
memory_limit: 0 # unlimited
admin_values: {}
# status_page_path: "/status" # Disabled by default
default_admin_values:
memory_limit: None # unlimited
enforced_admin_values: {}
effective_admin_values: "{{ default_admin_values | combine(admin_values) | combine(enforced_admin_values) }}"

@ -24,3 +24,5 @@
group: root
mode: u=rw,g=r,o=
notify: "reload php-fpm"
tags:
- nginx-php-pool-config

@ -37,6 +37,6 @@ env[{{ key }}] = {{ val | quote }}
{% if not allow_overwrite_includes %}
php_admin_value[include_path] = ".:{{ includes | join(':') }}:/usr/share/php"
{% endif %}
{% if memory_limit is defined and memory_limit %}
php_admin_value[memory_limit] = {{ memory_limit }}
{% endif %}
{% for key, value in admin_values.items() %}{% if value != None %}
php_admin_value[{{ key }}] = {{ value | quote }}
{% endif %}{% endfor %}

@ -3,7 +3,6 @@ server {
listen [::]:443 ssl http2;
server_name {{ effective_domain }};
ssl on;
ssl_certificate {{ acme_fullchain_location }};
ssl_certificate_key {{ acme_key_location }};

@ -10,3 +10,4 @@
notify: reload nginx
tags:
- certificate
- nginx-server-config

@ -3,7 +3,6 @@ server {
listen [::]:443 ssl http2;
server_name {{ effective_domain }};
ssl on;
ssl_certificate {{ acme_fullchain_location }};
ssl_certificate_key {{ acme_key_location }};

@ -0,0 +1,9 @@
---
instance_name: "drone-runner" # must be unique if multiple runners deployed to machine
docker_image: "drone/drone-runner-docker:1"
# drone_server_host: ci.example.com
drone_rpc_secret: "{{ lookup('file', 'credentials/' + drone_server_host + '/rpc_secret') }}" # sync with server/drone.io/server, because must be known to all runners
drone_runner_capacity: 4
drone_runner_name: "{{ inventory_hostname }}"

@ -0,0 +1,6 @@
---
allow_duplicates: yes
dependencies:
- role: docker/application

@ -0,0 +1,21 @@
---
- name: Start drone runner using docker-compose
docker_compose:
state: present
project_name: "{{ instance_name }}"
definition:
version: '2'
services:
drone-runner:
image: "{{ docker_image }}"
restart: always
environment:
DRONE_RPC_PROTO: https
DRONE_RPC_HOST: "{{ drone_server_host }}"
DRONE_RPC_SECRET: "{{ drone_rpc_secret }}"
DRONE_RUNER_CAPACITY: "{{ drone_runner_capacity }}"
DRONE_RUNNER_NAME: "{{ drone_runner_name }}"
DOCKER_API_VERSION: "1.39"
volumes:
- "/var/run/docker.sock:/var/run/docker.sock"

@ -0,0 +1,21 @@
---
# domain: ci.example.com
docker_image: "drone/drone:1"
# TODO Bind to socket path
# bind_port
#!socket_directory: "{{ user_directory }}/socket"
#!socket_path: "{{ socket_directory }}/socket"
# gitea_server_url: https://git.example.com/gitea
# gitea_client_id generated by gitea
# gitea_client_secret generated by gitea
instance_directory: "{{ global_webservers_directory }}/{{ domain }}"
data_directory: "{{ instance_directory }}/data"
drone_data_directory: "{{ data_directory }}/drone_volume"
drone_admin_user: "{{ global_username }}"
drone_rpc_secret: "{{ lookup('password', 'credentials/' + domain + '/rpc_secret chars=digits,ascii_letters length=80') }}" # sync with server/drone.io/runner, because must be known to all runners
drone_database_secret: "{{ lookup('password', 'credentials/' + domain + '/database_secret length=32 chars=0,1,2,3,4,5,6,7,8,9,a,b,c,d,e,f') }}"

@ -0,0 +1,15 @@
---
allow_duplicates: yes
dependencies:
- role: docker/application
- role: misc/backup_files
# domain
backup_directory: "{{ data_directory }}"
- role: misc/hdd_dir
# domain
hdd_source_dir: "{{ data_directory }}"
- role: nginx/proxy
# domain
backend_port: "{{ bind_port }}"

@ -0,0 +1,47 @@
---
- name: Create instance directory
file:
state: directory
path: "{{ instance_directory }}"
owner: root
group: root
mode: u=rwx,g=rx,o=
- name: Create general data directory
file:
state: directory
path: "{{ data_directory }}"
owner: root
group: root
mode: u=rwx,g=rx,o=
- name: Create data directory for drone volume
file:
state: directory
path: "{{ drone_data_directory }}"
# let docker/drone.io manage control permissions
- name: Start drone server using docker-compose
docker_compose:
state: present
project_name: "{{ domain }}"
definition:
version: '2'
services:
drone-server:
image: "{{ docker_image }}"
restart: always
environment:
DRONE_DATABASE_SECRET: "{{ drone_database_secret }}"
DRONE_GITEA_SERVER: "{{ gitea_server_url }}"
DRONE_GITEA_CLIENT_ID: "{{ gitea_client_id }}"
DRONE_GITEA_CLIENT_SECRET: "{{ gitea_client_secret }}"
DRONE_RPC_SECRET: "{{ drone_rpc_secret }}"
DRONE_SERVER_HOST: "{{ domain }}"
DRONE_SERVER_PROTO: https
DRONE_USER_CREATE: "username:{{ drone_admin_user }},admin:true"
ports:
- "127.0.0.1:{{ bind_port }}:80" # for nginx reverse proxy
volumes:
- "{{ data_directory }}:/data"

@ -6,6 +6,7 @@
name:
- git-core
- g++
- libmariadb-dev
- python-dev
- python-virtualenv

@ -64,6 +64,8 @@
owner: root
group: root
mode: "u=rwx,g=rx,o=r"
tags:
- deploy-auto-update-script
- name: Download gitea
command: "{{ gitea_update_script_path }}"

@ -10,7 +10,7 @@ readonly SERVICE_NAME={{ gitea_service_name | quote }};
set -euxo pipefail;
gpg --quiet --keyserver eu.pool.sks-keyservers.net --recv "$GPG_FINGERPRINT";
gpg --quiet --keyserver {{ default_gpg_keyserver_hostname | quote }} --recv "$GPG_FINGERPRINT";
function error() {
echo "$@" >&2;

@ -31,6 +31,8 @@
owner: root
group: root
mode: "u=rwx,g=rx,o=r"
tags:
- deploy-auto-update-script
- name: Download linx
command: "{{ update_script_path }}"

@ -13,6 +13,7 @@ nextcloud_release_remote_signature: "{{ nextcloud_release_remote }}.asc"
user_directory: "{{ global_webservers_directory }}/{{ domain }}"
nextcloud_installation_directory: "{{ user_directory }}/nextcloud" # directory name of inside downloaded tar zip
nextcloud_data_directory: "{{ user_directory }}/data"
scripts_directory: "{{ user_directory }}/scripts"
nextcloud_keyring: "{{ user_directory }}/nextcloud.gpg"
nextcloud_release_file: "{{ user_directory }}/nextcloud.tar.bz2"
@ -32,8 +33,14 @@ database_user: "{{ system_user }}"
nextcloud_admin_user: "admin"
nextcloud_admin_pass: "{{ lookup('password', 'credentials/' + inventory_hostname + '/' + domain + '/' + nextcloud_admin_user + ' length=80') }}"
default_phone_region: "DE"
files_chunk_size: "{{ 10 * 1024 * 1024 }}" # bytes
reset_password_link: "disabled" # "" means internal enabled, "disabled" means disabled, <link> means using this link
nginx_max_size: "{{ php_post_max_size|int + 4 * 1024 }}" # appending encryption overhead
php_post_max_size: "{{ php_upload_max_size|int + 4 * 1024 }}" # appending HTTP overhead
php_upload_max_size: "{{ files_chunk_size|int + 16 * 1024 }}" # appending internal overhead
import_config:
system:
# domain
@ -41,7 +48,7 @@ import_config:
trusted_domains:
- "{{ effective_domain }}"
# NextCloud
lost_password_link: "{{ reset_password_link }}" # disallow custom password reset
lost_password_link: "{{ reset_password_link }}"
# database
dbtype: mysql
dbhost: localhost
@ -56,6 +63,11 @@ import_config:
redis:
host: "{{ redis_socket_path }}"
port: 0
# user experience
default_phone_region: "{{ default_phone_region }}"
apps:
files:
max_chunk_size: "{{ files_chunk_size }}"
enabled_apps_list:
- accessibility

@ -20,7 +20,10 @@ dependencies:
src: "{{ nextcloud_installation_directory }}"
includes:
- "{{ nextcloud_installation_directory }}/apps"
memory_limit: 1G
admin_values:
memory_limit: 1G
post_max_size: "{{ php_post_max_size }}"
upload_max_size: "{{ php_upload_max_size }}"
- role: redis/instance
# domain
# system_user
@ -41,7 +44,7 @@ dependencies:
rewrite ^/.well-known/host-meta /public.php?service=host-meta last;
rewrite ^/.well-known/host-meta.json /public.php?service=host-meta-json last;
rewrite ^/.well-known/webfinger /public.php?service=webfinger last;
client_max_body_size 10240M;
client_max_body_size {{ nginx_max_size }};
#fastcgi_buffers 64 4K;
location / {
rewrite ^ /index.php;

@ -56,7 +56,10 @@
- name: Install Nextcloud
become_user: "{{ system_user }}"
command: >-
/usr/bin/php occ maintenance:install
/usr/bin/php
--define apc.enable_cli=1
occ
maintenance:install
--database mysql
--database-name {{ database_name | quote }}
--database-user {{ database_user | quote }}
@ -82,20 +85,41 @@
- name: Import additional Nextcloud configuration
become_user: "{{ system_user }}"
command: >-
/usr/bin/php occ
/usr/bin/php
--define apc.enable_cli=1
occ
config:import
{{ import_config_file | quote }}
args:
chdir: "{{ nextcloud_installation_directory }}"
creates: "{{ nextcloud_config }}"
when: import_config_file_task.changed
tags:
- nextcloud_config
- name: Create scripts directories
file:
state: directory
path: "{{ scripts_directory }}"
owner: "{{ system_user }}"
group: "{{ system_user }}"
mode: "u=rwx,g=rx,o="
- name: Install helper scripts
template:
src: "scripts/{{ item }}"
dest: "{{ scripts_directory }}/{{ item }}"
owner: "{{ system_user }}"
group: "{{ system_user }}"
mode: "u=rwx,g=rx,o="
loop:
- extract_app_list.py
- name: Install Nextcloud apps
become_user: "{{ system_user }}"
command: >-
/usr/bin/php occ
/usr/bin/php
--define apc.enable_cli=1
occ
app:install
{{ item | quote }}
args:
@ -113,7 +137,9 @@
- name: Disable some Nextcloud apps
become_user: "{{ system_user }}"
command: >-
/usr/bin/php occ
/usr/bin/php
--define apc.enable_cli=1
occ
app:disable
{{ item | quote }}
args:

@ -1,39 +0,0 @@
#!/usr/bin/env bash
readonly CHECKSUM_TYPE="sha256";
readonly CHECKSUM_APP="${CHECKSUM_TYPE}sum";
readonly GPG_FINGERPRINT="{{ nextcloud_gpg_fingerprint }}";
readonly NEXTCLOUD_USER="{{ system_user }}";
readonly NEXTCLOUD_DIR="{{ nextcloud_installation_directory }}";
readonly NEXTCLOUD_GIT_REPO="{{ nextcloud_source_repo }}";
readonly GIT_REPO="https://github.com/nextcloud/server";
readonly VERSION_REGEX='v\d+(\.\d+)*';
set -e;
gpg --quiet --keyserver eu.pool.sks-keyservers.net --recv "$GPG_FINGERPRINT";
function error() {
echo "$@" >&2;
}
function as() {
sudo -u "$NEXTCLOUD_USER" "$@";
}
cd "$NEXTCLOUD_DIR";
version="$(curl --silent "$GIT_REPO/releases.atom"
| grep --only-matching --perl-regexp '(?<=\s)<link.*/>'
| grep --only-matching --perl-regexp '(?<=href="'"$GIT_REPO"'/releases/tag/'"$VERSION_REGEX"'(?="/>)'
| sort --reverse --numeric-sort
| head --lines=1)";
if g verify-tag --raw "$TAG" 2>&1 | grep --fixed-strings "[GNUPG:] VALIDSIG $GPG_FINGERPRINT " > /dev/null; then
as composer update;
else
error "Invalid or missing signature for $TAG";
exit 1;
fi

@ -0,0 +1,33 @@
#!/usr/bin/env python3
import getpass
import subprocess
import json
import sys
NC_USER = """{{ system_user }}"""
NC_INSTALL_DIR = """{{ nextcloud_installation_directory }}"""
if __name__ == "__main__":
args_list = [
"/usr/bin/env",
"php",
"occ",
"app:list",
"--output=json"
]
if getpass.getuser() != NC_USER:
args_list = [
"sudo",
"-u", NC_USER,
] + args_list
try:
proc = subprocess.run(args_list, capture_output=True, check=True, cwd=NC_INSTALL_DIR, text=True)
except subprocess.CalledProcessError as e:
print(e.stderr, file=sys.stderr)
raise e
apps_nc = json.loads(proc.stdout)
for name, apps in reversed(sorted(apps_nc.items())):
print(f"{name}_apps_list:")
for app_name in apps:
print(f" - {app_name}")

@ -8,9 +8,9 @@ spotme_service_name: "{{ domain }}.service"
spotme_user_directory: "{{ global_webservers_directory }}/{{ domain }}"
spotme_installation_directory: "{{ spotme_user_directory }}/server"
service_environment_file: "{{ user_directory }}/{{ service_name }}.env"
service_environment_file: "{{ user_directory }}/{{ spotme_service_name }}.env"
database_user: "spotme"
database_user: "{{ spotme_system_user }}"
# database_pass from mysql/database
# database_name from mysql/database

@ -4,7 +4,7 @@
apt:
state: present
name:
- openjdk-8-jdk-headless
- openjdk-11-jdk-headless
update_cache: yes
# TODO Role for Git Username / Password Configuration
@ -22,6 +22,7 @@
args:
stdin: "{{ spotme_remote_user | urlencode }}"
register: remote_user_encoded
check_mode: no # only converts some data, does not change anything
changed_when: False
- name: Encode password for SpotMe remote source
@ -30,6 +31,7 @@
args:
stdin: "{{ spotme_remote_pass | urlencode }}"
register: remote_pass_encoded
check_mode: no # only converts some data, does not change anything
changed_when: False
- name: Store credentials for SpotMe remote source

@ -122,7 +122,7 @@
// *** Cookies and login sessions ***
// **********************************
define('SESSION_COOKIE_LIFETIME', 86400);
define('SESSION_COOKIE_LIFETIME', 2592000);
// Default lifetime of a session (e.g. login) cookie. In seconds,
// 0 means cookie will be deleted when browser closes.

@ -11,11 +11,14 @@
roles:
- role: bootstrap
- hosts: all
- name: Configure common roles expected by others
hosts: common_roles
roles:
- role: hostname
fqdn: "{{ inventory_hostname }}"
- role: common
tags:
- common
- role: fail2ban/application
- role: account
username: "{{ global_username }}"
@ -24,16 +27,18 @@
sudo: yes
# Enroll certain features not on ansible test/debug servers
- hosts: all:!ansible_debug
- hosts: common_roles:!ansible_debug
roles:
- role: misc/ssh_tg_notify
recipient_id: "{{ zocker_telegram_id }}"
tags:
- ssh_tg_notify
# Group specific configurations
- name: Include configuration for group bwcloud
import_playbook: playbooks/group_bwcloud.yml
- name: Include configuration for group surface3
import_playbook: playbooks/group_surface3.yml
- name: Include configuration for group dev_surface3
import_playbook: playbooks/group_dev_surface3.yml
- name: Include configuration for group os_raspbian
import_playbook: playbooks/group_os_raspbian.yml

Loading…
Cancel
Save