feat(exports): service access

This commit is contained in:
arcnmx 2024-02-19 17:34:39 -08:00
parent 91918b8061
commit 871b1c5b2d
69 changed files with 1317 additions and 509 deletions

View file

@ -8,7 +8,7 @@
inherit (nixlib.strings) splitString toLower;
inherit (nixlib.lists) imap0 elemAt;
inherit (nixlib.attrsets) mapAttrs listToAttrs nameValuePair;
inherit (nixlib.strings) substring fixedWidthString replaceStrings concatMapStringsSep;
inherit (nixlib.strings) hasPrefix hasInfix substring fixedWidthString replaceStrings concatMapStringsSep;
inherit (nixlib.trivial) flip toHexString bitOr;
toHexStringLower = v: toLower (toHexString v);
@ -35,6 +35,7 @@
mkWinPath = replaceStrings ["/"] ["\\"];
mkBaseDn = domain: concatMapStringsSep "," (part: "dc=${part}") (splitString "." domain);
mkAddress6 = addr: if hasInfix ":" addr && ! hasPrefix "[" addr then "[${addr}]" else addr;
mapListToAttrs = f: l: listToAttrs (map f l);
@ -77,7 +78,7 @@ in {
lib = {
domain = "gensokyo.zone";
inherit treeToModulesOutput userIs
eui64 mkWinPath mkBaseDn
eui64 mkWinPath mkBaseDn mkAddress6
toHexStringLower hexCharToInt
mapListToAttrs
mkAlmostOptionDefault mkAlmostDefault mkAlmostForce mapOverride mapOptionDefaults mapAlmostOptionDefaults mapDefaults

View file

@ -0,0 +1,20 @@
{config, lib, ...}: let
inherit (lib.options) mkOption;
inherit (lib.modules) mkOptionDefault;
cfg = config.services.keycloak;
in {
options.services.keycloak = with lib.types; {
protocol = mkOption {
type = enum [ "http" "https" ];
readOnly = true;
};
port = mkOption {
type = port;
readOnly = true;
};
};
config.services.keycloak = {
protocol = mkOptionDefault (if cfg.sslCertificate != null then "https" else "http");
port = mkOptionDefault cfg.settings."${cfg.protocol}-port";
};
}

View file

@ -1,6 +1,6 @@
{config, lib, ...}: let
inherit (lib.options) mkOption mkEnableOption;
inherit (lib.modules) mkIf mkDefault mkOptionDefault;
inherit (lib.modules) mkIf mkOptionDefault;
inherit (lib.lists) filter optional;
inherit (lib.strings) hasInfix concatStrings;
inherit (config.services) resolved;

View file

@ -4,12 +4,11 @@
inputs,
...
}: let
inherit (inputs.self.lib.lib) mkAlmostOptionDefault;
inherit (inputs.self.lib.lib) mkAlmostOptionDefault mkAddress6;
inherit (lib.options) mkOption mkEnableOption;
inherit (lib.modules) mkIf mkMerge mkBefore mkOptionDefault mkForce;
inherit (lib.attrsets) attrValues mapAttrs;
inherit (lib.lists) optional filter concatMap;
inherit (lib.strings) hasPrefix hasInfix;
inherit (config.services) nginx;
listenModule = { config, virtualHost, listenKind, ... }: {
options = with lib.types; {
@ -76,9 +75,8 @@
listenConfigs = let
# TODO: handle quic listener..?
mkListenHost = { addr, port }: let
addr' = if hasInfix ":" addr && !hasPrefix "[" addr then "[${addr}]" else addr;
host =
if addr != null then "${addr'}:${toString port}"
if addr != null then "${mkAddress6 addr}:${toString port}"
else toString port;
in assert port != null; host;
mkDirective = addr: let

View file

@ -73,14 +73,14 @@
cert = let
mkCopyCert = copyCert: {
name = mkDefault copyCert.name;
keyPath = mkAlmostOptionDefault copyCert.keyPath;
path = mkAlmostOptionDefault copyCert.path;
keyPath = mkDefault copyCert.keyPath;
path = mkDefault copyCert.path;
};
copyCertVhost = mkCopyCert nginx.virtualHosts.${cfg.cert.copyFromVhost}.ssl.cert;
copyCertStreamServer = mkCopyCert nginx.stream.servers.${cfg.cert.copyFromStreamServer}.ssl.cert;
in mkMerge [
(mkIf (cfg.cert.copyFromVhost != null) copyCertVhost)
(mkIf (cfg.cert.copyFromStreamServer != null) copyCertStreamServer)
(mkIf (cfg.cert.copyFromVhost != null) copyCertVhost)
];
};
};

View file

@ -1,13 +1,14 @@
{
config,
lib,
gensokyo-zone,
...
}: let
inherit (gensokyo-zone.lib) mkAddress6;
inherit (lib.options) mkOption mkEnableOption;
inherit (lib.modules) mkIf mkMerge mkBefore mkOptionDefault;
inherit (lib.attrsets) mapAttrsToList;
inherit (lib.lists) optional;
inherit (lib.strings) hasPrefix hasInfix;
cfg = config.services.nginx.stream;
upstreamServerModule = {config, name, ...}: {
options = with lib.types; {
@ -45,9 +46,7 @@
config = let
settings = mapAttrsToList (key: value: "${key}=${toString value}") config.settings;
in {
server = let
addr = if hasInfix ":" config.addr && ! hasPrefix "[" config.addr then "[${config.addr}]" else config.addr;
in mkOptionDefault "${addr}:${toString config.port}";
server = mkOptionDefault "${mkAddress6 config.addr}:${toString config.port}";
serverConfig = mkMerge (
[ (mkBefore config.server) ]
++ settings
@ -76,6 +75,9 @@
servers = mkOption {
type = attrsOf upstreamServer;
};
ssl = {
enable = mkEnableOption "ssl upstream";
};
extraConfig = mkOption {
type = lines;
default = "";
@ -119,11 +121,35 @@
type = lines;
internal = true;
};
proxy = {
upstream = mkOption {
type = nullOr str;
default = null;
};
url = mkOption {
type = nullOr str;
};
};
};
config = {
streamConfig = mkMerge [
proxy = {
url = mkOptionDefault (
if config.proxy.upstream != null then cfg.upstreams.${config.proxy.upstream}.name
else null
);
};
streamConfig = let
proxyUpstream = cfg.upstreams.${config.proxy.upstream};
in mkMerge [
config.extraConfig
(mkIf (config.proxy.upstream != null && proxyUpstream.ssl.enable) ''
proxy_ssl on;
proxy_ssl_verify off;
'')
(mkIf (config.proxy.url != null) ''
proxy_pass ${config.proxy.url};
'')
];
serverBlock = mkOptionDefault ''
server {

View file

@ -8,10 +8,12 @@
}: let
inherit (inputs.self) nixosConfigurations;
inherit (inputs.self.lib) systems;
inherit (inputs.self.lib.lib) domain;
inherit (inputs.self.lib.lib) domain mkAddress6;
inherit (lib.options) mkOption mkEnableOption;
inherit (lib.modules) mkIf mkMerge mkDefault mkOptionDefault;
inherit (lib.attrsets) mapAttrs;
inherit (lib.attrsets) mapAttrs attrValues;
inherit (lib.lists) findSingle;
inherit (lib.trivial) mapNullable;
cfg = config.access;
systemConfig = config;
systemAccess = access;
@ -42,20 +44,29 @@
config = {
networking.access = {
moduleArgAttrs = let
mkGetAddressFor = addressForAttr: hostName: network: let
mkGetAddressFor = nameAllowed: addressForAttr: hostName: network: let
forSystem = access.systemFor hostName;
err = throw "no lan interface found between ${config.networking.hostName} and ${hostName}";
err = throw "no interface found between ${config.networking.hostName} -> ${hostName}@${network}";
fallback = if nameAllowed
then lib.warn "getAddressFor hostname fallback for ${config.networking.hostName} -> ${hostName}@${network}" (access.getHostnameFor hostName network)
else err;
local = forSystem.access.${addressForAttr}.local or forSystem.access.address4ForNetwork.local or fallback;
int = forSystem.access.${addressForAttr}.int or forSystem.access.address4ForNetwork.int or fallback;
tail = forSystem.access.${addressForAttr}.tail or fallback;
in {
lan =
if has'Int then forSystem.access.${addressForAttr}.int or forSystem.access.${addressForAttr}.local or err
else if has'Local then forSystem.access.${addressForAttr}.local or err
else err;
${if has'Local then "local" else null} = forSystem.access.${addressForAttr}.local or err;
${if has'Int then "int" else null} = forSystem.access.${addressForAttr}.int or err;
${if has'Tail then "tail" else null} = forSystem.access.${addressForAttr}.tail or err;
}.${network} or err;
if hostName == system.name then forSystem.access.${addressForAttr}.localhost
else if has'Int then int
else if has'Local then local
else fallback;
${if has'Local then "local" else null} = local;
${if has'Int then "int" else null} = int;
${if has'Tail then "tail" else null} = tail;
}.${network} or fallback;
in {
inherit (systemAccess) hostnameForNetwork address4ForNetwork address6ForNetwork;
inherit (systemAccess)
hostnameForNetwork address4ForNetwork address6ForNetwork
systemForService systemForServiceId;
addressForNetwork = systemAccess.${addressForAttr};
systemFor = hostName:
if hostName == config.networking.hostName
@ -73,21 +84,39 @@
if hostName == config.networking.hostName
then config
else systemAccess.nixosForOrNull hostName;
getAddressFor = mkGetAddressFor addressForAttr;
getAddress4For = mkGetAddressFor "address4ForNetwork";
getAddress6For = mkGetAddressFor "address6ForNetwork";
getAddressFor = mkGetAddressFor true addressForAttr;
getAddress4For = mkGetAddressFor false "address4ForNetwork";
getAddress6For = mkGetAddressFor false "address6ForNetwork";
getHostnameFor = hostName: network: let
forSystem = access.systemFor hostName;
err = throw "no ${network} interface found between ${config.networking.hostName} and ${hostName}";
in {
lan =
if has'Int then forSystem.access.hostnameForNetwork.int or forSystem.access.hostnameForNetwork.local or err
if hostName == system.name then forSystem.access.hostnameForNetwork.localhost
else if has'Int then forSystem.access.hostnameForNetwork.int or forSystem.access.hostnameForNetwork.local or err
else if has'Local then forSystem.access.hostnameForNetwork.local or err
else err;
${if has'Local then "local" else null} = forSystem.access.hostnameForNetwork.local or err;
${if has'Int then "int" else null} = forSystem.access.hostnameForNetwork.int or err;
${if has'Tail then "tail" else null} = forSystem.access.hostnameForNetwork.tail or err;
}.${network} or err;
proxyUrlFor = {
system ? if serviceId != null then access.systemForServiceId serviceId else access.systemForService serviceName,
serviceName ? mapNullable (serviceId: (findSingle (s: s.id == serviceId) null null (attrValues system.exports.services)).name) serviceId,
serviceId ? null,
service ? system.exports.services.${serviceName},
portName ? "default",
network ? "lan",
scheme ? null,
}: let
port = service.ports.${portName};
scheme' = if scheme == null then port.protocol else scheme;
port' = if !port.enable
then throw "${system.name}.exports.services.${service.name}.ports.${portName} isn't enabled"
else ":${toString port.port}";
host = access.getAddressFor system.name network;
url = "${scheme'}://${mkAddress6 host}${port'}";
in assert service.enable; url;
};
};
networking.tempAddresses = mkIf cfg.global.enable (
@ -110,7 +139,6 @@ in {
type = str;
default = domain;
};
tailscale.enable = mkEnableOption "tailscale access";
global.enable = mkEnableOption "globally routeable";
hostnameForNetwork = mkOption {
type = attrsOf str;
@ -143,6 +171,7 @@ in {
hostnameForNetwork = mkMerge [
(mapAttrs (_: mapNetworkFqdn) config.network.networks)
{
localhost = mkOptionDefault "localhost";
lan = mkMerge [
(mapNetwork' mkDefault "fqdn" int)
(mapNetworkFqdn local)
@ -153,6 +182,7 @@ in {
address4ForNetwork = mkMerge [
(mapAttrs (_: mapNetwork4) config.network.networks)
{
localhost = mkOptionDefault "127.0.0.1";
lan = mkMerge [
(mapNetwork' mkDefault "address4" int)
(mapNetwork4 local)
@ -162,6 +192,7 @@ in {
address6ForNetwork = mkMerge [
(mapAttrs (_: mapNetwork6) config.network.networks)
{
localhost = mkOptionDefault "::1";
lan = mkMerge [
(mapNetwork' mkDefault "address6" int)
(mapNetwork6 local)
@ -176,6 +207,16 @@ in {
systemForOrNull = hostName: systems.${hostName}.config or null;
nixosFor = hostName: nixosConfigurations.${hostName}.config or (access.systemFor hostName).built.config;
nixosForOrNull = hostName: nixosConfigurations.${hostName}.config or (access.systemForOrNull hostName).built.config or null;
systemForService = service: let
hasService = system: system.config.exports.services.${service}.enable;
notFound = throw "no system found serving ${service}";
multiple = throw "multiple systems found serving ${service}";
in (findSingle hasService notFound multiple (attrValues systems)).config;
systemForServiceId = serviceId: let
hasService = system: findSingle (service: service.id == serviceId && service.enable) null multiple (attrValues system.config.exports.services) != null;
notFound = throw "no system found serving ${serviceId}";
multiple = throw "multiple systems found serving ${serviceId}";
in (findSingle hasService notFound multiple (attrValues systems)).config;
};
};
}

View file

@ -0,0 +1,22 @@
{lib, gensokyo-zone, ...}: let
inherit (gensokyo-zone.lib) mapAlmostOptionDefaults mkAlmostOptionDefault;
inherit (lib.attrsets) mapAttrs;
in {
config.exports.services.dnsmasq = { config, ... }: {
id = mkAlmostOptionDefault "dns";
nixos = {
serviceAttr = "dnsmasq";
};
defaults.port.listen = mkAlmostOptionDefault "lan";
ports = mapAttrs (_: mapAlmostOptionDefaults) {
default = {
port = 53;
transport = "udp";
};
tcp = {
port = config.ports.default.port;
transport = "tcp";
};
};
};
}

View file

@ -0,0 +1,48 @@
{
config,
name,
lib,
...
}: let
inherit (lib.options) mkOption mkEnableOption;
cfg = config.exports;
systemConfig = config;
exportModule = {
config,
name,
...
}: {
options = with lib.types; {
enable = mkEnableOption "exported service";
name = mkOption {
type = str;
default = name;
};
serviceName = mkOption {
type = str;
default = name;
};
id = mkOption {
type = str;
default = cfg.services.${config.serviceName}.id/* or config.name*/;
};
};
};
in {
options.exports = with lib.types; {
exports = mkOption {
type = attrsOf (submoduleWith {
modules = [exportModule];
specialArgs = {
machine = name;
inherit systemConfig;
};
});
default = {};
};
};
config = {
_module.args.exports = cfg;
};
}

View file

@ -0,0 +1,18 @@
{lib, gensokyo-zone, ...}: let
inherit (gensokyo-zone.lib) mapAlmostOptionDefaults mkAlmostOptionDefault;
inherit (lib.attrsets) mapAttrs;
in {
config.exports.services.freeipa = {
id = mkAlmostOptionDefault "freeipa";
ports = mapAttrs (_: mapAlmostOptionDefaults) {
default = {
port = 443;
protocol = "https";
};
redirect = {
port = 80;
protocol = "http";
};
};
};
}

View file

@ -0,0 +1,34 @@
{lib, gensokyo-zone, ...}: let
inherit (gensokyo-zone.lib) mapAlmostOptionDefaults mkAlmostOptionDefault;
inherit (lib.attrsets) mapAttrs;
in {
config.exports.services.freepbx = {
id = mkAlmostOptionDefault "pbx";
ports = mapAttrs (_: mapAlmostOptionDefaults) {
http = {
port = 80;
protocol = "http";
};
https = {
port = 443;
protocol = "https";
};
ucp = {
port = 8001;
protocol = "http";
};
ucp-ssl = {
port = 8003;
protocol = "https";
};
asterisk = {
port = 8088;
protocol = "http";
};
asterisk-ssl = {
port = 8089;
protocol = "https";
};
};
};
}

View file

@ -0,0 +1,45 @@
{lib, gensokyo-zone, ...}: let
inherit (gensokyo-zone.lib) mapAlmostOptionDefaults mkAlmostOptionDefault;
inherit (lib.modules) mkIf;
inherit (lib.attrsets) mapAttrs;
inherit (lib.lists) all imap0;
inherit (lib.trivial) id;
in {
config.exports.services.home-assistant = { config, ... }: let
mkAssertion = f: nixosConfig: let
cfg = nixosConfig.services.home-assistant;
in f nixosConfig cfg;
assertPort = nixosConfig: cfg: {
assertion = config.ports.default.port == cfg.config.http.server_port;
message = "port mismatch";
};
assertHomekitPort = let
portName = i: "homekit${toString i}";
mkAssertPort = i: homekit: config.ports.${portName i}.port or null == homekit.port;
in nixosConfig: cfg: {
assertion = all id (imap0 mkAssertPort cfg.config.homekit);
message = "homekit port mismatch";
};
in {
id = mkAlmostOptionDefault "home";
nixos = {
serviceAttr = "home-assistant";
assertions = mkIf config.enable [
(mkAssertion assertPort)
(mkAssertion assertHomekitPort)
];
};
defaults.port.listen = mkAlmostOptionDefault "lan";
ports = mapAttrs (_: mapAlmostOptionDefaults) {
default = {
port = 8123;
protocol = "http";
};
homekit0 = {
port = 21063;
transport = "tcp";
};
# TODO: cast udp port range 32768 to 60999
};
};
}

View file

@ -0,0 +1,21 @@
{lib, gensokyo-zone, ...}: let
inherit (gensokyo-zone.lib) mapAlmostOptionDefaults mkAlmostOptionDefault;
inherit (lib.modules) mkIf;
in {
config.exports.services.invidious = { config, ... }: {
id = mkAlmostOptionDefault "yt";
nixos = {
serviceAttr = "invidious";
assertions = mkIf config.enable [
(nixosConfig: {
assertion = config.ports.default.port == nixosConfig.services.invidious.port;
message = "port mismatch";
})
];
};
ports.default = mapAlmostOptionDefaults {
port = 3000;
protocol = "http";
};
};
}

View file

@ -0,0 +1,35 @@
{lib, gensokyo-zone, ...}: let
inherit (gensokyo-zone.lib) mapAlmostOptionDefaults;
inherit (lib.attrsets) mapAttrs;
in {
config.exports.services.kerberos = { config, ... }: {
id = "krb5";
ports = mapAttrs (_: mapAlmostOptionDefaults) {
default = {
port = 88;
transport = "tcp";
};
udp = {
port = config.ports.default.port;
transport = "udp";
};
kadmin = {
port = 749;
transport = "tcp";
};
kpasswd = {
port = 464;
transport = "tcp";
};
kpasswd-udp = {
port = config.ports.kpasswd.port;
transport = "udp";
};
ticket4 = {
enable = false;
port = 4444;
transport = "udp";
};
};
};
}

View file

@ -0,0 +1,37 @@
{lib, gensokyo-zone, ...}: let
inherit (gensokyo-zone.lib) mapAlmostOptionDefaults mkAlmostOptionDefault;
inherit (lib.modules) mkIf;
inherit (lib.attrsets) mapAttrs;
in {
config.exports.services.keycloak = { config, ... }: {
id = mkAlmostOptionDefault "sso";
nixos = {
serviceAttr = "keycloak";
assertions = let
mkAssertion = f: nixosConfig: let
cfg = nixosConfig.services.keycloak;
in f nixosConfig cfg;
in mkIf config.enable [
(mkAssertion (nixosConfig: cfg: {
assertion = config.ports.${cfg.protocol}.port == cfg.port;
message = "port mismatch";
}))
(mkAssertion (nixosConfig: cfg: {
assertion = config.ports.${cfg.protocol}.enable;
message = "port enable mismatch";
}))
];
};
ports = mapAttrs (_: mapAlmostOptionDefaults) {
http = {
enable = !config.ports.https.enable;
port = 8080;
protocol = "http";
};
https = {
port = 8443;
protocol = "https";
};
};
};
}

View file

@ -0,0 +1,19 @@
{lib, gensokyo-zone, ...}: let
inherit (gensokyo-zone.lib) mapAlmostOptionDefaults mkAlmostOptionDefault;
inherit (lib.attrsets) mapAttrs;
in {
config.exports.services.ldap = { config, ... }: {
defaults.port.listen = mkAlmostOptionDefault "lan";
ports = mapAttrs (_: mapAlmostOptionDefaults) {
default = {
port = 389;
transport = "tcp";
};
ssl = {
port = 636;
ssl = true;
listen = "wan";
};
};
};
}

View file

@ -0,0 +1,38 @@
{lib, gensokyo-zone, ...}: let
inherit (gensokyo-zone.lib) mapAlmostOptionDefaults mkAlmostOptionDefault;
inherit (lib.modules) mkIf;
inherit (lib.attrsets) mapAttrs;
inherit (lib.lists) all imap0;
inherit (lib.trivial) id;
in {
config.exports.services.mosquitto = { config, ... }: {
id = mkAlmostOptionDefault "mqtt";
nixos = {
serviceAttr = "mosquitto";
assertions = mkIf config.enable [
(nixosConfig: let
cfg = nixosConfig.services.mosquitto;
portName = i:
if i == 0 then "default"
else "listener${toString i}";
mkAssertPort = i: listener: config.ports.${portName i}.port or null == listener.port;
in {
assertion = all id (imap0 mkAssertPort cfg.listeners);
message = "port mismatch";
})
];
};
defaults.port.listen = mkAlmostOptionDefault "lan";
ports = mapAttrs (_: mapAlmostOptionDefaults) {
default = {
port = 1883;
transport = "tcp";
};
ssl = {
enable = false;
port = 8883;
ssl = true;
};
};
};
}

View file

@ -0,0 +1,18 @@
{lib, gensokyo-zone, ...}: let
inherit (gensokyo-zone.lib) mapAlmostOptionDefaults mkAlmostOptionDefault;
inherit (lib.attrsets) mapAttrs;
in {
config.exports.services.motion = { config, ... }: {
defaults.port.listen = mkAlmostOptionDefault "lan";
ports = mapAttrs (_: mapAlmostOptionDefaults) {
default = {
port = 8080;
protocol = "http";
};
stream = {
port = 8081;
protocol = "http";
};
};
};
}

View file

@ -0,0 +1,77 @@
{lib, gensokyo-zone, ...}: let
inherit (gensokyo-zone.lib) mapAlmostOptionDefaults mkAlmostOptionDefault;
inherit (lib.modules) mkIf;
inherit (lib.attrsets) mapAttrs;
in {
config.exports.services.nfs = { config, ... }: let
mkAssertion = f: nixosConfig: let
cfg = nixosConfig.services.nfs;
in f nixosConfig cfg;
mkAssertionPort = portName: mkAssertion (nixosConfig: cfg: let
portAttr = "${portName}Port";
in {
assertion = mkAssertPort config.ports.${portName} cfg.server.${portAttr};
message = "${portAttr} mismatch";
});
mkAssertPort = port: cfgPort: let
cmpPort = if port.enable then port.port else null;
in cfgPort == cmpPort;
in {
nixos = {
serviceAttrPath = [ "services" "nfs" "server" ];
assertions = mkIf config.enable [
(mkAssertionPort "statd")
(mkAssertionPort "lockd")
(mkAssertionPort "mountd")
(mkAssertion (nixosConfig: cfg: {
assertion = nixosConfig.services.rpcbind.enable == config.ports.rpcbind.enable;
message = "rpcbind enable mismatch";
}))
];
};
# TODO: expose over wan
defaults.port.listen = mkAlmostOptionDefault "lan";
ports = mapAttrs (_: mapAlmostOptionDefaults) {
default = {
port = 2049;
transport = "tcp";
};
udp = {
port = config.ports.default.port;
transport = "udp";
};
rpcbind = {
port = 111;
transport = "tcp";
};
rpcbind-udp = {
port = config.ports.rpcbind.port;
transport = "udp";
};
statd = {
port = 4000;
transport = "tcp";
};
statd-udp = {
port = config.ports.statd.port;
transport = "udp";
};
lockd = {
port = 4001;
transport = "tcp";
};
lockd-udp = {
port = config.ports.lockd.port;
transport = "udp";
};
mountd = {
port = 4002;
transport = "tcp";
};
mountd-udp = {
port = config.ports.mountd.port;
transport = "udp";
};
};
};
}

View file

@ -0,0 +1,43 @@
{lib, gensokyo-zone, ...}: let
inherit (gensokyo-zone.lib) mapAlmostOptionDefaults mkAlmostOptionDefault;
inherit (lib.attrsets) mapAttrs;
in {
config.exports.services.plex = {
nixos.serviceAttr = "plex";
defaults.port.listen = mkAlmostOptionDefault "lan";
ports = mapAttrs (_: mapAlmostOptionDefaults) {
default = {
port = 32400;
protocol = "http";
};
roku = {
port = 8324;
transport = "tcp";
};
dlna-tcp = {
port = 32469;
transport = "tcp";
};
dlna-udp = {
port = 1900;
transport = "udp";
};
gdm0 = {
port = 32410;
transport = "udp";
};
gdm1 = {
port = 32412;
transport = "udp";
};
gdm2 = {
port = 32413;
transport = "udp";
};
gdm3 = {
port = 32414;
transport = "udp";
};
};
};
}

View file

@ -0,0 +1,27 @@
{lib, gensokyo-zone, ...}: let
inherit (gensokyo-zone.lib) mapAlmostOptionDefaults;
inherit (lib.modules) mkIf;
in {
config.exports.services.postgresql = { config, ... }: let
mkAssertion = f: nixosConfig: let
cfg = nixosConfig.services.postgresql;
in f nixosConfig cfg;
in {
nixos = {
assertions = mkIf config.enable [
(mkAssertion (nixosConfig: cfg: {
assertion = config.ports.default.port == cfg.settings.port;
message = "port mismatch";
}))
(mkAssertion (nixosConfig: cfg: {
assertion = config.ports.default.enable == cfg.enableTCPIP;
message = "enableTCPIP mismatch";
}))
];
};
ports.default = mapAlmostOptionDefaults {
port = 5432;
transport = "tcp";
};
};
}

View file

@ -0,0 +1,12 @@
{lib, gensokyo-zone, ...}: let
inherit (gensokyo-zone.lib) mapAlmostOptionDefaults mkAlmostOptionDefault;
in {
config.exports.services.proxmox = { config, ... }: {
id = mkAlmostOptionDefault "prox";
defaults.port.listen = mkAlmostOptionDefault "lan";
ports.default = mapAlmostOptionDefaults {
port = 8006;
protocol = "https";
};
};
}

View file

@ -0,0 +1,29 @@
{lib, gensokyo-zone, ...}: let
inherit (gensokyo-zone.lib) mapAlmostOptionDefaults mkAlmostOptionDefault;
inherit (lib.attrsets) mapAttrs;
in {
config.exports.services.samba = {
id = mkAlmostOptionDefault "smb";
nixos.serviceAttr = "samba";
# TODO: expose over wan
defaults.port.listen = mkAlmostOptionDefault "lan";
ports = mapAttrs (_: mapAlmostOptionDefaults) {
port0 = {
port = 137;
transport = "udp";
};
port1 = {
port = 138;
transport = "udp";
};
port2 = {
port = 139;
transport = "tcp";
};
default = {
port = 445;
transport = "tcp";
};
};
};
}

View file

@ -0,0 +1,155 @@
{
config,
name,
lib,
gensokyo-zone,
...
}: let
inherit (gensokyo-zone.lib) mkAlmostOptionDefault;
inherit (lib.options) mkOption mkEnableOption;
inherit (lib.modules) mkIf mkMerge mkOptionDefault;
inherit (lib.attrsets) mapAttrsToList getAttrFromPath;
inherit (lib.trivial) mapNullable;
inherit (lib.strings) concatStringsSep;
systemConfig = config;
portModule = {config, service, ...}: {
options = with lib.types; {
enable =
mkEnableOption "port"
// {
default = true;
};
listen = mkOption {
type = enum ["wan" "lan" "int" "localhost"];
};
protocol = mkOption {
type = nullOr (enum ["http" "https"]);
default = null;
};
transport = mkOption {
type = enum ["tcp" "udp" "unix"];
};
path = mkOption {
type = nullOr path;
default = null;
description = "unix socket path";
};
ssl = mkOption {
type = bool;
default = false;
};
port = mkOption {
type = nullOr int;
};
};
config = {
transport = mkMerge [
(mkIf (config.protocol == "http" || config.protocol == "https") (mkOptionDefault "tcp"))
(mkIf config.ssl (mkOptionDefault "tcp"))
];
ssl = mkIf (config.protocol == "https") (
mkAlmostOptionDefault true
);
listen = mkOptionDefault service.defaults.port.listen;
};
};
serviceModule = {
config,
name,
...
}: {
options = with lib.types; {
enable = mkEnableOption "hosted service";
name = mkOption {
type = str;
default = name;
};
id = mkOption {
type = str;
default = config.name;
};
ports = mkOption {
type = attrsOf (submoduleWith {
modules = [portModule];
specialArgs = {
service = config;
};
});
};
nixos = {
serviceAttr = mkOption {
type = nullOr str;
default = null;
};
serviceAttrPath = mkOption {
type = nullOr (listOf str);
};
assertions = mkOption {
type = listOf (functionTo attrs);
default = [ ];
};
};
defaults = {
port = {
listen = mkOption {
type = str;
default = "int";
};
};
};
};
config = {
nixos = {
serviceAttrPath = mkOptionDefault (
mapNullable (serviceAttr: ["services" config.nixos.serviceAttr]) config.nixos.serviceAttr
);
assertions = let
serviceConfig = getAttrFromPath config.nixos.serviceAttrPath;
mkAssertion = f: nixosConfig: let
cfg = serviceConfig nixosConfig;
in f nixosConfig cfg;
enableAssertion = nixosConfig: cfg: {
assertion = (! cfg ? enable) || (config.enable == cfg.enable);
message = "enable == nixosConfig.${concatStringsSep "." config.nixos.serviceAttrPath}.enable";
};
in [
(mkIf (config.nixos.serviceAttrPath != null) (
mkAssertion enableAssertion
))
];
};
};
};
nixosModule = {config, system, ...}: let
mapAssertion = service: a: let
res = a config;
in res // {
message = "system.exports.${service.name}: " + res.message or "assertion failed";
};
assertions = mapAttrsToList (_: service: map (mapAssertion service) service.nixos.assertions) system.exports.services;
in {
config = {
assertions = mkMerge assertions;
# TODO: export ports via firewall according to enable/listen/etc
};
};
in {
options.exports = with lib.types; {
services = mkOption {
type = attrsOf (submoduleWith {
modules = [serviceModule];
specialArgs = {
machine = name;
inherit systemConfig;
};
});
default = {};
};
};
config = {
modules = mkIf (config.type == "NixOS") [
nixosModule
];
};
}

View file

@ -0,0 +1,7 @@
{...}: {
config.exports.services.tailscale = {
id = "tail";
nixos.serviceAttr = "tailscale";
ports = {};
};
}

View file

@ -0,0 +1,47 @@
{lib, gensokyo-zone, ...}: let
inherit (gensokyo-zone.lib) mapAlmostOptionDefaults mkAlmostOptionDefault;
inherit (lib.attrsets) mapAttrs;
in {
config.exports.services.unifi = { config, ... }: {
nixos.serviceAttr = "unifi";
defaults.port.listen = mkAlmostOptionDefault "lan";
ports = mapAttrs (_: mapAlmostOptionDefaults) {
management = {
# remote login
port = 8443;
protocol = "https";
listen = "int";
};
uap = {
# UAP to inform controller
port = 8080;
transport = "tcp";
};
portal = {
# HTTP portal redirect, if guest portal is enabled
port = 8880;
protocol = "http";
};
portal-secure = {
# HTTPS portal redirect
port = 8843;
protocol = "https";
};
speedtest = {
# UniFi mobile speed test
port = 6789;
transport = "tcp";
};
stun = {
port = 3478;
transport = "udp";
listen = "wan";
};
discovery = {
# device discovery
port = 10001;
transport = "udp";
};
};
};
}

View file

@ -0,0 +1,22 @@
{lib, gensokyo-zone, ...}: let
inherit (gensokyo-zone.lib) mapAlmostOptionDefaults mkAlmostOptionDefault;
inherit (lib.modules) mkIf;
in {
config.exports.services.vouch-proxy = { config, ... }: {
id = mkAlmostOptionDefault "login";
defaults.port.listen = mkAlmostOptionDefault "localhost";
nixos = {
serviceAttr = "vouch-proxy";
assertions = mkIf config.enable [
(nixosConfig: {
assertion = config.ports.default.port == nixosConfig.services.vouch-proxy.settings.vouch.port;
message = "port mismatch";
})
];
};
ports.default = mapAlmostOptionDefaults {
port = 30746;
protocol = "http";
};
};
}

View file

@ -0,0 +1,21 @@
{lib, gensokyo-zone, ...}: let
inherit (gensokyo-zone.lib) mapAlmostOptionDefaults mkAlmostOptionDefault;
inherit (lib.modules) mkIf;
in {
config.exports.services.zigbee2mqtt = { config, ... }: {
id = mkAlmostOptionDefault "z2m";
nixos = {
serviceAttr = "zigbee2mqtt";
assertions = mkIf config.enable [
(nixosConfig: {
assertion = config.ports.default.port == nixosConfig.services.zigbee2mqtt.settings.frontend.port;
message = "port mismatch";
})
];
};
ports.default = mapAlmostOptionDefaults {
port = 8072;
protocol = "http";
};
};
}

View file

@ -15,6 +15,11 @@ in {
inherit (lib.types) str listOf attrs unspecified enum;
inherit (lib.options) mkOption;
in {
name = mkOption {
type = str;
default = name;
readOnly = true;
};
arch = mkOption {
description = "Processor architecture of the host";
type = str;

View file

@ -6,7 +6,7 @@
}:
let
inherit (lib.options) mkOption mkEnableOption;
inherit (lib.modules) mkIf mkMerge mkBefore mkDefault;
inherit (lib.modules) mkIf mkMerge mkBefore mkDefault mkOptionDefault;
inherit (lib.strings) optionalString concatStringsSep;
inherit (config.services) tailscale;
inherit (config.services) nginx;
@ -86,6 +86,10 @@ in {
default = 4444;
};
kpasswd = mkOption {
type = port;
default = 464;
};
kadmin = mkOption {
type = port;
default = 749;
};
@ -108,46 +112,58 @@ in {
};
config = {
services.nginx = {
access.ldap = {
enable = mkDefault true;
host = mkDefault access.host;
port = mkDefault 389;
sslPort = mkDefault access.ldapPort;
useACMEHost = mkDefault virtualHosts.freeipa.ssl.cert.name;
bind.sslPort = mkIf access.preread.enable (mkDefault access.preread.ldapPort);
access.freeipa = {
host = mkOptionDefault (config.lib.access.getAddressFor (config.lib.access.systemForService "freeipa").name "lan");
};
resolver.addresses = mkIf access.preread.enable (mkMerge [
(mkDefault [ "[::1]:5353" "127.0.0.1:5353" ])
(mkIf config.systemd.network.enable [ "127.0.0.53" ])
]);
defaultSSLListenPort = mkIf access.preread.enable access.preread.port;
streamConfig = let
upstreams' = {
freeipa = "${access.host}:${toString access.port}";
ldap_freeipa = "${nginx.access.ldap.host}:${toString nginx.access.ldap.sslPort}";
ldap = "localhost:${toString nginx.access.ldap.bind.sslPort}";
nginx = "localhost:${toString nginx.defaultSSLListenPort}";
samba = if config.services.samba.enable
then "localhost:445"
else "smb.local.${config.networking.domain}:445";
stream = {
upstreams = {
freeipa.servers.access = let
system = config.lib.access.systemForService "freeipa";
inherit (system.exports.services) freeipa;
in {
addr = mkDefault (config.lib.access.getAddressFor system.name "lan");
port = mkOptionDefault freeipa.ports.default.port;
};
samba_access.servers.access = let
system = config.lib.access.systemForService "samba";
inherit (system.exports.services) samba;
in {
addr = mkDefault (config.lib.access.getAddressFor system.name "lan");
port = mkOptionDefault samba.ports.default.port;
};
ldaps_access.servers.access = {
addr = mkDefault "localhost";
port = mkOptionDefault nginx.stream.servers.ldap.listen.ldaps.port;
};
nginx.servers.access = {
addr = mkDefault "localhost";
port = mkOptionDefault nginx.defaultSSLListenPort;
};
};
servers = {
ldap = {
listen = {
ldaps.port = mkIf access.preread.enable (mkDefault access.preread.ldapPort);
};
proxy.upstream = mkDefault "ldap";
ssl.cert.copyFromVhost = mkDefault "freeipa";
};
};
};
streamConfig = let
upstreams = {
freeipa = "freeipa";
ldap = "ldaps_access";
ldap_freeipa = "ldaps";
samba = "samba_access";
nginx = "nginx";
};
upstreams = builtins.mapAttrs (name: _: name) upstreams';
preread = ''
upstream freeipa {
server ${upstreams'.freeipa};
}
upstream ldap_freeipa {
server ${upstreams'.ldap_freeipa};
}
upstream ldap {
server ${upstreams'.ldap};
}
upstream samba {
server ${upstreams'.samba};
}
upstream nginx {
server ${upstreams'.nginx};
}
map $ssl_preread_server_name $ssl_server_name {
hostnames;
${virtualHosts.freeipa.serverName} ${upstreams.freeipa};
@ -204,6 +220,11 @@ in {
listen [::]:${toString access.kerberos.ports.kpasswd} udp;
proxy_pass ${access.host}:${toString access.kerberos.ports.kpasswd};
}
server {
listen 0.0.0.0:${toString access.kerberos.ports.kadmin};
listen [::]:${toString access.kerberos.ports.kadmin};
proxy_pass ${access.host}:${toString access.kerberos.ports.kadmin};
}
'';
in mkMerge [
(mkIf access.preread.enable preread)
@ -265,6 +286,7 @@ in {
(mkIf access.kerberos.enable [
access.kerberos.ports.ticket
access.kerberos.ports.kpasswd
access.kerberos.ports.kadmin
])
(mkIf access.preread.enable [
636

View file

@ -1,49 +1,21 @@
{
config,
access,
lib,
...
}: let
inherit (lib.options) mkOption;
inherit (lib.modules) mkIf mkDefault;
inherit (lib.lists) head optional;
inherit (lib.strings) splitString;
inherit (lib.lists) optional;
inherit (config.services) nginx;
access = nginx.access.freepbx;
hasSsl = nginx.virtualHosts.freepbx'ucp.listen'.ucpSsl.enable;
system = access.systemForService "freepbx";
inherit (system.exports.services) freepbx;
in {
options.services.nginx.access.freepbx = with lib.types; {
host = mkOption {
type = str;
default = config.lib.access.getHostnameFor "freepbx" "lan";
};
url = mkOption {
type = str;
default = "https://${access.host}";
};
asteriskPort = mkOption {
type = port;
default = 8088;
};
asteriskSslPort = mkOption {
type = port;
default = 8089;
};
ucpPort = mkOption {
type = port;
default = 8001;
};
ucpSslPort = mkOption {
type = port;
default = 8003;
};
ucpUrl = mkOption {
type = str;
default = "https://${access.host}:${toString access.ucpSslPort}";
};
};
config.services.nginx = {
virtualHosts = let
proxyScheme = head (splitString ":" access.url);
proxyScheme = "https";
url = access.proxyUrlFor { serviceName = "freepbx"; portName = proxyScheme; };
ucpUrl = access.proxyUrlFor { serviceName = "freepbx"; portName = "ucp-ssl"; };
# TODO: ports.asterisk/asterisk-ssl?
extraConfig = ''
proxy_buffer_size 128k;
proxy_buffers 4 256k;
@ -57,11 +29,11 @@ in {
'';
locations = {
"/" = {
proxyPass = access.url;
proxyPass = mkDefault url;
};
"/socket.io" = {
proxy.websocket.enable = true;
proxyPass = "${access.ucpUrl}/socket.io";
proxyPass = mkDefault "${ucpUrl}/socket.io";
extraConfig = ''
proxy_hide_header Access-Control-Allow-Origin;
add_header Access-Control-Allow-Origin $pbx_scheme://$host;
@ -81,11 +53,11 @@ in {
ssl.cert.copyFromVhost = "freepbx";
listen' = {
ucp = {
port = access.ucpPort;
port = mkDefault freepbx.ports.ucp.port;
extraParameters = [ "default_server" ];
};
ucpSsl = {
port = access.ucpSslPort;
port = mkDefault freepbx.ports.ucp-ssl.port;
ssl = true;
extraParameters = [ "default_server" ];
};
@ -93,8 +65,9 @@ in {
proxy.websocket.enable = true;
vouch.enable = mkDefault true;
local.denyGlobal = mkDefault nginx.virtualHosts.freepbx.local.denyGlobal;
locations = {
inherit (locations) "/socket.io";
locations."/socket.io" = {
inherit (locations."/socket.io") proxy extraConfig;
proxyPass = mkDefault nginx.virtualHosts.freepbx.locations."/socket.io".proxyPass;
};
inherit extraConfig kTLS;
};
@ -103,23 +76,34 @@ in {
http = { };
https.ssl = true;
ucp = {
port = access.ucpPort;
port = mkDefault nginx.virtualHosts.freepbx'ucp.listen'.ucp.port;
};
ucpSsl = {
port = access.ucpSslPort;
port = mkDefault nginx.virtualHosts.freepbx'ucp.listen'.ucpSsl.port;
ssl = true;
};
};
ssl.cert.copyFromVhost = "freepbx";
local.enable = true;
inherit name locations extraConfig kTLS;
locations = {
"/" = {
proxyPass = mkDefault nginx.virtualHosts.freepbx.locations."/".proxyPass;
};
"/socket.io" = {
inherit (locations."/socket.io") proxy extraConfig;
proxyPass = mkDefault nginx.virtualHosts.freepbx.locations."/socket.io".proxyPass;
};
};
inherit name extraConfig kTLS;
};
};
};
config.networking.firewall = let
websocketPorts = [access.ucpPort] ++ optional hasSsl access.ucpSslPort;
websocketPorts = virtualHost: [
virtualHost.listen'.ucp.port
] ++ optional virtualHost.listen'.ucpSsl.enable virtualHost.listen'.ucpSsl.port;
in {
interfaces.local.allowedTCPPorts = websocketPorts;
allowedTCPPorts = mkIf (!nginx.virtualHosts.freepbx'ucp.local.denyGlobal) websocketPorts;
interfaces.local.allowedTCPPorts = websocketPorts nginx.virtualHosts.freepbx'local;
allowedTCPPorts = mkIf (!nginx.virtualHosts.freepbx'ucp.local.denyGlobal) (websocketPorts nginx.virtualHosts.freepbx'ucp);
};
}

View file

@ -1,9 +1,10 @@
{
config,
lib,
access,
...
}: let
inherit (lib.modules) mkIf mkDefault;
inherit (lib.modules) mkDefault;
inherit (config.services) home-assistant nginx;
name.shortServer = mkDefault "home";
listen' = {
@ -24,8 +25,9 @@ in {
websocket.enable = true;
headers.enableRecommended = true;
};
proxyPass = mkIf home-assistant.enable (mkDefault
"http://localhost:${toString home-assistant.config.http.server_port}"
proxyPass = mkDefault (
if home-assistant.enable then "http://localhost:${toString home-assistant.config.http.server_port}"
else access.proxyUrlFor { serviceName = "home-assistant"; }
);
};
};

View file

@ -1,9 +1,10 @@
{
config,
access,
lib,
...
}: let
inherit (lib.modules) mkIf mkMerge mkBefore mkDefault;
inherit (lib.modules) mkMerge mkBefore mkDefault;
inherit (lib.strings) replaceStrings concatStringsSep concatMapStringsSep escapeRegex;
inherit (config.services.nginx) virtualHosts;
cfg = config.services.invidious;
@ -86,8 +87,9 @@ in {
location
{
vouch.requireAuth = true;
proxyPass = mkIf cfg.enable (
mkDefault "http://localhost:${toString cfg.port}"
proxyPass = mkDefault (if cfg.enable
then "http://localhost:${toString cfg.port}"
else access.proxyUrlFor { serviceName = "invidious"; }
);
}
];

View file

@ -1,9 +1,10 @@
{
config,
lib,
access,
...
}: let
inherit (lib.modules) mkIf mkDefault;
inherit (lib.modules) mkDefault;
cfg = config.services.keycloak;
inherit (config.services) nginx;
in {
@ -13,11 +14,11 @@ in {
name.shortServer = mkDefault "sso";
ssl.force = mkDefault true;
locations."/".proxyPass = let
url = mkDefault (if cfg.sslCertificate != null
then "https://localhost:${toString cfg.settings.https-port}"
else "http://localhost:${toString cfg.settings.http-port}"
url = mkDefault "${cfg.protocol}://localhost:${toString cfg.port}";
in mkDefault (
if cfg.enable then url
else access.proxyUrlFor { serviceName = "keycloak"; portName = "https"; }
);
in mkIf cfg.enable (mkDefault url);
};
keycloak'local = {
name.shortServer = mkDefault "sso";

View file

@ -1,60 +1,44 @@
{
config,
lib,
access,
...
}: let
inherit (lib.options) mkOption;
inherit (lib.modules) mkIf mkMerge mkDefault;
inherit (lib.lists) concatMap;
inherit (lib.modules) mkDefault;
inherit (lib.attrsets) mapAttrs;
inherit (config.services) nginx;
inherit (config.services.nginx) virtualHosts;
access = config.services.nginx.access.kitchencam;
system = access.systemForServiceId "kitchen";
inherit (system.exports.services) motion;
in {
options.services.nginx.access.kitchencam = with lib.types; {
streamPort = mkOption {
type = port;
default = 8081;
};
host = mkOption {
type = str;
default = "kitchencam.local.${config.networking.domain}";
};
url = mkOption {
type = str;
default = "http://${access.host}:8080";
};
streamUrl = mkOption {
type = str;
default = "http://${access.host}:${toString access.streamPort}";
};
useACMEHost = mkOption {
type = nullOr str;
default = null;
};
};
config.services.nginx = {
virtualHosts = let
url = access.proxyUrlFor { inherit system; service = motion; };
streamUrl = access.proxyUrlFor { inherit system; service = motion; portName = "stream"; };
extraConfig = ''
proxy_redirect off;
proxy_buffering off;
'';
locations = {
"/" = {
proxyPass = access.url;
proxyPass = mkDefault url;
};
"~ ^/[0-9]+/(stream|motion|substream|current|source|status\\.json)$" = {
proxyPass = access.streamUrl;
proxyPass = mkDefault streamUrl;
inherit extraConfig;
};
"~ ^/(stream|motion|substream|current|source|cameras\\.json|status\\.json)$" = {
proxyPass = access.streamUrl;
proxyPass = mkDefault streamUrl;
inherit extraConfig;
};
};
listen' = {
http = { };
https.ssl = true;
stream.port = mkDefault access.streamPort;
stream = {
enable = mkDefault motion.ports.stream.enable;
port = mkDefault motion.ports.stream.port;
};
};
name.shortServer = mkDefault "kitchen";
kTLS = mkDefault true;
@ -64,13 +48,18 @@ in {
vouch.enable = true;
};
kitchencam'local = {
inherit name locations listen' kTLS;
inherit name listen' kTLS;
ssl.cert.copyFromVhost = "kitchencam";
local.enable = true;
locations = mapAttrs (name: location: location // {
proxyPass = mkDefault nginx.virtualHosts.kitchencam.locations.${name}.proxyPass;
}) locations;
};
};
};
config.networking.firewall.allowedTCPPorts = [
access.streamPort
config.networking.firewall.allowedTCPPorts = let
inherit (nginx.virtualHosts.kitchencam) listen';
in [
listen'.stream.port
];
}

View file

@ -1,32 +1,21 @@
{
config,
lib,
gensokyo-zone,
access,
...
}:
let
inherit (lib.options) mkOption mkEnableOption;
inherit (lib.modules) mkIf mkMerge;
inherit (lib.strings) concatMapStringsSep optionalString;
inherit (config.services.nginx) virtualHosts;
inherit (config.networking.access) cidrForNetwork localaddrs;
access = config.services.nginx.access.ldap;
inherit (gensokyo-zone.lib) mkAlmostOptionDefault;
inherit (lib.options) mkOption;
inherit (lib.modules) mkIf mkOptionDefault;
inherit (config.services) nginx;
portPlaintext = 389;
portSsl = 636;
allows = let
mkAllow = cidr: "allow ${cidr};";
allows = concatMapStringsSep "\n" mkAllow cidrForNetwork.allLocal.all + optionalString localaddrs.enable ''
include ${localaddrs.stateDir}/*.nginx.conf;
'';
in ''
${allows}
deny all;
'';
system = access.systemForService "ldap";
inherit (system.exports.services) ldap;
in {
options.services.nginx.access.ldap = with lib.types; {
enable = mkEnableOption "LDAP proxy";
host = mkOption {
type = str;
};
domain = mkOption {
type = str;
default = "ldap.${config.networking.domain}";
@ -43,67 +32,49 @@ in {
type = str;
default = "ldap.tail.${config.networking.domain}";
};
port = mkOption {
type = port;
default = portSsl;
};
sslPort = mkOption {
type = port;
default = portSsl;
};
bind = {
sslPort = mkOption {
type = port;
default = portSsl;
};
port = mkOption {
type = port;
default = portPlaintext;
};
};
useACMEHost = mkOption {
type = nullOr str;
default = virtualHosts.${access.domain}.useACMEHost or null;
};
};
config = {
services.nginx = {
streamConfig = let
cert = config.security.acme.certs.${access.useACMEHost};
proxySsl = port: optionalString (port == portSsl) ''
proxy_ssl on;
proxy_ssl_verify off;
'';
in mkIf access.enable (mkMerge [
''
server {
listen 0.0.0.0:${toString access.bind.port};
listen [::]:${toString access.bind.port};
${allows}
proxy_pass ${access.host}:${toString access.port};
${proxySsl access.port}
}
''
(mkIf (access.useACMEHost != null) ''
server {
listen 0.0.0.0:${toString access.bind.sslPort} ssl;
listen [::]:${toString access.bind.sslPort} ssl;
ssl_certificate ${cert.directory}/fullchain.pem;
ssl_certificate_key ${cert.directory}/key.pem;
ssl_trusted_certificate ${cert.directory}/chain.pem;
proxy_pass ${access.host}:${toString access.sslPort};
${proxySsl access.sslPort}
}
'')
]);
stream = {
upstreams = let
addr = mkAlmostOptionDefault (access.getAddressFor system.name "lan");
in {
ldap.servers.access = {
inherit addr;
port = mkOptionDefault ldap.ports.default.port;
};
ldaps = {
enable = mkAlmostOptionDefault ldap.ports.ssl.enable;
ssl.enable = mkAlmostOptionDefault true;
servers.access = {
inherit addr;
port = mkOptionDefault ldap.ports.ssl.port;
};
};
};
servers.ldap = {
listen = {
ldap.port = mkOptionDefault portPlaintext;
ldaps = {
port = mkOptionDefault portSsl;
ssl = true;
};
};
proxy.upstream = mkAlmostOptionDefault (
if nginx.stream.upstreams.ldaps.enable then "ldaps" else "ldap"
);
};
};
};
networking.firewall = {
networking.firewall = let
inherit (nginx.stream.servers.ldap) listen;
in {
interfaces.local.allowedTCPPorts = [
access.bind.port
listen.ldap.port
];
allowedTCPPorts = [
access.bind.sslPort
allowedTCPPorts = mkIf listen.ldaps.enable [
listen.ldaps.port
];
};
};

View file

@ -1,72 +1,59 @@
{
config,
lib,
inputs,
access,
gensokyo-zone,
...
}:
let
inherit (inputs.self.lib.lib) mkAlmostOptionDefault;
inherit (lib.options) mkOption mkEnableOption;
inherit (lib.modules) mkIf mkMerge mkOptionDefault;
inherit (gensokyo-zone.lib) mkAlmostOptionDefault;
inherit (lib.modules) mkIf mkOptionDefault;
inherit (config.services) nginx;
access = nginx.access.mosquitto;
portPlaintext = 1883;
portSsl = 8883;
system = access.systemForService "mosquitto";
inherit (system.exports.services) mosquitto;
in {
options.services.nginx.access.mosquitto = with lib.types; {
enable = mkEnableOption "MQTT proxy";
host = mkOption {
type = str;
};
port = mkOption {
type = port;
default = portPlaintext;
};
bind = {
sslPort = mkOption {
type = port;
default = portSsl;
};
port = mkOption {
type = port;
default = portPlaintext;
};
};
};
config = {
services.nginx = {
stream = {
upstreams.mosquitto = {
upstreams = let
addr = mkAlmostOptionDefault (access.getAddressFor system.name "lan");
in {
mqtt.servers.access = {
inherit addr;
port = mkOptionDefault mosquitto.ports.default.port;
};
mqtts = {
enable = mkAlmostOptionDefault mosquitto.ports.ssl.enable;
ssl.enable = true;
servers.access = {
addr = mkAlmostOptionDefault access.host;
port = mkOptionDefault access.port;
inherit addr;
port = mkOptionDefault mosquitto.ports.ssl.port;
};
};
};
servers.mosquitto = {
listen = {
mqtt.port = portPlaintext;
mqtt.port = mkOptionDefault portPlaintext;
mqtts = {
ssl = true;
port = portSsl;
port = mkOptionDefault portSsl;
};
};
extraConfig = let
proxySsl = port: mkIf (port == portSsl) ''
proxy_ssl on;
proxy_ssl_verify off;
'';
in mkMerge [
"proxy_pass ${nginx.stream.upstreams.mosquitto.name};"
(proxySsl access.port)
];
proxy.upstream = mkAlmostOptionDefault (
if nginx.stream.upstreams.mqtts.enable then "mqtts" else "mqtt"
);
};
};
};
networking.firewall = {
interfaces.local.allowedTCPPorts = [
access.bind.port
(mkIf nginx.stream.servers.mosquitto.listen.mqtts.enable access.bind.sslPort)
interfaces.local.allowedTCPPorts = let
inherit (nginx.stream.servers.mosquitto) listen;
in [
listen.mqtt.port
(mkIf listen.mqtts.enable listen.mqtts.port)
];
};
};

View file

@ -1,27 +1,14 @@
{
config,
lib,
access,
...
}: let
inherit (lib.options) mkOption;
inherit (lib.modules) mkIf mkDefault mkOptionDefault;
inherit (lib.modules) mkIf mkDefault;
inherit (config.services) nginx;
cfg = config.services.plex;
access = nginx.access.plex;
in {
options.services.nginx.access.plex = with lib.types; {
url = mkOption {
type = str;
};
externalPort = mkOption {
type = nullOr port;
default = null;
};
};
config.services.nginx = {
access.plex = mkIf cfg.enable {
url = mkOptionDefault "http://localhost:${toString cfg.port}";
};
virtualHosts = let
extraConfig = ''
# Some players don't reopen a socket and playback stops totally instead of resuming after an extended pause
@ -44,8 +31,11 @@ in {
proxy_buffering off;
'';
locations."/" = {
proxy.websocket.enable = true;
proxyPass = access.url;
proxy.websocket.enable = mkDefault true;
proxyPass = mkDefault (if cfg.enable
then "http://localhost:${toString cfg.port}"
else access.proxyUrlFor { serviceName = "plex"; }
);
};
name.shortServer = mkDefault "plex";
kTLS = mkDefault true;
@ -56,8 +46,8 @@ in {
http = { };
https.ssl = true;
external = {
enable = mkDefault (access.externalPort != null);
port = mkDefault access.externalPort;
enable = mkDefault false;
port = mkDefault 32400;
extraParameters = [ "default_server" ];
};
};
@ -69,7 +59,9 @@ in {
};
};
};
config.networking.firewall.allowedTCPPorts = mkIf (access.externalPort != null) [
access.externalPort
config.networking.firewall.allowedTCPPorts = let
inherit (nginx.virtualHosts.plex) listen';
in mkIf listen'.external.enable [
listen'.external.port
];
}

View file

@ -1,12 +1,13 @@
{
config,
lib,
access,
...
}: let
inherit (lib.modules) mkMerge mkDefault;
inherit (lib.modules) mkDefault;
inherit (lib.strings) escapeRegex;
inherit (config.services) nginx tailscale;
proxyPass = "https://reisen.local.${config.networking.domain}:8006/";
proxyPass = access.proxyUrlFor { serviceName = "proxmox"; } + "/";
in {
config.services.nginx.virtualHosts = let
locations."/" = {

View file

@ -1,70 +1,37 @@
{
config,
lib,
access,
...
}: let
inherit (lib.options) mkOption mkEnableOption;
inherit (lib.modules) mkIf mkDefault mkOptionDefault;
inherit (config.services) nginx tailscale unifi;
access = nginx.access.unifi;
inherit (lib.modules) mkDefault;
inherit (config.services) nginx;
in {
options.services.nginx.access.unifi = with lib.types; {
global = {
management = mkEnableOption "global management port access";
};
host = mkOption {
type = str;
};
url = mkOption {
type = str;
default = "https://${access.host}:${toString access.managementPort}";
};
managementPort = mkOption {
type = port;
default = 8443;
};
};
config.services.nginx = {
access.unifi = mkIf unifi.enable {
host = mkOptionDefault "localhost";
};
virtualHosts = let
extraConfig = ''
proxy_redirect off;
proxy_buffering off;
'';
locations."/" = {
proxyPass = mkDefault access.url;
};
name.shortServer = mkDefault "unifi";
kTLS = mkDefault true;
in {
unifi'management = mkIf access.global.management {
listen'.management = {
port = access.managementPort;
ssl = true;
extraParameters = [ "default_server" ];
};
ssl = {
force = true;
cert.copyFromVhost = "unifi";
};
inherit name locations extraConfig kTLS;
};
unifi = {
inherit name locations extraConfig kTLS;
inherit name extraConfig kTLS;
vouch.enable = mkDefault true;
ssl.force = mkDefault true;
locations."/" = {
proxyPass = mkDefault (access.proxyUrlFor { serviceName = "unifi"; portName = "management"; });
};
};
unifi'local = {
inherit name locations extraConfig kTLS;
inherit name extraConfig kTLS;
ssl.cert.copyFromVhost = "unifi";
local.enable = true;
locations."/" = {
proxyPass = mkDefault nginx.virtualHosts.unifi.locations."/".proxyPass;
};
};
};
config.networking.firewall = {
interfaces.local.allowedTCPPorts = [access.managementPort];
allowedTCPPorts = mkIf access.global.management [access.managementPort];
};
}

View file

@ -1,23 +1,16 @@
{
config,
lib,
access,
...
}: let
inherit (lib.modules) mkIf mkMerge mkDefault mkOptionDefault;
inherit (lib.modules) mkIf mkMerge mkDefault;
inherit (config) networking;
inherit (config.services) tailscale nginx;
cfg = config.services.vouch-proxy;
in {
config.services.nginx = {
virtualHosts = let
localVouchUrl = let
inherit (cfg.settings.vouch) listen;
host =
if listen == "0.0.0.0" || listen == "[::]"
then "localhost"
else listen;
in
"http://${host}:${toString cfg.settings.vouch.port}";
locations = {
"/" = {
ssl.force = true;
@ -54,7 +47,9 @@ in {
locations = mkMerge [
locations
{
"/".proxyPass = mkIf cfg.enable (mkDefault localVouchUrl);
"/".proxyPass = mkDefault (
access.proxyUrlFor { serviceName = "vouch-proxy"; serviceId = "login"; }
);
}
];
};
@ -73,7 +68,9 @@ in {
locations = mkMerge [
locations
{
"/".proxyPass = mkDefault nginx.virtualHosts.vouch.locations."/".proxyPass;
"/".proxyPass = mkDefault (
access.proxyUrlFor { serviceName = "vouch-proxy"; serviceId = "login.local"; }
);
}
(localLocations "sso.local.${networking.domain}")
];

View file

@ -1,9 +1,10 @@
{
config,
lib,
access,
...
}: let
inherit (lib.modules) mkIf mkDefault;
inherit (lib.modules) mkDefault;
inherit (config.services) nginx zigbee2mqtt;
name.shortServer = mkDefault "z2m";
in {
@ -12,8 +13,9 @@ in {
zigbee2mqtt = {
locations."/" = {
proxy.websocket.enable = true;
proxyPass = mkIf zigbee2mqtt.enable (
mkDefault "http://localhost:${toString zigbee2mqtt.settings.frontend.port}"
proxyPass = mkDefault (
if zigbee2mqtt.enable then "http://localhost:${toString zigbee2mqtt.settings.frontend.port}"
else access.proxyUrlFor { serviceName = "zigbee2mqtt"; }
);
};
inherit name;

View file

@ -1,4 +1,4 @@
{config, lib, access, ...}: let
{lib, access, ...}: let
inherit (lib.modules) mkDefault;
in {
config = {
@ -9,7 +9,7 @@ in {
ipv6SendRAConfig = {
Managed = mkDefault false;
EmitDNS = mkDefault true;
DNS = [ (access.getAddress6For "utsuho" "int") ];
DNS = [ (access.systemForService "dnsmasq").access.address6ForNetwork.int ];
# Domains = [ "int.${networking.domain}" ];
EmitDomains = mkDefault false;
RouterPreference = mkDefault "low";

View file

@ -16,7 +16,7 @@ in {
invidious_hmac_key = commonSecret;
};
networking.firewall.interfaces.local.allowedTCPPorts = [cfg.port];
networking.firewall.interfaces.int.allowedTCPPorts = [cfg.port];
users.groups.invidious = {};
users.users.invidious = {
isSystemUser = true;

View file

@ -34,7 +34,7 @@ in {
};
networking.firewall.interfaces.int.allowedTCPPorts = mkIf cfg.enable [
(if cfg.sslCertificate != null then cfg.settings.https-port else cfg.settings.http-port)
cfg.port
];
systemd.services.keycloak = mkIf cfg.enable {
serviceConfig.DynamicUser = mkForce false;
@ -52,7 +52,7 @@ in {
settings = {
hostname = mkDefault (if hostname-strict then hostname else null);
proxy = mkDefault (if cfg.sslCertificate != null then "reencrypt" else "edge");
proxy = mkDefault (if cfg.protocol == "https" then "reencrypt" else "edge");
hostname-strict = mkDefault hostname-strict;
hostname-strict-https = mkDefault hostname-strict;
proxy-headers = mkDefault "xforwarded";

View file

@ -18,8 +18,9 @@ in {
};
gensokyo-zone = let
toLdap = replaceStrings [ "idp." ] [ "ldap." ];
lanName = access.getHostnameFor "freeipa" "lan";
localName = access.getHostnameFor "freeipa" "local";
system = access.systemForService "kerberos";
lanName = access.getHostnameFor system.name "lan";
localName = access.getHostnameFor system.name "local";
ldapLan = toLdap lanName;
ldapLocal = toLdap localName;
in {

View file

@ -29,7 +29,7 @@ in {
networkConfig.MulticastDNS = true;
};
networking.nameservers' = mkIf enableDns (mkBefore [
{ address = access.getAddressFor "utsuho" "lan"; }
{ address = access.getAddressFor (access.systemForService "dnsmasq").name "lan"; }
]);
# prioritize our resolver over systemd-resolved!
system.nssDatabases.hosts = let

View file

@ -1,7 +1,8 @@
{ gensokyo-zone, access, config, lib, ... }: let
inherit (gensokyo-zone.lib) mkAlmostOptionDefault;
inherit (lib.modules) mkIf mkBefore mkAfter mkDefault;
inherit (lib.strings) replaceStrings;
inherit (lib.lists) tail;
inherit (lib.strings) splitString concatStringsSep;
cfg = config.services.sssd;
in {
imports = [
@ -12,13 +13,18 @@ in {
services.sssd = {
enable = (mkDefault true);
gensokyo-zone = let
toService = service: replaceStrings [ "idp." ] [ "${service}." ];
serviceFragment = service: service;
toService = service: hostname: let
segments = splitString "." hostname;
in concatStringsSep "." ([ (serviceFragment service) ] ++ tail segments);
toFreeipa = toService "freeipa";
toLdap = toService "ldap";
lanName = access.getHostnameFor "freeipa" "lan";
localName = access.getHostnameFor "freeipa" "local";
tailName = access.getHostnameFor "hakurei" "tail";
mkServers = serviceName: let
system = access.systemForService serviceName;
lanName = access.getHostnameFor system.name "lan";
localName = access.getHostnameFor system.name "local";
localToo = lanName != localName;
in {
servers = mkBefore [
lanName
(mkIf localToo localName)
@ -27,14 +33,14 @@ in {
(toFreeipa lanName)
(mkIf config.services.tailscale.enable (toFreeipa tailName))
]);
in {
krb5.servers = {
inherit servers backups;
};
in {
krb5.servers = mkServers "kerberos";
ipa.servers = mkServers "freeipa";
ldap = {
uris = {
backups = mkAlmostOptionDefault (mkAfter [
(mkIf config.services.tailscale.enable (toLdap tailName))
(mkIf config.services.tailscale.enable (toService "ldap" tailName))
]);
};
bind.passwordFile = mkIf (cfg.gensokyo-zone.backend == "ldap") config.sops.secrets.gensokyo-zone-peep-passwords.path;

View file

@ -4,7 +4,7 @@
lib,
...
}: let
inherit (lib.modules) mkIf mkMerge mkDefault;
inherit (lib.modules) mkIf mkDefault;
cfg = config.services.unifi;
in {
services.unifi = {
@ -14,23 +14,27 @@ in {
#mongodbPackage = mkDefault pkgs.mongodb-5_0;
};
networking.firewall.interfaces.local = mkIf cfg.enable {
allowedTCPPorts = mkMerge [
[
networking.firewall = mkIf cfg.enable {
interfaces.int = {
allowedTCPPorts = [
8443 # remote login
]
(mkIf (!cfg.openFirewall) [
];
};
interfaces.local = {
allowedTCPPorts = mkIf (!cfg.openFirewall) [
8080 # Port for UAP to inform controller.
8880 # Port for HTTP portal redirect, if guest portal is enabled.
8843 # Port for HTTPS portal redirect, ditto.
6789 # Port for UniFi mobile speed test.
])
];
allowedUDPPorts = mkIf (!cfg.openFirewall) [
3478 # UDP port used for STUN.
10001 # UDP port used for device discovery.
];
};
allowedUDPPorts = mkIf (!cfg.openFirewall) [
3478 # UDP port used for STUN.
];
};
users = mkIf cfg.enable {
users.unifi.uid = 990;

View file

@ -27,10 +27,9 @@ in {
user = "z2m";
password = "!secret z2m_pass";
server = let
utsuho = access.nixosFor "utsuho";
mqttHost = access.getHostnameFor "utsuho" "lan";
url = access.proxyUrlFor { serviceName = "mosquitto"; scheme = "mqtt"; };
in mkIf (!config.services.mosquitto.enable) (
assert utsuho.services.mosquitto.enable; mkAlmostDefault "mqtt://${mqttHost}:1883"
mkAlmostDefault url
);
};
homeassistant = true;

View file

@ -7,5 +7,9 @@ _: {
modules = [
./nixos.nix
];
access.tailscale.enable = true;
exports = {
services = {
tailscale.enable = true;
};
};
}

View file

@ -12,6 +12,7 @@
];
specialArgs = {
inherit name inputs std meta;
inherit (inputs.self.lib) gensokyo-zone;
};
})
(set.map (_: c: c) meta.systems);

View file

@ -30,4 +30,11 @@ _: {
mode = "0600";
};
};
exports = {
services = {
freeipa.enable = true;
ldap.enable = true;
kerberos.enable = true;
};
};
}

View file

@ -14,4 +14,9 @@ _: {
};
};
};
exports = {
services = {
freepbx.enable = true;
};
};
}

View file

@ -14,7 +14,19 @@ _: {
};
};
access = {
tailscale.enable = true;
global.enable = true;
};
exports = {
services = {
tailscale.enable = true;
samba.enable = true;
vouch-proxy = {
enable = true;
id = "login.local";
};
};
exports = {
plex.enable = true;
};
};
}

View file

@ -3,16 +3,11 @@
meta,
lib,
access,
gensokyo-zone,
...
}: let
inherit (gensokyo-zone.lib) mkAddress6;
inherit (lib.modules) mkIf mkMerge;
keycloak = access.nixosFor "keycloak";
mediabox = access.nixosFor "mediabox";
tei = access.nixosFor "tei";
utsuho = access.nixosFor "utsuho";
inherit (mediabox.services) plex;
inherit (tei.services) home-assistant zigbee2mqtt;
inherit (utsuho.services) unifi mosquitto;
inherit (config.services) nginx;
inherit (nginx) virtualHosts;
in {
@ -218,29 +213,8 @@ in {
};
};
services.nginx = let
inherit (nginx) access;
#inherit (config.lib.access) getHostnameFor;
getHostnameFor = config.lib.access.getAddress4For;
in {
services.nginx = {
vouch.enableLocal = false;
access.mosquitto = assert mosquitto.enable; {
host = getHostnameFor "utsuho" "lan";
};
access.plex = assert plex.enable; {
url = "http://${getHostnameFor "mediabox" "lan"}:${toString plex.port}";
externalPort = 41324;
};
access.unifi = assert unifi.enable; {
host = getHostnameFor "utsuho" "lan";
};
access.freeipa = {
host = getHostnameFor "freeipa" "lan";
kerberos.ports.kpasswd = 464;
};
access.kitchencam = {
streamPort = 41081;
};
stream.servers = {
mosquitto.ssl.cert.name = "mosquitto";
};
@ -254,18 +228,11 @@ in {
# we're not the real sso record-holder, so don't respond globally..
local.denyGlobal = true;
ssl.cert.enable = true;
locations."/".proxyPass = "https://${getHostnameFor "keycloak" "lan"}:8443";
};
vouch = let
inherit (keycloak.services) vouch-proxy;
in assert vouch-proxy.enable; {
vouch = {
ssl.cert.enable = true;
locations."/".proxyPass = "http://${getHostnameFor "keycloak" "lan"}:${toString vouch-proxy.settings.vouch.port}";
};
vouch'local = let
vouch-proxy = config.services.vouch-proxy;
in assert vouch-proxy.enable; {
locations."/".proxyPass = "http://localhost:${toString vouch-proxy.settings.vouch.port}";
vouch'local = {
# we're not running another for tailscale sorry...
name.includeTailscale = true;
};
@ -274,29 +241,27 @@ in {
local.denyGlobal = true;
ssl.cert.enable = true;
};
home-assistant = assert home-assistant.enable; {
home-assistant = {
# not the real hass record-holder, so don't respond globally..
local.denyGlobal = true;
ssl.cert.enable = true;
locations."/".proxyPass = "http://${getHostnameFor "tei" "lan"}:${toString home-assistant.config.http.server_port}";
};
zigbee2mqtt = assert zigbee2mqtt.enable; {
zigbee2mqtt = {
# not the real z2m record-holder, so don't respond globally..
local.denyGlobal = true;
ssl.cert.enable = true;
locations."/".proxyPass = "http://${getHostnameFor "tei" "lan"}:${toString zigbee2mqtt.settings.frontend.port}";
};
grocy = {
# not the real grocy record-holder, so don't respond globally..
local.denyGlobal = true;
ssl.cert.enable = true;
locations."/".proxyPass = "http://${getHostnameFor "tei" "lan"}";
locations."/".proxyPass = "http://${mkAddress6 (access.getAddressFor "tei" "lan")}";
};
barcodebuddy = {
# not the real bbuddy record-holder, so don't respond globally..
local.denyGlobal = true;
ssl.cert.enable = true;
locations."/".proxyPass = "http://${getHostnameFor "tei" "lan"}";
locations."/".proxyPass = "http://${mkAddress6 (access.getAddressFor "tei" "lan")}";
};
freepbx = {
ssl.cert.enable = true;
@ -305,14 +270,17 @@ in {
proxied.enable = "cloudflared";
ssl.cert.enable = true;
};
plex.ssl.cert.enable = true;
plex = {
ssl.cert.enable = true;
listen'.external = {
enable = true;
port = 41324;
};
};
kitchencam.ssl.cert.enable = true;
invidious = {
ssl.cert.enable = true;
};
invidious'int = {
locations."/".proxyPass = "http://${getHostnameFor "mediabox" "lan"}:${toString mediabox.services.invidious.port}";
};
};
};
services.samba.tls = {

View file

@ -7,5 +7,10 @@ _: {
modules = [
./nixos.nix
];
access.tailscale.enable = true;
exports = {
services = {
keycloak.enable = true;
vouch-proxy.enable = true;
};
};
}

View file

@ -1,4 +1,4 @@
{meta, config, ...}: {
{meta, config, access, ...}: {
imports = let
inherit (meta) nixos;
in [
@ -13,21 +13,25 @@
services.cloudflared = let
tunnelId = "c9a4b8c9-42d9-4566-8cff-eb63ca26809d";
inherit (config.services) keycloak vouch-proxy;
in {
tunnels.${tunnelId} = {
default = "http_status:404";
credentialsFile = config.sops.secrets.cloudflared-tunnel-keycloak.path;
ingress = let
keycloakHost = if keycloak.settings.hostname != null then keycloak.settings.hostname else "sso.${config.networking.domain}";
keyCloakScheme = if keycloak.sslCertificate != null then "https" else "http";
keycloakPort = keycloak.settings."${keyCloakScheme}-port";
keycloak'system = access.systemForService "keycloak";
inherit (keycloak'system.exports.services) keycloak;
vouch'system = access.systemForServiceId "login";
inherit (vouch'system.exports.services) vouch-proxy;
in {
${keycloakHost} = assert keycloak.enable; {
service = "${keyCloakScheme}://localhost:${toString keycloakPort}";
originRequest.${if keyCloakScheme == "https" then "noTLSVerify" else null} = true;
"${keycloak.id}.${config.networking.domain}" = let
portName = if keycloak.ports.https.enable then "https" else "http";
in {
service = access.proxyUrlFor { system = keycloak'system; service = keycloak; inherit portName; };
originRequest.${if keycloak.ports.${portName}.protocol == "https" then "noTLSVerify" else null} = true;
};
"${vouch-proxy.id}.${config.networking.domain}" = {
service = access.proxyUrlFor { system = vouch'system; service = vouch-proxy; };
};
${vouch-proxy.domain}.service = assert vouch-proxy.enable; "http://localhost:${toString vouch-proxy.settings.vouch.port}";
};
};
};

View file

@ -15,4 +15,13 @@ _: {
address6 = "fd0a::ba27:ebff:fea8:f4ff";
};
};
exports = {
services = {
motion = {
id = "kitchen";
enable = true;
ports.stream.port = 41081;
};
};
};
}

View file

@ -7,5 +7,9 @@ _: {
modules = [
./nixos.nix
];
access.tailscale.enable = true;
exports = {
services = {
tailscale.enable = true;
};
};
}

View file

@ -7,4 +7,10 @@ _: {
modules = [
./nixos.nix
];
exports = {
services = {
plex.enable = true;
invidious.enable = true;
};
};
}

View file

@ -7,5 +7,10 @@ _: {
modules = [
./nixos.nix
];
access.tailscale.enable = true;
exports = {
services = {
tailscale.enable = true;
nfs.enable = true;
};
};
}

View file

@ -10,4 +10,9 @@ _: {
address6 = "fd0c::2";
};
};
exports = {
services = {
proxmox.enable = true;
};
};
}

View file

@ -1,46 +1,14 @@
{
access,
config,
lib,
access,
...
}: let
inherit (lib.modules) mkIf;
inherit (lib.attrsets) listToAttrs nameValuePair;
inherit (access) nixosFor;
inherit (config.networking) hostName;
inherit (config.services) nginx;
inherit (config.services) home-assistant nginx;
cfg = config.services.cloudflared;
apartment = "5e85d878-c6b2-4b15-b803-9aeb63d63543";
accessHostFor = {
hostName,
system ? nixosFor hostName,
network ? "lan",
...
}: let
host = access.getHostnameFor hostName network;
in
if hostName == config.networking.hostName
then "localhost"
else host;
ingressForNginx = {
host ? system.networking.fqdn,
port ? 80,
hostName,
system ? nixosFor hostName,
} @ args:
nameValuePair host {
service = "http://${accessHostFor args}:${toString port}";
};
ingressForHass = {
host ? system.services.home-assistant.domain,
port ? system.services.home-assistant.config.http.server_port,
hostName,
system ? nixosFor hostName,
...
} @ args:
nameValuePair host {
service = "http://${accessHostFor args}:${toString port}";
};
localNginx = "http://localhost:${toString nginx.defaultHTTPListenPort}";
in {
sops.secrets.cloudflared-tunnel-apartment.owner = cfg.user;
services.cloudflared = {
@ -48,21 +16,20 @@ in {
${apartment} = {
credentialsFile = config.sops.secrets.cloudflared-tunnel-apartment.path;
default = "http_status:404";
ingress = listToAttrs [
(ingressForNginx {
host = nginx.virtualHosts.zigbee2mqtt.serverName;
inherit hostName;
})
(ingressForNginx {
host = nginx.virtualHosts.grocy.serverName;
inherit hostName;
})
(ingressForNginx {
host = nginx.virtualHosts.barcodebuddy.serverName;
inherit hostName;
})
(ingressForHass {inherit hostName;})
];
ingress = {
${nginx.virtualHosts.zigbee2mqtt.serverName} = {
service = localNginx;
};
${nginx.virtualHosts.grocy.serverName} = {
service = localNginx;
};
${nginx.virtualHosts.barcodebuddy.serverName} = {
service = localNginx;
};
${home-assistant.domain} = assert home-assistant.enable; {
service = access.proxyUrlFor { serviceName = "home-assistant"; };
};
};
};
};
};

View file

@ -7,5 +7,12 @@ _: {
modules = [
./nixos.nix
];
access.tailscale.enable = true;
exports = {
services = {
tailscale.enable = true;
home-assistant.enable = true;
zigbee2mqtt.enable = true;
postgresql.enable = true;
};
};
}

View file

@ -7,4 +7,11 @@ _: {
modules = [
./nixos.nix
];
exports = {
services = {
unifi.enable = true;
mosquitto.enable = true;
dnsmasq.enable = true;
};
};
}

View file

@ -18,7 +18,6 @@ in {
];
services.cloudflared = let
inherit (config.services) unifi;
inherit (nginx) virtualHosts defaultHTTPListenPort;
tunnelId = "28bcd3fc-3467-4997-806b-546ba9995028";
localNginx = "http://localhost:${toString defaultHTTPListenPort}";
@ -27,7 +26,7 @@ in {
default = "http_status:404";
credentialsFile = config.sops.secrets.cloudflared-tunnel-utsuho.path;
ingress = {
${virtualHosts.unifi.serverName} = assert unifi.enable; {
${virtualHosts.unifi.serverName} = {
service = localNginx;
};
};

View file

@ -100,7 +100,6 @@ module "tewi" {
zone_id = cloudflare_zone.gensokyo-zone_zone.id
subdomains = [
"home",
"id",
"z2m",
"grocy",
"bbuddy",

View file

@ -67,6 +67,7 @@
"modules/system/network".functor.enable = true;
"modules/system/proxmox".functor.enable = true;
"modules/system/extern".functor.enable = true;
"modules/system/exports".functor.enable = true;
"modules/home".functor.enable = true;
"modules/type".functor.enable = true;
"modules/extern/home".functor.enable = true;