From 32c96b2da77becadaa99c00c092efc1ba8180267 Mon Sep 17 00:00:00 2001 From: serinko <97586125+serinko@users.noreply.github.com> Date: Mon, 5 Jan 2026 14:07:33 +0100 Subject: [PATCH 1/9] make wireguard enabled flag bulletproof --- ansible/nym-node/playbooks/group_vars/all.yml | 20 +++++++++---------- ansible/nym-node/roles/nym/defaults/main.yml | 6 +----- ansible/nym-node/roles/nym/tasks/config.yml | 2 +- 3 files changed, 12 insertions(+), 16 deletions(-) diff --git a/ansible/nym-node/playbooks/group_vars/all.yml b/ansible/nym-node/playbooks/group_vars/all.yml index 0ddf91123ec..9f309d1e4b4 100644 --- a/ansible/nym-node/playbooks/group_vars/all.yml +++ b/ansible/nym-node/playbooks/group_vars/all.yml @@ -2,7 +2,7 @@ ansible_ssh_private_key_file: ~/.ssh/ # nym_version: "v2025.21-mozzarella" -# +# # NOTE: # if you want to pin Nym to a specific version instead of using the # latest release from GitHub in /tasks/main.yml then @@ -13,17 +13,17 @@ tunnel_manager_url: "https://github.com/nymtech/nym/raw/refs/heads/develop/scrip quic_bridge_deployment_url: "https://raw.githubusercontent.com/nymtech/nym/refs/heads/develop/scripts/nym-node-setup/quic_bridge_deployment.sh" # NOTE: These values will be used globally unless overwritten per node in inventory/all -ansible_user: root # used for ssh, like `ssh root@nym-exit.ch-1.mynodes.net` -email: "" # used in certbot, description.toml and landing page -website: "" # it is used in the description.toml -description: "" # or define per node in inventory/all +ansible_user: root # used for ssh, like `ssh root@nym-exit.ch-1.mynodes.net` +email: "" # used in certbot, description.toml and landing page +website: "" # it is used in the description.toml +description: "" # or define per node in inventory/all # NOTE: Set these vars if you want them globally for all nodes # Per node changes in inventory/all will overwrite these global ones: -hostname: "" # this is a fallback, keep it and setup hostname per node in inventory/all -# moniker: "" # if not setup here not in inventory/all it get's derived from the hostname -# mode: # entry-gateway/exit-gateway/mixnode -# wireguard_enabled: # true/false +hostname: "" # this is a fallback, keep it and setup hostname per node in inventory/all +# moniker: "" # if not setup here not in inventory/all it get's derived from the hostname +# mode: # entry-gateway/exit-gateway/mixnode +# wireguard_enabled: # true/false # NOTE: Possible vars to incule on landing page, etc. # operator_name: "" @@ -41,4 +41,4 @@ packages: - ca-certificates - jq - wget - - ufw \ No newline at end of file + - ufinteroperabilityw diff --git a/ansible/nym-node/roles/nym/defaults/main.yml b/ansible/nym-node/roles/nym/defaults/main.yml index 6f268f3ab0d..e36f6fc140d 100644 --- a/ansible/nym-node/roles/nym/defaults/main.yml +++ b/ansible/nym-node/roles/nym/defaults/main.yml @@ -6,10 +6,6 @@ nym_install_dir: /root/nym-binaries http_bind_address: "0.0.0.0:8080" # maps to --http-bind-address mixnet_bind_address: "0.0.0.0:1789" # maps to --mixnet-bind-address - -# WireGuard boolean -wireguard_enabled: "{{ wireguard_enabled | default(false) | bool }}" - # Landing page base dir, hostname is appended in the task landing_page_assets_base_dir: "/var/www" @@ -37,4 +33,4 @@ nym_ufw_rules: - { port: 8080, proto: tcp } - { port: 9000, proto: tcp } - { port: 9001, proto: tcp } - - { port: 51822, proto: udp } \ No newline at end of file + - { port: 51822, proto: udp } diff --git a/ansible/nym-node/roles/nym/tasks/config.yml b/ansible/nym-node/roles/nym/tasks/config.yml index 7c31b6ef5eb..5b742792e0c 100644 --- a/ansible/nym-node/roles/nym/tasks/config.yml +++ b/ansible/nym-node/roles/nym/tasks/config.yml @@ -25,7 +25,7 @@ {{ nym_extra_flags }} --hostname {{ hostname }} - --wireguard-enabled {{ wireguard_enabled }} + --wireguard-enabled {{ (wireguard_enabled | default('false') | bool) | ternary('true','false') }} --landing-page-assets-path {{ landing_page_assets_base_dir }}/{{ hostname }}/ {% if nym_write_flag %}-w{% endif %} {% if nym_init_only_flag %}--init-only{% endif %} From 27a7272a9eca298a5467b1d2d682096b9c9164c1 Mon Sep 17 00:00:00 2001 From: serinko <97586125+serinko@users.noreply.github.com> Date: Mon, 5 Jan 2026 14:20:29 +0100 Subject: [PATCH 2/9] correct firewall setting --- ansible/nym-node/roles/nym/tasks/firewall.yml | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/ansible/nym-node/roles/nym/tasks/firewall.yml b/ansible/nym-node/roles/nym/tasks/firewall.yml index 3159861a9dc..de32eb9d04a 100644 --- a/ansible/nym-node/roles/nym/tasks/firewall.yml +++ b/ansible/nym-node/roles/nym/tasks/firewall.yml @@ -1,3 +1,11 @@ +- name: Ensure UFW is installed + apt: + name: ufw + state: present + update_cache: yes + when: nym_ufw_enable + + - name: Configure UFW rules ufw: rule: allow @@ -14,9 +22,10 @@ - name: Allow bandwidth/topup rule inside WG tunnel command: > ufw allow in on nymwg to any port 51830 proto tcp comment 'bandwidth queries/topup' + changed_when: false when: - nym_ufw_enable - - (wireguard_enabled | bool) + - (wireguard_enabled | default(false) | bool) - name: Enable UFW ufw: From e2d4808045addf56d0fd236417032cea0ad26674 Mon Sep 17 00:00:00 2001 From: serinko <97586125+serinko@users.noreply.github.com> Date: Mon, 5 Jan 2026 14:42:29 +0100 Subject: [PATCH 3/9] add nginx handler --- ansible/nym-node/roles/base/tasks/main.yml | 7 ++-- .../nym-node/roles/nginx/handlers/main.yml | 5 +++ ansible/nym-node/roles/nginx/tasks/main.yml | 36 +++++++++++++------ ansible/nym-node/roles/nym/handlers/main.yml | 1 + ansible/nym-node/roles/nym/tasks/firewall.yml | 1 + 5 files changed, 37 insertions(+), 13 deletions(-) create mode 100644 ansible/nym-node/roles/nginx/handlers/main.yml diff --git a/ansible/nym-node/roles/base/tasks/main.yml b/ansible/nym-node/roles/base/tasks/main.yml index b7c3c4385e5..9be09c0a133 100644 --- a/ansible/nym-node/roles/base/tasks/main.yml +++ b/ansible/nym-node/roles/base/tasks/main.yml @@ -1,9 +1,10 @@ +--- - name: Set hostname hostname: name: "{{ hostname }}" when: hostname is defined and hostname | length > 0 -- name: Install aptitude +- name: Install aptitude apt: name: aptitude update_cache: yes @@ -14,9 +15,9 @@ apt: update_cache: yes upgrade: yes - + - name: Install essential packages package: name: "{{ packages }}" state: latest - update_cache: yes \ No newline at end of file + update_cache: yes diff --git a/ansible/nym-node/roles/nginx/handlers/main.yml b/ansible/nym-node/roles/nginx/handlers/main.yml new file mode 100644 index 00000000000..eb0e7f156f0 --- /dev/null +++ b/ansible/nym-node/roles/nginx/handlers/main.yml @@ -0,0 +1,5 @@ +--- +- name: Reload nginx + service: + name: nginx + state: reloaded diff --git a/ansible/nym-node/roles/nginx/tasks/main.yml b/ansible/nym-node/roles/nginx/tasks/main.yml index e40e73d32fb..4da9b64690a 100644 --- a/ansible/nym-node/roles/nginx/tasks/main.yml +++ b/ansible/nym-node/roles/nginx/tasks/main.yml @@ -1,3 +1,4 @@ +--- - name: Install nginx and certbot apt: name: @@ -17,45 +18,60 @@ template: src: landing.html.j2 dest: "/var/www/{{ hostname }}/index.html" - -- name: Remove default nginx site - file: - path: /etc/nginx/sites-enabled/default - state: absent + notify: Reload nginx - name: Add bare-bones nginx template template: src: nginx-site.conf.j2 dest: "/etc/nginx/sites-available/{{ hostname }}" + notify: Reload nginx - name: Enable nginx config file: src: "/etc/nginx/sites-available/{{ hostname }}" dest: "/etc/nginx/sites-enabled/{{ hostname }}" state: link + notify: Reload nginx - name: Validate nginx configuration command: nginx -t changed_when: false -- name: Obtain SSL certificate - command: - cmd: "certbot --nginx --non-interactive --agree-tos --redirect -m {{ email }} -d {{ hostname }}" +- name: Obtain SSL certificate (non-fatal) + block: + - name: Obtain SSL certificate + command: + cmd: "certbot --nginx --non-interactive --agree-tos --redirect -m {{ email }} -d {{ hostname }}" + register: certbot_result + changed_when: "'Congratulations' in certbot_result.stdout" + notify: Reload nginx + rescue: + - name: Certbot failed, leaving HTTP config in place + debug: + msg: "Certbot failed; keeping nginx running with current HTTP config so the site stays reachable." - name: Add wss config from nginx template template: src: wss-config.conf.j2 dest: "/etc/nginx/sites-available/nym-wss-config" + notify: Reload nginx - name: Enable WSS config file: src: "/etc/nginx/sites-available/nym-wss-config" dest: "/etc/nginx/sites-enabled/nym-wss-config" state: link + notify: Reload nginx - name: Validate nginx config after wss command: nginx -t changed_when: false -- name: Restart nginx to apply changes - service: name=nginx state=restarted enabled=yes +- name: Ensure nginx is enabled and running + service: + name: nginx + state: started + enabled: yes + +- name: Flush handlers (apply reloads after successful config tests) + meta: flush_handlers diff --git a/ansible/nym-node/roles/nym/handlers/main.yml b/ansible/nym-node/roles/nym/handlers/main.yml index 4bfa221a97b..b3dfc7048d3 100644 --- a/ansible/nym-node/roles/nym/handlers/main.yml +++ b/ansible/nym-node/roles/nym/handlers/main.yml @@ -1,3 +1,4 @@ +--- - name: Reload systemd systemd: daemon_reload: yes diff --git a/ansible/nym-node/roles/nym/tasks/firewall.yml b/ansible/nym-node/roles/nym/tasks/firewall.yml index de32eb9d04a..189625fd741 100644 --- a/ansible/nym-node/roles/nym/tasks/firewall.yml +++ b/ansible/nym-node/roles/nym/tasks/firewall.yml @@ -1,3 +1,4 @@ +--- - name: Ensure UFW is installed apt: name: ufw From 6a2cfcb395dd17bbe34f7170057c3457d4078810 Mon Sep 17 00:00:00 2001 From: serinko <97586125+serinko@users.noreply.github.com> Date: Mon, 5 Jan 2026 15:48:10 +0100 Subject: [PATCH 4/9] make systemd template case sensitive --- .../roles/nym/templates/nym-node.service.j2 | 4 ++-- ansible/nym-node/roles/tunnel/tasks/main.yml | 15 ++++++--------- 2 files changed, 8 insertions(+), 11 deletions(-) diff --git a/ansible/nym-node/roles/nym/templates/nym-node.service.j2 b/ansible/nym-node/roles/nym/templates/nym-node.service.j2 index 608a9ed47bb..fcaddbe5003 100644 --- a/ansible/nym-node/roles/nym/templates/nym-node.service.j2 +++ b/ansible/nym-node/roles/nym/templates/nym-node.service.j2 @@ -6,10 +6,10 @@ StartLimitBurst=10 [Service] User={{ ansible_user }} LimitNOFILE=65536 -ExecStart=/root/nym-binaries/nym-node run --mode {{ mode }} --accept-operator-terms-and-conditions --wireguard-enabled {{ wireguard_enabled }} +ExecStart=/root/nym-binaries/nym-node run --mode {{ mode }} --accept-operator-terms-and-conditions --wireguard-enabled {{ (wireguard_enabled | default(false) | bool) | ternary('true','false') }} KillSignal=SIGINT Restart=on-failure RestartSec=30 [Install] -WantedBy=multi-user.target \ No newline at end of file +WantedBy=multi-user.target diff --git a/ansible/nym-node/roles/tunnel/tasks/main.yml b/ansible/nym-node/roles/tunnel/tasks/main.yml index 16cca3c4aac..33f380aac7e 100644 --- a/ansible/nym-node/roles/tunnel/tasks/main.yml +++ b/ansible/nym-node/roles/tunnel/tasks/main.yml @@ -1,14 +1,11 @@ -- name: Download network-tunnel-manager.sh - tags: network tunnel manager - get_url: - url: "{{ tunnel_manager_url }}" - dest: "/root/nym-binaries/network-tunnel-manager.sh" - mode: "0755" - +--- - name: Configure tunnel manager - tags: network tunnel manager + tags: + - network_tunnel_manager become: true command: cmd: "/root/nym-binaries/network-tunnel-manager.sh {{ item }}" loop: - - complete_networking_configuration \ No newline at end of file + - complete_networking_configuration + register: tunnel_mgr + failed_when: false From b6af7f95ff4522eb2812f89f16bfdd68d2cb3855 Mon Sep 17 00:00:00 2001 From: serinko <97586125+serinko@users.noreply.github.com> Date: Tue, 6 Jan 2026 16:52:50 +0100 Subject: [PATCH 5/9] twek nginx and ssl template --- .../nym-node/roles/nginx/handlers/main.yml | 5 + ansible/nym-node/roles/nginx/tasks/main.yml | 116 +++++++++++++----- .../tasks/templates/nginx-site-ssl.conf.j2 | 30 +++++ .../nginx/tasks/templates/nginx-site.conf.j2 | 15 ++- 4 files changed, 127 insertions(+), 39 deletions(-) create mode 100644 ansible/nym-node/roles/nginx/tasks/templates/nginx-site-ssl.conf.j2 diff --git a/ansible/nym-node/roles/nginx/handlers/main.yml b/ansible/nym-node/roles/nginx/handlers/main.yml index eb0e7f156f0..1887b17b3a6 100644 --- a/ansible/nym-node/roles/nginx/handlers/main.yml +++ b/ansible/nym-node/roles/nginx/handlers/main.yml @@ -3,3 +3,8 @@ service: name: nginx state: reloaded + +- name: Restart nginx + service: + name: nginx + state: restarted diff --git a/ansible/nym-node/roles/nginx/tasks/main.yml b/ansible/nym-node/roles/nginx/tasks/main.yml index 4da9b64690a..599dfba07fa 100644 --- a/ansible/nym-node/roles/nginx/tasks/main.yml +++ b/ansible/nym-node/roles/nginx/tasks/main.yml @@ -6,72 +6,120 @@ - certbot - python3-certbot-nginx state: present + update_cache: yes -- name: Create web root directory +- name: Ensure web root directory exists file: path: "/var/www/{{ hostname }}" state: directory mode: "0755" -- name: Create landing page template - tags: landing +- name: Deploy landing page template: src: landing.html.j2 dest: "/var/www/{{ hostname }}/index.html" - notify: Reload nginx + mode: "0644" + notify: Restart nginx -- name: Add bare-bones nginx template +# Remove default site (safe on fresh + redeploy) +- name: Disable default nginx site symlink + file: + path: /etc/nginx/sites-enabled/default + state: absent + notify: Restart nginx + +- name: Remove default nginx site definition if present + file: + path: /etc/nginx/sites-available/default + state: absent + notify: Restart nginx + +# HTTP vhost (serves landing page + ACME challenge, and redirects other traffic to HTTPS) +- name: Deploy HTTP vhost for {{ hostname }} template: src: nginx-site.conf.j2 dest: "/etc/nginx/sites-available/{{ hostname }}" - notify: Reload nginx + mode: "0644" + notify: Restart nginx -- name: Enable nginx config +- name: Enable HTTP vhost (force correct symlink) file: src: "/etc/nginx/sites-available/{{ hostname }}" dest: "/etc/nginx/sites-enabled/{{ hostname }}" state: link - notify: Reload nginx + force: true + notify: Restart nginx + +- name: Ensure nginx is enabled and running (needed for ACME http-01) + service: + name: nginx + state: started + enabled: yes -- name: Validate nginx configuration +- name: Validate nginx configuration (HTTP) command: nginx -t changed_when: false -- name: Obtain SSL certificate (non-fatal) - block: - - name: Obtain SSL certificate - command: - cmd: "certbot --nginx --non-interactive --agree-tos --redirect -m {{ email }} -d {{ hostname }}" - register: certbot_result - changed_when: "'Congratulations' in certbot_result.stdout" - notify: Reload nginx - rescue: - - name: Certbot failed, leaving HTTP config in place - debug: - msg: "Certbot failed; keeping nginx running with current HTTP config so the site stays reachable." - -- name: Add wss config from nginx template +- name: Flush handlers (ensure HTTP vhost is active before certbot) + meta: flush_handlers + +# Obtain/renew cert without touching nginx config +- name: Obtain/renew certificate via webroot (non-fatal) + command: + cmd: > + certbot certonly --webroot + -w /var/www/{{ hostname }} + --non-interactive --agree-tos + --keep-until-expiring + -m {{ email }} -d {{ hostname }} + register: certbot_result + changed_when: "'Congratulations' in certbot_result.stdout" + failed_when: false + +# HTTPS vhost (only enable if cert exists) +- name: Check whether certificate exists + stat: + path: "/etc/letsencrypt/live/{{ hostname }}/fullchain.pem" + register: le_cert + +- name: Deploy HTTPS vhost for {{ hostname }} + template: + src: nginx-site-ssl.conf.j2 + dest: "/etc/nginx/sites-available/{{ hostname }}-ssl" + mode: "0644" + when: le_cert.stat.exists + notify: Restart nginx + +- name: Enable HTTPS vhost (force correct symlink) + file: + src: "/etc/nginx/sites-available/{{ hostname }}-ssl" + dest: "/etc/nginx/sites-enabled/{{ hostname }}-ssl" + state: link + force: true + when: le_cert.stat.exists + notify: Restart nginx + +# WSS vhost (only enable if cert exists) +- name: Deploy WSS vhost template: src: wss-config.conf.j2 dest: "/etc/nginx/sites-available/nym-wss-config" - notify: Reload nginx + mode: "0644" + when: le_cert.stat.exists + notify: Restart nginx -- name: Enable WSS config +- name: Enable WSS vhost (force correct symlink) file: src: "/etc/nginx/sites-available/nym-wss-config" dest: "/etc/nginx/sites-enabled/nym-wss-config" state: link - notify: Reload nginx + force: true + when: le_cert.stat.exists + notify: Restart nginx -- name: Validate nginx config after wss +- name: Validate nginx configuration (final) command: nginx -t changed_when: false -- name: Ensure nginx is enabled and running - service: - name: nginx - state: started - enabled: yes - -- name: Flush handlers (apply reloads after successful config tests) +- name: Flush handlers (apply restart after successful tests) meta: flush_handlers diff --git a/ansible/nym-node/roles/nginx/tasks/templates/nginx-site-ssl.conf.j2 b/ansible/nym-node/roles/nginx/tasks/templates/nginx-site-ssl.conf.j2 new file mode 100644 index 00000000000..26bd67533d7 --- /dev/null +++ b/ansible/nym-node/roles/nginx/tasks/templates/nginx-site-ssl.conf.j2 @@ -0,0 +1,30 @@ +server { + listen 443 ssl http2; + listen [::]:443 ssl http2; + + server_name {{ hostname }}; + + ssl_certificate /etc/letsencrypt/live/{{ hostname }}/fullchain.pem; + ssl_certificate_key /etc/letsencrypt/live/{{ hostname }}/privkey.pem; + + include /etc/letsencrypt/options-ssl-nginx.conf; + ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; + + # (Optional) keep this path valid; harmless even after cert issuance + location ^~ /.well-known/acme-challenge/ { + root /var/www/{{ hostname }}; + default_type "text/plain"; + try_files $uri =404; + } + + location / { + proxy_pass http://127.0.0.1:8080; + + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + + # Good practice when behind TLS + proxy_set_header X-Forwarded-Proto https; + } +} diff --git a/ansible/nym-node/roles/nginx/tasks/templates/nginx-site.conf.j2 b/ansible/nym-node/roles/nginx/tasks/templates/nginx-site.conf.j2 index e65548b5326..14f0e00b31d 100644 --- a/ansible/nym-node/roles/nginx/tasks/templates/nginx-site.conf.j2 +++ b/ansible/nym-node/roles/nginx/tasks/templates/nginx-site.conf.j2 @@ -4,10 +4,15 @@ server { server_name {{ hostname }}; + root /var/www/{{ hostname }}; + index index.html; + + location ^~ /.well-known/acme-challenge/ { + default_type "text/plain"; + try_files $uri =404; + } + location / { - proxy_pass http://127.0.0.1:8080; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header Host $host; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + return 301 https://$host$request_uri; } -} \ No newline at end of file +} From 02f5b86b86fd4edb9d2758d423b03873316a5b7b Mon Sep 17 00:00:00 2001 From: serinko <97586125+serinko@users.noreply.github.com> Date: Wed, 7 Jan 2026 12:52:45 +0100 Subject: [PATCH 6/9] finalize nginx and certbot configs --- ansible/nym-node/roles/nginx/tasks/main.yml | 86 +++++++++++++++---- .../tasks/templates/nginx-site-ssl.conf.j2 | 17 +--- .../nginx/tasks/templates/wss-config.conf.j2 | 5 +- ansible/nym-node/roles/nym/tasks/config.yml | 4 +- .../nym-node/roles/upgrade/tasks/finalize.yml | 2 +- 5 files changed, 74 insertions(+), 40 deletions(-) diff --git a/ansible/nym-node/roles/nginx/tasks/main.yml b/ansible/nym-node/roles/nginx/tasks/main.yml index 599dfba07fa..320431718e7 100644 --- a/ansible/nym-node/roles/nginx/tasks/main.yml +++ b/ansible/nym-node/roles/nginx/tasks/main.yml @@ -8,6 +8,31 @@ state: present update_cache: yes +- name: Ensure nginx snippets directory exists + file: + path: /etc/nginx/snippets + state: directory + mode: "0755" + +# own SSL defaults - don't rely on certbot files +- name: Install Nym SSL options snippet + copy: + dest: /etc/nginx/snippets/nym-ssl-options.conf + mode: "0644" + content: | + ssl_session_cache shared:NYMSSL:10m; + ssl_session_timeout 1d; + ssl_session_tickets off; + + ssl_protocols TLSv1.2 TLSv1.3; + ssl_prefer_server_ciphers off; + + # Reasonable modern cipher set (works across Ubuntu nginx builds) + ssl_ciphers "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305"; + + # OCSP stapling is nice but can break if resolver isn't set; keep minimal here. + notify: Restart nginx + - name: Ensure web root directory exists file: path: "/var/www/{{ hostname }}" @@ -21,7 +46,7 @@ mode: "0644" notify: Restart nginx -# Remove default site (safe on fresh + redeploy) +# remove default site - safe on fresh + redeploy - name: Disable default nginx site symlink file: path: /etc/nginx/sites-enabled/default @@ -34,8 +59,8 @@ state: absent notify: Restart nginx -# HTTP vhost (serves landing page + ACME challenge, and redirects other traffic to HTTPS) -- name: Deploy HTTP vhost for {{ hostname }} +# always deploy/enable HTTP vhost +- name: Deploy HTTP vhost template: src: nginx-site.conf.j2 dest: "/etc/nginx/sites-available/{{ hostname }}" @@ -50,44 +75,68 @@ force: true notify: Restart nginx +# detect if cert exists already +- name: Check whether certificate exists + stat: + path: "/etc/letsencrypt/live/{{ hostname }}/fullchain.pem" + register: le_cert + +# if cert does NOT exist yet, ensure SSL/WSS are NOT enabled +- name: Ensure SSL and WSS vhosts are disabled until cert exists + file: + path: "{{ item }}" + state: absent + loop: + - "/etc/nginx/sites-enabled/{{ hostname }}-ssl" + - "/etc/nginx/sites-enabled/nym-wss-config" + when: not le_cert.stat.exists + notify: Restart nginx + - name: Ensure nginx is enabled and running (needed for ACME http-01) service: name: nginx state: started enabled: yes -- name: Validate nginx configuration (HTTP) +- name: Validate nginx configuration (HTTP stage) command: nginx -t changed_when: false -- name: Flush handlers (ensure HTTP vhost is active before certbot) +- name: Flush handlers (ensure HTTP is active before certbot) meta: flush_handlers -# Obtain/renew cert without touching nginx config -- name: Obtain/renew certificate via webroot (non-fatal) +# certbot strategy: +# - if cert exists: webroot - doesn't touch nginx +# - else: --nginx works first-time; may touch nginx +- name: Obtain/renew certificate command: - cmd: > + cmd: >- + {% if le_cert.stat.exists %} certbot certonly --webroot -w /var/www/{{ hostname }} - --non-interactive --agree-tos - --keep-until-expiring + --non-interactive --agree-tos --keep-until-expiring + -m {{ email }} -d {{ hostname }} + {% else %} + certbot --nginx + --non-interactive --agree-tos --redirect -m {{ email }} -d {{ hostname }} + {% endif %} register: certbot_result - changed_when: "'Congratulations' in certbot_result.stdout" failed_when: false -# HTTPS vhost (only enable if cert exists) -- name: Check whether certificate exists +# re-check cert after certbot attempt +- name: Re-check whether certificate exists after certbot stat: path: "/etc/letsencrypt/live/{{ hostname }}/fullchain.pem" - register: le_cert + register: le_cert_after +# only deploy/enable SSL & WSS if cert exists - name: Deploy HTTPS vhost for {{ hostname }} template: src: nginx-site-ssl.conf.j2 dest: "/etc/nginx/sites-available/{{ hostname }}-ssl" mode: "0644" - when: le_cert.stat.exists + when: le_cert_after.stat.exists notify: Restart nginx - name: Enable HTTPS vhost (force correct symlink) @@ -96,16 +145,15 @@ dest: "/etc/nginx/sites-enabled/{{ hostname }}-ssl" state: link force: true - when: le_cert.stat.exists + when: le_cert_after.stat.exists notify: Restart nginx -# WSS vhost (only enable if cert exists) - name: Deploy WSS vhost template: src: wss-config.conf.j2 dest: "/etc/nginx/sites-available/nym-wss-config" mode: "0644" - when: le_cert.stat.exists + when: le_cert_after.stat.exists notify: Restart nginx - name: Enable WSS vhost (force correct symlink) @@ -114,7 +162,7 @@ dest: "/etc/nginx/sites-enabled/nym-wss-config" state: link force: true - when: le_cert.stat.exists + when: le_cert_after.stat.exists notify: Restart nginx - name: Validate nginx configuration (final) diff --git a/ansible/nym-node/roles/nginx/tasks/templates/nginx-site-ssl.conf.j2 b/ansible/nym-node/roles/nginx/tasks/templates/nginx-site-ssl.conf.j2 index 26bd67533d7..8ad1116c8ff 100644 --- a/ansible/nym-node/roles/nginx/tasks/templates/nginx-site-ssl.conf.j2 +++ b/ansible/nym-node/roles/nginx/tasks/templates/nginx-site-ssl.conf.j2 @@ -6,25 +6,12 @@ server { ssl_certificate /etc/letsencrypt/live/{{ hostname }}/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/{{ hostname }}/privkey.pem; - - include /etc/letsencrypt/options-ssl-nginx.conf; - ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; - - # (Optional) keep this path valid; harmless even after cert issuance - location ^~ /.well-known/acme-challenge/ { - root /var/www/{{ hostname }}; - default_type "text/plain"; - try_files $uri =404; - } + include /etc/nginx/snippets/nym-ssl-options.conf; location / { proxy_pass http://127.0.0.1:8080; - - proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; + proxy_set_header Host $host; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - - # Good practice when behind TLS - proxy_set_header X-Forwarded-Proto https; } } diff --git a/ansible/nym-node/roles/nginx/tasks/templates/wss-config.conf.j2 b/ansible/nym-node/roles/nginx/tasks/templates/wss-config.conf.j2 index 51ac957fb66..76ec941a72f 100644 --- a/ansible/nym-node/roles/nginx/tasks/templates/wss-config.conf.j2 +++ b/ansible/nym-node/roles/nginx/tasks/templates/wss-config.conf.j2 @@ -4,10 +4,9 @@ server { server_name {{ hostname }}; - ssl_certificate /etc/letsencrypt/live/{{ hostname }}/fullchain.pem; + ssl_certificate /etc/letsencrypt/live/{{ hostname }}/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/{{ hostname }}/privkey.pem; - include /etc/letsencrypt/options-ssl-nginx.conf; - ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; + include /etc/nginx/snippets/nym-ssl-options.conf; access_log /var/log/nginx/access.log; error_log /var/log/nginx/error.log; diff --git a/ansible/nym-node/roles/nym/tasks/config.yml b/ansible/nym-node/roles/nym/tasks/config.yml index 5b742792e0c..7c374e0a7fc 100644 --- a/ansible/nym-node/roles/nym/tasks/config.yml +++ b/ansible/nym-node/roles/nym/tasks/config.yml @@ -1,5 +1,5 @@ --- -# Useful when the host is behind a NAT +# useful when the host is behind a NAT - name: Fetch the public IP address command: "curl -4 canhazip.com" register: ipv4 @@ -11,7 +11,7 @@ public_ip: "{{ ipv4.stdout | default(ansible_default_ipv4.address) }}" - name: Initialize nym node - # Delete the part from --hostname onward if you run mode=mixnode only + # delete the part from --hostname onward if you run mode=mixnode only command: cmd: > {{ nym_install_dir }}/nym-node run diff --git a/ansible/nym-node/roles/upgrade/tasks/finalize.yml b/ansible/nym-node/roles/upgrade/tasks/finalize.yml index e6af60b342f..b17d74dcc5a 100644 --- a/ansible/nym-node/roles/upgrade/tasks/finalize.yml +++ b/ansible/nym-node/roles/upgrade/tasks/finalize.yml @@ -116,7 +116,7 @@ when: not ansible_check_mode and (upgrade_ok | default(false)) == false # optional: hard-fail the play for CI environments -#- name: Fail the play to signal upgrade failure +#- name: fail the play to signal upgrade failure # fail: # msg: "nym-node upgrade failed; rolled back to previous binary." # when: not ansible_check_mode and (upgrade_ok | default(false)) == false From ca0d8e539534c2dfd71e337fe29a96828259883f Mon Sep 17 00:00:00 2001 From: serinko <97586125+serinko@users.noreply.github.com> Date: Wed, 7 Jan 2026 12:59:05 +0100 Subject: [PATCH 7/9] add nginx purge command --- .../nym-node/configuration/proxy-configuration.mdx | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/documentation/docs/pages/operators/nodes/nym-node/configuration/proxy-configuration.mdx b/documentation/docs/pages/operators/nodes/nym-node/configuration/proxy-configuration.mdx index 8165f8a70a6..2c7faad6df9 100644 --- a/documentation/docs/pages/operators/nodes/nym-node/configuration/proxy-configuration.mdx +++ b/documentation/docs/pages/operators/nodes/nym-node/configuration/proxy-configuration.mdx @@ -332,3 +332,13 @@ wscat -c wss://: ``` - Check Swagger API of your node using the hostname: `https:///api/v1/swagger/#/` + +## Troubleshooting + +In some cases Nginx may cache expired certificates, old configurations and other snippets creating confussion in a proper routing of your server. To purge this cache you can run: +```sh +apt purge nginx nginx-common +apt install nginx +service nginx reload && service nginx restart +``` +This will pickup only the current configuration. \ No newline at end of file From 318d625cea969f8ff3d528b4cd62aa00795f2d7b Mon Sep 17 00:00:00 2001 From: serinko <97586125+serinko@users.noreply.github.com> Date: Wed, 7 Jan 2026 13:35:27 +0100 Subject: [PATCH 8/9] fix typo --- ansible/nym-node/playbooks/group_vars/all.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ansible/nym-node/playbooks/group_vars/all.yml b/ansible/nym-node/playbooks/group_vars/all.yml index 9f309d1e4b4..d48861c05ac 100644 --- a/ansible/nym-node/playbooks/group_vars/all.yml +++ b/ansible/nym-node/playbooks/group_vars/all.yml @@ -41,4 +41,4 @@ packages: - ca-certificates - jq - wget - - ufinteroperabilityw + - ufw From 9c3c023616087a0b1adfb0fd28711f26c3f99580 Mon Sep 17 00:00:00 2001 From: serinko <97586125+serinko@users.noreply.github.com> Date: Wed, 7 Jan 2026 14:30:50 +0100 Subject: [PATCH 9/9] add removing vm guide --- .../preliminary-steps/vps-setup/advanced.mdx | 41 +++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/documentation/docs/pages/operators/nodes/preliminary-steps/vps-setup/advanced.mdx b/documentation/docs/pages/operators/nodes/preliminary-steps/vps-setup/advanced.mdx index 95c3a2a864e..9863df4d92c 100644 --- a/documentation/docs/pages/operators/nodes/preliminary-steps/vps-setup/advanced.mdx +++ b/documentation/docs/pages/operators/nodes/preliminary-steps/vps-setup/advanced.mdx @@ -803,3 +803,44 @@ ssh root@ -i ~/.ssh/your_ssh_key Now your VM is almost ready for `nym-node` [setup](../../nym-node/setup). Before you proceed, ssh in and [configure all prerequisities](../vps-setup#vps-configuration) needed for `nym-node` installation and operation. + +## Removing Virtual Machines + +If you setup your VM in a wrong way, or you simply don't use it anymore, you can remove it. + + +**These commands will erase everything on the VM, make sure to backup everything you may need in the future bewfore executing this!** + + + +###### 1. SSH to the host server + +###### 2. List all VMs +```sh +virsh list --all +``` + +###### 3. Shut down and remove VM +- To remove a VM run this sequence +```sh +# shutdown +virsh shutdown +sleep 10 + +# destroy +virsh destroy + +# undefine and purge storage +virsh undefine --remove-all-storage + +# ensure the storage is deleted +rm /var/lib/libvirt/images/.img +``` + +###### 4. List all VMs again + +- The list should not contain the VM that you just deleted: +```sh +virsh list --all +``` + \ No newline at end of file