feat(pm): block missing nginx host connections #98

closed
opened by a.starrysky.fyi targeting main from private/minion/push-zoqyltwplryx

We previously returned One Of The Websites when nginx was accessed from a host that we didn't know about. That included direct IP address access as well as things which have been CNAMEd to us (either through a starred record or due to past services) but which aren't actually hosted by us.

This leads to a number of undesireable effects:

  • User confusion ("why does the aux docs website have Stalwart?")
  • Incorrect SSL certificates ("your blog seems to have an invalid certificate")
  • SSL being offered via direct IPs, which isn't possible to sign on the public internet

We can block this by making a default server to take control whenever nothing matches, and setting that default server to block all connections and reject all SSL handshakes

We need to have a certificate for this, but it needn't actually be valid for anything so let's self sign stuff...

Changed files
+58 -3
packetmix
systems
+36
packetmix/systems/common/nginx.nix
··· 1 + # SPDX-FileCopyrightText: 2025 FreshlyBakedCake 2 + # 3 + # SPDX-License-Identifier: MIT 4 + 5 + { lib, ... }: 6 + { 7 + # By default, nginx will serve a "best-effort" site even if there is no matching vhost 8 + # We can disable this by making a matching vhost and returning 444... 9 + # Notice how we don't enable nginx here: that makes this safe to deploy even on places that don't currently run nginx. We're effectively changing the default behavior 10 + services.nginx.virtualHosts."missinghost.invalid" = { 11 + default = true; 12 + 13 + addSSL = true; 14 + enableACME = true; 15 + acmeRoot = null; 16 + 17 + locations."/".return = "444"; 18 + 19 + extraConfig = '' 20 + ssl_reject_handshake on; 21 + ''; 22 + }; 23 + 24 + systemd.services."acme-missinghost.invalid".enable = false; 25 + systemd.timers."acme-missinghost.invalid".enable = false; 26 + 27 + systemd.targets."acme-finished-missinghost.invalid" = { 28 + requires = lib.mkForce [ "acme-selfsigned-missinghost.invalid.service" ]; 29 + after = lib.mkForce [ "acme-selfsigned-missinghost.invalid.service" ]; 30 + }; 31 + 32 + security.acme.certs."missinghost.invalid" = { 33 + webroot = "/dev/null"; 34 + email = "invalid@missinghost.invalid"; 35 + }; # Nix requires some values, even if we're actually disabling the acme-missinghost.invalid service... that's problematic if there are no defaults for the system 36 + }
+21
packetmix/systems/umber/grocy.nix
··· 1 + # SPDX-FileCopyrightText: 2025 FreshlyBakedCake 2 + # 3 + # SPDX-License-Identifier: MIT 4 + 5 + { lib, ... }: 6 + { 7 + services.grocy = { 8 + enable = true; 9 + hostName = "grocy.starrysky.fyi"; 10 + 11 + settings.currency = "GBP"; 12 + }; 13 + 14 + services.nginx.virtualHosts."grocy.starrysky.fyi" = { 15 + acmeRoot = null; 16 + forceSSL = lib.mkForce false; 17 + onlySSL = true; 18 + }; 19 + 20 + clicks.storage.impermanence.persist.directories = [ "/var/lib/grocy" ]; 21 + }
+1 -3
packetmix/systems/umber/silverbullet.nix
··· 28 28 services.nginx.virtualHosts."silverbullet.starrysky.fyi" = { 29 29 listenAddresses = [ "localhost.tailscale" ]; 30 30 31 - addSSL = true; 31 + onlySSL = true; 32 32 enableACME = true; 33 33 acmeRoot = null; 34 - 35 - serverAliases = [ "umber.clicks.domains" ]; 36 34 37 35 locations."/" = { 38 36 proxyPass = "http://$silverbullet_upstream_minion_only";