refactor: split hive-gateway into module dir, DHCP range into hive-network
This commit is contained in:
parent
ecaad48fad
commit
5f9af9fc97
8 changed files with 1143 additions and 1075 deletions
310
nix/modules/hive-gateway/options.nix
Normal file
310
nix/modules/hive-gateway/options.nix
Normal file
|
|
@ -0,0 +1,310 @@
|
|||
# Option declarations for `services.hyperhive.gateway.*`. The gateway
|
||||
# is always run alongside hyperhive (it's the single nginx in front of
|
||||
# every surface and the only thing exposed to the outside); there is
|
||||
# no enable flag. An operator who wants their own reverse proxy in
|
||||
# front points it at the gateway's `port`.
|
||||
{
|
||||
lib,
|
||||
config,
|
||||
...
|
||||
}:
|
||||
let
|
||||
cfg = config.services.hyperhive.gateway;
|
||||
in
|
||||
{
|
||||
options.services.hyperhive.gateway = {
|
||||
port = lib.mkOption {
|
||||
type = lib.types.port;
|
||||
default = 80;
|
||||
example = 8080;
|
||||
description = ''
|
||||
TCP port the gateway listens on. Default 80 (canonical web
|
||||
port). nginx inside the container binds <1024 because the
|
||||
container's init runs as root; if 80 is already taken on the
|
||||
host (existing nginx, traefik, etc.) override to an unused
|
||||
port like 8080 or move the conflicting service.
|
||||
'';
|
||||
};
|
||||
|
||||
upstreamHost = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "127.0.0.1";
|
||||
description = ''
|
||||
Host the gateway proxies non-static requests to. Defaults to
|
||||
`127.0.0.1` because the gateway container shares the host
|
||||
netns, so loopback resolves directly to hive-c0re.
|
||||
'';
|
||||
};
|
||||
|
||||
upstreamPort = lib.mkOption {
|
||||
type = lib.types.port;
|
||||
default = 7000;
|
||||
description = ''
|
||||
TCP port the gateway proxies non-static requests to. Defaults
|
||||
to `7000` (hive-c0re's out-of-the-box dashboard port). Operators
|
||||
who change `services.hyperhive.c0re.dashboardPort` should set
|
||||
`upstreamPort` to match — kept as a hardcoded default rather
|
||||
than a cross-reference to keep this module's options eval
|
||||
independent of c0re's option tree shape.
|
||||
'';
|
||||
};
|
||||
|
||||
openFirewall = lib.mkOption {
|
||||
type = lib.types.bool;
|
||||
default = false;
|
||||
example = true;
|
||||
description = ''
|
||||
Open `port` in the host firewall. Off by default (secure-by-default).
|
||||
Flip to `true` to expose the gateway to
|
||||
the operator's browser / external clients — required for any
|
||||
out-of-host reach, since the agents themselves talk to
|
||||
hive-c0re via the per-agent unix sockets and don't need the
|
||||
nginx vhost. Leave off when running behind another reverse
|
||||
proxy (e.g. caddy / traefik on the host) that handles TLS
|
||||
termination + forwards to `port`.
|
||||
|
||||
**Note**: this used to default to `true`. Add
|
||||
`services.hyperhive.gateway.openFirewall = true;` to your host
|
||||
config if external reach stopped working after a recent upgrade.
|
||||
'';
|
||||
};
|
||||
|
||||
localHostsEntry = lib.mkOption {
|
||||
type = lib.types.bool;
|
||||
default = false;
|
||||
example = true;
|
||||
description = ''
|
||||
Add an `/etc/hosts` entry mapping `services.hyperhive.domain`
|
||||
to `127.0.0.1` on the host. Useful for local deployments +
|
||||
tests where there's no real DNS for `services.hyperhive.domain`
|
||||
but the operator (or browser-based tests) want to hit
|
||||
`http://''${services.hyperhive.domain}` to exercise the
|
||||
gateway shape. Off by default — operators running with real
|
||||
DNS shouldn't have a stale `/etc/hosts` entry sticking
|
||||
around. Requires `services.hyperhive.domain` to be set.
|
||||
'';
|
||||
};
|
||||
|
||||
selfSignedTls = lib.mkOption {
|
||||
type = lib.types.bool;
|
||||
default = true;
|
||||
example = false;
|
||||
description = ''
|
||||
**DEPRECATED — ignored.** Self-signed TLS is now the implicit
|
||||
default: when neither `tls.certDir` nor `tls.acme.enable` is
|
||||
configured, the gateway generates and serves a hive-CA-signed
|
||||
leaf (see the `hive-tls` module). There is no explicit toggle and
|
||||
no http-only mode — matrix discovery hardcodes
|
||||
`https://<host>/.well-known/matrix/client`, so the gateway always
|
||||
terminates TLS. This option is retained as a no-op so existing
|
||||
configs eval; setting it (to either value) warns and has no
|
||||
effect, and it will be removed in a future release. Use
|
||||
`tls.certDir` or `tls.acme` to override the self-signed default.
|
||||
|
||||
See `docs/gateway.md` ("Self-signed TLS").
|
||||
'';
|
||||
};
|
||||
|
||||
useSelfSigned = lib.mkOption {
|
||||
type = lib.types.bool;
|
||||
internal = true;
|
||||
readOnly = true;
|
||||
default = cfg.tls.certDir == null && !cfg.tls.acme.enable;
|
||||
defaultText = lib.literalExpression "tls.certDir == null && !tls.acme.enable";
|
||||
description = ''
|
||||
Read-only derived flag: `true` when the gateway serves the
|
||||
self-signed (hive-CA-signed) leaf — i.e. neither `tls.certDir` nor
|
||||
`tls.acme.enable` is configured. Single source of truth for the
|
||||
self-signed condition; consumed by the `hive-tls` and `hive-ci`
|
||||
modules so the derivation isn't duplicated. Internal — not meant to
|
||||
be set by operators (use `tls.certDir` / `tls.acme` to override the
|
||||
self-signed default).
|
||||
'';
|
||||
};
|
||||
|
||||
httpsPort = lib.mkOption {
|
||||
type = lib.types.port;
|
||||
default = 443;
|
||||
example = 8443;
|
||||
description = ''
|
||||
TCP port for the TLS-terminated vhosts. Default 443. The gateway
|
||||
always terminates TLS (self-signed is the implicit floor when no
|
||||
`tls.certDir` / ACME is configured), so this port is always active
|
||||
alongside the plain-http `port`.
|
||||
'';
|
||||
};
|
||||
|
||||
tls = {
|
||||
certDir = lib.mkOption {
|
||||
type = lib.types.nullOr lib.types.path;
|
||||
default = null;
|
||||
example = lib.literalExpression ''"/var/lib/acme/example.com"'';
|
||||
description = ''
|
||||
Path to a host directory containing a TLS certificate and
|
||||
private key for nginx. When set, nginx listens on `httpsPort`
|
||||
and uses this cert, overriding the self-signed default — the
|
||||
auto-generated hive-CA-signed leaf is skipped entirely.
|
||||
|
||||
The directory is bind-mounted read-only into the gateway
|
||||
container at `/run/hive-tls/`. nginx reads
|
||||
`<certDir>/<tls.certName>` and `<certDir>/<tls.keyName>`.
|
||||
Default filenames (`cert.pem` / `key.pem`) match the output
|
||||
layout of nixpkgs's `security.acme` module.
|
||||
|
||||
Typical ACME setup:
|
||||
```nix
|
||||
security.acme.certs."example.com" = { ... };
|
||||
services.hyperhive.gateway.tls.certDir =
|
||||
config.security.acme.certs."example.com".directory;
|
||||
```
|
||||
|
||||
When using an external CA cert, peer hives can declare this
|
||||
hive in `services.hyperhive.swarm.peers` without
|
||||
`certFingerprint` — the standard CA bundle validates.
|
||||
|
||||
Mutual exclusion with `tls.acme.enable` — set one or the other,
|
||||
not both.
|
||||
'';
|
||||
};
|
||||
|
||||
certName = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "cert.pem";
|
||||
description = ''
|
||||
Filename of the TLS certificate within `tls.certDir`. Defaults
|
||||
to `cert.pem` which matches nixpkgs's `security.acme` output.
|
||||
'';
|
||||
};
|
||||
|
||||
keyName = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "key.pem";
|
||||
description = ''
|
||||
Filename of the TLS private key within `tls.certDir`. Defaults
|
||||
to `key.pem` which matches nixpkgs's `security.acme` output.
|
||||
'';
|
||||
};
|
||||
|
||||
acme = {
|
||||
enable = lib.mkOption {
|
||||
type = lib.types.bool;
|
||||
default = false;
|
||||
example = true;
|
||||
description = ''
|
||||
Let nginx inside the gateway container obtain and renew TLS
|
||||
certificates automatically via ACME (Let's Encrypt). When
|
||||
enabled, each vhost calls out to Let's Encrypt using the
|
||||
HTTP-01 challenge on `port` (default 80) and stores certs
|
||||
inside the gateway container's persistent state dir.
|
||||
|
||||
Requirements:
|
||||
- `services.hyperhive.domain` must be set and publicly
|
||||
DNS-resolvable to this host.
|
||||
- `services.hyperhive.gateway.openFirewall = true` so
|
||||
Let's Encrypt can reach `/.well-known/acme-challenge/`.
|
||||
- `tls.acme.email` must be set (ACME account contact).
|
||||
|
||||
Mutual exclusion: `tls.certDir` set together with
|
||||
`tls.acme.enable = true` fails at eval — pick one TLS source.
|
||||
|
||||
Typical setup:
|
||||
```nix
|
||||
services.hyperhive.gateway = {
|
||||
openFirewall = true;
|
||||
tls.acme = {
|
||||
enable = true;
|
||||
email = "admin@example.com";
|
||||
};
|
||||
};
|
||||
```
|
||||
|
||||
After enabling, peer hives can omit `certFingerprint` in
|
||||
`swarm.peers` — Let's Encrypt certs are CA-trusted
|
||||
by default.
|
||||
'';
|
||||
};
|
||||
|
||||
email = lib.mkOption {
|
||||
type = lib.types.nullOr lib.types.str;
|
||||
default = null;
|
||||
example = "admin@example.com";
|
||||
description = ''
|
||||
Email address for the ACME account registration with
|
||||
Let's Encrypt. Required when `tls.acme.enable = true`.
|
||||
Let's Encrypt sends expiry warnings to this address.
|
||||
'';
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
auth = {
|
||||
enable = lib.mkEnableOption ''
|
||||
HTTP basic auth on the gateway using an htpasswd file. When
|
||||
enabled, every request to the gateway's main vhost requires a
|
||||
valid username and password. nginx's built-in `auth_basic`
|
||||
module validates credentials against
|
||||
`/var/lib/hyperhive/gateway/gateway.htpasswd` on the host
|
||||
(exposed as `/run/hive-state/gateway.htpasswd` inside the
|
||||
container via the existing gateway state bind-mount). Off by default.
|
||||
|
||||
Manage users with `hivectl gateway create-user`, `delete-user`,
|
||||
and `list-users` — see `hivectl gateway --help` for usage.
|
||||
The htpasswd file is created automatically when auth is enabled;
|
||||
add at least one user before enabling to avoid locking everyone out.
|
||||
'';
|
||||
|
||||
realm = lib.mkOption {
|
||||
type = lib.types.strMatching "[^\"$]*";
|
||||
default = "hyperhive";
|
||||
example = "my-hive";
|
||||
description = ''
|
||||
HTTP Basic auth `realm` value sent in the `WWW-Authenticate`
|
||||
header when credentials are absent or rejected. Must not
|
||||
contain `"` or `$` (nginx string metacharacters).
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
hsts = {
|
||||
enable = lib.mkOption {
|
||||
type = lib.types.bool;
|
||||
default = false;
|
||||
description = ''
|
||||
Add `Strict-Transport-Security` to all gateway vhosts.
|
||||
|
||||
Disabled by default: HSTS pins HTTPS in the browser's HSTS
|
||||
preload list; enabling it on a deployment that later loses TLS
|
||||
will lock browsers out until the max-age expires. Only enable
|
||||
this when you are certain TLS is permanent.
|
||||
|
||||
The gateway always terminates TLS (self-signed floor), so
|
||||
HSTS is always served over https when enabled — but mind the
|
||||
warning above: HSTS pins https in the browser, so only enable it
|
||||
when TLS is permanent for this deployment.
|
||||
'';
|
||||
};
|
||||
|
||||
maxAge = lib.mkOption {
|
||||
type = lib.types.ints.positive;
|
||||
default = 31536000;
|
||||
example = 86400;
|
||||
description = ''
|
||||
Value for the `max-age` directive in seconds.
|
||||
Default: 31536000 (1 year), which is the value required for
|
||||
HSTS preload list submission. Use a shorter value (e.g. 86400)
|
||||
while testing so browsers forget the pin quickly.
|
||||
'';
|
||||
};
|
||||
|
||||
includeSubDomains = lib.mkOption {
|
||||
type = lib.types.bool;
|
||||
default = true;
|
||||
description = ''
|
||||
Whether to include `includeSubDomains` in the HSTS header.
|
||||
Only disable this if the gateway host has sub-domains that
|
||||
intentionally serve plain HTTP.
|
||||
'';
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
Loading…
Reference in a new issue