From 7579d4dcf5200ee260416031e0c1736bb548f922 Mon Sep 17 00:00:00 2001 From: Lars Nolden Date: Fri, 26 Jun 2026 16:12:57 +0200 Subject: [PATCH] change domains, ips and make flake check work --- .gitea/workflows/deploy.yml | 2 +- README.md | 23 ++++++++++++++++++----- frontend/default.nix | 5 ++--- frontend/src/supabase.ts | 2 +- frontend/src/vite-env.d.ts | 1 + nix/deploy.nix | 9 ++++++--- nix/dev/process-compose.nix | 21 ++++++++++++++++----- nix/hosts/app-host.nix | 6 +++--- nix/hosts/common.nix | 4 ++++ nix/hosts/mqtt-ingest.nix | 6 +++--- nix/modules/gebos-caddy.nix | 2 +- nix/supabase/docker-compose.yml | 8 ++++---- 12 files changed, 60 insertions(+), 29 deletions(-) create mode 100644 frontend/src/vite-env.d.ts diff --git a/.gitea/workflows/deploy.yml b/.gitea/workflows/deploy.yml index 9441ceb..9631a6f 100644 --- a/.gitea/workflows/deploy.yml +++ b/.gitea/workflows/deploy.yml @@ -13,7 +13,7 @@ jobs: run: | mkdir -p ~/.ssh install -m 600 /dev/stdin ~/.ssh/id_ed25519 <<< "${{ secrets.DEPLOY_SSH_KEY }}" - ssh-keyscan -H db-host.gebos.internal app-host.gebos.internal ingest.ge-bos.de >> ~/.ssh/known_hosts 2>/dev/null || true + ssh-keyscan -H db.gebos.online app.gebos.online ingest.gebos.online >> ~/.ssh/known_hosts 2>/dev/null || true - name: Build all closures run: nix flake check --no-build diff --git a/README.md b/README.md index 492da85..44f87f9 100644 --- a/README.md +++ b/README.md @@ -9,15 +9,28 @@ and CI/CD pipelines for the project. Everything is one `flake.nix`. Three hosts (see issue #21 for the full design discussion): -| Host | Role | Public hostname | -| ------------- | ----------------------------------------- | ---------------------- | -| `mqtt-ingest` | HiveMQ CE + Go MQTT→Postgres ingester | `ingest.ge-bos.de` | -| `db-host` | Postgres 17 + TimescaleDB (telemetry+auth)| internal only | -| `app-host` | Caddy + Kong + Supabase (compose) + SPA | `app.ge-bos.de`, `api.ge-bos.de` | +| Host | Role | Public hostname | Private IP | +| ------------- | ----------------------------------------- | ---------------------- | ---------- | +| `mqtt-ingest` | HiveMQ CE + Go MQTT→Postgres ingester | `ingest.gebos.online` | `10.0.0.4` | +| `db-host` | Postgres 17 + TimescaleDB (telemetry+auth)| `db.gebos.online` (SSH only) | `10.0.0.2` | +| `app-host` | Caddy + Kong + Supabase (compose) + SPA | `app.gebos.online`, `api.gebos.online` | `10.0.0.3` | Public REST surface is PostgREST + SQL `/rpc/` functions, fronted by Kong, TLS-terminated by Caddy. Supabase Studio is bound to `127.0.0.1` on `app-host` — reach it with `ssh -L 3000:127.0.0.1:3000 app-host`. +### Networking + +There is no private DNS. Each host has a public hostname (used for `deploy-rs` +SSH access from the Gitea runner, which is **not** on the private network, plus +TLS ingress where applicable), and a static `10.0.0.0/8` IP used for all +host-to-host traffic: + +- `db.gebos.online` is for SSH/deploy only — Postgres is never exposed publicly. +- `app-host` and `mqtt-ingest` reach Postgres at `10.0.0.2:5432` over the private + network. +- `db-host` only accepts Postgres (port 5432) from `10.0.0.0/8` (firewall rule + in `nix/hosts/db-host.nix`). + ## Repo layout ``` diff --git a/frontend/default.nix b/frontend/default.nix index 177259e..f24508f 100644 --- a/frontend/default.nix +++ b/frontend/default.nix @@ -1,12 +1,11 @@ -{ buildNpmPackage, lib }: +{ buildNpmPackage }: buildNpmPackage { pname = "gebos-frontend"; version = "0.0.0"; src = ./.; - # Filled in once package-lock.json exists. - npmDepsHash = lib.fakeHash; + npmDepsHash = "sha256-GeXgNbf94QRLNaZP8ma2vx19Vzm8jDWPeOTvE9exwm0="; installPhase = '' runHook preInstall diff --git a/frontend/src/supabase.ts b/frontend/src/supabase.ts index 3974838..d1ab1fe 100644 --- a/frontend/src/supabase.ts +++ b/frontend/src/supabase.ts @@ -11,7 +11,7 @@ const DEV_SUPABASE_ANON_KEY = const SUPABASE_URL = import.meta.env.VITE_SUPABASE_URL ?? - (import.meta.env.DEV ? DEV_SUPABASE_URL : "https://api.ge-bos.de"); + (import.meta.env.DEV ? DEV_SUPABASE_URL : "https://api.gebos.online"); const SUPABASE_ANON_KEY = import.meta.env.VITE_SUPABASE_ANON_KEY ?? diff --git a/frontend/src/vite-env.d.ts b/frontend/src/vite-env.d.ts new file mode 100644 index 0000000..11f02fe --- /dev/null +++ b/frontend/src/vite-env.d.ts @@ -0,0 +1 @@ +/// diff --git a/nix/deploy.nix b/nix/deploy.nix index 530b973..cbbdaf8 100644 --- a/nix/deploy.nix +++ b/nix/deploy.nix @@ -18,8 +18,11 @@ in nodes = { # Order matters: the Gitea Actions workflow invokes them in this sequence. - db-host = mkNode "db-host" "db-host.gebos.internal"; # TODO: real address - app-host = mkNode "app-host" "app-host.gebos.internal"; # TODO: real address - mqtt-ingest = mkNode "mqtt-ingest" "ingest.ge-bos.de"; + # SSH/deploy targets use public hostnames because the Gitea runner is not on + # the private network. Host-to-host traffic still uses the 10.0.0.0/8 IPs. + # See the host address table in README.md. + db-host = mkNode "db-host" "db.gebos.online"; + app-host = mkNode "app-host" "app.gebos.online"; + mqtt-ingest = mkNode "mqtt-ingest" "ingest.gebos.online"; }; } diff --git a/nix/dev/process-compose.nix b/nix/dev/process-compose.nix index 4853788..d487c54 100644 --- a/nix/dev/process-compose.nix +++ b/nix/dev/process-compose.nix @@ -38,6 +38,13 @@ let VITE_SUPABASE_URL = dev.supabaseUrl; VITE_SUPABASE_ANON_KEY = dev.anonKey; }; + + # services-flake ships no MQTT broker, so run mosquitto as a plain + # process-compose process. Anonymous access on the dev listener only. + mosquittoConf = pkgs.writeText "mosquitto-dev.conf" '' + listener ${toString dev.mqttPort} ${dev.mqttHost} + allow_anonymous true + ''; in { imports = [ services-flake.processComposeModules.default ]; @@ -48,14 +55,18 @@ in port = dev.pgPort; initialDatabases = [{ name = dev.pgDB; }]; }; - - mosquitto."broker" = { - enable = true; - port = dev.mqttPort; - }; }; settings.processes = { + broker = { + command = "${pkgs.mosquitto}/bin/mosquitto -c ${mosquittoConf}"; + readiness_probe = { + exec.command = "${pkgs.mosquitto}/bin/mosquitto_sub -h ${dev.mqttHost} -p ${toString dev.mqttPort} -t '$$SYS/#' -C 1 -W 2"; + initial_delay_seconds = 1; + period_seconds = 2; + }; + }; + ingester = { command = "${pkgs.go}/bin/go run ./ingester"; environment = ingesterEnv; diff --git a/nix/hosts/app-host.nix b/nix/hosts/app-host.nix index 7dcc1bc..cbf29b9 100644 --- a/nix/hosts/app-host.nix +++ b/nix/hosts/app-host.nix @@ -8,7 +8,7 @@ services.gebos.supabase = { enable = true; # Compose stack points at external db-host instead of the bundled `db` service. - postgresHost = "db-host.gebos.internal"; # TODO: real internal hostname + postgresHost = "10.0.0.2"; # db-host on the private network postgresPort = 5432; # Studio bound to loopback only — reach via `ssh -L 3000:127.0.0.1:3000 app-host`. studioBindAddress = "127.0.0.1"; @@ -18,8 +18,8 @@ enable = true; # Caddy terminates TLS, proxies to Kong on 127.0.0.1:8000. sites = { - "api.ge-bos.de" = { upstream = "127.0.0.1:8000"; }; # → Kong → PostgREST/GoTrue - "app.ge-bos.de" = { staticRoot = "${config.services.gebos.frontend.package}/share/frontend"; }; + "api.gebos.online" = { upstream = "127.0.0.1:8000"; }; # → Kong → PostgREST/GoTrue + "app.gebos.online" = { staticRoot = "${config.services.gebos.frontend.package}/share/frontend"; }; }; }; diff --git a/nix/hosts/common.nix b/nix/hosts/common.nix index 51486ba..c3a9114 100644 --- a/nix/hosts/common.nix +++ b/nix/hosts/common.nix @@ -23,6 +23,10 @@ enable = true; settings.PasswordAuthentication = false; }; + # TODO: restrict inbound SSH (port 22) to the Gitea deployment runner's IP + # only, instead of leaving it open to the world. Likely via + # services.openssh.openFirewall = false + a firewall.extraInputRules rule + # accepting tcp dport 22 from the runner's source address. networking.firewall.enable = true; diff --git a/nix/hosts/mqtt-ingest.nix b/nix/hosts/mqtt-ingest.nix index c6d39bc..25b27bd 100644 --- a/nix/hosts/mqtt-ingest.nix +++ b/nix/hosts/mqtt-ingest.nix @@ -15,15 +15,15 @@ services.gebos.ingester = { enable = true; # mqttBroker defaults to tcp://127.0.0.1:1883 (the local HiveMQ). - postgresUrl = "postgres://gebos_ingest@db-host.gebos.internal:5432/postgres?sslmode=require"; + postgresUrl = "postgres://gebos_ingest@10.0.0.2:5432/postgres?sslmode=require"; # GEBOS_POSTGRES_PASSWORD sourced from /run/secrets/gebos-env (sops-nix). }; - # Caddy fronts TLS for `ingest.ge-bos.de` and forwards to HiveMQ. + # Caddy fronts TLS for `ingest.gebos.online` and forwards to HiveMQ. services.gebos.caddy = { enable = true; sites = { - "ingest.ge-bos.de" = { tcpProxy = "127.0.0.1:8883"; }; + "ingest.gebos.online" = { tcpProxy = "127.0.0.1:8883"; }; }; }; diff --git a/nix/modules/gebos-caddy.nix b/nix/modules/gebos-caddy.nix index 56d4790..36db762 100644 --- a/nix/modules/gebos-caddy.nix +++ b/nix/modules/gebos-caddy.nix @@ -39,7 +39,7 @@ in config = lib.mkIf cfg.enable { services.caddy = { enable = true; - email = "ops@ge-bos.de"; # TODO: confirm ACME contact + email = "ops@gebos.online"; # TODO: confirm ACME contact extraConfig = lib.concatStringsSep "\n" (lib.mapAttrsToList siteBlock cfg.sites); }; diff --git a/nix/supabase/docker-compose.yml b/nix/supabase/docker-compose.yml index c9cba13..c5311e9 100644 --- a/nix/supabase/docker-compose.yml +++ b/nix/supabase/docker-compose.yml @@ -16,7 +16,7 @@ services: image: kong:2.8.1 restart: unless-stopped ports: - - "127.0.0.1:8000:8000/tcp" # Caddy proxies api.ge-bos.de here + - "127.0.0.1:8000:8000/tcp" # Caddy proxies api.gebos.online here environment: KONG_DATABASE: "off" KONG_DECLARATIVE_CONFIG: /home/kong/kong.yml @@ -29,10 +29,10 @@ services: environment: GOTRUE_DB_DRIVER: postgres GOTRUE_DB_DATABASE_URL: postgres://supabase_auth_admin:${POSTGRES_PASSWORD}@${POSTGRES_HOST}:${POSTGRES_PORT}/${POSTGRES_DB} - GOTRUE_SITE_URL: https://app.ge-bos.de + GOTRUE_SITE_URL: https://app.gebos.online GOTRUE_JWT_SECRET: ${JWT_SECRET} GOTRUE_JWT_EXP: "3600" - API_EXTERNAL_URL: https://api.ge-bos.de + API_EXTERNAL_URL: https://api.gebos.online rest: image: postgrest/postgrest:v12.0.2 @@ -52,7 +52,7 @@ services: STUDIO_PG_META_URL: http://meta:8080 POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} DEFAULT_ORGANIZATION_NAME: Gebos - SUPABASE_URL: https://api.ge-bos.de + SUPABASE_URL: https://api.gebos.online DASHBOARD_USERNAME: gebos DASHBOARD_PASSWORD: ${DASHBOARD_PASSWORD}