From 3f69aa6af14a9e4d79b42b42dbf93992c0c973d5 Mon Sep 17 00:00:00 2001 From: Alex Tavarez Date: Sat, 26 Sep 2026 14:18:27 -0400 Subject: [PATCH] new major update --- administrate@homeserver.yml | 476 +++++++-- administrate@vps.yml | 45 +- host_vars/staging0.test.yml | 27 +- host_vars/staging1.test.yml | 55 +- init@homeserver.yml | 53 +- init@vps.yml | 87 +- .../files/systemd/system/thrunet.service | 4 +- roles/init-server/handlers/core.yml | 20 + roles/init-server/handlers/userspace.yml | 52 +- .../tasks/contingent/pkg/avahi-daemon.yml | 7 + .../tasks/contingent/pkg/certbot.yml | 71 +- .../tasks/contingent/pkg/crowdsec.yml | 1 + .../tasks/contingent/pkg/dsnet.yml | 130 ++- .../tasks/contingent/pkg/headscale.yml | 11 +- .../tasks/contingent/pkg/iptables.yml | 101 ++ .../pkg/libpam-google-authenticator.yml | 24 +- .../tasks/contingent/pkg/podman.yml | 50 +- .../tasks/contingent/pkg/quartz.yml | 9 +- .../tasks/contingent/pkg/radicle.yml | 3 +- .../tasks/contingent/pkg/rustup.yml | 2 +- .../tasks/contingent/pkg/skato_compose.yml | 964 +++++++++++++++--- .../init-server/tasks/contingent/pkg/ufw.yml | 72 ++ roles/init-server/tasks/contingent/pkg/uv.yml | 2 +- .../tasks/contingent/pkg/wireguard.yml | 8 + roles/init-server/tasks/core@install-pkgs.yml | 7 + roles/init-server/tasks/netrc.yml | 4 +- .../init-server/templates/user/actual.env.j2 | 1 + roles/init-server/templates/user/all.env.j2 | 10 +- roles/init-server/templates/user/aria.env.j2 | 9 + .../templates/user/aria2/aria2.conf.j2 | 4 +- .../templates/user/config/caddy/Caddyfile.j2 | 2 +- .../user/config/glance/glance.yml.j2 | 14 +- .../user/config/opengist/config.yml.j2 | 2 +- .../user/config/tailscale/conf.json.j2 | 84 ++ .../templates/user/flexget/config.yml.j2 | 24 +- .../init-server/templates/user/forgejo.env.j2 | 28 + roles/init-server/templates/user/gitea.env.j2 | 17 - roles/init-server/templates/user/stash.env.j2 | 1 + .../templates/user/syncplay.env.j2 | 8 + .../usr/local/bin/dsnet-forward.sh.j2 | 22 - .../usr/local/bin/dsnet-routing.sh.j2 | 28 + .../usr/local/bin/dsnet-routing6.sh.j2 | 37 + vars/actual_settings.yml | 3 + vars/aria_settings.yml | 6 +- vars/crowdsec_settings.yml | 8 +- vars/flexget_settings.yml | 1 + vars/forgejo_settings.yml | 36 + vars/git_aliases.yml | 58 +- vars/gitea_settings.yml | 16 - vars/iptables_settings@homeserver.yml | 21 + vars/iptables_settings@vps.yml | 37 + vars/kavita_settings.yml | 2 + vars/local_facts.yml | 10 +- vars/mysql_settings.yml | 8 +- vars/openpgp_settings.yml | 13 + vars/podpose_settings.yml | 24 + vars/soulseek_settings.yml | 2 + vars/stash_settings.yml | 3 + vars/syncplay_settings.yml | 6 + vars/vpn_settings.yml | 13 +- 60 files changed, 2228 insertions(+), 615 deletions(-) create mode 100644 roles/init-server/tasks/contingent/pkg/avahi-daemon.yml create mode 100644 roles/init-server/tasks/contingent/pkg/iptables.yml create mode 100644 roles/init-server/tasks/contingent/pkg/ufw.yml create mode 100644 roles/init-server/templates/user/actual.env.j2 create mode 100644 roles/init-server/templates/user/aria.env.j2 create mode 100644 roles/init-server/templates/user/forgejo.env.j2 delete mode 100644 roles/init-server/templates/user/gitea.env.j2 create mode 100644 roles/init-server/templates/user/stash.env.j2 create mode 100644 roles/init-server/templates/user/syncplay.env.j2 delete mode 100755 roles/init-server/templates/usr/local/bin/dsnet-forward.sh.j2 create mode 100755 roles/init-server/templates/usr/local/bin/dsnet-routing.sh.j2 create mode 100755 roles/init-server/templates/usr/local/bin/dsnet-routing6.sh.j2 create mode 100644 vars/actual_settings.yml create mode 100644 vars/forgejo_settings.yml delete mode 100644 vars/gitea_settings.yml create mode 100644 vars/iptables_settings@homeserver.yml create mode 100644 vars/iptables_settings@vps.yml create mode 100644 vars/kavita_settings.yml create mode 100644 vars/openpgp_settings.yml create mode 100644 vars/soulseek_settings.yml create mode 100644 vars/stash_settings.yml create mode 100644 vars/syncplay_settings.yml diff --git a/administrate@homeserver.yml b/administrate@homeserver.yml index 6897ba0..5d56f35 100644 --- a/administrate@homeserver.yml +++ b/administrate@homeserver.yml @@ -2,38 +2,37 @@ hosts: "{{ chosen_host | default('staging1.test') }}" remote_user: "{{ chosen_user | default('senpai') }}" vars_files: + - vars/local_facts.yml # REQUIRED - vars/email_settings.yml - vars/podpose_settings.yml # REQUIRED - vars/source_code.yml # REQUIRED + - vars/iptables_settings@homeserver.yml - vars/caddy_settings.yml - vars/certbot_settings@homeserver.yml # REQUIRED - vars/mysql_settings.yml - vars/redis_settings.yml - vars/nextcloud_settings.yml - - vars/gitea_settings.yml + - vars/forgejo_settings.yml - vars/opengist_settings.yml + - vars/nodebb_settings.yml + - vars/syncplay_settings.yml - vars/vpn_settings.yml - vars/headscale_settings.yml - vars/tailscale_settings.yml - vars/glance_settings.yml - vars/surge_settings.yml - vars/aria_settings.yml + - vars/soulseek_settings.yml - vars/flexget_settings.yml # REQUIRED + - vars/actual_settings.yml + - vars/kavita_settings.yml + - vars/stash_settings.yml - vars/git_aliases.yml # REQUIRED - vars/vim_settings.yml # REQUIRED - vars/tor_settings.yml # REQUIRED + - vars/openpgp_settings.yml # REQUIRED - vars/config@{{ inventory_hostname | default('homeserver') }}.yml tasks: - - name: Saving HTTP/(S)FTP credentials - ansible.builtin.include_role: - name: init-server - tasks_from: netrc.yml - handlers_from: userspace - - name: Installing requisite packages - ansible.builtin.include_role: - name: init-server - tasks_from: userspace@install-pkgs - handlers_from: userspace - name: Acquiring home of current user when: ansible_facts["system"] == "Linux" ansible.builtin.shell: @@ -44,26 +43,117 @@ ansible.builtin.shell: cmd: "echo {{ ansible_user }}" register: current_user - - name: Disabling root user shell login - become: true - ansible.builtin.user: - name: root - shell: /sbin/nologin - tags: - - disable_root_shell - - name: Disable login for root user altogether - become: true - ansible.builtin.user: - name: root - password: "'*'" - tags: - - disable_root_login - name: Reorganizing userspace package groups into single list ansible.builtin.set_fact: - all_userspace_pkgs: "{{ pkgs | dict2items(key_name='pkg_group', value_name='pkgs') | map(attribute='pkgs') | list | map(attribute='userspace', default='no_userspace') | list | flatten | reject('search', 'no_userspace') | list }}" + all_userspace_pkgs: "{{ pkgs | dict2items(key_name='pkg_group', value_name='pkgs') | map(attribute='pkgs') | list | map(attribute='userspace', default='no_userspace') | list | flatten | reject('search', 'no_userspace') | map(attribute='name') | list }}" - name: Reorganizing core package groups into single list ansible.builtin.set_fact: - all_core_pkgs: "{{ pkgs | dict2items(key_name='pkg_group', value_name='pkgs') | map(attribute='pkgs') | list | map(attribute='core', default='no_core') | list | flatten | reject('search', 'no_core') | list }}" + all_core_pkgs: "{{ pkgs | dict2items(key_name='pkg_group', value_name='pkgs') | map(attribute='pkgs') | list | map(attribute='core', default='no_core') | list | flatten | reject('search', 'no_core') | map(attribute='name') | list }}" + - name: Saving HTTP/(S)FTP credentials + ansible.builtin.include_role: + name: init-server + tasks_from: netrc.yml + handlers_from: userspace + - name: Transferring OpenPGP / GPG keyring to managed node + block: + - name: Finding GPG keyrings for user + delegate_facts: true + delegate_to: localhost + become: true + become_user: "{{ local_facts['user_id'] }}" + ansible.builtin.find: + paths: "{{ local_facts['user_dir'] }}/.gnupg" + patterns: ["^pubring\\.kbx$"] + file_type: file + use_regex: true + register: gpg_keys + - name: Creating user directory for OpenPGP / GPG keyring + ansible.builtin.file: + path: "{{ ansible_user_home.stdout }}/.gnupg" + recurse: true + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + state: directory + mode: "700" + - name: Emitting file content as file in managed node + become: true + become_user: "{{ current_user.stdout }}" + ansible.builtin.copy: + content: "{{ lookup('file', item.path) }}" + dest: "{{ ansible_user_home.stdout }}/.gnupg/{{ (((fqdn | default(inventory_hostname)) | split('.'))[:-1] | join('.')) + (idx | string) }}.kbx" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + mode: "664" + force: true + backup: true + loop: "{{ gpg_keys.files }}" + loop_control: + index_var: idx + - name: Creating an OpenPGP / GPG batch file for OpenPGP / GPG key pair creation + become: true + ansible.builtin.blockinfile: + path: "/tmp/gpg-key{{ idx | string }}" + block: | + Key-Type: {{ item.encryption_method }} + Key-Length: {{ item.length | string }} + Subkey-Type: {{ item.primary_subkey.encryption_method }} + Subkey-Length: {{ item.primary_subkey.length | string }} + Name-Real: {{ item.name }} + Name-Comment: {{ item.alias }} + Name-Email: {{ item.email }} + Expire-Date: {{ item.expiration }} + Passphrase: {{ item.password }} + %pubring pubring.kbx + %secring trustdb.gpg + %commit + %echo GPG key pair created + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + mode: "600" + create: true + backup: true + loop: "{{ gpg.keypairs }}" + loop_control: + index_var: idx + register: gpg_key_requests + - name: Creating OpenPGP / GPG key pair + ansible.builtin.command: + argv: + - gpg + - "--batch" + - "--gen-key" + - "/tmp/gpg-key{{ idx | string }}" + loop: "{{ gpg.keypairs }}" + loop_control: + index_var: idx + - name: Saving OpenPGP / GPG key pair passphrases + become: true + ansible.builtin.copy: + content: "{{ item.password }}" + dest: "/tmp/gpg-key-pass{{ idx | string }}" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + mode: "600" + force: true + backup: true + loop: "{{ gpg.keypairs }}" + loop_control: + index_var: idx + # @TODO add "show" (debug task module) option task, as counterpart to below fetch module task + - name: Acquiring the OpenPGP / GPG key pair passphrases + when: true + ansible.builtin.fetch: + src: "/tmp/gpg-key-pass{{ idx | string }}" + dest: "./.tmp/{{ inventory_hostname }}-openpgp/openpgp.pass{{ idx | string }}" + flat: true + loop: "{{ gpg.keypairs }}" + loop_control: + index_var: idx + - name: Installing requisite packages + ansible.builtin.include_role: + name: init-server + tasks_from: userspace@install-pkgs + handlers_from: userspace - name: Configuring aliases for using git when: "'git' in all_core_pkgs or 'git' in all_userspace_pkgs" community.general.git_config: @@ -71,8 +161,251 @@ scope: global value: "{{ item[1] }}" loop: "{{ git_aliases }}" + - name: Checking presence of dependency for SoulSeek CLI + when: "('nvm' in all_core_pkgs or 'nvm' in all_userspace_pkgs) and not soulseek.containerized" + block: + - name: Installing SoulSeek CLI + community.general.npm: + name: "soulseek-cli" + global: true + state: present + - name: Checking presence of dependency for GoCryptFS + # when: "'gocryptfs' in all_core_pkgs or 'gocryptfs' in all_userspace_pkgs" + when: false + block: + - name: Allowing users ability to allow others for FUSE mount + become: true + ansible.builtin.lineinfile: + path: /etc/fuse.conf + regexp: "^#user_allow_other" + insertafter: EOF + line: "user_allow_other" + - name: Creating prerequisite directories for GoCryptFS cipher configuration files + ansible.builtin.file: + path: "{{ ansible_user_home.stdout }}/.config/gocryptfs" + recurse: true + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + mode: "755" + state: directory + register: cipher_configs + - name: Creating prerequisite directories for GoCryptFS XXX cipher + ansible.builtin.file: + path: "{{ ansible_user_home.stdout }}/.ciphers/.xxx" + recurse: true + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + mode: "755" + state: directory + register: xxx_cipher + - name: Creating password for GoCryptFS XXX cipher + become: true + ansible.builtin.copy: + content: "{{ lookup('password', './.tmp/' + inventory_hostname + '-xxx@gocryptfs.pass', chars=['ascii_lowercase', 'digits'], length=32) }}" + dest: /xxx-pass + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + mode: "600" + force: true + backup: true + - name: Creating GoCryptFS XXX cipher + ansible.builtin.command: + argv: + - gocryptfs + - "--init" + - "--sharedstorage" + - "--passfile" + - /xxx-pass + - "{{ xxx_cipher.path }}" + register: xxx_master_key + - name: Creating file containing XXX master key + become: true + ansible.builtin.copy: + content: "{{ xxx_master_key.stdout }}" + dest: /tmp/xxx-master-key + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + mode: "600" + force: true + backup: true + - name: Copying GoCryptFS XXX cipher configuration file over to corresponding directory + ansible.builtin.copy: + src: "{{ ansible_user_home.stdout }}/.ciphers/.xxx/gocryptfs.conf" + remote_src: true + dest: "{{ ansible_user_home.stdout }}/.config/gocryptfs/xxx.conf" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + mode: "600" + force: true + backup: true + - name: Removing GoCryptFS XXX cipher configuration file + ansible.builtin.file: + path: "{{ ansible_user_home.stdout }}/.ciphers/.xxx/gocryptfs.conf" + state: absent + - name: Creating prerequisite directories for XXX downloads cipher + ansible.builtin.file: + path: "{{ ansible_user_home.stdout }}/.ciphers/downloads/.xxx" + recurse: true + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + mode: "755" + state: directory + register: xxx_downloads_cipher + - name: Creating password for XXX downloads cipher + become: true + ansible.builtin.copy: + content: "{{ lookup('password', './.tmp/' + inventory_hostname + '-downloads_xxx@gocryptfs.pass', chars=['ascii_lowercase', 'digits'], length=32) }}" + dest: /xxx-downloads-pass + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + mode: "600" + force: true + backup: true + - name: Creating GoCryptFS XXX downloads cipher + ansible.builtin.command: + argv: + - gocryptfs + - "--init" + - "--sharedstorage" + - "--passfile" + - /xxx-downloads-pass + - "{{ xxx_downloads_cipher.path }}" + register: xxx_downloads_master_key + - name: Creating file containing XXX downloads master key + become: true + ansible.builtin.copy: + content: "{{ xxx_downloads_master_key.stdout }}" + dest: /tmp/xxx-downloads-master-key + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + mode: "600" + force: true + backup: true + - name: Copying GoCryptFS XXX downloads cipher configuration file over to corresponding directory + ansible.builtin.copy: + src: "{{ ansible_user_home.stdout }}/.ciphers/downloads/.xxx/gocryptfs.conf" + remote_src: true + dest: "{{ ansible_user_home.stdout }}/.config/gocryptfs/downloads-xxx.conf" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + mode: "600" + force: true + backup: true + - name: Removing GoCryptFS XXX downloads cipher configuration file + ansible.builtin.file: + path: "{{ ansible_user_home.stdout }}/.ciphers/downloads/.xxx/gocryptfs.conf" + state: absent + - name: Pausing to inquire about how to proceed + ansible.builtin.pause: + prompt: "Type \"fetch\" to get the cipher master keys, or \"show\" to see contents for manual copying instead" + echo: true + register: data_method + - name: Providing XXX cipher master key to control node machine + when: data_method.user_input == "fetch" + ansible.builtin.fetch: + src: /tmp/xxx-master-key + dest: "./.tmp/{{ inventory_hostname }}-xxx@gocryptfs.key" + flat: true + - name: Providing XXX downloads cipher master key to control node machine + when: data_method.user_input == "fetch" + ansible.builtin.fetch: + src: /tmp/xxx-downloads-master-key + dest: "./.tmp/{{ inventory_hostname }}-downloads_xxx@gocryptfs.key" + flat: true + - name: Presenting XXX cipher master key to control node machine + when: data_method.user_input == "show" + ansible.builtin.debug: + msg: "{{ xxx_master_key.stdout }}" + - name: Presenting XXX downloads cipher master key to control node machine + when: data_method.user_input == "show" + ansible.builtin.debug: + msg: "{{ xxx_downloads_master_key.stdout }}" + - name: Giving opportunity to manually copy contents of GoCryptFS cipher master keys + when: data_method.user_input == "show" + ansible.builtin.pause: + - name: Creating requisite hidden XXX directory for NSFW video torrent links file + ansible.builtin.file: + path: "{{ ansible_user_home.stdout }}/.xxx" + recurse: true + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + state: directory + mode: "755" + # - name: Mounting GoCryptFS XXX cipher + # ansible.builtin.command: + # argv: + # - gocryptfs + # - "--rw" + # - "--allow_other" + # - "--config" + # - "{{ ansible_user_home.stdout }}/.config/gocryptfs/xxx.conf" + # - "--passfile" + # - /xxx-pass + # - "{{ xxx_cipher.path }}" + # - "{{ ansible_user_home.stdout }}/.xxx" + - name: Mounting GoCryptFS XXX cipher + ansible.posix.mount: + path: "{{ ansible_user_home.stdout }}/.xxx" + src: "{{ xxx_cipher.path }}" + fstype: fuse./usr/bin/gocryptfs + opts: "rw,allow_other,quiet,config={{ ansible_user_home.stdout }}/.config/gocryptfs/xxx.conf,passfile=/xxx-pass" + dump: "0" + passno: "0" + state: mounted + - name: Creating requisite hidden file for NSFW video torrent links + ansible.builtin.file: + path: "{{ ansible_user_home.stdout }}/.xxx/.vids.csv" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + state: touch + mode: "644" + - name: Creating requisite hidden XXX download paths + ansible.builtin.file: + path: "{{ ansible_user_home.stdout }}/downloads/.xxx" + recurse: true + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + state: directory + mode: "755" + # - name: Mounting GoCryptFS XXX downloads cipher + # ansible.builtin.command: + # argv: + # - gocryptfs + # - "--rw" + # - "--allow_other" + # - "--config" + # - "{{ ansible_user_home.stdout }}/.config/gocryptfs/downloads-xxx.conf" + # - "--passfile" + # - /xxx-downloads-pass + # - "{{ xxx_downloads_cipher.path }}" + # - "{{ ansible_user_home.stdout }}/downloads/.xxx" + - name: Mounting GoCryptFS XXX downloads cipher + ansible.posix.mount: + path: "{{ ansible_user_home.stdout }}/downloads/.xxx" + src: "{{ xxx_downloads_cipher.path }}" + fstype: fuse./usr/bin/gocryptfs + opts: "rw,allow_other,quiet,config={{ ansible_user_home.stdout }}/.config/gocryptfs/downloads-xxx.conf,passfile=/xxx-downloads-pass" + dump: "0" + passno: "0" + state: mounted + - name: Creating requisite hidden download paths + ansible.builtin.file: + path: "{{ ansible_user_home.stdout }}/downloads/.xxx/flexget/vids/torrents" + recurse: true + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + state: directory + mode: "755" + - name: Creating requisite hidden download paths + ansible.builtin.file: + path: "{{ ansible_user_home.stdout }}/downloads/.xxx/media/vids" + recurse: true + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + state: directory + mode: "755" - name: Checking presence of dependency for flexget - when: "'uv' in all_core_pkgs and flexget.enabled" + when: "('uv' in all_core_pkgs or 'uv' in all_userspace_pkgs) and flexget.enabled and not flexget.containerized" block: - name: Installing flexget become: true @@ -87,7 +420,7 @@ - pysocks - name: Creating requisite download paths ansible.builtin.file: - path: "{{ ansible_user_home.stdout }}/downloads/flexget/vids/{{ item }}/torrents" + path: "{{ ansible_user_home.stdout }}/media/vids/{{ item }}" recurse: true owner: "{{ ansible_user }}" group: "{{ ansible_user }}" @@ -95,17 +428,18 @@ mode: "755" loop: - series + - features - name: Creating requisite download paths ansible.builtin.file: - path: "{{ ansible_user_home.stdout }}/downloads/flexget/vids/torrents" + path: "{{ ansible_user_home.stdout }}/downloads/flexget/vids/series/torrents" recurse: true owner: "{{ ansible_user }}" group: "{{ ansible_user }}" state: directory mode: "755" - - name: Creating requisite hidden download paths + - name: Creating requisite download paths ansible.builtin.file: - path: "{{ ansible_user_home.stdout }}/downloads/.xxx/flexget/vids/torrents" + path: "{{ ansible_user_home.stdout }}/downloads/flexget/vids/torrents" recurse: true owner: "{{ ansible_user }}" group: "{{ ansible_user }}" @@ -124,35 +458,32 @@ - features - name: Creating requisite hidden file for film torrent links ansible.builtin.file: - path: "{{ ansible_user_home.stdout }}/media/vids/{{ item }}.csv" + path: "{{ ansible_user_home.stdout }}/downloads/media/vids/features/.films.csv" owner: "{{ ansible_user }}" group: "{{ ansible_user }}" state: touch mode: "644" - loop: - - features/.films - - name: Creating requisite hidden download paths + - name: Creating requisite directory for NSFW video torrent links file ansible.builtin.file: - path: "{{ ansible_user_home.stdout }}/downloads/.xxx/media/vids" + path: "{{ ansible_user_home.stdout }}/.xxx" recurse: true owner: "{{ ansible_user }}" group: "{{ ansible_user }}" state: directory mode: "755" - - name: Creating requisite hidden file for NSFW video torrent links + - name: Creating requisite directory for flexget ansible.builtin.file: - path: "{{ ansible_user_home.stdout }}/.xxx/.{{ item }}.csv" + path: "{{ ansible_user_home.stdout }}/.flexget" + recurse: true owner: "{{ ansible_user }}" group: "{{ ansible_user }}" - state: touch - mode: "644" - loop: - - vids + state: directory + mode: "755" - name: Configuring flexget become: true become_user: "{{ current_user.stdout }}" ansible.builtin.template: - src: user/flexget/config.yml.j2 + src: roles/init-server/templates/user/flexget/config.yml.j2 variable_start_string: "<<" variable_end_string: ">>" dest: "{{ ansible_user_home.stdout }}/.flexget/config.yml" @@ -168,47 +499,20 @@ hour: "4-15" weekday: "1-5" job: "{{ ansible_user_home.stdout }}/.local/bin/flexget --cron execute" - - name: Checking presence of dependency - when: "'nvm' in all_core_pkgs or 'nvm' in all_userspace_pkgs" - block: - - name: Installing SoulSeek CLI - community.general.npm: - name: "soulseek-cli" - global: true - state: present - - name: Opening port 51820 + - name: Configure installed packages + ansible.builtin.meta: flush_handlers + - name: Disabling root user shell login become: true - ansible.builtin.iptables: - chain: INPUT - protocol: "{{ item }}" - destination_port: 51820 - jump: ACCEPT - comment: Open up port 51820 - loop: - - udp - - tcp - - name: Opening port 443 + ansible.builtin.user: + name: root + shell: /sbin/nologin + tags: + - disable_root_shell + - name: Disable login for root user altogether become: true - ansible.builtin.iptables: - chain: INPUT - protocol: "{{ item }}" - destination_port: 443 - jump: ACCEPT - comment: Open up port 443 - loop: - - udp - - tcp - - name: Opening ports - become: true - ansible.builtin.iptables: - chain: INPUT - protocol: tcp - destination_port: "{{ item }}" - jump: ACCEPT - comment: "Open up port {{ (item | string) }}" - loop: - - 80 - - 465 - - 587 - - 995 - - 993 \ No newline at end of file + ansible.builtin.user: + name: root + password: "'*'" + tags: + - disable_root_login + diff --git a/administrate@vps.yml b/administrate@vps.yml index 4fabb76..9dcb028 100644 --- a/administrate@vps.yml +++ b/administrate@vps.yml @@ -2,9 +2,10 @@ hosts: "{{ chosen_host | default('staging0.test') }}" remote_user: "{{ chosen_user | default('senpai') }}" vars_files: - - vars/surge_settings.yml - vars/git_aliases.yml # REQUIRED - vars/vim_settings.yml # REQUIRED + - vars/iptables_settings@vps.yml # REQUIRED + - vars/surge_settings.yml - vars/podpose_settings.yml # REQUIRED - vars/certbot_settings.yml # REQUIRED - vars/config@{{ inventory_hostname | default('vps') }}.yml @@ -14,6 +15,8 @@ name: init-server tasks_from: userspace@install-pkgs handlers_from: userspace + - name: Configure installed packages + ansible.builtin.meta: flush_handlers - name: Disabling root user shell login become: true ansible.builtin.user: @@ -35,10 +38,10 @@ # ansible.builtin.meta: end_play - name: Reorganizing userspace package groups into single list ansible.builtin.set_fact: - all_userspace_pkgs: "{{ pkgs | dict2items(key_name='pkg_group', value_name='pkgs') | map(attribute='pkgs') | list | map(attribute='userspace', default='no_userspace') | list | flatten | reject('search', 'no_userspace') | list }}" + all_userspace_pkgs: "{{ pkgs | dict2items(key_name='pkg_group', value_name='pkgs') | map(attribute='pkgs') | list | map(attribute='userspace', default='no_userspace') | list | flatten | reject('search', 'no_userspace') | map(attribute='name') | list }}" - name: Reorganizing core package groups into single list ansible.builtin.set_fact: - all_core_pkgs: "{{ pkgs | dict2items(key_name='pkg_group', value_name='pkgs') | map(attribute='pkgs') | list | map(attribute='core', default='no_core') | list | flatten | reject('search', 'no_core') | list }}" + all_core_pkgs: "{{ pkgs | dict2items(key_name='pkg_group', value_name='pkgs') | map(attribute='pkgs') | list | map(attribute='core', default='no_core') | list | flatten | reject('search', 'no_core') | map(attribute='name') | list }}" - name: Configuring aliases for using git when: "'git' in all_core_pkgs" community.general.git_config: @@ -46,39 +49,3 @@ scope: global value: "{{ item[1] }}" loop: "{{ git_aliases }}" - - name: Opening port 51820 - become: true - ansible.builtin.iptables: - chain: INPUT - protocol: "{{ item }}" - destination_port: 51820 - jump: ACCEPT - comment: Open up port 51820 - loop: - - udp - - tcp - - name: Opening port 443 - become: true - ansible.builtin.iptables: - chain: INPUT - protocol: "{{ item }}" - destination_port: 443 - jump: ACCEPT - comment: Open up port 443 - loop: - - udp - - tcp - - name: Opening ports - become: true - ansible.builtin.iptables: - chain: INPUT - protocol: tcp - destination_port: "{{ item }}" - jump: ACCEPT - comment: "Open up port {{ (item | string) }}" - loop: - - 80 - - 465 - - 587 - - 995 - - 993 diff --git a/host_vars/staging0.test.yml b/host_vars/staging0.test.yml index 5b06769..8478344 100644 --- a/host_vars/staging0.test.yml +++ b/host_vars/staging0.test.yml @@ -1,3 +1,4 @@ +ipv6_enabled: false # > representing password for Linux root user account of VPS password: "{{ lookup('password', './.tmp/' + inventory_hostname + '.pass', seed=inventory_hostname, encrypt='sha512_crypt') }}" # > representing API token for VPS cloud service @@ -147,7 +148,7 @@ pkgs: suites: ~ comps: ~ handler: default - - name: libvirt + - name: libvirt0 uri: ~ sigkey: ~ sources: ~ @@ -260,14 +261,6 @@ pkgs: comps: ~ handler: default # @TODO make sure to create handler inclusion of tasks in file under/at `tasks/contingent/pkg` for below - - name: iptables - uri: ~ - sigkey: ~ - sources: ~ - types: ~ - suites: ~ - comps: ~ - handler: default - name: gnupg uri: ~ sigkey: ~ @@ -316,7 +309,23 @@ pkgs: suites: ~ comps: ~ handler: headscale + - name: haveged + uri: ~ + sigkey: ~ + sources: ~ + types: ~ + suites: ~ + comps: ~ + handler: default userspace: + - name: iptables + uri: ~ + sigkey: ~ + sources: ~ + types: ~ + suites: ~ + comps: ~ + handler: iptables - name: "libpam-google-authenticator" uri: ~ sigkey: ~ diff --git a/host_vars/staging1.test.yml b/host_vars/staging1.test.yml index 57738d3..7a4d32a 100644 --- a/host_vars/staging1.test.yml +++ b/host_vars/staging1.test.yml @@ -1,4 +1,5 @@ # @TODO create inventory group variables akin to structure of sukaato group's for homeserver +ipv6_enabled: false # > representing password for Linux root user account of VPS password: "{{ lookup('password', './.tmp/' + inventory_hostname + '.pass', seed=inventory_hostname, encrypt='sha512_crypt') }}" # > representing Linux distro or OS image to be used for VPS @@ -121,7 +122,7 @@ pkgs: suites: ~ comps: ~ handler: default - - name: libvirt + - name: libvirt0 uri: ~ sigkey: ~ sources: ~ @@ -289,7 +290,7 @@ pkgs: suites: ~ comps: ~ handler: default - - name: dpkg-dev + - name: "dpkg-dev" uri: ~ sigkey: ~ sources: ~ @@ -304,8 +305,8 @@ pkgs: types: ~ suites: ~ comps: ~ - handler: default - - name: avahi-daemon + handler: ufw + - name: "avahi-daemon" uri: ~ sigkey: ~ sources: ~ @@ -313,7 +314,7 @@ pkgs: suites: ~ comps: ~ handler: default - - name: avahi-utils + - name: "avahi-utils" uri: ~ sigkey: ~ sources: ~ @@ -321,7 +322,7 @@ pkgs: suites: ~ comps: ~ handler: default - - name: libpam-u2f + - name: "libpam-u2f" uri: ~ sigkey: ~ sources: ~ @@ -345,7 +346,7 @@ pkgs: suites: ~ comps: ~ handler: default - - name: tftpd-hpa + - name: "tftpd-hpa" uri: ~ sigkey: ~ sources: ~ @@ -418,14 +419,14 @@ pkgs: comps: ~ handler: default userspace: - - name: aria2 - uri: ~ - sigkey: ~ - sources: ~ - types: ~ - suites: ~ - comps: ~ - handler: aria + # - name: aria2 + # uri: ~ + # sigkey: ~ + # sources: ~ + # types: ~ + # suites: ~ + # comps: ~ + # handler: aria - name: "libpam-google-authenticator" uri: ~ sigkey: ~ @@ -666,6 +667,14 @@ pkgs: suites: ~ comps: ~ handler: default + - name: haveged + uri: ~ + sigkey: ~ + sources: ~ + types: ~ + suites: ~ + comps: ~ + handler: default # :]> representing package groups installed by shell scripts script: # ]> representing user-level or supplemental shell script installations @@ -694,12 +703,12 @@ pkgs: archive: # ]> representing user-level or supplemental source archives userspace: - - name: surge - src: "https://github.com/SurgeDM/Surge/releases/download/v0.8.6/Surge_0.8.6_linux_amd64.tar.gz" - deploy: ~ - pre: ~ - post: ~ - handler: surge + # - name: surge + # src: "https://github.com/SurgeDM/Surge/releases/download/v0.8.6/Surge_0.8.6_linux_amd64.tar.gz" + # deploy: ~ + # pre: ~ + # post: ~ + # handler: surge - name: nvm src: "https://nodejs.org/dist/v24.16.0/node-v24.16.0-linux-x64.tar.xz" deploy: ~ @@ -761,8 +770,8 @@ pkgs: handler: sukaato - name: quartz src: "https://github.com/jackyzha0/quartz.git" - branch: v5.0.0 + branch: v5 deploy: ~ pre: ~ post: ~ - handler: quartz + handler: default diff --git a/init@homeserver.yml b/init@homeserver.yml index 8d0f531..d9bb968 100644 --- a/init@homeserver.yml +++ b/init@homeserver.yml @@ -9,10 +9,33 @@ - vars/local_facts.yml # REQUIRED - vars/wireguard_settings.yml # REQUIRED - vars/podpose_settings.yml # REQUIRED + - vars/iptables_settings@homeserver.yml - vars/surge_settings.yml - vars/users@{{ inventory_hostname | default('homeserver') }}.yml - vars/config@{{ inventory_hostname | default('homeserver') }}.yml tasks: + - name: Managing hostname + block: + - name: Updating hostname + become: true + ansible.builtin.hostname: + name: "{{ fqdn | default(inventory_hostname) }}" + - name: Updating hosts file + become: true + ansible.builtin.lineinfile: + path: /etc/hosts + regexp: "^127\\.0\\.1\\.1" + line: "127.0.1.1 {{ fqdn | default(inventory_hostname) }}" + insertbefore: BOF + state: present + - name: Updating host icon name + become: true + ansible.builtin.command: + cmd: "hostnamectl set-icon-name computer-server" + - name: Rebooting machine for hostname change + become: true + ansible.builtin.reboot: + msg: "Rebooting machine.." - name: Hardening SSH server ansible.builtin.include_role: name: init-server # required. The name of the role to be executed. @@ -31,31 +54,5 @@ ansible.builtin.include_role: name: init-server tasks_from: ssh-users - - name: Updating hostname - become: true - ansible.builtin.hostname: - name: "{{ fqdn | default(inventory_hostname) }}" - - name: Updating hosts file - become: true - ansible.builtin.lineinfile: - path: /etc/hosts - regexp: "^127\\.0\\.1\\.1" - line: "127.0.1.1 {{ fqdn | default(inventory_hostname) }}" - insertbefore: BOF - state: present - - name: Updating host icon name - become: true - ansible.builtin.command: - cmd: "hostnamectl set-icon-name computer-server" - - name: Notifying user that all processes have finished - ansible.builtin.debug: - msg: All processes finished. Hit enter to reboot machine. - - name: Ensuring user has read prior message regarding upcoming reboot - ansible.builtin.pause: - - name: Rebooting machine for hostname change - become: true - ansible.builtin.reboot: - msg: "Rebooting machine.." - connect_timeout: 0 - test_command: ~ - ignore_errors: true \ No newline at end of file + - name: Flushing handlers + ansible.builtin.meta: flush_handlers \ No newline at end of file diff --git a/init@vps.yml b/init@vps.yml index b63ef7b..2d42852 100644 --- a/init@vps.yml +++ b/init@vps.yml @@ -14,6 +14,28 @@ - vars/users@{{ inventory_hostname | default('vps') }}.yml - vars/config@{{ inventory_hostname | default('vps') }}.yml tasks: + - name: Managing hostname + block: + - name: Updating hostname + become: true + ansible.builtin.hostname: + name: "{{ fqdn | default(inventory_hostname) }}" + - name: Updating hosts file + become: true + ansible.builtin.lineinfile: + path: /etc/hosts + regexp: "^127\\.0\\.1\\.1" + line: "127.0.1.1 {{ fqdn | default(inventory_hostname) }}" + insertbefore: BOF + state: present + - name: Updating host icon name + become: true + ansible.builtin.command: + cmd: "hostnamectl set-icon-name computer-server" + - name: Rebooting machine for hostname change + become: true + ansible.builtin.reboot: + msg: "Rebooting machine.." - name: Hardening SSH server ansible.builtin.include_role: name: init-server # required. The name of the role to be executed. @@ -33,67 +55,4 @@ name: init-server tasks_from: ssh-users - name: Flushing handlers - ansible.builtin.meta: flush_handlers - - name: Updating hostname - become: true - ansible.builtin.hostname: - name: "{{ fqdn | default(inventory_hostname) }}" - - name: Updating hosts file - become: true - ansible.builtin.lineinfile: - path: /etc/hosts - regexp: "^127\\.0\\.1\\.1" - line: "127.0.1.1 {{ fqdn | default(inventory_hostname) }}" - insertbefore: BOF - state: present - - name: Updating host icon name - become: true - ansible.builtin.command: - cmd: "hostnamectl set-icon-name computer-server" - - name: Opening port 51820 - become: true - ansible.builtin.iptables: - chain: INPUT - protocol: "{{ item }}" - destination_port: 51820 - jump: ACCEPT - comment: Open up port 51820 - loop: - - udp - - tcp - - name: Opening port 443 - become: true - ansible.builtin.iptables: - chain: INPUT - protocol: "{{ item }}" - destination_port: 443 - jump: ACCEPT - comment: Open up port 443 - loop: - - udp - - tcp - - name: Opening ports - become: true - ansible.builtin.iptables: - chain: INPUT - protocol: tcp - destination_port: "{{ item }}" - jump: ACCEPT - comment: "Open up port {{ (item | string) }}" - loop: - - 80 - - 465 - - 587 - - 995 - - 993 - - name: Notifying user that all processes have finished - ansible.builtin.debug: - msg: All processes finished. Hit enter to reboot machine. - - name: Ensuring user has read prior message regarding upcoming reboot - ansible.builtin.pause: - - name: Rebooting machine for hostname change - become: true - ansible.builtin.reboot: - msg: "Rebooting machine.." - connect_timeout: 0 - test_command: ~ \ No newline at end of file + ansible.builtin.meta: flush_handlers \ No newline at end of file diff --git a/roles/init-server/files/systemd/system/thrunet.service b/roles/init-server/files/systemd/system/thrunet.service index 71d7dc2..558b4c2 100644 --- a/roles/init-server/files/systemd/system/thrunet.service +++ b/roles/init-server/files/systemd/system/thrunet.service @@ -5,8 +5,8 @@ Wants=network-online.target [Service] Type=oneshot -ExecStart=/usr/local/bin/dsnet-forward.sh start -ExecStop=/usr/local/bin/dsnet-forward.sh stop +ExecStart=/usr/local/bin/dsnet-routing.sh up +ExecStop=/usr/local/bin/dsnet-routing.sh down RemainAfterExit=yes # ExecReload=/usr/bin/dsnet sync diff --git a/roles/init-server/handlers/core.yml b/roles/init-server/handlers/core.yml index 5195c75..46d7ca5 100644 --- a/roles/init-server/handlers/core.yml +++ b/roles/init-server/handlers/core.yml @@ -9,6 +9,26 @@ ansible.builtin.include_tasks: file: tasks/contingent/pkg/git.yml listen: git +- name: Setting up TOTP + ansible.builtin.include_tasks: + file: tasks/contingent/pkg/libpam-google-authenticator.yml + listen: "libpam-google-authenticator" +- name: Setting up IPTables + ansible.builtin.include_tasks: + file: tasks/contingent/pkg/iptables.yml + listen: iptables +- name: Setting up IPTables + ansible.builtin.include_tasks: + file: tasks/contingent/pkg/ufw.yml + listen: ufw +- name: Setting up Podman + ansible.builtin.include_tasks: + file: tasks/contingent/pkg/podman.yml + listen: podman +- name: Setting up Avahi + ansible.builtin.include_tasks: + file: tasks/contingent/pkg/avahi-daemon.yml + listen: "avahi-daemon" - name: Setting up Certbot ansible.builtin.include_tasks: file: tasks/contingent/pkg/certbot.yml diff --git a/roles/init-server/handlers/userspace.yml b/roles/init-server/handlers/userspace.yml index 1c4d819..eb81bad 100644 --- a/roles/init-server/handlers/userspace.yml +++ b/roles/init-server/handlers/userspace.yml @@ -37,18 +37,46 @@ ansible.builtin.include_tasks: file: tasks/contingent/pkg/uv.yml listen: uv +- name: Setting up TOTP + ansible.builtin.include_tasks: + file: tasks/contingent/pkg/libpam-google-authenticator.yml + listen: libpam-google-authenticator +- name: Setting up IPTables + ansible.builtin.include_tasks: + file: tasks/contingent/pkg/iptables.yml + listen: iptables +- name: Setting up UFW + ansible.builtin.include_tasks: + file: tasks/contingent/pkg/ufw.yml + listen: ufw +- name: Setting up Certbot + ansible.builtin.include_tasks: + file: tasks/contingent/pkg/certbot.yml + listen: certbot +- name: Setting up Surge + ansible.builtin.include_tasks: + file: tasks/contingent/pkg/surge.yml + listen: surge +- name: Setting up Aria2 + ansible.builtin.include_tasks: + file: tasks/contingent/pkg/aria.yml + listen: aria +- name: Setting up Radicle + ansible.builtin.include_tasks: + file: tasks/contingent/pkg/radicle.yml + listen: radicle - name: Setting up Podman ansible.builtin.include_tasks: file: tasks/contingent/pkg/podman.yml listen: podman -- name: Setting up Podman Compose sources - ansible.builtin.include_tasks: - file: tasks/contingent/pkg/skato_compose.yml - listen: skato_compose - name: Setting up Quartz ansible.builtin.include_tasks: file: tasks/contingent/pkg/quartz.yml listen: quartz +- name: Setting up Podman Compose sources + ansible.builtin.include_tasks: + file: tasks/contingent/pkg/skato_compose.yml + listen: skato_compose - name: Setting up native CLI tools sources ansible.builtin.include_tasks: file: tasks/contingent/pkg/skato_cli.yml @@ -65,19 +93,3 @@ ansible.builtin.include_tasks: file: tasks/contingent/pkg/sukaato.yml listen: sukaato -- name: Setting up Surge - ansible.builtin.include_tasks: - file: tasks/contingent/pkg/surge.yml - listen: surge -- name: Setting up Aria2 - ansible.builtin.include_tasks: - file: tasks/contingent/pkg/aria.yml - listen: aria -- name: Setting up Radicle - ansible.builtin.include_tasks: - file: tasks/contingent/pkg/radicle.yml - listen: radicle -- name: Setting up TOTP - ansible.builtin.include_tasks: - file: tasks/contingent/pkg/libpam-google-authenticator.yml - listen: libpam-google-authenticator diff --git a/roles/init-server/tasks/contingent/pkg/avahi-daemon.yml b/roles/init-server/tasks/contingent/pkg/avahi-daemon.yml new file mode 100644 index 0000000..ae6455a --- /dev/null +++ b/roles/init-server/tasks/contingent/pkg/avahi-daemon.yml @@ -0,0 +1,7 @@ +- name: Starting and enabling SystemD service + become: true + ansible.builtin.systemd_service: + name: "avahi-daemon" + scope: system + enabled: true + state: started \ No newline at end of file diff --git a/roles/init-server/tasks/contingent/pkg/certbot.yml b/roles/init-server/tasks/contingent/pkg/certbot.yml index c47cda0..8a0d29d 100644 --- a/roles/init-server/tasks/contingent/pkg/certbot.yml +++ b/roles/init-server/tasks/contingent/pkg/certbot.yml @@ -18,22 +18,19 @@ - name: Creating domain arguments for certbot ansible.builtin.set_fact: certbot_domains: "{{ ['-d'] | product(web_fqdns) | map('join', '=') | list }}" -- name: Getting public IP +- name: Getting public IPv4 address community.general.ipify_facts: validate_certs: false timeout: 20 +- name: Getting public IPv6 address + when: ipv6_enabled + ansible.builtin.shell: + cmd: curl -6 icanhazip.com + register: ipify_public_ip6 - name: Starting domain name registration with standalone DNS when: "certbot.auth_method == 'dns' and not certbot.containerized" ignore_errors: true block: - - name: Opening port 53 - become: true - ansible.builtin.iptables: - chain: INPUT - protocol: tcp - destination_port: 53 - jump: ACCEPT - comment: Opening up port 53 # @NOTE https://github.com/siilike/certbot-dns-standalone - name: Exemplifying needed ACME record ansible.builtin.set_fact: @@ -42,6 +39,14 @@ _acme-challenge.{{ fqdn | default(inventory_hostname) }} IN CNAME {{ fqdn | default(inventory_hostname) }}.acme.{{ fqdn | default(inventory_hostname) }}. acme.{{ fqdn | default(inventory_hostname) }} IN NS ns.acme.{{ fqdn | default(inventory_hostname) }}. ns.acme.{{ fqdn | default(inventory_hostname) }} IN A {{ ipify_public_ip }} + - name: Exemplifying needed ACME record + when: ipv6_enabled + ansible.builtin.set_fact: + acme_record: | + {{ fqdn | default(inventory_hostname) }} IN A {{ ipify_public_ip6.stdout }} + _acme-challenge.{{ fqdn | default(inventory_hostname) }} IN CNAME {{ fqdn | default(inventory_hostname) }}.acme.{{ fqdn | default(inventory_hostname) }}. + acme.{{ fqdn | default(inventory_hostname) }} IN NS ns.acme.{{ fqdn | default(inventory_hostname) }}. + ns.acme.{{ fqdn | default(inventory_hostname) }} IN A {{ ipify_public_ip6.stdout }} - name: Informing user of need to set up ACME record ansible.builtin.debug: msg: "Please set ACME record in domain name provider:\n {{ acme_record }}" @@ -49,7 +54,7 @@ ansible.builtin.pause: - name: Running certbot to authenticate and acquire domain name certificates become: true - when: "certbot.mode == 'dev'" + when: "certbot.mode == 'dev' and not ipv6_enabled" ansible.builtin.command: argv: - certbot @@ -61,13 +66,30 @@ - "--agree-tos" - "--non-interactive" - "--dns-standalone-address={{ ipify_public_ip }}" - # - "--dns-standalone-ipv6-address={{ ansible_default_ipv6.address | default(ansible_all_ipv6_addresses[0]) }}" - "--dns-standalone-port={{ certbot.port }}" - "{{ certbot_domains[0] }}" - "{{ certbot_domains[1] }}" - name: Running certbot to authenticate and acquire domain name certificates become: true - when: "certbot.mode == 'prod'" + when: "certbot.mode == 'dev' and ipv6_enabled" + ansible.builtin.command: + argv: + - certbot + - certonly + - "--staging" + - "--debug" + - "--authenticator=dns-standalone" + - "--email={{ certbot.email }}" + - "--agree-tos" + - "--non-interactive" + - "--dns-standalone-address={{ ipify_public_ip }}" + - "--dns-standalone-ipv6-address={{ ipify_public_ip6.stdout }}" + - "--dns-standalone-port={{ certbot.port }}" + - "{{ certbot_domains[0] }}" + - "{{ certbot_domains[1] }}" + - name: Running certbot to authenticate and acquire domain name certificates + become: true + when: "certbot.mode == 'prod' and not ipv6_enabled" ansible.builtin.command: argv: - certbot @@ -77,7 +99,22 @@ - "--agree-tos" - "--non-interactive" - "--dns-standalone-address={{ ipify_public_ip }}" - # - "--dns-standalone-ipv6-address={{ ansible_default_ipv6.address | default(ansible_all_ipv6_addresses[0]) }}" + - "--dns-standalone-port={{ certbot.port }}" + - "{{ certbot_domains[0] }}" + - "{{ certbot_domains[1] }}" + - name: Running certbot to authenticate and acquire domain name certificates + become: true + when: "certbot.mode == 'prod' and ipv6_enabled" + ansible.builtin.command: + argv: + - certbot + - certonly + - "--authenticator=dns-standalone" + - "--email={{ certbot.email }}" + - "--agree-tos" + - "--non-interactive" + - "--dns-standalone-address={{ ipify_public_ip }}" + - "--dns-standalone-ipv6-address={{ ipify_public_ip6.stdout }}" - "--dns-standalone-port={{ certbot.port }}" - "{{ certbot_domains[0] }}" - "{{ certbot_domains[1] }}" @@ -85,14 +122,6 @@ when: "certbot.auth_method == 'standalone' and not certbot.containerized" ignore_errors: true block: - - name: Opening port 80 - become: true - ansible.builtin.iptables: - chain: INPUT - protocol: tcp - destination_port: 80 - jump: ACCEPT - comment: Open up port 80 - name: Acquiring domain certificates become: true when: "certbot.mode == 'dev'" diff --git a/roles/init-server/tasks/contingent/pkg/crowdsec.yml b/roles/init-server/tasks/contingent/pkg/crowdsec.yml index 2f60651..73272ad 100644 --- a/roles/init-server/tasks/contingent/pkg/crowdsec.yml +++ b/roles/init-server/tasks/contingent/pkg/crowdsec.yml @@ -37,6 +37,7 @@ name: crowdsec scope: system state: restarted + enabled: true - name: Adding remediation component or bouncer ansible.builtin.command: cmd: "cscli bouncers add {{ item }}" diff --git a/roles/init-server/tasks/contingent/pkg/dsnet.yml b/roles/init-server/tasks/contingent/pkg/dsnet.yml index a3b53f7..b0dcc45 100644 --- a/roles/init-server/tasks/contingent/pkg/dsnet.yml +++ b/roles/init-server/tasks/contingent/pkg/dsnet.yml @@ -8,6 +8,32 @@ ansible.builtin.shell: cmd: "echo {{ ansible_user }}" register: current_user +- name: Checking if ZFS filesystem is present + ansible.builtin.shell: + cmd: "findmnt -D | awk '{ print $2 }' | tail -n +2" + register: fs_result +- name: Logging presence of ZFS filesystem + when: "'zfs' in fs_result.stdout" + ansible.builtin.set_fact: + zfs_present: true +- name: Logging absence of ZFS filesystem + when: "'zfs' not in fs_result.stdout" + ansible.builtin.set_fact: + zfs_present: false +- name: Configuring ZFS support + when: zfs_present + block: + - name: Getting available pools + ansible.builtin.shell: + cmd: "zpool list | awk '{ print $1 }' | tail -n +2" + register: zpools + - name: Turning available pools into list + ansible.builtin.set_fact: + zpool_list: "{{ zpools | replace('\n', ' ') | split(' ') }}" + - name: Allowing choice of zpool + ansible.builtin.pause: + prompt: "Type preferred zpool from available (i.e., {{ zpool_list | join(', ') }})" + register: preferred_zpool - name: Initializing DSNet become: true ansible.builtin.command: @@ -16,16 +42,35 @@ become: true ansible.builtin.command: cmd: dsnet up +- name: Creating a ZFS dataset for DSNet configuration + when: zfs_present + become: true + community.general.zfs: + name: "{{ preferred_zpool.user_input }}/wireguard_clients" + state: present + extra_zfs_properties: + canmount: "on" + mountpoint: "{{ ansible_user_home.stdout }}/.wg" +- name: Creating a directory to house VPN service client configurations + become: true + ansible.builtin.file: + path: "{{ ansible_user_home.stdout }}/.wg" + recurse: true + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + mode: "700" + state: directory - name: Creating a directory to house VPN service client configurations ansible.builtin.file: path: "{{ ansible_user_home.stdout }}/.wg/authorized_clients.d" recurse: true owner: "{{ ansible_user }}" group: "{{ ansible_user }}" - mode: "755" + mode: "700" state: directory - name: Adding peer device for DSNet VPN service interface become: true + when: "((fqdn is defined and fqdn != None) and fqdn in item.name) or (item.name in groups.homeserver or item.name in groups['homeserver.test'])" ansible.builtin.shell: cmd: "dsnet add {{ item.name }}{{ idx }} --owner {{ current_user.stdout }} --description 'For {{ item.name }}--{{ item.desc }}' --confirm > {{ ansible_user_home.stdout }}/.wg/authorized_clients.d/{{ item.name }}{{ (idx | string) }}.conf" creates: "{{ ansible_user_home.stdout }}/.wg/authorized_clients.d/{{ item.name }}{{ idx }}.conf" @@ -33,6 +78,7 @@ loop_control: index_var: idx - name: Changing ownership of consequent DSNet VPN service client configurations + when: "((fqdn is defined and fqdn != None) and fqdn in item.name) or (item.name in groups.homeserver or item.name in groups['homeserver.test'])" ansible.builtin.file: path: "{{ ansible_user_home.stdout }}/.wg/authorized_clients.d/{{ item.name }}{{ (idx | string) }}.conf" owner: "{{ ansible_user }}" @@ -42,6 +88,39 @@ loop: "{{ vpn.clients }}" loop_control: index_var: idx +- name: Acquiring VPN server subnet + block: + - name: Acquiring VPN server IPv4 subnet + ansible.builtin.shell: + cmd: echo "$(" + owner: root + group: root + mode: "755" + force: true + backup: true +- name: Copying script for DSNet iptables rules + become: true + when: not ipv6_enabled + ansible.builtin.template: + src: usr/local/bin/dsnet-routing.sh.j2 + dest: /usr/local/bin/dsnet-routing.sh + variable_start_string: "<%" + variable_end_string: "%>" owner: root group: root mode: "755" @@ -144,16 +245,5 @@ - name: Reloading SystemD become: true ansible.builtin.systemd_service: - name: thrunet + scope: system daemon_reload: true -- name: Opening port 51820 - become: true - ansible.builtin.iptables: - chain: INPUT - protocol: "{{ item }}" - destination_port: 51820 - jump: ACCEPT - comment: Open up port 51820 - loop: - - udp - - tcp diff --git a/roles/init-server/tasks/contingent/pkg/headscale.yml b/roles/init-server/tasks/contingent/pkg/headscale.yml index d2fab35..b797c3c 100644 --- a/roles/init-server/tasks/contingent/pkg/headscale.yml +++ b/roles/init-server/tasks/contingent/pkg/headscale.yml @@ -82,9 +82,13 @@ - name: Placing Headscale authentication key into file on control node ansible.builtin.fetch: src: "{{ tailscale_admin_authkey_file.dest }}" - dest: "./.tmp/{{ inventory_hostname }}-{{ headscale.users.admin.username }}@headscale/headscale.key" + dest: "./.tmp/{{ item.name }}-{{ headscale.users.admin.username }}@headscale/headscale{{ (idx | string) }}.key" flat: true + loop: "{{ headscale.clients }}" + loop_control: + index_var: idx - name: Placing Headscale authentication key into file on control node + when: "((fqdn is defined and fqdn != None) and fqdn in item.name) or (item.name in groups.homeserver or item.name in groups['homeserver.test'])" ansible.builtin.fetch: src: "{{ tailscale_admin_authkey_file.dest }}" dest: "./roles/init-server/files/{{ item.name }}-{{ headscale.users.admin.username }}@headscale/headscale{{ (idx | string) }}.key" @@ -94,7 +98,10 @@ index_var: idx - name: Informing control node of acquired files ansible.builtin.debug: - msg: "The Headscale authentication key files have been duplicated to './.tmp/{{ inventory_hostname }}-{{ headscale.users.admin.username }}@headscale/headscale.key' at the control node." + msg: "The Headscale authentication key files have been duplicated to {{ item }} at the control node." + loop: + - "./roles/init-server/files/" + - "./.tmp/" - name: Giving control node user time to read the aforementiioned message ansible.builtin.pause: seconds: 30 \ No newline at end of file diff --git a/roles/init-server/tasks/contingent/pkg/iptables.yml b/roles/init-server/tasks/contingent/pkg/iptables.yml new file mode 100644 index 0000000..6d948e4 --- /dev/null +++ b/roles/init-server/tasks/contingent/pkg/iptables.yml @@ -0,0 +1,101 @@ +- name: Getting public IP addresses + block: + - name: Getting public IPv4 address + community.general.ipify_facts: + validate_certs: false + timeout: 20 + - name: Getting public IPv6 address + when: ipv6_enabled + ansible.builtin.shell: + cmd: curl -6 icanhazip.com + register: ipify_public_ip6 +- name: Getting available network interfaces + ansible.builtin.shell: + cmd: "ip link show | awk '/^[0-9]+: [a-z]/ { print $2 }' | grep -vE 'lo[0-9]*|wg[0-9]+-mullvad' | sed 's/://g'" + register: intrfaces +- name: Turning acquired network interfaces into list + ansible.builtin.set_fact: + main_interfaces: "{{ intrfaces.stdout | replace('\n', ' ') | split(' ') }}" +- name: Pre-emptively allowing inbound connections already established and related to outbound requests + ansible.builtin.iptables: + ip_version: both + chain: INPUT + match: + - conntrack + ctstate: + - ESTABLISHED + - RELATED + jump: ACCEPT + comment: allowing inbound replies +- name: Pre-emptively establishing fiirewall rules for inbound ICMP requests + become: true + block: + - name: Pre-emptively allowing inbound IPv4 ICMP connections + ansible.builtin.iptables: + ip_version: ipv4 + chain: INPUT + destination: "! {{ ipify_public_ip }}" + protocol: icmp + jump: ACCEPT + comment: ping + - name: Pre-emptively allowing inbound IPv6 ICMP connections + when: ipv6_enabled + ansible.builtin.iptables: + ip_version: ipv6 + chain: INPUT + destination: "! {{ ipify_public_ip6.stdout }}" + protocol: icmp + jump: ACCEPT + comment: ping +- name: Pre-emptively establishing firewall rules for loopback interface + become: true + block: + - name: Pre-emptively allowing all inbound connections on loopback device + ansible.builtin.iptables: + ip_version: both + chain: INPUT + in_interface: lo + jump: ACCEPT + comment: loopback +- name: Pre-emptively allowing inbound SSH + become: true + ansible.builtin.iptables: + ip_version: both + chain: INPUT + protocol: tcp + destination_port: 22 + in_interface: "{{ item }}" + jump: ACCEPT + comment: ssh + loop: "{{ main_interfaces }}" +- name: Crafting additional exceptions to the inbound default policy + become: true + when: iptables.allow.inbound is defined and iptables.allow.inbound != None + ansible.builtin.iptables: + ip_version: both + chain: INPUT + protocol: "{{ item[1].protocol | default('tcp') }}" + destination_port: "{{ item[1].port }}" + in_interface: "{{ item[1].interface | default(item[0]) }}" + jump: ACCEPT + comment: "{{ item[1].desc | default('srvr') }}" + loop: "{{ main_interfaces | product(iptables.allow.inbound) }}" +- name: Setting default policy for inbound connections + become: true + ansible.builtin.iptables: + chain: INPUT + policy: DROP +- name: Setting default policy for outbound connections + become: true + ansible.builtin.iptables: + chain: OUTPUT + policy: ACCEPT +- name: Backing up firewall rules + become: true + community.general.iptables_state: + ip_version: "{{ item[0] }}" + state: saved + path: /etc/iptables/initial_rules.{{ item[1] }} + loop: + - ["ipv4", "v4"] + - ["ipv6", "v6"] diff --git a/roles/init-server/tasks/contingent/pkg/libpam-google-authenticator.yml b/roles/init-server/tasks/contingent/pkg/libpam-google-authenticator.yml index 38deece..6ac6f68 100644 --- a/roles/init-server/tasks/contingent/pkg/libpam-google-authenticator.yml +++ b/roles/init-server/tasks/contingent/pkg/libpam-google-authenticator.yml @@ -67,12 +67,13 @@ owner: root group: root mode: "644" -- name: Prioritizing authentication methods in SSH +- name: Allowing interactive keyboard authentication become: true ansible.builtin.lineinfile: path: /etc/ssh/sshd_config.d/auth.conf - line: "KbdInteractiveAuthentication yes # enable if implementing TOTP 2FA" regexp: "^KbdInteractiveAuthentication" + line: "KbdInteractiveAuthentication yes # enable if implementing TOTP 2FA" + insertafter: EOF owner: root group: root mode: "644" @@ -80,12 +81,20 @@ become: true ansible.builtin.lineinfile: path: /etc/ssh/sshd_config.d/auth.conf + regexp: "^AuthenticationMethods" line: "AuthenticationMethods publickey keyboard-interactive:pam" insertafter: "^KbdInteractiveAuthentication" owner: root group: root mode: "644" -# - name: Adding Google Authenticator OTP module PAM authentication line for SSH +- name: Restarting SystemD SSH service + become: true + ansible.builtin.systemd_service: + name: sshd + scope: system + state: restarted + enabled: true +# - name: Adding Google Authenticator OTP module PAM authentication line for sudo # become: true # ansible.builtin.lineinfile: # path: /etc/pam.d/sudo @@ -94,7 +103,7 @@ # owner: root # group: root # mode: "644" -# - name: Adding Google Authenticator OTP module PAM authentication line for SSH +# - name: Adding Google Authenticator OTP module PAM authentication line for sudo # become: true # ansible.builtin.lineinfile: # path: /etc/pam.d/su @@ -102,9 +111,4 @@ # insertafter: "include common-auth$" # owner: root # group: root -# mode: "644" -- name: Restarting SystemD SSH service - become: true - ansible.builtin.systemd_service: - name: sshd - state: restarted \ No newline at end of file +# mode: "644" \ No newline at end of file diff --git a/roles/init-server/tasks/contingent/pkg/podman.yml b/roles/init-server/tasks/contingent/pkg/podman.yml index 82dbbb3..c590b3b 100644 --- a/roles/init-server/tasks/contingent/pkg/podman.yml +++ b/roles/init-server/tasks/contingent/pkg/podman.yml @@ -9,19 +9,49 @@ ansible.builtin.shell: cmd: "echo {{ ansible_user }}" register: current_user +- name: Checking if ZFS filesystem is present + ansible.builtin.shell: + cmd: "findmnt -D | awk '{ print $2 }' | tail -n +2" + register: fs_result +- name: Logging presence of ZFS filesystem + when: "'zfs' in fs_result.stdout" + ansible.builtin.set_fact: + zfs_present: true +- name: Logging absence of ZFS filesystem + when: "'zfs' not in fs_result.stdout" + ansible.builtin.set_fact: + zfs_present: false +- name: Configuring ZFS support + when: zfs_present + block: + - name: Getting available pools + ansible.builtin.shell: + cmd: "zpool list | awk '{ print $1 }' | tail -n +2" + register: zpools + - name: Turning available pools into list + ansible.builtin.set_fact: + zpool_list: "{{ zpools | replace('\n', ' ') | split(' ') }}" + - name: Allowing choice of zpool + ansible.builtin.pause: + prompt: "Type preferred zpool from available (i.e., {{ zpool_list | join(', ') }})" + register: preferred_zpool - name: Ensuring user namespace support is enabled become: true ansible.posix.sysctl: name: kernel.unprivileged_userns_clone value: "1" + sysctl_file: /etc/sysctl.d/10-security.conf sysctl_set: true + reload: true state: present - name: Ensuring user namespace support is enabled become: true ansible.posix.sysctl: name: net.ipv4.ip_unprivileged_port_start - value: "0" + value: "20" + sysctl_file: /etc/sysctl.d/10-security.conf sysctl_set: true + reload: true state: present - name: Creating container directory ansible.builtin.file: @@ -31,6 +61,24 @@ group: "{{ ansible_user }}" mode: "755" state: directory +- name: Creating a ZFS dataset for container secrets + become: true + when: zfs_present + community.general.zfs: + name: "{{ preferred_zpool.user_input }}/container_volumes" + state: present + extra_zfs_properties: + canmount: "on" + mountpoint: "{{ ansible_user_home.stdout }}/volumes" +- name: Configuring container image registries + become: true + ansible.builtin.file: + path: "{{ ansible_user_home.stdout }}/volumes" + recurse: true + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + mode: "755" + state: directory - name: Configuring container storage become: true become_user: "{{ current_user.stdout }}" diff --git a/roles/init-server/tasks/contingent/pkg/quartz.yml b/roles/init-server/tasks/contingent/pkg/quartz.yml index 57ccf1f..59210fa 100644 --- a/roles/init-server/tasks/contingent/pkg/quartz.yml +++ b/roles/init-server/tasks/contingent/pkg/quartz.yml @@ -36,7 +36,7 @@ become_user: "{{ current_user.stdout }}" community.general.npm: path: "{{ ansible_user_home.stdout }}/repos/.foreign/quartz" - state: latest + state: present - name: Configuring quartz software block: - name: Creating path for Quartz content files (path for Obsidian vaults) @@ -97,6 +97,13 @@ - plugin - install chdir: "{{ ansible_user_home.stdout }}/repos/.foreign/quartz" +- name: Creating directory for caddy configuration + ansible.builtin.file: + path: "{{ ansible_user_home.stdout }}/srv/notes.{{ web_fqdn }}" + recurse: true + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + state: directory - name: Starting quartz site web server become: true become_user: "{{ current_user.stdout }}" diff --git a/roles/init-server/tasks/contingent/pkg/radicle.yml b/roles/init-server/tasks/contingent/pkg/radicle.yml index ee55838..a9ee44f 100644 --- a/roles/init-server/tasks/contingent/pkg/radicle.yml +++ b/roles/init-server/tasks/contingent/pkg/radicle.yml @@ -15,13 +15,12 @@ ansible.builtin.shell: cmd: "{{ ansible_user_home.stdout }}/.local/bin/radicle-install.sh" creates: "{{ ansible_user_home.stdout }}/.radicle" - async: 600 + async: 300 poll: 5 - name: Bootstrapping Radicle become: true block: - name: Linking binaries to directories already in PATH environment variable - ignore_errors: true ansible.builtin.file: src: "{{ ansible_user_home.stdout }}/.radicle/bin/{{ item }}" dest: "/usr/bin/{{ item }}" diff --git a/roles/init-server/tasks/contingent/pkg/rustup.yml b/roles/init-server/tasks/contingent/pkg/rustup.yml index 52fc730..68f9023 100644 --- a/roles/init-server/tasks/contingent/pkg/rustup.yml +++ b/roles/init-server/tasks/contingent/pkg/rustup.yml @@ -15,7 +15,7 @@ ansible.builtin.shell: cmd: "{{ ansible_user_home.stdout }}/.local/bin/rustup-install.sh -yq" creates: "{{ ansible_user_home.stdout }}/.cargo/bin" - async: 600 + async: 300 poll: 5 - name: Linking binaries to directories already in PATH environment variable become: true diff --git a/roles/init-server/tasks/contingent/pkg/skato_compose.yml b/roles/init-server/tasks/contingent/pkg/skato_compose.yml index 16dbf70..44f8bca 100644 --- a/roles/init-server/tasks/contingent/pkg/skato_compose.yml +++ b/roles/init-server/tasks/contingent/pkg/skato_compose.yml @@ -9,6 +9,32 @@ ansible.builtin.shell: cmd: "echo {{ ansible_user }}" register: current_user +- name: Checking if ZFS filesystem is present + ansible.builtin.shell: + cmd: "findmnt -D | awk '{ print $2 }' | tail -n +2" + register: fs_result +- name: Logging presence of ZFS filesystem + when: "'zfs' in fs_result.stdout" + ansible.builtin.set_fact: + zfs_present: true +- name: Logging absence of ZFS filesystem + when: "'zfs' not in fs_result.stdout" + ansible.builtin.set_fact: + zfs_present: false +- name: Configuring ZFS support + when: zfs_present + block: + - name: Getting available pools + ansible.builtin.shell: + cmd: "zpool list | awk '{ print $1 }' | tail -n +2" + register: zpools + - name: Turning available pools into list + ansible.builtin.set_fact: + zpool_list: "{{ zpools | replace('\n', ' ') | split(' ') }}" + - name: Allowing choice of zpool + ansible.builtin.pause: + prompt: "Type preferred zpool from available (i.e., {{ zpool_list | join(', ') }})" + register: preferred_zpool - name: Informing user of requirement of two main domains when: (certbot.domains | length) < 2 or (certbot.domains | length) > 2 ansible.builtin.fail: @@ -17,6 +43,24 @@ when: (certbot.domains | select("regex", "^\\*\\.") | list | length) == 0 ansible.builtin.fail: msg: At least one of the FQDNs must have a wildcard +- name: Getting public IP addresses + block: + - name: Getting public IPv4 address + community.general.ipify_facts: + validate_certs: false + timeout: 20 + - name: Getting public IPv6 address + when: ipv6_enabled + ansible.builtin.shell: + cmd: curl -6 icanhazip.com + register: ipify_public_ip6 +- name: Getting available network interfaces + ansible.builtin.shell: + cmd: "ip link show | awk '/^[0-9]+: [a-z]/ { print $2 }' | grep -vE 'lo[0-9]*|wg[0-9]+-mullvad' | sed 's/://g'" + register: intrfaces +- name: Turning acquired network interfaces into list + ansible.builtin.set_fact: + main_interfaces: "{{ intrfaces.stdout | replace('\n', ' ') | split(' ') }}" # - name: Setting the FQDN for development # when: compose.mode == "dev" # ansible.builtin.set_fact: @@ -53,17 +97,32 @@ cmd: "git init --bare {{ source_code.repos.compose.name }}.git" chdir: "{{ ansible_user_home.stdout }}/src" creates: "{{ ansible_user_home.stdout }}/src/{{ source_code.repos.compose.name }}.git" -- name: Creating required SMTP-related container secret +- name: Creating a ZFS dataset for container secrets become: true - # become_user: "{{ current_user.stdout }}" - ansible.builtin.lineinfile: - line: "{{ email.smtp.password }}" - path: "{{ ansible_user_home.stdout }}/.podsecrets/email.pass" - insertafter: EOF + when: zfs_present + community.general.zfs: + name: "{{ preferred_zpool.user_input }}/container_secrets" + state: present + extra_zfs_properties: + canmount: "on" + mountpoint: "{{ ansible_user_home.stdout }}/.podsecrets" +- name: Creating a directory for container secrets + become: true + ansible.builtin.file: + path: "{{ ansible_user_home.stdout }}/.podsecrets" + recurse: true + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + mode: "644" + state: directory +- name: Creating required SMTP-related container secret + become: true + ansible.builtin.copy: + content: "{{ email.smtp.password }}" + dest: "{{ ansible_user_home.stdout }}/.podsecrets/email.pass" owner: "{{ ansible_user }}" group: "{{ ansible_user }}" mode: "644" - create: true backup: true - name: Creating environment file become: true @@ -89,14 +148,6 @@ force: true backup: true register: email_environ -- name: Creating a directory for container secrets - ansible.builtin.file: - path: "{{ ansible_user_home.stdout }}/.podsecrets" - recurse: true - owner: "{{ ansible_user }}" - group: "{{ ansible_user }}" - mode: "644" - state: directory - name: Creating directory for DBMS server configuration ansible.builtin.file: path: "{{ ansible_user_home.stdout }}/.config/{{ item }}" @@ -110,14 +161,84 @@ - letsencrypt - mysql - redis - - gitea + - forgejo - opengist + - nodebb + - syncplay - tailscale - glance + - surge + - grocy + - kavita + - stash +- name: Creating environment file for NSFW streaming container specified in Compose file + become: true + become_user: "{{ current_user.stdout }}" + when: "source_code.repos.compose.run and stash.containerized and (compose.containers.stash is defined and compose.containers.stash != None)" + ansible.builtin.template: + src: user/stash.env.j2 + dest: "{{ ansible_user_home.stdout }}/.stash.env" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + mode: "644" + force: true + backup: true + register: stash_environ +- name: Creating environment file for financial budgeting container specified in Compose file + become: true + become_user: "{{ current_user.stdout }}" + ansible.builtin.template: + src: user/actual.env.j2 + dest: "{{ ansible_user_home.stdout }}/.actual.env" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + mode: "644" + force: true + backup: true + register: actual_environ +- name: Creating directory for DBMS server configuration + ansible.builtin.file: + path: "{{ ansible_user_home.stdout }}/.{{ item }}" + recurse: true + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + mode: "755" + state: directory + loop: + - aria2 + - flexget + - xxx +- name: Creating directory for DBMS server configuration + ansible.builtin.file: + path: "{{ ansible_user_home.stdout }}/{{ item }}" + recurse: true + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + mode: "755" + state: directory + loop: + - media + - downloads +- name: Creating a ZFS dataset for container secrets + become: true + when: zfs_present + community.general.zfs: + name: "{{ preferred_zpool.user_input }}/server_contents" + state: present + extra_zfs_properties: + canmount: "on" + mountpoint: "{{ ansible_user_home.stdout }}/srv" +- name: Creating directory for main website root + ansible.builtin.file: + path: "{{ ansible_user_home.stdout }}/srv" + recurse: true + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + mode: "755" + state: directory - name: Creating directory for main website root ansible.builtin.file: path: "{{ ansible_user_home.stdout }}/srv/{{ web_fqdn }}" - recurse: true owner: "{{ ansible_user }}" group: "{{ ansible_user }}" mode: "755" @@ -140,7 +261,7 @@ - name: Calculating desired container state for VPN client ansible.builtin.set_fact: # @TODO write case of at least one vpn client having a boolean attribute declaring whether it is a container - vpn_run: "{{ source_code.repos.compose.run and (vpn.clients | selectattr('name', 'in', web_fqdn) | length) == 1 and (compose.containers.vpn is defined and compose.containers.vpn != None) }}" + vpn_run: "{{ source_code.repos.compose.run and (compose.containers.vpn is defined and compose.containers.vpn != None) }}" - name: Calculating desired container state for web server or reverse proxy server ansible.builtin.set_fact: server_run: "{{ source_code.repos.compose.run and caddy.containerized and (compose.containers.webserver is defined and compose.containers.webserver != None) }}" @@ -158,16 +279,49 @@ cloud_run: "{{ source_code.repos.compose.run and nextcloud.containerized and (compose.containers.cloud is defined and compose.containers.cloud != None) }}" - name: Calculating desired container state for forge server ansible.builtin.set_fact: - forge_run: "{{ source_code.repos.compose.run and gitea.containerized and (compose.containers.forge is defined and compose.containers.forge != None ) }}" + forge_run: "{{ source_code.repos.compose.run and forgejo.containerized and (compose.containers.forge is defined and compose.containers.forge != None ) }}" - name: Calculating desired container state for pastebin server ansible.builtin.set_fact: pbin_run: "{{ source_code.repos.compose.run and gist.containerized and (compose.containers.pastebin is defined and compose.containers.pastebin != None) }}" + - name: Calculating desired container state for pastebin server + ansible.builtin.set_fact: + # cast_run: "{{ source_code.repos.compose.run and cast.containerized and (compose.containers.cast is defined and compose.containers.cast != None) }}" + cast_run: "{{ (compose.containers.cast is defined and compose.containers.cast != None) }}" - name: Calculating desired container state for tailnet client ansible.builtin.set_fact: tail_run: "{{ source_code.repos.compose.run and tailscale.containerized and (compose.containers.tail is defined and compose.containers.tail != None) }}" - name: Calculating desired container state for tailnet dashboard ansible.builtin.set_fact: dash_run: "{{ source_code.repos.compose.run and glance.containerized and (compose.containers.dash is defined and compose.containers.dash != None) }}" + - name: Calculating whether correct domain for given configuration detected + ansible.builtin.set_fact: + correct_domain: "{{ (inventory_hostname in groups['homeserver'] or inventory_hostname in groups['homeserver.test']) or ((web_fqdn is defined and web_fqdn != None) and (web_fqdn in groups['homeserver'] or web_fqdn in groups['homeserver.test'])) }}" +- name: Creating environment file for pastebin server container specified by Compose file + become: true + become_user: "{{ current_user.stdout }}" + when: vpn_run or pbin_run + ansible.builtin.template: + src: user/opengist.env.j2 + dest: "{{ ansible_user_home.stdout }}/.opengist.env" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + mode: "644" + force: true + backup: true + register: opengist_environ +- name: Creating environment file for forge server container specified by Compose file + become: true + become_user: "{{ current_user.stdout }}" + when: vpn_run or forge_run + ansible.builtin.template: + src: user/forgejo.env.j2 + dest: "{{ ansible_user_home.stdout }}/.forgejo.env" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + mode: "644" + force: true + backup: true + register: forgejo_environ - name: Running VPN client Compose files when: vpn_run block: @@ -181,62 +335,128 @@ - name: Copying VPN client configuration files become: true become_user: "{{ current_user.stdout }}" - when: ((fqdn is defined and fqdn != None) and item.name == fqdn) or item.name == inventory_hostname + when: "source_code.repos.compose.rpath == '/dependent' and ((inventory_hostname in item.name or web_fqdn in item.name) or correct_domain)" ansible.builtin.copy: src: "user/wg/authorized_clients.d/{{ item.name }}{{ (idx | string) }}.conf" dest: "{{ ansible_user_home.stdout }}/.wg/authorized_clients.d/" owner: "{{ ansible_user }}" group: "{{ ansible_user }}" - mode: "644" + mode: "600" force: true backup: true loop: "{{ vpn.clients }}" loop_control: index_var: idx + register: vpn_client_configs + - name: Change allowed IPs policy for VPN client to include all IPv4 addresses + # when: "source_code.repos.compose.rpath == '/dependent' and ((((fqdn is defined and fqdn != None) and fqdn in item.name) or (item.name in groups.homeserver or item.name in groups['homeserver.test'])))" + when: "source_code.repos.compose.rpath == '/dependent' and ((inventory_hostname in item.name or web_fqdn in item.name) or correct_domain)" + ansible.builtin.lineinfile: + path: "{{ ansible_user_home.stdout }}/.wg/authorized_clients.d/{{ item.name }}{{ (idx | string) }}.conf" + regexp: "^AllowedIPs ?= ?(([0-9]+\\.)+[0-9]+(\\/[0-9]+)?(, ?)?)*$" + line: "AllowedIPs=0.0.0.0/0" + insertafter: EOF + state: present + loop: "{{ vpn.clients }}" + loop_control: + index_var: idx + - name: Change allowed IPs policy for VPN client to include all IPv6 addresses + # when: "source_code.repos.compose.rpath == '/dependent' and ((((fqdn is defined and fqdn != None) and fqdn in item.name) or (item.name in groups.homeserver or item.name in groups['homeserver.test'])))" + when: "source_code.repos.compose.rpath == '/dependent' and ((inventory_hostname in item.name or web_fqdn in item.name) or correct_domain)" + ansible.builtin.lineinfile: + path: "{{ ansible_user_home.stdout }}/.wg/authorized_clients.d/{{ item.name }}{{ (idx | string) }}.conf" + regexp: "^AllowedIPs ?= ?(([0-9]*:)+[0-9]*(\\/[0-9]+)?(, ?)?)*$" + line: "AllowedIPs=::/0" + insertafter: EOF + state: present + loop: "{{ vpn.clients }}" + loop_control: + index_var: idx + - name: Creating IPTables rule for VPN configuration + # when: "source_code.repos.compose.rpath == '/dependent' and not ipv6_enabled and (((fqdn is defined and fqdn != None) and fqdn in item.name) or (item.name in groups.homeserver or item.name in groups['homeserver.test']))" + when: "source_code.repos.compose.rpath == '/dependent' and not ipv6_enabled and ((inventory_hostname in item.name or web_fqdn in item.name) or correct_domain)" + ansible.builtin.blockinfile: + path: "{{ ansible_user_home.stdout }}/.wg/authorized_clients.d/{{ item.name }}{{ (idx | string) }}.conf" + marker: "# <-- {mark} ANSIBLE MANAGED IPTABLES RULES -->" + block: | + # Up rules + PreUp=iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT + PreUp=ip6tables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT + PreUp=iptables -A INPUT -p icmp ! -d {{ ipify_public_ip }} -j ACCEPT + PreUp=iptables -A INPUT -i lo -j ACCEPT + PreUp=ip6tables -A INPUT -i lo -j ACCEPT + PreUp=iptables -P INPUT DROP + PreUp=ip6tables -P INPUT DROP + # Down rules + PostDown=iptables -D INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT + PostDown=ip6tables -D INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT + PostDown=iptables -D INPUT -p icmp ! -d {{ ipify_public_ip }} -j ACCEPT + PostDown=iptables -D INPUT -i lo -j ACCEPT + PostDown=ip6tables -D INPUT -i lo -j ACCEPT + PostDown=iptables -P INPUT DROP + PostDown=ip6tables -P INPUT DROP + insertafter: "^PrivateKey ?= ?" + state: present + loop: "{{ vpn.clients }}" + loop_control: + index_var: idx + - name: Creating IPTables rule for VPN configuration + # when: "source_code.repos.compose.rpath == '/dependent' and ipv6_enabled and ((((fqdn is defined and fqdn != None) and fqdn in item.name) or (item.name in groups.homeserver or item.name in groups['homeserver.test'])))" + when: "source_code.repos.compose.rpath == '/dependent' and ipv6_enabled and ((inventory_hostname in item.name or web_fqdn in item.name) or correct_domain)" + ansible.builtin.blockinfile: + path: "{{ ansible_user_home.stdout }}/.wg/authorized_clients.d/{{ item.name }}{{ (idx | string) }}.conf" + marker: "# <-- {mark} ANSIBLE MANAGED IPTABLES RULES -->" + block: | + # Up rules + PreUp=iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT + PreUp=ip6tables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT + PreUp=iptables -A INPUT -p icmp ! -d {{ ipify_public_ip }} -j ACCEPT + PreUp=ip6tables -A INPUT -p icmpv6 ! -d {{ ipify_public_ip6.stdout }} -j ACCEPT + PreUp=iptables -A INPUT -i lo -j ACCEPT + PreUp=ip6tables -A INPUT -i lo -j ACCEPT + PreUp=iptables -P INPUT DROP + PreUp=ip6tables -P INPUT DROP + # Down rules + PostDown=iptables -D INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT + PostDown=ip6tables -D INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT + PostDown=iptables -D INPUT -p icmp ! -d {{ ipify_public_ip }} -j ACCEPT + PostDown=ip6tables -D INPUT -p icmpv6 ! -d {{ ipify_public_ip6.stdout }} -j ACCEPT + PostDown=iptables -D INPUT -i lo -j ACCEPT + PostDown=ip6tables -D INPUT -i lo -j ACCEPT + PostDown=iptables -P INPUT DROP + PostDown=ip6tables -P INPUT DROP + insertafter: "^PrivateKey ?= ?" + state: present + loop: "{{ vpn.clients }}" + loop_control: + index_var: idx - name: Creating and deploying VPN client container specified by Compose file become: true become_user: "{{ current_user.stdout }}" - when: source_code.repos.compose.run and (vpn.clients | selectattr("name", "in", web_fqdn) | length) == 1 and (compose.containers.vpn is defined and compose.containers.vpn != None) + when: source_code.repos.compose.rpath == '/dependent' ansible.builtin.command: cmd: "podman-compose up -d {{ compose.containers.vpn.name }}" chdir: "{{ new_compose_path.dest }}{{ source_code.repos.compose.rpath | default('/independent') }}" - - name: Opening port 51820 - become: true - ansible.builtin.iptables: - chain: INPUT - protocol: "{{ item }}" - destination_port: 51820 - jump: ACCEPT - comment: Open up port 51820 - loop: - - udp - - tcp - name: Spinning up database management container specified by Compose file when: dbms_run block: - name: Creating required container secret for DBMS server container's root account become: true - # become_user: "{{ current_user.stdout }}" - ansible.builtin.lineinfile: - line: "{{ mysql.password }}" - path: "{{ ansible_user_home.stdout }}/.podsecrets/root-mysql.pass" - insertafter: EOF + ansible.builtin.copy: + content: "{{ mysql.password }}" + dest: "{{ ansible_user_home.stdout }}/.podsecrets/root-mysql.pass" owner: "{{ ansible_user }}" group: "{{ ansible_user }}" mode: "644" - create: true backup: true - name: Creatng required container secret for DBMS server container's primary non-root account become: true - # become_user: "{{ current_user.stdout }}" - ansible.builtin.lineinfile: - line: "{{ mysql.users.admin.password }}" - path: "{{ ansible_user_home.stdout }}/.podsecrets/user-mysql.pass" - insertafter: EOF + ansible.builtin.copy: + content: "{{ mysql.users.admin.password }}" + dest: "{{ ansible_user_home.stdout }}/.podsecrets/user-mysql.pass" owner: "{{ ansible_user }}" group: "{{ ansible_user }}" mode: "644" - create: true backup: true - name: Creating environment file for DBMS server container specified in Compose file become: true @@ -298,16 +518,13 @@ detach: true - name: Creating required cache-related container secret for cloud server container specified by Compose file become: true - # become_user: "{{ current_user.stdout }}" when: cache_run - ansible.builtin.lineinfile: - line: "{{ redis.password }}" + ansible.builtin.copy: + content: "{{ redis.password }}" dest: "{{ ansible_user_home.stdout }}/.podsecrets/redis.pass" - insertafter: EOF owner: "{{ ansible_user }}" group: "{{ ansible_user }}" mode: "644" - create: true backup: true - name: Creating environment file for cloud server container specified by Compose file become: true @@ -321,6 +538,47 @@ force: true backup: true register: nextcloud_environ + # - name: Debugging + # ansible.builtin.debug: + # msg: "{{ vpn.clients }}" + # - name: Prematurely ending + # ansible.builtin.meta: end_play + - name: Allowing rate limiting concurrent connections per IP on start + # when: "source_code.repos.compose.rpath == '/dependent' and vpn_run and (((fqdn is defined and fqdn != None) and fqdn in item.name) or (item.name in groups.homeserver or item.name in groups['homeserver.test']))" + when: "source_code.repos.compose.rpath == '/dependent' and vpn_run and ((inventory_hostname in item.name or web_fqdn in item.name) or correct_domain)" + ansible.builtin.blockinfile: + path: "{{ ansible_user_home.stdout }}/.wg/authorized_clients.d/{{ item.name }}{{ (idx | string) }}.conf" + marker: "# <-- {mark} ANSIBLE OPEN PORT 993 -->" + block: | + PreUp=iptables -A INPUT -p tcp --dport 993 -j ACCEPT + PreUp=ip6tables -A INPUT -p tcp --dport 993 -j ACCEPT + insertafter: "^PreUp ?= ?" + state: present + loop: "{{ vpn.clients }}" + loop_control: + index_var: idx + - name: Allowing rate limiting concurrent connections per IP on start + # when: "source_code.repos.compose.rpath == '/dependent' and vpn_run and (((fqdn is defined and fqdn != None) and fqdn in item.name) or (item.name in groups.homeserver or item.name in groups['homeserver.test']))" + when: "source_code.repos.compose.rpath == '/dependent' and vpn_run and ((inventory_hostname in item.name or web_fqdn in item.name) or correct_domain)" + ansible.builtin.blockinfile: + path: "{{ ansible_user_home.stdout }}/.wg/authorized_clients.d/{{ item.name }}{{ (idx | string) }}.conf" + marker: "# <-- {mark} ANSIBLE CLOSE PORT 993 -->" + block: | + PostDown=iptables -A INPUT -p tcp --dport 993 -j ACCEPT + PostDown=ip6tables -A INPUT -p tcp --dport 993 -j ACCEPT + insertafter: "^PostDown ?= ?" + state: present + loop: "{{ vpn.clients }}" + loop_control: + index_var: idx + - name: Restarting VPN container + when: "source_code.repos.compose.rpath == '/dependent' and vpn_run" + become: true + become_user: "{{ current_user.stdout }}" + containers.podman.podman_container: + name: "{{ compose.containers.vpn.name }}" + state: started + force_restart: true - name: Creating and deploying DBMS server container specified by Compose file become: true become_user: "{{ current_user.stdout }}" @@ -398,32 +656,56 @@ - "{{ mysql.users.admin.username }}" - "-p{{ mysql.users.admin.password }}" - "-e" - - "'CREATE DATABASE IF NOT EXISTS {{ mysql.users.admin.databases.gitea.name }};'" + - "'CREATE DATABASE IF NOT EXISTS {{ mysql.users.admin.databases.forgejo.name }};'" detach: true - - name: Creating environment file for forge server container specified by Compose file + - name: Allowing rate limiting concurrent connections per IP on start + # when: "source_code.repos.compose.rpath == '/dependent' and vpn_run and (((fqdn is defined and fqdn != None) and fqdn in item.name) or (item.name in groups.homeserver or item.name in groups['homeserver.test']))" + when: "source_code.repos.compose.rpath == '/dependent' and vpn_run and ((inventory_hostname in item.name or web_fqdn in item.name) or correct_domain)" + ansible.builtin.blockinfile: + path: "{{ ansible_user_home.stdout }}/.wg/authorized_clients.d/{{ item.name }}{{ (idx | string) }}.conf" + marker: "# <-- {mark} ANSIBLE OPEN PORT 2121 -->" + block: | + PreUp=iptables -A INPUT -p tcp --dport 2121 -j ACCEPT + PreUp=ip6tables -A INPUT -p tcp --dport 2121 -j ACCEPT + insertafter: "^PreUp ?= ?" + state: present + loop: "{{ vpn.clients }}" + loop_control: + index_var: idx + - name: Disabling rate limiting concurrent connections per IP on stop + # when: "source_code.repos.compose.rpath == '/dependent' and vpn_run and (((fqdn is defined and fqdn != None) and fqdn in item.name) or (item.name in groups.homeserver or item.name in groups['homeserver.test']))" + when: "source_code.repos.compose.rpath == '/dependent' and vpn_run and ((inventory_hostname in item.name or web_fqdn in item.name) or correct_domain)" + ansible.builtin.blockinfile: + path: "{{ ansible_user_home.stdout }}/.wg/authorized_clients.d/{{ item.name }}{{ (idx | string) }}.conf" + marker: "# <-- {mark} ANSIBLE CLOSE PORT 2121 -->" + block: | + PostDown=iptables -A INPUT -p tcp --dport 2121 -j ACCEPT + PostDown=ip6tables -A INPUT -p tcp --dport 2121 -j ACCEPT + insertafter: "^PostDown ?= ?" + state: present + loop: "{{ vpn.clients }}" + loop_control: + index_var: idx + - name: Restarting VPN container become: true become_user: "{{ current_user.stdout }}" - ansible.builtin.template: - src: user/gitea.env.j2 - dest: "{{ ansible_user_home.stdout }}/.gitea.env" - owner: "{{ ansible_user }}" - group: "{{ ansible_user }}" - mode: "644" - force: true - backup: true - register: gitea_environ + when: "source_code.repos.compose.rpath == '/dependent' and vpn_run" + containers.podman.podman_container: + name: "{{ compose.containers.vpn.name }}" + state: started + force_restart: true - name: Creating and deploying forge server container specified by Compose file become: true become_user: "{{ current_user.stdout }}" ansible.builtin.command: - cmd: "podman-compose --env-file {{ mysql_environ.dest }} --env-file {{ email_environ.dest }} --env-file {{ gitea_environ.dest }} --env-file {{ environ.dest }} up -d {{ compose.containers.forge.name }}" + cmd: "podman-compose --env-file {{ mysql_environ.dest }} --env-file {{ email_environ.dest }} --env-file {{ forgejo_environ.dest }} --env-file {{ environ.dest }} up -d {{ compose.containers.forge.name }}" chdir: "{{ new_compose_path.dest }}{{ source_code.repos.compose.rpath | default('/independent') }}" - name: Adding Caddyfile subdomain entry when: "server_run and mode == 'prod'" ansible.builtin.blockinfile: path: "{{ ansible_user_home.stdout }}/.config/caddy/Caddyfile" block: | - {{ gitea.subdomain }}.{{ web_fqdn }} { + {{ forgejo.subdomain }}.{{ web_fqdn }} { reverse_proxy localhost:3000 handle_errors { @@ -435,7 +717,7 @@ } } prepend_newline: true - marker: "# <-- {mark} ANSIBLE MANAGED CLOUD DOMAIN -->" + marker: "# <-- {mark} ANSIBLE MANAGED FORGE DOMAIN -->" owner: "{{ ansible_user }}" group: "{{ ansible_user }}" create: true @@ -446,7 +728,7 @@ ansible.builtin.blockinfile: path: "{{ ansible_user_home.stdout }}/.config/caddy/Caddyfile" block: | - http://{{ gitea.subdomain }}.{{ web_fqdn }} { + http://{{ forgejo.subdomain }}.{{ web_fqdn }} { reverse_proxy localhost:3000 handle_errors { @@ -458,7 +740,7 @@ } } prepend_newline: true - marker: "# <-- {mark} ANSIBLE MANAGED CLOUD DOMAIN -->" + marker: "# <-- {mark} ANSIBLE MANAGED FORGE DOMAIN -->" owner: "{{ ansible_user }}" group: "{{ ansible_user }}" create: true @@ -489,19 +771,6 @@ - "-e" - "'CREATE DATABASE IF NOT EXISTS {{ mysql.users.admin.databases.opengist.name }};'" detach: true - - name: Creating environment file for pastebin server container specified by Compose file - become: true - become_user: "{{ current_user.stdout }}" - when: pbin_run - ansible.builtin.template: - src: user/opengist.env.j2 - dest: "{{ ansible_user_home.stdout }}/.opengist.env" - owner: "{{ ansible_user }}" - group: "{{ ansible_user }}" - mode: "644" - force: true - backup: true - register: opengist_environ - name: Creating gist configuration file become: true become_user: "{{ current_user.stdout }}" @@ -514,6 +783,42 @@ mode: "644" force: true backup: true + - name: Allowing rate limiting concurrent connections per IP on start + # when: "source_code.repos.compose.rpath == '/dependent' and vpn_run and (((fqdn is defined and fqdn != None) and fqdn in item.name) or (item.name in groups.homeserver or item.name in groups['homeserver.test']))" + when: "source_code.repos.compose.rpath == '/dependent' and vpn_run and ((inventory_hostname in item.name or web_fqdn in item.name) or correct_domain)" + ansible.builtin.blockinfile: + path: "{{ ansible_user_home.stdout }}/.wg/authorized_clients.d/{{ item.name }}{{ (idx | string) }}.conf" + marker: "# <-- {mark} ANSIBLE OPEN PORT 2323 -->" + block: | + PreUp=iptables -A INPUT -p tcp --dport 2323 -j ACCEPT + PreUp=ip6tables -A INPUT -p tcp --dport 2323 -j ACCEPT + insertafter: "^PreUp ?= ?" + state: present + loop: "{{ vpn.clients }}" + loop_control: + index_var: idx + - name: Disabling rate limiting concurrent connections per IP on stop + # when: "source_code.repos.compose.rpath == '/dependent' and vpn_run and (((fqdn is defined and fqdn != None) and fqdn in item.name) or (item.name in groups.homeserver or item.name in groups['homeserver.test']))" + when: "source_code.repos.compose.rpath == '/dependent' and vpn_run and ((inventory_hostname in item.name or web_fqdn in item.name) or correct_domain)" + ansible.builtin.blockinfile: + path: "{{ ansible_user_home.stdout }}/.wg/authorized_clients.d/{{ item.name }}{{ (idx | string) }}.conf" + marker: "# <-- {mark} ANSIBLE CLOSE PORT 2323 -->" + block: | + PostDown=iptables -D INPUT -p tcp --dport 2323 -j ACCEPT + PostDown=ip6tables -D INPUT -p tcp --dport 2323 -j ACCEPT + insertafter: "^PostDown ?= ?" + state: present + loop: "{{ vpn.clients }}" + loop_control: + index_var: idx + - name: Restarting VPN container + when: "source_code.repos.compose.rpath == '/dependent' and vpn_run" + become: true + become_user: "{{ current_user.stdout }}" + containers.podman.podman_container: + name: "{{ compose.containers.vpn.name }}" + state: started + force_restart: true - name: Creating and deploying pastebin server container specified by Compose file become: true become_user: "{{ current_user.stdout }}" @@ -538,7 +843,7 @@ } } prepend_newline: true - marker: "# <-- {mark} ANSIBLE MANAGED CLOUD DOMAIN -->" + marker: "# <-- {mark} ANSIBLE MANAGED PASTEBIN DOMAIN -->" owner: "{{ ansible_user }}" group: "{{ ansible_user }}" create: true @@ -561,7 +866,7 @@ } } prepend_newline: true - marker: "# <-- {mark} ANSIBLE MANAGED CLOUD DOMAIN -->" + marker: "# <-- {mark} ANSIBLE MANAGED PASTEBIN DOMAIN -->" owner: "{{ ansible_user }}" group: "{{ ansible_user }}" create: true @@ -575,6 +880,93 @@ # name: "{{ compose.containers.webserver.name }}" # state: started # force_restart: true +- name: Spinning up video casting server container specified by Compose file + when: "source_code.repos.compose.rpath == '/dependent' and cast_run" + block: + - name: Allowing rate limiting concurrent connections per IP on start + # when: "source_code.repos.compose.rpath == '/dependent' and vpn_run and (((fqdn is defined and fqdn != None) and fqdn in item.name) or (item.name in groups.homeserver or item.name in groups['homeserver.test']))" + when: "source_code.repos.compose.rpath == '/dependent' and vpn_run and ((inventory_hostname in item.name or web_fqdn in item.name) or correct_domain)" + ansible.builtin.blockinfile: + path: "{{ ansible_user_home.stdout }}/.wg/authorized_clients.d/{{ item.name }}{{ (idx | string) }}.conf" + marker: "# <-- {mark} ANSIBLE OPEN PORT 8080,1935 -->" + block: | + PreUp=iptables -A INPUT -p tcp --dport 8080 -j ACCEPT + PreUp=iptables -A INPUT -p tcp --dport 1935 -j ACCEPT + PreUp=ip6tables -A INPUT -p tcp --dport 8080 -j ACCEPT + PreUp=ip6tables -A INPUT -p tcp --dport 1935 -j ACCEPT + insertafter: "^PreUp ?= ?" + state: present + loop: "{{ vpn.clients }}" + loop_control: + index_var: idx + - name: Disabling rate limiting concurrent connections per IP on stop + # when: "source_code.repos.compose.rpath == '/dependent' and vpn_run and (((fqdn is defined and fqdn != None) and fqdn in item.name) or (item.name in groups.homeserver or item.name in groups['homeserver.test']))" + when: "source_code.repos.compose.rpath == '/dependent' and vpn_run and ((inventory_hostname in item.name or web_fqdn in item.name) or correct_domain)" + ansible.builtin.blockinfile: + path: "{{ ansible_user_home.stdout }}/.wg/authorized_clients.d/{{ item.name }}{{ (idx | string) }}.conf" + marker: "# <-- {mark} ANSIBLE CLOSE PORT 8080, 1935 -->" + block: | + PostDown=iptables -D INPUT -p tcp --dport 8080 -j ACCEPT + PostDown=iptables -D INPUT -p tcp --dport 1935 -j ACCEPT + PostDown=ip6tables -D INPUT -p tcp --dport 8080 -j ACCEPT + PostDown=ip6tables -D INPUT -p tcp --dport 1935 -j ACCEPT + insertafter: "^PostDown ?= ?" + state: present + loop: "{{ vpn.clients }}" + loop_control: + index_var: idx + - name: Creating and deploying video casting server container specified by Compose file + become: true + become_user: "{{ current_user.stdout }}" + ansible.builtin.command: + cmd: "podman-compose --env-file {{ environ.dest }} up -d {{ compose.containers.cast.name }}" + chdir: "{{ new_compose_path.dest }}{{ source_code.repos.compose.rpath | default('/independent') }}" + - name: Adding Caddyfile subdomain entry + when: "server_run and mode == 'prod'" + ansible.builtin.blockinfile: + path: "{{ ansible_user_home.stdout }}/.config/caddy/Caddyfile" + block: | + cast.{{ web_fqdn }} { + reverse_proxy localhost:8080 + + handle_errors { + root /srv/{{ web_fqdn }} + rewrite /error/{err.status_code}.html + file_server + templates + encode gzip + } + } + prepend_newline: true + marker: "# <-- {mark} ANSIBLE MANAGED CAST DOMAIN -->" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + create: true + mode: "644" + state: present + - name: Adding Caddyfile subdomain entry + when: "server_run and (mode == 'dev' or caddy.scheme == 'http')" + ansible.builtin.blockinfile: + path: "{{ ansible_user_home.stdout }}/.config/caddy/Caddyfile" + block: | + http://cast.{{ web_fqdn }} { + reverse_proxy localhost:8080 + + handle_errors { + root /srv/{{ web_fqdn }} + rewrite /error/{err.status_code}.html + file_server + templates + encode gzip + } + } + prepend_newline: true + marker: "# <-- {mark} ANSIBLE MANAGED CAST DOMAIN -->" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + create: true + mode: "644" + state: present - name: Spinning up web or reverse proxy server container specified by Compose file when: server_run block: @@ -589,19 +981,59 @@ - certbot - notes - blog + - name: Creating a ZFS dataset for Caddy container configuration + become: true + when: zfs_present + community.general.zfs: + name: "{{ preferred_zpool.user_input }}/caddy_config" + state: present + extra_zfs_properties: + canmount: "on" + mountpoint: "{{ ansible_user_home.stdout }}/.caddy" - name: Creating directory for Caddy configuration ansible.builtin.file: path: "{{ ansible_user_home.stdout }}/.caddy" recurse: true owner: "{{ ansible_user }}" group: "{{ ansible_user }}" + mode: "755" state: directory - - name: Creating and deploying webserver / reverse proxy server container specified by Compose file + - name: Allowing rate limiting concurrent connections per IP on start + # when: "source_code.repos.compose.rpath == '/dependent' and vpn_run and (((fqdn is defined and fqdn != None) and fqdn in item.name) or (item.name in groups.homeserver or item.name in groups['homeserver.test']))" + when: "source_code.repos.compose.rpath == '/dependent' and vpn_run and ((inventory_hostname in item.name or web_fqdn in item.name) or correct_domain)" + ansible.builtin.blockinfile: + path: "{{ ansible_user_home.stdout }}/.wg/authorized_clients.d/{{ item.name }}{{ (idx | string) }}.conf" + marker: "# <-- {mark} ANSIBLE OPEN PORT 80 -->" + block: | + PreUp=iptables -A INPUT -p tcp --dport 80 -j ACCEPT + PreUp=ip6tables -A INPUT -p tcp --dport 80 -j ACCEPT + insertafter: "^PreUp ?= ?" + state: present + loop: "{{ vpn.clients }}" + loop_control: + index_var: idx + - name: Disabling rate limiting concurrent connections per IP on stop + # when: "source_code.repos.compose.rpath == '/dependent' and vpn_run and (((fqdn is defined and fqdn != None) and fqdn in item.name) or (item.name in groups.homeserver or item.name in groups['homeserver.test']))" + when: "source_code.repos.compose.rpath == '/dependent' and vpn_run and ((inventory_hostname in item.name or web_fqdn in item.name) or correct_domain)" + ansible.builtin.blockinfile: + path: "{{ ansible_user_home.stdout }}/.wg/authorized_clients.d/{{ item.name }}{{ (idx | string) }}.conf" + marker: "# <-- {mark} ANSIBLE CLOSE PORT 80 -->" + block: | + PostDown=iptables -D INPUT -p tcp --dport 80 -j ACCEPT + PostDown=ip6tables -D INPUT -p tcp --dport 80 -j ACCEPT + insertafter: "^PostDown ?= ?" + state: present + loop: "{{ vpn.clients }}" + loop_control: + index_var: idx + - name: Restarting VPN container + when: "source_code.repos.compose.rpath == '/dependent' and vpn_run" become: true become_user: "{{ current_user.stdout }}" - ansible.builtin.command: - cmd: "podman-compose --env-file {{ environ.dest }} up -d {{ compose.containers.webserver.name }}" - chdir: "{{ new_compose_path.dest }}{{ source_code.repos.compose.rpath | default('/independent') }}" + containers.podman.podman_container: + name: "{{ compose.containers.vpn.name }}" + state: started + force_restart: true - name: Creating volume subdirectory for main website root's error pages ansible.builtin.file: path: "{{ ansible_user_home.stdout }}/srv/{{ web_fqdn }}/error" @@ -716,34 +1148,12 @@ group: "{{ ansible_user }}" mode: "644" state: present - - name: Opening port 443 + - name: Creating and deploying webserver / reverse proxy server container specified by Compose file become: true - ansible.builtin.iptables: - chain: INPUT - protocol: "{{ item }}" - destination_port: 443 - jump: ACCEPT - comment: Open up port 443 - loop: - - udp - - tcp - - name: Opening ports - become: true - ansible.builtin.iptables: - chain: INPUT - protocol: tcp - destination_port: "{{ item }}" - jump: ACCEPT - comment: "Open up port {{ (item | string) }}" - loop: - - 80 - # - name: Restarting webserver / reverse proxy container - # become: true - # become_user: "{{ current_user.stdout }}" - # containers.podman.podman_container: - # name: "{{ compose.containers.webserver.name }}" - # state: started - # force_restart: true + become_user: "{{ current_user.stdout }}" + ansible.builtin.command: + cmd: "podman-compose --env-file {{ environ.dest }} up -d {{ compose.containers.webserver.name }}" + chdir: "{{ new_compose_path.dest }}{{ source_code.repos.compose.rpath | default('/independent') }}" - name: Spinning up ACME challenge container specified by Compose file when: ssl_run ignore_errors: true @@ -760,19 +1170,80 @@ force: true backup: true register: certbot_environ + - name: Allowing rate limiting concurrent connections per IP on start + # when: "source_code.repos.compose.rpath == '/dependent' and vpn_run and (((fqdn is defined and fqdn != None) and fqdn in item.name) or (item.name in groups.homeserver or item.name in groups['homeserver.test']))" + when: "source_code.repos.compose.rpath == '/dependent' and vpn_run and ((inventory_hostname in item.name or web_fqdn in item.name) or correct_domain)" + ansible.builtin.blockinfile: + path: "{{ ansible_user_home.stdout }}/.wg/authorized_clients.d/{{ item.name }}{{ (idx | string) }}.conf" + marker: "# <-- {mark} ANSIBLE OPEN PORT 443 -->" + block: | + PreUp=iptables -A INPUT -p tcp --dport 443 -j ACCEPT + PreUp=iptables -A INPUT -p udp --dport 443 -j ACCEPT + PreUp=ip6tables -A INPUT -p tcp --dport 443 -j ACCEPT + PreUp=ip6tables -A INPUT -p udp --dport 443 -j ACCEPT + insertafter: "^PreUp ?= ?" + state: present + loop: "{{ vpn.clients }}" + loop_control: + index_var: idx + - name: Disabling rate limiting concurrent connections per IP on stop + # when: "source_code.repos.compose.rpath == '/dependent' and vpn_run and (((fqdn is defined and fqdn != None) and fqdn in item.name) or (item.name in groups.homeserver or item.name in groups['homeserver.test']))" + when: "source_code.repos.compose.rpath == '/dependent' and vpn_run and ((inventory_hostname in item.name or web_fqdn in item.name) or correct_domain)" + ansible.builtin.blockinfile: + path: "{{ ansible_user_home.stdout }}/.wg/authorized_clients.d/{{ item.name }}{{ (idx | string) }}.conf" + marker: "# <-- {mark} ANSIBLE CLOSE PORT 443 -->" + block: | + PostDown=iptables -D INPUT -p tcp --dport 443 -j ACCEPT + PostDown=iptables -D INPUT -p udp --dport 443 -j ACCEPT + PostDown=ip6tables -D INPUT -p tcp --dport 443 -j ACCEPT + PostDown=ip6tables -D INPUT -p udp --dport 443 -j ACCEPT + insertafter: "^PostDown ?= ?" + state: present + loop: "{{ vpn.clients }}" + loop_control: + index_var: idx + - name: Restarting VPN container + when: "source_code.repos.compose.rpath == '/dependent' and vpn_run" + become: true + become_user: "{{ current_user.stdout }}" + containers.podman.podman_container: + name: "{{ compose.containers.vpn.name }}" + state: started + force_restart: true - name: Creating and deploying SSL/TLS certificate acquisition container specified by Compose file become: true become_user: "{{ current_user.stdout }}" ansible.builtin.command: cmd: "podman-compose --env-file {{ certbot_environ.dest }} --env-file {{ environ.dest }} up -d {{ compose.containers.ssl.name }}" chdir: "{{ new_compose_path.dest }}{{ source_code.repos.compose.rpath | default('/independent') }}" +- name: Spinning up synchronized playback container specified by Compose file + when: (compose.containers.syncplay is defined and compose.containers.syncplay != None) and syncplay.containerized and source_code.repos.compose.rpath == '/dependent' + block: + - name: Creating environment file for synchronized playback container specified in Compose file + become: true + become_user: "{{ current_user.stdout }}" + ansible.builtin.template: + src: user/syncplay.env.j2 + dest: "{{ ansible_user_home.stdout }}/.syncplay.env" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + mode: "644" + force: true + backup: true + register: syncplay_environ + - name: Creating and deploying synchronized playback container specified by Compose file + become: true + become_user: "{{ current_user.stdout }}" + ansible.builtin.command: + cmd: "podman-compose --env-file {{ syncplay_environ.dest }} --env-file {{ environ.dest }} up -d {{ compose.containers.syncplay.name }}" + chdir: "{{ new_compose_path.dest }}{{ source_code.repos.compose.rpath | default('/independent') }}" - name: Spinning up tailnet container specified by Compose file - when: tail_run + when: "tail_run and (source_code.repos.compose.rpath == '/dependent')" block: - name: Creating required tailnet container secret for tailnet container specified by Compose file become: true - # become_user: "{{ current_user.stdout }}" - when: ((fqdn is defined and fqdn != None) and item.name == fqdn) or item.name == inventory_hostname + # when: ((fqdn is defined and fqdn != None) and fqdn in item.name) or (item.name in groups.homeserver or item.name in groups['homeserver.test']) + when: "((inventory_hostname in item.name or web_fqdn in item.name) or correct_domain)" ansible.builtin.copy: src: "{{ item.name }}-{{ headscale.users.admin.username }}@headscale/headscale{{ (idx | string) }}.key" dest: "{{ ansible_user_home.stdout }}/.podsecrets/headscale.key" @@ -792,6 +1263,8 @@ dest: "{{ ansible_user_home.stdout }}/.config/tailscale/conf.json" owner: "{{ ansible_user }}" group: "{{ ansible_user }}" + block_start_string: "<%" + block_end_string: "%>" mode: "644" force: true backup: true @@ -799,10 +1272,10 @@ become: true become_user: "{{ current_user.stdout }}" ansible.builtin.command: - cmd: "podman-compose --env-file {{ environ.dest }} up -d {{ compose.containers.tail.name }}" + cmd: "podman-compose --env-file {{ environ.dest }} --env-file {{ stash_environ.dest }} --env-file {{ actual_environ.dest }} up -d {{ compose.containers.tail.name }}" chdir: "{{ new_compose_path.dest }}{{ source_code.repos.compose.rpath | default('/independent') }}" - name: Spinning up dashboard container specified by Compose file - when: tail_run and dash_run + when: dash_run block: - name: Creating glance configuration file become: true @@ -821,16 +1294,253 @@ ansible.builtin.command: cmd: "podman-compose up -d {{ compose.containers.dash.name }}" chdir: "{{ new_compose_path.dest }}{{ source_code.repos.compose.rpath | default('/independent') }}" -- name: Restarting webserver / reverse proxy container - become: true - become_user: "{{ current_user.stdout }}" - when: server_run - containers.podman.podman_container: - name: "{{ compose.containers.webserver.name }}" - state: started - force_restart: true +- name: Spinning up download manager container specified by Compose file + when: "source_code.repos.compose.run and (compose.containers.surge is defined and compose.containers.surge != None)" + block: + - name: Creating and deploying download manager container specified by Compose file + become: true + become_user: "{{ current_user.stdout }}" + ansible.builtin.command: + cmd: "podman-compose up -d {{ compose.containers.surge.name }}" + chdir: "{{ new_compose_path.dest }}{{ source_code.repos.compose.rpath | default('/independent') }}" + - name: Acquiring Surge service token + become: true + become_user: "{{ current_user.stdout }}" + containers.podman.podman_container_exec: + name: "{{ compose.containers.surge.name }}" + argv: + - surge + - token + detach: true + register: surge_token + - name: Creating token file for container + ansible.builtin.copy: + content: "{{ surge_token.stdout }}" + dest: "{{ ansible_user_home.stdout }}/.local/surge/token" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + mode: "600" + register: surge_token_file + - name: Restarting Surge container + become: true + become_user: "{{ current_user.stdout }}" + containers.podman.podman_container: + name: "{{ compose.containers.surge.name }}" + state: started + force_restart: true + - name: Pausing to inquire about how to proceed + ansible.builtin.pause: + prompt: "Type \"fetch\" to get the Surge API token, or \"show\" to see it for manual copying instead" + echo: true + register: data_method + - name: Presenting Surge API token to Control Node + when: data_method.user_input == "show" + ansible.builtin.debug: + msg: "Make sure to store the following API token for Surge:\n {{ surge_token.stdout }}" + - name: Giving opportunity to manually copy Surge API token + when: data_method.user_input == "show" + ansible.builtin.pause: + - name: Placing Surge API token into file on control node + when: data_method.user_input == "fetch" + ansible.builtin.fetch: + src: "{{ surge_token_file.dest }}" + dest: "./.tmp/{{ inventory_hostname }}-surge/surge.secret" + flat: true +- name: Spinning up Aria2 container specified by Compose file + when: "source_code.repos.compose.run and aria.containerized and (compose.containers.aria is defined and compose.containers.aria != None)" + block: + - name: Creating required SMTP-related container secret + become: true + ansible.builtin.copy: + content: "{{ aria.rpc.secret }}" + dest: "{{ ansible_user_home.stdout }}/.podsecrets/aria.token" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + mode: "644" + backup: true + - name: Creating environment file for Aria2 container specified in Compose file + become: true + become_user: "{{ current_user.stdout }}" + ansible.builtin.template: + src: user/aria.env.j2 + dest: "{{ ansible_user_home.stdout }}/.aria.env" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + mode: "644" + force: true + backup: true + register: aria_environ + - name: Creating directory to store Aria2 configuration file + ansible.builtin.file: + path: "{{ ansible_user_home.stdout }}/.aria2" + recurse: true + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + mode: "755" + state: directory + - name: Creating Aria2 configuration + become: true + become_user: "{{ current_user.stdout }}" + ansible.builtin.template: + src: user/aria2/aria2.conf.j2 + dest: "{{ ansible_user_home.stdout }}/.aria2/aria2.conf" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + mode: "644" + force: true + backup: true + # validate: "aria2c --check" + - name: Creating and deploying Aria2 container specified by Compose file + become: true + become_user: "{{ current_user.stdout }}" + ansible.builtin.command: + cmd: "podman-compose --env-file {{ aria_environ.dest }} up -d {{ compose.containers.aria.name }}" + chdir: "{{ new_compose_path.dest }}{{ source_code.repos.compose.rpath | default('/independent') }}" +- name: Spinning up Aria2 Web UI container specified by Compose file + when: "source_code.repos.compose.run and (compose.containers.ariang is defined and compose.containers.ariang != None)" + block: + - name: Creating and deploying Aria2 Web UI container specified by Compose file + become: true + become_user: "{{ current_user.stdout }}" + ansible.builtin.command: + cmd: "podman-compose up -d {{ compose.containers.ariang.name }}" + chdir: "{{ new_compose_path.dest }}{{ source_code.repos.compose.rpath | default('/independent') }}" +- name: Spinning up download automator container specified by Compose file + when: "source_code.repos.compose.run and (compose.containers.flexget is defined and compose.containers.flexget != None) and (flexget.enabled and flexget.containerized)" + block: + - name: Creating requisite download paths + ansible.builtin.file: + path: "{{ ansible_user_home.stdout }}/media/vids/{{ item }}" + recurse: true + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + state: directory + mode: "755" + loop: + - series + - features + - name: Creating requisite download paths + ansible.builtin.file: + path: "{{ ansible_user_home.stdout }}/downloads/flexget/vids/series/torrents" + recurse: true + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + state: directory + mode: "755" + - name: Creating requisite download paths + ansible.builtin.file: + path: "{{ ansible_user_home.stdout }}/downloads/flexget/vids/torrents" + recurse: true + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + state: directory + mode: "755" + - name: Creating requisite hidden download paths + ansible.builtin.file: + path: "{{ ansible_user_home.stdout }}/downloads/.xxx/flexget/vids/torrents" + recurse: true + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + state: directory + mode: "755" + - name: Creating requisite hidden download paths + ansible.builtin.file: + path: "{{ ansible_user_home.stdout }}/downloads/media/vids/{{ item }}" + recurse: true + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + state: directory + mode: "755" + loop: + - series + - features + - name: Creating requisite hidden file for film torrent links + ansible.builtin.file: + path: "{{ ansible_user_home.stdout }}/downloads/media/vids/features/.films.csv" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + state: touch + mode: "644" + - name: Creating requisite hidden download paths + ansible.builtin.file: + path: "{{ ansible_user_home.stdout }}/downloads/.xxx/media/vids" + recurse: true + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + state: directory + mode: "755" + - name: Creating requisite hidden file for NSFW video torrent links + ansible.builtin.file: + path: "{{ ansible_user_home.stdout }}/.xxx/.vids.csv" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + state: touch + mode: "644" + - name: Configuring flexget + become: true + become_user: "{{ current_user.stdout }}" + ansible.builtin.template: + src: roles/init-server/templates/user/flexget/config.yml.j2 + variable_start_string: "<<" + variable_end_string: ">>" + dest: "{{ ansible_user_home.stdout }}/.flexget/config.yml" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + force: true + backup: true + # validate: "flexget check" + - name: Creating and deploying downlaod automator container specified by Compose file + become: true + become_user: "{{ current_user.stdout }}" + ansible.builtin.command: + cmd: "podman-compose up -d {{ compose.containers.flexget.name }}" + chdir: "{{ new_compose_path.dest }}{{ source_code.repos.compose.rpath | default('/independent') }}" +- name: Spinning up file-sharing container specified by Compose file + when: "source_code.repos.compose.run and (compose.containers.soulseek is defined and compose.containers.soulseek != None)" + block: + - name: Creating and deploying file-sharing container specified by Compose file + become: true + become_user: "{{ current_user.stdout }}" + ansible.builtin.command: + cmd: "podman-compose up -d {{ compose.containers.soulseek.name }}" + chdir: "{{ new_compose_path.dest }}{{ source_code.repos.compose.rpath | default('/independent') }}" +- name: Spinning up grocy container specified by Compose file + when: "source_code.repos.compose.run and (compose.containers.grocy is defined and compose.containers.grocy != None)" + block: + - name: Creating and deploying download manager container specified by Compose file + become: true + become_user: "{{ current_user.stdout }}" + ansible.builtin.command: + cmd: "podman-compose up -d {{ compose.containers.grocy.name }}" + chdir: "{{ new_compose_path.dest }}{{ source_code.repos.compose.rpath | default('/independent') }}" +- name: Spinning up financial budgeting container specified by Compose file + when: "source_code.repos.compose.run and actual.containerized and (compose.containers.budget is defined and compose.containers.budget != None)" + block: + - name: Creating and deploying financial budgeting container specified by Compose file + become: true + become_user: "{{ current_user.stdout }}" + ansible.builtin.command: + cmd: "podman-compose --env-file {{ actual_environ.dest }} up -d {{ compose.containers.budget.name }}" + chdir: "{{ new_compose_path.dest }}{{ source_code.repos.compose.rpath | default('/independent') }}" +- name: Spinning up ebook library container specified by Compose file + when: "source_code.repos.compose.run and kavita.containerized and (compose.containers.reader is defined and compose.containers.reader != None)" + block: + - name: Creating and deploying ebook library container specified by Compose file + become: true + become_user: "{{ current_user.stdout }}" + ansible.builtin.command: + cmd: "podman-compose up -d {{ compose.containers.reader.name }}" + chdir: "{{ new_compose_path.dest }}{{ source_code.repos.compose.rpath | default('/independent') }}" +- name: Spinning up NSFW streaming container specified by Compose file + when: "source_code.repos.compose.run and stash.containerized and (compose.containers.stash is defined and compose.containers.stash != None)" + block: + - name: Creating and deploying NSFW streaming container specified by Compose file + become: true + become_user: "{{ current_user.stdout }}" + ansible.builtin.command: + cmd: "podman-compose --env-file {{ stash_environ.dest }} up -d {{ compose.containers.stash.name }}" + chdir: "{{ new_compose_path.dest }}{{ source_code.repos.compose.rpath | default('/independent') }}" - name: Making running containers persist on user logout become: true ansible.builtin.shell: - cmd: loginctl enable-linger $(whoami) - + cmd: loginctl enable-linger $(whoami) \ No newline at end of file diff --git a/roles/init-server/tasks/contingent/pkg/ufw.yml b/roles/init-server/tasks/contingent/pkg/ufw.yml new file mode 100644 index 0000000..881f51b --- /dev/null +++ b/roles/init-server/tasks/contingent/pkg/ufw.yml @@ -0,0 +1,72 @@ +- name: Getting public IP addresses + block: + - name: Getting public IPv4 address + community.general.ipify_facts: + validate_certs: false + timeout: 20 + - name: Getting public IPv6 address + when: ipv6_enabled + ansible.builtin.shell: + cmd: curl -6 icanhazip.com + register: ipify_public_ip6 +- name: Getting available network interfaces + ansible.builtin.shell: + cmd: "ip link show | awk '/^[0-9]+: [a-z]/ { print $2 }' | grep -vE 'lo[0-9]*|wg[0-9]+-mullvad' | sed 's/://g'" + register: intrfaces +- name: Turning acquired network interfaces into list + ansible.builtin.set_fact: + main_interfaces: "{{ intrfaces.stdout | replace('\n', ' ') | split(' ') }}" +- name: Enabling UFW startup + become: true + ansible.builtin.systemd_service: + name: ufw + scope: system + state: started + enabled: true +- name: Enabling firewall startup + community.general.ufw: + state: enabled +- name: Pre-emptively establishing firewall rules for loopback interface + become: true + block: + - name: Pre-emptively allowing inbound connections on loopback interface + community.general.ufw: + rule: allow + direction: in + interface: lo + - name: Allowing outbound connections on loopback interface + community.general.ufw: + rule: allow + direction: out + interface: lo + - name: Denying inbound connections from loopback address on rest of interfaces + community.general.ufw: + rule: deny + direction: in + interface: "{{ item[1] }}" + from_ip: "{{ item[0] }}" + loop: "{{ ['127.0.0.0/8', '::/128'] | product(main_interfaces) }}" +- name: Pre-emptively allowing rate-limited inbound SSH + become: true + community.general.ufw: + rule: allow + name: ssh +- name: Crafting additional exceptions to the inbound default policy + become: true + community.general.ufw: + rule: allow + direction: in + proto: "{{ item[1].protocol | default('tcp') }}" + interface: "{{ item[1].interface | default(item[0]) }}" + port: "{{ item[1].port }}" + loop: "{{ main_interfaces | product(iptables.allow.inbound) }}" +- name: Setting default policy for inbound connections + become: true + community.general.ufw: + direction: incoming + default: deny +- name: Setting default policy for outbound connections + become: true + community.general.ufw: + direction: outgoing + default: allow \ No newline at end of file diff --git a/roles/init-server/tasks/contingent/pkg/uv.yml b/roles/init-server/tasks/contingent/pkg/uv.yml index c93329d..c87163b 100644 --- a/roles/init-server/tasks/contingent/pkg/uv.yml +++ b/roles/init-server/tasks/contingent/pkg/uv.yml @@ -15,7 +15,7 @@ ansible.builtin.shell: cmd: "{{ ansible_user_home.stdout }}/.local/bin/uv-install.sh" creates: "{{ ansible_user_home.stdout }}/.local/bin/uv" - async: 600 + async: 300 poll: 5 - name: Linking binaries to directories already in PATH environment variable become: true diff --git a/roles/init-server/tasks/contingent/pkg/wireguard.yml b/roles/init-server/tasks/contingent/pkg/wireguard.yml index 8a3a124..ea9882c 100644 --- a/roles/init-server/tasks/contingent/pkg/wireguard.yml +++ b/roles/init-server/tasks/contingent/pkg/wireguard.yml @@ -13,25 +13,33 @@ ansible.posix.sysctl: name: net.ipv4.ip_forward value: "1" + sysctl_file: /etc/sysctl.d/10-security.conf sysctl_set: true + reload: true state: present - name: Marking IPv4 forwarded traffic as valid ansible.posix.sysctl: name: net.ipv4.conf.all.src_valid_mark value: "1" + sysctl_file: /etc/sysctl.d/10-security.conf sysctl_set: true + reload: true state: present - name: Enabling IPv6 forwarding ansible.posix.sysctl: name: net.ipv6.conf.all.forwarding value: "1" + sysctl_file: /etc/sysctl.d/10-security.conf sysctl_set: true + reload: true state: present - name: Marking IPv6 traffic as valid ansible.posix.sysctl: name: net.ipv6.conf.all.src_valid_mark value: "1" + sysctl_file: /etc/sysctl.d/10-security.conf sysctl_set: true + reload: true state: present - name: Preparing to allow Wireguard logging when: "wireguard.debug" diff --git a/roles/init-server/tasks/core@install-pkgs.yml b/roles/init-server/tasks/core@install-pkgs.yml index 2f3e986..90bd5e0 100644 --- a/roles/init-server/tasks/core@install-pkgs.yml +++ b/roles/init-server/tasks/core@install-pkgs.yml @@ -38,6 +38,13 @@ owner: "{{ ansible_user }}" group: "{{ ansible_user }}" state: directory +- name: Creating prerequisite directory tree for git repos + ansible.builtin.file: + path: "{{ ansible_user_home.stdout }}/repos/.foreign" + recurse: true + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + state: directory - name: Installing Linux software when: ansible_facts["system"] == "Linux" block: diff --git a/roles/init-server/tasks/netrc.yml b/roles/init-server/tasks/netrc.yml index 7d111c9..163789a 100644 --- a/roles/init-server/tasks/netrc.yml +++ b/roles/init-server/tasks/netrc.yml @@ -17,4 +17,6 @@ dest: "{{ ansible_user_home.stdout }}/.netrc" owner: "{{ ansible_user }}" group: "{{ ansible_user }}" - mode: "600" \ No newline at end of file + mode: "600" + force: true + backup: true \ No newline at end of file diff --git a/roles/init-server/templates/user/actual.env.j2 b/roles/init-server/templates/user/actual.env.j2 new file mode 100644 index 0000000..eb0d6e1 --- /dev/null +++ b/roles/init-server/templates/user/actual.env.j2 @@ -0,0 +1 @@ +ACTUAL_LISTEN_PORT={{ actual.port }} \ No newline at end of file diff --git a/roles/init-server/templates/user/all.env.j2 b/roles/init-server/templates/user/all.env.j2 index 2d4aa4d..1941a32 100644 --- a/roles/init-server/templates/user/all.env.j2 +++ b/roles/init-server/templates/user/all.env.j2 @@ -9,9 +9,15 @@ SERVER_FQDN={{ (certbot.domains | reject("regex", "^\\*\\.") | list)[0] }} {% if nextcloud.subdomain is defined and nextcloud != None %} NEXTCLOUD_SUBDOMAIN={{ nextcloud.subdomain }} {% endif %} -{% if gitea is defined and gitea != None %} -GITEA_SUBDOMAIN={{ gitea.subdomain }} +{% if forgejo is defined and forgejo != None %} +FORGEJO_SUBDOMAIN={{ forgejo.subdomain }} {% endif %} {% if gist is defined and gist != None %} OG_SUBDOMAIN={{ gist.subdomain }} +{% endif %} +{% if syncplay is defined and syncplay != None %} +SYNCPLAY_SUBDOMAIN={{ syncplay.subdomain }} +{% endif %} +{% if nodebb is defined and nodebb != None %} +NODEBB_SUBDOMAIN={{ nodebb.subdomain }} {% endif %} \ No newline at end of file diff --git a/roles/init-server/templates/user/aria.env.j2 b/roles/init-server/templates/user/aria.env.j2 new file mode 100644 index 0000000..23e710c --- /dev/null +++ b/roles/init-server/templates/user/aria.env.j2 @@ -0,0 +1,9 @@ +{% if aria.rpc.enabled %} +ARIA_RPC_LISTEN_PORT={{ aria.rpc.port }} +# {% if aria.rpc.credentials is undefined or aria.rpc.credentials == None %} +# ARIA_RPC_SECRET_TOKEN={{ aria.rpc.secret }} +# {% endif %} +{% endif %} +{% if aria.trnt.dht.enabled %} +ARIA_TRNT_LISTEN_PORT={{ aria.trnt.dht.port }} +{% endif %} \ No newline at end of file diff --git a/roles/init-server/templates/user/aria2/aria2.conf.j2 b/roles/init-server/templates/user/aria2/aria2.conf.j2 index 02dbc00..e0f4977 100644 --- a/roles/init-server/templates/user/aria2/aria2.conf.j2 +++ b/roles/init-server/templates/user/aria2/aria2.conf.j2 @@ -37,7 +37,7 @@ max-resume-failure-tries=0 {% if aria.rpc.enabled %} enable-rpc=true -{% if mode == "prod" or aria.rpc.scheme == "https" %} +{% if mode == "prod" and aria.rpc.scheme == "https" %} rpc-secure=true {% if not certbot.containerized %} @@ -59,7 +59,7 @@ rpc-listen-all=false rpc-listen-port={{ aria.rpc.port | string }} {% if aria.rpc.credentials is undefined or aria.rpc.credentials == None %} -rpc-secret={{ aria.rpc.secret | default(lookup('password', './.tmp/' + inventory_hostname + '-aria/aria.secret', chars=['ascii_lowercase', 'digits'], length=32)) }} +rpc-secret={{ aria.rpc.secret }} {% else %} rpc-user={{ aria.rpc.credentials.username }} rpc-password={{ aria.rpc.credentials.password }} diff --git a/roles/init-server/templates/user/config/caddy/Caddyfile.j2 b/roles/init-server/templates/user/config/caddy/Caddyfile.j2 index c12c280..61df652 100644 --- a/roles/init-server/templates/user/config/caddy/Caddyfile.j2 +++ b/roles/init-server/templates/user/config/caddy/Caddyfile.j2 @@ -7,7 +7,7 @@ http://<< web_fqdn >> { } } :80 { - root /srv/certbot.<< web_fqdn >> + root ~/srv/certbot.<< web_fqdn >> browse } << web_fqdn >> { diff --git a/roles/init-server/templates/user/config/glance/glance.yml.j2 b/roles/init-server/templates/user/config/glance/glance.yml.j2 index 20b0330..f0a2948 100644 --- a/roles/init-server/templates/user/config/glance/glance.yml.j2 +++ b/roles/init-server/templates/user/config/glance/glance.yml.j2 @@ -1,9 +1,10 @@ server: port: {{ glance.port }} - {% if glance.proxied %} +{% if glance.proxied %} proxied: true - {% endif %} +{% endif %} base-url: / + assets-path: /app/assets pages: - name: home head-widgets: @@ -48,7 +49,7 @@ pages: url: https://hexdocs.pm/?q={QUERY}&packages=plug%3Alatest%2Cphoenix%3Alatest%2Cphoenix_html%3Alatest%2Cphoenix_live_view%3Alatest%2Cphoenix_pubsub%3Alatest%2Cphoenix_template%3Alatest - title: Javascript shortcut: "@js" - shortcut: https://javascript.info/search?query={QUERY} + url: https://javascript.info/search?query={QUERY} - title: MDN shortcut: "@mdn" url: https://developer.mozilla.org/en-US/search?q={QUERY} @@ -330,10 +331,3 @@ pages: - UCDG73pGqESS1XcEVY_0xwWw - UCR2uRTQ53V_egXKFflMMaaw - UCzGUT9PjV3SMBwjWXUYh4HA - - name: services - columns: - - size: full - widgets: - - type: docker-containers - title: status - hide-by-default: false diff --git a/roles/init-server/templates/user/config/opengist/config.yml.j2 b/roles/init-server/templates/user/config/opengist/config.yml.j2 index 1353f9a..8f13ebe 100644 --- a/roles/init-server/templates/user/config/opengist/config.yml.j2 +++ b/roles/init-server/templates/user/config/opengist/config.yml.j2 @@ -6,4 +6,4 @@ log-level: info {% endif %} custom.static-links: - name: Playbooks - path: {{ "https://" + gitea.subdomain + "." + web_fqdn + "/admin/skato-ansible" }} \ No newline at end of file + path: {{ "https://" + forgejo.subdomain + "." + web_fqdn + "/admin/skato-ansible" }} \ No newline at end of file diff --git a/roles/init-server/templates/user/config/tailscale/conf.json.j2 b/roles/init-server/templates/user/config/tailscale/conf.json.j2 index 4839ede..8c38147 100644 --- a/roles/init-server/templates/user/config/tailscale/conf.json.j2 +++ b/roles/init-server/templates/user/config/tailscale/conf.json.j2 @@ -18,6 +18,48 @@ "Proxy": "http://localhost:8080" } } + }, + "aria.${TS_CERT_DOMAIN}:443": { + "Handlers": { + "/": { + "Proxy": "http://localhost:6880" + } + } + }, + "slsk.${TS_CERT_DOMAIN}:443": { + "Handlers": { + "/": { + "Proxy": "http://localhost:5030" + } + } + }, + "grocy.${TS_CERT_DOMAIN}:443": { + "Handlers": { + "/": { + "Proxy": "http://localhost:80" + } + } + }, + "budget.${TS_CERT_DOMAIN}:443": { + "Handlers": { + "/": { + "Proxy": "http://localhost:5006" + } + } + }, + "reader.${TS_CERT_DOMAIN}:443": { + "Handlers": { + "/": { + "Proxy": "http://localhost:9999" + } + } + }, + "stash.${TS_CERT_DOMAIN}:443": { + "Handlers": { + "/": { + "Proxy": "http://localhost:5000" + } + } } <% elif mode == "dev" or tailscale.scheme == "http" %> "${TS_CERT_DOMAIN}:80": { @@ -26,6 +68,48 @@ "Proxy": "http://localhost:8080" } } + }, + "aria.${TS_CERT_DOMAIN}:80": { + "Handlers": { + "/": { + "Proxy": "http://localhost:6880" + } + } + }, + "slsk.${TS_CERT_DOMAIN}:80": { + "Handlers": { + "/": { + "Proxy": "http://localhost:5030" + } + } + }, + "grocy.${TS_CERT_DOMAIN}:80": { + "Handlers": { + "/": { + "Proxy": "http://localhost:80" + } + } + }, + "budget.${TS_CERT_DOMAIN}:80": { + "Handlers": { + "/": { + "Proxy": "http://localhost:5006" + } + } + }, + "reader.${TS_CERT_DOMAIN}:80": { + "Handlers": { + "/": { + "Proxy": "http://localhost:9999" + } + } + }, + "stash.${TS_CERT_DOMAIN}:80": { + "Handlers": { + "/": { + "Proxy": "http://localhost:5000" + } + } } <% endif %> }, diff --git a/roles/init-server/templates/user/flexget/config.yml.j2 b/roles/init-server/templates/user/flexget/config.yml.j2 index 91003e1..ae9169d 100644 --- a/roles/init-server/templates/user/flexget/config.yml.j2 +++ b/roles/init-server/templates/user/flexget/config.yml.j2 @@ -36,7 +36,7 @@ templates: tasks: # @NOTE uncommenting aria2 block requires commenting out download and proxy blocks populate_folders: - {% if download_mode == 'direct' %} + {% if flexget.download_mode == 'direct' %} proxy: https: 'socks5://<< tor.proxy.hostname >>:<< tor.proxy.port >>' {% endif %} @@ -63,7 +63,7 @@ tasks: template: limited_series {% if flexget.download_mode == 'aria2' %} aria2: - server: << aria.rpc.hostname >> + server: << aria.rpc.hostname | default((fqdn | default(inventory_hostname))) >> port: << aria.rpc.port >> {% if aria.rpc.secret != None and aria.rpc.secret is defined %} secret: << aria.rpc.secret >> @@ -71,8 +71,8 @@ tasks: username: << aria.rpc.credentials.username >> password: << aria.rpc.credentials.password >> {% endif %} - {% if mode == 'prod' %} - scheme: << aria.rpc.scheme >> + {% if aria.rpc.scheme == 'https' and mode == 'prod' %} + scheme: https {% else %} scheme: http {% endif %} @@ -86,14 +86,14 @@ tasks: {% endif %} # @NOTE uncommenting aria2 block requires commenting out download and proxy blocks fap_queue: - {% if download_mode == 'direct' %} + {% if flexget.download_mode == 'direct' %} proxy: https: 'socks5://<< tor.proxy.hostname >>:<< tor.proxy.port >>' {% endif %} template: porn_vids {% if flexget.download_mode == 'aria2' %} aria2: - server: << aria.rpc.hostname >> + server: << aria.rpc.hostname | default((fqdn | default(inventory_hostname))) >> port: << aria.rpc.port >> {% if aria.rpc.secret != None and aria.rpc.secret is defined %} secret: << aria.rpc.secret >> @@ -102,8 +102,8 @@ tasks: username: << aria.rpc.credentials.username >> password: << aria.rpc.credentials.password >> {% endif %} - {% if mode == 'prod' %} - scheme: << aria.rpc.scheme >> + {% if aria.rpc.scheme == 'https' and mode == 'prod' %} + scheme: https {% else %} scheme: http {% endif %} @@ -117,14 +117,14 @@ tasks: {% endif %} # @NOTE uncommenting aria2 block requires commenting out download and proxy blocks film_queue: - {% if download_mode == 'direct' %} + {% if flexget.download_mode == 'direct' %} proxy: https: 'socks5://<< tor.proxy.hostname >>:<< tor.proxy.port >>' {% endif %} template: feature_films {% if flexget.download_mode == 'aria2' %} aria2: - server: << aria.rpc.hostname >> + server: << aria.rpc.hostname | default((fqdn | default(inventory_hostname))) >> port: << aria.rpc.port >> {% if aria.rpc.secret != None or aria.rpc.secret is defined %} secret: << aria.rpc.secret >> @@ -133,8 +133,8 @@ tasks: username: << aria.rpc.credentials.username >> password: << aria.rpc.credentials.password >> {% endif %} - {% if mode == 'prod' %} - scheme: << aria.rpc.scheme >> + {% if aria.rpc.scheme == 'https' and mode == 'prod' %} + scheme: https {% else %} scheme: http {% endif %} diff --git a/roles/init-server/templates/user/forgejo.env.j2 b/roles/init-server/templates/user/forgejo.env.j2 new file mode 100644 index 0000000..13e1052 --- /dev/null +++ b/roles/init-server/templates/user/forgejo.env.j2 @@ -0,0 +1,28 @@ +FORGEJO_MODE={{ mode | default("dev") }} +FORGEJO_NAME={{ (web_fqdn | split("."))[0].upper() + " Grimoire" }} +FORGEJO_SLOGAN={{ forgejo.description }} +FORGEJO_SSH_PORT={{ forgejo.ssh.port }} +FORGEJO_LANDING={{ forgejo.site.landing }} +FORGEJO_TRUSTED_PROXIES={{ forgejo.trusted.proxies | join(",") }} +FORGEJO_DESC={{ forgejo.description }} +FORGEJO_LOCK_REASONS={{ forgejo.site.lock_reasons | join(",") }} +FORGEJO_KANBAN_COLUMNS={{ forgejo.site.kanban_columns | join(",") }} +FORGEJO_TRIAGE_CATEGORIES={{ forgejo.site.triage_categories | join(",")}} + +{% if compose.mode == "dev" %} +FORGEJO_PROTOCOL=http +{% elif compose.mode == "prod" %} +FORGEJO_PROTOCOL=https +{% endif %} + +{% if forgejo.site.registration.enabled %} +FORGEJO_PROHIBIT_REGISTRATION=false +{% else %} +FORGEJO_PROHIBIT_REGISTRATION=true +{% endif %} + +FORGEJO_DB_TYPE={{ forgejo.db.type }} + +{% if forgejo.db.type == "mysql" %} +FORGEJO_DB_NAME={{ forgejo.db.name }} +{% endif %} \ No newline at end of file diff --git a/roles/init-server/templates/user/gitea.env.j2 b/roles/init-server/templates/user/gitea.env.j2 deleted file mode 100644 index 0e740f1..0000000 --- a/roles/init-server/templates/user/gitea.env.j2 +++ /dev/null @@ -1,17 +0,0 @@ -GITEA_MODE={{ mode | default("dev") }} -GITEA_NAME={{ (web_fqdn | split("."))[0].upper() + " Nous" }} -GITEA_SSH_PORT={{ gitea.ssh.port }} -GITEA_LANDING={{ gitea.site.landing }} -GITEA_TRUSTED_PROXIES={{ gitea.trusted.proxies | join(",") }} - -{% if gitea.site.registration.enabled %} -GITEA_PROHIBIT_REGISTRATION=false -{% else %} -GITEA_PROHIBIT_REGISTRATION=true -{% endif %} - -GITEA_DB_TYPE={{ gitea.db.type }} - -{% if gitea.db.type == "mysql" %} -GITEA_DB_NAME={{ gitea.db.name }} -{% endif %} \ No newline at end of file diff --git a/roles/init-server/templates/user/stash.env.j2 b/roles/init-server/templates/user/stash.env.j2 new file mode 100644 index 0000000..fb20901 --- /dev/null +++ b/roles/init-server/templates/user/stash.env.j2 @@ -0,0 +1 @@ +STASH_LISTEN_PORT={{ stash.port }} \ No newline at end of file diff --git a/roles/init-server/templates/user/syncplay.env.j2 b/roles/init-server/templates/user/syncplay.env.j2 new file mode 100644 index 0000000..46af48f --- /dev/null +++ b/roles/init-server/templates/user/syncplay.env.j2 @@ -0,0 +1,8 @@ +SYNCPLAY_PASSWORD={{ syncplay.password }} +SYNCPLAY_MOTD={{ syncplay.motd }} +{% if mode == "prod" %} +SYNCPLAY_TLS=ON +{% else %} +SYNCPLAY_TLS=OFF +{% endif %} +SYNCPLAY_SALT={{ syncplay.salt }} diff --git a/roles/init-server/templates/usr/local/bin/dsnet-forward.sh.j2 b/roles/init-server/templates/usr/local/bin/dsnet-forward.sh.j2 deleted file mode 100755 index 909d116..0000000 --- a/roles/init-server/templates/usr/local/bin/dsnet-forward.sh.j2 +++ /dev/null @@ -1,22 +0,0 @@ -#!/bin/bash -set -euo pipefail - -if [[ "$1" == "start" ]]; then - /usr/sbin/iptables -A FORWARD -i dsnet -p tcp --sport 80 -j ACCEPT # HTTP - /usr/sbin/iptables -A FORWARD -i dsnet -p tcp --sport 443 -j ACCEPT # HTTPS (HTTP + SSL/TLS) - /usr/sbin/iptables -A FORWARD -i dsnet -p udp --sport 443 -j ACCEPT # HTTPS (HTTP + SSL/TLS) - /usr/sbin/iptables -A FORWARD -i dsnet -p tcp --sport 465 -j ACCEPT # SMTP (send) - /usr/sbin/iptables -A FORWARD -i dsnet -p tcp --sport 587 -j ACCEPT # SMTP (send) - /usr/sbin/iptables -A FORWARD -i dsnet -p tcp --sport 995 -j ACCEPT # POP3 (receive) - /usr/sbin/iptables -A FORWARD -i dsnet -p tcp --sport 993 -j ACCEPT # IMAP (receive) - /usr/sbin/iptables -t nat -A POSTROUTING -o dsnet -j MASQUERADE -elif [[ "$1" == "stop" ]]; then - /usr/sbin/iptables -D FORWARD -i dsnet -p tcp --sport 80 -j ACCEPT # HTTP - /usr/sbin/iptables -D FORWARD -i dsnet -p tcp --sport 443 -j ACCEPT # HTTPS (HTTP + SSL/TLS) - /usr/sbin/iptables -D FORWARD -i dsnet -p udp --sport 443 -j ACCEPT # HTTPS (HTTP + SSL/TLS) - /usr/sbin/iptables -D FORWARD -i dsnet -p tcp --sport 465 -j ACCEPT # SMTP (send) - /usr/sbin/iptables -D FORWARD -i dsnet -p tcp --sport 587 -j ACCEPT # SMTP (send) - /usr/sbin/iptables -D FORWARD -i dsnet -p tcp --sport 995 -j ACCEPT # POP3 (receive) - /usr/sbin/iptables -D FORWARD -i dsnet -p tcp --sport 993 -j ACCEPT # IMAP (receive) - /usr/sbin/iptables -t nat -D POSTROUTING -o dsnet -j MASQUERADE -fi \ No newline at end of file diff --git a/roles/init-server/templates/usr/local/bin/dsnet-routing.sh.j2 b/roles/init-server/templates/usr/local/bin/dsnet-routing.sh.j2 new file mode 100755 index 0000000..64e43e6 --- /dev/null +++ b/roles/init-server/templates/usr/local/bin/dsnet-routing.sh.j2 @@ -0,0 +1,28 @@ +#!/bin/bash +set -euo pipefail + +MASQ_TARGET_NET4=$(echo "$( 0 %} +PORT_DESTS=(<% vpn.server.forwards | map('string') | join(" ") %>) +{% endif %} + +# sudo sysctl -w net.ipv4.ip_forward=1 +# sudo sysctl -w net.ipv4.conf.all.src_valid_mark=1 +sudo sysctl -p + +if [[ "$1" == "up" ]]; then + CLIENT_IP4=<% vpn_client_ip.stdout %> + for portdest in ${PORT_DESTS[@]}; do + sudo iptables -t nat -A PREROUTING -d <% ipify_public_ip %> -p tcp --dport "$portdest" -j DNAT --to-destination "${CLIENT_IP4}:${portdest}" + done + sudo iptables -t nat -A POSTROUTING -s <% masq_target_net_ip.stdout %> -j MASQUERADE +elif [[ "$1" == "down" ]]; then + CLIENT_IP4=<% vpn_client_ip.stdout %> + for portdest in ${PORT_DESTS[@]}; do + sudo iptables -t nat -D PREROUTING -d <% ipify_public_ip %> -p tcp --dport "$portdest" -j DNAT --to-destination "${CLIENT_IP4}:${portdest}" + done + sudo iptables -t nat -D POSTROUTING -s <% masq_target_net_ip.stdout %> -j MASQUERADE +fi \ No newline at end of file diff --git a/roles/init-server/templates/usr/local/bin/dsnet-routing6.sh.j2 b/roles/init-server/templates/usr/local/bin/dsnet-routing6.sh.j2 new file mode 100755 index 0000000..161e025 --- /dev/null +++ b/roles/init-server/templates/usr/local/bin/dsnet-routing6.sh.j2 @@ -0,0 +1,37 @@ +#!/bin/bash +set -euo pipefail + +MASQ_TARGET_NET4=$(echo "$( 0 %} +PORT_DESTS=(<% vpn.server.forwards | map('string') | join(" ") %>) +{% endif %} + +# sudo sysctl -w net.ipv4.ip_forward=1 +# sudo sysctl -w net.ipv6.conf.all.forwarding=1 +# sudo sysctl -w net.ipv4.conf.all.src_valid_mark=1 +sudo sysctl -p + +if [[ "$1" == "up" ]]; then + CLIENT_IP4=<% vpn_client_ip.stdout %> + CLIENT_IP6=<% vpn_client_ip.stdout %> + for portdest in ${PORT_DESTS[@]}; do + sudo iptables -t nat -A PREROUTING -d <% ipify_public_ip %> -p tcp --dport "$portdest" -j DNAT --to-destination "${CLIENT_IP4}:${portdest}" + sudo ip6tables -t nat -A PREROUTING -d <% ipify_public_ip6.stdout %> -p tcp --dport "$portdest" -j DNAT --to-destination "${CLIENT_IP6}:${portdest}" + done + sudo iptables -t nat -A POSTROUTING -s <% masq_target_net_ip.stdout %> -j MASQUERADE + sudo ip6tables -t nat -A POSTROUTING -s <% masq_target_net_ip6.stdout %> -j MASQUERADE +elif [[ "$1" == "down" ]]; then + CLIENT_IP4=<% vpn_client_ip.stdout %> + CLIENT_IP6=<% vpn_client_ip.stdout %> + for portdest in ${PORT_DESTS[@]}; do + sudo iptables -t nat -D PREROUTING -d <% ipify_public_ip %> -p tcp --dport "$portdest" -j DNAT --to-destination "${CLIENT_IP4}:${portdest}" + sudo ip6tables -t nat -D PREROUTING -d <% ipify_public_ip6.stdout %> -p tcp --dport "$portdest" -j DNAT --to-destination "${CLIENT_IP6}:${portdest}" + done + sudo iptables -t nat -D POSTROUTING -s <% masq_target_net_ip.stdout %> -j MASQUERADE + sudo ip6tables -t nat -D POSTROUTING -s <% masq_target_net_ip6.stdout %> -j MASQUERADE +fi \ No newline at end of file diff --git a/vars/actual_settings.yml b/vars/actual_settings.yml new file mode 100644 index 0000000..9a7fbb8 --- /dev/null +++ b/vars/actual_settings.yml @@ -0,0 +1,3 @@ +actual: + containerized: true + port: 5006 \ No newline at end of file diff --git a/vars/aria_settings.yml b/vars/aria_settings.yml index 28bd7bd..383753c 100644 --- a/vars/aria_settings.yml +++ b/vars/aria_settings.yml @@ -2,12 +2,12 @@ aria: containerized: true rpc: - enabled: false + enabled: true listen_all: true scheme: http hostname: localhost port: 6800 - secret: ~ + secret: "{{ lookup('password', './.tmp/' + inventory_hostname + '-aria/aria.secret', chars=['ascii_lowercase', 'digits'], length=32) }}" credentials: ~ # dest: "{{ ansible_user_home.stdout }}/downloads/aria2" log: warn @@ -50,7 +50,7 @@ aria: trnt: dht: enabled: true - port: 6881-6999 + port: 6940 entrypoint: ~ # path: "{{ ansible_user_home.stdout }}/.aria2/dht.dat" # path6: "{{ ansible_user_home.stdout }}/.aria2/dht6.dat" diff --git a/vars/crowdsec_settings.yml b/vars/crowdsec_settings.yml index 86708fa..22537bb 100644 --- a/vars/crowdsec_settings.yml +++ b/vars/crowdsec_settings.yml @@ -1,9 +1,11 @@ crowdsec: - containerized: false + containerized: true port: 6050 colls: - - "crowdsecurity/base-http-scenarios" - - "crowdsecurity/proftpd" + - "crowdsecurity/caddy" + - "crowdsecurity/nextcloud" + - "crowdsecurity/mysql" + - "crowdsecurity/linux" parsers: [] scenarios: [] postoverflows: [] \ No newline at end of file diff --git a/vars/flexget_settings.yml b/vars/flexget_settings.yml index b7101fb..ee23bb6 100644 --- a/vars/flexget_settings.yml +++ b/vars/flexget_settings.yml @@ -1,3 +1,4 @@ flexget: enabled: true + containerized: true download_mode: direct \ No newline at end of file diff --git a/vars/forgejo_settings.yml b/vars/forgejo_settings.yml new file mode 100644 index 0000000..f0e5859 --- /dev/null +++ b/vars/forgejo_settings.yml @@ -0,0 +1,36 @@ +forgejo: + containerized: true + subdomain: git + description: A cybernetic spellbook + trusted: + proxies: + - "127.0.0.1" + - "::1" + db: + type: mysql + name: "{{ mysql.users.admin.databases.forgejo.name }}" + ssh: + port: 2121 + site: + registration: + enabled: false + landing: explore + lock_reasons: + - Ongoing Demon War + - Excessive Mana Dumping + - Improper Domain Expansion + - Dark Pattern Spell + - Spell Crafted + - Successfully Healed + - Delegated as Esoterica + kanban_columns: + - considered + - planned + - assigned + - ongoing + - completed + triage_categories: + - hex backlog + - fatal hex + - critical hex + - nuisance hex \ No newline at end of file diff --git a/vars/git_aliases.yml b/vars/git_aliases.yml index 08d87e6..10f4d50 100644 --- a/vars/git_aliases.yml +++ b/vars/git_aliases.yml @@ -1,57 +1,5 @@ git_aliases: - - [ckot, checkout] - - [chbr, checkout] - - [ch_br, checkout] - - [tch_br, "'checkout -b'"] - - [tchbr, "'checkout -b'"] - - [tchbr, "'checkout -b'"] - - [add_br, "'checkout -b'"] - - [addbr, "'checkout -b'"] - - [rm_br, "'branch -D'"] - - [rmbr, "'branch -D'"] - - [del_br, "'branch -D'"] - - [delbr, "'branch -D'"] - - [tch_rmt, "'remote add'"] - - [tchrmt, "'remote add'"] - - [add_rmt, "'remote add'"] - - [addrmt, "'remote add'"] - - [rm_rmt, "'remote remove'"] - - [rmrmt, "'remote remove'"] - - [del_rmt, "'remote remove'"] - - [delrmt, "'remote remove'"] - - [cfg, config] - - [psh, push] - - [snd, push] - - [send, push] - - [post, push] - - [pl, pull] - - [receive, pull] - - [get, pull] - - [fch, fetch] - - [hist, log] - - [scenes, log] + - [co, checkout] - [br, branch] - - [lsbr, "'branch -v'"] - - [ls_br, "'branch -v'"] - - [rmt, remote] - - [ls_rmt, "'remote -v'"] - - [lsrmt, "'remote -v'"] - - [cmmt, commit] - - [register, commit] - - [st, status] - - [stat, status] - - [state, status] - - [current, status] - - [restart, "reset --hard"] - - [undo, "'reset HEAD~1 --mixed'"] - - [unstage, "'reset HEAD --'"] - - [stage, add] - - [touch, add] - - [tch, add] - - [del, rm] - - [prev, "'log -1 HEAD'"] - - [last, "'log -1 HEAD'"] - - [finito, "'!git push origin HEAD'"] - - [chkpnt, "'!git add -A && git commit -m'"] - - [sgn, "'commit --amend --no-edit -S'"] - - [rvrt, revert] \ No newline at end of file + - [ci, commit] + - [st, status] \ No newline at end of file diff --git a/vars/gitea_settings.yml b/vars/gitea_settings.yml deleted file mode 100644 index 7e8c382..0000000 --- a/vars/gitea_settings.yml +++ /dev/null @@ -1,16 +0,0 @@ -gitea: - containerized: true - subdomain: git - trusted: - proxies: - - "127.0.0.1" - - "::1" - db: - type: mysql - name: "{{ mysql.users.admin.databases.gitea.name }}" - ssh: - port: 2323 - site: - registration: - enabled: true - landing: explore \ No newline at end of file diff --git a/vars/iptables_settings@homeserver.yml b/vars/iptables_settings@homeserver.yml new file mode 100644 index 0000000..68d573b --- /dev/null +++ b/vars/iptables_settings@homeserver.yml @@ -0,0 +1,21 @@ +iptables: + inbound_default: DROP + outbound_default: ACCEPT + allow: + inbound: + # - port: 51820 + # interface: ~ + # protocol: udp + # desc: vpn + - port: 5353 + interface: ~ + protocol: udp + desc: mdns + - port: 22 + interface: ~ + protocol: tcp + desc: ssh + outbound: [] + drop: + inbound: [] + outbound: [] \ No newline at end of file diff --git a/vars/iptables_settings@vps.yml b/vars/iptables_settings@vps.yml new file mode 100644 index 0000000..22a1b3a --- /dev/null +++ b/vars/iptables_settings@vps.yml @@ -0,0 +1,37 @@ +iptables: + inbound_default: DROP + outbound_default: ACCEPT + allow: + inbound: + - port: 51820 + interface: ~ + protocol: udp + desc: vpn + - port: 9191 + interface: ~ + protocol: udp + desc: tailnet + - port: 53 + interface: ~ + protocol: udp + desc: dns + - port: 53 + interface: ~ + protocol: tcp + desc: dns + - port: 80 + interface: ~ + protocol: tcp + desc: http + - port: 443 + interface: ~ + protocol: tcp + desc: https + - port: 443 + interface: ~ + protocol: udp + desc: https + outbound: [] + drop: + inbound: [] + outbound: [] \ No newline at end of file diff --git a/vars/kavita_settings.yml b/vars/kavita_settings.yml new file mode 100644 index 0000000..91775f2 --- /dev/null +++ b/vars/kavita_settings.yml @@ -0,0 +1,2 @@ +kavita: + containerized: true \ No newline at end of file diff --git a/vars/local_facts.yml b/vars/local_facts.yml index a83e1fe..4542afc 100644 --- a/vars/local_facts.yml +++ b/vars/local_facts.yml @@ -1,3 +1,11 @@ local_facts: user_id: rika - user_dir: /home/rika \ No newline at end of file + user_dir: /home/rika + # gpg: + # private_keys: + # - id: 558041D5CF2AB23B + # password: ~ + # - id: F0CA546935C02C76 + # password: ~ + # - id: CE245A7D7CEE3639 + # password: ~ \ No newline at end of file diff --git a/vars/mysql_settings.yml b/vars/mysql_settings.yml index c6b2e8b..3e4b77b 100644 --- a/vars/mysql_settings.yml +++ b/vars/mysql_settings.yml @@ -1,6 +1,6 @@ mysql: containerized: true - password: "{{ lookup('password', './.tmp/' + inventory_hostname + '-root@mysql/mysql.pass', chars=['ascii_lowercase', 'digits'], length=8) }}" + password: "{{ lookup('password', './.tmp/' + inventory_hostname + '-root@mysql/mysql.pass', chars=['ascii_lowercase', 'digits'], length=12) }}" users: admin: username: admin @@ -10,8 +10,8 @@ mysql: name: testdb nextcloud: name: nextcloud - gitea: - name: gitea + forgejo: + name: forgejo opengist: name: opengist - password: "{{ lookup('password', './.tmp/' + inventory_hostname + '-user@mysql/mysql.pass', chars=['ascii_lowercase', 'digits'], length=8) }}" + password: "{{ lookup('password', './.tmp/' + inventory_hostname + '-user@mysql/mysql.pass', chars=['ascii_lowercase', 'digits'], length=12) }}" diff --git a/vars/openpgp_settings.yml b/vars/openpgp_settings.yml new file mode 100644 index 0000000..be7bae0 --- /dev/null +++ b/vars/openpgp_settings.yml @@ -0,0 +1,13 @@ +gpg: + keypairs: + - email: admin@sukaato.moe + name: Administrator + alias: admin + encryption_method: RSA + length: 4096 + primary_subkey: + encryption_method: RSA + length: 4096 + expiration: 0 + password: "{{ lookup('password', './.tmp/' + inventory_hostname + '-admin@openpgp/openpgp.pass', chars=['ascii_lowercase', 'digits'], length=10) }}" + diff --git a/vars/podpose_settings.yml b/vars/podpose_settings.yml index 45512e7..5a5d8e1 100644 --- a/vars/podpose_settings.yml +++ b/vars/podpose_settings.yml @@ -17,8 +17,32 @@ compose: name: forge pastebin: name: gist + forum: + name: forum + syncplay: + name: syncplay tail: name: tailclient dash: name: dash + aria: + name: aria + ariang: + name: ariang + flexget: + name: flexget + surge: + name: surge + soulseek: + name: soulseek + grocy: + name: grocy + budget: + name: budget + reader: + name: reader + stash: + name: stash + cast: + name: cast diff --git a/vars/soulseek_settings.yml b/vars/soulseek_settings.yml new file mode 100644 index 0000000..4dde1db --- /dev/null +++ b/vars/soulseek_settings.yml @@ -0,0 +1,2 @@ +soulseek: + containerized: true \ No newline at end of file diff --git a/vars/stash_settings.yml b/vars/stash_settings.yml new file mode 100644 index 0000000..cb169d9 --- /dev/null +++ b/vars/stash_settings.yml @@ -0,0 +1,3 @@ +stash: + containerized: true + port: 9999 \ No newline at end of file diff --git a/vars/syncplay_settings.yml b/vars/syncplay_settings.yml new file mode 100644 index 0000000..5d64e0b --- /dev/null +++ b/vars/syncplay_settings.yml @@ -0,0 +1,6 @@ +syncplay: + subdomain: watch + containerized: true + password: "{{ lookup('password', './.tmp/' + inventory_hostname + '-syncplay.pass', chars=['ascii_lowercase', 'digits'], length=32) }}" + salt: "andCUT" + motd: "{{ 'Welcome to ' + (((fqdn | default(inventory_hostname)) | split('.'))[:-1] | join('.')).upper() + ' cinema! 📽️🍿' }}" \ No newline at end of file diff --git a/vars/vpn_settings.yml b/vars/vpn_settings.yml index 00905d7..16e3f08 100644 --- a/vars/vpn_settings.yml +++ b/vars/vpn_settings.yml @@ -4,14 +4,11 @@ vpn: name: dsnet interface: dsnet forwards: - - [tcp,"80"] # HTTP - - [tcp,"443"] # HTTPS - - [tcp,"465"] # SMTP - - [tcp,"587"] # SMTP - - [tcp,"995"] # POP3 - - [tcp,"110"] # POP3 - - [tcp,"143"] # IMAP - - [tcp,"993"] # IMAP + - 80 # HTTP + - 443 # HTTPS + - 465 # SMTPS + - 993 # IMAPS + - 1935 # RTMP clients: - name: staging1.test desc: "my homelab server"