chore: nf-fmt-nix

This commit is contained in:
arcnmx 2024-05-13 15:13:58 -07:00
parent 7486517713
commit 9903866044
160 changed files with 4570 additions and 3019 deletions

View file

@ -3,19 +3,25 @@
whitelist = [ whitelist = [
"overlays/default.nix" "overlays/default.nix"
"ci/fmt.nix" "ci/fmt.nix"
"docs/derivation.nix"
"devShells.nix" "devShells.nix"
"shell.nix" "shell.nix"
"generate.nix"
"lib.nix" "lib.nix"
"outputs.nix" "outputs.nix"
"tree.nix" "tree.nix"
]; ];
whitelistDirs = [ whitelistDirs = [
"modules/extern" "modules/extern"
"modules/nixos"
"modules/system" "modules/system"
"nixos"
"overlays"
"packages"
"systems" "systems"
]; ];
blacklistDirs = [ blacklistDirs = [
"overlays" "modules/nixos/ldap"
"ci" "ci"
]; ];
}; };

View file

@ -151,19 +151,23 @@
LDAPSASL_NOCANON = "on"; LDAPSASL_NOCANON = "on";
}; };
arc = let arc = let
ldapdm = cmd: pkgs.writeShellScriptBin "dm-${cmd}" '' ldapdm = cmd:
${cmd} -D 'cn=Directory Manager' -y <(bitw get -f password ldap-directory-manager) "$@" pkgs.writeShellScriptBin "dm-${cmd}" ''
''; ${cmd} -D 'cn=Directory Manager' -y <(bitw get -f password ldap-directory-manager) "$@"
in default.overrideAttrs (default: { '';
nativeBuildInputs = default.nativeBuildInputs ++ [ in
(ldapdm "ldapwhoami") default.overrideAttrs (default: {
(ldapdm "ldappasswd") nativeBuildInputs =
(ldapdm "ldapsearch") default.nativeBuildInputs
(ldapdm "ldapadd") ++ [
(ldapdm "ldapmodify") (ldapdm "ldapwhoami")
(ldapdm "ldapdelete") (ldapdm "ldappasswd")
]; (ldapdm "ldapsearch")
}); (ldapdm "ldapadd")
(ldapdm "ldapmodify")
(ldapdm "ldapdelete")
];
});
in { in {
inherit default arc; inherit default arc;
} }

View file

@ -19,23 +19,33 @@
}; };
nodeSystems = let nodeSystems = let
matchesNode = nodeName: system: system.config.proxmox.enabled && system.config.proxmox.node.name == nodeName; matchesNode = nodeName: system: system.config.proxmox.enabled && system.config.proxmox.node.name == nodeName;
in nodeName: filterAttrs (_: matchesNode nodeName) systems; in
nodeName: filterAttrs (_: matchesNode nodeName) systems;
mkNodeSystem = system: { mkNodeSystem = system: {
inherit (system.config.access) hostName; inherit (system.config.access) hostName;
network = let network = let
inherit (system.config.network) networks; inherit (system.config.network) networks;
in { in {
networks = { networks = {
int = if networks.int.enable or false then { int =
inherit (networks.int) macAddress address4 address6; if networks.int.enable or false
} else null; then {
local = if networks.local.enable or false then { inherit (networks.int) macAddress address4 address6;
inherit (networks.local) macAddress address4 address6; }
} else null; else null;
tail = if networks.tail.enable or false then { local =
inherit (networks.tail) address4 address6; if networks.local.enable or false
macAddress = null; then {
} else null; inherit (networks.local) macAddress address4 address6;
}
else null;
tail =
if networks.tail.enable or false
then {
inherit (networks.tail) address4 address6;
macAddress = null;
}
else null;
}; };
}; };
}; };
@ -43,10 +53,12 @@
mkExtern = system: let mkExtern = system: let
enabledFiles = filterAttrs (_: file: file.enable) system.extern.files; enabledFiles = filterAttrs (_: file: file.enable) system.extern.files;
in { in {
files = mapAttrs' (_: file: nameValuePair file.path { files = mapAttrs' (_: file:
source = assert file.relativeSource != null; file.relativeSource; nameValuePair file.path {
inherit (file) owner group mode; source = assert file.relativeSource != null; file.relativeSource;
}) enabledFiles; inherit (file) owner group mode;
})
enabledFiles;
}; };
mkNode = system: { mkNode = system: {
users = mkNodeUsers templateUsers; users = mkNodeUsers templateUsers;
@ -58,13 +70,17 @@
}; };
mkNetwork = system: { mkNetwork = system: {
inherit (system.config.access) hostName; inherit (system.config.access) hostName;
networks = { networks =
int = null; {
local = null; int = null;
tail = null; local = null;
} // mapAttrs' (_: network: nameValuePair network.name { tail = null;
inherit (network) macAddress address4 address6; }
}) system.config.network.networks; // mapAttrs' (_: network:
nameValuePair network.name {
inherit (network) macAddress address4 address6;
})
system.config.network.networks;
}; };
mkSystem = name: system: { mkSystem = name: system: {
network = mkNetwork system; network = mkNetwork system;
@ -72,6 +88,7 @@
in { in {
nodes = let nodes = let
nodes = filterAttrs (_: node: node.config.proxmox.node.enable) systems; nodes = filterAttrs (_: node: node.config.proxmox.node.enable) systems;
in mapAttrs (_: mkNode) nodes; in
mapAttrs (_: mkNode) nodes;
systems = mapAttrs mkSystem systems; systems = mapAttrs mkSystem systems;
} }

69
lib.nix
View file

@ -23,20 +23,27 @@
parts' = Regex.match ''^([^:]+)://(\[[0-9a-fA-F:]+]|[^/:\[]+)(|:[0-9]+)(|/.*)$'' url; parts' = Regex.match ''^([^:]+)://(\[[0-9a-fA-F:]+]|[^/:\[]+)(|:[0-9]+)(|/.*)$'' url;
parts = parts'.value; parts = parts'.value;
port' = List.index parts 2; port' = List.index parts 2;
in assert Opt.isJust parts'; rec { in
inherit url parts; assert Opt.isJust parts'; rec {
scheme = List.index parts 0; inherit url parts;
host = List.index parts 1; scheme = List.index parts 0;
port = if port' != "" then UInt.Parse (Str.removePrefix ":" port') else null; host = List.index parts 1;
hostport = host + port'; port =
path = List.index parts 3; if port' != ""
}; then UInt.Parse (Str.removePrefix ":" port')
else null;
hostport = host + port';
path = List.index parts 3;
};
userIs = group: user: builtins.elem group (user.extraGroups ++ [user.group]); userIs = group: user: builtins.elem group (user.extraGroups ++ [user.group]);
mkWinPath = Str.replace ["/"] ["\\"]; mkWinPath = Str.replace ["/"] ["\\"];
mkBaseDn = domain: Str.concatMapSep "," (part: "dc=${part}") (Regex.splitOn "\\." domain); mkBaseDn = domain: Str.concatMapSep "," (part: "dc=${part}") (Regex.splitOn "\\." domain);
mkAddress6 = addr: if Str.hasInfix ":" addr && ! Str.hasPrefix "[" addr then "[${addr}]" else addr; mkAddress6 = addr:
if Str.hasInfix ":" addr && ! Str.hasPrefix "[" addr
then "[${addr}]"
else addr;
coalesce = values: Opt.default null (List.find (v: v != null) values); coalesce = values: Opt.default null (List.find (v: v != null) values);
mapListToAttrs = f: l: listToAttrs (map f l); mapListToAttrs = f: l: listToAttrs (map f l);
@ -85,13 +92,43 @@ in {
Std = inputs.std-fl.lib; Std = inputs.std-fl.lib;
lib = { lib = {
domain = "gensokyo.zone"; domain = "gensokyo.zone";
inherit treeToModulesOutput userIs inherit
eui64 parseUrl mkWinPath mkBaseDn mkAddress6 treeToModulesOutput
mapListToAttrs coalesce userIs
mkAlmostOptionDefault mkAlmostDefault mkAlmostForce mapOverride mapOptionDefaults mapAlmostOptionDefaults mapDefaults eui64
overrideOptionDefault overrideAlmostOptionDefault overrideDefault overrideAlmostDefault overrideNone overrideAlmostForce overrideForce overrideVM parseUrl
orderJustBefore orderBefore orderAlmostBefore orderNone orderAfter orderAlmostAfter orderJustAfter mkWinPath
mkJustBefore mkAlmostBefore mkAlmostAfter mkJustAfter; mkBaseDn
mkAddress6
mapListToAttrs
coalesce
mkAlmostOptionDefault
mkAlmostDefault
mkAlmostForce
mapOverride
mapOptionDefaults
mapAlmostOptionDefaults
mapDefaults
overrideOptionDefault
overrideAlmostOptionDefault
overrideDefault
overrideAlmostDefault
overrideNone
overrideAlmostForce
overrideForce
overrideVM
orderJustBefore
orderBefore
orderAlmostBefore
orderNone
orderAfter
orderAlmostAfter
orderJustAfter
mkJustBefore
mkAlmostBefore
mkAlmostAfter
mkJustAfter
;
inherit (inputs.arcexprs.lib) unmerged json; inherit (inputs.arcexprs.lib) unmerged json;
}; };
gensokyo-zone = { gensokyo-zone = {

View file

@ -1,4 +1,8 @@
{inputs, ...}: {lib, osConfig, ...}: let {inputs, ...}: {
lib,
osConfig,
...
}: let
inherit (inputs.self.lib) meta; inherit (inputs.self.lib) meta;
inherit (lib.modules) mkIf; inherit (lib.modules) mkIf;
in { in {

View file

@ -19,18 +19,25 @@ let
system = gensokyo-zone.systems.${config.systemName}.config; system = gensokyo-zone.systems.${config.systemName}.config;
networks = let networks = let
fallbackNetwork = fallbackNetwork =
if system.network.networks.local.enable or false && access.local.enable then "local" if system.network.networks.local.enable or false && access.local.enable
else if system.access.global.enable then null then "local"
else if system.network.networks.int.enable or false then "int" else if system.access.global.enable
else if system.network.networks.local.enable or false then "local" then null
else if system.network.networks.int.enable or false
then "int"
else if system.network.networks.local.enable or false
then "local"
else null; else null;
networks = map (name: coalesce [ name fallbackNetwork ]) config.networks; networks = map (name: coalesce [name fallbackNetwork]) config.networks;
in unique networks; in
unique networks;
in { in {
options = with lib.types; { options = with lib.types; {
enable = mkEnableOption "ssh client configuration" // { enable =
default = true; mkEnableOption "ssh client configuration"
}; // {
default = true;
};
name = mkOption { name = mkOption {
type = str; type = str;
default = name; default = name;
@ -66,38 +73,46 @@ let
enabledNetworks = filterAttrs (_: net: net.enable) system.network.networks; enabledNetworks = filterAttrs (_: net: net.enable) system.network.networks;
networkNames = mapAttrsToList (_: net: net.name) enabledNetworks; networkNames = mapAttrsToList (_: net: net.name) enabledNetworks;
networks = filter (name: name == null || elem name networkNames) cfg.networks; networks = filter (name: name == null || elem name networkNames) cfg.networks;
in mkOptionDefault networks; in
mkOptionDefault networks;
set = { set = {
matchBlocksSettings = let matchBlocksSettings = let
canonNetworkName' = intersectLists networks [ null "int" "local" ]; canonNetworkName' = intersectLists networks [null "int" "local"];
canonNetworkName = if canonNetworkName' != [ ] then head canonNetworkName' else null; canonNetworkName =
in mapListToAttrs (network: let if canonNetworkName' != []
name = config.name + optionalString (network != canonNetworkName) "-${network}"; then head canonNetworkName'
inherit (system.exports.services) sshd; else null;
port = head ( in
optional (network == null && sshd.ports.global.enable or false) sshd.ports.global.port mapListToAttrs (network: let
++ optional (sshd.ports.public.enable or false) sshd.ports.public.port name = config.name + optionalString (network != canonNetworkName) "-${network}";
++ [ sshd.ports.standard.port ] inherit (system.exports.services) sshd;
); port = head (
needsProxy = network == "int" || (network == "local" && !access.local.enable); optional (network == null && sshd.ports.global.enable or false) sshd.ports.global.port
in nameValuePair name { ++ optional (sshd.ports.public.enable or false) sshd.ports.public.port
hostname = mkDefault ( ++ [sshd.ports.standard.port]
if network == null then system.access.fqdn );
else system.network.networks.${network}.fqdn needsProxy = network == "int" || (network == "local" && !access.local.enable);
); in
user = mkIf (config.user != null) (mkDefault config.user); nameValuePair name {
port = mkIf (port != 22) (mkDefault port); hostname = mkDefault (
proxyJump = mkIf needsProxy (lib.warnIf (config.name == cfg.proxyJump) "proxyJump self-reference" (mkAlmostOptionDefault ( if network == null
cfg.proxyJump then system.access.fqdn
))); else system.network.networks.${network}.fqdn
identitiesOnly = mkIf (config.systemName == "u7pro") (mkAlmostOptionDefault true); );
extraOptions = mkMerge [ user = mkIf (config.user != null) (mkDefault config.user);
(unmerged.mergeAttrs config.extraOptions) port = mkIf (port != 22) (mkDefault port);
{ proxyJump = mkIf needsProxy (lib.warnIf (config.name == cfg.proxyJump) "proxyJump self-reference" (mkAlmostOptionDefault (
HostKeyAlias = mkIf (config.hostName != null && network != null) (mkOptionDefault system.access.fqdn); cfg.proxyJump
} )));
]; identitiesOnly = mkIf (config.systemName == "u7pro") (mkAlmostOptionDefault true);
}) networks; extraOptions = mkMerge [
(unmerged.mergeAttrs config.extraOptions)
{
HostKeyAlias = mkIf (config.hostName != null && network != null) (mkOptionDefault system.access.fqdn);
}
];
})
networks;
}; };
}; };
}; };
@ -124,7 +139,7 @@ let
}; };
hosts = mkOption { hosts = mkOption {
type = attrsOf (submoduleWith { type = attrsOf (submoduleWith {
modules = [ sshHostModule ]; modules = [sshHostModule];
specialArgs = { specialArgs = {
inherit gensokyo-zone osConfig homeConfig pkgs; inherit gensokyo-zone osConfig homeConfig pkgs;
}; };
@ -132,7 +147,7 @@ let
}; };
networks = mkOption { networks = mkOption {
type = listOf (nullOr str); type = listOf (nullOr str);
default = [ null ]; default = [null];
}; };
proxyJump = mkOption { proxyJump = mkOption {
type = str; type = str;
@ -150,7 +165,8 @@ let
}; };
config = { config = {
proxyJump = mkOptionDefault ( proxyJump = mkOptionDefault (
if config.hosts.hakurei.enable then config.hosts.hakurei.name if config.hosts.hakurei.enable
then config.hosts.hakurei.name
else gensokyo-zone.systems.hakurei.config.access.fqdn else gensokyo-zone.systems.hakurei.config.access.fqdn
); );
networks = mkOptionDefault [ networks = mkOptionDefault [
@ -159,51 +175,55 @@ let
]; ];
hosts = mapAttrs (name: system: let hosts = mapAttrs (name: system: let
enabled = system.config.access.online.enable && system.config.exports.services.sshd.enable; enabled = system.config.access.online.enable && system.config.exports.services.sshd.enable;
in mkIf enabled { in
systemName = mkOptionDefault name; mkIf enabled {
}) gensokyo-zone.systems; systemName = mkOptionDefault name;
})
gensokyo-zone.systems;
set = { set = {
matchBlocksSettings = let matchBlocksSettings = let
mkMatchBlocksHost = host: mkIf host.enable (unmerged.mergeAttrs host.set.matchBlocksSettings); mkMatchBlocksHost = host: mkIf host.enable (unmerged.mergeAttrs host.set.matchBlocksSettings);
in mkMerge ( in
mapAttrsToList (_: mkMatchBlocksHost) config.hosts mkMerge (
); mapAttrsToList (_: mkMatchBlocksHost) config.hosts
);
}; };
}; };
}; };
in { in
config, {
osConfig, config,
lib, osConfig,
gensokyo-zone, lib,
pkgs, gensokyo-zone,
... pkgs,
}: let ...
inherit (lib.options) mkOption; }: let
inherit (lib.modules) mkIf; inherit (lib.options) mkOption;
inherit (gensokyo-zone.lib) unmerged; inherit (lib.modules) mkIf;
cfg = config.gensokyo-zone.ssh; inherit (gensokyo-zone.lib) unmerged;
in { cfg = config.gensokyo-zone.ssh;
options.gensokyo-zone.ssh = mkOption { in {
type = lib.types.submoduleWith { options.gensokyo-zone.ssh = mkOption {
modules = [sshModule]; type = lib.types.submoduleWith {
specialArgs = { modules = [sshModule];
inherit gensokyo-zone pkgs; specialArgs = {
inherit osConfig; inherit gensokyo-zone pkgs;
homeConfig = config; inherit osConfig;
homeConfig = config;
};
}; };
default = {};
}; };
default = { };
};
config = { config = {
gensokyo-zone.ssh = { gensokyo-zone.ssh = {
};
programs.ssh = mkIf cfg.enable {
matchBlocks = unmerged.mergeAttrs cfg.set.matchBlocksSettings;
};
lib.gensokyo-zone.ssh = {
inherit cfg sshModule sshHostModule;
};
}; };
programs.ssh = mkIf cfg.enable { }
matchBlocks = unmerged.mergeAttrs cfg.set.matchBlocksSettings;
};
lib.gensokyo-zone.ssh = {
inherit cfg sshModule sshHostModule;
};
};
}

View file

@ -1,4 +1,4 @@
{ ... }: { {...}: {
imports = [ imports = [
../../nixos/ipa.nix ../../nixos/ipa.nix
]; ];

View file

@ -1,4 +1,4 @@
{ ... }: { {...}: {
imports = [ imports = [
../../nixos/network/netgroups.nix ../../nixos/network/netgroups.nix
]; ];

View file

@ -1,4 +1,4 @@
{ ... }: { {...}: {
imports = [ imports = [
../../nixos/sssd/sssd.nix ../../nixos/sssd/sssd.nix
../../nixos/sssd/pam.nix ../../nixos/sssd/pam.nix

View file

@ -35,7 +35,7 @@ in {
nixosConfig = config; nixosConfig = config;
}; };
}; };
default = { }; default = {};
}; };
config = { config = {

View file

@ -42,7 +42,7 @@
type = listOf str; type = listOf str;
}; };
fallback = mkOption { fallback = mkOption {
type = nullOr (enum [ "cloudflare" "google" ]); type = nullOr (enum ["cloudflare" "google"]);
default = "cloudflare"; default = "cloudflare";
}; };
fallbackNameservers = mkOption { fallbackNameservers = mkOption {
@ -67,32 +67,38 @@
]; ];
nameservers = let nameservers = let
inherit (gensokyo-zone.systems) utsuho hakurei; inherit (gensokyo-zone.systems) utsuho hakurei;
in mkMerge [ in
(mkOptionDefault [ ]) mkMerge [
(mkIf access.local.enable [ (mkOptionDefault [])
(mkIf enableIPv6 utsuho.config.access.address6ForNetwork.local) (mkIf access.local.enable [
utsuho.config.access.address4ForNetwork.local (mkIf enableIPv6 utsuho.config.access.address6ForNetwork.local)
]) utsuho.config.access.address4ForNetwork.local
# TODO: mirror or tunnel on hakurei or something .-. ])
(mkIf (access.tail.enabled && false) [ # TODO: mirror or tunnel on hakurei or something .-.
(mkIf enableIPv6 hakurei.config.access.address6ForNetwork.tail) (mkIf (access.tail.enabled && false) [
hakurei.config.access.address4ForNetwork.tail (mkIf enableIPv6 hakurei.config.access.address6ForNetwork.tail)
]) hakurei.config.access.address4ForNetwork.tail
]; ])
fallbackNameservers = mkOptionDefault {
cloudflare = [
"1.1.1.1#cloudflare-dns.com"
"1.0.0.1#cloudflare-dns.com"
]; ];
google = optionals enableIPv6 [ fallbackNameservers =
"[2001:4860:4860::8888]#dns.google" mkOptionDefault
"[2001:4860:4860::8844]#dns.google" {
] ++ [ cloudflare = [
"8.8.8.8#dns.google" "1.1.1.1#cloudflare-dns.com"
"8.8.4.4#dns.google" "1.0.0.1#cloudflare-dns.com"
]; ];
${toString null} = [ ]; google =
}.${toString config.fallback}; optionals enableIPv6 [
"[2001:4860:4860::8888]#dns.google"
"[2001:4860:4860::8844]#dns.google"
]
++ [
"8.8.8.8#dns.google"
"8.8.4.4#dns.google"
];
${toString null} = [];
}
.${toString config.fallback};
set = { set = {
nssSettings = { nssSettings = {
hosts = mkMerge [ hosts = mkMerge [
@ -123,11 +129,11 @@ in {
nixosConfig = config; nixosConfig = config;
}; };
}; };
default = { }; default = {};
}; };
config = { config = {
networking.nameservers = mkIf (cfg.enable && cfg.nameservers != [ ]) (mkMerge [ networking.nameservers = mkIf (cfg.enable && cfg.nameservers != []) (mkMerge [
(mkBefore cfg.nameservers) (mkBefore cfg.nameservers)
cfg.fallbackNameservers cfg.fallbackNameservers
]); ]);

View file

@ -40,9 +40,11 @@
default = toUpper config.domain; default = toUpper config.domain;
}; };
ca = { ca = {
trust = mkEnableOption "trust CA" // { trust =
default = true; mkEnableOption "trust CA"
}; // {
default = true;
};
pem = mkOption { pem = mkOption {
type = path; type = path;
}; };
@ -59,7 +61,7 @@
}; };
urls = mkOption { urls = mkOption {
type = listOf str; type = listOf str;
default = [ "ldaps://${config.ldap.host}" ]; default = ["ldaps://${config.ldap.host}"];
}; };
baseDn = mkOption { baseDn = mkOption {
type = str; type = str;
@ -75,21 +77,21 @@
}; };
passwordFileKrb5 = mkOption { passwordFileKrb5 = mkOption {
type = path; type = path;
example = lib.literalExpression "\${pkgs.writeText "ldap.kdb5" '' example = lib.literalExpression "\${pkgs.writeText " ldap.kdb5 " ''
${config.bind.dn}#{HEX}616e6f6e796d6f7573 ${config.bind.dn}#{HEX}616e6f6e796d6f7573
''}"; ''}";
}; };
passwordFileSssdEnv = mkOption { passwordFileSssdEnv = mkOption {
type = path; type = path;
example = lib.literalExpression "\${pkgs.writeText "ldap.kdb5" '' example = lib.literalExpression "\${pkgs.writeText " ldap.kdb5 " ''
${"SSSD_AUTHTOK_" + replaceStrings [ "." ] [ "_" ] (toUpper config.domain)}=verysecretpassword ${"SSSD_AUTHTOK_" + replaceStrings ["."] ["_"] (toUpper config.domain)}=verysecretpassword
''}"; ''}";
}; };
}; };
}; };
db = { db = {
backend = mkOption { backend = mkOption {
type = enum [ "kldap" "ipa" ]; type = enum ["kldap" "ipa"];
default = "kldap"; default = "kldap";
}; };
}; };
@ -99,7 +101,7 @@
}; };
authToLocalNames = mkOption { authToLocalNames = mkOption {
type = attrsOf str; type = attrsOf str;
default = { }; default = {};
example = { example = {
"arc@${config.realm}" = "arc"; "arc@${config.realm}" = "arc";
}; };
@ -108,26 +110,30 @@
enable = mkEnableOption "sssd"; enable = mkEnableOption "sssd";
pam.enable = mkEnableOption "PAM"; pam.enable = mkEnableOption "PAM";
backend = mkOption { backend = mkOption {
type = enum [ "ipa" "ldap" ]; type = enum ["ipa" "ldap"];
default = { default =
ipa = "ipa"; {
kldap = "ldap"; ipa = "ipa";
}.${config.db.backend}; kldap = "ldap";
}
.${config.db.backend};
}; };
}; };
ntp = { ntp = {
enable = mkEnableOption "ntp" // { enable =
default = true; mkEnableOption "ntp"
}; // {
default = true;
};
servers = mkOption { servers = mkOption {
type = listOf str; type = listOf str;
example = [ config.ipa.host ]; example = [config.ipa.host];
default = [ "2.fedora.pool.ntp.org" ]; default = ["2.fedora.pool.ntp.org"];
}; };
}; };
nfs = { nfs = {
enable = mkEnableOption "nfs"; enable = mkEnableOption "nfs";
package = mkPackageOption pkgs "nfs-utils" { }; package = mkPackageOption pkgs "nfs-utils" {};
idmapd = { idmapd = {
localDomain = mkOption { localDomain = mkOption {
type = bool; type = bool;
@ -135,11 +141,11 @@
}; };
localRealms = mkOption { localRealms = mkOption {
type = listOf str; type = listOf str;
default = [ config.realm ]; default = [config.realm];
}; };
methods = mkOption { methods = mkOption {
type = listOf str; type = listOf str;
default = [ "nsswitch" ]; default = ["nsswitch"];
}; };
authToLocalNames = mkOption { authToLocalNames = mkOption {
type = attrsOf str; type = attrsOf str;
@ -185,7 +191,8 @@
url = "https://${config.ipa.httpHost}/ipa/config/ca.crt"; url = "https://${config.ipa.httpHost}/ipa/config/ca.crt";
sha256 = "sha256-PKjnjn1jIq9x4BX8+WGkZfj4HQtmnHqmFSALqggo91o="; sha256 = "sha256-PKjnjn1jIq9x4BX8+WGkZfj4HQtmnHqmFSALqggo91o=";
}; };
in mkOptionDefault caPem; in
mkOptionDefault caPem;
ldap = { ldap = {
urls = mkMerge [ urls = mkMerge [
(mkIf access.local.enable (mkOptionDefault (mkBefore [ (mkIf access.local.enable (mkOptionDefault (mkBefore [
@ -200,22 +207,23 @@
]; ];
bind = let bind = let
inherit (nixosConfig.sops) secrets; inherit (nixosConfig.sops) secrets;
in mkIf (nixosOptions ? sops.secrets && secrets ? gensokyo-zone-krb5-passwords) { in
passwordFileKrb5 = mkOptionDefault nixosConfig.sops.secrets.gensokyo-zone-krb5-passwords.path; mkIf (nixosOptions ? sops.secrets && secrets ? gensokyo-zone-krb5-passwords) {
passwordFile = mkOptionDefault nixosConfig.sops.secrets.gensokyo-zone-krb5-peep-password.path; passwordFileKrb5 = mkOptionDefault nixosConfig.sops.secrets.gensokyo-zone-krb5-passwords.path;
passwordFileSssdEnv = mkOptionDefault nixosConfig.sops.secrets.gensokyo-zone-sssd-passwords.path; passwordFile = mkOptionDefault nixosConfig.sops.secrets.gensokyo-zone-krb5-peep-password.path;
}; passwordFileSssdEnv = mkOptionDefault nixosConfig.sops.secrets.gensokyo-zone-sssd-passwords.path;
};
}; };
db.backend = mkIf enabled.ipa (mkAlmostOptionDefault "ipa"); db.backend = mkIf enabled.ipa (mkAlmostOptionDefault "ipa");
nfs = { nfs = {
package = mkIf (elem "umich_ldap" config.nfs.idmapd.methods) (mkAlmostOptionDefault pkgs.nfs-utils-ldap); package = mkIf (elem "umich_ldap" config.nfs.idmapd.methods) (mkAlmostOptionDefault pkgs.nfs-utils-ldap);
idmapd = { idmapd = {
methods = mkMerge [ methods = mkMerge [
(mkIf (config.nfs.idmapd.authToLocalNames != { }) ( (mkIf (config.nfs.idmapd.authToLocalNames != {}) (
mkOptionDefault (mkBefore [ "static" ]) mkOptionDefault (mkBefore ["static"])
)) ))
(mkIf (!enabled.sssd) ( (mkIf (!enabled.sssd) (
mkOptionDefault [ "umich_ldap" ] mkOptionDefault ["umich_ldap"]
)) ))
]; ];
}; };
@ -243,55 +251,63 @@
}; };
}; };
sssdSettings = let sssdSettings = let
servers = optional access.local.enable "idp.local.${config.domain}" servers =
++ [ "_srv" ]; optional access.local.enable "idp.local.${config.domain}"
++ ["_srv"];
backups = mkMerge [ backups = mkMerge [
(mkIf access.tail.enabled (mkAlmostOptionDefault [ "ipa.tail.${config.domain}" ])) (mkIf access.tail.enabled (mkAlmostOptionDefault ["ipa.tail.${config.domain}"]))
(mkIf access.local.enable (mkAlmostOptionDefault [ "ipa.local.${config.domain}" ])) (mkIf access.local.enable (mkAlmostOptionDefault ["ipa.local.${config.domain}"]))
]; ];
in mkIf config.sssd.enable { in
enable = mkAlmostOptionDefault true; mkIf config.sssd.enable {
gensokyo-zone = { enable = mkAlmostOptionDefault true;
backend = mkAlmostOptionDefault config.sssd.backend; gensokyo-zone = {
krb5.servers = { backend = mkAlmostOptionDefault config.sssd.backend;
servers = servers ++ [ config.host ]; krb5.servers = {
inherit backups; servers = servers ++ [config.host];
inherit backups;
};
ipa.servers = {
servers = servers ++ [config.ipa.host];
inherit backups;
};
ldap = {
bind.passwordFile = mkAlmostOptionDefault config.ldap.bind.passwordFile;
uris.backups = mkIf access.tail.enabled (mkAlmostOptionDefault (mkAfter [
"ldaps://ldap.tail.${config.domain}"
]));
};
}; };
ipa.servers = { environmentFile = mkIf (config.sssd.backend == "ldap") (
servers = servers ++ [ config.ipa.host ]; mkAlmostOptionDefault
inherit backups; config.ldap.bind.passwordFileSssdEnv
}; );
ldap = { services = {
bind.passwordFile = mkAlmostOptionDefault config.ldap.bind.passwordFile; ifp.enable = mkAlmostOptionDefault true;
uris.backups = mkIf access.tail.enabled (mkAlmostOptionDefault (mkAfter [ pam.enable = mkIf (!config.sssd.pam.enable) (mkDefault false);
"ldaps://ldap.tail.${config.domain}"
]));
}; };
}; };
environmentFile = mkIf (config.sssd.backend == "ldap") (mkAlmostOptionDefault
config.ldap.bind.passwordFileSssdEnv
);
services = {
ifp.enable = mkAlmostOptionDefault true;
pam.enable = mkIf (!config.sssd.pam.enable) (mkDefault false);
};
};
ipaSettings = mkIf config.ipa.enable (mapAlmostOptionDefaults { ipaSettings = mkIf config.ipa.enable (mapAlmostOptionDefaults {
enable = true; enable = true;
certificate = config.ca.pem; certificate = config.ca.pem;
basedn = config.ldap.baseDn; basedn = config.ldap.baseDn;
domain = config.domain; domain = config.domain;
realm = config.realm; realm = config.realm;
server = config.ipa.server; server = config.ipa.server;
# TODO: dyndns? # TODO: dyndns?
} // { }
overrideConfigs = mapAlmostOptionDefaults { // {
sssd = false; overrideConfigs = mapAlmostOptionDefaults {
krb5 = false; sssd = false;
}; krb5 = false;
}); };
});
nfsSettings = mkIf config.nfs.enable { nfsSettings = mkIf config.nfs.enable {
${if nixosOptions ? services.nfs.settings then "settings" else null} = mkMerge [ ${
if nixosOptions ? services.nfs.settings
then "settings"
else null
} = mkMerge [
{ {
gssd = mapOptionDefaults { gssd = mapOptionDefaults {
#use-machine-creds = false; #use-machine-creds = false;
@ -314,7 +330,11 @@
}; };
}) })
]; ];
${if nixosOptions ? services.nfs.settings then null else "extraConfig"} = mkMerge [ ${
if nixosOptions ? services.nfs.settings
then null
else "extraConfig"
} = mkMerge [
'' ''
[gssd] [gssd]
#use-machine-creds = false #use-machine-creds = false
@ -344,10 +364,10 @@
Domain = mkForce config.domain; Domain = mkForce config.domain;
Local-Realms = concatStringsSep "," config.nfs.idmapd.localRealms; Local-Realms = concatStringsSep "," config.nfs.idmapd.localRealms;
}; };
Translation.Method = mkIf (config.nfs.idmapd.methods != [ "nsswitch" ]) (mkForce ( Translation.Method = mkIf (config.nfs.idmapd.methods != ["nsswitch"]) (mkForce (
concatStringsSep "," config.nfs.idmapd.methods concatStringsSep "," config.nfs.idmapd.methods
)); ));
Static = mkIf (config.nfs.idmapd.authToLocalNames != { }) config.nfs.idmapd.authToLocalNames; Static = mkIf (config.nfs.idmapd.authToLocalNames != {}) config.nfs.idmapd.authToLocalNames;
UMICH_SCHEMA = mkIf (elem "umich_ldap" config.nfs.idmapd.methods) (mapOptionDefaults { UMICH_SCHEMA = mkIf (elem "umich_ldap" config.nfs.idmapd.methods) (mapOptionDefaults {
LDAP_server = config.ldap.host; LDAP_server = config.ldap.host;
LDAP_use_ssl = true; LDAP_use_ssl = true;
@ -389,7 +409,7 @@ in {
nixosOptions = options; nixosOptions = options;
}; };
}; };
default = { }; default = {};
}; };
config = { config = {
@ -413,10 +433,11 @@ in {
hosts = let hosts = let
inherit (gensokyo-zone.systems) freeipa; inherit (gensokyo-zone.systems) freeipa;
# TODO: consider hakurei instead... # TODO: consider hakurei instead...
in mkIf (cfg.enable && !config.gensokyo-zone.dns.enable or false && config.gensokyo-zone.access.local.enable) { in
${freeipa.config.access.address6ForNetwork.local} = mkIf config.networking.enableIPv6 (mkBefore [ cfg.host ]); mkIf (cfg.enable && !config.gensokyo-zone.dns.enable or false && config.gensokyo-zone.access.local.enable) {
${freeipa.config.access.address4ForNetwork.local} = mkBefore [ cfg.host ]; ${freeipa.config.access.address6ForNetwork.local} = mkIf config.networking.enableIPv6 (mkBefore [cfg.host]);
}; ${freeipa.config.access.address4ForNetwork.local} = mkBefore [cfg.host];
};
}; };
environment.etc = { environment.etc = {
"request-key.conf" = mkIf (cfg.enable && cfg.nfs.enable && cfg.sssd.enable) { "request-key.conf" = mkIf (cfg.enable && cfg.nfs.enable && cfg.sssd.enable) {
@ -425,24 +446,30 @@ in {
export LD_LIBRARY_PATH="$LD_LIBRARY_PATH:${config.system.nssModules.path}" export LD_LIBRARY_PATH="$LD_LIBRARY_PATH:${config.system.nssModules.path}"
exec ${cfg.nfs.package}/bin/nfsidmap "$@" exec ${cfg.nfs.package}/bin/nfsidmap "$@"
''; '';
in mkForce (pkgs.writeText "request-key.conf" '' in
create id_resolver * * ${nfsidmap} -t 600 %k %d mkForce (pkgs.writeText "request-key.conf" ''
''); create id_resolver * * ${nfsidmap} -t 600 %k %d
'');
}; };
}; };
${if options ? sops.secrets then "sops" else null}.secrets = let ${
if options ? sops.secrets
then "sops"
else null
}.secrets = let
sopsFile = mkDefault ../secrets/krb5.yaml; sopsFile = mkDefault ../secrets/krb5.yaml;
in mkIf cfg.enable { in
gensokyo-zone-krb5-passwords = mkIf (cfg.db.backend == "kldap") { mkIf cfg.enable {
inherit sopsFile; gensokyo-zone-krb5-passwords = mkIf (cfg.db.backend == "kldap") {
inherit sopsFile;
};
gensokyo-zone-krb5-peep-password = mkIf (cfg.sssd.backend == "ldap") {
inherit sopsFile;
};
gensokyo-zone-sssd-passwords = mkIf (cfg.sssd.backend == "ldap") {
inherit sopsFile;
};
}; };
gensokyo-zone-krb5-peep-password = mkIf (cfg.sssd.backend == "ldap") {
inherit sopsFile;
};
gensokyo-zone-sssd-passwords = mkIf (cfg.sssd.backend == "ldap") {
inherit sopsFile;
};
};
lib.gensokyo-zone.krb5 = { lib.gensokyo-zone.krb5 = {
inherit cfg krb5Module; inherit cfg krb5Module;
}; };

View file

@ -33,20 +33,28 @@
options = with lib.types; { options = with lib.types; {
enable = mkEnableOption "kyuuto"; enable = mkEnableOption "kyuuto";
media = { media = {
enable = mkEnableOption "/mnt/kyuuto-media" // { enable =
default = true; mkEnableOption "/mnt/kyuuto-media"
}; // {
krb5.enable = mkEnableOption "krb5" // { default = true;
default = enabled.krb5; };
}; krb5.enable =
mkEnableOption "krb5"
// {
default = enabled.krb5;
};
}; };
transfer = { transfer = {
enable = mkEnableOption "/mnt/kyuuto-transfer" // { enable =
default = true; mkEnableOption "/mnt/kyuuto-transfer"
}; // {
krb5.enable = mkEnableOption "krb5" // { default = true;
default = enabled.krb5; };
}; krb5.enable =
mkEnableOption "krb5"
// {
default = enabled.krb5;
};
}; };
shared.enable = mkEnableOption "/mnt/kyuuto-shared"; shared.enable = mkEnableOption "/mnt/kyuuto-shared";
domain = mkOption { domain = mkOption {
@ -135,12 +143,13 @@
(mkIf config.nfs.enable "nfs4") (mkIf config.nfs.enable "nfs4")
(mkIf config.smb.enable "smb3") (mkIf config.smb.enable "smb3")
]; ];
options = mkMerge (setFilesystemOptions ++ [ options = mkMerge (setFilesystemOptions
(mkIf config.media.krb5.enable [ ++ [
"sec=krb5" (mkIf config.media.krb5.enable [
(mkIf config.nfs.enable "nfsvers=4") "sec=krb5"
]) (mkIf config.nfs.enable "nfsvers=4")
]); ])
]);
}; };
"/mnt/kyuuto-transfer" = mkIf config.transfer.enable { "/mnt/kyuuto-transfer" = mkIf config.transfer.enable {
device = mkMerge [ device = mkMerge [
@ -151,12 +160,17 @@
(mkIf config.nfs.enable "nfs4") (mkIf config.nfs.enable "nfs4")
(mkIf config.smb.enable "smb3") (mkIf config.smb.enable "smb3")
]; ];
options = mkMerge (setFilesystemOptions ++ [ options = mkMerge (setFilesystemOptions
(mkIf config.media.krb5.enable [ ++ [
(if access.local.enable || access.tail.enabled then "sec=sys:krb5" else "sec=krb5") (mkIf config.media.krb5.enable [
#(mkIf config.nfs.enable "nfsvers=3") (
]) if access.local.enable || access.tail.enabled
]); then "sec=sys:krb5"
else "sec=krb5"
)
#(mkIf config.nfs.enable "nfsvers=3")
])
]);
}; };
"/mnt/kyuuto-shared" = mkIf (config.shared.enable && config.smb.enable) { "/mnt/kyuuto-shared" = mkIf (config.shared.enable && config.smb.enable) {
device = mkIf (config.smb.user != null) ''\\smb.${config.domain}\shared''; device = mkIf (config.smb.user != null) ''\\smb.${config.domain}\shared'';
@ -204,7 +218,7 @@ in {
nixosConfig = config; nixosConfig = config;
}; };
}; };
default = { }; default = {};
}; };
config = { config = {

View file

@ -46,9 +46,11 @@
default = "ssh"; default = "ssh";
}; };
ssh = { ssh = {
commonKey = mkEnableOption "shared secret nixbld key" // { commonKey =
default = true; mkEnableOption "shared secret nixbld key"
}; // {
default = true;
};
user = mkOption { user = mkOption {
type = str; type = str;
default = "nixbld"; default = "nixbld";
@ -105,8 +107,8 @@
]; ];
builder = { builder = {
systems = mkMerge [ systems = mkMerge [
(mkIf config.builder.cross.aarch64 (mkOptionDefault [ "aarch64-linux" ])) (mkIf config.builder.cross.aarch64 (mkOptionDefault ["aarch64-linux"]))
(mkIf config.builder.cross.armv7l (mkOptionDefault [ "armv7l-linux" ])) (mkIf config.builder.cross.armv7l (mkOptionDefault ["armv7l-linux"]))
]; ];
domain = mkMerge [ domain = mkMerge [
(mkIf access.tail.enabled (mkAlmostOptionDefault "nixbld.tail.${domain}")) (mkIf access.tail.enabled (mkAlmostOptionDefault "nixbld.tail.${domain}"))
@ -114,9 +116,11 @@
]; ];
ssh.key = let ssh.key = let
inherit (nixosConfig.sops) secrets; inherit (nixosConfig.sops) secrets;
in mkIf (nixosOptions ? sops.secrets && secrets ? gensokyo-zone-nix-bld-key) (mkAlmostOptionDefault in
nixosConfig.sops.secrets.gensokyo-zone-nix-bld-key.path mkIf (nixosOptions ? sops.secrets && secrets ? gensokyo-zone-nix-bld-key) (
); mkAlmostOptionDefault
nixosConfig.sops.secrets.gensokyo-zone-nix-bld-key.path
);
setBuildMachine = { setBuildMachine = {
hostName = config.builder.domain; hostName = config.builder.domain;
protocol = config.builder.protocol; protocol = config.builder.protocol;
@ -145,7 +149,7 @@ in {
nixosOptions = options; nixosOptions = options;
}; };
}; };
default = { }; default = {};
}; };
config = { config = {
@ -153,13 +157,18 @@ in {
settings = unmerged.merge cfg.setNixSettings; settings = unmerged.merge cfg.setNixSettings;
buildMachines = unmerged.merge cfg.setNixBuildMachines; buildMachines = unmerged.merge cfg.setNixBuildMachines;
}; };
${if options ? sops.secrets then "sops" else null}.secrets = let ${
if options ? sops.secrets
then "sops"
else null
}.secrets = let
sopsFile = mkDefault ../secrets/nix.yaml; sopsFile = mkDefault ../secrets/nix.yaml;
in mkIf cfg.enable { in
gensokyo-zone-nix-bld-key = mkIf cfg.builder.ssh.commonKey { mkIf cfg.enable {
inherit sopsFile; gensokyo-zone-nix-bld-key = mkIf cfg.builder.ssh.commonKey {
inherit sopsFile;
};
}; };
};
lib.gensokyo-zone.nix = { lib.gensokyo-zone.nix = {
inherit cfg nixModule; inherit cfg nixModule;
}; };

View file

@ -162,7 +162,7 @@
modules = [userModule]; modules = [userModule];
inherit specialArgs; inherit specialArgs;
}); });
default = { }; default = {};
}; };
excludeUsers = mkOption { excludeUsers = mkOption {
type = listOf str; type = listOf str;
@ -172,7 +172,7 @@
modules = [groupModule]; modules = [groupModule];
inherit specialArgs; inherit specialArgs;
}); });
default = { }; default = {};
}; };
excludeGroups = mkOption { excludeGroups = mkOption {
type = listOf str; type = listOf str;

View file

@ -76,10 +76,12 @@ in {
]; ];
}; };
allLan = { allLan = {
v4 = cfg.cidrForNetwork.loopback.v4 v4 =
cfg.cidrForNetwork.loopback.v4
++ cfg.cidrForNetwork.local.v4 ++ cfg.cidrForNetwork.local.v4
++ cfg.cidrForNetwork.int.v4; ++ cfg.cidrForNetwork.int.v4;
v6 = cfg.cidrForNetwork.loopback.v6 v6 =
cfg.cidrForNetwork.loopback.v6
++ cfg.cidrForNetwork.local.v6 ++ cfg.cidrForNetwork.local.v6
++ cfg.cidrForNetwork.int.v6; ++ cfg.cidrForNetwork.int.v6;
}; };

View file

@ -55,7 +55,7 @@ in {
nftables.ruleset = mkIf cfg.enable (mkBefore cfg.nftablesInclude); nftables.ruleset = mkIf cfg.enable (mkBefore cfg.nftablesInclude);
firewall = { firewall = {
interfaces.local = { interfaces.local = {
nftables.conditions = mkIf (cfg.enable && networking.enableIPv6) [ "ip6 saddr $localrange6" ]; nftables.conditions = mkIf (cfg.enable && networking.enableIPv6) ["ip6 saddr $localrange6"];
}; };
}; };
}; };

View file

@ -15,33 +15,42 @@
hasSops = options ? sops.secrets; hasSops = options ? sops.secrets;
in { in {
options.networking.access.peeps = with lib.types; { options.networking.access.peeps = with lib.types; {
enable = mkEnableOption "peeps" // { default = hasSops; }; enable = mkEnableOption "peeps" // {default = hasSops;};
ranges = mkOption { ranges = mkOption {
type = attrsOf str; type = attrsOf str;
default = { }; default = {};
}; };
stateDir = mkOption { stateDir = mkOption {
type = path; type = path;
default = "/run/access/peeps"; default = "/run/access/peeps";
}; };
}; };
config.${if hasSops then "sops" else null}.secrets = let config.${
if hasSops
then "sops"
else null
}.secrets = let
sopsFile = mkDefault ../../../nixos/secrets/access.yaml; sopsFile = mkDefault ../../../nixos/secrets/access.yaml;
sopsSecrets = mapAttrs' (name: _: nameValuePair (mkSopsName name) { sopsSecrets = mapAttrs' (name: _:
inherit sopsFile; nameValuePair (mkSopsName name) {
path = mkDefault "${cfg.stateDir}/${name}.nft"; inherit sopsFile;
}) cfg.ranges; path = mkDefault "${cfg.stateDir}/${name}.nft";
in mkIf cfg.enable sopsSecrets; })
cfg.ranges;
in
mkIf cfg.enable sopsSecrets;
config.networking = let config.networking = let
nftRanges = mapAttrsToList (name: range: let nftRanges = mapAttrsToList (name: range: let
nft = "define ${mkNftName name} = ${range}"; nft = "define ${mkNftName name} = ${range}";
in mkBefore nft) cfg.ranges; in
mkBefore nft)
cfg.ranges;
condition = "ip6 saddr { ${concatStringsSep "," (mapAttrsToList (name: _: "$" + mkNftName name) cfg.ranges)} }"; condition = "ip6 saddr { ${concatStringsSep "," (mapAttrsToList (name: _: "$" + mkNftName name) cfg.ranges)} }";
in { in {
nftables.ruleset = mkIf cfg.enable (mkMerge ( nftables.ruleset = mkIf cfg.enable (mkMerge (
nftRanges nftRanges
++ [ (mkBefore ''include "${cfg.stateDir}/*.nft"'') ] ++ [(mkBefore ''include "${cfg.stateDir}/*.nft"'')]
)); ));
firewall.interfaces.peeps = { firewall.interfaces.peeps = {
nftables.enable = cfg.enable; nftables.enable = cfg.enable;

View file

@ -1,4 +1,4 @@
{ gensokyo-zone, ... }: { {gensokyo-zone, ...}: {
config.lib = { config.lib = {
inherit gensokyo-zone; inherit gensokyo-zone;
}; };

View file

@ -1,4 +1,10 @@
{ config, lib, gensokyo-zone, pkgs, ... }: let {
config,
lib,
gensokyo-zone,
pkgs,
...
}: let
inherit (gensokyo-zone.lib) mkAlmostOptionDefault mapOptionDefaults unmerged; inherit (gensokyo-zone.lib) mkAlmostOptionDefault mapOptionDefaults unmerged;
inherit (lib.options) mkOption mkEnableOption mkPackageOption; inherit (lib.options) mkOption mkEnableOption mkPackageOption;
inherit (lib.modules) mkIf mkMerge mkDefault mkOptionDefault; inherit (lib.modules) mkIf mkMerge mkDefault mkOptionDefault;
@ -8,17 +14,20 @@
cfg = config.services.barcodebuddy; cfg = config.services.barcodebuddy;
toEnvName = key: "BBUDDY_" + key; toEnvName = key: "BBUDDY_" + key;
toEnvValue = value: toEnvValue = value:
if value == true then "true" if value == true
else if value == false then "false" then "true"
else if isList value then concatStringsSep ";" (imap0 (i: v: "${toString i}=${toEnvValue v}") value) else if value == false
then "false"
else if isList value
then concatStringsSep ";" (imap0 (i: v: "${toString i}=${toEnvValue v}") value)
else toString value; else toString value;
toEnvPair = key: value: nameValuePair (toEnvName key) (toEnvValue value); toEnvPair = key: value: nameValuePair (toEnvName key) (toEnvValue value);
toPhpEnvPair = key: value: nameValuePair (toEnvName key) ''"${toEnvValue value}"''; toPhpEnvPair = key: value: nameValuePair (toEnvName key) ''"${toEnvValue value}"'';
in { in {
options.services.barcodebuddy = with lib.types; { options.services.barcodebuddy = with lib.types; {
enable = mkEnableOption "Barcode Buddy"; enable = mkEnableOption "Barcode Buddy";
package = mkPackageOption pkgs "barcodebuddy" { }; package = mkPackageOption pkgs "barcodebuddy" {};
phpPackageUnwrapped = mkPackageOption pkgs "php83" { }; phpPackageUnwrapped = mkPackageOption pkgs "php83" {};
hostName = mkOption { hostName = mkOption {
type = str; type = str;
}; };
@ -38,7 +47,7 @@ in {
enable = mkEnableOption "reverse proxy"; enable = mkEnableOption "reverse proxy";
trustedAddresses = mkOption { trustedAddresses = mkOption {
type = listOf str; type = listOf str;
default = [ "127.0.0.1" "::1" ]; default = ["127.0.0.1" "::1"];
}; };
}; };
screen = { screen = {
@ -65,13 +74,15 @@ in {
type = nullOr str; type = nullOr str;
default = null; default = null;
}; };
/* TODO: passwordFile = mkOption { /*
TODO: passwordFile = mkOption {
type = nullOr path; type = nullOr path;
default = null; default = null;
};*/ };
*/
}; };
settings = mkOption { settings = mkOption {
type = attrsOf (oneOf [ str bool int (listOf str) ]); type = attrsOf (oneOf [str bool int (listOf str)]);
description = "https://github.com/Forceu/barcodebuddy/blob/master/config-dist.php"; description = "https://github.com/Forceu/barcodebuddy/blob/master/config-dist.php";
}; };
nginxConfig = mkOption { nginxConfig = mkOption {
@ -95,9 +106,19 @@ in {
bbuddyConfig.services.barcodebuddy = { bbuddyConfig.services.barcodebuddy = {
settings = let settings = let
defaults = mapOptionDefaults { defaults = mapOptionDefaults {
${if cfg.screen.enable then "PORT_WEBSOCKET_SERVER" else null} = cfg.screen.websocketPort; ${
if cfg.screen.enable
then "PORT_WEBSOCKET_SERVER"
else null
} =
cfg.screen.websocketPort;
SEARCH_ENGINE = "https://google.com/search?q="; SEARCH_ENGINE = "https://google.com/search?q=";
${if cfg.reverseProxy.enable then "TRUSTED_PROXIES" else null} = cfg.reverseProxy.trustedAddresses; ${
if cfg.reverseProxy.enable
then "TRUSTED_PROXIES"
else null
} =
cfg.reverseProxy.trustedAddresses;
DISABLE_AUTHENTICATION = false; DISABLE_AUTHENTICATION = false;
DATABASE_PATH = cfg.databasePath; DATABASE_PATH = cfg.databasePath;
AUTHDB_PATH = cfg.authDatabasePath; AUTHDB_PATH = cfg.authDatabasePath;
@ -109,7 +130,8 @@ in {
REDIS_PORT = cfg.redis.port; REDIS_PORT = cfg.redis.port;
REDIS_PW = toString cfg.redis.password; REDIS_PW = toString cfg.redis.password;
}; };
in mkMerge [ defaults (mkIf cfg.redis.enable redis) ]; in
mkMerge [defaults (mkIf cfg.redis.enable redis)];
nginxConfig = '' nginxConfig = ''
index index.php index.html index.htm; index index.php index.html index.htm;
''; '';
@ -125,13 +147,18 @@ in {
}; };
redis = let redis = let
redis = config.services.redis.servers.${cfg.redis.server}; redis = config.services.redis.servers.${cfg.redis.server};
in mkIf (cfg.redis.server != null) { in
enable = mkAlmostOptionDefault redis.enable; mkIf (cfg.redis.server != null) {
ip = mkOptionDefault (if redis.bind == null then "localhost" else redis.bind); enable = mkAlmostOptionDefault redis.enable;
port = mkIf (redis.port != 0) (mkOptionDefault redis.port); ip = mkOptionDefault (
password = mkAlmostOptionDefault redis.requirePass; if redis.bind == null
# TODO: passwordFile = mkAlmostOptionDefault redis.requirePassFile; then "localhost"
}; else redis.bind
);
port = mkIf (redis.port != 0) (mkOptionDefault redis.port);
password = mkAlmostOptionDefault redis.requirePass;
# TODO: passwordFile = mkAlmostOptionDefault redis.requirePassFile;
};
}; };
conf.users.users.barcodebuddy = { conf.users.users.barcodebuddy = {
isSystemUser = true; isSystemUser = true;
@ -146,7 +173,10 @@ in {
user = "barcodebuddy"; user = "barcodebuddy";
inherit (config.services.nginx) group; inherit (config.services.nginx) group;
phpPackage = cfg.phpPackageUnwrapped.withExtensions ({ enabled, all }: [ phpPackage = cfg.phpPackageUnwrapped.withExtensions ({
enabled,
all,
}: [
all.curl all.curl
all.mbstring all.mbstring
all.sqlite3 all.sqlite3
@ -190,7 +220,7 @@ in {
}; };
}; };
conf.systemd.services.bbuddy-websocket = mkIf cfg.screen.enable { conf.systemd.services.bbuddy-websocket = mkIf cfg.screen.enable {
wantedBy = [ "multi-user.target" ]; wantedBy = ["multi-user.target"];
environment = mapAttrs' toEnvPair cfg.settings; environment = mapAttrs' toEnvPair cfg.settings;
unitConfig = { unitConfig = {
Description = "Run websocket server for barcodebuddy screen feature"; Description = "Run websocket server for barcodebuddy screen feature";
@ -202,5 +232,6 @@ in {
User = "barcodebuddy"; User = "barcodebuddy";
}; };
}; };
in mkMerge [ bbuddyConfig (mkIf cfg.enable conf) ]; in
mkMerge [bbuddyConfig (mkIf cfg.enable conf)];
} }

View file

@ -82,7 +82,7 @@ in {
interfaces.local = { interfaces.local = {
allowedTCPPorts = mkMerge [ allowedTCPPorts = mkMerge [
(mkIf (!cfg.homekit.openFirewall) homekitTcp) (mkIf (!cfg.homekit.openFirewall) homekitTcp)
(mkIf (!cfg.openFirewall) [ cfg.config.http.server_port ]) (mkIf (!cfg.openFirewall) [cfg.config.http.server_port])
]; ];
allowedUDPPortRanges = mkIf (!cfg.cast.openFirewall) castUdpRanges; allowedUDPPortRanges = mkIf (!cfg.cast.openFirewall) castUdpRanges;
}; };

View file

@ -14,7 +14,7 @@
cfg = config.security.ipa; cfg = config.security.ipa;
in { in {
options.security.ipa = with lib.types; { options.security.ipa = with lib.types; {
package = mkPackageOption pkgs "freeipa" { }; package = mkPackageOption pkgs "freeipa" {};
overrideConfigs = { overrideConfigs = {
krb5 = mkOption { krb5 = mkOption {
type = bool; type = bool;
@ -36,67 +36,70 @@ in {
config.services.sssd = let config.services.sssd = let
inherit (config.services) sssd; inherit (config.services) sssd;
ipaDebugLevel = 65510; ipaDebugLevel = 65510;
in mkIf cfg.enable { in
debugLevel = mkAlmostOptionDefault ipaDebugLevel; mkIf cfg.enable {
domains = { debugLevel = mkAlmostOptionDefault ipaDebugLevel;
${cfg.domain} = { domains = {
ldap.extraAttrs.user = { ${cfg.domain} = {
mail = "mail"; ldap.extraAttrs.user = {
sn = "sn"; mail = "mail";
givenname = "givenname"; sn = "sn";
telephoneNumber = "telephoneNumber"; givenname = "givenname";
lock = "nsaccountlock"; telephoneNumber = "telephoneNumber";
}; lock = "nsaccountlock";
settings = mapOptionDefaults { };
id_provider = "ipa"; settings =
auth_provider = "ipa"; mapOptionDefaults {
access_provider = "ipa"; id_provider = "ipa";
chpass_provider = "ipa"; auth_provider = "ipa";
ipa_domain = cfg.domain; access_provider = "ipa";
chpass_provider = "ipa";
ipa_domain = cfg.domain;
ipa_server = [ "_srv_" cfg.server ]; ipa_server = ["_srv_" cfg.server];
ipa_hostname = "${config.networking.hostName}.${cfg.domain}"; ipa_hostname = "${config.networking.hostName}.${cfg.domain}";
cache_credentials = cfg.cacheCredentials; cache_credentials = cfg.cacheCredentials;
krb5_store_password_if_offline = cfg.offlinePasswords; krb5_store_password_if_offline = cfg.offlinePasswords;
dyndns_update = cfg.dyndns.enable; dyndns_update = cfg.dyndns.enable;
dyndns_iface = cfg.dyndns.interface; dyndns_iface = cfg.dyndns.interface;
ldap_tls_cacert = "/etc/ipa/ca.crt"; ldap_tls_cacert = "/etc/ipa/ca.crt";
} // { }
krb5_realm = mkIf (toLower cfg.domain != toLower cfg.realm) (mkOptionDefault cfg.realm); // {
krb5_realm = mkIf (toLower cfg.domain != toLower cfg.realm) (mkOptionDefault cfg.realm);
};
}; };
}; };
services = {
nss.settings = mapOptionDefaults {
homedir_substring = "/home";
};
pam.settings = mapOptionDefaults {
pam_pwd_expiration_warning = 3;
pam_verbosity = 3;
};
sudo = {
enable = mkAlmostOptionDefault true;
settings = mapOptionDefaults {
debug_level = ipaDebugLevel;
};
};
ssh.enable = mkAlmostOptionDefault true;
ifp = {
enable = mkAlmostOptionDefault true;
settings = mapOptionDefaults {
allowed_uids = cfg.ifpAllowedUids;
};
};
};
configText = mkIf (cfg.overrideConfigs.sssd) (mkAlmostOptionDefault null);
config = mkIf (sssd.configText != null) (mkAlmostForce sssd.configText);
}; };
services = {
nss.settings = mapOptionDefaults {
homedir_substring = "/home";
};
pam.settings = mapOptionDefaults {
pam_pwd_expiration_warning = 3;
pam_verbosity = 3;
};
sudo = {
enable = mkAlmostOptionDefault true;
settings = mapOptionDefaults {
debug_level = ipaDebugLevel;
};
};
ssh.enable = mkAlmostOptionDefault true;
ifp = {
enable = mkAlmostOptionDefault true;
settings = mapOptionDefaults {
allowed_uids = cfg.ifpAllowedUids;
};
};
};
configText = mkIf (cfg.overrideConfigs.sssd) (mkAlmostOptionDefault null);
config = mkIf (sssd.configText != null) (mkAlmostForce sssd.configText);
};
config.security.krb5 = mkIf cfg.enable { config.security.krb5 = mkIf cfg.enable {
enable = mkAlmostForce false; enable = mkAlmostForce false;
package = mkAlmostOptionDefault pkgs.krb5Full; package = mkAlmostOptionDefault pkgs.krb5Full;
@ -136,8 +139,9 @@ in {
}; };
config.environment.etc."krb5.conf" = let config.environment.etc."krb5.conf" = let
inherit (config.security) krb5; inherit (config.security) krb5;
format = import (modulesPath + "/security/krb5/krb5-conf-format.nix") { inherit pkgs lib; } { }; format = import (modulesPath + "/security/krb5/krb5-conf-format.nix") {inherit pkgs lib;} {};
in mkIf (cfg.enable && !cfg.overrideConfigs.krb5) { in
text = mkForce (format.generate "krb5.conf" krb5.settings).text; mkIf (cfg.enable && !cfg.overrideConfigs.krb5) {
}; text = mkForce (format.generate "krb5.conf" krb5.settings).text;
};
} }

View file

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

View file

@ -1,4 +1,10 @@
{ gensokyo-zone, pkgs, config, lib, ... }: let {
gensokyo-zone,
pkgs,
config,
lib,
...
}: let
inherit (gensokyo-zone.lib) mkBaseDn mapDefaults mkAlmostOptionDefault mapOptionDefaults domain; inherit (gensokyo-zone.lib) mkBaseDn mapDefaults mkAlmostOptionDefault mapOptionDefaults domain;
inherit (lib.options) mkOption mkEnableOption; inherit (lib.options) mkOption mkEnableOption;
inherit (lib.modules) mkIf mkDefault mkOptionDefault mkForce; inherit (lib.modules) mkIf mkDefault mkOptionDefault mkForce;
@ -50,12 +56,12 @@ in {
}; };
}; };
db.backend = mkOption { db.backend = mkOption {
type = enum [ "kldap" "ipa" ]; type = enum ["kldap" "ipa"];
default = "kldap"; default = "kldap";
}; };
authToLocalNames = mkOption { authToLocalNames = mkOption {
type = attrsOf str; type = attrsOf str;
default = { }; default = {};
}; };
}; };
config = { config = {
@ -64,32 +70,36 @@ in {
krb5-ldap = pkgs.krb5.override { krb5-ldap = pkgs.krb5.override {
withLdap = true; withLdap = true;
}; };
in mkIf (cfg.enable && cfg.db.backend == "kldap") (mkDefault pkgs.krb5-ldap or krb5-ldap); in
mkIf (cfg.enable && cfg.db.backend == "kldap") (mkDefault pkgs.krb5-ldap or krb5-ldap);
settings = mkIf cfg.enable { settings = mkIf cfg.enable {
dbmodules = { dbmodules = {
genso-kldap = mkIf (cfg.db.backend == "kldap") (mapDefaults { genso-kldap = mkIf (cfg.db.backend == "kldap") (mapDefaults {
db_library = "kldap"; db_library = "kldap";
ldap_servers = concatStringsSep " " cfg.ldap.urls; ldap_servers = concatStringsSep " " cfg.ldap.urls;
ldap_kdc_dn = cfg.ldap.bind.dn; ldap_kdc_dn = cfg.ldap.bind.dn;
ldap_kerberos_container_dn = cfg.ldap.baseDn; ldap_kerberos_container_dn = cfg.ldap.baseDn;
} // { }
ldap_service_password_file = mkIf (cfg.ldap.bind.passwordFile != null) (mkDefault cfg.ldap.bind.passwordFile); // {
}); ldap_service_password_file = mkIf (cfg.ldap.bind.passwordFile != null) (mkDefault cfg.ldap.bind.passwordFile);
});
genso-ipa = mkIf (cfg.db.backend == "ipa") (mapDefaults { genso-ipa = mkIf (cfg.db.backend == "ipa") (mapDefaults {
db_library = "${ipa.package}/lib/krb5/plugins/kdb/ipadb.so"; db_library = "${ipa.package}/lib/krb5/plugins/kdb/ipadb.so";
}); });
${cfg.realm} = mkIf ipa.enable (mkForce { }); ${cfg.realm} = mkIf ipa.enable (mkForce {});
};
realms.${cfg.realm} = mapDefaults {
kdc = "${cfg.host}:88";
master_kdc = "${cfg.host}:88";
admin_server = "${cfg.host}:749";
default_domain = cfg.domain;
pkinit_anchors = [ "FILE:${cfg.ca.cert}" ];
} // {
database_module = mkOptionDefault "genso-${cfg.db.backend}";
auth_to_local_names = mkIf (cfg.authToLocalNames != { }) (mkDefault (subsection cfg.authToLocalNames));
}; };
realms.${cfg.realm} =
mapDefaults {
kdc = "${cfg.host}:88";
master_kdc = "${cfg.host}:88";
admin_server = "${cfg.host}:749";
default_domain = cfg.domain;
pkinit_anchors = ["FILE:${cfg.ca.cert}"];
}
// {
database_module = mkOptionDefault "genso-${cfg.db.backend}";
auth_to_local_names = mkIf (cfg.authToLocalNames != {}) (mkDefault (subsection cfg.authToLocalNames));
};
domain_realm = mapOptionDefaults { domain_realm = mapOptionDefaults {
${cfg.domain} = cfg.realm; ${cfg.domain} = cfg.realm;
".${cfg.domain}" = cfg.realm; ".${cfg.domain}" = cfg.realm;
@ -112,7 +122,8 @@ in {
url = "https://ipa.${cfg.domain}/ipa/config/ca.crt"; url = "https://ipa.${cfg.domain}/ipa/config/ca.crt";
sha256 = "sha256-PKjnjn1jIq9x4BX8+WGkZfj4HQtmnHqmFSALqggo91o="; sha256 = "sha256-PKjnjn1jIq9x4BX8+WGkZfj4HQtmnHqmFSALqggo91o=";
}; };
in mkOptionDefault caPem; in
mkOptionDefault caPem;
db.backend = mkIf ipa.enable (mkAlmostOptionDefault "ipa"); db.backend = mkIf ipa.enable (mkAlmostOptionDefault "ipa");
ldap.urls = mkOptionDefault [ ldap.urls = mkOptionDefault [
"ldaps://ldap.${cfg.domain}" "ldaps://ldap.${cfg.domain}"
@ -120,16 +131,18 @@ in {
]; ];
}; };
}; };
networking.timeServers = mkIf (cfg.enable && enabled) [ "2.fedora.pool.ntp.org" ]; networking.timeServers = mkIf (cfg.enable && enabled) ["2.fedora.pool.ntp.org"];
security.ipa = mkIf cfg.enable { security.ipa = mkIf cfg.enable {
certificate = mkDefault cfg.ca.cert; certificate = mkDefault cfg.ca.cert;
basedn = mkDefault cfg.ldap.baseDn; basedn = mkDefault cfg.ldap.baseDn;
domain = mkDefault cfg.domain; domain = mkDefault cfg.domain;
realm = mkDefault cfg.realm; realm = mkDefault cfg.realm;
server = mkDefault cfg.canonHost; server = mkDefault cfg.canonHost;
ifpAllowedUids = [ ifpAllowedUids =
"root" [
] ++ config.users.groups.wheel.members; "root"
]
++ config.users.groups.wheel.members;
dyndns.enable = mkDefault false; dyndns.enable = mkDefault false;
}; };
}; };

View file

@ -1,5 +1,11 @@
let let
allowListModule = {config, name, gensokyo-zone, lib, ...}: let allowListModule = {
config,
name,
gensokyo-zone,
lib,
...
}: let
inherit (gensokyo-zone.Std) UInt; inherit (gensokyo-zone.Std) UInt;
inherit (lib.options) mkOption; inherit (lib.options) mkOption;
inherit (lib.modules) mkOptionDefault; inherit (lib.modules) mkOptionDefault;
@ -11,10 +17,10 @@ let
default = name; default = name;
}; };
xuid = mkOption { xuid = mkOption {
type = oneOf [ int str ]; type = oneOf [int str];
}; };
permission = mkOption { permission = mkOption {
type = enum [ "visitor" "member" "operator" ]; type = enum ["visitor" "member" "operator"];
default = "member"; default = "member";
}; };
settings = mkOption { settings = mkOption {
@ -25,10 +31,12 @@ let
}; };
}; };
config = let config = let
xuid = { xuid =
string = toString (UInt.FromHex config.xuid); {
int = toString config.xuid; string = toString (UInt.FromHex config.xuid);
}.${typeOf config.xuid}; int = toString config.xuid;
}
.${typeOf config.xuid};
in { in {
settings = { settings = {
name = mkOptionDefault config.name; name = mkOptionDefault config.name;
@ -41,16 +49,22 @@ let
}; };
}; };
}; };
packModule = {config, lib, ...}: let packModule = {
config,
lib,
...
}: let
inherit (lib.options) mkOption mkEnableOption; inherit (lib.options) mkOption mkEnableOption;
inherit (lib.modules) mkIf mkOptionDefault; inherit (lib.modules) mkIf mkOptionDefault;
inherit (lib.strings) splitString; inherit (lib.strings) splitString;
inherit (builtins) typeOf; inherit (builtins) typeOf;
in { in {
options = with lib.types; { options = with lib.types; {
enable = mkEnableOption "pack" // { enable =
default = true; mkEnableOption "pack"
}; // {
default = true;
};
package = mkOption { package = mkOption {
type = nullOr package; type = nullOr package;
default = null; default = null;
@ -59,90 +73,194 @@ let
type = str; type = str;
}; };
packType = mkOption { packType = mkOption {
type = enum [ "resource_packs" "behavior_packs" ]; type = enum ["resource_packs" "behavior_packs"];
}; };
packId = mkOption { packId = mkOption {
type = str; type = str;
}; };
version = mkOption { version = mkOption {
type = oneOf [ str (listOf str) ]; type = oneOf [str (listOf str)];
}; };
settings = mkOption { settings = mkOption {
type = attrsOf (oneOf [ str (listOf str) ]); type = attrsOf (oneOf [str (listOf str)]);
}; };
}; };
config = { config = {
packId = mkIf (config.package != null && config.package ? minecraft-bedrock.pack.pack_id) (mkOptionDefault packId = mkIf (config.package != null && config.package ? minecraft-bedrock.pack.pack_id) (
mkOptionDefault
config.package.minecraft-bedrock.pack.pack_id config.package.minecraft-bedrock.pack.pack_id
); );
packType = mkIf (config.package != null && config.package ? minecraft-bedrock.pack.type) (mkOptionDefault packType = mkIf (config.package != null && config.package ? minecraft-bedrock.pack.type) (
mkOptionDefault
config.package.minecraft-bedrock.pack.type config.package.minecraft-bedrock.pack.type
); );
version = mkIf (config.package != null && config.package ? minecraft-bedrock.pack.version) (mkOptionDefault version = mkIf (config.package != null && config.package ? minecraft-bedrock.pack.version) (
mkOptionDefault
config.package.minecraft-bedrock.pack.version config.package.minecraft-bedrock.pack.version
); );
packDir = mkIf (config.package != null && config.package ? minecraft-bedrock.pack.dir) (mkOptionDefault packDir = mkIf (config.package != null && config.package ? minecraft-bedrock.pack.dir) (
mkOptionDefault
config.package.minecraft-bedrock.pack.dir config.package.minecraft-bedrock.pack.dir
); );
settings = { settings = {
pack_id = mkOptionDefault config.packId; pack_id = mkOptionDefault config.packId;
version = mkOptionDefault { version =
string = splitString "." config.version; mkOptionDefault
list = config.version; {
}.${typeOf config.version}; string = splitString "." config.version;
list = config.version;
}
.${typeOf config.version};
}; };
}; };
}; };
in { config, gensokyo-zone, lib, pkgs, ... }: let in
# see https://gist.github.com/datakurre/cfdf627fb23ed8ff62bb7b3520b92674 {
inherit (gensokyo-zone.lib) mapOptionDefaults; config,
inherit (lib.options) mkOption mkPackageOption; gensokyo-zone,
inherit (lib.modules) mkIf mkMerge mkOptionDefault; lib,
inherit (lib.attrsets) filterAttrs mapAttrsToList; pkgs,
inherit (lib.lists) optional; ...
inherit (lib.strings) concatStringsSep; }: let
inherit (lib.trivial) boolToString; # see https://gist.github.com/datakurre/cfdf627fb23ed8ff62bb7b3520b92674
inherit (lib.meta) getExe; inherit (gensokyo-zone.lib) mapOptionDefaults;
inherit (builtins) toJSON; inherit (lib.options) mkOption mkPackageOption;
cfg = config.services.minecraft-bedrock-server; inherit (lib.modules) mkIf mkMerge mkOptionDefault;
inherit (lib.attrsets) filterAttrs mapAttrsToList;
inherit (lib.lists) optional;
inherit (lib.strings) concatStringsSep;
inherit (lib.trivial) boolToString;
inherit (lib.meta) getExe;
inherit (builtins) toJSON;
cfg = config.services.minecraft-bedrock-server;
cfgToString = v: if builtins.isBool v then boolToString v else toString v; cfgToString = v:
if builtins.isBool v
then boolToString v
else toString v;
serverPropertiesFile = pkgs.writeText "server.properties" ('' serverPropertiesFile = pkgs.writeText "server.properties" (''
# server.properties managed by NixOS configuration # server.properties managed by NixOS configuration
'' + concatStringsSep "\n" (mapAttrsToList ''
(n: v: "${n}=${cfgToString v}") cfg.serverProperties)); + concatStringsSep "\n" (mapAttrsToList
in { (n: v: "${n}=${cfgToString v}")
options.services.minecraft-bedrock-server = with lib.types; { cfg.serverProperties));
enable = mkOption { in {
type = bool; options.services.minecraft-bedrock-server = with lib.types; {
default = false; enable = mkOption {
description = '' type = bool;
If enabled, start a Minecraft Bedrock Server. The server default = false;
data will be loaded from and saved to description = ''
<option>services.minecraft-bedrock-server.dataDir</option>. If enabled, start a Minecraft Bedrock Server. The server
''; data will be loaded from and saved to
<option>services.minecraft-bedrock-server.dataDir</option>.
'';
};
dataDir = mkOption {
type = path;
default = "/var/lib/minecraft-bedrock";
description = ''
Directory to store Minecraft Bedrock database and other state/data files.
'';
};
serverProperties = mkOption {
type = attrsOf (oneOf [bool int str float]);
example = literalExample ''
{
server-name = "Dedicated Server";
gamemode = "survival";
difficulty = "easy";
allow-cheats = false;
max-players = 10;
online-mode = false;
white-list = false;
server-port = 19132;
server-portv6 = 19133;
view-distance = 32;
tick-distance = 4;
player-idle-timeout = 30;
max-threads = 8;
level-name = "Bedrock level";
level-seed = "";
default-player-permission-level = "member";
texturepack-required = false;
content-log-file-enabled = false;
compression-threshold = 1;
server-authoritative-movement = "server-auth";
player-movement-score-threshold = 20;
player-movement-distance-threshold = 0.3;
player-movement-duration-threshold-in-ms = 500;
correct-player-movement = false;
}
'';
description = ''
Minecraft Bedrock server properties for the server.properties file.
'';
};
package =
mkPackageOption pkgs "minecraft-bedrock-server" {}
// {
description = "Version of minecraft-bedrock-server to run.";
};
openFirewall = mkOption {
type = bool;
default = false;
};
user = mkOption {
type = str;
default = "minecraft-bedrock";
};
group = mkOption {
type = str;
default = cfg.user;
};
allowPlayers = mkOption {
type = nullOr (attrsOf (submoduleWith {
modules = [allowListModule];
specialArgs = {
inherit gensokyo-zone;
nixosConfig = config;
};
}));
default = null;
};
allowList = mkOption {
type = nullOr path;
};
permissions = mkOption {
type = nullOr path;
};
packs = mkOption {
type = attrsOf (submoduleWith {
modules = [packModule];
specialArgs = {
inherit gensokyo-zone;
nixosConfig = config;
};
});
default = {};
};
}; };
dataDir = mkOption { config = let
type = path; confService.services.minecraft-bedrock-server = {
default = "/var/lib/minecraft-bedrock"; serverProperties = mapOptionDefaults {
description = ''
Directory to store Minecraft Bedrock database and other state/data files.
'';
};
serverProperties = mkOption {
type = attrsOf (oneOf [ bool int str float ]);
example = literalExample ''
{
server-name = "Dedicated Server"; server-name = "Dedicated Server";
gamemode = "survival"; gamemode = "survival";
difficulty = "easy"; difficulty = "easy";
allow-cheats = false; allow-cheats = false;
max-players = 10; max-players = 10;
online-mode = false; online-mode = false;
white-list = false; allow-list = cfg.allowList != null;
server-port = 19132; server-port = 19132;
server-portv6 = 19133; server-portv6 = 19133;
view-distance = 32; view-distance = 32;
@ -160,191 +278,118 @@ in {
player-movement-distance-threshold = 0.3; player-movement-distance-threshold = 0.3;
player-movement-duration-threshold-in-ms = 500; player-movement-duration-threshold-in-ms = 500;
correct-player-movement = false; correct-player-movement = false;
}
'';
description = ''
Minecraft Bedrock server properties for the server.properties file.
'';
};
package = mkPackageOption pkgs "minecraft-bedrock-server" { }// {
description = "Version of minecraft-bedrock-server to run.";
};
openFirewall = mkOption {
type = bool;
default = false;
};
user = mkOption {
type = str;
default = "minecraft-bedrock";
};
group = mkOption {
type = str;
default = cfg.user;
};
allowPlayers = mkOption {
type = nullOr (attrsOf (submoduleWith {
modules = [ allowListModule ];
specialArgs = {
inherit gensokyo-zone;
nixosConfig = config;
}; };
})); allowList = let
default = null; allowPlayers = mapAttrsToList (_: allow: allow.settings) cfg.allowPlayers;
}; allowListJson = pkgs.writeText "minecraft-bedrock-server-allowlist.json" (
toJSON allowPlayers
allowList = mkOption { );
type = nullOr path; in
}; mkOptionDefault (
if cfg.allowPlayers != null
permissions = mkOption { then allowListJson
type = nullOr path; else null
}; );
permissions = let
packs = mkOption { permissions = mapAttrsToList (_: allow: allow.permissionSettings) cfg.allowPlayers;
type = attrsOf (submoduleWith { permissionsJson = pkgs.writeText "minecraft-bedrock-server-permissions.json" (
modules = [ packModule ]; toJSON permissions
specialArgs = { );
inherit gensokyo-zone; in
nixosConfig = config; mkOptionDefault (
}; if cfg.allowPlayers != null
}); then permissionsJson
default = { }; else null
}; );
};
config = let
confService.services.minecraft-bedrock-server = {
serverProperties = mapOptionDefaults {
server-name = "Dedicated Server";
gamemode = "survival";
difficulty = "easy";
allow-cheats = false;
max-players = 10;
online-mode = false;
allow-list = cfg.allowList != null;
server-port = 19132;
server-portv6 = 19133;
view-distance = 32;
tick-distance = 4;
player-idle-timeout = 30;
max-threads = 8;
level-name = "Bedrock level";
level-seed = "";
default-player-permission-level = "member";
texturepack-required = false;
content-log-file-enabled = false;
compression-threshold = 1;
server-authoritative-movement = "server-auth";
player-movement-score-threshold = 20;
player-movement-distance-threshold = 0.3;
player-movement-duration-threshold-in-ms = 500;
correct-player-movement = false;
}; };
allowList = let conf.users.users.${cfg.user} = {
allowPlayers = mapAttrsToList (_: allow: allow.settings) cfg.allowPlayers; inherit (cfg) group;
allowListJson = pkgs.writeText "minecraft-bedrock-server-allowlist.json" ( description = "Minecraft server service user";
toJSON allowPlayers home = cfg.dataDir;
); createHome = true;
in mkOptionDefault ( isSystemUser = true;
if cfg.allowPlayers != null then allowListJson };
else null conf.users.groups.${cfg.group} = {};
);
permissions = let
permissions = mapAttrsToList (_: allow: allow.permissionSettings) cfg.allowPlayers;
permissionsJson = pkgs.writeText "minecraft-bedrock-server-permissions.json" (
toJSON permissions
);
in mkOptionDefault (
if cfg.allowPlayers != null then permissionsJson
else null
);
};
conf.users.users.${cfg.user} = {
inherit (cfg) group;
description = "Minecraft server service user";
home = cfg.dataDir;
createHome = true;
isSystemUser = true;
};
conf.users.groups.${cfg.group} = {};
conf.systemd.services.minecraft-bedrock-server = { conf.systemd.services.minecraft-bedrock-server = {
description = "Minecraft Bedrock Server Service"; description = "Minecraft Bedrock Server Service";
wantedBy = [ "multi-user.target" ]; wantedBy = ["multi-user.target"];
after = [ "network.target" ]; after = ["network.target"];
serviceConfig = { serviceConfig = {
BindReadOnlyPaths = let BindReadOnlyPaths = let
packageResources = map (subpath: "${cfg.package}/var/lib/minecraft-bedrock/${subpath}:${cfg.dataDir}/${subpath}") ([ packageResources = map (subpath: "${cfg.package}/var/lib/minecraft-bedrock/${subpath}:${cfg.dataDir}/${subpath}") ([
"definitions/attachables" "definitions/attachables"
"definitions/biomes" "definitions/biomes"
"definitions/feature_rules" "definitions/feature_rules"
"definitions/features" "definitions/features"
"definitions/persona" "definitions/persona"
"definitions/sdl_layouts" "definitions/sdl_layouts"
"definitions/spawn_groups" "definitions/spawn_groups"
"resource_packs/vanilla" "resource_packs/vanilla"
"resource_packs/chemistry" "resource_packs/chemistry"
"config/default" "config/default"
"bedrock_server_symbols.debug" "bedrock_server_symbols.debug"
"env-vars" "env-vars"
] ++ optional (cfg.permissions == null) "permissions.json"); ]
mkWorldPacks = type: let ++ optional (cfg.permissions == null) "permissions.json");
enabledPacks = filterAttrs (_: pack: pack.enable && pack.packType == "${type}_packs") cfg.packs; mkWorldPacks = type: let
jsonName = "world_${type}_packs.json"; enabledPacks = filterAttrs (_: pack: pack.enable && pack.packType == "${type}_packs") cfg.packs;
packsJson = mapAttrsToList (_: pack: pack.settings) enabledPacks; jsonName = "world_${type}_packs.json";
packsJsonPath = pkgs.writeText jsonName (toJSON packsJson); packsJson = mapAttrsToList (_: pack: pack.settings) enabledPacks;
in mkIf (enabledPacks != { }) [ packsJsonPath = pkgs.writeText jsonName (toJSON packsJson);
"${packsJsonPath}:${cfg.dataDir}/worlds/${cfg.serverProperties.level-name}/${jsonName}" in
mkIf (enabledPacks != {}) [
"${packsJsonPath}:${cfg.dataDir}/worlds/${cfg.serverProperties.level-name}/${jsonName}"
];
mapWorldPacks = packs: let
enabledPacks = filterAttrs (_: pack: pack.enable && pack.package != null) packs;
mapPackPath = _: pack: let
subDir = "${pack.packType}/${pack.packDir}";
in "${pack.package}/${cfg.package.dataDir}/${subDir}:${cfg.dataDir}/${subDir}";
in
mapAttrsToList mapPackPath enabledPacks;
packsPaths = mkMerge [
(mkWorldPacks "behavior")
(mkWorldPacks "resource")
(mapWorldPacks cfg.packs)
];
in
mkMerge [
packageResources
(mkIf (cfg.allowList != null) ["${cfg.allowList}:${cfg.dataDir}/allowlist.json"])
(mkIf (cfg.permissions != null) ["${cfg.permissions}:${cfg.dataDir}/permissions.json"])
(mkIf (cfg.packs != {}) packsPaths)
];
ExecStart = [
"${getExe cfg.package}"
]; ];
mapWorldPacks = packs: let Restart = "always";
enabledPacks = filterAttrs (_: pack: pack.enable && pack.package != null) packs; User = cfg.user;
mapPackPath = _: pack: let WorkingDirectory = cfg.dataDir;
subDir = "${pack.packType}/${pack.packDir}"; LogFilterPatterns = [
in "${pack.package}/${cfg.package.dataDir}/${subDir}:${cfg.dataDir}/${subDir}"; "~.*minecraft:trial_chambers/chamber/end"
in mapAttrsToList mapPackPath enabledPacks; "~Running AutoCompaction"
packsPaths = mkMerge [
(mkWorldPacks "behavior")
(mkWorldPacks "resource")
(mapWorldPacks cfg.packs)
]; ];
in mkMerge [ };
packageResources
(mkIf (cfg.allowList != null) [ "${cfg.allowList}:${cfg.dataDir}/allowlist.json" ]) preStart = ''
(mkIf (cfg.permissions != null) [ "${cfg.permissions}:${cfg.dataDir}/permissions.json" ]) mkdir -p behavior_packs
(mkIf (cfg.packs != { }) packsPaths) ln -sf ${cfg.package}/var/lib/minecraft-bedrock/behavior_packs/* behavior_packs/
]; cp -f ${serverPropertiesFile} server.properties
ExecStart = [ chmod +w server.properties
"${getExe cfg.package}" '';
];
Restart = "always";
User = cfg.user;
WorkingDirectory = cfg.dataDir;
LogFilterPatterns = [
"~.*minecraft:trial_chambers/chamber/end"
"~Running AutoCompaction"
];
}; };
preStart = '' conf.networking.firewall = let
mkdir -p behavior_packs ports = [cfg.serverProperties.server-port cfg.serverProperties.server-portv6];
ln -sf ${cfg.package}/var/lib/minecraft-bedrock/behavior_packs/* behavior_packs/ in
cp -f ${serverPropertiesFile} server.properties mkIf cfg.openFirewall {
chmod +w server.properties allowedUDPPorts = ports;
''; };
}; in
mkMerge [
conf.networking.firewall = let confService
ports = [ cfg.serverProperties.server-port cfg.serverProperties.server-portv6 ]; (mkIf cfg.enable conf)
in mkIf cfg.openFirewall { ];
allowedUDPPorts = ports; }
};
in mkMerge [
confService
(mkIf cfg.enable conf)
];
}

View file

@ -9,14 +9,18 @@
inherit (lib.strings) concatStringsSep; inherit (lib.strings) concatStringsSep;
inherit (config.system) nssDatabases; inherit (config.system) nssDatabases;
inherit (config) networking; inherit (config) networking;
netgroupMemberModule = { config, name, ... }: { netgroupMemberModule = {
config,
name,
...
}: {
options = with lib.types; { options = with lib.types; {
hostname = mkOption { hostname = mkOption {
type = str; type = str;
default = name; default = name;
}; };
user = mkOption { user = mkOption {
type = either (enum [ null "-" ]) str; type = either (enum [null "-"]) str;
default = "-"; default = "-";
}; };
domain = mkOption { domain = mkOption {
@ -32,7 +36,11 @@
triple = mkOptionDefault "(${config.hostname},${toString config.user},${config.domain})"; triple = mkOptionDefault "(${config.hostname},${toString config.user},${config.domain})";
}; };
}; };
netgroupModule = { config, name, ... }: { netgroupModule = {
config,
name,
...
}: {
options = with lib.types; { options = with lib.types; {
name = mkOption { name = mkOption {
type = str; type = str;
@ -40,14 +48,14 @@
}; };
members = mkOption { members = mkOption {
type = attrsOf (submodule netgroupMemberModule); type = attrsOf (submodule netgroupMemberModule);
default = { }; default = {};
}; };
fileLine = mkOption { fileLine = mkOption {
type = str; type = str;
}; };
}; };
config = { config = {
fileLine = mkOptionDefault (concatStringsSep " " ([ config.name ] ++ mapAttrsToList (_: member: member.triple) config.members)); fileLine = mkOptionDefault (concatStringsSep " " ([config.name] ++ mapAttrsToList (_: member: member.triple) config.members));
}; };
}; };
in { in {
@ -60,7 +68,7 @@ in {
networking = { networking = {
netgroups = mkOption { netgroups = mkOption {
type = attrsOf (submodule netgroupModule); type = attrsOf (submodule netgroupModule);
default = { }; default = {};
}; };
extraNetgroups = mkOption { extraNetgroups = mkOption {
type = lines; type = lines;
@ -71,17 +79,17 @@ in {
config = { config = {
system.nssDatabases = { system.nssDatabases = {
netgroup = mkMerge [ netgroup = mkMerge [
(mkBefore [ "files" ]) (mkBefore ["files"])
(mkAfter [ "nis" ]) (mkAfter ["nis"])
]; ];
}; };
environment.etc."nsswitch.conf".text = mkIf (nssDatabases.netgroup != [ ]) (mkAfter '' environment.etc."nsswitch.conf".text = mkIf (nssDatabases.netgroup != []) (mkAfter ''
netgroup: ${concatStringsSep " " nssDatabases.netgroup} netgroup: ${concatStringsSep " " nssDatabases.netgroup}
''); '');
environment.etc."netgroup" = mkIf (networking.netgroups != { } || networking.extraNetgroups != "") { environment.etc."netgroup" = mkIf (networking.netgroups != {} || networking.extraNetgroups != "") {
text = mkMerge ( text = mkMerge (
mapAttrsToList (_: ng: ng.fileLine) networking.netgroups mapAttrsToList (_: ng: ng.fileLine) networking.netgroups
++ [ networking.extraNetgroups ] ++ [networking.extraNetgroups]
); );
}; };
}; };

View file

@ -1,4 +1,8 @@
{config, lib, ...}: let {
config,
lib,
...
}: let
inherit (lib.options) mkOption mkEnableOption; inherit (lib.options) mkOption mkEnableOption;
inherit (lib.modules) mkIf mkOptionDefault; inherit (lib.modules) mkIf mkOptionDefault;
inherit (lib.lists) filter optional; inherit (lib.lists) filter optional;
@ -7,21 +11,32 @@
enabledNameservers = filter (ns: ns.enable) (config.networking.nameservers'); enabledNameservers = filter (ns: ns.enable) (config.networking.nameservers');
nameserverModule = {config, ...}: let nameserverModule = {config, ...}: let
dnsPort = 53; dnsPort = 53;
mkResolvedValue = { address, port, interface ? null, host ? null }: let mkResolvedValue = {
address,
port,
interface ? null,
host ? null,
}: let
isIpv6 = hasInfix ":" address; isIpv6 = hasInfix ":" address;
isPlain = port == dnsPort && interface == null && host == null; isPlain = port == dnsPort && interface == null && host == null;
addr = if isIpv6 && !isPlain then "[${address}]" else address; addr =
in concatStrings ( if isIpv6 && !isPlain
[ addr ] then "[${address}]"
++ optional (port != dnsPort) ":${toString port}" else address;
++ optional (interface != null) "%${interface}" in
++ optional (host != null) "#${host}" concatStrings (
); [addr]
++ optional (port != dnsPort) ":${toString port}"
++ optional (interface != null) "%${interface}"
++ optional (host != null) "#${host}"
);
in { in {
options = with lib.types; { options = with lib.types; {
enable = mkEnableOption "nameserver" // { enable =
default = true; mkEnableOption "nameserver"
}; // {
default = true;
};
address = mkOption { address = mkOption {
type = str; type = str;
}; };
@ -59,12 +74,16 @@ in {
options.networking = with lib.types; { options.networking = with lib.types; {
nameservers' = mkOption { nameservers' = mkOption {
type = listOf (submodule nameserverModule); type = listOf (submodule nameserverModule);
default = { }; default = {};
}; };
}; };
config = { config = {
networking.nameservers = mkIf (config.networking.nameservers' != [ ]) ( networking.nameservers = mkIf (config.networking.nameservers' != []) (
map (ns: if resolved.enable then ns.resolvedValue else ns.value) enabledNameservers map (ns:
if resolved.enable
then ns.resolvedValue
else ns.value)
enabledNameservers
); );
}; };
} }

View file

@ -21,16 +21,20 @@
(mkIf (cfg.server.mountdPort != null) cfg.server.mountdPort) (mkIf (cfg.server.mountdPort != null) cfg.server.mountdPort)
]; ];
concatFlags = concatStringsSep ","; concatFlags = concatStringsSep ",";
clientModule = { config, name, ... }: { clientModule = {
config,
name,
...
}: {
options = with lib.types; { options = with lib.types; {
machine = mkOption { machine = mkOption {
type = oneOf [ str (listOf str) ]; type = oneOf [str (listOf str)];
default = name; default = name;
example = "*"; example = "*";
}; };
flags = mkOption { flags = mkOption {
type = listOf str; type = listOf str;
default = [ ]; default = [];
}; };
entry = mkOption { entry = mkOption {
type = str; type = str;
@ -38,12 +42,17 @@
}; };
config = { config = {
entry = let entry = let
flags = optionalString (config.flags != [ ]) "(${concatFlags config.flags})"; flags = optionalString (config.flags != []) "(${concatFlags config.flags})";
machines = toList config.machine; machines = toList config.machine;
in mkOptionDefault (concatMapStringsSep " " (machine: machine + flags) machines); in
mkOptionDefault (concatMapStringsSep " " (machine: machine + flags) machines);
}; };
}; };
exportModule = { config, name, ... }: { exportModule = {
config,
name,
...
}: {
options = with lib.types; { options = with lib.types; {
path = mkOption { path = mkOption {
type = path; type = path;
@ -60,12 +69,14 @@
}; };
}; };
config = { config = {
flags = mkOptionDefault (cfg.export.flagSets.common or [ ]); flags = mkOptionDefault (cfg.export.flagSets.common or []);
fileLine = let fileLine = let
parts = [ config.path ] parts =
++ optional (config.flags != [ ]) "-${concatFlags config.flags}" [config.path]
++ optional (config.flags != []) "-${concatFlags config.flags}"
++ mapAttrsToList (_: client: client.entry) config.clients; ++ mapAttrsToList (_: client: client.entry) config.clients;
in mkOptionDefault (concatStringsSep " " parts); in
mkOptionDefault (concatStringsSep " " parts);
}; };
}; };
in { in {
@ -74,15 +85,15 @@ in {
flagSets = mkOption { flagSets = mkOption {
type = lazyAttrsOf (listOf str); type = lazyAttrsOf (listOf str);
default = { default = {
common = [ "no_subtree_check" ]; common = ["no_subtree_check"];
}; };
}; };
root = mkOption { root = mkOption {
type = nullOr (submodule [ type = nullOr (submodule [
exportModule exportModule
({ ... }: { ({...}: {
flags = mkMerge [ flags = mkMerge [
(cfg.export.flagSets.common or [ ]) (cfg.export.flagSets.common or [])
]; ];
}) })
]); ]);
@ -90,7 +101,7 @@ in {
}; };
paths = mkOption { paths = mkOption {
type = attrsOf (submodule exportModule); type = attrsOf (submodule exportModule);
default = { }; default = {};
}; };
}; };
}; };

View file

@ -111,7 +111,7 @@
}; };
conditions = mkOption { conditions = mkOption {
type = types.listOf types.str; type = types.listOf types.str;
default = [ "iifname ${name}" ]; default = ["iifname ${name}"];
}; };
}; };
}; };

View file

@ -1,28 +1,33 @@
{ {lib, ...}: let
lib,
...
}: let
inherit (lib.options) mkOption mkEnableOption; inherit (lib.options) mkOption mkEnableOption;
inherit (lib.modules) mkIf mkOverride; inherit (lib.modules) mkIf mkOverride;
mkExtraForce = mkOverride 25; mkExtraForce = mkOverride 25;
locationModule = { config, virtualHost, ... }: { locationModule = {
config,
virtualHost,
...
}: {
options = with lib.types; { options = with lib.types; {
enable = mkEnableOption "enable location" // { enable =
default = true; mkEnableOption "enable location"
}; // {
default = true;
};
}; };
config = mkIf (!virtualHost.enable || !config.enable) { config = mkIf (!virtualHost.enable || !config.enable) {
extraConfig = mkExtraForce "deny all;"; extraConfig = mkExtraForce "deny all;";
}; };
}; };
hostModule = { config, ... }: { hostModule = {config, ...}: {
options = with lib.types; { options = with lib.types; {
enable = mkEnableOption "enable server" // { enable =
default = true; mkEnableOption "enable server"
}; // {
default = true;
};
locations = mkOption { locations = mkOption {
type = attrsOf (submoduleWith { type = attrsOf (submoduleWith {
modules = [ locationModule ]; modules = [locationModule];
shorthandOnlyDefinesConfig = true; shorthandOnlyDefinesConfig = true;
}); });
}; };
@ -39,7 +44,7 @@ in {
options = with lib.types; { options = with lib.types; {
services.nginx.virtualHosts = mkOption { services.nginx.virtualHosts = mkOption {
type = attrsOf (submoduleWith { type = attrsOf (submoduleWith {
modules = [ hostModule ]; modules = [hostModule];
shorthandOnlyDefinesConfig = true; shorthandOnlyDefinesConfig = true;
}); });
}; };

View file

@ -30,7 +30,7 @@ let
}; };
passHeaders = mkOption { passHeaders = mkOption {
type = attrsOf bool; type = attrsOf bool;
default = { }; default = {};
description = "fastcgi_pass_header"; description = "fastcgi_pass_header";
}; };
socket = mkOption { socket = mkOption {
@ -43,7 +43,8 @@ let
config = { config = {
fastcgi = { fastcgi = {
socket = mkIf (cfg.phpfpmPool != null) (mkAlmostOptionDefault socket = mkIf (cfg.phpfpmPool != null) (
mkAlmostOptionDefault
nixosConfig.services.phpfpm.pools.${cfg.phpfpmPool}.socket nixosConfig.services.phpfpm.pools.${cfg.phpfpmPool}.socket
); );
params = mapOptionDefaults { params = mapOptionDefaults {
@ -60,18 +61,24 @@ let
extraConfig = let extraConfig = let
passHeadersConfig = map (header: "fastcgi_pass_header ${xvars.escapeString header};") passHeaders; passHeadersConfig = map (header: "fastcgi_pass_header ${xvars.escapeString header};") passHeaders;
paramsConfig = mapAttrsToList (param: value: mkJustAfter "fastcgi_param ${param} ${xvars.escapeString value};") params; paramsConfig = mapAttrsToList (param: value: mkJustAfter "fastcgi_param ${param} ${xvars.escapeString value};") params;
in mkIf cfg.enable (mkMerge ([ in
(mkIf cfg.includeDefaults (mkAlmostBefore '' mkIf cfg.enable (mkMerge ([
include ${nginx.package}/conf/fastcgi.conf; (mkIf cfg.includeDefaults (mkAlmostBefore ''
'')) include ${nginx.package}/conf/fastcgi.conf;
(mkIf (cfg.socket != null) (mkJustAfter '' ''))
fastcgi_pass unix:${cfg.socket}; (mkIf (cfg.socket != null) (mkJustAfter ''
'')) fastcgi_pass unix:${cfg.socket};
] ++ passHeadersConfig ''))
++ paramsConfig)); ]
++ passHeadersConfig
++ paramsConfig));
}; };
}; };
hostModule = {config, lib, ...}: let hostModule = {
config,
lib,
...
}: let
inherit (lib.options) mkOption; inherit (lib.options) mkOption;
in { in {
options = with lib.types; { options = with lib.types; {
@ -80,15 +87,13 @@ let
}; };
}; };
}; };
in { in
lib, {lib, ...}: let
... inherit (lib.options) mkOption;
}: let in {
inherit (lib.options) mkOption; options = with lib.types; {
in { services.nginx.virtualHosts = mkOption {
options = with lib.types; { type = attrsOf (submodule [hostModule]);
services.nginx.virtualHosts = mkOption { };
type = attrsOf (submodule [hostModule]);
}; };
}; }
}

View file

@ -1,5 +1,12 @@
let let
locationModule = { config, virtualHost, xvars, gensokyo-zone, lib, ... }: let locationModule = {
config,
virtualHost,
xvars,
gensokyo-zone,
lib,
...
}: let
inherit (gensokyo-zone.lib) mapOptionDefaults; inherit (gensokyo-zone.lib) mapOptionDefaults;
inherit (lib.options) mkOption; inherit (lib.options) mkOption;
inherit (lib.modules) mkIf mkMerge mkAfter mkOptionDefault; inherit (lib.modules) mkIf mkMerge mkAfter mkOptionDefault;
@ -13,25 +20,32 @@ let
default = true; default = true;
}; };
set = mkOption { set = mkOption {
type = attrsOf (nullOr (oneOf [ str (listOf str) ])); type = attrsOf (nullOr (oneOf [str (listOf str)]));
}; };
}; };
config = let config = let
mkHeader = name: value: mkHeader = name: value:
if isList value then mkMerge (map (mkHeader name) value) if isList value
then mkMerge (map (mkHeader name) value)
else mkAfter "add_header ${name} ${xvars.escapeString value};"; else mkAfter "add_header ${name} ${xvars.escapeString value};";
setHeaders = mapAttrsToList (name: value: mkIf (value != null) (mkHeader name value)) cfg.set; setHeaders = mapAttrsToList (name: value: mkIf (value != null) (mkHeader name value)) cfg.set;
in { in {
headers = { headers = {
set = mkMerge [ set = mkMerge [
(mkOptionDefault { }) (mkOptionDefault {})
(mkIf cfg.inheritServerDefaults (mapOptionDefaults virtualHost.headers.set)) (mkIf cfg.inheritServerDefaults (mapOptionDefaults virtualHost.headers.set))
]; ];
}; };
extraConfig = mkMerge setHeaders; extraConfig = mkMerge setHeaders;
}; };
}; };
hostModule = { config, nixosConfig, gensokyo-zone, lib, ... }: let hostModule = {
config,
nixosConfig,
gensokyo-zone,
lib,
...
}: let
inherit (gensokyo-zone.lib) mapOptionDefaults; inherit (gensokyo-zone.lib) mapOptionDefaults;
inherit (lib.options) mkOption; inherit (lib.options) mkOption;
inherit (nixosConfig.services) nginx; inherit (nixosConfig.services) nginx;
@ -39,12 +53,12 @@ let
options = with lib.types; { options = with lib.types; {
headers = { headers = {
set = mkOption { set = mkOption {
type = attrsOf (nullOr (oneOf [ str (listOf str) ])); type = attrsOf (nullOr (oneOf [str (listOf str)]));
}; };
}; };
locations = mkOption { locations = mkOption {
type = attrsOf (submoduleWith { type = attrsOf (submoduleWith {
modules = [ locationModule ]; modules = [locationModule];
shorthandOnlyDefinesConfig = true; shorthandOnlyDefinesConfig = true;
}); });
}; };
@ -55,22 +69,20 @@ let
}; };
}; };
}; };
in { in
lib, {lib, ...}: let
... inherit (lib.options) mkOption;
}: let in {
inherit (lib.options) mkOption; options.services.nginx = with lib.types; {
in { headers = {
options.services.nginx = with lib.types; { set = mkOption {
headers = { type = attrsOf (nullOr (oneOf [str (listOf str)]));
set = mkOption { default = {
type = attrsOf (nullOr (oneOf [ str (listOf str) ])); };
default = {
}; };
}; };
virtualHosts = mkOption {
type = attrsOf (submodule [hostModule]);
};
}; };
virtualHosts = mkOption { }
type = attrsOf (submodule [hostModule]);
};
};
}

View file

@ -10,11 +10,18 @@
inherit (lib.attrsets) attrValues mapAttrs; inherit (lib.attrsets) attrValues mapAttrs;
inherit (lib.lists) optional filter concatMap; inherit (lib.lists) optional filter concatMap;
inherit (config.services) nginx; inherit (config.services) nginx;
listenModule = { config, virtualHost, listenKind, ... }: { listenModule = {
config,
virtualHost,
listenKind,
...
}: {
options = with lib.types; { options = with lib.types; {
enable = mkEnableOption "this port" // { enable =
default = true; mkEnableOption "this port"
}; // {
default = true;
};
addr = mkOption { addr = mkOption {
type = nullOr str; type = nullOr str;
default = null; default = null;
@ -34,7 +41,7 @@
}; };
extraParameters = mkOption { extraParameters = mkOption {
type = listOf str; type = listOf str;
default = [ ]; default = [];
}; };
proxyProtocol = mkOption { proxyProtocol = mkOption {
type = bool; type = bool;
@ -59,11 +66,13 @@
(mkIf (listenKind == "streamServer" && !config.ssl && virtualHost.ssl.enable && virtualHost.ssl.force != false) (mkForce false)) (mkIf (listenKind == "streamServer" && !config.ssl && virtualHost.ssl.enable && virtualHost.ssl.force != false) (mkForce false))
]; ];
port = mkIf (listenKind == "virtualHost") (mkOptionDefault ( port = mkIf (listenKind == "virtualHost") (mkOptionDefault (
if config.ssl then nginx.defaultSSLListenPort else nginx.defaultHTTPListenPort if config.ssl
then nginx.defaultSSLListenPort
else nginx.defaultHTTPListenPort
)); ));
addresses = mkMerge [ addresses = mkMerge [
(mkOptionDefault virtualHost.listenAddresses') (mkOptionDefault virtualHost.listenAddresses')
(mkIf (config.addr != null) (mkAlmostOptionDefault [ config.addr ])) (mkIf (config.addr != null) (mkAlmostOptionDefault [config.addr]))
]; ];
listenParameters = mkOptionDefault ( listenParameters = mkOptionDefault (
optional config.ssl "ssl" optional config.ssl "ssl"
@ -74,26 +83,44 @@
); );
listenConfigs = let listenConfigs = let
# TODO: handle quic listener..? # TODO: handle quic listener..?
mkListenHost = { addr, port }: let mkListenHost = {
addr,
port,
}: let
host = host =
if addr != null then "${mkAddress6 addr}:${toString port}" if addr != null
then "${mkAddress6 addr}:${toString port}"
else toString port; else toString port;
in assert port != null; host; in
assert port != null; host;
mkDirective = addr: let mkDirective = addr: let
host = mkListenHost { inherit addr; inherit (config) port; }; host = mkListenHost {
in mkMerge ( inherit addr;
[ (mkBefore host) ] inherit (config) port;
++ config.listenParameters };
); in
in mkOptionDefault (map (mkDirective) config.addresses); mkMerge (
[(mkBefore host)]
++ config.listenParameters
);
in
mkOptionDefault (map mkDirective config.addresses);
listenDirectives = mkMerge (map (conf: mkOptionDefault "listen ${conf};") config.listenConfigs); listenDirectives = mkMerge (map (conf: mkOptionDefault "listen ${conf};") config.listenConfigs);
}; };
}; };
listenType = { specialArgs, modules ? [ ] }: lib.types.submoduleWith { listenType = {
inherit specialArgs; specialArgs,
modules = [ listenModule ] ++ modules; modules ? [],
}; }:
hostModule = { nixosConfig, config, ... }: let lib.types.submoduleWith {
inherit specialArgs;
modules = [listenModule] ++ modules;
};
hostModule = {
nixosConfig,
config,
...
}: let
cfg = attrValues config.listen'; cfg = attrValues config.listen';
enabledCfg = filter (port: port.enable) cfg; enabledCfg = filter (port: port.enable) cfg;
mkListen = listen: addr: let mkListen = listen: addr: let
@ -101,7 +128,8 @@
inherit addr; inherit addr;
inherit (listen) port ssl extraParameters proxyProtocol; inherit (listen) port ssl extraParameters proxyProtocol;
}; };
in mapAttrs (_: mkAlmostOptionDefault) listenAttrs; in
mapAttrs (_: mkAlmostOptionDefault) listenAttrs;
mkListens = listen: map (mkListen listen) listen.addresses; mkListens = listen: map (mkListen listen) listen.addresses;
in { in {
options = with lib.types; { options = with lib.types; {
@ -113,7 +141,7 @@
listenKind = "virtualHost"; listenKind = "virtualHost";
}; };
}); });
default = { }; default = {};
}; };
listenAddresses' = mkOption { listenAddresses' = mkOption {
type = listOf str; type = listOf str;
@ -122,16 +150,22 @@
}; };
config = { config = {
enable = mkIf (cfg != [ ] && enabledCfg == [ ]) (mkForce false); enable = mkIf (cfg != [] && enabledCfg == []) (mkForce false);
listenAddresses' = mkOptionDefault ( listenAddresses' = mkOptionDefault (
if config.listenAddresses != [ ] then config.listenAddresses else nginx.defaultListenAddresses if config.listenAddresses != []
then config.listenAddresses
else nginx.defaultListenAddresses
); );
listen = mkIf (cfg != { }) (mkAlmostOptionDefault ( listen = mkIf (cfg != {}) (mkAlmostOptionDefault (
concatMap (mkListens) enabledCfg concatMap mkListens enabledCfg
)); ));
}; };
}; };
streamServerModule = { nixosConfig, config, ... }: let streamServerModule = {
nixosConfig,
config,
...
}: let
enabledListen = filter (port: port.enable) (attrValues config.listen); enabledListen = filter (port: port.enable) (attrValues config.listen);
in { in {
options = with lib.types; { options = with lib.types; {
@ -144,7 +178,7 @@
listenKind = "streamServer"; listenKind = "streamServer";
}; };
}); });
default = { }; default = {};
}; };
listenAddresses = mkOption { listenAddresses = mkOption {
type = nullOr (listOf str); type = nullOr (listOf str);
@ -163,11 +197,13 @@
}; };
config = { config = {
enable = mkIf (config.listen != { } && enabledListen == [ ]) (mkForce false); enable = mkIf (config.listen != {} && enabledListen == []) (mkForce false);
listenAddresses' = mkOptionDefault ( listenAddresses' = mkOptionDefault (
if config.listenAddresses != null then config.listenAddresses else nginx.defaultListenAddresses if config.listenAddresses != null
then config.listenAddresses
else nginx.defaultListenAddresses
); );
streamConfig = mkIf (config.listen != { }) (mkMerge ( streamConfig = mkIf (config.listen != {}) (mkMerge (
map (listen: mkBefore listen.listenDirectives) enabledListen map (listen: mkBefore listen.listenDirectives) enabledListen
)); ));
}; };
@ -176,13 +212,13 @@ in {
options.services.nginx = with lib.types; { options.services.nginx = with lib.types; {
virtualHosts = mkOption { virtualHosts = mkOption {
type = attrsOf (submoduleWith { type = attrsOf (submoduleWith {
modules = [ hostModule ]; modules = [hostModule];
shorthandOnlyDefinesConfig = true; shorthandOnlyDefinesConfig = true;
}); });
}; };
stream.servers = mkOption { stream.servers = mkOption {
type = attrsOf (submoduleWith { type = attrsOf (submoduleWith {
modules = [ streamServerModule ]; modules = [streamServerModule];
shorthandOnlyDefinesConfig = false; shorthandOnlyDefinesConfig = false;
}); });
}; };

View file

@ -8,55 +8,62 @@
inherit (lib.strings) concatMapStringsSep optionalString; inherit (lib.strings) concatMapStringsSep optionalString;
inherit (config.services) tailscale; inherit (config.services) tailscale;
inherit (config.networking.access) cidrForNetwork localaddrs; inherit (config.networking.access) cidrForNetwork localaddrs;
mkAddrVar = remoteAddr: varPrefix: '' mkAddrVar = remoteAddr: varPrefix:
set ${varPrefix}tailscale 0; ''
'' + optionalString tailscale.enable '' set ${varPrefix}tailscale 0;
if (${remoteAddr} ~ "^fd7a:115c:a1e0:(:|ab12:)") { ''
set ${varPrefix}tailscale 1; + optionalString tailscale.enable ''
} if (${remoteAddr} ~ "^fd7a:115c:a1e0:(:|ab12:)") {
if (${remoteAddr} ~ "^100\.(6[4-9]|([7-9]|1[01])[0-9]|12[0-7])\.[0-9]+\.[0-9]+") { set ${varPrefix}tailscale 1;
set ${varPrefix}tailscale 1; }
} if (${remoteAddr} ~ "^100\.(6[4-9]|([7-9]|1[01])[0-9]|12[0-7])\.[0-9]+\.[0-9]+") {
'' + '' set ${varPrefix}tailscale 1;
set ${varPrefix}lan 0; }
if (${remoteAddr} ~ "^10\.1\.1\.[0-9]+") { ''
set ${varPrefix}lan 1; + ''
} set ${varPrefix}lan 0;
if (${remoteAddr} ~ "^fd0a::") { if (${remoteAddr} ~ "^10\.1\.1\.[0-9]+") {
set ${varPrefix}lan 1; set ${varPrefix}lan 1;
} }
if (${remoteAddr} ~ "^fe80::") { if (${remoteAddr} ~ "^fd0a::") {
set ${varPrefix}lan 1; set ${varPrefix}lan 1;
} }
set ${varPrefix}int 0; if (${remoteAddr} ~ "^fe80::") {
if (${remoteAddr} ~ "^10\.9\.1\.[0-9]+") { set ${varPrefix}lan 1;
set ${varPrefix}lan 1; }
} set ${varPrefix}int 0;
if (${remoteAddr} ~ "^fd0c::") { if (${remoteAddr} ~ "^10\.9\.1\.[0-9]+") {
set ${varPrefix}int 1; set ${varPrefix}lan 1;
} }
set ${varPrefix}localhost 0; if (${remoteAddr} ~ "^fd0c::") {
if (${remoteAddr} = "::1") { set ${varPrefix}int 1;
set ${varPrefix}localhost 1; }
} set ${varPrefix}localhost 0;
if (${remoteAddr} ~ "127\.0\.0\.[0-9]+") { if (${remoteAddr} = "::1") {
set ${varPrefix}localhost 1; set ${varPrefix}localhost 1;
} }
set ${varPrefix}client 0; if (${remoteAddr} ~ "127\.0\.0\.[0-9]+") {
if (${varPrefix}tailscale) { set ${varPrefix}localhost 1;
set ${varPrefix}client 1; }
} set ${varPrefix}client 0;
if (${varPrefix}lan) { if (${varPrefix}tailscale) {
set ${varPrefix}client 1; set ${varPrefix}client 1;
} }
if (${varPrefix}int) { if (${varPrefix}lan) {
set ${varPrefix}client 1; set ${varPrefix}client 1;
} }
if (${varPrefix}localhost) { if (${varPrefix}int) {
set ${varPrefix}client 1; set ${varPrefix}client 1;
} }
''; if (${varPrefix}localhost) {
localModule = {config, xvars, ...}: let set ${varPrefix}client 1;
}
'';
localModule = {
config,
xvars,
...
}: let
cfg = config.local; cfg = config.local;
in { in {
options.local = with lib.types; { options.local = with lib.types; {
@ -97,11 +104,12 @@
${allows} ${allows}
deny all; deny all;
''; '';
in mkMerge [ in
(mkIf cfg.emitDenyGlobal (mkBefore allowDirectives)) mkMerge [
(mkIf cfg.emitVars (mkBefore (mkAddrVar "$remote_addr" "$local_"))) (mkIf cfg.emitDenyGlobal (mkBefore allowDirectives))
(mkIf (cfg.emitVars && config.xvars.enable) (mkBefore (mkAddrVar (xvars.remote_addr.get) "$x_local_"))) (mkIf cfg.emitVars (mkBefore (mkAddrVar "$remote_addr" "$local_")))
]; (mkIf (cfg.emitVars && config.xvars.enable) (mkBefore (mkAddrVar (xvars.remote_addr.get) "$x_local_")))
];
}; };
}; };
locationModule = { locationModule = {

View file

@ -1,4 +1,9 @@
{pkgs, config, lib, ...}: let {
pkgs,
config,
lib,
...
}: let
inherit (lib.options) mkOption mkEnableOption; inherit (lib.options) mkOption mkEnableOption;
inherit (lib.modules) mkIf mkMerge mkAfter mkOptionDefault; inherit (lib.modules) mkIf mkMerge mkAfter mkOptionDefault;
inherit (lib.strings) hasPrefix; inherit (lib.strings) hasPrefix;
@ -10,7 +15,8 @@
luaModule = {config, ...}: let luaModule = {config, ...}: let
cfg = config.lua; cfg = config.lua;
mkSetBy = var: value: mkSetBy = var: value:
if hasPrefix "/" "${value}" then "set_by_lua_file \$${var} ${value};" if hasPrefix "/" "${value}"
then "set_by_lua_file \$${var} ${value};"
else '' else ''
set_by_lua_block ''$${var} { set_by_lua_block ''$${var} {
${value} ${value}
@ -25,12 +31,12 @@
}; };
files = mkOption { files = mkOption {
type = listOf path; type = listOf path;
default = [ ]; default = [];
}; };
}; };
set = mkOption { set = mkOption {
type = attrsOf (either path lines); type = attrsOf (either path lines);
default = { }; default = {};
}; };
}; };
config = { config = {
@ -40,25 +46,27 @@
${cfg.access.block} ${cfg.access.block}
} }
'')) ''))
(mkIf (cfg.access.files != [ ]) (assert lua.http.enable; mkMerge ( (mkIf (cfg.access.files != []) (assert lua.http.enable;
map (file: "access_by_lua_file ${file};") cfg.access.files mkMerge (
))) map (file: "access_by_lua_file ${file};") cfg.access.files
(mkIf (cfg.set != { }) (assert lua.http.enable && lua.ndk.enable; mkMerge ( )))
mapAttrsToList mkSetBy cfg.set (mkIf (cfg.set != {}) (assert lua.http.enable && lua.ndk.enable;
))) mkMerge (
mapAttrsToList mkSetBy cfg.set
)))
]; ];
}; };
}; };
locationModule = {config, ...}: { locationModule = {config, ...}: {
imports = [ luaModule ]; imports = [luaModule];
}; };
hostModule = {config, ...}: { hostModule = {config, ...}: {
imports = [ luaModule ]; imports = [luaModule];
options = with lib.types; { options = with lib.types; {
locations = mkOption { locations = mkOption {
type = attrsOf (submoduleWith { type = attrsOf (submoduleWith {
modules = [ locationModule ]; modules = [locationModule];
shorthandOnlyDefinesConfig = true; shorthandOnlyDefinesConfig = true;
}); });
}; };
@ -84,7 +92,7 @@ in {
}; };
virtualHosts = mkOption { virtualHosts = mkOption {
type = attrsOf (submoduleWith { type = attrsOf (submoduleWith {
modules = [ hostModule ]; modules = [hostModule];
shorthandOnlyDefinesConfig = true; shorthandOnlyDefinesConfig = true;
}); });
}; };
@ -92,18 +100,20 @@ in {
config = { config = {
services.nginx = { services.nginx = {
lua = { lua = {
modules = [ modules =
cfg.luaPackage.pkgs.lua-resty-core [
] ++ cfg.luaPackage.pkgs.lua-resty-core.propagatedBuildInputs; cfg.luaPackage.pkgs.lua-resty-core
]
++ cfg.luaPackage.pkgs.lua-resty-core.propagatedBuildInputs;
luaPath = mkMerge ( luaPath = mkMerge (
map luaPkgPath cfg.modules map luaPkgPath cfg.modules
++ [ (mkAfter ";") ] ++ [(mkAfter ";")]
); );
}; };
additionalModules = mkMerge [ additionalModules = mkMerge [
(mkIf cfg.ndk.enable [ pkgs.nginxModules.develkit ]) (mkIf cfg.ndk.enable [pkgs.nginxModules.develkit])
(mkIf cfg.http.enable [ pkgs.nginxModules.lua ]) (mkIf cfg.http.enable [pkgs.nginxModules.lua])
(mkIf cfg.upstream.enable [ pkgs.nginxModules.lua-upstream ]) (mkIf cfg.upstream.enable [pkgs.nginxModules.lua-upstream])
]; ];
}; };
systemd.services.nginx = mkIf config.services.nginx.enable { systemd.services.nginx = mkIf config.services.nginx.enable {

View file

@ -46,18 +46,21 @@
config = { config = {
name = { name = {
qualifier = mkOptionDefault ( qualifier = mkOptionDefault (
if config.local.enable then "local" if config.local.enable
then "local"
else null else null
); );
includeTailscale = mkOptionDefault ( includeTailscale = mkOptionDefault (
config.local.enable && tailscale.enable && cfg.qualifier != "tail" config.local.enable && tailscale.enable && cfg.qualifier != "tail"
); );
localName = mkOptionDefault ( localName = mkOptionDefault (
if cfg.includeLocal then "${cfg.shortServer}.local.${networking.domain}" if cfg.includeLocal
then "${cfg.shortServer}.local.${networking.domain}"
else null else null
); );
tailscaleName = mkOptionDefault ( tailscaleName = mkOptionDefault (
if cfg.includeTailscale then "${cfg.shortServer}.tail.${networking.domain}" if cfg.includeTailscale
then "${cfg.shortServer}.tail.${networking.domain}"
else null else null
); );
}; };
@ -71,7 +74,7 @@
(mkIf (cfg.tailscaleName != null) cfg.tailscaleName) (mkIf (cfg.tailscaleName != null) cfg.tailscaleName)
]); ]);
allServerNames = mkOptionDefault (filter (name: ! hasPrefix "@" name) ( allServerNames = mkOptionDefault (filter (name: ! hasPrefix "@" name) (
[ config.serverName ] ++ config.serverAliases [config.serverName] ++ config.serverAliases
)); ));
otherServerNames = mkOptionDefault (filter (name: ! hasPrefix "@" name) ( otherServerNames = mkOptionDefault (filter (name: ! hasPrefix "@" name) (
config.serverAliases config.serverAliases

View file

@ -1,5 +1,12 @@
let let
serverModule = {config, nixosConfig, name, gensokyo-zone, lib, ...}: let serverModule = {
config,
nixosConfig,
name,
gensokyo-zone,
lib,
...
}: let
inherit (gensokyo-zone.lib) mkAlmostOptionDefault; inherit (gensokyo-zone.lib) mkAlmostOptionDefault;
inherit (lib.options) mkOption mkEnableOption; inherit (lib.options) mkOption mkEnableOption;
inherit (lib.modules) mkIf mkBefore mkOptionDefault; inherit (lib.modules) mkIf mkBefore mkOptionDefault;
@ -13,7 +20,7 @@ let
enable = mkEnableOption "ngx_stream_ssl_preread_module"; enable = mkEnableOption "ngx_stream_ssl_preread_module";
upstream = mkOption { upstream = mkOption {
type = str; type = str;
default = "$preread_" + replaceStrings [ "'" ] [ "_" ] name; default = "$preread_" + replaceStrings ["'"] ["_"] name;
}; };
upstreams = mkOption { upstreams = mkOption {
type = nullOr (attrsOf str); type = nullOr (attrsOf str);
@ -25,9 +32,10 @@ let
config = let config = let
inherit (nginx.stream) upstreams; inherit (nginx.stream) upstreams;
mkUpstream = host: upstream: "${host} ${upstreams.${upstream}.name};"; mkUpstream = host: upstream: "${host} ${upstreams.${upstream}.name};";
upstreams' = removeAttrs cfg.upstreams [ "default" ]; upstreams' = removeAttrs cfg.upstreams ["default"];
upstreamLines = mapAttrsToList mkUpstream upstreams' upstreamLines =
++ optional (cfg.upstreams ? default) (mkUpstream "default" cfg.upstreams.default); mapAttrsToList mkUpstream upstreams'
++ optional (cfg.upstreams ? default) (mkUpstream "default" cfg.upstreams.default);
in { in {
ssl.preread = { ssl.preread = {
streamConfig = mkIf (cfg.upstreams != null) '' streamConfig = mkIf (cfg.upstreams != null) ''
@ -46,59 +54,65 @@ let
serverBlock = mkIf cfg.enable (mkOptionDefault (mkBefore cfg.streamConfig)); serverBlock = mkIf cfg.enable (mkOptionDefault (mkBefore cfg.streamConfig));
}; };
}; };
in {config, gensokyo-zone, lib, ...}: let in
inherit (gensokyo-zone.lib) mkAlmostOptionDefault; {
inherit (lib.options) mkOption mkEnableOption; config,
inherit (lib.modules) mkIf mkDefault mkOptionDefault; gensokyo-zone,
cfg = config.services.nginx.ssl.preread; lib,
in { ...
options.services.nginx = with lib.types; { }: let
ssl.preread = { inherit (gensokyo-zone.lib) mkAlmostOptionDefault;
enable = mkEnableOption "ssl preread"; inherit (lib.options) mkOption mkEnableOption;
listenPort = mkOption { inherit (lib.modules) mkIf mkDefault mkOptionDefault;
type = port; cfg = config.services.nginx.ssl.preread;
default = 444; in {
}; options.services.nginx = with lib.types; {
serverPort = mkOption { ssl.preread = {
type = port; enable = mkEnableOption "ssl preread";
default = 443; listenPort = mkOption {
}; type = port;
serverName = mkOption { default = 444;
type = str;
default = "preread'https";
};
upstreamName = mkOption {
type = str;
default = "preread'nginx";
};
};
stream.servers = mkOption {
type = attrsOf (submoduleWith {
modules = [serverModule];
shorthandOnlyDefinesConfig = false;
});
};
};
config = {
services.nginx = {
defaultSSLListenPort = mkIf cfg.enable cfg.listenPort;
stream = {
upstreams.${cfg.upstreamName} = mkIf cfg.enable {
ssl.enable = true;
servers.access = {
addr = mkDefault "localhost";
port = mkOptionDefault cfg.listenPort;
};
}; };
servers.${cfg.serverName} = { serverPort = mkOption {
enable = mkIf (!cfg.enable) (mkAlmostOptionDefault false); type = port;
listen.https.port = cfg.serverPort; default = 443;
ssl.preread = { };
enable = true; serverName = mkOption {
upstreams.default = mkOptionDefault cfg.upstreamName; type = str;
default = "preread'https";
};
upstreamName = mkOption {
type = str;
default = "preread'nginx";
};
};
stream.servers = mkOption {
type = attrsOf (submoduleWith {
modules = [serverModule];
shorthandOnlyDefinesConfig = false;
});
};
};
config = {
services.nginx = {
defaultSSLListenPort = mkIf cfg.enable cfg.listenPort;
stream = {
upstreams.${cfg.upstreamName} = mkIf cfg.enable {
ssl.enable = true;
servers.access = {
addr = mkDefault "localhost";
port = mkOptionDefault cfg.listenPort;
};
};
servers.${cfg.serverName} = {
enable = mkIf (!cfg.enable) (mkAlmostOptionDefault false);
listen.https.port = cfg.serverPort;
ssl.preread = {
enable = true;
upstreams.default = mkOptionDefault cfg.upstreamName;
};
}; };
}; };
}; };
}; };
}; }
}

View file

@ -1,5 +1,5 @@
let let
xHeadersProxied = { xvars }: '' xHeadersProxied = {xvars}: ''
${xvars.init "forwarded_for" "$proxy_add_x_forwarded_for"} ${xvars.init "forwarded_for" "$proxy_add_x_forwarded_for"}
if ($http_x_forwarded_proto) { if ($http_x_forwarded_proto) {
${xvars.init "scheme" "$http_x_forwarded_proto"} ${xvars.init "scheme" "$http_x_forwarded_proto"}
@ -18,7 +18,14 @@ let
${xvars.init "forwarded_server" "$http_x_forwarded_server"} ${xvars.init "forwarded_server" "$http_x_forwarded_server"}
} }
''; '';
locationModule = { config, virtualHost, xvars, gensokyo-zone, lib, ... }: let locationModule = {
config,
virtualHost,
xvars,
gensokyo-zone,
lib,
...
}: let
inherit (gensokyo-zone.lib) mkJustBefore mkAlmostOptionDefault; inherit (gensokyo-zone.lib) mkJustBefore mkAlmostOptionDefault;
inherit (lib.options) mkOption; inherit (lib.options) mkOption;
inherit (lib.modules) mkIf mkMerge mkOptionDefault; inherit (lib.modules) mkIf mkMerge mkOptionDefault;
@ -27,7 +34,7 @@ let
options = with lib.types; { options = with lib.types; {
proxied = { proxied = {
enable = mkOption { enable = mkOption {
type = enum [ false true "cloudflared" ]; type = enum [false true "cloudflared"];
default = false; default = false;
}; };
enabled = mkOption { enabled = mkOption {
@ -60,12 +67,19 @@ let
xvars.enable = mkIf cfg.enabled true; xvars.enable = mkIf cfg.enabled true;
extraConfig = mkMerge [ extraConfig = mkMerge [
(mkIf emitVars ( (mkIf emitVars (
mkJustBefore (xHeadersProxied { inherit xvars; }) mkJustBefore (xHeadersProxied {inherit xvars;})
)) ))
]; ];
}; };
}; };
hostModule = { config, nixosConfig, xvars, gensokyo-zone, lib, ... }: let hostModule = {
config,
nixosConfig,
xvars,
gensokyo-zone,
lib,
...
}: let
inherit (gensokyo-zone.lib) mkAlmostOptionDefault orderJustBefore unmerged; inherit (gensokyo-zone.lib) mkAlmostOptionDefault orderJustBefore unmerged;
inherit (lib.options) mkOption; inherit (lib.options) mkOption;
inherit (lib.modules) mkIf mkOrder mkDefault; inherit (lib.modules) mkIf mkOrder mkDefault;
@ -75,7 +89,7 @@ let
options = with lib.types; { options = with lib.types; {
proxied = { proxied = {
enable = mkOption { enable = mkOption {
type = enum [ false true "cloudflared" ]; type = enum [false true "cloudflared"];
default = false; default = false;
}; };
enabled = mkOption { enabled = mkOption {
@ -93,7 +107,7 @@ let
}; };
locations = mkOption { locations = mkOption {
type = attrsOf (submoduleWith { type = attrsOf (submoduleWith {
modules = [ locationModule ]; modules = [locationModule];
shorthandOnlyDefinesConfig = true; shorthandOnlyDefinesConfig = true;
}); });
}; };
@ -105,14 +119,23 @@ let
proxied = { proxied = {
cloudflared = let cloudflared = let
listen = config.listen'.proxied; listen = config.listen'.proxied;
scheme = if listen.ssl then "https" else "http"; scheme =
in mkIf (cfg.enable == "cloudflared") { if listen.ssl
ingressSettings.${config.serverName} = { then "https"
service = "${scheme}://localhost:${toString listen.port}"; else "http";
originRequest.${if scheme == "https" then "noTLSVerify" else null} = true; in
mkIf (cfg.enable == "cloudflared") {
ingressSettings.${config.serverName} = {
service = "${scheme}://localhost:${toString listen.port}";
originRequest.${
if scheme == "https"
then "noTLSVerify"
else null
} =
true;
};
getIngress = {}: unmerged.mergeAttrs cfg.cloudflared.ingressSettings;
}; };
getIngress = {}: unmerged.mergeAttrs cfg.cloudflared.ingressSettings;
};
}; };
xvars.enable = mkIf cfg.enabled true; xvars.enable = mkIf cfg.enabled true;
local.denyGlobal = mkIf listenProxied (mkDefault true); local.denyGlobal = mkIf listenProxied (mkDefault true);
@ -123,74 +146,75 @@ let
}; };
}; };
extraConfig = mkIf (cfg.enabled && config.xvars.enable) ( extraConfig = mkIf (cfg.enabled && config.xvars.enable) (
mkOrder (orderJustBefore + 25) (xHeadersProxied { inherit xvars; }) mkOrder (orderJustBefore + 25) (xHeadersProxied {inherit xvars;})
); );
}; };
}; };
in { in
config, {
system, config,
gensokyo-zone, system,
lib, gensokyo-zone,
... lib,
}: let ...
inherit (gensokyo-zone.lib) mkAlmostOptionDefault; }: let
inherit (lib.options) mkOption mkEnableOption; inherit (gensokyo-zone.lib) mkAlmostOptionDefault;
inherit (lib.modules) mkIf mkOptionDefault; inherit (lib.options) mkOption mkEnableOption;
inherit (lib.attrsets) attrValues; inherit (lib.modules) mkIf mkOptionDefault;
inherit (lib.lists) any; inherit (lib.attrsets) attrValues;
inherit (config.services) nginx; inherit (lib.lists) any;
cfg = nginx.proxied; inherit (config.services) nginx;
in { cfg = nginx.proxied;
options.services.nginx = with lib.types; { in {
proxied = { options.services.nginx = with lib.types; {
enable = mkEnableOption "proxy"; proxied = {
listenAddr = mkOption { enable = mkEnableOption "proxy";
type = str; listenAddr = mkOption {
default = "[::]"; type = str;
default = "[::]";
};
listenPort = mkOption {
type = port;
default = 9080;
};
}; };
listenPort = mkOption { virtualHosts = mkOption {
type = port; type = attrsOf (submodule [hostModule]);
default = 9080;
}; };
}; };
virtualHosts = mkOption { config = {
type = attrsOf (submodule [hostModule]); services.nginx = let
}; warnEnable = lib.warnIf (cfg.enable != hasProxiedHosts) "services.nginx.proxied.enable expected to be set";
}; hasProxiedHosts = any (virtualHost: virtualHost.enable && virtualHost.proxied.enabled) (attrValues nginx.virtualHosts);
config = { in {
services.nginx = let upstreams' = {
warnEnable = lib.warnIf (cfg.enable != hasProxiedHosts) "services.nginx.proxied.enable expected to be set"; nginx'proxied = mkIf (warnEnable cfg.enable) {
hasProxiedHosts = any (virtualHost: virtualHost.enable && virtualHost.proxied.enabled) (attrValues nginx.virtualHosts); servers.local = {
in { accessService = {
upstreams' = { system = system.name;
nginx'proxied = mkIf (warnEnable cfg.enable) { name = "nginx";
servers.local = { port = "proxied";
accessService = { };
system = system.name;
name = "nginx";
port = "proxied";
}; };
}; };
}; };
}; virtualHosts = {
virtualHosts = { fallback'proxied = mkIf cfg.enable {
fallback'proxied = mkIf cfg.enable { serverName = null;
serverName = null; reuseport = mkAlmostOptionDefault true;
reuseport = mkAlmostOptionDefault true; default = mkAlmostOptionDefault true;
default = mkAlmostOptionDefault true; listen'.proxied = {
listen'.proxied = { addr = mkAlmostOptionDefault cfg.listenAddr;
addr = mkAlmostOptionDefault cfg.listenAddr; port = mkAlmostOptionDefault cfg.listenPort;
port = mkAlmostOptionDefault cfg.listenPort; };
locations."/".extraConfig = mkAlmostOptionDefault ''
return 502;
'';
}; };
locations."/".extraConfig = mkAlmostOptionDefault ''
return 502;
'';
}; };
}; };
networking.firewall.interfaces.lan = mkIf nginx.enable {
allowedTCPPorts = mkIf cfg.enable [cfg.listenPort];
};
}; };
networking.firewall.interfaces.lan = mkIf nginx.enable { }
allowedTCPPorts = mkIf cfg.enable [ cfg.listenPort ];
};
};
}

View file

@ -1,5 +1,12 @@
let let
proxyModule = {config, name, options, gensokyo-zone, lib, ...}: let proxyModule = {
config,
name,
options,
gensokyo-zone,
lib,
...
}: let
inherit (lib.options) mkOption mkEnableOption; inherit (lib.options) mkOption mkEnableOption;
inherit (lib.modules) mkIf mkMerge mkAfter mkOptionDefault; inherit (lib.modules) mkIf mkMerge mkAfter mkOptionDefault;
inherit (lib.strings) optionalString; inherit (lib.strings) optionalString;
@ -32,21 +39,38 @@ let
]); ]);
}; };
}; };
serverModule = {config, name, options, gensokyo-zone, lib, ...}: let serverModule = {
config,
name,
options,
gensokyo-zone,
lib,
...
}: let
inherit (lib.modules) mkIf mkAfter; inherit (lib.modules) mkIf mkAfter;
cfg = config.proxy; cfg = config.proxy;
in { in {
imports = [ proxyModule ]; imports = [proxyModule];
config = let config = let
warnProxy = lib.warnIf (!cfg.enable && options.proxy.url.isDefined) "nginx.stream.servers.${name}.proxy.url set without proxy.enable"; warnProxy = lib.warnIf (!cfg.enable && options.proxy.url.isDefined) "nginx.stream.servers.${name}.proxy.url set without proxy.enable";
in { in {
streamConfig = warnProxy (mkIf cfg.enable (mkAfter streamConfig = warnProxy (mkIf cfg.enable (
mkAfter
"proxy_pass ${cfg.url};" "proxy_pass ${cfg.url};"
)); ));
}; };
}; };
locationModule = { config, nixosConfig, name, virtualHost, xvars, gensokyo-zone, lib, ... }: let locationModule = {
config,
nixosConfig,
name,
virtualHost,
xvars,
gensokyo-zone,
lib,
...
}: let
inherit (gensokyo-zone.lib) mkJustBefore mkJustAfter mkAlmostOptionDefault mapOptionDefaults coalesce parseUrl; inherit (gensokyo-zone.lib) mkJustBefore mkJustAfter mkAlmostOptionDefault mapOptionDefaults coalesce parseUrl;
inherit (lib.options) mkOption mkEnableOption; inherit (lib.options) mkOption mkEnableOption;
inherit (lib.modules) mkIf mkMerge mkBefore mkOptionDefault; inherit (lib.modules) mkIf mkMerge mkBefore mkOptionDefault;
@ -57,7 +81,7 @@ let
inherit (nixosConfig.services) nginx; inherit (nixosConfig.services) nginx;
cfg = config.proxy; cfg = config.proxy;
in { in {
imports = [ proxyModule ]; imports = [proxyModule];
options = with lib.types; { options = with lib.types; {
proxy = { proxy = {
@ -75,9 +99,11 @@ let
host = mkOption { host = mkOption {
type = nullOr str; type = nullOr str;
}; };
websocket.enable = mkEnableOption "websocket proxy" // { websocket.enable =
default = cfg.inheritServerDefaults && virtualHost.proxy.websocket.enable; mkEnableOption "websocket proxy"
}; // {
default = cfg.inheritServerDefaults && virtualHost.proxy.websocket.enable;
};
parsed = { parsed = {
scheme = mkOption { scheme = mkOption {
type = nullOr str; type = nullOr str;
@ -94,7 +120,7 @@ let
}; };
headers = { headers = {
enableRecommended = mkOption { enableRecommended = mkOption {
type = enum [ true false "nixpkgs" ]; type = enum [true false "nixpkgs"];
}; };
rewriteReferer.enable = mkEnableOption "rewrite referer host"; rewriteReferer.enable = mkEnableOption "rewrite referer host";
set = mkOption { set = mkOption {
@ -102,7 +128,7 @@ let
}; };
hide = mkOption { hide = mkOption {
type = attrsOf bool; type = attrsOf bool;
default = { }; default = {};
}; };
}; };
redirect = { redirect = {
@ -121,7 +147,7 @@ let
}; };
}; };
config = let config = let
emitHeaders = setHeaders' != { }; emitHeaders = setHeaders' != {};
url = parseUrl config.proxyPass; url = parseUrl config.proxyPass;
upstream = nginx.upstreams'.${cfg.upstream}; upstream = nginx.upstreams'.${cfg.upstream};
upstreamServer = upstream.servers.${upstream.defaultServerName}; upstreamServer = upstream.servers.${upstream.defaultServerName};
@ -129,7 +155,10 @@ let
hasUpstream = cfg.upstream != null && !dynamicUpstream; hasUpstream = cfg.upstream != null && !dynamicUpstream;
hasUpstreamServer = upstream.defaultServerName != null; hasUpstreamServer = upstream.defaultServerName != null;
recommendedHeaders = { recommendedHeaders = {
Host = if cfg.host == null then xvars.get.proxy_hostport else cfg.host; Host =
if cfg.host == null
then xvars.get.proxy_hostport
else cfg.host;
Referer = xvars.get.referer; Referer = xvars.get.referer;
X-Real-IP = xvars.get.remote_addr; X-Real-IP = xvars.get.remote_addr;
X-Forwarded-For = xvars.get.forwarded_for; X-Forwarded-For = xvars.get.forwarded_for;
@ -137,12 +166,15 @@ let
X-Forwarded-Host = xvars.get.host; X-Forwarded-Host = xvars.get.host;
X-Forwarded-Server = xvars.get.forwarded_server; X-Forwarded-Server = xvars.get.forwarded_server;
}; };
schemePort = { schemePort =
http = 80; {
https = 443; http = 80;
}.${cfg.parsed.scheme} or (throw "unsupported proxy_scheme ${toString cfg.parsed.scheme}"); https = 443;
upstreamHost = coalesce ([ upstream.host ] ++ optional hasUpstreamServer upstreamServer.addr); }
port = coalesce [ cfg.parsed.port schemePort ]; .${cfg.parsed.scheme}
or (throw "unsupported proxy_scheme ${toString cfg.parsed.scheme}");
upstreamHost = coalesce ([upstream.host] ++ optional hasUpstreamServer upstreamServer.addr);
port = coalesce [cfg.parsed.port schemePort];
hostport = cfg.parsed.host + optionalString (port != schemePort) ":${toString port}"; hostport = cfg.parsed.host + optionalString (port != schemePort) ":${toString port}";
initProxyVars = let initProxyVars = let
initScheme = xvars.init "proxy_scheme" config.xvars.defaults.proxy_scheme; initScheme = xvars.init "proxy_scheme" config.xvars.defaults.proxy_scheme;
@ -174,8 +206,12 @@ let
${xvars.init "proxy_hostport" xvars.get.proxy_host} ${xvars.init "proxy_hostport" xvars.get.proxy_host}
} }
''; '';
init = if cfg.upstream != null then initUpstream else initDynamic; init =
in init; if cfg.upstream != null
then initUpstream
else initDynamic;
in
init;
hostHeader = coalesce [ hostHeader = coalesce [
cfg.headers.set.Host or null cfg.headers.set.Host or null
cfg.host cfg.host
@ -191,8 +227,9 @@ let
''; '';
setHeaders' = filterAttrs (_: header: header != null) cfg.headers.set; setHeaders' = filterAttrs (_: header: header != null) cfg.headers.set;
setHeaders = concatStringsSep "\n" (mapAttrsToList ( setHeaders = concatStringsSep "\n" (mapAttrsToList (
name: value: "proxy_set_header ${name} ${xvars.escapeString value};" name: value: "proxy_set_header ${name} ${xvars.escapeString value};"
) setHeaders'); )
setHeaders');
hideHeaders = mapAttrsToList (header: hide: mkIf hide "proxy_hide_header ${xvars.escapeString header};") cfg.headers.hide; hideHeaders = mapAttrsToList (header: hide: mkIf hide "proxy_hide_header ${xvars.escapeString header};") cfg.headers.hide;
in { in {
xvars = { xvars = {
@ -210,12 +247,16 @@ let
url = mkIf (cfg.inheritServerDefaults && virtualHost.proxy.url != null) (mkOptionDefault virtualHost.proxy.url); url = mkIf (cfg.inheritServerDefaults && virtualHost.proxy.url != null) (mkOptionDefault virtualHost.proxy.url);
headers = { headers = {
enableRecommended = mkOptionDefault ( enableRecommended = mkOptionDefault (
if cfg.enable && (!cfg.inheritServerDefaults || virtualHost.proxy.headers.enableRecommended != false) then true if cfg.enable && (!cfg.inheritServerDefaults || virtualHost.proxy.headers.enableRecommended != false)
else if cfg.inheritServerDefaults then virtualHost.proxy.headers.enableRecommended then true
else if nginx.recommendedProxySettings then "nixpkgs" else false else if cfg.inheritServerDefaults
then virtualHost.proxy.headers.enableRecommended
else if nginx.recommendedProxySettings
then "nixpkgs"
else false
); );
set = mkMerge [ set = mkMerge [
(mkOptionDefault { }) (mkOptionDefault {})
(mkIf (cfg.headers.enableRecommended == true) (mapOptionDefaults recommendedHeaders)) (mkIf (cfg.headers.enableRecommended == true) (mapOptionDefaults recommendedHeaders))
(mkIf (cfg.host != null) { (mkIf (cfg.host != null) {
Host = mkIf (cfg.headers.enableRecommended != "nixpkgs") (mkAlmostOptionDefault cfg.host); Host = mkIf (cfg.headers.enableRecommended != "nixpkgs") (mkAlmostOptionDefault cfg.host);
@ -230,8 +271,10 @@ let
]; ];
}; };
host = mkOptionDefault ( host = mkOptionDefault (
if cfg.inheritServerDefaults && virtualHost.proxy.host != null then virtualHost.proxy.host if cfg.inheritServerDefaults && virtualHost.proxy.host != null
else if cfg.headers.enableRecommended == false then null then virtualHost.proxy.host
else if cfg.headers.enableRecommended == false
then null
else xvars.get.host else xvars.get.host
); );
parsed = { parsed = {
@ -242,11 +285,13 @@ let
mapNullable (_: url.path) config.proxyPass mapNullable (_: url.path) config.proxyPass
); );
host = mkOptionDefault ( host = mkOptionDefault (
if hasUpstream then assert url.host == upstream.name; upstreamHost if hasUpstream
then assert url.host == upstream.name; upstreamHost
else mapNullable (_: url.host) config.proxyPass else mapNullable (_: url.host) config.proxyPass
); );
port = mkOptionDefault ( port = mkOptionDefault (
if hasUpstream && hasUpstreamServer && url.port == null then assert url.host == upstream.name; upstreamServer.port if hasUpstream && hasUpstreamServer && url.port == null
then assert url.host == upstream.name; upstreamServer.port
else mapNullable (_: url.port) config.proxyPass else mapNullable (_: url.port) config.proxyPass
); );
}; };
@ -254,15 +299,22 @@ let
proxyPass = mkIf cfg.enable (mkAlmostOptionDefault (removeSuffix "/" cfg.url + cfg.path)); proxyPass = mkIf cfg.enable (mkAlmostOptionDefault (removeSuffix "/" cfg.url + cfg.path));
recommendedProxySettings = mkAlmostOptionDefault (cfg.headers.enableRecommended == "nixpkgs"); recommendedProxySettings = mkAlmostOptionDefault (cfg.headers.enableRecommended == "nixpkgs");
extraConfig = mkIf cfg.enabled (mkMerge ([ extraConfig = mkIf cfg.enabled (mkMerge ([
(mkIf virtualHost.xvars.enable (mkJustBefore initProxyVars)) (mkIf virtualHost.xvars.enable (mkJustBefore initProxyVars))
(mkIf (cfg.headers.rewriteReferer.enable) (mkJustBefore rewriteReferer)) (mkIf (cfg.headers.rewriteReferer.enable) (mkJustBefore rewriteReferer))
(mkIf (cfg.redirect.enable) (mkBefore redirect)) (mkIf (cfg.redirect.enable) (mkBefore redirect))
(mkIf (emitHeaders) (mkJustAfter setHeaders)) (mkIf emitHeaders (mkJustAfter setHeaders))
(mkIf cfg.websocket.enable "proxy_cache_bypass $http_upgrade;") (mkIf cfg.websocket.enable "proxy_cache_bypass $http_upgrade;")
] ++ hideHeaders)); ]
++ hideHeaders));
}; };
}; };
hostModule = { config, nixosConfig, gensokyo-zone, lib, ... }: let hostModule = {
config,
nixosConfig,
gensokyo-zone,
lib,
...
}: let
inherit (gensokyo-zone.lib) mapOptionDefaults mapAlmostOptionDefaults; inherit (gensokyo-zone.lib) mapOptionDefaults mapAlmostOptionDefaults;
inherit (lib.options) mkOption mkEnableOption; inherit (lib.options) mkOption mkEnableOption;
inherit (lib.modules) mkIf mkOptionDefault; inherit (lib.modules) mkIf mkOptionDefault;
@ -288,13 +340,16 @@ let
}; };
websocket.enable = mkEnableOption "websocket proxy"; websocket.enable = mkEnableOption "websocket proxy";
headers.enableRecommended = mkOption { headers.enableRecommended = mkOption {
type = enum [ true false "nixpkgs" ]; type = enum [true false "nixpkgs"];
default = if nginx.recommendedProxySettings then "nixpkgs" else false; default =
if nginx.recommendedProxySettings
then "nixpkgs"
else false;
}; };
}; };
locations = mkOption { locations = mkOption {
type = attrsOf (submoduleWith { type = attrsOf (submoduleWith {
modules = [ locationModule ]; modules = [locationModule];
shorthandOnlyDefinesConfig = true; shorthandOnlyDefinesConfig = true;
}); });
}; };
@ -303,16 +358,18 @@ let
needsReferer = loc: loc.proxy.enabled && loc.proxy.headers.rewriteReferer.enable; needsReferer = loc: loc.proxy.enabled && loc.proxy.headers.rewriteReferer.enable;
confCopy = let confCopy = let
proxyHost = nginx.virtualHosts.${cfg.copyFromVhost}; proxyHost = nginx.virtualHosts.${cfg.copyFromVhost};
in mapAlmostOptionDefaults { in
inherit (proxyHost.proxy) host url upstream; mapAlmostOptionDefaults {
} // { inherit (proxyHost.proxy) host url upstream;
websocket = mapAlmostOptionDefaults { }
inherit (proxyHost.proxy.websocket) enable; // {
websocket = mapAlmostOptionDefaults {
inherit (proxyHost.proxy.websocket) enable;
};
headers = mapAlmostOptionDefaults {
inherit (proxyHost.proxy.headers) enableRecommended;
};
}; };
headers = mapAlmostOptionDefaults {
inherit (proxyHost.proxy.headers) enableRecommended;
};
};
in { in {
xvars = { xvars = {
parseReferer = mkIf (anyLocations needsReferer) true; parseReferer = mkIf (anyLocations needsReferer) true;
@ -326,21 +383,19 @@ let
proxy = mkIf (cfg.copyFromVhost != null) confCopy; proxy = mkIf (cfg.copyFromVhost != null) confCopy;
}; };
}; };
in { in
lib, {lib, ...}: let
... inherit (lib.options) mkOption;
}: let in {
inherit (lib.options) mkOption; options.services.nginx = with lib.types; {
in { virtualHosts = mkOption {
options.services.nginx = with lib.types; { type = attrsOf (submodule [hostModule]);
virtualHosts = mkOption { };
type = attrsOf (submodule [hostModule]); stream.servers = mkOption {
type = attrsOf (submoduleWith {
modules = [serverModule];
shorthandOnlyDefinesConfig = false;
});
};
}; };
stream.servers = mkOption { }
type = attrsOf (submoduleWith {
modules = [serverModule];
shorthandOnlyDefinesConfig = false;
});
};
};
}

View file

@ -1,5 +1,11 @@
let let
sslModule = { config, nixosConfig, gensokyo-zone, lib, ... }: let sslModule = {
config,
nixosConfig,
gensokyo-zone,
lib,
...
}: let
inherit (lib.options) mkOption; inherit (lib.options) mkOption;
inherit (lib.modules) mkIf mkMerge mkDefault mkOptionDefault; inherit (lib.modules) mkIf mkMerge mkDefault mkOptionDefault;
inherit (nixosConfig.services) nginx; inherit (nixosConfig.services) nginx;
@ -12,7 +18,7 @@ let
}; };
force = mkOption { force = mkOption {
# TODO: "force-nonlocal"? exceptions for tailscale? # TODO: "force-nonlocal"? exceptions for tailscale?
type = enum [ false true "only" "reject" ]; type = enum [false true "only" "reject"];
default = false; default = false;
}; };
forced = mkOption { forced = mkOption {
@ -60,14 +66,19 @@ let
}; };
copyCertVhost = mkCopyCert nginx.virtualHosts.${cfg.cert.copyFromVhost}.ssl.cert; copyCertVhost = mkCopyCert nginx.virtualHosts.${cfg.cert.copyFromVhost}.ssl.cert;
copyCertStreamServer = mkCopyCert nginx.stream.servers.${cfg.cert.copyFromStreamServer}.ssl.cert; copyCertStreamServer = mkCopyCert nginx.stream.servers.${cfg.cert.copyFromStreamServer}.ssl.cert;
in mkMerge [ in
(mkIf (cfg.cert.copyFromStreamServer != null) copyCertStreamServer) mkMerge [
(mkIf (cfg.cert.copyFromVhost != null) copyCertVhost) (mkIf (cfg.cert.copyFromStreamServer != null) copyCertStreamServer)
]; (mkIf (cfg.cert.copyFromVhost != null) copyCertVhost)
];
}; };
}; };
}; };
sslProxyModule = { config, lib, ... }: let sslProxyModule = {
config,
lib,
...
}: let
inherit (lib.options) mkOption mkEnableOption; inherit (lib.options) mkOption mkEnableOption;
inherit (lib.modules) mkIf mkMerge mkAfter; inherit (lib.modules) mkIf mkMerge mkAfter;
inherit (config) proxy; inherit (config) proxy;
@ -78,9 +89,11 @@ let
type = bool; type = bool;
}; };
verify = mkEnableOption "proxy_ssl_verify"; verify = mkEnableOption "proxy_ssl_verify";
sni = mkEnableOption "proxy_ssl_server_name" // { sni =
default = cfg.host != null; mkEnableOption "proxy_ssl_server_name"
}; // {
default = cfg.host != null;
};
host = mkOption { host = mkOption {
type = nullOr str; type = nullOr str;
default = null; default = null;
@ -97,18 +110,26 @@ let
]); ]);
}; };
}; };
streamServerModule = { config, nixosConfig, gensokyo-zone, lib, ... }: let streamServerModule = {
config,
nixosConfig,
gensokyo-zone,
lib,
...
}: let
inherit (gensokyo-zone.lib) mkAlmostDefault; inherit (gensokyo-zone.lib) mkAlmostDefault;
inherit (lib.options) mkEnableOption; inherit (lib.options) mkEnableOption;
inherit (lib.modules) mkIf mkMerge mkOptionDefault; inherit (lib.modules) mkIf mkMerge mkOptionDefault;
cfg = config.ssl; cfg = config.ssl;
in { in {
imports = [ sslModule sslProxyModule ]; imports = [sslModule sslProxyModule];
options = with lib.types; { options = with lib.types; {
ssl = { ssl = {
kTLS = mkEnableOption "kTLS support" // { kTLS =
default = true; mkEnableOption "kTLS support"
}; // {
default = true;
};
}; };
}; };
config = let config = let
@ -126,104 +147,124 @@ let
(mkIf cfg.kTLS "ssl_conf_command Options KTLS;") (mkIf cfg.kTLS "ssl_conf_command Options KTLS;")
]; ];
confProxy.extraConfig = mkIf proxy.ssl.enable "proxy_ssl on;"; confProxy.extraConfig = mkIf proxy.ssl.enable "proxy_ssl on;";
in mkMerge [ in
conf mkMerge [
(mkIf cfg.enable confSsl) conf
(mkIf proxy.enable confProxy) (mkIf cfg.enable confSsl)
]; (mkIf proxy.enable confProxy)
];
}; };
in { in
config, {
gensokyo-zone, config,
lib, gensokyo-zone,
... lib,
}: let ...
inherit (gensokyo-zone.lib) mkAlmostOptionDefault; }: let
inherit (lib.options) mkOption mkEnableOption; inherit (gensokyo-zone.lib) mkAlmostOptionDefault;
inherit (lib.modules) mkIf mkMerge mkDefault mkOptionDefault; inherit (lib.options) mkOption mkEnableOption;
inherit (lib.attrsets) mapAttrsToList; inherit (lib.modules) mkIf mkMerge mkDefault mkOptionDefault;
inherit (lib.trivial) warnIf; inherit (lib.attrsets) mapAttrsToList;
inherit (lib.strings) hasPrefix; inherit (lib.trivial) warnIf;
inherit (config.services) nginx; inherit (lib.strings) hasPrefix;
forceRedirectConfig = { virtualHost, xvars }: '' inherit (config.services) nginx;
if (${xvars.get.scheme} = http) { forceRedirectConfig = {
return ${toString virtualHost.redirectCode} https://${xvars.get.host}$request_uri; virtualHost,
} xvars,
''; }: ''
locationModule = { config, virtualHost, xvars, ... }: let if (${xvars.get.scheme} = http) {
cfg = config.ssl; return ${toString virtualHost.redirectCode} https://${xvars.get.host}$request_uri;
emitForce = cfg.force && !virtualHost.ssl.forced; }
in { '';
imports = [ sslProxyModule ]; locationModule = {
options.ssl = { config,
force = mkEnableOption "redirect to SSL"; virtualHost,
xvars,
...
}: let
cfg = config.ssl;
emitForce = cfg.force && !virtualHost.ssl.forced;
in {
imports = [sslProxyModule];
options.ssl = {
force = mkEnableOption "redirect to SSL";
};
config = {
proxy.ssl.enable = mkOptionDefault (hasPrefix "https://" config.proxyPass);
xvars.enable = mkIf emitForce true;
extraConfig = mkIf emitForce (forceRedirectConfig {inherit xvars virtualHost;});
};
}; };
config = { hostModule = {
proxy.ssl.enable = mkOptionDefault (hasPrefix "https://" config.proxyPass); config,
xvars.enable = mkIf emitForce true; xvars,
extraConfig = mkIf emitForce (forceRedirectConfig { inherit xvars virtualHost; }); ...
}; }: let
}; cfg = config.ssl;
hostModule = { config, xvars, ... }: let emitForce = cfg.forced && config.proxied.enabled;
cfg = config.ssl; in {
emitForce = cfg.forced && config.proxied.enabled; imports = [sslModule];
in { options = with lib.types; {
imports = [ sslModule ]; ssl = {
options = with lib.types; { cert = {
ssl = { enable = mkEnableOption "ssl cert via name.shortServer";
cert = { };
enable = mkEnableOption "ssl cert via name.shortServer"; };
locations = mkOption {
type = attrsOf (submoduleWith {
modules = [locationModule];
shorthandOnlyDefinesConfig = true;
});
}; };
}; };
locations = mkOption { config = {
type = attrsOf (submoduleWith { ssl = {
modules = [ locationModule ]; cert = let
shorthandOnlyDefinesConfig = true; certConfig.name = mkIf cfg.cert.enable (warnIf (config.name.shortServer == null) "ssl.cert.enable set but name.shortServer is null" (
mkAlmostOptionDefault config.name.shortServer
));
in
certConfig;
};
addSSL = mkIf (cfg.enable && (cfg.force == false || emitForce)) (mkDefault true);
forceSSL = mkIf (cfg.enable && cfg.force == true && !emitForce) (mkDefault true);
onlySSL = mkIf (cfg.enable && cfg.force == "only" && !emitForce) (mkDefault true);
rejectSSL = mkIf (cfg.force == "reject") (mkDefault true);
useACMEHost = mkAlmostOptionDefault cfg.cert.name;
sslCertificate = mkIf (cfg.cert.path != null) (mkAlmostOptionDefault cfg.cert.path);
sslCertificateKey = mkIf (cfg.cert.keyPath != null) (mkAlmostOptionDefault cfg.cert.keyPath);
kTLS = mkAlmostOptionDefault true;
xvars.enable = mkIf emitForce true;
extraConfig = mkIf emitForce (forceRedirectConfig {
virtualHost = config;
inherit xvars;
}); });
}; };
}; };
config = { in {
ssl = { options.services.nginx = with lib.types; {
cert = let virtualHosts = mkOption {
certConfig.name = mkIf cfg.cert.enable (warnIf (config.name.shortServer == null) "ssl.cert.enable set but name.shortServer is null" ( type = attrsOf (submoduleWith {
mkAlmostOptionDefault config.name.shortServer modules = [hostModule];
)); shorthandOnlyDefinesConfig = true;
in certConfig; });
};
stream.servers = mkOption {
type = attrsOf (submoduleWith {
modules = [streamServerModule];
shorthandOnlyDefinesConfig = false;
});
}; };
addSSL = mkIf (cfg.enable && (cfg.force == false || emitForce)) (mkDefault true);
forceSSL = mkIf (cfg.enable && cfg.force == true && !emitForce) (mkDefault true);
onlySSL = mkIf (cfg.enable && cfg.force == "only" && !emitForce) (mkDefault true);
rejectSSL = mkIf (cfg.force == "reject") (mkDefault true);
useACMEHost = mkAlmostOptionDefault cfg.cert.name;
sslCertificate = mkIf (cfg.cert.path != null) (mkAlmostOptionDefault cfg.cert.path);
sslCertificateKey = mkIf (cfg.cert.keyPath != null) (mkAlmostOptionDefault cfg.cert.keyPath);
kTLS = mkAlmostOptionDefault true;
xvars.enable = mkIf emitForce true;
extraConfig = mkIf emitForce (forceRedirectConfig { virtualHost = config; inherit xvars; });
}; };
}; config.systemd.services.nginx = let
in { mapStreamServer = server:
options.services.nginx = with lib.types; { mkIf (server.enable && server.ssl.enable && server.ssl.cert.name != null) {
virtualHosts = mkOption { wants = ["acme-finished-${server.ssl.cert.name}.target"];
type = attrsOf (submoduleWith { after = ["acme-selfsigned-${server.ssl.cert.name}.service"];
modules = [ hostModule ]; before = ["acme-${server.ssl.cert.name}.service"];
shorthandOnlyDefinesConfig = true; };
}); streamServerCerts = mapAttrsToList (_: mapStreamServer) nginx.stream.servers;
}; in
stream.servers = mkOption { mkIf nginx.enable (mkMerge streamServerCerts);
type = attrsOf (submoduleWith { }
modules = [ streamServerModule ];
shorthandOnlyDefinesConfig = false;
});
};
};
config.systemd.services.nginx = let
mapStreamServer = server: mkIf (server.enable && server.ssl.enable && server.ssl.cert.name != null) {
wants = [ "acme-finished-${server.ssl.cert.name}.target" ];
after = [ "acme-selfsigned-${server.ssl.cert.name}.service" ];
before = [ "acme-${server.ssl.cert.name}.service" ];
};
streamServerCerts = mapAttrsToList (_: mapStreamServer) nginx.stream.servers;
in mkIf nginx.enable (mkMerge streamServerCerts);
}

View file

@ -10,9 +10,11 @@
cfg = config.services.nginx.stream; cfg = config.services.nginx.stream;
serverModule = {config, ...}: { serverModule = {config, ...}: {
options = with lib.types; { options = with lib.types; {
enable = mkEnableOption "stream server block" // { enable =
default = true; mkEnableOption "stream server block"
}; // {
default = true;
};
extraConfig = mkOption { extraConfig = mkOption {
type = lines; type = lines;
default = ""; default = "";
@ -49,7 +51,7 @@ in {
nixosConfig = config; nixosConfig = config;
}; };
}); });
default = { }; default = {};
}; };
}; };
config.services.nginx = { config.services.nginx = {

View file

@ -1,5 +1,13 @@
let let
upstreamServerAccessModule = {config, nixosConfig, name, gensokyo-zone, lib, upstreamKind, ...}: let upstreamServerAccessModule = {
config,
nixosConfig,
name,
gensokyo-zone,
lib,
upstreamKind,
...
}: let
inherit (lib.options) mkOption; inherit (lib.options) mkOption;
inherit (lib.modules) mkIf mkMerge mkOptionDefault; inherit (lib.modules) mkIf mkMerge mkOptionDefault;
inherit (gensokyo-zone.lib) mkAlmostOptionDefault; inherit (gensokyo-zone.lib) mkAlmostOptionDefault;
@ -57,12 +65,20 @@ let
port = mkOptionDefault port.port; port = mkOptionDefault port.port;
ssl.enable = mkIf port.ssl (mkAlmostOptionDefault true); ssl.enable = mkIf port.ssl (mkAlmostOptionDefault true);
}; };
in mkMerge [ in
confAccess mkMerge [
(mkIf cfg.enable conf) confAccess
]; (mkIf cfg.enable conf)
];
}; };
upstreamServerModule = {config, name, gensokyo-zone, lib, upstreamKind, ...}: let upstreamServerModule = {
config,
name,
gensokyo-zone,
lib,
upstreamKind,
...
}: let
inherit (gensokyo-zone.lib) mkAddress6; inherit (gensokyo-zone.lib) mkAddress6;
inherit (lib.options) mkOption mkEnableOption; inherit (lib.options) mkOption mkEnableOption;
inherit (lib.modules) mkIf mkMerge mkBefore mkOptionDefault; inherit (lib.modules) mkIf mkMerge mkBefore mkOptionDefault;
@ -72,9 +88,11 @@ let
inherit (lib.trivial) isBool; inherit (lib.trivial) isBool;
in { in {
options = with lib.types; { options = with lib.types; {
enable = mkEnableOption "upstream server" // { enable =
default = true; mkEnableOption "upstream server"
}; // {
default = true;
};
addr = mkOption { addr = mkOption {
type = str; type = str;
default = name; default = name;
@ -90,8 +108,8 @@ let
example = "unix:/tmp/backend3"; example = "unix:/tmp/backend3";
}; };
settings = mkOption { settings = mkOption {
type = attrsOf (oneOf [ int str bool ]); type = attrsOf (oneOf [int str bool]);
default = { }; default = {};
}; };
extraConfig = mkOption { extraConfig = mkOption {
type = str; type = str;
@ -108,21 +126,30 @@ let
}; };
config = let config = let
mapSetting = key: value: mapSetting = key: value:
if isBool value then mkIf value key if isBool value
then mkIf value key
else "${key}=${toString value}"; else "${key}=${toString value}";
settings = mapAttrsToList mapSetting config.settings; settings = mapAttrsToList mapSetting config.settings;
port = optionalString (config.port != null) ":${toString config.port}"; port = optionalString (config.port != null) ":${toString config.port}";
in { in {
server = mkOptionDefault "${mkAddress6 config.addr}${port}"; server = mkOptionDefault "${mkAddress6 config.addr}${port}";
serverConfig = mkMerge ( serverConfig = mkMerge (
[ (mkBefore config.server) ] [(mkBefore config.server)]
++ settings ++ settings
++ optional (config.extraConfig != "") config.extraConfig ++ optional (config.extraConfig != "") config.extraConfig
); );
serverDirective = mkOptionDefault "server ${config.serverConfig};"; serverDirective = mkOptionDefault "server ${config.serverConfig};";
}; };
}; };
upstreamModule = {config, name, nixosConfig, gensokyo-zone, lib, upstreamKind, ...}: let upstreamModule = {
config,
name,
nixosConfig,
gensokyo-zone,
lib,
upstreamKind,
...
}: let
inherit (gensokyo-zone.lib) mkAlmostOptionDefault unmerged; inherit (gensokyo-zone.lib) mkAlmostOptionDefault unmerged;
inherit (lib.options) mkOption mkEnableOption; inherit (lib.options) mkOption mkEnableOption;
inherit (lib.modules) mkIf mkMerge mkOptionDefault; inherit (lib.modules) mkIf mkMerge mkOptionDefault;
@ -132,19 +159,21 @@ let
in { in {
options = with lib.types; let options = with lib.types; let
upstreamServer = submoduleWith { upstreamServer = submoduleWith {
modules = [ upstreamServerModule upstreamServerAccessModule ]; modules = [upstreamServerModule upstreamServerAccessModule];
specialArgs = { specialArgs = {
inherit nixosConfig gensokyo-zone upstreamKind; inherit nixosConfig gensokyo-zone upstreamKind;
upstream = config; upstream = config;
}; };
}; };
in { in {
enable = mkEnableOption "upstream block" // { enable =
default = true; mkEnableOption "upstream block"
}; // {
default = true;
};
name = mkOption { name = mkOption {
type = str; type = str;
default = replaceStrings [ "'" ] [ "_" ] name; default = replaceStrings ["'"] ["_"] name;
}; };
servers = mkOption { servers = mkOption {
type = attrsOf upstreamServer; type = attrsOf upstreamServer;
@ -183,13 +212,13 @@ let
config = let config = let
enabledServers = filterAttrs (_: server: server.enable) config.servers; enabledServers = filterAttrs (_: server: server.enable) config.servers;
assertServers = v: assert enabledServers != { }; v; assertServers = v: assert enabledServers != {}; v;
in { in {
ssl.enable = mkIf (any (server: server.ssl.enable) (attrValues enabledServers)) (mkAlmostOptionDefault true); ssl.enable = mkIf (any (server: server.ssl.enable) (attrValues enabledServers)) (mkAlmostOptionDefault true);
defaultServerName = findSingle (_: true) null null (attrNames enabledServers); defaultServerName = findSingle (_: true) null null (attrNames enabledServers);
upstreamConfig = mkMerge ( upstreamConfig = mkMerge (
mapAttrsToList (_: server: mkIf server.enable server.serverDirective) config.servers mapAttrsToList (_: server: mkIf server.enable server.serverDirective) config.servers
++ [ config.extraConfig ] ++ [config.extraConfig]
); );
upstreamBlock = mkOptionDefault '' upstreamBlock = mkOptionDefault ''
upstream ${config.name} { upstream ${config.name} {
@ -199,16 +228,28 @@ let
upstreamSettings = assertServers (mkOptionDefault { upstreamSettings = assertServers (mkOptionDefault {
#extraConfig = config.upstreamConfig; #extraConfig = config.upstreamConfig;
extraConfig = config.extraConfig; extraConfig = config.extraConfig;
servers = mapAttrs' (name: server: nameValuePair (if server.enable then server.server else "disabled_${name}") (mkIf server.enable (mkMerge [ servers = mapAttrs' (name: server:
server.settings nameValuePair (
(mkIf (server.extraConfig != "") { if server.enable
${config.extraConfig} = true; then server.server
}) else "disabled_${name}"
]))) config.servers; ) (mkIf server.enable (mkMerge [
server.settings
(mkIf (server.extraConfig != "") {
${config.extraConfig} = true;
})
])))
config.servers;
}); });
}; };
}; };
serverModule = {config, nixosConfig, gensokyo-zone, lib, ...}: let serverModule = {
config,
nixosConfig,
gensokyo-zone,
lib,
...
}: let
inherit (gensokyo-zone.lib) mkAlmostOptionDefault; inherit (gensokyo-zone.lib) mkAlmostOptionDefault;
inherit (lib.options) mkOption; inherit (lib.options) mkOption;
inherit (lib.modules) mkIf; inherit (lib.modules) mkIf;
@ -229,7 +270,8 @@ let
dynamicUpstream = hasPrefix "$" config.proxy.upstream; dynamicUpstream = hasPrefix "$" config.proxy.upstream;
hasUpstream = config.proxy.upstream != null && !dynamicUpstream; hasUpstream = config.proxy.upstream != null && !dynamicUpstream;
proxyPass = proxyPass =
if dynamicUpstream then config.proxy.upstream if dynamicUpstream
then config.proxy.upstream
else assert proxyUpstream.enable; proxyUpstream.name; else assert proxyUpstream.enable; proxyUpstream.name;
in { in {
proxy = { proxy = {
@ -242,7 +284,12 @@ let
}; };
}; };
}; };
proxyUpstreamModule = {config, nixosConfig, lib, ...}: let proxyUpstreamModule = {
config,
nixosConfig,
lib,
...
}: let
inherit (lib.options) mkOption; inherit (lib.options) mkOption;
in { in {
options = with lib.types; { options = with lib.types; {
@ -253,42 +300,63 @@ let
}; };
}; };
}; };
locationModule = {config, nixosConfig, virtualHost, gensokyo-zone, lib, ...}: let locationModule = {
config,
nixosConfig,
virtualHost,
gensokyo-zone,
lib,
...
}: let
inherit (gensokyo-zone.lib) mkAlmostOptionDefault; inherit (gensokyo-zone.lib) mkAlmostOptionDefault;
inherit (lib.modules) mkIf mkOptionDefault; inherit (lib.modules) mkIf mkOptionDefault;
inherit (lib.strings) hasPrefix; inherit (lib.strings) hasPrefix;
inherit (nixosConfig.services) nginx; inherit (nixosConfig.services) nginx;
in { in {
imports = [ proxyUpstreamModule ]; imports = [proxyUpstreamModule];
config = let config = let
proxyUpstream = nginx.upstreams'.${config.proxy.upstream}; proxyUpstream = nginx.upstreams'.${config.proxy.upstream};
proxyScheme = if config.proxy.ssl.enable then "https" else "http"; proxyScheme =
if config.proxy.ssl.enable
then "https"
else "http";
dynamicUpstream = hasPrefix "$" config.proxy.upstream; dynamicUpstream = hasPrefix "$" config.proxy.upstream;
hasUpstream = config.proxy.upstream != null && !dynamicUpstream; hasUpstream = config.proxy.upstream != null && !dynamicUpstream;
proxyHost = proxyHost =
if dynamicUpstream then config.proxy.upstream if dynamicUpstream
then config.proxy.upstream
else assert proxyUpstream.enable; proxyUpstream.name; else assert proxyUpstream.enable; proxyUpstream.name;
in { in {
proxy = { proxy = {
upstream = mkOptionDefault virtualHost.proxy.upstream; upstream = mkOptionDefault virtualHost.proxy.upstream;
enable = mkIf (config.proxy.upstream != null && virtualHost.proxy.upstream == null) true; enable = mkIf (config.proxy.upstream != null && virtualHost.proxy.upstream == null) true;
url = mkIf (config.proxy.upstream != null) (mkAlmostOptionDefault url = mkIf (config.proxy.upstream != null) (
mkAlmostOptionDefault
"${proxyScheme}://${proxyHost}" "${proxyScheme}://${proxyHost}"
); );
ssl = { ssl = {
enable = mkAlmostOptionDefault (if hasUpstream then proxyUpstream.ssl.enable else false); enable = mkAlmostOptionDefault (
if hasUpstream
then proxyUpstream.ssl.enable
else false
);
host = mkIf hasUpstream (mkAlmostOptionDefault proxyUpstream.ssl.host); host = mkIf hasUpstream (mkAlmostOptionDefault proxyUpstream.ssl.host);
}; };
host = mkIf (hasUpstream && proxyUpstream.host != null) (mkAlmostOptionDefault proxyUpstream.host); host = mkIf (hasUpstream && proxyUpstream.host != null) (mkAlmostOptionDefault proxyUpstream.host);
}; };
}; };
}; };
hostModule = {config, nixosConfig, lib, ...}: let hostModule = {
config,
nixosConfig,
lib,
...
}: let
inherit (lib.options) mkOption; inherit (lib.options) mkOption;
inherit (lib.modules) mkOptionDefault; inherit (lib.modules) mkOptionDefault;
in { in {
imports = [ proxyUpstreamModule ]; imports = [proxyUpstreamModule];
options = with lib.types; { options = with lib.types; {
locations = mkOption { locations = mkOption {
@ -302,68 +370,76 @@ let
}; };
}; };
}; };
in { in
config, {
lib, config,
gensokyo-zone, lib,
... gensokyo-zone,
}: let ...
inherit (gensokyo-zone.lib) unmerged; }: let
inherit (lib.options) mkOption; inherit (gensokyo-zone.lib) unmerged;
inherit (lib.modules) mkIf mkMerge; inherit (lib.options) mkOption;
inherit (lib.attrsets) mapAttrsToList; inherit (lib.modules) mkIf mkMerge;
cfg = config.services.nginx; inherit (lib.attrsets) mapAttrsToList;
in { cfg = config.services.nginx;
options.services.nginx = with lib.types; { in {
upstreams' = mkOption { options.services.nginx = with lib.types; {
type = attrsOf (submoduleWith { upstreams' = mkOption {
modules = [upstreamModule];
shorthandOnlyDefinesConfig = false;
specialArgs = {
inherit gensokyo-zone;
nixosConfig = config;
upstreamKind = "virtualHost";
};
});
default = { };
};
virtualHosts = mkOption {
type = attrsOf (submodule hostModule);
};
stream = {
upstreams = mkOption {
type = attrsOf (submoduleWith { type = attrsOf (submoduleWith {
modules = [upstreamModule]; modules = [upstreamModule];
shorthandOnlyDefinesConfig = false; shorthandOnlyDefinesConfig = false;
specialArgs = { specialArgs = {
inherit gensokyo-zone; inherit gensokyo-zone;
nixosConfig = config; nixosConfig = config;
upstreamKind = "stream"; upstreamKind = "virtualHost";
}; };
}); });
default = { }; default = {};
}; };
servers = mkOption { virtualHosts = mkOption {
type = attrsOf (submoduleWith { type = attrsOf (submodule hostModule);
modules = [serverModule]; };
shorthandOnlyDefinesConfig = false; stream = {
}); upstreams = mkOption {
type = attrsOf (submoduleWith {
modules = [upstreamModule];
shorthandOnlyDefinesConfig = false;
specialArgs = {
inherit gensokyo-zone;
nixosConfig = config;
upstreamKind = "stream";
};
});
default = {};
};
servers = mkOption {
type = attrsOf (submoduleWith {
modules = [serverModule];
shorthandOnlyDefinesConfig = false;
});
};
}; };
}; };
}; config.services.nginx = let
config.services.nginx = let confStream.streamConfig = mkMerge (
confStream.streamConfig = mkMerge ( mapAttrsToList (_: upstream: mkIf upstream.enable upstream.upstreamBlock) cfg.stream.upstreams
mapAttrsToList (_: upstream: mkIf upstream.enable upstream.upstreamBlock) cfg.stream.upstreams );
); useUpstreams = true;
useUpstreams = true; confUpstreams.upstreams = mkMerge (mapAttrsToList (_: upstream:
confUpstreams.upstreams = mkMerge (mapAttrsToList (_: upstream: mkIf upstream.enable { mkIf upstream.enable {
${upstream.name} = unmerged.mergeAttrs upstream.upstreamSettings; ${upstream.name} = unmerged.mergeAttrs upstream.upstreamSettings;
}) cfg.upstreams'); })
confBlock.commonHttpConfig = mkMerge ( cfg.upstreams');
mapAttrsToList (_: upstream: mkIf upstream.enable upstream.upstreamBlock) cfg.upstreams' confBlock.commonHttpConfig = mkMerge (
); mapAttrsToList (_: upstream: mkIf upstream.enable upstream.upstreamBlock) cfg.upstreams'
in mkMerge [ );
confStream in
(if useUpstreams then confUpstreams else confBlock) mkMerge [
]; confStream
} (
if useUpstreams
then confUpstreams
else confBlock
)
];
}

View file

@ -13,7 +13,12 @@
inherit (config) networking; inherit (config) networking;
inherit (config.services) vouch-proxy nginx tailscale; inherit (config.services) vouch-proxy nginx tailscale;
inherit (nginx) vouch; inherit (nginx) vouch;
locationModule = {config, virtualHost, xvars, ...}: { locationModule = {
config,
virtualHost,
xvars,
...
}: {
options.vouch = with lib.types; { options.vouch = with lib.types; {
requireAuth = mkEnableOption "require auth to access this location"; requireAuth = mkEnableOption "require auth to access this location";
setProxyHeader = mkOption { setProxyHeader = mkOption {
@ -26,29 +31,35 @@
enableVouchLocal = virtualHost.vouch.localSso.enable; enableVouchLocal = virtualHost.vouch.localSso.enable;
enableVouchTail = enableVouchLocal && tailscale.enable && false; enableVouchTail = enableVouchLocal && tailscale.enable && false;
allowOrigin = url: "add_header Access-Control-Allow-Origin ${url};"; allowOrigin = url: "add_header Access-Control-Allow-Origin ${url};";
in mkIf config.vouch.requireAuth { in
lua = mkIf virtualHost.vouch.auth.lua.enable { mkIf config.vouch.requireAuth {
access.block = mkMerge [ lua = mkIf virtualHost.vouch.auth.lua.enable {
(mkBefore virtualHost.vouch.auth.lua.accessRequest) access.block = mkMerge [
(mkBefore virtualHost.vouch.auth.lua.accessVariables) (mkBefore virtualHost.vouch.auth.lua.accessRequest)
(mkBefore virtualHost.vouch.auth.lua.accessLogic) (mkBefore virtualHost.vouch.auth.lua.accessVariables)
]; (mkBefore virtualHost.vouch.auth.lua.accessLogic)
];
};
xvars.enable = mkIf (enableVouchTail || virtualHost.vouch.auth.lua.enable) true;
proxy.headers.set.X-Vouch-User = mkOptionDefault "$auth_resp_x_vouch_user";
extraConfig = assert virtualHost.vouch.enable;
mkMerge [
(mkIf (!virtualHost.vouch.requireAuth) virtualHost.vouch.auth.requestDirective)
(allowOrigin vouch.url)
(allowOrigin vouch.authUrl)
(mkIf enableVouchLocal (allowOrigin vouch.localUrl))
(mkIf enableVouchLocal (allowOrigin "sso.local.${networking.domain}"))
(mkIf enableVouchTail (allowOrigin "${xvars.get.scheme}://${vouch.tailDomain}"))
];
}; };
xvars.enable = mkIf (enableVouchTail || virtualHost.vouch.auth.lua.enable) true;
proxy.headers.set.X-Vouch-User = mkOptionDefault "$auth_resp_x_vouch_user";
extraConfig = assert virtualHost.vouch.enable; mkMerge [
(mkIf (!virtualHost.vouch.requireAuth) virtualHost.vouch.auth.requestDirective)
(allowOrigin vouch.url)
(allowOrigin vouch.authUrl)
(mkIf enableVouchLocal (allowOrigin vouch.localUrl))
(mkIf enableVouchLocal (allowOrigin "sso.local.${networking.domain}"))
(mkIf enableVouchTail (allowOrigin "${xvars.get.scheme}://${vouch.tailDomain}"))
];
};
}; };
hostModule = {config, xvars, ...}: let hostModule = {
config,
xvars,
...
}: let
cfg = config.vouch; cfg = config.vouch;
mkHeaderVar = header: toLower (replaceStrings [ "-" ] [ "_" ] header); mkHeaderVar = header: toLower (replaceStrings ["-"] ["_"] header);
mkUpstreamVar = header: "\$upstream_http_${mkHeaderVar header}"; mkUpstreamVar = header: "\$upstream_http_${mkHeaderVar header}";
in { in {
options = with lib.types; { options = with lib.types; {
@ -57,12 +68,16 @@
}; };
vouch = { vouch = {
enable = mkEnableOption "vouch auth proxy"; enable = mkEnableOption "vouch auth proxy";
localSso.enable = mkEnableOption "lan-local vouch" // { localSso.enable =
default = vouch.localSso.enable && config.local.enable; mkEnableOption "lan-local vouch"
}; // {
requireAuth = mkEnableOption "require auth to access this host" // { default = vouch.localSso.enable && config.local.enable;
default = true; };
}; requireAuth =
mkEnableOption "require auth to access this host"
// {
default = true;
};
auth = { auth = {
lua = { lua = {
enable = mkEnableOption "lua"; enable = mkEnableOption "lua";
@ -129,9 +144,12 @@
return ngx.exit(ngx.HTTP_INTERNAL_SERVER_ERROR) return ngx.exit(ngx.HTTP_INTERNAL_SERVER_ERROR)
end end
''); '');
accessVariables = mkMerge (mapAttrsToList (authVar: header: mkOptionDefault accessVariables = mkMerge (mapAttrsToList (
''ngx.var["${authVar}"] = ngx.ctx.auth_res.header["${header}"] or ""'' authVar: header:
) cfg.auth.variables); mkOptionDefault
''ngx.var["${authVar}"] = ngx.ctx.auth_res.header["${header}"] or ""''
)
cfg.auth.variables);
}; };
errorLocation = mkIf cfg.auth.lua.enable (mkAlmostOptionDefault null); errorLocation = mkIf cfg.auth.lua.enable (mkAlmostOptionDefault null);
requestDirective = mkIf cfg.auth.lua.enable (mkAlmostOptionDefault ""); requestDirective = mkIf cfg.auth.lua.enable (mkAlmostOptionDefault "");
@ -161,15 +179,19 @@
(mkIf cfg.localSso.enable localVouchUrl) (mkIf cfg.localSso.enable localVouchUrl)
(mkIf (cfg.localSso.enable && tailscale.enable) tailVouchUrl) (mkIf (cfg.localSso.enable && tailscale.enable) tailVouchUrl)
]; ];
in mkIf cfg.enable (mkMerge ( in
[ mkIf cfg.enable (mkMerge (
(mkIf (cfg.requireAuth) (mkBefore cfg.auth.requestDirective)) [
(mkIf (cfg.auth.errorLocation != null) "error_page 401 = ${cfg.auth.errorLocation};") (mkIf (cfg.requireAuth) (mkBefore cfg.auth.requestDirective))
] ++ setVouchUrl (mkIf (cfg.auth.errorLocation != null) "error_page 401 = ${cfg.auth.errorLocation};")
++ mapAttrsToList (authVar: header: mkIf (!cfg.auth.lua.enable) ( ]
mkBefore "auth_request_set \$${authVar} ${mkUpstreamVar header};" ++ setVouchUrl
)) cfg.auth.variables ++ mapAttrsToList (authVar: header:
)); mkIf (!cfg.auth.lua.enable) (
mkBefore "auth_request_set \$${authVar} ${mkUpstreamVar header};"
))
cfg.auth.variables
));
xvars.enable = mkIf cfg.enable true; xvars.enable = mkIf cfg.enable true;
locations = mkIf cfg.enable { locations = mkIf cfg.enable {
"/" = mkIf cfg.requireAuth { "/" = mkIf cfg.requireAuth {
@ -181,18 +203,30 @@
return 302 $vouch_url/login?url=${xvars.get.scheme}://${xvars.get.host}$request_uri&X-Vouch-Token=$auth_resp_jwt&error=$auth_resp_err; return 302 $vouch_url/login?url=${xvars.get.scheme}://${xvars.get.host}$request_uri&X-Vouch-Token=$auth_resp_jwt&error=$auth_resp_err;
''; '';
}; };
${cfg.auth.requestLocation} = { config, xvars, ... }: { ${cfg.auth.requestLocation} = {
config,
xvars,
...
}: {
proxy = { proxy = {
enable = true; enable = true;
inheritServerDefaults = false; inheritServerDefaults = false;
upstream = mkDefault ( upstream = mkDefault (
if vouch.doubleProxy.enable then "vouch'proxy" if vouch.doubleProxy.enable
else if cfg.localSso.enable then "vouch'auth'local" then "vouch'proxy"
else if cfg.localSso.enable
then "vouch'auth'local"
else "vouch'auth" else "vouch'auth"
); );
# nginx-proxied vouch must use X-Forwarded-Host, but vanilla vouch requires Host # nginx-proxied vouch must use X-Forwarded-Host, but vanilla vouch requires Host
host = if config.proxy.upstream == "vouch'proxy" host =
then (if cfg.localSso.enable then vouch.doubleProxy.localServerName else vouch.doubleProxy.serverName) if config.proxy.upstream == "vouch'proxy"
then
(
if cfg.localSso.enable
then vouch.doubleProxy.localServerName
else vouch.doubleProxy.serverName
)
else xvars.get.host; else xvars.get.host;
headers = { headers = {
set.Content-Length = ""; set.Content-Length = "";
@ -212,9 +246,11 @@ in {
vouch = { vouch = {
enable = mkEnableOption "vouch auth proxy"; enable = mkEnableOption "vouch auth proxy";
localSso = { localSso = {
enable = mkEnableOption "lan-local auth" // { enable =
default = true; mkEnableOption "lan-local auth"
}; // {
default = true;
};
}; };
doubleProxy = { doubleProxy = {
enable = mkOption { enable = mkOption {
@ -271,7 +307,7 @@ in {
enable = vouch.enable; enable = vouch.enable;
servers = { servers = {
local = localVouch; local = localVouch;
service = { upstream, ... }: { service = {upstream, ...}: {
enable = mkIf upstream.servers.local.enable false; enable = mkIf upstream.servers.local.enable false;
accessService = { accessService = {
name = "vouch-proxy"; name = "vouch-proxy";
@ -283,10 +319,12 @@ in {
vouch'auth'local = { vouch'auth'local = {
enable = vouch.enable && vouch.localSso.enable; enable = vouch.enable && vouch.localSso.enable;
servers = { servers = {
local = localVouch // { local =
enable = mkAlmostOptionDefault false; localVouch
}; // {
service = { upstream, ... }: { enable = mkAlmostOptionDefault false;
};
service = {upstream, ...}: {
enable = mkIf upstream.servers.local.enable false; enable = mkIf upstream.servers.local.enable false;
accessService = { accessService = {
name = "vouch-proxy"; name = "vouch-proxy";
@ -299,18 +337,18 @@ in {
enable = vouch.enable && vouch.doubleProxy.enable; enable = vouch.enable && vouch.doubleProxy.enable;
# TODO: need exported hosts options for this to detect the correct host/port/etc # TODO: need exported hosts options for this to detect the correct host/port/etc
servers = { servers = {
lan = { upstream, ... }: { lan = {upstream, ...}: {
enable = mkAlmostOptionDefault (!upstream.servers.int.enable); enable = mkAlmostOptionDefault (!upstream.servers.int.enable);
addr = mkAlmostOptionDefault "login.local.${networking.domain}"; addr = mkAlmostOptionDefault "login.local.${networking.domain}";
port = mkOptionDefault 9080; port = mkOptionDefault 9080;
ssl.enable = mkAlmostOptionDefault true; ssl.enable = mkAlmostOptionDefault true;
}; };
int = { upstream, ... }: { int = {upstream, ...}: {
enable = mkAlmostOptionDefault system.network.networks.int.enable or false; enable = mkAlmostOptionDefault system.network.networks.int.enable or false;
addr = mkAlmostOptionDefault "login.int.${networking.domain}"; addr = mkAlmostOptionDefault "login.int.${networking.domain}";
port = mkOptionDefault 9080; port = mkOptionDefault 9080;
}; };
tail = { upstream, ... }: { tail = {upstream, ...}: {
enable = mkAlmostOptionDefault (tailscale.enable && !upstream.servers.lan.enable && !upstream.servers.int.enable); enable = mkAlmostOptionDefault (tailscale.enable && !upstream.servers.lan.enable && !upstream.servers.int.enable);
addr = mkAlmostOptionDefault "login.tail.${networking.domain}"; addr = mkAlmostOptionDefault "login.tail.${networking.domain}";
port = mkOptionDefault 9080; port = mkOptionDefault 9080;

View file

@ -1,5 +1,10 @@
let let
locationModule = { config, virtualHost, lib, ... }: let locationModule = {
config,
virtualHost,
lib,
...
}: let
inherit (lib.options) mkEnableOption mkOption; inherit (lib.options) mkEnableOption mkOption;
inherit (lib.attrsets) mapAttrs; inherit (lib.attrsets) mapAttrs;
cfg = config.xvars; cfg = config.xvars;
@ -8,7 +13,7 @@ let
enable = mkEnableOption "$x_variables"; enable = mkEnableOption "$x_variables";
defaults = mkOption { defaults = mkOption {
type = attrsOf (nullOr str); type = attrsOf (nullOr str);
default = { }; default = {};
}; };
lib = mkOption { lib = mkOption {
type = attrs; type = attrs;
@ -18,15 +23,28 @@ let
xvars = { xvars = {
lib = let lib = let
xvars = virtualHost.xvars.lib; xvars = virtualHost.xvars.lib;
get = mapAttrs (name: default: if virtualHost.xvars.enable then "$x_${name}" else assert default != null; default) cfg.defaults; get = mapAttrs (name: default:
in xvars // { if virtualHost.xvars.enable
get = xvars.get // get; then "$x_${name}"
}; else assert default != null; default)
cfg.defaults;
in
xvars
// {
get = xvars.get // get;
};
}; };
_module.args.xvars = config.xvars.lib; _module.args.xvars = config.xvars.lib;
}; };
}; };
hostModule = { config, nixosConfig, gensokyo-zone, xvars, lib, ... }: let hostModule = {
config,
nixosConfig,
gensokyo-zone,
xvars,
lib,
...
}: let
inherit (gensokyo-zone.lib) mkJustBefore; inherit (gensokyo-zone.lib) mkJustBefore;
inherit (lib.options) mkOption mkEnableOption; inherit (lib.options) mkOption mkEnableOption;
inherit (lib.modules) mkIf mkMerge mkOptionDefault; inherit (lib.modules) mkIf mkMerge mkOptionDefault;
@ -36,11 +54,16 @@ let
inherit (lib.trivial) isInt; inherit (lib.trivial) isInt;
cfg = config.xvars; cfg = config.xvars;
escapeString = value: escapeString = value:
if value == "" then ''""'' if value == ""
else if isInt value then toString value then ''""''
else if hasPrefix ''"'' value || hasPrefix "'" value then value # already escaped, may include trailing arguments else if isInt value
else if hasInfix ''"'' value then "'${value}'" then toString value
else if hasInfix " " value || hasInfix ";" value || hasInfix "'" value then ''"${value}"'' else if hasPrefix ''"'' value || hasPrefix "'" value
then value # already escaped, may include trailing arguments
else if hasInfix ''"'' value
then "'${value}'"
else if hasInfix " " value || hasInfix ";" value || hasInfix "'" value
then ''"${value}"''
else value; else value;
anyLocations = f: any (loc: loc.enable && f loc) (attrValues config.locations); anyLocations = f: any (loc: loc.enable && f loc) (attrValues config.locations);
in { in {
@ -66,7 +89,7 @@ let
}; };
locations = mkOption { locations = mkOption {
type = attrsOf (submoduleWith { type = attrsOf (submoduleWith {
modules = [ locationModule ]; modules = [locationModule];
shorthandOnlyDefinesConfig = true; shorthandOnlyDefinesConfig = true;
specialArgs = { specialArgs = {
inherit nixosConfig gensokyo-zone; inherit nixosConfig gensokyo-zone;
@ -99,7 +122,11 @@ let
referer_path = null; referer_path = null;
}); });
lib = { lib = {
get = mapAttrs (name: default: if cfg.enable then "$x_${name}" else assert default != null; default) cfg.defaults; get = mapAttrs (name: default:
if cfg.enable
then "$x_${name}"
else assert default != null; default)
cfg.defaults;
init = name: value: assert cfg.enable && cfg.defaults ? ${name}; "set $x_${name} ${escapeString value};"; init = name: value: assert cfg.enable && cfg.defaults ? ${name}; "set $x_${name} ${escapeString value};";
inherit escapeString; inherit escapeString;
}; };
@ -111,24 +138,25 @@ let
_module.args.xvars = config.xvars.lib; _module.args.xvars = config.xvars.lib;
}; };
}; };
in { in
config, {
lib, config,
gensokyo-zone, lib,
... gensokyo-zone,
}: let ...
inherit (lib.options) mkOption; }: let
in { inherit (lib.options) mkOption;
options = with lib.types; { in {
services.nginx.virtualHosts = mkOption { options = with lib.types; {
type = attrsOf (submoduleWith { services.nginx.virtualHosts = mkOption {
modules = [ hostModule ]; type = attrsOf (submoduleWith {
shorthandOnlyDefinesConfig = true; modules = [hostModule];
specialArgs = { shorthandOnlyDefinesConfig = true;
inherit gensokyo-zone; specialArgs = {
nixosConfig = config; inherit gensokyo-zone;
}; nixosConfig = config;
}); };
});
};
}; };
}; }
}

View file

@ -40,9 +40,11 @@
allow = mkEnableOption "tailscale TCP connections"; allow = mkEnableOption "tailscale TCP connections";
}; };
int = { int = {
allow = mkEnableOption "internal TCP connections" // { allow =
default = config.authentication.local.allow; mkEnableOption "internal TCP connections"
}; // {
default = config.authentication.local.allow;
};
}; };
local = { local = {
allow = mkEnableOption "local TCP connections"; allow = mkEnableOption "local TCP connections";

View file

@ -45,28 +45,32 @@ in {
}; };
netbiosHostAddresses = mkOption { netbiosHostAddresses = mkOption {
type = attrsOf (listOf str); type = attrsOf (listOf str);
default = { }; default = {};
}; };
lmhosts = mkOption { lmhosts = mkOption {
type = attrsOf str; type = attrsOf str;
default = { }; default = {};
}; };
}; };
ldap = { ldap = {
enable = mkEnableOption "LDAP"; enable = mkEnableOption "LDAP";
passdb = { passdb = {
enable = mkEnableOption "LDAP authentication" // { enable =
default = true; mkEnableOption "LDAP authentication"
}; // {
default = true;
};
backend = mkOption { backend = mkOption {
type = enum [ "ldapsam" "ipasam" ]; type = enum ["ldapsam" "ipasam"];
default = "ldapsam"; default = "ldapsam";
}; };
}; };
idmap = { idmap = {
enable = mkEnableOption "LDAP users" // { enable =
default = true; mkEnableOption "LDAP users"
}; // {
default = true;
};
domain = mkOption { domain = mkOption {
type = str; type = str;
default = "*"; default = "*";
@ -98,12 +102,16 @@ in {
}; };
}; };
tls = { tls = {
enable = mkEnableOption "tls" // { enable =
default = cfg.tls.certPath != null; mkEnableOption "tls"
}; // {
peer.enable = mkEnableOption "peer verification" // { default = cfg.tls.certPath != null;
default = cfg.tls.caPath != null; };
}; peer.enable =
mkEnableOption "peer verification"
// {
default = cfg.tls.caPath != null;
};
useACMECert = mkOption { useACMECert = mkOption {
type = nullOr str; type = nullOr str;
default = null; default = null;
@ -212,19 +220,30 @@ in {
config = { config = {
services.samba = { services.samba = {
package = mkIf cfg.ldap.enable (mkAlmostOptionDefault ( package = mkIf cfg.ldap.enable (mkAlmostOptionDefault (
if cfg.ldap.passdb.enable && cfg.ldap.passdb.backend == "ipasam" then pkgs.samba-ipa else pkgs.samba-ldap if cfg.ldap.passdb.enable && cfg.ldap.passdb.backend == "ipasam"
then pkgs.samba-ipa
else pkgs.samba-ldap
)); ));
domain = { domain = {
isWorkgroup = mkOptionDefault (cfg.securityType != "domain" && cfg.securityType != "ads"); isWorkgroup = mkOptionDefault (cfg.securityType != "domain" && cfg.securityType != "ads");
netbiosName' = let netbiosName' = let
name = if cfg.domain.netbiosName != null then cfg.domain.netbiosName else config.networking.hostName; name =
in mkOptionDefault (if cfg.domain.isWorkgroup then toUpper name else name); if cfg.domain.netbiosName != null
then cfg.domain.netbiosName
else config.networking.hostName;
in
mkOptionDefault (
if cfg.domain.isWorkgroup
then toUpper name
else name
);
netbiosHostAddresses = mkIf (cfg.domain.netbiosName != null) { netbiosHostAddresses = mkIf (cfg.domain.netbiosName != null) {
${cfg.domain.netbiosName'} = [ "127.0.0.1" "::1" ]; ${cfg.domain.netbiosName'} = ["127.0.0.1" "::1"];
}; };
lmhosts = let lmhosts = let
addrs = mapAttrsToList (name: map (flip nameValuePair name)) cfg.domain.netbiosHostAddresses; addrs = mapAttrsToList (name: map (flip nameValuePair name)) cfg.domain.netbiosHostAddresses;
in listToAttrs (concatLists addrs); in
listToAttrs (concatLists addrs);
}; };
ldap = { ldap = {
adminPasswordPath = mkIf (cfg.ldap.adminDn != null && hasPrefix "name=anonymous," cfg.ldap.adminDn) (mkAlmostOptionDefault ( adminPasswordPath = mkIf (cfg.ldap.adminDn != null && hasPrefix "name=anonymous," cfg.ldap.adminDn) (mkAlmostOptionDefault (
@ -251,58 +270,61 @@ in {
}) })
]; ];
settings = mkMerge ([ settings = mkMerge ([
{ {
"use sendfile" = mkOptionDefault true; "use sendfile" = mkOptionDefault true;
"mdns name" = mkOptionDefault "mdns"; "mdns name" = mkOptionDefault "mdns";
"name resolve order" = mkOptionDefault [ "lmhosts" "host" "bcast" ]; "name resolve order" = mkOptionDefault ["lmhosts" "host" "bcast"];
workgroup = mkIf (cfg.domain.name != null) (mkOptionDefault cfg.domain.name); workgroup = mkIf (cfg.domain.name != null) (mkOptionDefault cfg.domain.name);
"netbios name" = mkIf (cfg.domain.netbiosName != null) (mkOptionDefault cfg.domain.netbiosName); "netbios name" = mkIf (cfg.domain.netbiosName != null) (mkOptionDefault cfg.domain.netbiosName);
} }
(mkIf (cfg.passdb.smbpasswd.path != null) { (mkIf (cfg.passdb.smbpasswd.path != null) {
"passdb backend" = mkOptionDefault "smbpasswd:${cfg.passdb.smbpasswd.path}"; "passdb backend" = mkOptionDefault "smbpasswd:${cfg.passdb.smbpasswd.path}";
}) })
(mkIf cfg.ldap.enable { (mkIf cfg.ldap.enable {
"ldap ssl" = mkIf (hasPrefix "ldaps://" cfg.ldap.url) (mkOptionDefault "off"); "ldap ssl" = mkIf (hasPrefix "ldaps://" cfg.ldap.url) (mkOptionDefault "off");
"ldap admin dn" = mkIf (cfg.ldap.adminDn != null) (mkOptionDefault cfg.ldap.adminDn); "ldap admin dn" = mkIf (cfg.ldap.adminDn != null) (mkOptionDefault cfg.ldap.adminDn);
"ldap suffix" = mkOptionDefault cfg.ldap.baseDn; "ldap suffix" = mkOptionDefault cfg.ldap.baseDn;
}) })
(mkIf cfg.kerberos.enable { (mkIf cfg.kerberos.enable {
"realm" = mkOptionDefault cfg.kerberos.realm; "realm" = mkOptionDefault cfg.kerberos.realm;
"kerberos method" = mkOptionDefault ( "kerberos method" = mkOptionDefault (
if cfg.kerberos.keytabPath != null then "dedicated keytab" if cfg.kerberos.keytabPath != null
else "system keytab" then "dedicated keytab"
); else "system keytab"
"dedicated keytab file" = mkIf (cfg.kerberos.keytabPath != null) (mkOptionDefault );
"FILE:${cfg.kerberos.keytabPath}" "dedicated keytab file" = mkIf (cfg.kerberos.keytabPath != null) (
); mkOptionDefault
"kerberos encryption types" = mkOptionDefault "strong"; "FILE:${cfg.kerberos.keytabPath}"
"create krb5 conf" = mkOptionDefault false; );
}) "kerberos encryption types" = mkOptionDefault "strong";
(mkIf cfg.enableWinbindd { "create krb5 conf" = mkOptionDefault false;
"winbind nss info" = mkOptionDefault "rfc2307"; })
"winbind use default domain" = mkOptionDefault true; (mkIf cfg.enableWinbindd {
}) "winbind nss info" = mkOptionDefault "rfc2307";
(mkIf cfg.tls.enable { "winbind use default domain" = mkOptionDefault true;
"tls enabled" = mkOptionDefault true; })
"tls verify peer" = mkIf cfg.tls.peer.enable (mkOptionDefault "ca_and_name_if_available"); (mkIf cfg.tls.enable {
"tls certfile" = mkIf (cfg.tls.certPath != null) (mkOptionDefault cfg.tls.certPath); "tls enabled" = mkOptionDefault true;
"tls keyfile" = mkIf (cfg.tls.keyPath != null) (mkOptionDefault cfg.tls.keyPath); "tls verify peer" = mkIf cfg.tls.peer.enable (mkOptionDefault "ca_and_name_if_available");
"tls cafile" = mkIf (cfg.tls.caPath != null) (mkOptionDefault cfg.tls.caPath); "tls certfile" = mkIf (cfg.tls.certPath != null) (mkOptionDefault cfg.tls.certPath);
"tls crlfile" = mkIf (cfg.tls.crlPath != null) (mkOptionDefault cfg.tls.crlPath); "tls keyfile" = mkIf (cfg.tls.keyPath != null) (mkOptionDefault cfg.tls.keyPath);
}) "tls cafile" = mkIf (cfg.tls.caPath != null) (mkOptionDefault cfg.tls.caPath);
(mkIf cfg.usershare.enable { "tls crlfile" = mkIf (cfg.tls.crlPath != null) (mkOptionDefault cfg.tls.crlPath);
"usershare allow guests" = mkOptionDefault true; })
"usershare max shares" = mkOptionDefault 16; (mkIf cfg.usershare.enable {
"usershare owner only" = mkOptionDefault true; "usershare allow guests" = mkOptionDefault true;
"usershare template share" = mkOptionDefault cfg.usershare.templateShare; "usershare max shares" = mkOptionDefault 16;
"usershare path" = mkOptionDefault cfg.usershare.path; "usershare owner only" = mkOptionDefault true;
"usershare prefix allow list" = mkOptionDefault [ cfg.usershare.path ]; "usershare template share" = mkOptionDefault cfg.usershare.templateShare;
}) "usershare path" = mkOptionDefault cfg.usershare.path;
(mkIf cfg.guest.enable { "usershare prefix allow list" = mkOptionDefault [cfg.usershare.path];
"map to guest" = mkOptionDefault "Bad User"; })
"guest account" = mkOptionDefault cfg.guest.user; (mkIf cfg.guest.enable {
}) "map to guest" = mkOptionDefault "Bad User";
] ++ mapAttrsToList (_: idmap: mapAttrs' (key: value: nameValuePair "idmap config ${idmap.domain} : ${key}" (mkOptionDefault value)) idmap.settings) cfg.idmap.domains); "guest account" = mkOptionDefault cfg.guest.user;
})
]
++ mapAttrsToList (_: idmap: mapAttrs' (key: value: nameValuePair "idmap config ${idmap.domain} : ${key}" (mkOptionDefault value)) idmap.settings) cfg.idmap.domains);
extraConfig = mkMerge ( extraConfig = mkMerge (
mapAttrsToList (key: value: ''${key} = ${settingValue value}'') cfg.settings mapAttrsToList (key: value: ''${key} = ${settingValue value}'') cfg.settings
++ [ ++ [
@ -340,11 +362,11 @@ in {
]; ];
networking.hosts = mkIf (cfg.enable && cfg.domain.netbiosName != null) { networking.hosts = mkIf (cfg.enable && cfg.domain.netbiosName != null) {
"::1" = mkAfter [ cfg.domain.netbiosName' ]; "::1" = mkAfter [cfg.domain.netbiosName'];
# not a typo... # not a typo...
"127.0.0.2" = mkAfter [ cfg.domain.netbiosName' ]; "127.0.0.2" = mkAfter [cfg.domain.netbiosName'];
}; };
environment.etc."samba/lmhosts" = mkIf (cfg.enable && cfg.domain.lmhosts != { }) { environment.etc."samba/lmhosts" = mkIf (cfg.enable && cfg.domain.lmhosts != {}) {
text = mkMerge ( text = mkMerge (
mapAttrsToList (address: name: "${address} ${name}") cfg.domain.lmhosts mapAttrsToList (address: name: "${address} ${name}") cfg.domain.lmhosts
); );

View file

@ -1,11 +1,20 @@
{ config, lib, utils, ... }: let {
config,
lib,
utils,
...
}: let
inherit (lib.options) mkOption; inherit (lib.options) mkOption;
inherit (lib.modules) mkIf mkMerge mkOptionDefault; inherit (lib.modules) mkIf mkMerge mkOptionDefault;
inherit (lib.attrsets) mapAttrsToList; inherit (lib.attrsets) mapAttrsToList;
inherit (lib.lists) head; inherit (lib.lists) head;
inherit (lib.strings) splitString; inherit (lib.strings) splitString;
inherit (utils) escapeSystemdPath; inherit (utils) escapeSystemdPath;
mountModule = { config, name, ... }: { mountModule = {
config,
name,
...
}: {
options = with lib.types; { options = with lib.types; {
source = mkOption { source = mkOption {
type = path; type = path;
@ -32,23 +41,38 @@
}; };
}; };
}; };
mkMountType' = { rootDir, specialArgs, modules ? [ ] }: let mkMountType' = {
rootDirModule = { ... }: { rootDir,
specialArgs,
modules ? [],
}: let
rootDirModule = {...}: {
config.rootDir = mkOptionDefault rootDir; config.rootDir = mkOptionDefault rootDir;
}; };
in lib.types.submoduleWith { in
modules = [ mountModule rootDirModule ] ++ modules; lib.types.submoduleWith {
inherit specialArgs; modules = [mountModule rootDirModule] ++ modules;
}; inherit specialArgs;
mkMountType = args: with lib.types; coercedTo path (path: { path = mkOptionDefault path; }) (mkMountType' args); };
serviceModule = { config, nixosConfig, ... }: let mkMountType = args: with lib.types; coercedTo path (path: {path = mkOptionDefault path;}) (mkMountType' args);
serviceModule = {
config,
nixosConfig,
...
}: let
cfg = config.gensokyo-zone; cfg = config.gensokyo-zone;
mapSharedMounts = f: mapAttrsToList (_: target: mapSharedMounts = f:
f target mapAttrsToList (
) cfg.sharedMounts; _: target:
mapCacheMounts = f: mapAttrsToList (_: target: f target
f target )
) cfg.cacheMounts; cfg.sharedMounts;
mapCacheMounts = f:
mapAttrsToList (
_: target:
f target
)
cfg.cacheMounts;
mkRequire = mount: mount.mountUnit; mkRequire = mount: mount.mountUnit;
mkBindPath = mount: "${mount.source}:${mount.path}"; mkBindPath = mount: "${mount.source}:${mount.path}";
specialArgs = { specialArgs = {
@ -56,28 +80,34 @@
inherit nixosConfig; inherit nixosConfig;
}; };
mountUnits = mkMerge [ mountUnits = mkMerge [
(mkIf (cfg.sharedMounts != { }) (mapSharedMounts mkRequire)) (mkIf (cfg.sharedMounts != {}) (mapSharedMounts mkRequire))
(mkIf (cfg.cacheMounts != { }) (mapCacheMounts mkRequire)) (mkIf (cfg.cacheMounts != {}) (mapCacheMounts mkRequire))
]; ];
in { in {
options.gensokyo-zone = with lib.types; { options.gensokyo-zone = with lib.types; {
sharedMounts = mkOption { sharedMounts = mkOption {
type = attrsOf (mkMountType { rootDir = "/mnt/shared"; inherit specialArgs; }); type = attrsOf (mkMountType {
default = { }; rootDir = "/mnt/shared";
inherit specialArgs;
});
default = {};
}; };
cacheMounts = mkOption { cacheMounts = mkOption {
type = attrsOf (mkMountType { rootDir = "/mnt/caches"; inherit specialArgs; }); type = attrsOf (mkMountType {
default = { }; rootDir = "/mnt/caches";
inherit specialArgs;
});
default = {};
}; };
}; };
config = { config = {
requires = mountUnits; requires = mountUnits;
after = mountUnits; after = mountUnits;
serviceConfig = mkMerge [ serviceConfig = mkMerge [
(mkIf (cfg.sharedMounts != { }) { (mkIf (cfg.sharedMounts != {}) {
BindPaths = mapSharedMounts mkBindPath; BindPaths = mapSharedMounts mkBindPath;
}) })
(mkIf (cfg.cacheMounts != { }) { (mkIf (cfg.cacheMounts != {}) {
BindPaths = mapCacheMounts mkBindPath; BindPaths = mapCacheMounts mkBindPath;
}) })
]; ];
@ -87,7 +117,7 @@ in {
options = with lib.types; { options = with lib.types; {
systemd.services = mkOption { systemd.services = mkOption {
type = attrsOf (submoduleWith { type = attrsOf (submoduleWith {
modules = [ serviceModule ]; modules = [serviceModule];
shorthandOnlyDefinesConfig = true; shorthandOnlyDefinesConfig = true;
specialArgs = { specialArgs = {
nixosConfig = config; nixosConfig = config;

View file

@ -1,4 +1,10 @@
{ gensokyo-zone, pkgs, config, lib, ... }: let {
gensokyo-zone,
pkgs,
config,
lib,
...
}: let
inherit (gensokyo-zone.lib) mkAlmostOptionDefault mapOptionDefaults mapAlmostOptionDefaults mapDefaults; inherit (gensokyo-zone.lib) mkAlmostOptionDefault mapOptionDefaults mapAlmostOptionDefaults mapDefaults;
inherit (lib.options) mkOption mkEnableOption; inherit (lib.options) mkOption mkEnableOption;
inherit (lib.modules) mkIf mkMerge mkAfter mkDefault mkOptionDefault; inherit (lib.modules) mkIf mkMerge mkAfter mkDefault mkOptionDefault;
@ -6,7 +12,7 @@
inherit (config.services) sssd; inherit (config.services) sssd;
genso = krb5.gensokyo-zone; genso = krb5.gensokyo-zone;
cfg = sssd.gensokyo-zone; cfg = sssd.gensokyo-zone;
serverModule = { config, ... }: { serverModule = {config, ...}: {
options = with lib.types; { options = with lib.types; {
servers = mkOption { servers = mkOption {
type = nullOr (listOf str); type = nullOr (listOf str);
@ -14,14 +20,14 @@
}; };
backups = mkOption { backups = mkOption {
type = listOf str; type = listOf str;
default = [ ]; default = [];
}; };
serverName = mkOption { serverName = mkOption {
type = str; type = str;
internal = true; internal = true;
}; };
serverKind = mkOption { serverKind = mkOption {
type = enum [ "server" "uri" ]; type = enum ["server" "uri"];
default = "server"; default = "server";
internal = true; internal = true;
}; };
@ -35,35 +41,42 @@
in { in {
settings = { settings = {
${key} = mkIf (config.servers != null) (mkOptionDefault config.servers); ${key} = mkIf (config.servers != null) (mkOptionDefault config.servers);
${keyBackups} = mkIf (config.backups != [ ]) (mkOptionDefault config.backups); ${keyBackups} = mkIf (config.backups != []) (mkOptionDefault config.backups);
}; };
}; };
}; };
mkServerType = { modules }: lib.types.submoduleWith { mkServerType = {modules}:
modules = [ serverModule ] ++ modules; lib.types.submoduleWith {
specialArgs = { modules = [serverModule] ++ modules;
inherit gensokyo-zone pkgs; specialArgs = {
nixosConfig = config; inherit gensokyo-zone pkgs;
nixosConfig = config;
};
}; };
}; mkServerOption = {
mkServerOption = { name, kind ? "server" }: let name,
serverInfoModule = { ... }: { kind ? "server",
}: let
serverInfoModule = {...}: {
config = { config = {
serverName = mkOptionDefault name; serverName = mkOptionDefault name;
serverKind = mkAlmostOptionDefault kind; serverKind = mkAlmostOptionDefault kind;
}; };
}; };
in mkOption { in
type = mkServerType { mkOption {
modules = [ serverInfoModule ]; type = mkServerType {
modules = [serverInfoModule];
};
default = {};
}; };
default = { };
};
in { in {
options.services.sssd.gensokyo-zone = with lib.types; { options.services.sssd.gensokyo-zone = with lib.types; {
enable = mkEnableOption "realm" // { enable =
default = genso.enable; mkEnableOption "realm"
}; // {
default = genso.enable;
};
ldap = { ldap = {
bind = { bind = {
passwordFile = mkOption { passwordFile = mkOption {
@ -71,24 +84,29 @@ in {
default = null; default = null;
}; };
}; };
uris = mkServerOption { name = "ldap"; kind = "uri"; }; uris = mkServerOption {
name = "ldap";
kind = "uri";
};
}; };
krb5 = { krb5 = {
servers = mkServerOption { name = "krb5"; }; servers = mkServerOption {name = "krb5";};
}; };
ipa = { ipa = {
servers = mkServerOption { name = "ipa"; } // { servers =
default = { mkServerOption {name = "ipa";}
inherit (cfg.krb5.servers) servers backups; // {
default = {
inherit (cfg.krb5.servers) servers backups;
};
}; };
};
hostName = mkOption { hostName = mkOption {
type = str; type = str;
default = config.networking.fqdn; default = config.networking.fqdn;
}; };
}; };
backend = mkOption { backend = mkOption {
type = enum [ "ldap" "ipa" ]; type = enum ["ldap" "ipa"];
default = "ipa"; default = "ipa";
}; };
}; };
@ -97,24 +115,26 @@ in {
# or "ipaNTSecurityIdentifier" which isn't set for most groups, maybe check netgroups..? # or "ipaNTSecurityIdentifier" which isn't set for most groups, maybe check netgroups..?
objectsid = "sambaSID"; objectsid = "sambaSID";
backendDomainSettings = { backendDomainSettings = {
ldap = mapDefaults { ldap =
id_provider = "ldap"; mapDefaults {
auth_provider = "krb5"; id_provider = "ldap";
access_provider = "ldap"; auth_provider = "krb5";
ldap_tls_cacert = "/etc/ssl/certs/ca-bundle.crt"; access_provider = "ldap";
} // mapOptionDefaults { ldap_tls_cacert = "/etc/ssl/certs/ca-bundle.crt";
ldap_access_order = [ "host" ]; }
ldap_schema = "IPA"; // mapOptionDefaults {
ldap_default_bind_dn = genso.ldap.bind.dn; ldap_access_order = ["host"];
ldap_search_base = genso.ldap.baseDn; ldap_schema = "IPA";
ldap_user_search_base = "cn=users,cn=accounts,${genso.ldap.baseDn}"; ldap_default_bind_dn = genso.ldap.bind.dn;
ldap_group_search_base = "cn=groups,cn=accounts,${genso.ldap.baseDn}"; ldap_search_base = genso.ldap.baseDn;
ldap_user_uuid = "ipaUniqueID"; ldap_user_search_base = "cn=users,cn=accounts,${genso.ldap.baseDn}";
ldap_user_ssh_public_key = "ipaSshPubKey"; ldap_group_search_base = "cn=groups,cn=accounts,${genso.ldap.baseDn}";
ldap_user_objectsid = objectsid; ldap_user_uuid = "ipaUniqueID";
ldap_group_uuid = "ipaUniqueID"; ldap_user_ssh_public_key = "ipaSshPubKey";
ldap_group_objectsid = objectsid; ldap_user_objectsid = objectsid;
}; ldap_group_uuid = "ipaUniqueID";
ldap_group_objectsid = objectsid;
};
ipa = mapOptionDefaults { ipa = mapOptionDefaults {
id_provider = "ipa"; id_provider = "ipa";
auth_provider = "ipa"; auth_provider = "ipa";
@ -124,26 +144,28 @@ in {
dyndns_iface = ipa.dyndns.interface; dyndns_iface = ipa.dyndns.interface;
}; };
}; };
domainSettings = mapAlmostOptionDefaults { domainSettings =
ipa_hostname = cfg.ipa.hostName; mapAlmostOptionDefaults {
} // mapOptionDefaults { ipa_hostname = cfg.ipa.hostName;
enumerate = true; }
ipa_domain = genso.domain; // mapOptionDefaults {
krb5_realm = genso.realm; enumerate = true;
cache_credentials = ipa.cacheCredentials; ipa_domain = genso.domain;
krb5_store_password_if_offline = ipa.offlinePasswords; krb5_realm = genso.realm;
#min_id = 8000; cache_credentials = ipa.cacheCredentials;
#max_id = 8999; krb5_store_password_if_offline = ipa.offlinePasswords;
}; #min_id = 8000;
#max_id = 8999;
};
in { in {
gensokyo-zone = { gensokyo-zone = {
krb5.servers.servers = mkMerge [ krb5.servers.servers = mkMerge [
[ genso.host ] [genso.host]
(mkAfter [ "_srv" genso.canonHost ]) (mkAfter ["_srv" genso.canonHost])
]; ];
ldap.uris = { ldap.uris = {
servers = mkMerge [ servers = mkMerge [
(mkAfter [ "_srv" ]) (mkAfter ["_srv"])
genso.ldap.urls genso.ldap.urls
]; ];
}; };
@ -191,4 +213,3 @@ in {
}; };
}; };
} }

View file

@ -9,8 +9,8 @@
inherit (lib.modules) mkIf; inherit (lib.modules) mkIf;
inherit (lib.attrsets) genAttrs; inherit (lib.attrsets) genAttrs;
cfg = config.services.sssd; cfg = config.services.sssd;
pamRulesModule = { ... }: let pamRulesModule = {...}: let
rules = [ "account" "auth" "password" "session" ]; rules = ["account" "auth" "password" "session"];
mkRuleConfig = ruleName: { mkRuleConfig = ruleName: {
sss = mkIf cfg.enable { sss = mkIf cfg.enable {
enable = mkIf (!cfg.services.pam.enable) (mkAlmostForce false); enable = mkIf (!cfg.services.pam.enable) (mkAlmostForce false);
@ -19,7 +19,7 @@
in { in {
config = genAttrs rules mkRuleConfig; config = genAttrs rules mkRuleConfig;
}; };
pamServiceModule = { ... }: { pamServiceModule = {...}: {
options = with lib.types; { options = with lib.types; {
rules = mkOption { rules = mkOption {
type = submodule pamRulesModule; type = submodule pamRulesModule;

View file

@ -15,20 +15,23 @@
inherit (lib) generators; inherit (lib) generators;
cfg = config.services.sssd; cfg = config.services.sssd;
mkValuePrimitive = value: mkValuePrimitive = value:
if value == true then "True" if value == true
else if value == false then "False" then "True"
else if value == false
then "False"
else toString value; else toString value;
toINI = generators.toINI { toINI = generators.toINI {
mkKeyValue = generators.mkKeyValueDefault { mkKeyValue = generators.mkKeyValueDefault {
mkValueString = value: mkValueString = value:
if isList value then concatMapStringsSep ", " mkValuePrimitive value if isList value
then concatMapStringsSep ", " mkValuePrimitive value
else mkValuePrimitive value; else mkValuePrimitive value;
} " = "; } " = ";
}; };
primitiveType = with lib.types; oneOf [ str int bool ]; primitiveType = with lib.types; oneOf [str int bool];
valueType = with lib.types; oneOf [ primitiveType (listOf primitiveType) ]; valueType = with lib.types; oneOf [primitiveType (listOf primitiveType)];
settingsType = lib.types.attrsOf valueType; settingsType = lib.types.attrsOf valueType;
serviceModule = { name, ... }: { serviceModule = {name, ...}: {
options = with lib.types; { options = with lib.types; {
enable = mkEnableOption "${name} service"; enable = mkEnableOption "${name} service";
name = mkOption { name = mkOption {
@ -38,22 +41,24 @@
}; };
settings = mkOption { settings = mkOption {
type = settingsType; type = settingsType;
default = { }; default = {};
}; };
}; };
}; };
nssModule = { nixosConfig, ... }: { nssModule = {nixosConfig, ...}: {
options = { options = {
# TODO: passwd.enable = mkEnableOption "passwd" // { default = true; }; # TODO: passwd.enable = mkEnableOption "passwd" // { default = true; };
shadow.enable = mkEnableOption "shadow" // { default = nixosConfig.services.sssd.services.pam.enable; }; shadow.enable = mkEnableOption "shadow" // {default = nixosConfig.services.sssd.services.pam.enable;};
netgroup.enable = mkEnableOption "netgroup" // { default = true; }; netgroup.enable = mkEnableOption "netgroup" // {default = true;};
}; };
}; };
domainModule = { name, ... }: { domainModule = {name, ...}: {
options = with lib.types; { options = with lib.types; {
enable = mkEnableOption "domain" // { enable =
default = true; mkEnableOption "domain"
}; // {
default = true;
};
domain = mkOption { domain = mkOption {
type = str; type = str;
default = name; default = name;
@ -63,17 +68,17 @@
}; };
}; };
}; };
domainLdapModule = { config, ... }: let domainLdapModule = {config, ...}: let
cfg = config.ldap; cfg = config.ldap;
in { in {
options.ldap = with lib.types; { options.ldap = with lib.types; {
extraAttrs.user = mkOption { extraAttrs.user = mkOption {
type = attrsOf str; type = attrsOf str;
default = { }; default = {};
}; };
authtok = { authtok = {
type = mkOption { type = mkOption {
type = enum [ "password" "obfuscated_password" ]; type = enum ["password" "obfuscated_password"];
default = "password"; default = "password";
}; };
password = mkOption { password = mkOption {
@ -87,7 +92,7 @@
passwordVar = mkOption { passwordVar = mkOption {
type = str; type = str;
internal = true; internal = true;
default = "SSSD_AUTHTOK_" + replaceStrings [ "-" "." ] [ "_" "_" ] (toUpper config.domain); default = "SSSD_AUTHTOK_" + replaceStrings ["-" "."] ["_" "_"] (toUpper config.domain);
}; };
}; };
}; };
@ -95,14 +100,16 @@
authtokConfig = mkIf (cfg.authtok.password != null || cfg.authtok.passwordFile != null) { authtokConfig = mkIf (cfg.authtok.password != null || cfg.authtok.passwordFile != null) {
ldap_default_authtok_type = mkOptionDefault cfg.authtok.type; ldap_default_authtok_type = mkOptionDefault cfg.authtok.type;
ldap_default_authtok = mkOptionDefault ( ldap_default_authtok = mkOptionDefault (
if cfg.authtok.passwordFile != null then "\$${cfg.authtok.passwordVar}" if cfg.authtok.passwordFile != null
then "\$${cfg.authtok.passwordVar}"
else cfg.authtok.password else cfg.authtok.password
); );
}; };
extraAttrsConfig = mkIf (cfg.extraAttrs.user != { }) { extraAttrsConfig = mkIf (cfg.extraAttrs.user != {}) {
ldap_user_extra_attrs = let ldap_user_extra_attrs = let
mkAttr = name: attr: "${name}:${attr}"; mkAttr = name: attr: "${name}:${attr}";
in mapAttrsToList mkAttr cfg.extraAttrs.user; in
mapAttrsToList mkAttr cfg.extraAttrs.user;
}; };
in { in {
settings = mkMerge [ settings = mkMerge [
@ -119,7 +126,7 @@ in {
}; };
domains = mkOption { domains = mkOption {
type = attrsOf (submoduleWith { type = attrsOf (submoduleWith {
modules = [ domainModule domainLdapModule ]; modules = [domainModule domainLdapModule];
specialArgs = { specialArgs = {
nixosConfig = config; nixosConfig = config;
}; };
@ -135,25 +142,27 @@ in {
}; };
}; };
services = let services = let
mkServiceOption = name: { modules ? [ ] }: mkOption { mkServiceOption = name: {modules ? []}:
type = submoduleWith { mkOption {
modules = [ serviceModule ] ++ modules; type = submoduleWith {
specialArgs = { modules = [serviceModule] ++ modules;
inherit name; specialArgs = {
nixosConfig = config; inherit name;
nixosConfig = config;
};
}; };
}; };
};
services = { services = {
nss = { modules = [ nssModule ]; }; nss = {modules = [nssModule];};
pam = { }; pam = {};
ifp = { }; ifp = {};
sudo = { }; sudo = {};
autofs = { }; autofs = {};
ssh = { }; ssh = {};
pac = { }; pac = {};
}; };
in mapAttrs mkServiceOption services; in
mapAttrs mkServiceOption services;
settings = mkOption { settings = mkOption {
type = attrsOf settingsType; type = attrsOf settingsType;
}; };
@ -175,11 +184,14 @@ in {
domains = map (domain: domain.domain) enabledDomains; domains = map (domain: domain.domain) enabledDomains;
}; };
}; };
domainSettings = map (domain: { domainSettings =
"domain/${domain.domain}" = mapAttrs (_: mkOptionDefault) domain.settings; map (domain: {
}) enabledDomains; "domain/${domain.domain}" = mapAttrs (_: mkOptionDefault) domain.settings;
settings = [ defaultSettings serviceSettings ] ++ domainSettings; })
in mkMerge settings; enabledDomains;
settings = [defaultSettings serviceSettings] ++ domainSettings;
in
mkMerge settings;
services = { services = {
nss.enable = mkAlmostOptionDefault true; nss.enable = mkAlmostOptionDefault true;
pam.enable = mkAlmostOptionDefault true; pam.enable = mkAlmostOptionDefault true;
@ -187,24 +199,30 @@ in {
extraUserAttrs = listToAttrs (concatMap (domain: map (flip nameValuePair {}) (attrNames domain.ldap.extraAttrs.user)) enabledDomains); extraUserAttrs = listToAttrs (concatMap (domain: map (flip nameValuePair {}) (attrNames domain.ldap.extraAttrs.user)) enabledDomains);
mkExtraAttr = name: _: "+${name}"; mkExtraAttr = name: _: "+${name}";
in { in {
user_attributes = mkIf (extraUserAttrs != { }) (mkOptionDefault ( user_attributes = mkIf (extraUserAttrs != {}) (mkOptionDefault (
mapAttrsToList mkExtraAttr extraUserAttrs mapAttrsToList mkExtraAttr extraUserAttrs
)); ));
}; };
sudo = { }; sudo = {};
autofs = { }; autofs = {};
ssh = { }; ssh = {};
pac = { }; pac = {};
}; };
configText = mkOptionDefault (toINI cfg.settings); configText = mkOptionDefault (toINI cfg.settings);
config = mkIf (cfg.configText != null) (mkAlmostOptionDefault cfg.configText); config = mkIf (cfg.configText != null) (mkAlmostOptionDefault cfg.configText);
}; };
config.system.nssDatabases = let config.system.nssDatabases = let
inherit (cfg.services) nss; inherit (cfg.services) nss;
in mkIf cfg.enable { in
${if options ? system.nssDatabases.netgroup then "netgroup" else null} = mkIf (nss.enable && nss.netgroup.enable) [ "sss" ]; mkIf cfg.enable {
shadow = mkIf (!nss.enable || !nss.shadow.enable) ( ${
mkForce [ "files" ] if options ? system.nssDatabases.netgroup
); then "netgroup"
}; else null
} =
mkIf (nss.enable && nss.netgroup.enable) ["sss"];
shadow = mkIf (!nss.enable || !nss.shadow.enable) (
mkForce ["files"]
);
};
} }

View file

@ -12,7 +12,11 @@
inherit (lib.lists) singleton; inherit (lib.lists) singleton;
inherit (lib.strings) removePrefix; inherit (lib.strings) removePrefix;
cfg = config.services.steam.accountSwitch; cfg = config.services.steam.accountSwitch;
machineModule = { config, name, ... }: { machineModule = {
config,
name,
...
}: {
options = with lib.types; { options = with lib.types; {
name = mkOption { name = mkOption {
type = str; type = str;
@ -67,7 +71,7 @@ in {
}; };
machines = mkOption { machines = mkOption {
type = attrsOf (submodule machineModule); type = attrsOf (submodule machineModule);
default = { }; default = {};
}; };
}; };
@ -93,34 +97,40 @@ in {
inherit owner; inherit owner;
inherit (shared) group mode; inherit (shared) group mode;
}; };
setupFiles = singleton { setupFiles =
${cfg.rootDir} = toplevel; singleton {
${cfg.binDir} = toplevel; ${cfg.rootDir} = toplevel;
${cfg.binDir + "/users"} = shared; ${cfg.binDir} = toplevel;
${cfg.dataDir} = toplevel; ${cfg.binDir + "/users"} = shared;
${cfg.sharedDataDir} = shared; ${cfg.dataDir} = toplevel;
${cfg.workingDir} = toplevel; ${cfg.sharedDataDir} = shared;
${cfg.sharedWorkingDir} = shared; ${cfg.workingDir} = toplevel;
} ++ map (owner: { ${cfg.sharedWorkingDir} = shared;
${cfg.dataDir + "/${owner}"} = personal owner; }
${cfg.workingDir + "/${owner}"} = personal owner; ++ map (owner: {
}) cfg.users ${cfg.dataDir + "/${owner}"} = personal owner;
++ mapAttrsToList (_: machine: { ${cfg.workingDir + "/${owner}"} = personal owner;
${cfg.dataDir + "/${machine.name}"} = personal machine.owner; })
${cfg.workingDir + "/${machine.name}"} = personal machine.owner; cfg.users
}) cfg.machines; ++ mapAttrsToList (_: machine: {
userBinFiles = listToAttrs (map (user: nameValuePair "${cfg.binDir}/users/${user}.bat" { ${cfg.dataDir + "/${machine.name}"} = personal machine.owner;
inherit (toplevel) owner group; ${cfg.workingDir + "/${machine.name}"} = personal machine.owner;
mode = "0755"; })
type = "copy"; cfg.machines;
src = pkgs.writeTextFile { userBinFiles = listToAttrs (map (user:
name = "steam-${user}.bat"; nameValuePair "${cfg.binDir}/users/${user}.bat" {
executable = true; inherit (toplevel) owner group;
text = '' mode = "0755";
setx GENSO_STEAM_USER ${user} type = "copy";
''; src = pkgs.writeTextFile {
}; name = "steam-${user}.bat";
}) cfg.users); executable = true;
text = ''
setx GENSO_STEAM_USER ${user}
'';
};
})
cfg.users);
in { in {
enable = mkIf (cfg.enable || cfg.setup) true; enable = mkIf (cfg.enable || cfg.setup) true;
files = mkMerge [ files = mkMerge [
@ -132,14 +142,16 @@ in {
mkSharePathWith = { mkSharePathWith = {
path, path,
winRoot ? "%GENSO_SMB_SHARED_MOUNT%", winRoot ? "%GENSO_SMB_SHARED_MOUNT%",
}: mkWinPath ( }:
winRoot mkWinPath (
+ "/${cfg.sharePath}" winRoot
+ "/${removePrefix (cfg.rootDir + "/") path}" + "/${cfg.sharePath}"
); + "/${removePrefix (cfg.rootDir + "/") path}"
mkSharePath = path: config.lib.steam.mkSharePathWith { );
inherit path; mkSharePath = path:
}; config.lib.steam.mkSharePathWith {
inherit path;
};
}; };
}; };
} }

View file

@ -17,8 +17,15 @@
sortedVersions = sort (a: b: versionOlder a.version b.version) (attrValues cfg.versions); sortedVersions = sort (a: b: versionOlder a.version b.version) (attrValues cfg.versions);
prevVersionFor = version: let prevVersionFor = version: let
olderVersions = filter (v: versionOlder v.version version) sortedVersions; olderVersions = filter (v: versionOlder v.version version) sortedVersions;
in if olderVersions != [] then last olderVersions else null; in
versionModule = { config, name, ... }: { if olderVersions != []
then last olderVersions
else null;
versionModule = {
config,
name,
...
}: {
options = with lib.types; { options = with lib.types; {
version = mkOption { version = mkOption {
type = str; type = str;
@ -40,14 +47,18 @@
); );
}; };
}; };
fileModule = { config, name, ... }: { fileModule = {
config,
name,
...
}: {
options = with lib.types; { options = with lib.types; {
relativePath = mkOption { relativePath = mkOption {
type = str; type = str;
default = name; default = name;
}; };
type = mkOption { type = mkOption {
type = enum [ "file" "directory" ]; type = enum ["file" "directory"];
default = "file"; default = "file";
}; };
versioned = mkOption { versioned = mkOption {
@ -55,13 +66,16 @@
default = false; default = false;
}; };
target = mkOption { target = mkOption {
type = enum [ "user" "shared" "game" ]; type = enum ["user" "shared" "game"];
default = "user"; default = "user";
}; };
mode = { mode = {
file = mkOption { file = mkOption {
type = str; type = str;
default = if hasSuffix ".exe" config.relativePath || hasSuffix ".dll" config.relativePath then "775" else "664"; default =
if hasSuffix ".exe" config.relativePath || hasSuffix ".dll" config.relativePath
then "775"
else "664";
}; };
dir = mkOption { dir = mkOption {
type = str; type = str;
@ -75,7 +89,7 @@
type = functionTo path; type = functionTo path;
}; };
srcStyle = mkOption { srcStyle = mkOption {
type = enum [ "empty" "copy" "symlink" "symlink-shallow" ]; type = enum ["empty" "copy" "symlink" "symlink-shallow"];
default = "symlink"; default = "symlink";
}; };
workingPathFor = mkOption { workingPathFor = mkOption {
@ -88,7 +102,7 @@
type = functionTo (nullOr path); type = functionTo (nullOr path);
}; };
initStyle = mkOption { initStyle = mkOption {
type = enum [ "none" "copy" "symlink" "symlink-shallow" ]; type = enum ["none" "copy" "symlink" "symlink-shallow"];
default = "copy"; default = "copy";
}; };
setup = { setup = {
@ -106,32 +120,56 @@
versionPathFor = version: optionalString config.versioned "/${version}"; versionPathFor = version: optionalString config.versioned "/${version}";
in { in {
init = mkOptionDefault ( init = mkOptionDefault (
if config.target == "game" then null if config.target == "game"
else if config.type == "directory" then "${emptyDir}" then null
else if hasSuffix ".json" config.relativePath then "${emptyJson}" else if config.type == "directory"
else if hasSuffix ".dll" config.relativePath || hasSuffix ".exe" config.relativePath then "${emptyExecutable}" then "${emptyDir}"
else if hasSuffix ".json" config.relativePath
then "${emptyJson}"
else if hasSuffix ".dll" config.relativePath || hasSuffix ".exe" config.relativePath
then "${emptyExecutable}"
else "${emptyFile}" else "${emptyFile}"
); );
initFor = mkOptionDefault ( initFor = mkOptionDefault (
{ user, version }: config.init
);
ownerFor = mkOptionDefault (user:
if config.target == "user" then user else "admin"
);
srcPathFor = mkOptionDefault ({ user, version }:
{ {
shared = cfg.sharedDataDir + versionPathFor version; user,
user = cfg.dataDirFor user + versionPathFor version; version,
game = cfg.gameDirFor version; }:
}.${config.target} or (throw "unsupported target") config.init
+ "/${config.relativePath}"
); );
workingPathFor = mkOptionDefault ({ user, version }: ownerFor = mkOptionDefault (
cfg.workingDirFor { inherit user version; } user:
+ "/${config.relativePath}" if config.target == "user"
then user
else "admin"
);
srcPathFor = mkOptionDefault (
{
user,
version,
}:
{
shared = cfg.sharedDataDir + versionPathFor version;
user = cfg.dataDirFor user + versionPathFor version;
game = cfg.gameDirFor version;
}
.${config.target}
or (throw "unsupported target")
+ "/${config.relativePath}"
);
workingPathFor = mkOptionDefault (
{
user,
version,
}:
cfg.workingDirFor {inherit user version;}
+ "/${config.relativePath}"
); );
# TODO: setup.shared and do inits seperately! # TODO: setup.shared and do inits seperately!
setup.script = { user, version }@args: let setup.script = {
user,
version,
} @ args: let
owner = config.ownerFor user; owner = config.ownerFor user;
srcPath = config.srcPathFor args; srcPath = config.srcPathFor args;
workingPath = config.workingPathFor args; workingPath = config.workingPathFor args;
@ -148,85 +186,120 @@
fi fi
chown ${owner}:${cfg.group} ${escapeShellArg dest} chown ${owner}:${cfg.group} ${escapeShellArg dest}
''; '';
mkStyle = { style, src }: if style != "none" && src == { mkStyle = {
file = "${emptyFile}"; style,
directory = "${emptyDir}"; src,
}.${config.type} then "empty" else style; }:
doInit = { style, src, dest }: { if
none = "true"; style
copy = { != "none"
file = '' && src
if [[ -L ${escapeShellArg dest} ]]; then == {
rm -f ${escapeShellArg dest} file = "${emptyFile}";
elif [[ -e ${escapeShellArg dest} ]]; then directory = "${emptyDir}";
echo ERR: something is in the way of copying ${escapeShellArg dest} >&2 }
.${config.type}
then "empty"
else style;
doInit = {
style,
src,
dest,
}:
{
none = "true";
copy =
{
file = ''
if [[ -L ${escapeShellArg dest} ]]; then
rm -f ${escapeShellArg dest}
elif [[ -e ${escapeShellArg dest} ]]; then
echo ERR: something is in the way of copying ${escapeShellArg dest} >&2
exit 1
fi
cp -TP --no-preserve=all ${escapeShellArg src} ${escapeShellArg dest}
chmod ${config.mode.file} ${escapeShellArg dest}
chown ${owner}:${cfg.group} ${escapeShellArg dest}
'';
directory = ''
${mkdir dest}
cp -rTP --no-preserve=all ${escapeShellArg src} ${escapeShellArg dest}
chown -R ${owner}:${cfg.group} ${escapeShellArg dest}
find ${escapeShellArg dest} -type f -exec chmod -m${config.mode.file} "{}" \;
'';
}
.${config.type};
empty =
{
directory = ''
${mkdir dest}
'';
file = ''
touch ${escapeShellArg dest}
chmod ${config.mode.file} ${escapeShellArg dest}
chown ${owner}:${cfg.group} ${escapeShellArg dest}
'';
}
.${config.type};
symlink = ''
if [[ -e ${escapeShellArg dest} && ! -L ${escapeShellArg dest} ]]; then
echo ERR: something is in the way of linking ${escapeShellArg dest} >&2
exit 1 exit 1
fi fi
cp -TP --no-preserve=all ${escapeShellArg src} ${escapeShellArg dest} ln -sfT ${escapeShellArg src} ${escapeShellArg dest}
chmod ${config.mode.file} ${escapeShellArg dest}
chown ${owner}:${cfg.group} ${escapeShellArg dest}
''; '';
directory = '' symlink-shallow =
${mkdir dest} {
cp -rTP --no-preserve=all ${escapeShellArg src} ${escapeShellArg dest} directory = ''
chown -R ${owner}:${cfg.group} ${escapeShellArg dest} ${mkdir dest}
find ${escapeShellArg dest} -type f -exec chmod -m${config.mode.file} "{}" \; ln -sf ${escapeShellArg src}/* ${escapeShellArg dest}/
''; '';
}.${config.type}; }
empty = { .${config.type};
directory = '' }
${mkdir dest} .${mkStyle {inherit style src;}};
''; doSetup = {
file = '' style,
touch ${escapeShellArg dest} src,
chmod ${config.mode.file} ${escapeShellArg dest} dest,
chown ${owner}:${cfg.group} ${escapeShellArg dest} }:
''; rec {
}.${config.type}; none = "true";
symlink = '' copy =
if [[ -e ${escapeShellArg dest} && ! -L ${escapeShellArg dest} ]]; then {
echo ERR: something is in the way of linking ${escapeShellArg dest} >&2 file = ''
exit 1 ${empty}
fi '';
ln -sfT ${escapeShellArg src} ${escapeShellArg dest} directory = ''
''; ${empty}
symlink-shallow = { if [[ ${escapeShellArg dest}/* != ${escapeShellArg dest}/\* ]]; then
directory = '' chmod -m${config.mode.file} ${escapeShellArg dest}/*
${mkdir dest} fi
ln -sf ${escapeShellArg src}/* ${escapeShellArg dest}/ '';
''; }
}.${config.type}; .${config.type};
}.${mkStyle { inherit style src; }}; empty =
doSetup = { style, src, dest }: rec { {
none = "true"; directory = ''
copy = { chmod ${config.mode.dir} ${escapeShellArg dest}
file = '' chown ${owner}:${cfg.group} ${escapeShellArg dest}
${empty} '';
''; file = ''
directory = '' chmod ${config.mode.file} ${escapeShellArg dest}
${empty} chown ${owner}:${cfg.group} ${escapeShellArg dest}
if [[ ${escapeShellArg dest}/* != ${escapeShellArg dest}/\* ]]; then '';
chmod -m${config.mode.file} ${escapeShellArg dest}/* }
fi .${config.type};
''; symlink = "true";
}.${config.type}; symlink-shallow =
empty = { {
directory = '' directory = ''
chmod ${config.mode.dir} ${escapeShellArg dest} ${mkdir.directory}
chown ${owner}:${cfg.group} ${escapeShellArg dest} '';
''; }
file = '' .${config.type};
chmod ${config.mode.file} ${escapeShellArg dest} }
chown ${owner}:${cfg.group} ${escapeShellArg dest} .${mkStyle {inherit style src;}};
'';
}.${config.type};
symlink = "true";
symlink-shallow = {
directory = ''
${mkdir.directory}
'';
}.${config.type};
}.${mkStyle { inherit style src; }};
init = doInit { init = doInit {
style = config.initStyle; style = config.initStyle;
src = initPath; src = initPath;
@ -242,37 +315,46 @@
src = srcPath; src = srcPath;
dest = workingPath; dest = workingPath;
}; };
checkFlag = { checkFlag =
file = { {
none = "e"; file =
copy = "f"; {
symlink = "L"; none = "e";
}.${config.initStyle}; copy = "f";
directory = { symlink = "L";
none = "e"; }
copy = "d"; .${config.initStyle};
symlink-shallow = "d"; directory =
symlink = "L"; {
}.${config.initStyle}; none = "e";
}.${config.type}; copy = "d";
symlink-shallow = "d";
symlink = "L";
}
.${config.initStyle};
}
.${config.type};
checkParent = '' checkParent = ''
if [[ ! -d ${escapeShellArg parentWorkingPath} ]]; then if [[ ! -d ${escapeShellArg parentWorkingPath} ]]; then
echo ERR: parent of ${escapeShellArg workingPath} does not exist >&2 echo ERR: parent of ${escapeShellArg workingPath} does not exist >&2
exit 1 exit 1
fi fi
''; '';
check = if initPath != null then '' check =
if [[ ! -${checkFlag} ${escapeShellArg srcPath} ]]; then if initPath != null
${init} then ''
else if [[ ! -${checkFlag} ${escapeShellArg srcPath} ]]; then
${setup} ${init}
fi else
'' else '' ${setup}
if [[ ! -${checkFlag} ${escapeShellArg srcPath} ]]; then fi
echo ERR: src ${escapeShellArg srcPath} for ${escapeShellArg workingPath} does not exist >&2 ''
exit 1 else ''
fi if [[ ! -${checkFlag} ${escapeShellArg srcPath} ]]; then
''; echo ERR: src ${escapeShellArg srcPath} for ${escapeShellArg workingPath} does not exist >&2
exit 1
fi
'';
in '' in ''
${checkParent} ${checkParent}
${check} ${check}
@ -280,7 +362,11 @@
''; '';
}; };
}; };
userModule = { config, name, ... }: { userModule = {
config,
name,
...
}: {
options = with lib.types; { options = with lib.types; {
name = mkOption { name = mkOption {
type = str; type = str;
@ -294,7 +380,7 @@
}; };
emptyFile = pkgs.writeText "empty.txt" ""; emptyFile = pkgs.writeText "empty.txt" "";
emptyJson = pkgs.writeText "empty.json" "{}"; emptyJson = pkgs.writeText "empty.json" "{}";
emptyDir = pkgs.runCommand "empty" { } '' emptyDir = pkgs.runCommand "empty" {} ''
mkdir $out mkdir $out
''; '';
emptyExecutable = pkgs.writeTextFile { emptyExecutable = pkgs.writeTextFile {
@ -347,9 +433,11 @@
rmdir "%STEAM_BS_LIBRARY%" rmdir "%STEAM_BS_LIBRARY%"
mklink /D "%STEAM_BS_LIBRARY%" "%STEAM_BS_LAUNCH%" mklink /D "%STEAM_BS_LIBRARY%" "%STEAM_BS_LAUNCH%"
''; '';
launch = '' launch =
cd /d "%STEAM_BS_LIBRARY%" ''
'' + ''"%STEAM_BS_LIBRARY%\Beat Saber.exe"''; cd /d "%STEAM_BS_LIBRARY%"
''
+ ''"%STEAM_BS_LIBRARY%\Beat Saber.exe"'';
setup = '' setup = ''
rmdir "%STEAM_BS_APPDATA%" rmdir "%STEAM_BS_APPDATA%"
rmdir "%STEAM_BS_LIBRARY%" rmdir "%STEAM_BS_LIBRARY%"
@ -404,7 +492,10 @@
${launch} ${launch}
${eof} ${eof}
''; '';
beatsaber-user = { user, version }: '' beatsaber-user = {
user,
version,
}: ''
set GENSO_STEAM_USER=${user} set GENSO_STEAM_USER=${user}
set GENSO_STEAM_BS_VERSION=${version} set GENSO_STEAM_BS_VERSION=${version}
${vars} ${vars}
@ -416,19 +507,25 @@
setx GENSO_STEAM_BS_VERSION Vanilla setx GENSO_STEAM_BS_VERSION Vanilla
''; '';
mksetupbeatsaber = { user, version }: let mksetupbeatsaber = {
setupFiles = mapAttrsToList (_: file: file.setup.script { inherit user version; }) cfg.files; user,
in pkgs.writeShellScript "setupbeatsaber-${user}-${version}" '' version,
set -eu }: let
export PATH="$PATH:${makeBinPath [ pkgs.coreutils ]}" setupFiles = mapAttrsToList (_: file: file.setup.script {inherit user version;}) cfg.files;
${concatStringsSep "\n" setupFiles} in
''; pkgs.writeShellScript "setupbeatsaber-${user}-${version}" ''
set -eu
export PATH="$PATH:${makeBinPath [pkgs.coreutils]}"
${concatStringsSep "\n" setupFiles}
'';
in { in {
options.services.steam.beatsaber = with lib.types; { options.services.steam.beatsaber = with lib.types; {
enable = mkEnableOption "beatsaber scripts"; enable = mkEnableOption "beatsaber scripts";
setup = mkEnableOption "beatsaber data" // { setup =
default = accountSwitch.setup; mkEnableOption "beatsaber data"
}; // {
default = accountSwitch.setup;
};
group = mkOption { group = mkOption {
type = str; type = str;
default = "beatsaber"; default = "beatsaber";
@ -438,7 +535,7 @@ in {
}; };
versions = mkOption { versions = mkOption {
type = attrsOf (submodule versionModule); type = attrsOf (submodule versionModule);
default = { }; default = {};
}; };
setupServiceNames = mkOption { setupServiceNames = mkOption {
type = listOf str; type = listOf str;
@ -446,7 +543,7 @@ in {
}; };
files = mkOption { files = mkOption {
type = attrsOf (submodule fileModule); type = attrsOf (submodule fileModule);
default = { }; default = {};
}; };
users = mkOption { users = mkOption {
type = attrsOf (submodule userModule); type = attrsOf (submodule userModule);
@ -489,7 +586,11 @@ in {
}; };
workingDirFor = mkOption { workingDirFor = mkOption {
type = functionTo path; type = functionTo path;
default = { user, version }: cfg.userWorkingDirFor user + "/${version}"; default = {
user,
version,
}:
cfg.userWorkingDirFor user + "/${version}";
}; };
}; };
@ -498,9 +599,9 @@ in {
bsUsers = filterAttrs (_: userIs cfg.group) config.users.users; bsUsers = filterAttrs (_: userIs cfg.group) config.users.users;
allVersions = mapAttrsToList (_: version: version.version) cfg.versions; allVersions = mapAttrsToList (_: version: version.version) cfg.versions;
gameFiles = { gameFiles = {
"Beat Saber.exe" = { }; "Beat Saber.exe" = {};
"UnityCrashHandler64.exe" = { }; "UnityCrashHandler64.exe" = {};
"UnityPlayer.dll" = { }; "UnityPlayer.dll" = {};
"MonoBleedingEdge".type = "directory"; "MonoBleedingEdge".type = "directory";
}; };
sharedFiles = { sharedFiles = {
@ -526,7 +627,7 @@ in {
}; };
"BeatSaberVersion.txt" = { "BeatSaberVersion.txt" = {
versioned = true; versioned = true;
initFor = { version, ... }: pkgs.writeText "BeatSaberVersion-${version}.txt" version; initFor = {version, ...}: pkgs.writeText "BeatSaberVersion-${version}.txt" version;
}; };
"IPA.exe".versioned = true; "IPA.exe".versioned = true;
"IPA.exe.config".versioned = true; "IPA.exe.config".versioned = true;
@ -538,19 +639,19 @@ in {
#initStyle = "symlink-shallow"; #initStyle = "symlink-shallow";
#initFor = { version, ... }: cfg.gameDirFor version + "/${bsdata}"; #initFor = { version, ... }: cfg.gameDirFor version + "/${bsdata}";
initStyle = "none"; initStyle = "none";
srcPathFor = { version, ... }: cfg.gameDirFor version + "/${bsdata}"; srcPathFor = {version, ...}: cfg.gameDirFor version + "/${bsdata}";
srcStyle = "symlink-shallow"; srcStyle = "symlink-shallow";
}; };
"${bsdata}/Managed" = { "${bsdata}/Managed" = {
type = "directory"; type = "directory";
versioned = true; versioned = true;
initFor = { version, ... }: cfg.gameDirFor version + "/${bsdata}/Managed"; initFor = {version, ...}: cfg.gameDirFor version + "/${bsdata}/Managed";
}; };
# TODO: remove this to use multiple folders # TODO: remove this to use multiple folders
"${bsdata}/CustomLevels" = { "${bsdata}/CustomLevels" = {
type = "directory"; type = "directory";
initStyle = "none"; initStyle = "none";
srcPathFor = { ... }: cfg.sharedDataDir + "/CustomLevels"; srcPathFor = {...}: cfg.sharedDataDir + "/CustomLevels";
}; };
CustomAvatars = { CustomAvatars = {
type = "directory"; type = "directory";
@ -579,7 +680,7 @@ in {
"UserData/ScoreSaber/Replays" = { "UserData/ScoreSaber/Replays" = {
type = "directory"; type = "directory";
initStyle = "none"; initStyle = "none";
srcPathFor = { ... }: cfg.sharedDataDir + "/Replays"; srcPathFor = {...}: cfg.sharedDataDir + "/Replays";
}; };
"UserData/Beat Saber IPA.json".versioned = true; "UserData/Beat Saber IPA.json".versioned = true;
"UserData/SongCore/" = { "UserData/SongCore/" = {
@ -619,8 +720,8 @@ in {
}; };
"UserData/Saber Factory/Cache".type = "directory"; "UserData/Saber Factory/Cache".type = "directory";
"UserData/Saber Factory/Textures".type = "directory"; "UserData/Saber Factory/Textures".type = "directory";
"UserData/BeatSaverDownloader.ini" = { }; "UserData/BeatSaverDownloader.ini" = {};
"UserData/BeatSaverUpdater.json" = { }; "UserData/BeatSaverUpdater.json" = {};
"UserData/SongDetailsCache.proto".versioned = true; "UserData/SongDetailsCache.proto".versioned = true;
"UserData/SongDetailsCache.proto.Direct.etag".versioned = true; "UserData/SongDetailsCache.proto.Direct.etag".versioned = true;
}; };
@ -636,7 +737,7 @@ in {
srcStyle = "empty"; srcStyle = "empty";
}; };
"UserData/Saber Factory/Presets".type = "directory"; "UserData/Saber Factory/Presets".type = "directory";
"UserData/Saber Factory/TrailConfig.json" = { }; "UserData/Saber Factory/TrailConfig.json" = {};
"UserData/SongCore" = { "UserData/SongCore" = {
type = "directory"; type = "directory";
versioned = true; versioned = true;
@ -658,17 +759,23 @@ in {
"UserData/JDFixer.json".versioned = true; "UserData/JDFixer.json".versioned = true;
}; };
userDataFiles = [ userDataFiles = [
"modprefs.ini" "Disabled Mods.json" "modprefs.ini"
"Disabled Mods.json"
"AutoPauseStealth.json" "AutoPauseStealth.json"
"BeatSaberMarkupLanguage.json" "BeatSaberMarkupLanguage.json"
"BeatSaviorData.ini" "BeatSaviorData.ini"
"BetterSongList.json" "BetterSongList.json"
"BetterSongSearch.json" "BetterSongSearch.json"
"bookmarkedSongs.json" "votedSongs.json" "bookmarkedSongs.json"
"votedSongs.json"
"Chroma.json" "Chroma.json"
"Cinema.json" "Cinema.json"
"CountersPlus.json" "CountersPlus.json"
"CustomAvatars.CalibrationData.dat" "CustomAvatars.json" "CustomNotes.json" "Custom Platforms.json" "CustomWalls.json" "CustomAvatars.CalibrationData.dat"
"CustomAvatars.json"
"CustomNotes.json"
"Custom Platforms.json"
"CustomWalls.json"
"DrinkWater.json" "DrinkWater.json"
"EasyOffset.json" "EasyOffset.json"
"Enhancements.json" "Enhancements.json"
@ -702,20 +809,25 @@ in {
"Tweaks55.json" "Tweaks55.json"
"UITweaks.json" "UITweaks.json"
]; ];
mapSharedFile = file: file // { mapSharedFile = file:
target = "shared"; file
}; // {
mapGameFile = file: file // { target = "shared";
target = "game"; };
}; mapGameFile = file:
mapUserDataFile = file: nameValuePair "UserData/${file}" { file
target = "user"; // {
}; target = "game";
};
mapUserDataFile = file:
nameValuePair "UserData/${file}" {
target = "user";
};
in { in {
defaultVersion = mkIf (allVersions != [ ]) (mkOptionDefault ( defaultVersion = mkIf (allVersions != []) (mkOptionDefault (
head allVersions head allVersions
)); ));
users = mapAttrs (_: user: { name = mkDefault user.name; }) bsUsers; users = mapAttrs (_: user: {name = mkDefault user.name;}) bsUsers;
setupServiceNames = mkOptionDefault ( setupServiceNames = mkOptionDefault (
mapAttrsToList (_: user: "steam-setup-beatsaber-${user.name}.service") cfg.users mapAttrsToList (_: user: "steam-setup-beatsaber-${user.name}.service") cfg.users
); );
@ -736,11 +848,16 @@ in {
serviceConfig = { serviceConfig = {
Type = mkOptionDefault "oneshot"; Type = mkOptionDefault "oneshot";
RemainAfterExit = mkOptionDefault true; RemainAfterExit = mkOptionDefault true;
ExecStart = mkMerge (mapAttrsToList (_: user: ExecStart = mkMerge (mapAttrsToList (
(mapAttrsToList (_: version: _: user: (mapAttrsToList (
"${mksetupbeatsaber { user = user.name; inherit (version) version; }}" _: version: "${mksetupbeatsaber {
) cfg.versions) user = user.name;
) cfg.users); inherit (version) version;
}}"
)
cfg.versions)
)
cfg.users);
}; };
}; };
services.tmpfiles = let services.tmpfiles = let
@ -774,132 +891,157 @@ in {
"AppData" "AppData"
"UserData" "UserData"
]; ];
setupFiles = [ setupFiles =
[
{
${cfg.sharedDataDir} = toplevel;
${cfg.binDir} = shared;
}
(listToAttrs (
map (
folder:
nameValuePair "${cfg.sharedDataDir}/${folder}" shared
)
sharedFolders
))
]
++ concatLists (mapAttrsToList (
_: user:
singleton {
${cfg.dataDirFor user.name} = personal user.name;
"${cfg.dataDirFor user.name}/AppData" = personal user.name;
"${cfg.dataDirFor user.name}/UserData" = personal user.name;
}
++ mapAttrsToList (_: version: {
"${cfg.dataDirFor user.name}/${version.version}" = personal user.name;
${cfg.userWorkingDirFor user.name} = personal user.name;
${
cfg.workingDirFor {
user = user.name;
inherit (version) version;
}
} =
personal user.name;
})
cfg.versions
)
cfg.users)
++ mapAttrsToList (_: version: {
"${cfg.sharedDataDir}/${version.version}" = shared;
})
cfg.versions;
versionBinFiles =
mapAttrs' (
_: version:
nameValuePair
"${cfg.binDir}/${replaceStrings ["."] ["_"] version.version}.bat"
{
inherit (bin) owner group mode type;
src = pkgs.writeTextFile {
name = "beatsaber-${version.version}.bat";
executable = true;
text = ''
setx GENSO_STEAM_BS_VERSION ${version.version}
'';
};
}
)
cfg.versions;
userBinFiles =
mapAttrs' (
_: user:
nameValuePair
"${cfg.binDir}/${user.name}.bat"
{
inherit (bin) owner group mode type;
src = pkgs.writeTextFile {
name = "beatsaber-${user.name}.bat";
executable = true;
text = beatsaber-user {
user = user.name;
version = user.preferredVersion;
};
};
}
)
cfg.users;
binFiles =
{ {
${cfg.sharedDataDir} = toplevel; "${cfg.binDir}/mount.bat" = {
${cfg.binDir} = shared; inherit (bin) owner group mode type;
} src = pkgs.writeTextFile {
(listToAttrs ( name = "beatsaber-mount.bat";
map (folder: executable = true;
nameValuePair "${cfg.sharedDataDir}/${folder}" shared text = mountbeatsaber;
) sharedFolders };
))
] ++ concatLists (mapAttrsToList (_: user:
singleton {
${cfg.dataDirFor user.name} = personal user.name;
"${cfg.dataDirFor user.name}/AppData" = personal user.name;
"${cfg.dataDirFor user.name}/UserData" = personal user.name;
} ++ mapAttrsToList (_: version: {
"${cfg.dataDirFor user.name}/${version.version}" = personal user.name;
${cfg.userWorkingDirFor user.name} = personal user.name;
${cfg.workingDirFor { user = user.name; inherit (version) version; }} = personal user.name;
}) cfg.versions
) cfg.users)
++ mapAttrsToList (_: version: {
"${cfg.sharedDataDir}/${version.version}" = shared;
}) cfg.versions;
versionBinFiles = mapAttrs' (_: version: nameValuePair
"${cfg.binDir}/${replaceStrings [ "." ] [ "_" ] version.version}.bat"
{
inherit (bin) owner group mode type;
src = pkgs.writeTextFile {
name = "beatsaber-${version.version}.bat";
executable = true;
text = ''
setx GENSO_STEAM_BS_VERSION ${version.version}
'';
}; };
} "${cfg.binDir}/launch.bat" = {
) cfg.versions; inherit (bin) owner group mode type;
userBinFiles = mapAttrs' (_: user: nameValuePair src = pkgs.writeTextFile {
"${cfg.binDir}/${user.name}.bat" name = "beatsaber-launch.bat";
{ executable = true;
inherit (bin) owner group mode type; text = launchbeatsaber;
src = pkgs.writeTextFile { };
name = "beatsaber-${user.name}.bat"; };
executable = true; "${cfg.binDir}/fpfc.bat" = {
text = beatsaber-user { inherit (bin) owner group mode type;
user = user.name; src = pkgs.writeTextFile {
version = user.preferredVersion; name = "beatsaber-fpfc.bat";
executable = true;
text = fpfcbeatsaber;
};
};
"${cfg.binDir}/setup.bat" = {
inherit (bin) owner group mode type;
src = pkgs.writeTextFile {
name = "beatsaber-setup.bat";
executable = true;
text = setupbeatsaber;
};
};
"${cfg.binDir}/local-launch.bat" = {
inherit (bin) owner group mode type;
src = pkgs.writeTextFile {
name = "beatsaber-local-launch.bat";
executable = true;
text = localbeatsaber-launch;
};
};
"${cfg.binDir}/local-mount.bat" = {
inherit (bin) owner group mode type;
src = pkgs.writeTextFile {
name = "beatsaber-local-mount.bat";
executable = true;
text = localbeatsaber-mount;
};
};
"${cfg.binDir}/local-vanilla.bat" = {
inherit (bin) owner group mode type;
src = pkgs.writeTextFile {
name = "beatsaber-local-vanilla.bat";
executable = true;
text = localbeatsaber-vanilla;
};
};
"${cfg.binDir}/vanilla.bat" = {
inherit (bin) owner group mode type;
src = pkgs.writeTextFile {
name = "beatsaber-version-vanilla.bat";
executable = true;
text = vanilla;
};
};
"${cfg.binDir}/ModAssistant.exe" = {
inherit (bin) owner group mode type;
src = pkgs.fetchurl {
url = "https://github.com/Assistant/ModAssistant/releases/download/v1.1.32/ModAssistant.exe";
hash = "sha256-ozu2gYFiz+2BjptqL80DmUopbahbyGKFO1IPd7BhVPM=";
executable = true;
}; };
}; };
} }
) cfg.users; // versionBinFiles
binFiles = { // userBinFiles;
"${cfg.binDir}/mount.bat" = {
inherit (bin) owner group mode type;
src = pkgs.writeTextFile {
name = "beatsaber-mount.bat";
executable = true;
text = mountbeatsaber;
};
};
"${cfg.binDir}/launch.bat" = {
inherit (bin) owner group mode type;
src = pkgs.writeTextFile {
name = "beatsaber-launch.bat";
executable = true;
text = launchbeatsaber;
};
};
"${cfg.binDir}/fpfc.bat" = {
inherit (bin) owner group mode type;
src = pkgs.writeTextFile {
name = "beatsaber-fpfc.bat";
executable = true;
text = fpfcbeatsaber;
};
};
"${cfg.binDir}/setup.bat" = {
inherit (bin) owner group mode type;
src = pkgs.writeTextFile {
name = "beatsaber-setup.bat";
executable = true;
text = setupbeatsaber;
};
};
"${cfg.binDir}/local-launch.bat" = {
inherit (bin) owner group mode type;
src = pkgs.writeTextFile {
name = "beatsaber-local-launch.bat";
executable = true;
text = localbeatsaber-launch;
};
};
"${cfg.binDir}/local-mount.bat" = {
inherit (bin) owner group mode type;
src = pkgs.writeTextFile {
name = "beatsaber-local-mount.bat";
executable = true;
text = localbeatsaber-mount;
};
};
"${cfg.binDir}/local-vanilla.bat" = {
inherit (bin) owner group mode type;
src = pkgs.writeTextFile {
name = "beatsaber-local-vanilla.bat";
executable = true;
text = localbeatsaber-vanilla;
};
};
"${cfg.binDir}/vanilla.bat" = {
inherit (bin) owner group mode type;
src = pkgs.writeTextFile {
name = "beatsaber-version-vanilla.bat";
executable = true;
text = vanilla;
};
};
"${cfg.binDir}/ModAssistant.exe" = {
inherit (bin) owner group mode type;
src = pkgs.fetchurl {
url = "https://github.com/Assistant/ModAssistant/releases/download/v1.1.32/ModAssistant.exe";
hash = "sha256-ozu2gYFiz+2BjptqL80DmUopbahbyGKFO1IPd7BhVPM=";
executable = true;
};
};
} // versionBinFiles
// userBinFiles;
in { in {
enable = mkIf cfg.setup true; enable = mkIf cfg.setup true;
files = mkIf cfg.setup (mkMerge ( files = mkIf cfg.setup (mkMerge (

View file

@ -24,7 +24,10 @@
... ...
}: let }: let
cfg = config.networking.access; cfg = config.networking.access;
addressForAttr = if config.networking.enableIPv6 then "address6ForNetwork" else "address4ForNetwork"; addressForAttr =
if config.networking.enableIPv6
then "address6ForNetwork"
else "address4ForNetwork";
has'Int = system.network.networks.int.enable or false; has'Int = system.network.networks.int.enable or false;
has'Local = system.network.networks.local.enable or false; has'Local = system.network.networks.local.enable or false;
has'Tail' = system.network.networks.tail.enable or false; has'Tail' = system.network.networks.tail.enable or false;
@ -48,26 +51,53 @@
forSystem = access.systemFor hostName; forSystem = access.systemFor hostName;
forSystemHas = network: forSystem.access ? ${addressForAttr}.${network} || forSystem.access ? address4ForNetwork.${network}; forSystemHas = network: forSystem.access ? ${addressForAttr}.${network} || forSystem.access ? address4ForNetwork.${network};
err = throw "no interface found between ${config.networking.hostName} -> ${hostName}@${network}"; err = throw "no interface found between ${config.networking.hostName} -> ${hostName}@${network}";
fallback = if nameAllowed fallback =
if nameAllowed
then lib.warn "getAddressFor hostname fallback for ${config.networking.hostName} -> ${hostName}@${network}" (access.getHostnameFor hostName network) then lib.warn "getAddressFor hostname fallback for ${config.networking.hostName} -> ${hostName}@${network}" (access.getHostnameFor hostName network)
else err; else err;
local = forSystem.access.${addressForAttr}.local or forSystem.access.address4ForNetwork.local or fallback; local = forSystem.access.${addressForAttr}.local or forSystem.access.address4ForNetwork.local or fallback;
int = forSystem.access.${addressForAttr}.int or forSystem.access.address4ForNetwork.int or fallback; int = forSystem.access.${addressForAttr}.int or forSystem.access.address4ForNetwork.int or fallback;
tail = forSystem.access.${addressForAttr}.tail or fallback; tail = forSystem.access.${addressForAttr}.tail or fallback;
in { in
lan = {
if hostName == system.name then forSystem.access.${addressForAttr}.localhost lan =
else if has'Int && forSystemHas "int" then int if hostName == system.name
else if has'Local && forSystemHas "local" then local then forSystem.access.${addressForAttr}.localhost
else fallback; else if has'Int && forSystemHas "int"
${if has'Local then "local" else null} = local; then int
${if has'Int then "int" else null} = int; else if has'Local && forSystemHas "local"
${if has'Tail then "tail" else null} = tail; then local
}.${network} or fallback; 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 { in {
inherit (systemAccess) inherit
hostnameForNetwork address4ForNetwork address6ForNetwork (systemAccess)
systemForService systemForServiceId; hostnameForNetwork
address4ForNetwork
address6ForNetwork
systemForService
systemForServiceId
;
addressForNetwork = systemAccess.${addressForAttr}; addressForNetwork = systemAccess.${addressForAttr};
systemFor = hostName: systemFor = hostName:
if hostName == config.networking.hostName if hostName == config.networking.hostName
@ -91,34 +121,63 @@
getHostnameFor = hostName: network: let getHostnameFor = hostName: network: let
forSystem = access.systemFor hostName; forSystem = access.systemFor hostName;
err = throw "no hostname found between ${config.networking.hostName} and ${hostName}@${network}"; err = throw "no hostname found between ${config.networking.hostName} and ${hostName}@${network}";
in { in
lan = {
if hostName == system.name then forSystem.access.hostnameForNetwork.localhost lan =
else if has'Int && forSystem.access.hostnameForNetwork ? int then forSystem.access.hostnameForNetwork.int if hostName == system.name
else if has'Local && forSystem.access.hostnameForNetwork ? local then forSystem.access.hostnameForNetwork.local then forSystem.access.hostnameForNetwork.localhost
else err; else if has'Int && forSystem.access.hostnameForNetwork ? int
${if has'Local then "local" else null} = forSystem.access.hostnameForNetwork.local or err; then forSystem.access.hostnameForNetwork.int
${if has'Int then "int" else null} = forSystem.access.hostnameForNetwork.int or err; else if has'Local && forSystem.access.hostnameForNetwork ? local
${if has'Tail then "tail" else null} = forSystem.access.hostnameForNetwork.tail or err; then forSystem.access.hostnameForNetwork.local
}.${network} 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 = { proxyUrlFor = {
system ? if serviceId != null then access.systemForServiceId serviceId else access.systemForService serviceName, 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, serviceName ? mapNullable (serviceId: (findSingle (s: s.id == serviceId) null null (attrValues system.exports.services)).name) serviceId,
serviceId ? null, serviceId ? null,
service ? system.exports.services.${serviceName}, service ? system.exports.services.${serviceName},
portName ? "default", portName ? "default",
network ? "lan", network ? "lan",
scheme ? null, scheme ? null,
getAddressFor ? "getAddressFor" getAddressFor ? "getAddressFor",
}: let }: let
port = service.ports.${portName}; port = service.ports.${portName};
scheme' = if scheme == null then port.protocol else scheme; scheme' =
port' = if !port.enable 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" then throw "${system.name}.exports.services.${service.name}.ports.${portName} isn't enabled"
else ":${toString port.port}"; else ":${toString port.port}";
host = access.${getAddressFor} system.name network; host = access.${getAddressFor} system.name network;
url = "${scheme'}://${mkAddress6 host}${port'}"; url = "${scheme'}://${mkAddress6 host}${port'}";
in assert service.enable; url; in
assert service.enable; url;
}; };
}; };
networking.tempAddresses = mkIf cfg.global.enable ( networking.tempAddresses = mkIf cfg.global.enable (
@ -142,9 +201,11 @@ in {
default = domain; default = domain;
}; };
global.enable = mkEnableOption "globally routeable"; global.enable = mkEnableOption "globally routeable";
online.enable = mkEnableOption "a deployed machine" // { online.enable =
default = true; mkEnableOption "a deployed machine"
}; // {
default = true;
};
hostnameForNetwork = mkOption { hostnameForNetwork = mkOption {
type = attrsOf str; type = attrsOf str;
default = {}; default = {};
@ -164,7 +225,12 @@ in {
]; ];
access = let access = let
noNetwork = { enable = false; address4 = null; address6 = null; fqdn = null; }; noNetwork = {
enable = false;
address4 = null;
address6 = null;
fqdn = null;
};
local = config.network.networks.local or noNetwork; local = config.network.networks.local or noNetwork;
int = config.network.networks.int or noNetwork; int = config.network.networks.int or noNetwork;
mapNetwork' = mkDefault: attr: network: mkIf (network.enable && network.${attr} != null) (mkDefault network.${attr}); mapNetwork' = mkDefault: attr: network: mkIf (network.enable && network.${attr} != null) (mkDefault network.${attr});
@ -216,12 +282,14 @@ in {
hasService = system: system.config.exports.services.${service}.enable; hasService = system: system.config.exports.services.${service}.enable;
notFound = throw "no system found serving ${service}"; notFound = throw "no system found serving ${service}";
multiple = throw "multiple systems found serving ${service}"; multiple = throw "multiple systems found serving ${service}";
in (findSingle hasService notFound multiple (attrValues systems)).config; in
(findSingle hasService notFound multiple (attrValues systems)).config;
systemForServiceId = serviceId: let systemForServiceId = serviceId: let
hasService = system: findSingle (service: service.id == serviceId && service.enable) null multiple (attrValues system.config.exports.services) != null; hasService = system: findSingle (service: service.id == serviceId && service.enable) null multiple (attrValues system.config.exports.services) != null;
notFound = throw "no system found serving ${serviceId}"; notFound = throw "no system found serving ${serviceId}";
multiple = throw "multiple systems found serving ${serviceId}"; multiple = throw "multiple systems found serving ${serviceId}";
in (findSingle hasService notFound multiple (attrValues systems)).config; in
(findSingle hasService notFound multiple (attrValues systems)).config;
}; };
}; };
} }

View file

@ -10,9 +10,11 @@
inherit (lib.modules) mkIf mkOptionDefault; inherit (lib.modules) mkIf mkOptionDefault;
in { in {
options.ci = with lib.types; { options.ci = with lib.types; {
enable = mkEnableOption "build via CI" // { enable =
default = config.type == "NixOS"; mkEnableOption "build via CI"
}; // {
default = config.type == "NixOS";
};
allowFailure = mkOption { allowFailure = mkOption {
type = bool; type = bool;
default = false; default = false;

View file

@ -1,4 +1,8 @@
{lib, gensokyo-zone, ...}: let {
lib,
gensokyo-zone,
...
}: let
inherit (gensokyo-zone.lib) mapAlmostOptionDefaults mkAlmostOptionDefault; inherit (gensokyo-zone.lib) mapAlmostOptionDefaults mkAlmostOptionDefault;
inherit (lib.attrsets) mapAttrs; inherit (lib.attrsets) mapAttrs;
in { in {

View file

@ -1,8 +1,12 @@
{lib, gensokyo-zone, ...}: let {
lib,
gensokyo-zone,
...
}: let
inherit (gensokyo-zone.lib) mapAlmostOptionDefaults mkAlmostOptionDefault; inherit (gensokyo-zone.lib) mapAlmostOptionDefaults mkAlmostOptionDefault;
inherit (lib.attrsets) mapAttrs; inherit (lib.attrsets) mapAttrs;
in { in {
config.exports.services.dnsmasq = { config, ... }: { config.exports.services.dnsmasq = {config, ...}: {
id = mkAlmostOptionDefault "dns"; id = mkAlmostOptionDefault "dns";
nixos = { nixos = {
serviceAttr = "dnsmasq"; serviceAttr = "dnsmasq";

View file

@ -24,7 +24,12 @@
}; };
id = mkOption { id = mkOption {
type = str; type = str;
default = cfg.services.${config.serviceName}.id/* or config.name*/; default =
cfg.services.${config.serviceName}.id
/*
or config.name
*/
;
}; };
}; };
}; };

View file

@ -1,4 +1,8 @@
{lib, gensokyo-zone, ...}: let {
lib,
gensokyo-zone,
...
}: let
inherit (gensokyo-zone.lib) mapAlmostOptionDefaults mkAlmostOptionDefault; inherit (gensokyo-zone.lib) mapAlmostOptionDefaults mkAlmostOptionDefault;
inherit (lib.attrsets) mapAttrs; inherit (lib.attrsets) mapAttrs;
in { in {

View file

@ -1,4 +1,8 @@
{lib, gensokyo-zone, ...}: let {
lib,
gensokyo-zone,
...
}: let
inherit (gensokyo-zone.lib) mapAlmostOptionDefaults mkAlmostOptionDefault; inherit (gensokyo-zone.lib) mapAlmostOptionDefaults mkAlmostOptionDefault;
inherit (lib.attrsets) mapAttrs; inherit (lib.attrsets) mapAttrs;
in { in {

View file

@ -1,14 +1,19 @@
{lib, gensokyo-zone, ...}: let {
lib,
gensokyo-zone,
...
}: let
inherit (gensokyo-zone.lib) mapAlmostOptionDefaults mkAlmostOptionDefault; inherit (gensokyo-zone.lib) mapAlmostOptionDefaults mkAlmostOptionDefault;
inherit (lib.modules) mkIf; inherit (lib.modules) mkIf;
inherit (lib.attrsets) mapAttrs; inherit (lib.attrsets) mapAttrs;
inherit (lib.lists) all imap0; inherit (lib.lists) all imap0;
inherit (lib.trivial) id; inherit (lib.trivial) id;
in { in {
config.exports.services.home-assistant = { config, ... }: let config.exports.services.home-assistant = {config, ...}: let
mkAssertion = f: nixosConfig: let mkAssertion = f: nixosConfig: let
cfg = nixosConfig.services.home-assistant; cfg = nixosConfig.services.home-assistant;
in f nixosConfig cfg; in
f nixosConfig cfg;
assertPort = nixosConfig: cfg: { assertPort = nixosConfig: cfg: {
assertion = config.ports.default.port == cfg.config.http.server_port; assertion = config.ports.default.port == cfg.config.http.server_port;
message = "port mismatch"; message = "port mismatch";
@ -16,10 +21,11 @@ in {
assertHomekitPort = let assertHomekitPort = let
portName = i: "homekit${toString i}"; portName = i: "homekit${toString i}";
mkAssertPort = i: homekit: config.ports.${portName i}.port or null == homekit.port; mkAssertPort = i: homekit: config.ports.${portName i}.port or null == homekit.port;
in nixosConfig: cfg: { in
assertion = all id (imap0 mkAssertPort cfg.config.homekit); nixosConfig: cfg: {
message = "homekit port mismatch"; assertion = all id (imap0 mkAssertPort cfg.config.homekit);
}; message = "homekit port mismatch";
};
in { in {
id = mkAlmostOptionDefault "home"; id = mkAlmostOptionDefault "home";
nixos = { nixos = {

View file

@ -1,8 +1,12 @@
{lib, gensokyo-zone, ...}: let {
lib,
gensokyo-zone,
...
}: let
inherit (gensokyo-zone.lib) mapAlmostOptionDefaults mkAlmostOptionDefault; inherit (gensokyo-zone.lib) mapAlmostOptionDefaults mkAlmostOptionDefault;
inherit (lib.modules) mkIf; inherit (lib.modules) mkIf;
in { in {
config.exports.services.invidious = { config, ... }: { config.exports.services.invidious = {config, ...}: {
id = mkAlmostOptionDefault "yt"; id = mkAlmostOptionDefault "yt";
nixos = { nixos = {
serviceAttr = "invidious"; serviceAttr = "invidious";

View file

@ -1,8 +1,12 @@
{lib, gensokyo-zone, ...}: let {
lib,
gensokyo-zone,
...
}: let
inherit (gensokyo-zone.lib) mapAlmostOptionDefaults; inherit (gensokyo-zone.lib) mapAlmostOptionDefaults;
inherit (lib.attrsets) mapAttrs; inherit (lib.attrsets) mapAttrs;
in { in {
config.exports.services.kerberos = { config, ... }: { config.exports.services.kerberos = {config, ...}: {
id = "krb5"; id = "krb5";
ports = mapAttrs (_: mapAlmostOptionDefaults) { ports = mapAttrs (_: mapAlmostOptionDefaults) {
default = { default = {

View file

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

View file

@ -1,8 +1,12 @@
{lib, gensokyo-zone, ...}: let {
lib,
gensokyo-zone,
...
}: let
inherit (gensokyo-zone.lib) mapAlmostOptionDefaults mkAlmostOptionDefault; inherit (gensokyo-zone.lib) mapAlmostOptionDefaults mkAlmostOptionDefault;
inherit (lib.attrsets) mapAttrs; inherit (lib.attrsets) mapAttrs;
in { in {
config.exports.services.ldap = { config, ... }: { config.exports.services.ldap = {config, ...}: {
defaults.port.listen = mkAlmostOptionDefault "lan"; defaults.port.listen = mkAlmostOptionDefault "lan";
ports = mapAttrs (_: mapAlmostOptionDefaults) { ports = mapAttrs (_: mapAlmostOptionDefaults) {
default = { default = {

View file

@ -1,12 +1,17 @@
{lib, gensokyo-zone, ...}: let {
lib,
gensokyo-zone,
...
}: let
inherit (gensokyo-zone.lib) mapAlmostOptionDefaults mkAlmostOptionDefault; inherit (gensokyo-zone.lib) mapAlmostOptionDefaults mkAlmostOptionDefault;
inherit (lib.modules) mkIf; inherit (lib.modules) mkIf;
inherit (lib.attrsets) mapAttrs; inherit (lib.attrsets) mapAttrs;
in { in {
config.exports.services.minecraft-bedrock-server = { config, ... }: let config.exports.services.minecraft-bedrock-server = {config, ...}: let
mkAssertion = f: nixosConfig: let mkAssertion = f: nixosConfig: let
cfg = nixosConfig.services.minecraft-bedrock-server; cfg = nixosConfig.services.minecraft-bedrock-server;
in f nixosConfig cfg; in
f nixosConfig cfg;
in { in {
nixos = { nixos = {
serviceAttr = "minecraft-bedrock-server"; serviceAttr = "minecraft-bedrock-server";

View file

@ -1,11 +1,15 @@
{lib, gensokyo-zone, ...}: let {
lib,
gensokyo-zone,
...
}: let
inherit (gensokyo-zone.lib) mapAlmostOptionDefaults mkAlmostOptionDefault; inherit (gensokyo-zone.lib) mapAlmostOptionDefaults mkAlmostOptionDefault;
inherit (lib.modules) mkIf; inherit (lib.modules) mkIf;
inherit (lib.attrsets) mapAttrs; inherit (lib.attrsets) mapAttrs;
inherit (lib.lists) all imap0; inherit (lib.lists) all imap0;
inherit (lib.trivial) id; inherit (lib.trivial) id;
in { in {
config.exports.services.mosquitto = { config, ... }: { config.exports.services.mosquitto = {config, ...}: {
id = mkAlmostOptionDefault "mqtt"; id = mkAlmostOptionDefault "mqtt";
nixos = { nixos = {
serviceAttr = "mosquitto"; serviceAttr = "mosquitto";
@ -13,7 +17,8 @@ in {
(nixosConfig: let (nixosConfig: let
cfg = nixosConfig.services.mosquitto; cfg = nixosConfig.services.mosquitto;
portName = i: portName = i:
if i == 0 then "default" if i == 0
then "default"
else "listener${toString i}"; else "listener${toString i}";
mkAssertPort = i: listener: config.ports.${portName i}.port or null == listener.port; mkAssertPort = i: listener: config.ports.${portName i}.port or null == listener.port;
in { in {

View file

@ -1,8 +1,12 @@
{lib, gensokyo-zone, ...}: let {
lib,
gensokyo-zone,
...
}: let
inherit (gensokyo-zone.lib) mapAlmostOptionDefaults mkAlmostOptionDefault; inherit (gensokyo-zone.lib) mapAlmostOptionDefaults mkAlmostOptionDefault;
inherit (lib.attrsets) mapAttrs; inherit (lib.attrsets) mapAttrs;
in { in {
config.exports.services.motion = { config, ... }: { config.exports.services.motion = {config, ...}: {
defaults.port.listen = mkAlmostOptionDefault "lan"; defaults.port.listen = mkAlmostOptionDefault "lan";
ports = mapAttrs (_: mapAlmostOptionDefaults) { ports = mapAttrs (_: mapAlmostOptionDefaults) {
default = { default = {

View file

@ -1,24 +1,34 @@
{lib, gensokyo-zone, ...}: let {
lib,
gensokyo-zone,
...
}: let
inherit (gensokyo-zone.lib) mapAlmostOptionDefaults mkAlmostOptionDefault; inherit (gensokyo-zone.lib) mapAlmostOptionDefaults mkAlmostOptionDefault;
inherit (lib.modules) mkIf; inherit (lib.modules) mkIf;
inherit (lib.attrsets) mapAttrs; inherit (lib.attrsets) mapAttrs;
in { in {
config.exports.services.nfs = { config, ... }: let config.exports.services.nfs = {config, ...}: let
mkAssertion = f: nixosConfig: let mkAssertion = f: nixosConfig: let
cfg = nixosConfig.services.nfs; cfg = nixosConfig.services.nfs;
in f nixosConfig cfg; in
mkAssertionPort = portName: mkAssertion (nixosConfig: cfg: let f nixosConfig cfg;
portAttr = "${portName}Port"; mkAssertionPort = portName:
in { mkAssertion (nixosConfig: cfg: let
assertion = mkAssertPort config.ports.${portName} cfg.server.${portAttr}; portAttr = "${portName}Port";
message = "${portAttr} mismatch"; in {
}); assertion = mkAssertPort config.ports.${portName} cfg.server.${portAttr};
message = "${portAttr} mismatch";
});
mkAssertPort = port: cfgPort: let mkAssertPort = port: cfgPort: let
cmpPort = if port.enable then port.port else null; cmpPort =
in cfgPort == cmpPort; if port.enable
then port.port
else null;
in
cfgPort == cmpPort;
in { in {
nixos = { nixos = {
serviceAttrPath = [ "services" "nfs" "server" ]; serviceAttrPath = ["services" "nfs" "server"];
assertions = mkIf config.enable [ assertions = mkIf config.enable [
(mkAssertionPort "statd") (mkAssertionPort "statd")
(mkAssertionPort "lockd") (mkAssertionPort "lockd")

View file

@ -1,12 +1,17 @@
{lib, gensokyo-zone, ...}: let {
lib,
gensokyo-zone,
...
}: let
inherit (gensokyo-zone.lib) mapAlmostOptionDefaults mkAlmostOptionDefault; inherit (gensokyo-zone.lib) mapAlmostOptionDefaults mkAlmostOptionDefault;
inherit (lib.modules) mkIf; inherit (lib.modules) mkIf;
inherit (lib.attrsets) mapAttrs; inherit (lib.attrsets) mapAttrs;
in { in {
config.exports.services.nginx = { config, ... }: let config.exports.services.nginx = {config, ...}: let
mkAssertion = f: nixosConfig: let mkAssertion = f: nixosConfig: let
cfg = nixosConfig.services.nginx; cfg = nixosConfig.services.nginx;
in f nixosConfig cfg; in
f nixosConfig cfg;
assertPorts = nixosConfig: cfg: { assertPorts = nixosConfig: cfg: {
assertion = config.ports.http.port == cfg.defaultHTTPListenPort && config.ports.https.port == cfg.defaultSSLListenPort; assertion = config.ports.http.port == cfg.defaultHTTPListenPort && config.ports.https.port == cfg.defaultSSLListenPort;
message = "ports mismatch"; message = "ports mismatch";

View file

@ -1,22 +1,28 @@
{lib, gensokyo-zone, ...}: let {
lib,
gensokyo-zone,
...
}: let
inherit (gensokyo-zone.lib) mapAlmostOptionDefaults mkAlmostOptionDefault; inherit (gensokyo-zone.lib) mapAlmostOptionDefaults mkAlmostOptionDefault;
inherit (lib.modules) mkIf; inherit (lib.modules) mkIf;
inherit (lib.attrsets) mapAttrs; inherit (lib.attrsets) mapAttrs;
in { in {
config.exports.services.openwebrx = { config, ... }: { config.exports.services.openwebrx = {config, ...}: {
id = mkAlmostOptionDefault "webrx"; id = mkAlmostOptionDefault "webrx";
nixos = { nixos = {
serviceAttr = "openwebrx"; serviceAttr = "openwebrx";
assertions = let assertions = let
mkAssertion = f: nixosConfig: let mkAssertion = f: nixosConfig: let
cfg = nixosConfig.services.openwebrx; cfg = nixosConfig.services.openwebrx;
in f nixosConfig cfg; in
in mkIf config.enable [ f nixosConfig cfg;
(mkAssertion (nixosConfig: cfg: { in
assertion = config.ports.default.port == cfg.port; mkIf config.enable [
message = "port mismatch"; (mkAssertion (nixosConfig: cfg: {
})) assertion = config.ports.default.port == cfg.port;
]; message = "port mismatch";
}))
];
}; };
defaults.port.listen = mkAlmostOptionDefault "lan"; defaults.port.listen = mkAlmostOptionDefault "lan";
ports = mapAttrs (_: mapAlmostOptionDefaults) { ports = mapAttrs (_: mapAlmostOptionDefaults) {

View file

@ -1,4 +1,8 @@
{lib, gensokyo-zone, ...}: let {
lib,
gensokyo-zone,
...
}: let
inherit (gensokyo-zone.lib) mapAlmostOptionDefaults mkAlmostOptionDefault; inherit (gensokyo-zone.lib) mapAlmostOptionDefaults mkAlmostOptionDefault;
inherit (lib.attrsets) mapAttrs; inherit (lib.attrsets) mapAttrs;
in { in {

View file

@ -1,11 +1,16 @@
{lib, gensokyo-zone, ...}: let {
lib,
gensokyo-zone,
...
}: let
inherit (gensokyo-zone.lib) mapAlmostOptionDefaults; inherit (gensokyo-zone.lib) mapAlmostOptionDefaults;
inherit (lib.modules) mkIf; inherit (lib.modules) mkIf;
in { in {
config.exports.services.postgresql = { config, ... }: let config.exports.services.postgresql = {config, ...}: let
mkAssertion = f: nixosConfig: let mkAssertion = f: nixosConfig: let
cfg = nixosConfig.services.postgresql; cfg = nixosConfig.services.postgresql;
in f nixosConfig cfg; in
f nixosConfig cfg;
in { in {
nixos = { nixos = {
serviceAttr = "postgresql"; serviceAttr = "postgresql";

View file

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

View file

@ -1,4 +1,8 @@
{lib, gensokyo-zone, ...}: let {
lib,
gensokyo-zone,
...
}: let
inherit (gensokyo-zone.lib) mapAlmostOptionDefaults mkAlmostOptionDefault; inherit (gensokyo-zone.lib) mapAlmostOptionDefaults mkAlmostOptionDefault;
inherit (lib.attrsets) mapAttrs; inherit (lib.attrsets) mapAttrs;
in { in {

View file

@ -12,7 +12,11 @@
inherit (lib.trivial) mapNullable; inherit (lib.trivial) mapNullable;
inherit (lib.strings) concatStringsSep; inherit (lib.strings) concatStringsSep;
systemConfig = config; systemConfig = config;
portModule = {config, service, ...}: { portModule = {
config,
service,
...
}: {
options = with lib.types; { options = with lib.types; {
enable = enable =
mkEnableOption "port" mkEnableOption "port"
@ -86,7 +90,7 @@
}; };
assertions = mkOption { assertions = mkOption {
type = listOf (functionTo attrs); type = listOf (functionTo attrs);
default = [ ]; default = [];
}; };
}; };
defaults = { defaults = {
@ -107,7 +111,8 @@
serviceConfig = getAttrFromPath config.nixos.serviceAttrPath; serviceConfig = getAttrFromPath config.nixos.serviceAttrPath;
mkAssertion = f: nixosConfig: let mkAssertion = f: nixosConfig: let
cfg = serviceConfig nixosConfig; cfg = serviceConfig nixosConfig;
in f nixosConfig cfg; in
f nixosConfig cfg;
enableAssertion = nixosConfig: cfg: { enableAssertion = nixosConfig: cfg: {
assertion = (! cfg ? enable) || (config.enable == cfg.enable); assertion = (! cfg ? enable) || (config.enable == cfg.enable);
message = "enable == nixosConfig.${concatStringsSep "." config.nixos.serviceAttrPath}.enable"; message = "enable == nixosConfig.${concatStringsSep "." config.nixos.serviceAttrPath}.enable";
@ -120,12 +125,18 @@
}; };
}; };
}; };
nixosModule = {config, system, ...}: let nixosModule = {
config,
system,
...
}: let
mapAssertion = service: a: let mapAssertion = service: a: let
res = a config; res = a config;
in res // { in
message = "system.exports.${service.name}: " + res.message or "assertion failed"; res
}; // {
message = "system.exports.${service.name}: " + res.message or "assertion failed";
};
assertions = mapAttrsToList (_: service: map (mapAssertion service) service.nixos.assertions) system.exports.services; assertions = mapAttrsToList (_: service: map (mapAssertion service) service.nixos.assertions) system.exports.services;
in { in {
config = { config = {

View file

@ -1,13 +1,18 @@
{lib, gensokyo-zone, ...}: let {
lib,
gensokyo-zone,
...
}: let
inherit (gensokyo-zone.lib) mapAlmostOptionDefaults mkAlmostOptionDefault; inherit (gensokyo-zone.lib) mapAlmostOptionDefaults mkAlmostOptionDefault;
inherit (lib.modules) mkIf; inherit (lib.modules) mkIf;
inherit (lib.attrsets) mapAttrs filterAttrs mapAttrsToList; inherit (lib.attrsets) mapAttrs filterAttrs mapAttrsToList;
inherit (lib.lists) sort; inherit (lib.lists) sort;
in { in {
config.exports.services.sshd = { config, ... }: let config.exports.services.sshd = {config, ...}: let
mkAssertion = f: nixosConfig: let mkAssertion = f: nixosConfig: let
cfg = nixosConfig.services.openssh; cfg = nixosConfig.services.openssh;
in f nixosConfig cfg; in
f nixosConfig cfg;
sorted = sort (a: b: a > b); sorted = sort (a: b: a > b);
assertPorts = nixosConfig: cfg: let assertPorts = nixosConfig: cfg: let
nixosPorts = cfg.ports; nixosPorts = cfg.ports;

View file

@ -1,8 +1,12 @@
{lib, gensokyo-zone, ...}: let {
lib,
gensokyo-zone,
...
}: let
inherit (gensokyo-zone.lib) mapAlmostOptionDefaults mkAlmostOptionDefault; inherit (gensokyo-zone.lib) mapAlmostOptionDefaults mkAlmostOptionDefault;
inherit (lib.attrsets) mapAttrs; inherit (lib.attrsets) mapAttrs;
in { in {
config.exports.services.unifi = { config, ... }: { config.exports.services.unifi = {config, ...}: {
nixos.serviceAttr = "unifi"; nixos.serviceAttr = "unifi";
defaults.port.listen = mkAlmostOptionDefault "lan"; defaults.port.listen = mkAlmostOptionDefault "lan";
ports = mapAttrs (_: mapAlmostOptionDefaults) { ports = mapAttrs (_: mapAlmostOptionDefaults) {

View file

@ -1,8 +1,12 @@
{lib, gensokyo-zone, ...}: let {
lib,
gensokyo-zone,
...
}: let
inherit (gensokyo-zone.lib) mapAlmostOptionDefaults mkAlmostOptionDefault; inherit (gensokyo-zone.lib) mapAlmostOptionDefaults mkAlmostOptionDefault;
inherit (lib.modules) mkIf; inherit (lib.modules) mkIf;
in { in {
config.exports.services.vouch-proxy = { config, ... }: { config.exports.services.vouch-proxy = {config, ...}: {
id = mkAlmostOptionDefault "login"; id = mkAlmostOptionDefault "login";
defaults.port.listen = mkAlmostOptionDefault "localhost"; defaults.port.listen = mkAlmostOptionDefault "localhost";
nixos = { nixos = {

View file

@ -1,8 +1,12 @@
{lib, gensokyo-zone, ...}: let {
lib,
gensokyo-zone,
...
}: let
inherit (gensokyo-zone.lib) mapAlmostOptionDefaults mkAlmostOptionDefault; inherit (gensokyo-zone.lib) mapAlmostOptionDefaults mkAlmostOptionDefault;
inherit (lib.modules) mkIf; inherit (lib.modules) mkIf;
in { in {
config.exports.services.zigbee2mqtt = { config, ... }: { config.exports.services.zigbee2mqtt = {config, ...}: {
id = mkAlmostOptionDefault "z2m"; id = mkAlmostOptionDefault "z2m";
nixos = { nixos = {
serviceAttr = "zigbee2mqtt"; serviceAttr = "zigbee2mqtt";

View file

@ -1,13 +1,21 @@
let let
fileModule = {config, name, gensokyo-zone, lib, ...}: let fileModule = {
config,
name,
gensokyo-zone,
lib,
...
}: let
inherit (lib.options) mkOption mkEnableOption; inherit (lib.options) mkOption mkEnableOption;
inherit (lib.modules) mkOptionDefault; inherit (lib.modules) mkOptionDefault;
inherit (lib.strings) hasPrefix removePrefix; inherit (lib.strings) hasPrefix removePrefix;
in { in {
options = with lib.types; { options = with lib.types; {
enable = mkEnableOption "external file" // { enable =
default = true; mkEnableOption "external file"
}; // {
default = true;
};
path = mkOption { path = mkOption {
type = str; type = str;
default = name; default = name;
@ -35,25 +43,33 @@ let
relativeSource = let relativeSource = let
flakeRoot = toString gensokyo-zone.self + "/"; flakeRoot = toString gensokyo-zone.self + "/";
sourcePath = toString config.source; sourcePath = toString config.source;
in mkOptionDefault ( in
if hasPrefix flakeRoot sourcePath then removePrefix flakeRoot sourcePath mkOptionDefault (
else null if hasPrefix flakeRoot sourcePath
); then removePrefix flakeRoot sourcePath
else null
);
}; };
}; };
in {config, gensokyo-zone, lib, ...}: let in
inherit (lib.options) mkOption; {
in { config,
options.extern = with lib.types; { gensokyo-zone,
files = mkOption { lib,
type = attrsOf (submoduleWith { ...
modules = [ fileModule ]; }: let
specialArgs = { inherit (lib.options) mkOption;
inherit gensokyo-zone; in {
system = config; options.extern = with lib.types; {
}; files = mkOption {
}); type = attrsOf (submoduleWith {
default = { }; modules = [fileModule];
specialArgs = {
inherit gensokyo-zone;
system = config;
};
});
default = {};
};
}; };
}; }
}

View file

@ -41,7 +41,7 @@ in {
}; };
modules = mkOption { modules = mkOption {
type = listOf unspecified; type = listOf unspecified;
default = [ ]; default = [];
}; };
specialArgs = mkOption { specialArgs = mkOption {
type = attrs; type = attrs;
@ -101,10 +101,13 @@ in {
darwin = inputs.darwin.lib.darwinSystem; darwin = inputs.darwin.lib.darwinSystem;
macos = inputs.darwin.lib.darwinSystem; macos = inputs.darwin.lib.darwinSystem;
} }
.${string.toLower config.type} or null; .${string.toLower config.type}
built = mkOptionDefault (mapNullable (builder: builder { or null;
inherit (config) system modules specialArgs; built = mkOptionDefault (mapNullable (builder:
}) config.builder); builder {
inherit (config) system modules specialArgs;
})
config.builder);
specialArgs = { specialArgs = {
inherit name inputs std Std meta; inherit name inputs std Std meta;
inherit (inputs.self.lib) gensokyo-zone; inherit (inputs.self.lib) gensokyo-zone;

View file

@ -1,9 +1,19 @@
{config, lib, inputs, ...}: let {
config,
lib,
inputs,
...
}: let
inherit (inputs.self.lib.lib) eui64; inherit (inputs.self.lib.lib) eui64;
inherit (lib.options) mkOption mkEnableOption; inherit (lib.options) mkOption mkEnableOption;
inherit (lib.modules) mkIf mkOptionDefault; inherit (lib.modules) mkIf mkOptionDefault;
inherit (lib.trivial) mapNullable; inherit (lib.trivial) mapNullable;
networkModule = { config, name, system, ... }: let networkModule = {
config,
name,
system,
...
}: let
knownNetworks = { knownNetworks = {
local.slaac = { local.slaac = {
enable = true; enable = true;
@ -13,9 +23,11 @@
}; };
in { in {
options = with lib.types; { options = with lib.types; {
enable = mkEnableOption "network" // { enable =
default = true; mkEnableOption "network"
}; // {
default = true;
};
slaac = { slaac = {
enable = mkOption { enable = mkOption {
type = bool; type = bool;
@ -65,12 +77,12 @@ in {
options.network = with lib.types; { options.network = with lib.types; {
networks = mkOption { networks = mkOption {
type = attrsOf (submoduleWith { type = attrsOf (submoduleWith {
modules = [ networkModule ]; modules = [networkModule];
specialArgs = { specialArgs = {
system = config; system = config;
}; };
}); });
default = { }; default = {};
}; };
}; };
} }

View file

@ -1,4 +1,8 @@
{config, lib, ...}: let {
config,
lib,
...
}: let
inherit (lib.options) mkOption mkEnableOption; inherit (lib.options) mkOption mkEnableOption;
cfg = config.proxmox.container; cfg = config.proxmox.container;
in { in {

View file

@ -1,4 +1,10 @@
{config, gensokyo-zone, lib, Std, ...}: let {
config,
gensokyo-zone,
lib,
Std,
...
}: let
inherit (Std) UInt; inherit (Std) UInt;
inherit (gensokyo-zone.lib) unmerged eui64 mkAlmostOptionDefault mapAlmostOptionDefaults; inherit (gensokyo-zone.lib) unmerged eui64 mkAlmostOptionDefault mapAlmostOptionDefaults;
inherit (lib.options) mkOption mkEnableOption; inherit (lib.options) mkOption mkEnableOption;
@ -9,11 +15,18 @@
inherit (lib.trivial) mapNullable; inherit (lib.trivial) mapNullable;
cfg = config.proxmox.network; cfg = config.proxmox.network;
internalOffset = 32; internalOffset = 32;
networkInterfaceModule = { config, name, system, ... }: { networkInterfaceModule = {
config,
name,
system,
...
}: {
options = with lib.types; { options = with lib.types; {
enable = mkEnableOption "network interface" // { enable =
default = true; mkEnableOption "network interface"
}; // {
default = true;
};
bridge = mkOption { bridge = mkOption {
type = str; type = str;
default = "vmbr0"; default = "vmbr0";
@ -30,7 +43,7 @@
default = null; default = null;
}; };
address4 = mkOption { address4 = mkOption {
type = nullOr (either (enum [ "dhcp" ]) str); type = nullOr (either (enum ["dhcp"]) str);
default = null; default = null;
}; };
gateway4 = mkOption { gateway4 = mkOption {
@ -38,7 +51,7 @@
default = null; default = null;
}; };
address6 = mkOption { address6 = mkOption {
type = nullOr (either (enum [ "auto" "dhcp" ]) str); type = nullOr (either (enum ["auto" "dhcp"]) str);
default = null; default = null;
}; };
gateway6 = mkOption { gateway6 = mkOption {
@ -47,13 +60,15 @@
}; };
firewall.enable = mkEnableOption "firewall"; firewall.enable = mkEnableOption "firewall";
vm.model = mkOption { vm.model = mkOption {
type = enum [ "virtio" "e1000" "rtl8139" "vmxnet3" ]; type = enum ["virtio" "e1000" "rtl8139" "vmxnet3"];
default = "virtio"; default = "virtio";
}; };
mdns = { mdns = {
enable = mkEnableOption "mDNS" // { enable =
default = config.local.enable && config.id == "net0"; mkEnableOption "mDNS"
}; // {
default = config.local.enable && config.id == "net0";
};
}; };
slaac = { slaac = {
postfix = mkOption { postfix = mkOption {
@ -76,9 +91,11 @@
}; };
}; };
networkd = { networkd = {
enable = mkEnableOption "systemd.network" // { enable =
default = true; mkEnableOption "systemd.network"
}; // {
default = true;
};
name = mkOption { name = mkOption {
type = str; type = str;
default = config.name; default = config.name;
@ -90,14 +107,20 @@
}; };
}; };
config = let config = let
hasAddr4 = ! elem config.address4 [ null "dhcp" ]; hasAddr4 = ! elem config.address4 [null "dhcp"];
hasAddr6 = ! elem config.address6 [ null "dhcp" "auto" ]; hasAddr6 = ! elem config.address6 [null "dhcp" "auto"];
conf = { conf = {
local = mkIf config.local.enable { local = mkIf config.local.enable {
address4 = mkOptionDefault (if hasAddr4 then config.address4 else null); address4 = mkOptionDefault (
if hasAddr4
then config.address4
else null
);
address6 = mkOptionDefault ( address6 = mkOptionDefault (
if config.address6 == "auto" && config.slaac.postfix != null then "fd0a::${config.slaac.postfix}" if config.address6 == "auto" && config.slaac.postfix != null
else if hasAddr6 then config.address6 then "fd0a::${config.slaac.postfix}"
else if hasAddr6
then config.address6
else null else null
); );
}; };
@ -123,7 +146,7 @@
Type = mkOptionDefault "ether"; Type = mkOptionDefault "ether";
}; };
linkConfig = mkMerge [ linkConfig = mkMerge [
(mkIf config.mdns.enable { Multicast = mkOptionDefault true; }) (mkIf config.mdns.enable {Multicast = mkOptionDefault true;})
]; ];
networkConfig = mkMerge [ networkConfig = mkMerge [
(mkIf (config.address6 == "auto") { (mkIf (config.address6 == "auto") {
@ -134,17 +157,20 @@
}) })
]; ];
address = mkMerge [ address = mkMerge [
(mkIf (! elem config.address4 [ null "dhcp" ]) [ config.address4 ]) (mkIf (! elem config.address4 [null "dhcp"]) [config.address4])
(mkIf (! elem config.address6 [ null "auto" "dhcp" ]) [ config.address6 ]) (mkIf (! elem config.address6 [null "auto" "dhcp"]) [config.address6])
]; ];
gateway = mkMerge [ gateway = mkMerge [
(mkIf (config.gateway4 != null) [ config.gateway4 ]) (mkIf (config.gateway4 != null) [config.gateway4])
(mkIf (config.gateway6 != null) [ config.gateway6 ]) (mkIf (config.gateway6 != null) [config.gateway6])
]; ];
DHCP = mkAlmostOptionDefault ( DHCP = mkAlmostOptionDefault (
if config.address4 == "dhcp" && config.address6 == "dhcp" then "yes" if config.address4 == "dhcp" && config.address6 == "dhcp"
else if config.address6 == "dhcp" then "ipv6" then "yes"
else if config.address4 == "dhcp" then "ipv4" else if config.address6 == "dhcp"
then "ipv6"
else if config.address4 == "dhcp"
then "ipv4"
else "no" else "no"
); );
}; };
@ -157,11 +183,11 @@
address4 = mkAlmostOptionDefault "10.9.1.${toString index}/24"; address4 = mkAlmostOptionDefault "10.9.1.${toString index}/24";
address6 = mkAlmostOptionDefault "fd0c::${UInt.toHexLower index}/64"; address6 = mkAlmostOptionDefault "fd0c::${UInt.toHexLower index}/64";
macAddress = mkIf (system.proxmox.network.interfaces.net0.macAddress or null != null && hasPrefix "BC:24:11:" system.proxmox.network.interfaces.net0.macAddress) (mkAlmostOptionDefault ( macAddress = mkIf (system.proxmox.network.interfaces.net0.macAddress or null != null && hasPrefix "BC:24:11:" system.proxmox.network.interfaces.net0.macAddress) (mkAlmostOptionDefault (
replaceStrings [ "BC:24:11:" ] [ "BC:24:19:" ] system.proxmox.network.interfaces.net0.macAddress replaceStrings ["BC:24:11:"] ["BC:24:19:"] system.proxmox.network.interfaces.net0.macAddress
)); ));
networkd.name = mkDefault "_00-int"; networkd.name = mkDefault "_00-int";
networkd.networkSettings = { networkd.networkSettings = {
domains = mkDefault [ ]; # int.${domain}? domains = mkDefault []; # int.${domain}?
linkConfig.RequiredForOnline = false; linkConfig.RequiredForOnline = false;
ipv6AcceptRAConfig = { ipv6AcceptRAConfig = {
Token = mkOptionDefault "static:::${UInt.toHexLower index}"; Token = mkOptionDefault "static:::${UInt.toHexLower index}";
@ -172,21 +198,22 @@
}; };
}; };
}; };
in mkMerge [ in
conf mkMerge [
(mkIf config.internal.enable confInternal) conf
]; (mkIf config.internal.enable confInternal)
];
}; };
in { in {
options.proxmox.network = with lib.types; { options.proxmox.network = with lib.types; {
interfaces = mkOption { interfaces = mkOption {
type = attrsOf (submoduleWith { type = attrsOf (submoduleWith {
modules = [ networkInterfaceModule ]; modules = [networkInterfaceModule];
specialArgs = { specialArgs = {
system = config; system = config;
}; };
}); });
default = { }; default = {};
}; };
internal = { internal = {
interface = mkOption { interface = mkOption {

View file

@ -1,4 +1,9 @@
{config, lib, gensokyo-zone, ...}: let {
config,
lib,
gensokyo-zone,
...
}: let
inherit (gensokyo-zone.lib) mkAlmostOptionDefault; inherit (gensokyo-zone.lib) mkAlmostOptionDefault;
inherit (lib.options) mkOption mkEnableOption; inherit (lib.options) mkOption mkEnableOption;
inherit (lib.modules) mkIf; inherit (lib.modules) mkIf;

View file

@ -1,4 +1,8 @@
{config, lib, ...}: let {
config,
lib,
...
}: let
inherit (lib.options) mkOption mkEnableOption; inherit (lib.options) mkOption mkEnableOption;
cfg = config.proxmox; cfg = config.proxmox;
in { in {

View file

@ -25,7 +25,8 @@ in {
requireAuth = false; requireAuth = false;
}; };
proxy = { proxy = {
upstream = mkIf barcodebuddy.enable (mkDefault upstream = mkIf barcodebuddy.enable (
mkDefault
"nginx'proxied" "nginx'proxied"
); );
host = mkDefault serverName; host = mkDefault serverName;
@ -48,7 +49,7 @@ in {
upstream = mkDefault nginx.virtualHosts.barcodebuddy.proxy.upstream; upstream = mkDefault nginx.virtualHosts.barcodebuddy.proxy.upstream;
host = mkDefault nginx.virtualHosts.barcodebuddy.proxy.host; host = mkDefault nginx.virtualHosts.barcodebuddy.proxy.host;
}; };
locations."/" = { config, ... }: { locations."/" = {config, ...}: {
proxy = { proxy = {
headers.enableRecommended = true; headers.enableRecommended = true;
redirect = { redirect = {

View file

@ -5,8 +5,7 @@
gensokyo-zone, gensokyo-zone,
lib, lib,
... ...
}: }: let
let
inherit (gensokyo-zone.lib) mapOptionDefaults; inherit (gensokyo-zone.lib) mapOptionDefaults;
inherit (lib.options) mkOption mkEnableOption; inherit (lib.options) mkOption mkEnableOption;
inherit (lib.modules) mkIf mkMerge mkDefault mkOptionDefault; inherit (lib.modules) mkIf mkMerge mkDefault mkOptionDefault;
@ -19,7 +18,11 @@ let
ssl_verify_client optional_no_ca; ssl_verify_client optional_no_ca;
''; '';
locations = { locations = {
"/" = { config, xvars, ... }: { "/" = {
config,
xvars,
...
}: {
proxy = { proxy = {
enable = true; enable = true;
upstream = "freeipa"; upstream = "freeipa";
@ -67,9 +70,11 @@ in {
}; };
}; };
kerberos = { kerberos = {
enable = mkEnableOption "proxy kerberos" // { enable =
default = true; mkEnableOption "proxy kerberos"
}; // {
default = true;
};
ports = { ports = {
ticket = mkOption { ticket = mkOption {
type = port; type = port;
@ -177,7 +182,7 @@ in {
kticket4 = mkKrb5Upstream "ticket4"; kticket4 = mkKrb5Upstream "ticket4";
}; };
servers = let servers = let
mkKrb5Server = tcpPort: udpPort: { name, ... }: { mkKrb5Server = tcpPort: udpPort: {name, ...}: {
enable = mkDefault nginx.stream.upstreams.${name}.enable; enable = mkDefault nginx.stream.upstreams.${name}.enable;
listen = { listen = {
tcp = mkIf (tcpPort != null) { tcp = mkIf (tcpPort != null) {
@ -187,7 +192,7 @@ in {
udp = mkIf (udpPort != null) { udp = mkIf (udpPort != null) {
enable = mkDefault kerberos.ports.${udpPort}.enable; enable = mkDefault kerberos.ports.${udpPort}.enable;
port = mkOptionDefault kerberos.ports.${udpPort}.port; port = mkOptionDefault kerberos.ports.${udpPort}.port;
extraParameters = [ "udp" ]; extraParameters = ["udp"];
}; };
}; };
proxy.upstream = name; proxy.upstream = name;
@ -208,11 +213,12 @@ in {
ssl.cert.copyFromVhost = mkDefault "freeipa"; ssl.cert.copyFromVhost = mkDefault "freeipa";
}; };
}; };
in mkMerge [ in
conf mkMerge [
(mkIf nginx.ssl.preread.enable prereadConf) conf
(mkIf cfg.kerberos.enable kerberosConf) (mkIf nginx.ssl.preread.enable prereadConf)
]; (mkIf cfg.kerberos.enable kerberosConf)
];
virtualHosts = let virtualHosts = let
name.shortServer = mkDefault "ipa"; name.shortServer = mkDefault "ipa";
name'cockpit.shortServer = mkDefault "ipa-cock"; name'cockpit.shortServer = mkDefault "ipa-cock";
@ -233,7 +239,11 @@ in {
name.shortServer = mkDefault "idp-ca"; name.shortServer = mkDefault "idp-ca";
locations."/" = mkMerge [ locations."/" = mkMerge [
locations."/" locations."/"
({config, virtualHost, ...}: { ({
config,
virtualHost,
...
}: {
proxy.ssl.host = virtualHost.serverName; proxy.ssl.host = virtualHost.serverName;
proxy.host = config.proxy.ssl.host; proxy.host = config.proxy.ssl.host;
}) })
@ -276,7 +286,7 @@ in {
}; };
freeipa'ldap'local = { freeipa'ldap'local = {
serverName = mkDefault ldap.localDomain; serverName = mkDefault ldap.localDomain;
serverAliases = [ ldap.intDomain ]; serverAliases = [ldap.intDomain];
ssl.cert.copyFromVhost = "freeipa'ldap"; ssl.cert.copyFromVhost = "freeipa'ldap";
globalRedirect = virtualHosts.freeipa'web'local.serverName; globalRedirect = virtualHosts.freeipa'web'local.serverName;
local.enable = true; local.enable = true;
@ -295,16 +305,18 @@ in {
inherit (nginx.stream.servers) krb5 kadmin kpasswd kticket4; inherit (nginx.stream.servers) krb5 kadmin kpasswd kticket4;
in { in {
allowedTCPPorts = mkMerge [ allowedTCPPorts = mkMerge [
(mkIf cfg.kerberos.enable (map (server: (mkIf cfg.kerberos.enable (map (
mkIf (server.enable && server.listen.tcp.enable) server.listen.tcp.port server:
) [ krb5 kticket4 kpasswd kadmin ])) mkIf (server.enable && server.listen.tcp.enable) server.listen.tcp.port
) [krb5 kticket4 kpasswd kadmin]))
(mkIf nginx.ssl.preread.enable [ (mkIf nginx.ssl.preread.enable [
ldapsPort ldapsPort
]) ])
]; ];
allowedUDPPorts = mkIf cfg.kerberos.enable (map (server: allowedUDPPorts = mkIf cfg.kerberos.enable (map (
mkIf (server.enable && server.listen.udp.enable) server.listen.udp.port server:
) [ krb5 kticket4 kpasswd ]); mkIf (server.enable && server.listen.udp.enable) server.listen.udp.port
) [krb5 kticket4 kpasswd]);
}; };
}; };
} }

View file

@ -13,8 +13,15 @@ in {
config.services.nginx = { config.services.nginx = {
virtualHosts = let virtualHosts = let
proxyScheme = "https"; proxyScheme = "https";
url = access.proxyUrlFor { serviceName = "freepbx"; portName = proxyScheme; }; url = access.proxyUrlFor {
ucpUrl = access.proxyUrlFor { serviceName = "freepbx"; portName = "ucp-ssl"; getAddressFor = "getAddress4For"; }; serviceName = "freepbx";
portName = proxyScheme;
};
ucpUrl = access.proxyUrlFor {
serviceName = "freepbx";
portName = "ucp-ssl";
getAddressFor = "getAddress4For";
};
ucpPath = "/socket.io"; ucpPath = "/socket.io";
# TODO: ports.asterisk/asterisk-ssl? # TODO: ports.asterisk/asterisk-ssl?
extraConfig = '' extraConfig = ''
@ -23,7 +30,7 @@ in {
proxy_busy_buffers_size 256k; proxy_busy_buffers_size 256k;
''; '';
locations = { locations = {
"/" = { xvars, ... }: { "/" = {xvars, ...}: {
xvars.enable = true; xvars.enable = true;
proxy = { proxy = {
enable = true; enable = true;
@ -33,7 +40,11 @@ in {
}; };
}; };
}; };
${ucpPath} = { xvars, virtualHost, ... }: { ${ucpPath} = {
xvars,
virtualHost,
...
}: {
proxy = { proxy = {
enable = true; enable = true;
websocket.enable = true; websocket.enable = true;
@ -63,12 +74,12 @@ in {
listen' = { listen' = {
ucp = { ucp = {
port = mkDefault freepbx.ports.ucp.port; port = mkDefault freepbx.ports.ucp.port;
extraParameters = [ "default_server" ]; extraParameters = ["default_server"];
}; };
ucpSsl = { ucpSsl = {
port = mkDefault freepbx.ports.ucp-ssl.port; port = mkDefault freepbx.ports.ucp-ssl.port;
ssl = true; ssl = true;
extraParameters = [ "default_server" ]; extraParameters = ["default_server"];
}; };
}; };
proxy = { proxy = {
@ -84,7 +95,7 @@ in {
}; };
freepbx'local = { freepbx'local = {
listen' = { listen' = {
http = { }; http = {};
https.ssl = true; https.ssl = true;
ucp = { ucp = {
port = mkDefault nginx.virtualHosts.freepbx'ucp.listen'.ucp.port; port = mkDefault nginx.virtualHosts.freepbx'ucp.listen'.ucp.port;
@ -103,9 +114,11 @@ in {
}; };
}; };
config.networking.firewall = let config.networking.firewall = let
websocketPorts = virtualHost: [ websocketPorts = virtualHost:
virtualHost.listen'.ucp.port [
] ++ optional virtualHost.listen'.ucpSsl.enable virtualHost.listen'.ucpSsl.port; virtualHost.listen'.ucp.port
]
++ optional virtualHost.listen'.ucpSsl.enable virtualHost.listen'.ucpSsl.port;
in { in {
interfaces.local.allowedTCPPorts = websocketPorts nginx.virtualHosts.freepbx'local; interfaces.local.allowedTCPPorts = websocketPorts nginx.virtualHosts.freepbx'local;
allowedTCPPorts = mkIf (!nginx.virtualHosts.freepbx'ucp.local.denyGlobal) (websocketPorts nginx.virtualHosts.freepbx'ucp); allowedTCPPorts = mkIf (!nginx.virtualHosts.freepbx'ucp.local.denyGlobal) (websocketPorts nginx.virtualHosts.freepbx'ucp);

View file

@ -20,7 +20,11 @@
headers.set.X-Grocy-User = mkOptionDefault "$grocy_user"; headers.set.X-Grocy-User = mkOptionDefault "$grocy_user";
}; };
}; };
luaAuthHost = { config, xvars, ... }: { luaAuthHost = {
config,
xvars,
...
}: {
vouch.auth.lua = { vouch.auth.lua = {
enable = true; enable = true;
accessRequest = '' accessRequest = ''
@ -56,16 +60,20 @@ in {
proxied.enable = true; proxied.enable = true;
local.denyGlobal = true; local.denyGlobal = true;
}; };
grocy = mkMerge [ luaAuthHost { grocy = mkMerge [
inherit name extraConfig locations; luaAuthHost
vouch.enable = true; {
proxy = { inherit name extraConfig locations;
upstream = mkIf grocy.enable (mkDefault vouch.enable = true;
"nginx'proxied" proxy = {
); upstream = mkIf grocy.enable (
host = mkDefault serverName; mkDefault
}; "nginx'proxied"
} ]; );
host = mkDefault serverName;
};
}
];
grocy'local = { grocy'local = {
inherit name; inherit name;
local.enable = mkDefault true; local.enable = mkDefault true;
@ -78,20 +86,23 @@ in {
proxy.enable = true; proxy.enable = true;
}; };
}; };
grocy'local'int = mkMerge [ luaAuthHost { grocy'local'int = mkMerge [
# internal proxy workaround for http2 lua compat issues luaAuthHost
serverName = serverName'local; {
inherit name extraConfig locations; # internal proxy workaround for http2 lua compat issues
proxy = { serverName = serverName'local;
upstream = mkDefault nginx.virtualHosts.grocy.proxy.upstream; inherit name extraConfig locations;
host = mkDefault nginx.virtualHosts.grocy.proxy.host; proxy = {
}; upstream = mkDefault nginx.virtualHosts.grocy.proxy.upstream;
proxied.enable = true; host = mkDefault nginx.virtualHosts.grocy.proxy.host;
vouch = { };
enable = true; proxied.enable = true;
localSso.enable = true; vouch = {
}; enable = true;
} ]; localSso.enable = true;
};
}
];
}; };
}; };
} }

View file

@ -7,12 +7,12 @@
inherit (config.services) nginx home-assistant; inherit (config.services) nginx home-assistant;
name.shortServer = mkDefault "home"; name.shortServer = mkDefault "home";
listen' = { listen' = {
http = { }; http = {};
https.ssl = true; https.ssl = true;
hass = { hass = {
enable = !home-assistant.enable; enable = !home-assistant.enable;
port = mkDefault home-assistant.config.http.server_port; port = mkDefault home-assistant.config.http.server_port;
extraParameters = [ "default_server" ]; extraParameters = ["default_server"];
}; };
}; };
upstreamName = "home-assistant'access"; upstreamName = "home-assistant'access";
@ -24,7 +24,7 @@ in {
addr = mkDefault "localhost"; addr = mkDefault "localhost";
port = mkIf home-assistant.enable (mkDefault home-assistant.config.http.server_port); port = mkIf home-assistant.enable (mkDefault home-assistant.config.http.server_port);
}; };
service = { upstream, ... }: { service = {upstream, ...}: {
enable = mkIf upstream.servers.local.enable (mkDefault false); enable = mkIf upstream.servers.local.enable (mkDefault false);
accessService = { accessService = {
name = "home-assistant"; name = "home-assistant";
@ -63,7 +63,8 @@ in {
}; };
config.networking.firewall.allowedTCPPorts = let config.networking.firewall.allowedTCPPorts = let
inherit (nginx.virtualHosts.home-assistant'local) listen'; inherit (nginx.virtualHosts.home-assistant'local) listen';
in mkIf nginx.virtualHosts.home-assistant'local.enable [ in
(mkIf listen'.hass.enable listen'.hass.port) mkIf nginx.virtualHosts.home-assistant'local.enable [
]; (mkIf listen'.hass.enable listen'.hass.port)
];
} }

View file

@ -19,7 +19,7 @@ in {
addr = mkDefault "localhost"; addr = mkDefault "localhost";
port = mkIf cfg.enable (mkDefault cfg.port); port = mkIf cfg.enable (mkDefault cfg.port);
}; };
service = { upstream, ... }: { service = {upstream, ...}: {
enable = mkIf upstream.servers.local.enable (mkDefault false); enable = mkIf upstream.servers.local.enable (mkDefault false);
accessService = { accessService = {
name = "invidious"; name = "invidious";
@ -40,7 +40,7 @@ in {
proxy_redirect off; proxy_redirect off;
proxy_buffering off; proxy_buffering off;
''; '';
location = { xvars, ... }: { location = {xvars, ...}: {
proxy = { proxy = {
enable = true; enable = true;
websocket.enable = true; websocket.enable = true;
@ -61,7 +61,11 @@ in {
upstream = "nginx'proxied"; upstream = "nginx'proxied";
host = mkDefault nginx.virtualHosts.invidious'int.serverName; host = mkDefault nginx.virtualHosts.invidious'int.serverName;
}; };
locations."/" = { xvars, virtualHost, ... }: { locations."/" = {
xvars,
virtualHost,
...
}: {
proxy.enable = true; proxy.enable = true;
extraConfig = '' extraConfig = ''
proxy_http_version 1.1; proxy_http_version 1.1;
@ -72,7 +76,11 @@ in {
''; '';
}; };
}; };
invidious'int = { config, xvars, ... }: { invidious'int = {
config,
xvars,
...
}: {
serverName = "@invidious_internal"; serverName = "@invidious_internal";
proxied.enable = true; proxied.enable = true;
local.denyGlobal = true; local.denyGlobal = true;
@ -115,7 +123,7 @@ in {
}; };
inherit extraConfig; inherit extraConfig;
}; };
invidious'local = { xvars, ... }: { invidious'local = {xvars, ...}: {
local.enable = true; local.enable = true;
ssl.cert.copyFromVhost = "invidious"; ssl.cert.copyFromVhost = "invidious";
proxy = { proxy = {

View file

@ -18,7 +18,7 @@ in {
port = mkDefault cfg.port; port = mkDefault cfg.port;
ssl.enable = mkIf (cfg.protocol == "https") true; ssl.enable = mkIf (cfg.protocol == "https") true;
}; };
access = { upstream, ... }: { access = {upstream, ...}: {
enable = mkDefault (!upstream.servers.local.enable or false); enable = mkDefault (!upstream.servers.local.enable or false);
accessService = { accessService = {
name = "keycloak"; name = "keycloak";

View file

@ -12,8 +12,15 @@
in { in {
config.services.nginx = { config.services.nginx = {
virtualHosts = let virtualHosts = let
url = access.proxyUrlFor { inherit system; service = motion; }; url = access.proxyUrlFor {
streamUrl = access.proxyUrlFor { inherit system; service = motion; portName = "stream"; }; inherit system;
service = motion;
};
streamUrl = access.proxyUrlFor {
inherit system;
service = motion;
portName = "stream";
};
extraConfig = '' extraConfig = ''
proxy_redirect off; proxy_redirect off;
proxy_buffering off; proxy_buffering off;
@ -32,7 +39,7 @@ in {
}; };
}; };
listen' = { listen' = {
http = { }; http = {};
https.ssl = true; https.ssl = true;
stream = { stream = {
enable = mkDefault motion.ports.stream.enable; enable = mkDefault motion.ports.stream.enable;
@ -49,9 +56,12 @@ in {
inherit name listen'; inherit name listen';
ssl.cert.copyFromVhost = "kitchencam"; ssl.cert.copyFromVhost = "kitchencam";
local.enable = true; local.enable = true;
locations = mapAttrs (name: location: location // { locations = mapAttrs (name: location:
proxyPass = mkDefault nginx.virtualHosts.kitchencam.locations.${name}.proxyPass; location
}) locations; // {
proxyPass = mkDefault nginx.virtualHosts.kitchencam.locations.${name}.proxyPass;
})
locations;
}; };
}; };
}; };

View file

@ -4,8 +4,7 @@
gensokyo-zone, gensokyo-zone,
access, access,
... ...
}: }: let
let
inherit (gensokyo-zone.lib) mkAlmostOptionDefault; inherit (gensokyo-zone.lib) mkAlmostOptionDefault;
inherit (lib.options) mkOption; inherit (lib.options) mkOption;
inherit (lib.modules) mkIf mkOptionDefault; inherit (lib.modules) mkIf mkOptionDefault;
@ -42,7 +41,7 @@ in {
inherit (nginx.stream.upstreams.ldaps.servers.access.accessService) system name id port; inherit (nginx.stream.upstreams.ldaps.servers.access.accessService) system name id port;
}; };
}; };
ldap = { upstream, ... }: { ldap = {upstream, ...}: {
enable = mkIf upstream.servers.ldaps.enable false; enable = mkIf upstream.servers.ldaps.enable false;
accessService = { accessService = {
inherit (nginx.stream.upstreams.ldap.servers.access.accessService) system name id port; inherit (nginx.stream.upstreams.ldap.servers.access.accessService) system name id port;
@ -54,7 +53,7 @@ in {
name = "ldap"; name = "ldap";
}; };
}; };
ldaps = { config, ... }: { ldaps = {config, ...}: {
enable = mkAlmostOptionDefault config.servers.access.enable; enable = mkAlmostOptionDefault config.servers.access.enable;
servers.access = { servers.access = {
accessService = { accessService = {

View file

@ -4,8 +4,7 @@
access, access,
gensokyo-zone, gensokyo-zone,
... ...
}: }: let
let
inherit (gensokyo-zone.lib) mkAlmostOptionDefault; inherit (gensokyo-zone.lib) mkAlmostOptionDefault;
inherit (lib.modules) mkIf mkOptionDefault; inherit (lib.modules) mkIf mkOptionDefault;
inherit (config.services) nginx; inherit (config.services) nginx;
@ -42,7 +41,9 @@ in {
}; };
}; };
proxy.upstream = mkAlmostOptionDefault ( proxy.upstream = mkAlmostOptionDefault (
if nginx.stream.upstreams.mqtts.enable then "mqtts" else "mqtt" if nginx.stream.upstreams.mqtts.enable
then "mqtts"
else "mqtt"
); );
}; };
}; };

View file

@ -1,4 +1,8 @@
{config, lib, ...}: let {
config,
lib,
...
}: let
inherit (lib.modules) mkIf; inherit (lib.modules) mkIf;
cfg = config.services.nginx; cfg = config.services.nginx;
in { in {

View file

@ -15,7 +15,7 @@ in {
addr = mkDefault "localhost"; addr = mkDefault "localhost";
port = mkIf openwebrx.enable (mkDefault openwebrx.port); port = mkIf openwebrx.enable (mkDefault openwebrx.port);
}; };
service = { upstream, ... }: { service = {upstream, ...}: {
enable = mkIf upstream.servers.local.enable (mkDefault false); enable = mkIf upstream.servers.local.enable (mkDefault false);
accessService = { accessService = {
name = "openwebrx"; name = "openwebrx";

View file

@ -16,7 +16,7 @@ in {
addr = mkDefault "localhost"; addr = mkDefault "localhost";
port = mkDefault cfg.port; port = mkDefault cfg.port;
}; };
access = { upstream, ... }: { access = {upstream, ...}: {
enable = mkDefault (!upstream.servers.local.enable); enable = mkDefault (!upstream.servers.local.enable);
accessService.name = "plex"; accessService.name = "plex";
}; };
@ -65,12 +65,12 @@ in {
inherit name locations extraConfig; inherit name locations extraConfig;
proxy.upstream = mkDefault upstreamName; proxy.upstream = mkDefault upstreamName;
listen' = { listen' = {
http = { }; http = {};
https.ssl = true; https.ssl = true;
external = { external = {
enable = mkDefault false; enable = mkDefault false;
port = mkDefault 32400; port = mkDefault 32400;
extraParameters = [ "default_server" ]; extraParameters = ["default_server"];
}; };
}; };
}; };
@ -88,7 +88,8 @@ in {
}; };
config.networking.firewall.allowedTCPPorts = let config.networking.firewall.allowedTCPPorts = let
inherit (nginx.virtualHosts.plex) listen'; inherit (nginx.virtualHosts.plex) listen';
in mkIf listen'.external.enable [ in
listen'.external.port mkIf listen'.external.enable [
]; listen'.external.port
];
} }

View file

@ -7,7 +7,7 @@
inherit (lib.modules) mkDefault; inherit (lib.modules) mkDefault;
inherit (lib.strings) escapeRegex; inherit (lib.strings) escapeRegex;
inherit (config.services) nginx tailscale; inherit (config.services) nginx tailscale;
proxyPass = access.proxyUrlFor { serviceName = "proxmox"; } + "/"; proxyPass = access.proxyUrlFor {serviceName = "proxmox";} + "/";
in { in {
config.services.nginx.virtualHosts = let config.services.nginx.virtualHosts = let
locations."/" = { locations."/" = {

Some files were not shown because too many files have changed in this diff Show more