diff --git a/nixos/machines/railbird-sf.nix b/nixos/machines/railbird-sf.nix index 6224ed8d..7fa8ae0a 100644 --- a/nixos/machines/railbird-sf.nix +++ b/nixos/machines/railbird-sf.nix @@ -30,6 +30,11 @@ file = ../secrets/org-api-ssh-key.age; mode = "0400"; # Restrictive permissions for SSH key }; + # DuckDNS token for the rocket-sense.duckdns.org dynamic-DNS updater below. + age.secrets.duckdns-token = { + file = ../secrets/duckdns-token.railbird-sf.age; + mode = "0400"; + }; services.org-agenda-api-host = { enable = true; @@ -121,6 +126,40 @@ security.acme.certs."rocket-sense.duckdns.org".extraDomainNames = ["rbsf.tplinkdns.com"]; + # Dynamic-DNS updater: keep rocket-sense.duckdns.org pointed at this node's + # current public IP. The residential WAN IP changes (e.g. after an ISP + # outage/failover), and without this the public hostname goes stale and the + # site becomes unreachable until manually re-pointed. Leaving ip= blank tells + # DuckDNS to detect the source IP of this request, so it always reflects + # whatever connection the node is currently egressing through. + systemd.services.duckdns = { + description = "DuckDNS updater for rocket-sense.duckdns.org"; + after = ["network-online.target"]; + wants = ["network-online.target"]; + serviceConfig = { + Type = "oneshot"; + DynamicUser = true; + LoadCredential = ["token:${config.age.secrets.duckdns-token.path}"]; + }; + script = '' + token="$(cat "$CREDENTIALS_DIRECTORY/token")" + resp="$(${pkgs.curl}/bin/curl -fsS --retry 3 --max-time 30 \ + "https://www.duckdns.org/update?domains=rocket-sense&token=$token&ip=")" + echo "DuckDNS response: $resp" + [ "$resp" = "OK" ] || { echo "DuckDNS update failed"; exit 1; } + ''; + }; + + systemd.timers.duckdns = { + description = "Periodic DuckDNS update for rocket-sense.duckdns.org"; + wantedBy = ["timers.target"]; + timerConfig = { + OnBootSec = "1min"; + OnUnitActiveSec = "5min"; + Persistent = true; + }; + }; + # Open the standard Syncthing sync/discovery ports on the host firewall. # Note: you may still need router/NAT port-forwards for inbound access from the internet. services.syncthing.openDefaultPorts = true; diff --git a/nixos/secrets/duckdns-token.railbird-sf.age b/nixos/secrets/duckdns-token.railbird-sf.age new file mode 100644 index 00000000..f6d3f480 --- /dev/null +++ b/nixos/secrets/duckdns-token.railbird-sf.age @@ -0,0 +1,58 @@ +age-encryption.org/v1 +-> ssh-ed25519 ZgrTqA dbWFiSzLK7bR7hT7GwIMNNvD8WnZreFfDrtcA+LY/BE +OzTFip9dHKtI7EKWa/ati8LwVJJbSE6G3t6QFg4G6KQ +-> ssh-ed25519 ZaBdSg VkV8WJLCadquI8PH6mlEhFyXnBBQLtBAyixQJqaOT2w +xhyXTY215Vd8rWzkq8qD4rcHu3JjJh4wRG2+NGoYLnQ +-> ssh-ed25519 MHZylw Mjfvi/elwLcWlNhCBfYulnV6DkYdM2en0uhJUXctfQk +EspZ5/1PRZAEatU/Jh/s77aizKfQSpQ9ulipwCZS0tk +-> ssh-ed25519 sIUg6g gMwhuJAx5lURu7nD3TeWw8v7n9Kmn8Na83J6iNye1U4 +FYtpIENIwosPhD4fOd8hjvbAYIhbJM+y+XiSeBjGIIM +-> ssh-ed25519 TnanwQ ihbJQDgyaoiSq30PktEjTlcQlzrXNKVVVVAT29eSI2g +ap3GKydDNX4nZC6mBaGC0BGFN0TveqcFOu91bbFJ2rs +-> ssh-ed25519 cH4aug YUs7dBlvrWhWU3WYPvkN30ozOgqvoMGOz1R9TDiVJVg +cuA4RDIc9m4og0zm+dd+3zO7OnbnI4Iisc/HCWEgQts +-> ssh-ed25519 ggrAFQ o2eVPsHgVCD+3EnYTzjziiYeqViVTFlmTtpe0oGulgU ++C2Yd50lkcXhQRnIu12EyY6U3Mjf8hAZZ33saicTWBo +-> ssh-rsa gwJx0Q +xaIephd6lidO23X/owa9Xn2SBqJxjjiaMoot/gn9gygTl9/TwvQ6g/XBXbuDjLkn +LjSJ9cRoyYMbzv8z21rmtuNRibNCoMR75+zff4BMf6tPpzdrtVAw05edwYp5hxtg +G9kv70WiLLah11Nn8ebluH1tMjbO4LKhy5chomielQ8yk27XBzRZS8M3Plmy2+za +AlUeUOsk5ExYl4jjbkz6RsHSryThbQjrcEBBYT3MVFzE4btiNsdCVsW5qfugZLnh +c9MSPzeDk5IrfNOZ35ZeFVUDkyrNuHno4VZKLp07I9mRPqLvvkOaAtIEcBag8agd +W/D5O3idVNc64/tp4icckw +-> ssh-ed25519 YFIoHA 7SE4NKISDqbR1aJmfLy3hXkPg12kUbiMShQ3hx+zOUw +SGQOn5t7dtt3e3+s4cvnoDGRjViQmDccFJWMF4HGsY8 +-> ssh-ed25519 KQfiow LwUOPqeamtQ1i1RcxWEAtQDhHUny8kph4wmiRuPUnHs +q7VAs48mMeonKEn8IdX97CgxOHjRbY0nIYUbOmmysII +-> ssh-ed25519 kScIxg 4bEfbh/SnGsW8Kblr6Pzap1oobu+V+UsTeLHl4UAURs +7/t8Mo1jpr8650s3w/4L9FEfHLR/sH90xsBWKVzI2As +-> ssh-ed25519 HzX1zw qhd1iJVv4s7TbLAfVTqUTxxBaDsccDM/ypApAZcY5mE +oanEYt0MN3pcemOUMX+8sE7XxHW8h/IZ4dxaBJXh8Pk +-> ssh-ed25519 KQfiow IBfrzlbi++0rIa3A33S4gaFpBSOGAszqxeeFKLWpBjo +yBB/8ZnsIe4lPIR0vRLDpuFFLFgSxcryV1KCON257fw +-> ssh-ed25519 1o2X0w xUUvBiLbre/LWzHzMBbfE5unuO3UhGa7OcDi6rPy2Q8 +KEuttdevVM3gPbea8xwq375qwf8TtrBPt3DeUUNH3EY +-> ssh-ed25519 KQ5iUA +h6kpwlMYc/Q4NbKpEsRCA6RjPpN5D1aV7eZeDFBDkk +Frm74ENiTFXBu/CjGYT0jRuPVO9qgHw5zoV/TbIOjUo +-> ssh-ed25519 AKGkDw aE1oI3u8LrY3XMymyoQUU5r5MyGDSpSMbcHtEZShwiU +gY9V3AbkonUl4sLcdfwaUFe0xKQIRxsFv2fq8VG+U0M +-> ssh-ed25519 0eS5+A zdhSEeS0/gD5WGTCmPK3jVraO/jzgRhwD0/qOHpz8GQ +QldMBke5zXTlVodSeKF0B8MTzxYipQ9cX6/VoaEZVp4 +-> ssh-ed25519 9/4Prw JgMakHMTZyE7BNepkgTOGeMxkKcWEmUcq1CRm+IscyI +A/0EJhNWgofrESAaH3x12QpQujJ/bIIm9iGdGb9KQGg +-> ssh-ed25519 gAk3+Q EEorMwdbnlr3BsEdu9gg7jzivtnqzEPgGwZokSxGG2g +d9pq1Rbz685QKjcEF0KH3mIKwIzwZIVFkl+ujtT20rU +-> ssh-ed25519 X6eGtQ 7fWfLj6hTNYj1BxGPHlrcV9LdV75hYAAr+HXRT2GwWU +PjfOl8mGOhxWRfIGSiZ0l8Z/MXO8P1iBQnBUF+bLLl0 +-> ssh-ed25519 0ma8Cw /FJJIBd2qfD2eNrXfDvd6cXxGeXbbYwSQktjyL1THQE +Q/wv7dasqq5yXe6RQdlsfgqW4ibxPHoreee1xBHi5Gs +-> ssh-ed25519 Tp0Z1Q fxdPsJ9c73yp6cU9UdAHX9YfF29U//wUCAucCBE44V4 +lfTkurTHTanPAO3VbYoGRYHoWZ+BlwIoHE8lhVeRqXk +-> ssh-ed25519 ePNWZQ Qmc1C1keGmWgob+Xjh4kaVPAtDk6rG8PEvtdWFlbuxU +vKVO5t6TK4aWvMPw3O8ALq2glenVHEGFEGtYgsGf0k8 +-> ssh-ed25519 hILzzA DgZif0MMn8xBc9b8qi0IrjsUAFrZW79Rx5aRIRQNXn4 +ULkpXlqF0/zEl/8urDRipv9ErbRhxXE1ipojjuu6+sI +-> ssh-ed25519 qQi7yA EfyNuGZmnrRF+ikVpmaeyLRkiUzYsjNfovMlq1acTj0 +0LuL347kis91fjiCOFpViIoO92cp5dFXJ22CL1GMJes +--- TwXaFhbbk8goUF8gbDnISLXupN+vmC9PgXoadMcVDaY +ϙCIIkQwBU'5CTXGU>i8};[%d;#)).$[F \ No newline at end of file diff --git a/nixos/secrets/secrets.nix b/nixos/secrets/secrets.nix index 85ecbb69..7d1c8566 100644 --- a/nixos/secrets/secrets.nix +++ b/nixos/secrets/secrets.nix @@ -18,6 +18,7 @@ in { "1896Folsom-k3s-token.age".publicKeys = keys.agenixKeys ++ keys.railbird-sf; "api_service_account_key.json.age".publicKeys = keys.agenixKeys; "k3s-registry.yaml.age".publicKeys = keys.agenixKeys ++ keys.railbird-sf; + "duckdns-token.railbird-sf.age".publicKeys = keys.agenixKeys ++ keys.railbird-sf; "discourse-admin-password.age".publicKeys = keys.hostKeys; "discourse-secret-key-base.age".publicKeys = keys.hostKeys; "vaultwarden-environment-file.age".publicKeys = keys.hostKeys;