feat: add a Caddy reverse-proxy option to the NixOS module
This commit is contained in:
parent
4075afbb11
commit
a244dab94e
2 changed files with 47 additions and 3 deletions
|
|
@ -58,7 +58,7 @@ For a NixOS host, `nixosModules.default` provides `services.gsmol-bridge`:
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
The service runs under `DynamicUser`, restarts on failure, and — like the standalone script — listens on `127.0.0.1` by default. `nginx.enable` puts a TLS-terminating reverse proxy in front rather than exposing the bridge directly: it has no TLS of its own, and a browser refuses a `ws://` connection from an `https://` page, so a real deployment needs the proxy regardless of `--allow-origin`. `openFirewall = true` is for exposing the bridge's port directly instead, which is not the recommended path (see "Notes and limits").
|
The service runs under `DynamicUser`, restarts on failure, and — like the standalone script — listens on `127.0.0.1` by default. `nginx.enable` (or `caddy.enable`, wiring into `services.caddy.virtualHosts` — automatic HTTPS, no `enableACME`/`forceSSL` equivalent to set) puts a TLS-terminating reverse proxy in front rather than exposing the bridge directly: it has no TLS of its own, and a browser refuses a `ws://` connection from an `https://` page, so a real deployment needs one of the two regardless of `--allow-origin`. Enable only one; a deployment that replaces `services.caddy.configFile` with a hand-written Caddyfile instead of using `virtualHosts` needs the domain added there directly, proxying to `host`:`port`. `openFirewall = true` is for exposing the bridge's port directly instead of either, which is not the recommended path (see "Notes and limits").
|
||||||
|
|
||||||
`services.gsmol-bridge.presetServer = { host, publicKey }` bakes a server's key into the pages this deployment serves, so a first-time visitor is pre-pinned instead of asked to copy a base32 key into settings by hand (§4). The value reaches the browser over this deployment's own TLS, so it only stands in for "from the operator, through a trusted channel" when the deployment operator and the smolmaild operator are the same trusted party — set it only for a server you operate or otherwise vouch for. It seeds the first run only: the browser stores it as an ordinary pin from then on, replaceable or removable in settings like any other.
|
`services.gsmol-bridge.presetServer = { host, publicKey }` bakes a server's key into the pages this deployment serves, so a first-time visitor is pre-pinned instead of asked to copy a base32 key into settings by hand (§4). The value reaches the browser over this deployment's own TLS, so it only stands in for "from the operator, through a trusted channel" when the deployment operator and the smolmaild operator are the same trusted party — set it only for a server you operate or otherwise vouch for. It seeds the first run only: the browser stores it as an ordinary pin from then on, replaceable or removable in settings like any other.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -72,8 +72,9 @@ in
|
||||||
Address the bridge listens on. The bridge speaks plain HTTP/WS with no
|
Address the bridge listens on. The bridge speaks plain HTTP/WS with no
|
||||||
TLS of its own, and browsers refuse a `ws://` connection from an
|
TLS of its own, and browsers refuse a `ws://` connection from an
|
||||||
`https://` page — so leave this at loopback and put a reverse proxy
|
`https://` page — so leave this at loopback and put a reverse proxy
|
||||||
(see `services.gsmol-bridge.nginx`) in front for TLS and public
|
(`services.gsmol-bridge.nginx` or `.caddy`) in front for TLS and
|
||||||
exposure, rather than binding this directly to a public interface.
|
public exposure, rather than binding this directly to a public
|
||||||
|
interface.
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -123,10 +124,42 @@ in
|
||||||
description = "Public hostname to serve gsmol from.";
|
description = "Public hostname to serve gsmol from.";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
caddy = {
|
||||||
|
enable = lib.mkOption {
|
||||||
|
type = lib.types.bool;
|
||||||
|
default = false;
|
||||||
|
description = ''
|
||||||
|
Put a Caddy virtual host in front of the bridge, with automatic
|
||||||
|
HTTPS — no `enableACME`/`forceSSL` to set, Caddy does this itself
|
||||||
|
for any site address that isn't `http://`-prefixed.
|
||||||
|
|
||||||
|
This wires into the standard `services.caddy.virtualHosts` option
|
||||||
|
and has no effect if your own configuration replaces
|
||||||
|
`services.caddy.configFile` outright with a hand-written Caddyfile
|
||||||
|
— that style of deployment needs the domain added to that file
|
||||||
|
instead (e.g. an imported drop-in), proxying to
|
||||||
|
`services.gsmol-bridge.host`:`.port`.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
domain = lib.mkOption {
|
||||||
|
type = lib.types.str;
|
||||||
|
example = "mail.example.org";
|
||||||
|
description = "Public hostname to serve gsmol from.";
|
||||||
|
};
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
config = lib.mkIf cfg.enable (lib.mkMerge [
|
config = lib.mkIf cfg.enable (lib.mkMerge [
|
||||||
{
|
{
|
||||||
|
assertions = [
|
||||||
|
{
|
||||||
|
assertion = !(cfg.nginx.enable && cfg.caddy.enable);
|
||||||
|
message = "services.gsmol-bridge: enable only one of .nginx or .caddy";
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
systemd.services.gsmol-bridge = {
|
systemd.services.gsmol-bridge = {
|
||||||
description = "gsmol web client and Smol Mail bridge";
|
description = "gsmol web client and Smol Mail bridge";
|
||||||
wantedBy = [ "multi-user.target" ];
|
wantedBy = [ "multi-user.target" ];
|
||||||
|
|
@ -186,5 +219,16 @@ in
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
})
|
})
|
||||||
|
|
||||||
|
(lib.mkIf cfg.caddy.enable {
|
||||||
|
services.caddy.enable = true;
|
||||||
|
# No proxyWebsockets-style flag needed: Caddy's reverse_proxy upgrades a
|
||||||
|
# WebSocket connection transparently, and it self-provisions TLS for a
|
||||||
|
# non-http:// site address, so neither forceSSL nor enableACME has a
|
||||||
|
# caddy-side equivalent to set here.
|
||||||
|
services.caddy.virtualHosts.${cfg.caddy.domain}.extraConfig = ''
|
||||||
|
reverse_proxy ${cfg.host}:${toString cfg.port}
|
||||||
|
'';
|
||||||
|
})
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue