diff options
author | Matthew Fennell <matthew@fennell.dev> | 2024-04-04 22:54:05 +0100 |
---|---|---|
committer | Matthew Fennell <matthew@fennell.dev> | 2024-04-04 22:54:05 +0100 |
commit | de867dadbcc3c69d97acf96bf3e86d11295eea39 (patch) | |
tree | 2962ef65772790097581f19a2af9b12ac30087e4 | |
parent | d29a60b1afec942fe4f7ca7baa95d870afaae83e (diff) |
Bring repo up-to-date with current prod config
-rw-r--r-- | Makefile | 18 | ||||
-rw-r--r-- | files/50-disable-password-auth.conf | 5 | ||||
-rw-r--r-- | files/nginx_conf.j2 | 31 | ||||
-rw-r--r-- | files/prosody.cfg.lua.j2 | 22 | ||||
-rw-r--r-- | files/virtual_host.cfg.lua.j2 | 20 | ||||
-rw-r--r-- | playbook.yaml | 218 |
6 files changed, 285 insertions, 29 deletions
@@ -3,17 +3,29 @@ # # SPDX-License-Identifier: AGPL-3.0-only -.PHONY: check lint staging prod +.PHONY: check staging prod lint staging-fresh prod-fresh check: make lint make staging staging: - ansible-playbook --inventory inventory/staging.ini playbook.yaml + ansible-playbook --forks 1 --inventory inventory/staging.ini playbook.yaml prod: - ansible-playbook --inventory inventory/prod.ini playbook.yaml + ansible-playbook --forks 1 --inventory inventory/prod.ini playbook.yaml lint: precious --ascii lint --all + +staging-fresh: + ~/Documents/libcloud/recreate xmpp-continuous-nonprod + ANSIBLE_SSH_ARGS="-F /tmp/early_ssh_config" ansible-playbook --forks 1 \ + --inventory inventory/staging.ini playbook.yaml + +prod-fresh: + echo "You are about to recreate PROD. Sleeping for 60s to let this sink in" + sleep 60 + ~/Documents/libcloud/recreate xmpp-prod + ANSIBLE_SSH_ARGS="-F /tmp/early_ssh_config" ansible-playbook --forks 1 \ + --inventory inventory/prod.ini playbook.yaml diff --git a/files/50-disable-password-auth.conf b/files/50-disable-password-auth.conf new file mode 100644 index 0000000..6c684d3 --- /dev/null +++ b/files/50-disable-password-auth.conf @@ -0,0 +1,5 @@ +# SPDX-FileCopyrightText: 2024 Matthew Fennell <matthew@fennell.dev> +# +# SPDX-License-Identifier: AGPL-3.0-only + +PasswordAuthentication no diff --git a/files/nginx_conf.j2 b/files/nginx_conf.j2 new file mode 100644 index 0000000..fbddce8 --- /dev/null +++ b/files/nginx_conf.j2 @@ -0,0 +1,31 @@ +# SPDX-FileCopyrightText: 2024 Matthew Fennell <matthew@fennell.dev> +# +# SPDX-License-Identifier: AGPL-3.0-only + +server { + listen 443 ssl; + listen [::]:443; + + ssl_certificate /etc/prosody/certs/{{ virtual_host }}.crt; + ssl_certificate_key /etc/prosody/certs/{{ virtual_host }}.key; + + server_name {{ delegate_host }}; + + location / { + proxy_pass https://localhost:5281; + proxy_set_header Host "{{ delegate_host }}"; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_buffering off; + tcp_nodelay on; + } +} + +server { + listen 80; + listen [::]:80; + + server_name {{ delegate_host }}; + + return 301 https://$host$request_uri; +} diff --git a/files/prosody.cfg.lua.j2 b/files/prosody.cfg.lua.j2 index b6d9fc1..e20ba4e 100644 --- a/files/prosody.cfg.lua.j2 +++ b/files/prosody.cfg.lua.j2 @@ -2,8 +2,6 @@ -- -- SPDX-License-Identifier: AGPL-3.0-only -admins = { } - plugin_paths = { "/usr/local/lib/prosody/modules" } modules_enabled = { @@ -17,6 +15,8 @@ modules_enabled = { "dialback"; "disco"; "external_services"; + "http"; + "http_libjs"; "limits"; "mam"; "mimicking"; @@ -25,9 +25,6 @@ modules_enabled = { "posix"; "private"; "proxy65"; - "register"; - "register_apps"; - "reload_modules"; "roster"; "saslauth"; "server_contact_info"; @@ -41,10 +38,6 @@ modules_enabled = { "version"; } -reload_modules = { - "tls"; -} - modules_disabled = { } pidfile = "/run/prosody/prosody.pid"; @@ -62,15 +55,18 @@ authentication = "internal_hashed" certificates = "certs" c2s_direct_tls_ports = { 5223 } s2s_direct_tls_ports = { 5270 } +use_dane = true -- From Monal considerations for XMPP server admins smacks_hibernation_time = 86400 allow_registration = true -site_apps_show = { - "conversations"; - "dino"; - "monal"; +invite_expiry = 86400 * 365 +registration_invite_only = true + +firewall_scripts = { + "module:scripts/spam-blocking.pfw"; + "module:script/spam-blocklists.pfw"; } limits = { diff --git a/files/virtual_host.cfg.lua.j2 b/files/virtual_host.cfg.lua.j2 index 6db9e98..272e24b 100644 --- a/files/virtual_host.cfg.lua.j2 +++ b/files/virtual_host.cfg.lua.j2 @@ -4,9 +4,27 @@ VirtualHost "{{ virtual_host }}" +admins = { "admin@{{ virtual_host }}" }; + +modules_enabled = { + "invites"; + "invites_adhoc"; + "invites_page"; + "invites_register"; + "register"; + "register_apps"; +} + +contact_info = { + abuse = { "xmpp:abuse@{{ virtual_host }}" }; + admin = { "xmpp:admin@{{ virtual_host }}" }; +} + turn_external_host = "turn.{{ virtual_host }}" turn_external_secret = "{{ turn_secret }}" +allow_user_invites = true +http_external_url = "https://{{ virtual_host }}/" site_name = "{{ virtual_host }}" Component "upload.{{ virtual_host }}" "http_file_share" @@ -19,3 +37,5 @@ modules_enabled = { log_all_rooms = true restrict_room_creation = "local" +muc_room_default_public = false +muc_room_default_public_jids = true diff --git a/playbook.yaml b/playbook.yaml index bfbc5eb..5de4fea 100644 --- a/playbook.yaml +++ b/playbook.yaml @@ -51,6 +51,16 @@ group: admin mode: preserve + - name: Retrieve DANE hash + ansible.builtin.shell: + cmd: > + openssl x509 -in ~/.lego/certificates/{{ virtual_host }}.crt -noout + -pubkey | openssl pkey -pubin -outform DER | openssl dgst -sha256 + -binary | hexdump -ve '/1 "%02x"' + register: dane_hash + changed_when: false + delegate_to: localhost + # We allow status code 400 here as this is returned by deSEC if the domain # already exists. Ideally, we should filter out genuinely good/bad requests # here using the response. @@ -98,6 +108,7 @@ ttl: 3600 records: "{{ domain_keys }}" delegate_to: localhost + when: domain_with_ds != "" - name: Ensure records are registered in subdomain ansible.builtin.uri: @@ -107,22 +118,66 @@ headers: Authorization: Token {{ desec_token }} body: + - subname: "conference" + type: CNAME + ttl: 3600 + records: ["{{ delegate_host }}."] - subname: "turn" type: CNAME ttl: 3600 - records: ["{{ virtual_host }}."] + records: ["{{ delegate_host }}."] - subname: "upload" type: CNAME ttl: 3600 - records: ["{{ virtual_host }}."] + records: ["{{ delegate_host }}."] + - subname: "_xmpp-client._tcp" + type: SRV + ttl: 3600 + records: ["0 5 5222 {{ delegate_host }}."] - subname: "_xmpps-client._tcp" type: SRV ttl: 3600 - records: ["0 5 5223 {{ virtual_host }}."] + records: ["0 5 5223 {{ delegate_host }}."] + - subname: "_xmpp-server._tcp" + type: SRV + ttl: 3600 + records: ["0 5 5269 {{ delegate_host }}."] - subname: "_xmpps-server._tcp" type: SRV ttl: 3600 - records: ["0 5 5270 {{ virtual_host }}."] + records: ["0 5 5270 {{ delegate_host }}."] + - subname: "_xmpps-server._tcp.upload" + type: SRV + ttl: 3600 + records: ["0 5 5270 {{ delegate_host }}."] + - subname: "_xmpps-server._tcp.conference" + type: SRV + ttl: 3600 + records: ["0 5 5270 {{ delegate_host }}."] + - subname: '{{ "_5222._tcp" + tlsa_appended_subdomain }}' + type: TLSA + ttl: 3600 + records: ["3 1 1 {{ dane_hash.stdout }}"] + - subname: '{{ "_5223._tcp" + tlsa_appended_subdomain }}' + type: TLSA + ttl: 3600 + records: ["3 1 1 {{ dane_hash.stdout }}"] + - subname: '{{ "_5269._tcp" + tlsa_appended_subdomain }}' + type: TLSA + ttl: 3600 + records: ["3 1 1 {{ dane_hash.stdout }}"] + - subname: '{{ "_5270._tcp" + tlsa_appended_subdomain }}' + type: TLSA + ttl: 3600 + records: ["3 1 1 {{ dane_hash.stdout }}"] + - subname: "" + type: CAA + ttl: 3600 + records: + - "0 issue \"letsencrypt.org;validationmethods=dns-01;accounturi=\ + {{ acme_account_uri_prod }}\"" + - "0 issue \"letsencrypt.org;validationmethods=dns-01;accounturi=\ + {{ acme_account_uri_nonprod }}\"" delegate_to: localhost # We specifically use apt instead of the more general package module here, @@ -136,10 +191,14 @@ name: - borgmatic # Backups - coturn # Audio / video calling server + - libjs-bootstrap4 # Used by invite webpage + - libjs-jquery # Used by invite webpage - lua-dbi-postgresql # Prosody postgres connection + - nginx # Serve invite webpages - postgresql # Database - prosody # XMPP server - prosody-modules # Extra addons + - python3-pexpect # Used by ansible expect role - python3-psycopg2 # Used by ansible postgres role - ufw # Firewall state: present @@ -154,7 +213,7 @@ loop: - OpenSSH - Turnserver - - WWW + - WWW Full - XMPP become: true @@ -185,6 +244,33 @@ - 5281 # XEP-0363 become: true + - name: Ensure default nginx config is removed + ansible.builtin.file: + path: "/etc/nginx/sites-enabled/default" + state: absent + become: true + notify: Restart nginx + + - name: Ensure nginx config is installed + ansible.builtin.template: + src: "{{ playbook_dir }}/files/nginx_conf.j2" + dest: /etc/nginx/sites-available/{{ virtual_host }} + owner: root + group: root + mode: "0644" + become: true + notify: Restart nginx + + - name: Ensure nginx config is enabled + ansible.builtin.file: + src: /etc/nginx/sites-available/{{ virtual_host }} + dest: /etc/nginx/sites-enabled/{{ virtual_host }} + owner: root + group: root + state: link + become: true + notify: Restart nginx + - name: Ensure turn is configured ansible.builtin.template: src: "{{ playbook_dir }}/files/turnserver.conf.j2" @@ -193,7 +279,7 @@ group: prosody mode: "0640" become: true - notify: Reload coturn + notify: Restart coturn - name: Ensure prosody database is set up community.postgresql.postgresql_db: @@ -250,7 +336,7 @@ group: prosody mode: "0640" become: true - notify: Reload prosody + notify: Restart prosody - name: Ensure host-specific prosody configuration is available ansible.builtin.template: @@ -260,7 +346,7 @@ group: prosody mode: "0644" become: true - notify: Reload prosody + notify: Restart prosody - name: Ensure host-specific prosody configuration is set ansible.builtin.file: @@ -270,7 +356,70 @@ group: prosody state: link become: true - notify: Reload prosody + notify: Restart prosody + + - name: Ensure localhost prosody configuration is removed + ansible.builtin.file: + path: "/etc/prosody/conf.d/localhost.cfg.lua" + state: absent + become: true + notify: Restart prosody + + - name: Ensure localhost prosody configuration is not available + ansible.builtin.file: + path: "/etc/prosody/conf.avail/localhost.cfg.lua" + state: absent + become: true + + - name: Ensure example prosody configuration is not available + ansible.builtin.file: + path: "/etc/prosody/conf.avail/example.com.cfg.lua" + state: absent + become: true + + - name: Ensure prosody key is installed + ansible.builtin.copy: + src: "~/.lego/certificates/{{ virtual_host }}.key" + dest: "/etc/prosody/certs" + mode: "0640" + owner: root + group: prosody + become: true + notify: Restart prosody + + - name: Ensure prosody certificate is installed + ansible.builtin.copy: + src: "~/.lego/certificates/{{ virtual_host }}.crt" + dest: "/etc/prosody/certs" + mode: "0640" + owner: root + group: prosody + become: true + notify: Restart prosody + + - name: Ensure prosody users are registered + ansible.builtin.expect: + command: "prosodyctl adduser {{ item.name }}@{{ virtual_host }}" + responses: + (?i)password: "{{ item.password }}" + become: true + register: adduser_result + changed_when: + "'That user already exists' not in adduser_result.stdout_lines" + failed_when: false + no_log: true + with_items: "{{ users }}" + + - name: Ensure prosody users have the given password + ansible.builtin.expect: + command: "prosodyctl passwd {{ item.name }}@{{ virtual_host }}" + responses: + (?i)password: "{{ item.password }}" + become: true + register: passwd_result + changed_when: true + no_log: true + with_items: "{{ users }}" - name: Ensure prosody is enabled ansible.builtin.service: @@ -306,20 +455,51 @@ validate: validate-borgmatic-config --config %s become: true + # Vultr adds a custom sshd_config file that enabled password authentication. + # I don't want this to be enabled, since I'm already copying the public key. + - name: Ensure password authentication is not explicitly enabled + ansible.builtin.file: + path: "/etc/ssh/sshd_config.d/50-cloud-init.conf" + state: absent + become: true + notify: Restart sshd + + - name: Ensure password based authentication is disabled + ansible.builtin.copy: + src: "{{ playbook_dir }}/files/50-disable-password-auth.conf" + dest: "/etc/ssh/sshd_config.d/50-disable-password-auth.conf" + owner: root + group: root + mode: "0644" + become: true + notify: Restart sshd + handlers: - - name: Reload prosody + - name: Restart prosody ansible.builtin.service: name: prosody - state: reloaded + state: restarted become: true - - name: Reload coturn + - name: Restart coturn ansible.builtin.service: name: coturn state: restarted become: true + - name: Restart sshd + ansible.builtin.service: + name: sshd + state: restarted + become: true + + - name: Restart nginx + ansible.builtin.service: + name: nginx + state: restarted + become: true + vars: domain_keys: >- {{- domain.json["keys"] @@ -327,5 +507,17 @@ | flatten | select("search", " 13 2 ") -}} parent_host: "{{ virtual_host.split('.')[1:] | join('.') }}" - domain_with_ds: "{{ parent_domain.json | map(attribute='name') | first }}" + domain_with_ds: >- + {{- parent_domain.json + | map(attribute='name') + | first + | default(None) -}} ds_subname: "{{ virtual_host | regex_replace('.' + domain_with_ds, '') }}" + delegate_host: >- + {{- virtual_host + if delegate_prefix == "" + else delegate_prefix + "." + virtual_host -}} + tlsa_appended_subdomain: >- + {{- "" + if delegate_prefix == "" + else "." + delegate_prefix -}} |