initial ESS Community NixOS module
This commit is contained in:
parent
18b970a750
commit
997726aecd
11 changed files with 443 additions and 39 deletions
|
|
@ -1,3 +1,4 @@
|
|||
{
|
||||
common = import ./common;
|
||||
ess-helm = import ./ess-helm;
|
||||
}
|
||||
|
|
|
|||
301
modules/nixos/ess-helm/default.nix
Normal file
301
modules/nixos/ess-helm/default.nix
Normal file
|
|
@ -0,0 +1,301 @@
|
|||
{
|
||||
config,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
|
||||
let
|
||||
cfg = config.services.ess-helm;
|
||||
|
||||
inherit (lib)
|
||||
literalExpression
|
||||
mkDefault
|
||||
mkEnableOption
|
||||
mkIf
|
||||
mkOption
|
||||
recursiveUpdate
|
||||
types
|
||||
;
|
||||
|
||||
chartModule =
|
||||
{
|
||||
defaultRepo,
|
||||
defaultName,
|
||||
defaultVersion,
|
||||
defaultHash,
|
||||
...
|
||||
}:
|
||||
types.submodule {
|
||||
options = {
|
||||
repo = mkOption {
|
||||
type = types.str;
|
||||
default = defaultRepo;
|
||||
description = "Helm chart repository URL. For OCI registries this must include the chart name (e.g. `oci://ghcr.io/org/repo/chart`).";
|
||||
};
|
||||
|
||||
name = mkOption {
|
||||
type = types.str;
|
||||
default = defaultName;
|
||||
description = "Chart name. For HTTP repositories appended to the pull command. For OCI repositories only used to name the store derivation.";
|
||||
};
|
||||
|
||||
version = mkOption {
|
||||
type = types.str;
|
||||
default = defaultVersion;
|
||||
description = "Chart version to deploy.";
|
||||
example = "26.5.0";
|
||||
};
|
||||
|
||||
hash = mkOption {
|
||||
type = types.str;
|
||||
default = defaultHash;
|
||||
description = "SRI hash of the chart `.tgz` archive.";
|
||||
example = "sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=";
|
||||
};
|
||||
|
||||
extraValues = mkOption {
|
||||
type = types.attrs;
|
||||
default = { };
|
||||
description = "Additional Helm values merged on top of the module-generated ones";
|
||||
example = literalExpression ''
|
||||
{
|
||||
synapse.additional."user-config.yaml".config = "max_upload_size: 100M";
|
||||
}
|
||||
'';
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
serviceModule =
|
||||
{ defaultSubdomain }:
|
||||
types.submodule (
|
||||
{ config, ... }:
|
||||
{
|
||||
options = {
|
||||
subdomain = mkOption {
|
||||
type = types.str;
|
||||
default = defaultSubdomain;
|
||||
description = "Subdomain for this service. Full hostname is `<subdomain>.<serverName>`, or just `<serverName>` when empty (Synapse).";
|
||||
};
|
||||
|
||||
forceSSL = mkOption {
|
||||
type = types.bool;
|
||||
default = true;
|
||||
description = "Redirect plain-HTTP requests to HTTPS.";
|
||||
};
|
||||
|
||||
enableACME = mkOption {
|
||||
type = types.bool;
|
||||
description = "Obtain a Let's Encrypt certificate for this vhost. Defaults to the value of `forceSSL`.";
|
||||
};
|
||||
};
|
||||
|
||||
config.enableACME = mkDefault config.forceSSL;
|
||||
}
|
||||
);
|
||||
|
||||
hostFor = svc: if svc.subdomain == "" then cfg.serverName else "${svc.subdomain}.${cfg.serverName}";
|
||||
|
||||
vhostFor = svc: {
|
||||
inherit (svc) forceSSL enableACME;
|
||||
locations."/" = {
|
||||
proxyPass = "http://127.0.0.1:${toString cfg.ingressNginxPort}";
|
||||
proxyWebsockets = true;
|
||||
extraConfig = ''
|
||||
proxy_set_header X-Forwarded-For $remote_addr;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_read_timeout 86400s;
|
||||
proxy_send_timeout 86400s;
|
||||
proxy_buffering off;
|
||||
client_max_body_size 50M;
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
in
|
||||
{
|
||||
options.services.ess-helm = {
|
||||
|
||||
enable = mkEnableOption "Element Server Suite Community";
|
||||
|
||||
serverName = mkOption {
|
||||
type = types.str;
|
||||
description = "Matrix server name — the domain part of every Matrix ID (`@user:<serverName>`). Must point at this host and **cannot be changed** after the first deployment without wiping the database.";
|
||||
example = "example.com";
|
||||
};
|
||||
|
||||
openFirewall = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
description = "Open the Matrix RTC WebRTC ports in the firewall (TCP 30001, UDP 30002).";
|
||||
};
|
||||
|
||||
configureNginx = mkOption {
|
||||
type = types.bool;
|
||||
default = true;
|
||||
description = "Configure nginx as a TLS-terminating reverse proxy for all ESS services, proxying to ingress-nginx on `ingressNginxPort`.";
|
||||
};
|
||||
|
||||
ingressNginxPort = mkOption {
|
||||
type = types.port;
|
||||
default = 30080;
|
||||
description = "NodePort for ingress-nginx plain HTTP. Must be in the Kubernetes NodePort range (30000–32767).";
|
||||
};
|
||||
|
||||
ingressNginxTlsPort = mkOption {
|
||||
type = types.port;
|
||||
default = 30443;
|
||||
description = "NodePort reserved for ingress-nginx HTTPS (unused at runtime but required by the chart schema). Must be in the Kubernetes NodePort range (30000–32767).";
|
||||
};
|
||||
|
||||
ipFamily = mkOption {
|
||||
type = types.enum [
|
||||
"ipv4"
|
||||
"ipv6"
|
||||
"dual"
|
||||
];
|
||||
default = "ipv4";
|
||||
description = "IP family for the ESS cluster networking.";
|
||||
};
|
||||
|
||||
synapse = mkOption {
|
||||
type = serviceModule { defaultSubdomain = ""; };
|
||||
default = { };
|
||||
description = "Synapse homeserver (served at the root serverName domain).";
|
||||
};
|
||||
|
||||
matrixAuthenticationService = mkOption {
|
||||
type = serviceModule { defaultSubdomain = "auth"; };
|
||||
default = { };
|
||||
description = "Matrix Authentication Service (MAS).";
|
||||
};
|
||||
|
||||
elementWeb = mkOption {
|
||||
type = serviceModule { defaultSubdomain = "chat"; };
|
||||
default = { };
|
||||
description = "Element Web client.";
|
||||
};
|
||||
|
||||
elementAdmin = mkOption {
|
||||
type = serviceModule { defaultSubdomain = "admin"; };
|
||||
default = { };
|
||||
description = "Element Admin console.";
|
||||
};
|
||||
|
||||
matrixRTC = mkOption {
|
||||
type = serviceModule { defaultSubdomain = "mrtc"; };
|
||||
default = { };
|
||||
description = "Matrix RTC SFU signalling endpoint.";
|
||||
};
|
||||
|
||||
ess = mkOption {
|
||||
type = chartModule {
|
||||
name = "ess";
|
||||
defaultRepo = "oci://ghcr.io/element-hq/ess-helm/matrix-stack";
|
||||
defaultName = "matrix-stack";
|
||||
defaultVersion = "26.5.0";
|
||||
defaultHash = "sha256-2YfDk29c8MoEa7rJXnHiKgKVqL7I0mmIcbtqgh2tunU=";
|
||||
};
|
||||
default = { };
|
||||
description = "ESS Community Helm chart pin and extra values.";
|
||||
};
|
||||
|
||||
ingressNginx = mkOption {
|
||||
type = chartModule {
|
||||
name = "ingressNginx";
|
||||
defaultRepo = "https://kubernetes.github.io/ingress-nginx";
|
||||
defaultName = "ingress-nginx";
|
||||
defaultVersion = "4.15.1";
|
||||
defaultHash = "sha256-Pv8L0YFR1uaxxEFGNBBXFEPdoax4KSyxiTRmKN54Tww=";
|
||||
};
|
||||
default = { };
|
||||
description = "ingress-nginx Helm chart pin and extra values.";
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
config = mkIf cfg.enable {
|
||||
|
||||
services.k3s = {
|
||||
enable = true;
|
||||
role = "server";
|
||||
disable = [ "traefik" ]; # use ingress-nginx
|
||||
|
||||
autoDeployCharts = {
|
||||
|
||||
ingress-nginx = recursiveUpdate {
|
||||
inherit (cfg.ingressNginx)
|
||||
repo
|
||||
name
|
||||
version
|
||||
hash
|
||||
;
|
||||
targetNamespace = "ingress-nginx";
|
||||
createNamespace = true;
|
||||
values = {
|
||||
controller = {
|
||||
ingressClassResource.default = true;
|
||||
service = {
|
||||
type = "NodePort";
|
||||
nodePorts = {
|
||||
http = cfg.ingressNginxPort;
|
||||
https = cfg.ingressNginxTlsPort;
|
||||
};
|
||||
};
|
||||
config = {
|
||||
use-forwarded-headers = "true";
|
||||
compute-full-forwarded-for = "true";
|
||||
proxy-read-timeout = "86400";
|
||||
proxy-send-timeout = "86400";
|
||||
proxy-body-size = "50m";
|
||||
};
|
||||
};
|
||||
};
|
||||
} { values = cfg.ingressNginx.extraValues; };
|
||||
|
||||
ess = recursiveUpdate {
|
||||
inherit (cfg.ess)
|
||||
repo
|
||||
name
|
||||
version
|
||||
hash
|
||||
;
|
||||
targetNamespace = "ess";
|
||||
createNamespace = true;
|
||||
values = {
|
||||
inherit (cfg) serverName;
|
||||
ingress = {
|
||||
className = "nginx";
|
||||
tlsEnabled = false;
|
||||
};
|
||||
synapse.ingress.host = hostFor cfg.synapse;
|
||||
matrixAuthenticationService.ingress.host = hostFor cfg.matrixAuthenticationService;
|
||||
elementWeb.ingress.host = hostFor cfg.elementWeb;
|
||||
elementAdmin.ingress.host = hostFor cfg.elementAdmin;
|
||||
matrixRTC.ingress.host = hostFor cfg.matrixRTC;
|
||||
networking.ipFamily = cfg.ipFamily;
|
||||
};
|
||||
} { values = cfg.ess.extraValues; };
|
||||
|
||||
};
|
||||
};
|
||||
|
||||
services.nginx = mkIf cfg.configureNginx {
|
||||
enable = true;
|
||||
virtualHosts = {
|
||||
"${hostFor cfg.synapse}" = vhostFor cfg.synapse;
|
||||
"${hostFor cfg.matrixAuthenticationService}" = vhostFor cfg.matrixAuthenticationService;
|
||||
"${hostFor cfg.elementWeb}" = vhostFor cfg.elementWeb;
|
||||
"${hostFor cfg.elementAdmin}" = vhostFor cfg.elementAdmin;
|
||||
"${hostFor cfg.matrixRTC}" = vhostFor cfg.matrixRTC;
|
||||
};
|
||||
};
|
||||
|
||||
networking.firewall = mkIf cfg.openFirewall {
|
||||
allowedTCPPorts = [ 30001 ];
|
||||
allowedUDPPorts = [ 30002 ];
|
||||
};
|
||||
|
||||
environment.variables.KUBECONFIG = "/etc/rancher/k3s/k3s.yaml";
|
||||
};
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue