1
1
Fork 1
mirror of https://github.com/oddlama/nix-config.git synced 2025-10-10 23:00:39 +02:00

chore: format everything

This commit is contained in:
oddlama 2024-11-26 13:34:55 +01:00
parent deca311c68
commit 7ccd7856ee
No known key found for this signature in database
GPG key ID: 14EFE510775FE39A
162 changed files with 4750 additions and 3718 deletions

View file

@ -2,9 +2,9 @@
config,
lib,
...
}: let
inherit
(lib)
}:
let
inherit (lib)
assertMsg
elem
filter
@ -16,10 +16,11 @@
removeSuffix
types
;
in {
in
{
options.security.acme.wildcardDomains = mkOption {
type = types.listOf types.str;
default = [];
default = [ ];
description = ''
List of domains to which a wilcard certificate exists under the same name in `certs`.
All of these certs will automatically have `*.<domain>` appended to `extraDomainNames`.
@ -27,32 +28,36 @@ in {
};
options.services.nginx.virtualHosts = mkOption {
type = types.attrsOf (types.submodule (submod: {
options.useACMEWildcardHost = mkOption {
type = types.bool;
default = false;
description = ''Automatically set useACMEHost with the correct wildcard domain for the virtualHosts's main domain.'';
};
config = let
# This retrieves all matching wildcard certs that would include the corresponding domain.
# If no such domain is found then an assertion is triggered.
domain = submod.config._module.args.name;
matchingCerts =
if elem domain config.security.acme.wildcardDomains
then [domain]
else
filter
(x: !hasInfix "." (removeSuffix ".${x}" domain))
config.security.acme.wildcardDomains;
in
mkIf submod.config.useACMEWildcardHost {
useACMEHost = assert assertMsg (matchingCerts != []) "No wildcard certificate was defined that matches ${domain}";
head matchingCerts;
type = types.attrsOf (
types.submodule (submod: {
options.useACMEWildcardHost = mkOption {
type = types.bool;
default = false;
description = ''Automatically set useACMEHost with the correct wildcard domain for the virtualHosts's main domain.'';
};
}));
config =
let
# This retrieves all matching wildcard certs that would include the corresponding domain.
# If no such domain is found then an assertion is triggered.
domain = submod.config._module.args.name;
matchingCerts =
if elem domain config.security.acme.wildcardDomains then
[ domain ]
else
filter (x: !hasInfix "." (removeSuffix ".${x}" domain)) config.security.acme.wildcardDomains;
in
mkIf submod.config.useACMEWildcardHost {
useACMEHost =
assert assertMsg (
matchingCerts != [ ]
) "No wildcard certificate was defined that matches ${domain}";
head matchingCerts;
};
})
);
};
config.security.acme.certs = genAttrs config.security.acme.wildcardDomains (domain: {
extraDomainNames = ["*.${domain}"];
extraDomainNames = [ "*.${domain}" ];
});
}

View file

@ -3,9 +3,9 @@
pkgs,
config,
...
}: let
inherit
(lib)
}:
let
inherit (lib)
getExe
mkEnableOption
mkIf
@ -18,11 +18,12 @@
configFile = formatType.generate "config.json" cfg.settings;
dataDir = "/var/lib/actual";
formatType = pkgs.formats.json {};
in {
formatType = pkgs.formats.json { };
in
{
options.services.actual = {
enable = mkEnableOption "actual, a privacy focused app for managing your finances";
package = mkPackageOption pkgs "actual-server" {};
package = mkPackageOption pkgs "actual-server" { };
user = mkOption {
type = types.str;
@ -59,7 +60,7 @@ in {
};
settings = mkOption {
default = {};
default = { };
type = types.submodule {
freeformType = formatType.type;
@ -87,10 +88,10 @@ in {
};
config = mkIf cfg.enable {
networking.firewall.allowedTCPPorts = mkIf cfg.openFirewall [cfg.settings.port];
networking.firewall.allowedTCPPorts = mkIf cfg.openFirewall [ cfg.settings.port ];
users.groups = mkIf (cfg.group == "actual") {
${cfg.group} = {};
${cfg.group} = { };
};
users.users = mkIf (cfg.user == "actual") {
@ -103,7 +104,7 @@ in {
systemd.services.actual = {
description = "Actual server, a local-first personal finance app";
after = ["network.target"];
after = [ "network.target" ];
environment.ACTUAL_CONFIG_PATH = configFile;
serviceConfig = {
ExecStart = getExe cfg.package;
@ -146,7 +147,7 @@ in {
];
UMask = "0077";
};
wantedBy = ["multi-user.target"];
wantedBy = [ "multi-user.target" ];
};
};
}

View file

@ -3,9 +3,9 @@
globals,
lib,
...
}: let
inherit
(lib)
}:
let
inherit (lib)
attrValues
flip
mkIf
@ -13,46 +13,51 @@
mkOption
types
;
in {
in
{
options.backups.storageBoxes = mkOption {
description = "Backups to Hetzner Storage Boxes using restic";
default = {};
type = types.attrsOf (types.submodule (submod: {
options = {
name = mkOption {
description = "The name of the storage box to backup to. The box must be defined in the globals. Defaults to the attribute name.";
default = submod.config._module.args.name;
type = types.str;
};
default = { };
type = types.attrsOf (
types.submodule (submod: {
options = {
name = mkOption {
description = "The name of the storage box to backup to. The box must be defined in the globals. Defaults to the attribute name.";
default = submod.config._module.args.name;
type = types.str;
};
subuser = mkOption {
description = "The name of the storage box subuser as defined in the globals, mapping this user to a subuser id.";
type = types.str;
};
subuser = mkOption {
description = "The name of the storage box subuser as defined in the globals, mapping this user to a subuser id.";
type = types.str;
};
paths = mkOption {
description = "The paths to backup.";
type = types.listOf types.path;
paths = mkOption {
description = "The paths to backup.";
type = types.listOf types.path;
};
};
};
}));
})
);
};
config = mkIf (config.backups.storageBoxes != {}) {
config = mkIf (config.backups.storageBoxes != { }) {
age.secrets.restic-encryption-password.generator.script = "alnum";
age.secrets.restic-ssh-privkey.generator.script = "ssh-ed25519";
services.restic.backups = mkMerge (flip map (attrValues config.backups.storageBoxes)
(boxCfg: {
services.restic.backups = mkMerge (
flip map (attrValues config.backups.storageBoxes) (boxCfg: {
"storage-box-${boxCfg.name}" = {
hetznerStorageBox = let
box = globals.hetzner.storageboxes.${boxCfg.name};
in {
enable = true;
inherit (box) mainUser;
inherit (box.users.${boxCfg.subuser}) subUid path;
sshAgeSecret = "restic-ssh-privkey";
};
hetznerStorageBox =
let
box = globals.hetzner.storageboxes.${boxCfg.name};
in
{
enable = true;
inherit (box) mainUser;
inherit (box.users.${boxCfg.subuser}) subUid path;
sshAgeSecret = "restic-ssh-privkey";
};
# A) We need to backup stuff from other users, so run as root.
# B) We also need to be root because the ssh key will only
@ -76,6 +81,7 @@ in {
# "--keep-yearly 75"
# ];
};
}));
})
);
};
}

View file

@ -2,9 +2,9 @@
lib,
config,
...
}: let
inherit
(lib)
}:
let
inherit (lib)
concatLists
flip
mapAttrsToList
@ -15,65 +15,86 @@
;
cfg = config.users.deterministicIds;
in {
in
{
options = {
users.deterministicIds = mkOption {
default = {};
default = { };
description = ''
Maps a user or group name to its expected uid/gid values. If a user/group is
used on the system without specifying a uid/gid, this module will assign the
corresponding ids defined here, or show an error if the definition is missing.
'';
type = types.attrsOf (types.submodule {
options = {
uid = mkOption {
type = types.nullOr types.int;
default = null;
description = "The uid to assign if it is missing in `users.users.<name>`.";
type = types.attrsOf (
types.submodule {
options = {
uid = mkOption {
type = types.nullOr types.int;
default = null;
description = "The uid to assign if it is missing in `users.users.<name>`.";
};
gid = mkOption {
type = types.nullOr types.int;
default = null;
description = "The gid to assign if it is missing in `users.groups.<name>`.";
};
};
gid = mkOption {
type = types.nullOr types.int;
default = null;
description = "The gid to assign if it is missing in `users.groups.<name>`.";
};
};
});
}
);
};
users.users = mkOption {
type = types.attrsOf (types.submodule ({name, ...}: {
config.uid = let
deterministicUid = cfg.${name}.uid or null;
in
mkIf (deterministicUid != null) (mkDefault deterministicUid);
}));
type = types.attrsOf (
types.submodule (
{ name, ... }:
{
config.uid =
let
deterministicUid = cfg.${name}.uid or null;
in
mkIf (deterministicUid != null) (mkDefault deterministicUid);
}
)
);
};
users.groups = mkOption {
type = types.attrsOf (types.submodule ({name, ...}: {
config.gid = let
deterministicGid = cfg.${name}.gid or null;
in
mkIf (deterministicGid != null) (mkDefault deterministicGid);
}));
type = types.attrsOf (
types.submodule (
{ name, ... }:
{
config.gid =
let
deterministicGid = cfg.${name}.gid or null;
in
mkIf (deterministicGid != null) (mkDefault deterministicGid);
}
)
);
};
};
config = {
assertions =
concatLists (flip mapAttrsToList config.users.users (name: user: [
{
assertion = user.uid != null;
message = "non-deterministic uid detected for '${name}', please assign one via `users.deterministicIds`";
concatLists (
flip mapAttrsToList config.users.users (
name: user: [
{
assertion = user.uid != null;
message = "non-deterministic uid detected for '${name}', please assign one via `users.deterministicIds`";
}
{
assertion = !user.autoSubUidGidRange;
message = "non-deterministic subUids/subGids detected for: ${name}";
}
]
)
)
++ flip mapAttrsToList config.users.groups (
name: group: {
assertion = group.gid != null;
message = "non-deterministic gid detected for '${name}', please assign one via `users.deterministicIds`";
}
{
assertion = !user.autoSubUidGidRange;
message = "non-deterministic subUids/subGids detected for: ${name}";
}
]))
++ flip mapAttrsToList config.users.groups (name: group: {
assertion = group.gid != null;
message = "non-deterministic gid detected for '${name}', please assign one via `users.deterministicIds`";
});
);
};
}

View file

@ -3,9 +3,9 @@
lib,
nodes,
...
}: let
inherit
(lib)
}:
let
inherit (lib)
attrNames
concatMap
concatStringsSep
@ -21,16 +21,20 @@
;
nodeName = config.node.name;
mkForwardedOption = path:
mkForwardedOption =
path:
mkOption {
type = mkOptionType {
name = "Same type that the receiving option `${concatStringsSep "." path}` normally accepts.";
merge = _loc: defs:
builtins.filter
(x: builtins.isAttrs x -> ((x._type or "") != "__distributed_config_empty"))
(map (x: x.value) defs);
merge =
_loc: defs:
builtins.filter (x: builtins.isAttrs x -> ((x._type or "") != "__distributed_config_empty")) (
map (x: x.value) defs
);
};
default = {
_type = "__distributed_config_empty";
};
default = {_type = "__distributed_config_empty";};
description = ''
Anything specified here will be forwarded to `${concatStringsSep "." path}`
on the given node. Forwarding happens as-is to the raw values,
@ -39,31 +43,69 @@
};
forwardedOptions = [
["age" "secrets"]
["networking" "nftables" "chains"]
["services" "nginx" "upstreams"]
["services" "nginx" "virtualHosts"]
["services" "influxdb2" "provision" "organizations"]
["services" "kanidm" "provision" "groups"]
["services" "kanidm" "provision" "systems" "oauth2"]
[
"age"
"secrets"
]
[
"networking"
"nftables"
"chains"
]
[
"services"
"nginx"
"upstreams"
]
[
"services"
"nginx"
"virtualHosts"
]
[
"services"
"influxdb2"
"provision"
"organizations"
]
[
"services"
"kanidm"
"provision"
"groups"
]
[
"services"
"kanidm"
"provision"
"systems"
"oauth2"
]
];
attrsForEachOption = f: foldl' (acc: path: recursiveUpdate acc (setAttrByPath path (f path))) {} forwardedOptions;
in {
attrsForEachOption =
f: foldl' (acc: path: recursiveUpdate acc (setAttrByPath path (f path))) { } forwardedOptions;
in
{
options.nodes = mkOption {
description = "Options forwareded to the given node.";
default = {};
type = types.attrsOf (types.submodule {
options = attrsForEachOption mkForwardedOption;
});
default = { };
type = types.attrsOf (
types.submodule {
options = attrsForEachOption mkForwardedOption;
}
);
};
config = let
getConfig = path: otherNode: let
cfg = nodes.${otherNode}.config.nodes.${nodeName} or null;
config =
let
getConfig =
path: otherNode:
let
cfg = nodes.${otherNode}.config.nodes.${nodeName} or null;
in
optionals (cfg != null) (getAttrFromPath path cfg);
mergeConfigFromOthers = path: mkMerge (concatMap (getConfig path) (attrNames nodes));
in
optionals (cfg != null) (getAttrFromPath path cfg);
mergeConfigFromOthers = path: mkMerge (concatMap (getConfig path) (attrNames nodes));
in
attrsForEachOption mergeConfigFromOthers;
}

View file

@ -2,9 +2,9 @@
lib,
options,
...
}: let
inherit
(lib)
}:
let
inherit (lib)
mkOption
types
;
@ -15,10 +15,11 @@
description = "The network to which this endpoint is associated.";
};
};
in {
in
{
options = {
globals = mkOption {
default = {};
default = { };
type = types.submodule {
options = {
root = {
@ -40,91 +41,100 @@ in {
};
net = mkOption {
type = types.attrsOf (types.submodule (netSubmod: {
options = {
cidrv4 = mkOption {
type = types.nullOr types.net.cidrv4;
description = "The CIDRv4 of this network";
default = null;
type = types.attrsOf (
types.submodule (netSubmod: {
options = {
cidrv4 = mkOption {
type = types.nullOr types.net.cidrv4;
description = "The CIDRv4 of this network";
default = null;
};
cidrv6 = mkOption {
type = types.nullOr types.net.cidrv6;
description = "The CIDRv6 of this network";
default = null;
};
hosts = mkOption {
type = types.attrsOf (
types.submodule (hostSubmod: {
options = {
id = mkOption {
type = types.int;
description = "The id of this host in the network";
};
ipv4 = mkOption {
type = types.nullOr types.net.ipv4;
description = "The IPv4 of this host";
readOnly = true;
default =
if netSubmod.config.cidrv4 == null then
null
else
lib.net.cidr.host hostSubmod.config.id netSubmod.config.cidrv4;
};
ipv6 = mkOption {
type = types.nullOr types.net.ipv6;
description = "The IPv6 of this host";
readOnly = true;
default =
if netSubmod.config.cidrv6 == null then
null
else
lib.net.cidr.host hostSubmod.config.id netSubmod.config.cidrv6;
};
cidrv4 = mkOption {
type = types.nullOr types.str; # FIXME: this is not types.net.cidr because it would zero out the host part
description = "The IPv4 of this host including CIDR mask";
readOnly = true;
default =
if netSubmod.config.cidrv4 == null then
null
else
lib.net.cidr.hostCidr hostSubmod.config.id netSubmod.config.cidrv4;
};
cidrv6 = mkOption {
type = types.nullOr types.str; # FIXME: this is not types.net.cidr because it would zero out the host part
description = "The IPv6 of this host including CIDR mask";
readOnly = true;
default =
if netSubmod.config.cidrv6 == null then
null
else
lib.net.cidr.hostCidr hostSubmod.config.id netSubmod.config.cidrv6;
};
};
})
);
};
};
cidrv6 = mkOption {
type = types.nullOr types.net.cidrv6;
description = "The CIDRv6 of this network";
default = null;
};
hosts = mkOption {
type = types.attrsOf (types.submodule (hostSubmod: {
options = {
id = mkOption {
type = types.int;
description = "The id of this host in the network";
};
ipv4 = mkOption {
type = types.nullOr types.net.ipv4;
description = "The IPv4 of this host";
readOnly = true;
default =
if netSubmod.config.cidrv4 == null
then null
else lib.net.cidr.host hostSubmod.config.id netSubmod.config.cidrv4;
};
ipv6 = mkOption {
type = types.nullOr types.net.ipv6;
description = "The IPv6 of this host";
readOnly = true;
default =
if netSubmod.config.cidrv6 == null
then null
else lib.net.cidr.host hostSubmod.config.id netSubmod.config.cidrv6;
};
cidrv4 = mkOption {
type = types.nullOr types.str; # FIXME: this is not types.net.cidr because it would zero out the host part
description = "The IPv4 of this host including CIDR mask";
readOnly = true;
default =
if netSubmod.config.cidrv4 == null
then null
else lib.net.cidr.hostCidr hostSubmod.config.id netSubmod.config.cidrv4;
};
cidrv6 = mkOption {
type = types.nullOr types.str; # FIXME: this is not types.net.cidr because it would zero out the host part
description = "The IPv6 of this host including CIDR mask";
readOnly = true;
default =
if netSubmod.config.cidrv6 == null
then null
else lib.net.cidr.hostCidr hostSubmod.config.id netSubmod.config.cidrv6;
};
};
}));
};
};
}));
})
);
};
services = mkOption {
type = types.attrsOf (types.submodule {
options = {
domain = mkOption {
type = types.str;
description = "The domain under which this service can be reached";
type = types.attrsOf (
types.submodule {
options = {
domain = mkOption {
type = types.str;
description = "The domain under which this service can be reached";
};
};
};
});
}
);
};
monitoring = {
ping = mkOption {
type = types.attrsOf (types.submodule {
options =
defaultOptions
// {
type = types.attrsOf (
types.submodule {
options = defaultOptions // {
hostv4 = mkOption {
type = types.nullOr types.str;
description = "The IP/hostname to ping via ipv4.";
@ -137,14 +147,14 @@ in {
default = null;
};
};
});
}
);
};
http = mkOption {
type = types.attrsOf (types.submodule {
options =
defaultOptions
// {
type = types.attrsOf (
types.submodule {
options = defaultOptions // {
url = mkOption {
type = types.either (types.listOf types.str) types.str;
description = "The url to connect to.";
@ -168,14 +178,14 @@ in {
default = false;
};
};
});
}
);
};
dns = mkOption {
type = types.attrsOf (types.submodule {
options =
defaultOptions
// {
type = types.attrsOf (
types.submodule {
options = defaultOptions // {
server = mkOption {
type = types.str;
description = "The DNS server to query.";
@ -192,14 +202,14 @@ in {
default = "A";
};
};
});
}
);
};
tcp = mkOption {
type = types.attrsOf (types.submodule {
options =
defaultOptions
// {
type = types.attrsOf (
types.submodule {
options = defaultOptions // {
host = mkOption {
type = types.str;
description = "The IP/hostname to connect to.";
@ -210,22 +220,25 @@ in {
description = "The port to connect to.";
};
};
});
}
);
};
};
mail = {
domains = mkOption {
default = {};
default = { };
description = "All domains on which we receive mail.";
type = types.attrsOf (types.submodule {
options = {
public = mkOption {
type = types.bool;
description = "Whether the domain should be available for use by any user";
type = types.attrsOf (
types.submodule {
options = {
public = mkOption {
type = types.bool;
description = "Whether the domain should be available for use by any user";
};
};
};
});
}
);
};
primary = mkOption {
@ -247,72 +260,78 @@ in {
};
macs = mkOption {
default = {};
default = { };
type = types.attrsOf types.str;
description = "Known MAC addresses for external devices.";
};
hetzner.storageboxes = mkOption {
default = {};
default = { };
description = "Storage box configurations.";
type = types.attrsOf (types.submodule {
options = {
mainUser = mkOption {
type = types.str;
description = "Main username for the storagebox";
};
type = types.attrsOf (
types.submodule {
options = {
mainUser = mkOption {
type = types.str;
description = "Main username for the storagebox";
};
users = mkOption {
default = {};
description = "Subuser configurations.";
type = types.attrsOf (types.submodule {
options = {
subUid = mkOption {
type = types.int;
description = "The subuser id";
};
users = mkOption {
default = { };
description = "Subuser configurations.";
type = types.attrsOf (
types.submodule {
options = {
subUid = mkOption {
type = types.int;
description = "The subuser id";
};
path = mkOption {
type = types.str;
description = "The home path for this subuser (i.e. backup destination)";
};
};
});
path = mkOption {
type = types.str;
description = "The home path for this subuser (i.e. backup destination)";
};
};
}
);
};
};
};
});
}
);
};
# Mirror of the kanidm.persons option.
kanidm.persons = mkOption {
description = "Provisioning of kanidm persons";
default = {};
type = types.attrsOf (types.submodule {
options = {
displayName = mkOption {
description = "Display name";
type = types.str;
};
default = { };
type = types.attrsOf (
types.submodule {
options = {
displayName = mkOption {
description = "Display name";
type = types.str;
};
legalName = mkOption {
description = "Full legal name";
type = types.nullOr types.str;
default = null;
};
legalName = mkOption {
description = "Full legal name";
type = types.nullOr types.str;
default = null;
};
mailAddresses = mkOption {
description = "Mail addresses. First given address is considered the primary address.";
type = types.listOf types.str;
default = [];
};
mailAddresses = mkOption {
description = "Mail addresses. First given address is considered the primary address.";
type = types.listOf types.str;
default = [ ];
};
groups = mkOption {
description = "List of groups this person should belong to.";
type = types.listOf types.str;
default = [];
groups = mkOption {
description = "List of groups this person should belong to.";
type = types.listOf types.str;
default = [ ];
};
};
};
});
}
);
};
};
};

View file

@ -1,10 +1,11 @@
{lib, ...}: let
inherit
(lib)
{ lib, ... }:
let
inherit (lib)
mkOption
types
;
in {
in
{
options.node.secretsDir = mkOption {
description = "Path to the secrets directory for this node.";
type = types.path;

View file

@ -3,9 +3,9 @@
lib,
pkgs,
...
}: let
inherit
(lib)
}:
let
inherit (lib)
attrValues
concatLists
concatStringsSep
@ -32,8 +32,7 @@
versionOlder
;
inherit
(lib.types)
inherit (lib.types)
attrsOf
bool
enum
@ -54,7 +53,8 @@
hardenedClients = filterAttrs (_: client: client.hardened) cfg.clients;
toHardenedClientList = fn: map fn (attrValues hardenedClients);
toHardenedClientAttrs = fn: mapAttrs' (_: fn) hardenedClients;
in {
in
{
meta.maintainers = with maintainers; [
misuzu
thubrecht
@ -62,8 +62,11 @@ in {
];
imports = [
(mkRemovedOptionModule ["services" "netbird" "tunnels"]
"The option `services.netbird.tunnels` has been renamed to `services.netbird.clients`")
(mkRemovedOptionModule [
"services"
"netbird"
"tunnels"
] "The option `services.netbird.tunnels` has been renamed to `services.netbird.clients`")
];
options.services.netbird = {
@ -85,17 +88,17 @@ in {
```
'';
};
package = mkPackageOption pkgs "netbird" {};
package = mkPackageOption pkgs "netbird" { };
ui.enable = mkOption {
type = bool;
default = config.services.displayManager.sessionPackages != [];
default = config.services.displayManager.sessionPackages != [ ];
defaultText = literalExpression ''config.services.displayManager.sessionPackages != [ ]'';
description = ''
Controls presence `netbird-ui` wrappers, defaults to presence of graphical sessions.
'';
};
ui.package = mkPackageOption pkgs "netbird-ui" {};
ui.package = mkPackageOption pkgs "netbird-ui" { };
clients = mkOption {
type = attrsOf (
@ -104,7 +107,8 @@ in {
name,
config,
...
}: {
}:
{
options = {
port = mkOption {
type = port;
@ -211,18 +215,21 @@ in {
wrapper = mkOption {
type = package;
internal = true;
default = let
makeWrapperArgs = concatLists (
mapAttrsToList
(key: value: ["--set-default" key value])
config.environment
);
in
default =
let
makeWrapperArgs = concatLists (
mapAttrsToList (key: value: [
"--set-default"
key
value
]) config.environment
);
in
pkgs.stdenv.mkDerivation {
name = "${cfg.package.name}-wrapper-${name}";
meta.mainProgram = "netbird-${name}";
nativeBuildInputs = with pkgs; [makeWrapper];
phases = ["installPhase"];
nativeBuildInputs = with pkgs; [ makeWrapper ];
phases = [ "installPhase" ];
installPhase = concatStringsSep "\n" [
''
mkdir -p "$out/bin"
@ -246,7 +253,7 @@ in {
# see https://github.com/netbirdio/netbird/blob/88747e3e0191abc64f1e8c7ecc65e5e50a1527fd/client/internal/config.go#L49-L82
config = mkOption {
inherit (pkgs.formats.json {}) type;
inherit (pkgs.formats.json { }) type;
defaultText = literalExpression ''
{
DisableAutoConnect = !config.autoStart;
@ -292,7 +299,7 @@ in {
}
)
);
default = {};
default = { };
description = ''
Attribute set of Netbird client daemons, by default each one will:
@ -327,7 +334,8 @@ in {
let
name = "wt0";
client = cfg.clients."${name}";
in {
in
{
services.netbird.clients."${name}" = {
port = mkDefault 51820;
name = mkDefault "netbird";
@ -336,36 +344,39 @@ in {
};
environment.systemPackages = [
(lib.hiPrio (pkgs.runCommand "${name}-as-default" {} ''
mkdir -p "$out/bin"
for binary in netbird ${optionalString cfg.ui.enable "netbird-ui"} ; do
ln -s "${client.wrapper}/bin/$binary-${name}" "$out/bin/$binary"
done
''))
(lib.hiPrio (
pkgs.runCommand "${name}-as-default" { } ''
mkdir -p "$out/bin"
for binary in netbird ${optionalString cfg.ui.enable "netbird-ui"} ; do
ln -s "${client.wrapper}/bin/$binary-${name}" "$out/bin/$binary"
done
''
))
];
}
))
{
boot.extraModulePackages =
optional
(cfg.clients != {} && (versionOlder kernel.version "5.6"))
kernelPackages.wireguard;
boot.extraModulePackages = optional (
cfg.clients != { } && (versionOlder kernel.version "5.6")
) kernelPackages.wireguard;
environment.systemPackages =
toClientList (client: client.wrapper)
# omitted due to https://github.com/netbirdio/netbird/issues/1562
#++ optional (cfg.clients != { }) cfg.package
# omitted due to https://github.com/netbirdio/netbird/issues/1581
#++ optional (cfg.clients != { } && cfg.ui.enable) cfg.ui.package
;
environment.systemPackages = toClientList (client: client.wrapper)
# omitted due to https://github.com/netbirdio/netbird/issues/1562
#++ optional (cfg.clients != { }) cfg.package
# omitted due to https://github.com/netbirdio/netbird/issues/1581
#++ optional (cfg.clients != { } && cfg.ui.enable) cfg.ui.package
;
networking.dhcpcd.denyInterfaces = toClientList (client: client.interface);
networking.networkmanager.unmanaged = toClientList (client: "interface-name:${client.interface}");
networking.firewall.allowedUDPPorts = concatLists (toClientList (client: optional client.openFirewall client.port));
networking.firewall.allowedUDPPorts = concatLists (
toClientList (client: optional client.openFirewall client.port)
);
systemd.network.networks = mkIf config.networking.useNetworkd (toClientAttrs (
client:
systemd.network.networks = mkIf config.networking.useNetworkd (
toClientAttrs (
client:
nameValuePair "50-netbird-${client.interface}" {
matchConfig = {
Name = client.interface;
@ -375,22 +386,26 @@ in {
ActivationPolicy = "manual";
};
}
));
)
);
environment.etc = toClientAttrs (client:
environment.etc = toClientAttrs (
client:
nameValuePair "${client.name}/config.d/50-nixos.json" {
text = builtins.toJSON client.config;
mode = "0444";
});
}
);
systemd.services = toClientAttrs (client:
systemd.services = toClientAttrs (
client:
nameValuePair client.name {
description = "A WireGuard-based mesh network that connects your devices into a single private network";
documentation = ["https://netbird.io/docs/"];
documentation = [ "https://netbird.io/docs/" ];
after = ["network.target"];
wantedBy = ["multi-user.target"];
after = [ "network.target" ];
wantedBy = [ "multi-user.target" ];
path = optional (!config.services.resolved.enable) pkgs.openresolv;
@ -413,52 +428,59 @@ in {
};
stopIfChanged = false;
});
}
);
}
# Hardening section
(mkIf (hardenedClients != {}) {
users.groups = toHardenedClientAttrs (client: nameValuePair client.name {});
users.users = toHardenedClientAttrs (client:
(mkIf (hardenedClients != { }) {
users.groups = toHardenedClientAttrs (client: nameValuePair client.name { });
users.users = toHardenedClientAttrs (
client:
nameValuePair client.name {
isSystemUser = true;
home = "/var/lib/${client.name}";
group = client.name;
});
}
);
systemd.services = toHardenedClientAttrs (client:
nameValuePair client.name (mkIf client.hardened {
serviceConfig = {
RuntimeDirectoryMode = "0750";
systemd.services = toHardenedClientAttrs (
client:
nameValuePair client.name (
mkIf client.hardened {
serviceConfig = {
RuntimeDirectoryMode = "0750";
User = client.name;
Group = client.name;
User = client.name;
Group = client.name;
# settings implied by DynamicUser=true, without actully using it,
# see https://www.freedesktop.org/software/systemd/man/latest/systemd.exec.html#DynamicUser=
RemoveIPC = true;
PrivateTmp = true;
ProtectSystem = "strict";
ProtectHome = "yes";
# settings implied by DynamicUser=true, without actully using it,
# see https://www.freedesktop.org/software/systemd/man/latest/systemd.exec.html#DynamicUser=
RemoveIPC = true;
PrivateTmp = true;
ProtectSystem = "strict";
ProtectHome = "yes";
AmbientCapabilities =
[
# see https://man7.org/linux/man-pages/man7/capabilities.7.html
# see https://docs.netbird.io/how-to/installation#running-net-bird-in-docker
#
# seems to work fine without CAP_SYS_ADMIN and CAP_SYS_RESOURCE
# CAP_NET_BIND_SERVICE could be added to allow binding on low ports, but is not required,
# see https://github.com/netbirdio/netbird/pull/1513
AmbientCapabilities =
[
# see https://man7.org/linux/man-pages/man7/capabilities.7.html
# see https://docs.netbird.io/how-to/installation#running-net-bird-in-docker
#
# seems to work fine without CAP_SYS_ADMIN and CAP_SYS_RESOURCE
# CAP_NET_BIND_SERVICE could be added to allow binding on low ports, but is not required,
# see https://github.com/netbirdio/netbird/pull/1513
# failed creating tunnel interface wt-priv: [operation not permitted
"CAP_NET_ADMIN"
# failed to pull up wgInterface [wt-priv]: failed to create ipv4 raw socket: socket: operation not permitted
"CAP_NET_RAW"
]
# required for eBPF filter, used to be subset of CAP_SYS_ADMIN
++ optional (versionAtLeast kernel.version "5.8") "CAP_BPF"
++ optional (versionOlder kernel.version "5.8") "CAP_SYS_ADMIN";
};
}));
# failed creating tunnel interface wt-priv: [operation not permitted
"CAP_NET_ADMIN"
# failed to pull up wgInterface [wt-priv]: failed to create ipv4 raw socket: socket: operation not permitted
"CAP_NET_RAW"
]
# required for eBPF filter, used to be subset of CAP_SYS_ADMIN
++ optional (versionAtLeast kernel.version "5.8") "CAP_BPF"
++ optional (versionOlder kernel.version "5.8") "CAP_SYS_ADMIN";
};
}
)
);
# see https://github.com/systemd/systemd/blob/17f3e91e8107b2b29fe25755651b230bbc81a514/src/resolve/org.freedesktop.resolve1.policy#L43-L43
security.polkit.extraConfig = mkIf config.services.resolved.enable ''
@ -478,13 +500,23 @@ in {
})
# migration & temporary fixups section
{
systemd.services = toClientAttrs (client:
systemd.services = toClientAttrs (
client:
nameValuePair client.name {
preStart = ''
set -eEuo pipefail
${optionalString (client.logLevel == "trace" || client.logLevel == "debug") "set -x"}
PATH="${makeBinPath (with pkgs; [coreutils jq diffutils])}:$PATH"
PATH="${
makeBinPath (
with pkgs;
[
coreutils
jq
diffutils
]
)
}:$PATH"
export ${toShellVars client.environment}
# merge /etc/${client.name}/config.d' into "$NB_CONFIG"
@ -507,7 +539,8 @@ in {
fi
}
'';
});
}
);
}
];
}

View file

@ -2,9 +2,9 @@
config,
lib,
...
}: let
inherit
(lib)
}:
let
inherit (lib)
attrNames
filterAttrs
flip
@ -13,69 +13,71 @@
nameValuePair
types
;
in {
in
{
options.services.nginx.upstreams = mkOption {
type = types.attrsOf (types.submodule {
options.monitoring = {
enable = mkOption {
type = types.bool;
description = "Whether to add a global monitoring entry for this upstream";
default = false;
};
type = types.attrsOf (
types.submodule {
options.monitoring = {
enable = mkOption {
type = types.bool;
description = "Whether to add a global monitoring entry for this upstream";
default = false;
};
path = mkOption {
type = types.str;
description = "The path to query.";
default = "";
};
path = mkOption {
type = types.str;
description = "The path to query.";
default = "";
};
expectedStatus = mkOption {
type = types.int;
default = 200;
description = "The HTTP status code to expect.";
};
expectedStatus = mkOption {
type = types.int;
default = 200;
description = "The HTTP status code to expect.";
};
expectedBodyRegex = mkOption {
type = types.nullOr types.str;
description = "A regex pattern to expect in the body.";
default = null;
};
expectedBodyRegex = mkOption {
type = types.nullOr types.str;
description = "A regex pattern to expect in the body.";
default = null;
};
useHttps = mkOption {
type = types.bool;
description = "Whether to use https to connect to this upstream when monitoring";
default = false;
};
useHttps = mkOption {
type = types.bool;
description = "Whether to use https to connect to this upstream when monitoring";
default = false;
};
skipTlsVerification = mkOption {
type = types.bool;
description = "Skip tls verification when using https.";
default = false;
skipTlsVerification = mkOption {
type = types.bool;
description = "Skip tls verification when using https.";
default = false;
};
};
};
});
}
);
};
config = let
monitoredUpstreams = filterAttrs (_: x: x.monitoring.enable) config.services.nginx.upstreams;
in {
globals.monitoring.http = flip mapAttrs' monitoredUpstreams (
upstreamName: upstream: let
schema =
if upstream.monitoring.useHttps
then "https"
else "http";
in
config =
let
monitoredUpstreams = filterAttrs (_: x: x.monitoring.enable) config.services.nginx.upstreams;
in
{
globals.monitoring.http = flip mapAttrs' monitoredUpstreams (
upstreamName: upstream:
let
schema = if upstream.monitoring.useHttps then "https" else "http";
in
nameValuePair "${config.node.name}-upstream-${upstreamName}" {
url = map (server: "${schema}://${server}${upstream.monitoring.path}") (attrNames upstream.servers);
network = "local-${config.node.name}";
inherit
(upstream.monitoring)
inherit (upstream.monitoring)
expectedBodyRegex
expectedStatus
skipTlsVerification
;
}
);
};
);
};
}

View file

@ -2,9 +2,9 @@
lib,
config,
...
}: let
inherit
(lib)
}:
let
inherit (lib)
concatStringsSep
mkDefault
mkEnableOption
@ -15,7 +15,8 @@
;
cfg = config.meta.oauth2-proxy;
in {
in
{
options.meta.oauth2-proxy = {
enable = mkEnableOption "oauth2 proxy";
@ -31,85 +32,93 @@ in {
};
options.services.nginx.virtualHosts = mkOption {
type = types.attrsOf (types.submodule ({config, ...}: {
options.oauth2 = {
enable = mkEnableOption "access protection of this resource using oauth2-proxy.";
allowedGroups = mkOption {
type = types.listOf types.str;
default = [];
description = ''
A list of groups that are allowed to access this resource, or the
empty list to allow any authenticated client.
'';
};
X-User = mkOption {
type = types.str;
default = "$upstream_http_x_auth_request_preferred_username";
description = "The variable to set as X-User";
};
X-Email = mkOption {
type = types.str;
default = "$upstream_http_x_auth_request_email";
description = "The variable to set as X-User";
};
};
options.locations = mkOption {
type = types.attrsOf (types.submodule (locationSubmod: {
options.setOauth2Headers = mkOption {
type = types.bool;
default = true;
description = "Whether to add oauth2 specific headers to this location. Only takes effect is oauth2 is actually enabled on the parent vhost.";
type = types.attrsOf (
types.submodule (
{ config, ... }:
{
options.oauth2 = {
enable = mkEnableOption "access protection of this resource using oauth2-proxy.";
allowedGroups = mkOption {
type = types.listOf types.str;
default = [ ];
description = ''
A list of groups that are allowed to access this resource, or the
empty list to allow any authenticated client.
'';
};
X-User = mkOption {
type = types.str;
default = "$upstream_http_x_auth_request_preferred_username";
description = "The variable to set as X-User";
};
X-Email = mkOption {
type = types.str;
default = "$upstream_http_x_auth_request_email";
description = "The variable to set as X-User";
};
};
config = mkIf (config.oauth2.enable && locationSubmod.config.setOauth2Headers) {
options.locations = mkOption {
type = types.attrsOf (
types.submodule (locationSubmod: {
options.setOauth2Headers = mkOption {
type = types.bool;
default = true;
description = "Whether to add oauth2 specific headers to this location. Only takes effect is oauth2 is actually enabled on the parent vhost.";
};
config = mkIf (config.oauth2.enable && locationSubmod.config.setOauth2Headers) {
extraConfig = ''
proxy_set_header X-User $user;
proxy_set_header X-Email $email;
add_header Set-Cookie $auth_cookie;
'';
};
})
);
};
config = mkIf config.oauth2.enable {
extraConfig = ''
proxy_set_header X-User $user;
proxy_set_header X-Email $email;
add_header Set-Cookie $auth_cookie;
auth_request /oauth2/auth;
error_page 401 = @redirectToAuth2ProxyLogin;
# set variables that can be used in locations.<name>.extraConfig
# pass information via X-User and X-Email headers to backend,
# requires running with --set-xauthrequest flag
auth_request_set $user ${config.oauth2.X-User};
auth_request_set $email ${config.oauth2.X-Email};
# if you enabled --cookie-refresh, this is needed for it to work with auth_request
auth_request_set $auth_cookie $upstream_http_set_cookie;
'';
locations."@redirectToAuth2ProxyLogin" = {
# FIXME: allow referring to another node for the portaldomain
setOauth2Headers = false;
return = "307 https://${cfg.portalDomain}/oauth2/start?rd=$scheme://$host$request_uri";
extraConfig = ''
auth_request off;
'';
};
locations."= /oauth2/auth" = {
setOauth2Headers = false;
proxyPass =
"http://oauth2-proxy/oauth2/auth"
+ optionalString (
config.oauth2.allowedGroups != [ ]
) "?allowed_groups=${concatStringsSep "," config.oauth2.allowedGroups}";
extraConfig = ''
auth_request off;
internal;
proxy_set_header X-Scheme $scheme;
# nginx auth_request includes headers but not body
proxy_set_header Content-Length "";
proxy_pass_request_body off;
'';
};
};
}));
};
config = mkIf config.oauth2.enable {
extraConfig = ''
auth_request /oauth2/auth;
error_page 401 = @redirectToAuth2ProxyLogin;
# set variables that can be used in locations.<name>.extraConfig
# pass information via X-User and X-Email headers to backend,
# requires running with --set-xauthrequest flag
auth_request_set $user ${config.oauth2.X-User};
auth_request_set $email ${config.oauth2.X-Email};
# if you enabled --cookie-refresh, this is needed for it to work with auth_request
auth_request_set $auth_cookie $upstream_http_set_cookie;
'';
locations."@redirectToAuth2ProxyLogin" = {
# FIXME: allow referring to another node for the portaldomain
setOauth2Headers = false;
return = "307 https://${cfg.portalDomain}/oauth2/start?rd=$scheme://$host$request_uri";
extraConfig = ''
auth_request off;
'';
};
locations."= /oauth2/auth" = {
setOauth2Headers = false;
proxyPass =
"http://oauth2-proxy/oauth2/auth"
+ optionalString (config.oauth2.allowedGroups != [])
"?allowed_groups=${concatStringsSep "," config.oauth2.allowedGroups}";
extraConfig = ''
auth_request off;
internal;
proxy_set_header X-Scheme $scheme;
# nginx auth_request includes headers but not body
proxy_set_header Content-Length "";
proxy_pass_request_body off;
'';
};
};
}));
}
)
);
};
config = mkIf cfg.enable {
@ -149,11 +158,11 @@ in {
RestartSec = "60"; # Retry every minute
};
users.groups.oauth2-proxy.members = ["nginx"];
users.groups.oauth2-proxy.members = [ "nginx" ];
services.nginx = {
upstreams.oauth2-proxy = {
servers."unix:/run/oauth2-proxy/oauth2-proxy.sock" = {};
servers."unix:/run/oauth2-proxy/oauth2-proxy.sock" = { };
extraConfig = ''
zone oauth2-proxy 64k;
keepalive 2;

View file

@ -5,9 +5,9 @@
nodes,
globals,
...
}: let
inherit
(lib)
}:
let
inherit (lib)
mkEnableOption
mkIf
mkOption
@ -15,7 +15,8 @@
;
cfg = config.meta.promtail;
in {
in
{
options.meta.promtail = {
enable = mkEnableOption "promtail to push logs to a loki instance.";
# TODO: FIXME: this should not be named proxy. get domain from globals and name this secretAggregatorNode or smth.
@ -114,15 +115,15 @@ in {
];
relabel_configs = [
{
source_labels = ["__journal__hostname"];
source_labels = [ "__journal__hostname" ];
target_label = "host";
}
{
source_labels = ["__journal_priority"];
source_labels = [ "__journal_priority" ];
target_label = "priority";
}
{
source_labels = ["__journal_priority_keyword"];
source_labels = [ "__journal_priority_keyword" ];
target_label = "level";
}
#{
@ -130,15 +131,15 @@ in {
# target_label = "unit";
#}
{
source_labels = ["__journal__systemd_user_unit"];
source_labels = [ "__journal__systemd_user_unit" ];
target_label = "user_unit";
}
{
source_labels = ["__journal__boot_id"];
source_labels = [ "__journal__boot_id" ];
target_label = "boot_id";
}
{
source_labels = ["__journal__comm"];
source_labels = [ "__journal__comm" ];
target_label = "command";
}
];

View file

@ -3,9 +3,9 @@
inputs,
lib,
...
}: let
inherit
(lib)
}:
let
inherit (lib)
assertMsg
literalExpression
mapAttrs
@ -16,29 +16,31 @@
# If the given expression is a bare set, it will be wrapped in a function,
# so that the imported file can always be applied to the inputs, similar to
# how modules can be functions or sets.
constSet = x:
if builtins.isAttrs x
then (_: x)
else x;
constSet = x: if builtins.isAttrs x then (_: x) else x;
# Try to access the extra builtin we loaded via nix-plugins.
# Throw an error if that doesn't exist.
rageImportEncrypted = assert assertMsg (builtins ? extraBuiltins.rageImportEncrypted) "The extra builtin 'rageImportEncrypted' is not available, so repo.secrets cannot be decrypted. Did you forget to add nix-plugins and point it to `./nix/extra-builtins.nix` ?";
rageImportEncrypted =
assert assertMsg (builtins ? extraBuiltins.rageImportEncrypted)
"The extra builtin 'rageImportEncrypted' is not available, so repo.secrets cannot be decrypted. Did you forget to add nix-plugins and point it to `./nix/extra-builtins.nix` ?";
builtins.extraBuiltins.rageImportEncrypted;
# This "imports" an encrypted .nix.age file by evaluating the decrypted content.
importEncrypted = path:
importEncrypted =
path:
constSet (
if builtins.pathExists path
then rageImportEncrypted inputs.self.secretsConfig.masterIdentities path
else {}
if builtins.pathExists path then
rageImportEncrypted inputs.self.secretsConfig.masterIdentities path
else
{ }
);
cfg = config.repo;
in {
in
{
options.repo = {
secretFiles = mkOption {
default = {};
default = { };
type = types.attrsOf types.path;
example = literalExpression "{ local = ./secrets.nix.age; }";
description = ''

View file

@ -6,9 +6,9 @@
nodes,
pkgs,
...
}: let
inherit
(lib)
}:
let
inherit (lib)
concatLists
elem
flip
@ -26,8 +26,9 @@
;
cfg = config.meta.telegraf;
mkIfNotEmpty = xs: mkIf (xs != []) xs;
in {
mkIfNotEmpty = xs: mkIf (xs != [ ]) xs;
in
{
options.meta.telegraf = {
enable = mkEnableOption "telegraf to push metrics to influx.";
@ -39,7 +40,7 @@ in {
secrets = mkOption {
type = types.attrsOf types.path;
default = {};
default = { };
example = {
"@INFLUX_TOKEN@" = "/run/agenix/influx-token";
};
@ -48,7 +49,7 @@ in {
availableMonitoringNetworks = mkOption {
type = types.listOf types.str;
example = ["internet"];
example = [ "internet" ];
description = ''
Any of the global monitoring definitions which has a network from this list
will automatically be monitored via telegraf. Set this to any networks that
@ -88,7 +89,7 @@ in {
config = mkIf (!minimal && cfg.enable) {
# Monitor anything that can only be monitored from this node
meta.telegraf.availableMonitoringNetworks = ["local-${config.node.name}"];
meta.telegraf.availableMonitoringNetworks = [ "local-${config.node.name}" ];
assertions = [
{
@ -106,9 +107,10 @@ in {
};
services.influxdb2.provision.organizations.machines.auths."telegraf (${config.node.name})" = {
readBuckets = ["telegraf"];
writeBuckets = ["telegraf"];
tokenFile = nodes.${cfg.influxdb2.node}.config.age.secrets."telegraf-influxdb-token-${config.node.name}".path;
readBuckets = [ "telegraf" ];
writeBuckets = [ "telegraf" ];
tokenFile =
nodes.${cfg.influxdb2.node}.config.age.secrets."telegraf-influxdb-token-${config.node.name}".path;
};
};
@ -121,28 +123,32 @@ in {
meta.telegraf.secrets."@INFLUX_TOKEN@" = config.age.secrets.telegraf-influxdb-token.path;
security.elewrap.telegraf-sensors = mkIf cfg.scrapeSensors {
command = ["${pkgs.lm_sensors}/bin/sensors" "-A" "-u"];
command = [
"${pkgs.lm_sensors}/bin/sensors"
"-A"
"-u"
];
targetUser = "root";
allowedUsers = ["telegraf"];
allowedUsers = [ "telegraf" ];
};
security.elewrap.telegraf-nvme = mkIf config.services.smartd.enable {
command = ["${pkgs.nvme-cli}/bin/nvme"];
command = [ "${pkgs.nvme-cli}/bin/nvme" ];
targetUser = "root";
allowedUsers = ["telegraf"];
allowedUsers = [ "telegraf" ];
passArguments = true;
};
security.elewrap.telegraf-smartctl = mkIf config.services.smartd.enable {
command = ["${pkgs.smartmontools}/bin/smartctl"];
command = [ "${pkgs.smartmontools}/bin/smartctl" ];
targetUser = "root";
allowedUsers = ["telegraf"];
allowedUsers = [ "telegraf" ];
passArguments = true;
};
services.telegraf = {
enable = true;
environmentFiles = ["/dev/null"]; # Needed so the config file is copied to /run/telegraf
environmentFiles = [ "/dev/null" ]; # Needed so the config file is copied to /run/telegraf
extraConfig = {
agent = {
interval = "10s";
@ -158,112 +164,135 @@ in {
};
outputs = {
influxdb_v2 = {
urls = ["https://${cfg.influxdb2.domain}"];
urls = [ "https://${cfg.influxdb2.domain}" ];
token = "@INFLUX_TOKEN@";
inherit (cfg.influxdb2) organization bucket;
};
};
inputs =
{
conntrack = {};
cpu = {};
disk = {};
diskio = {};
internal = {};
interrupts = {};
kernel = {};
kernel_vmstat = {};
linux_sysctl_fs = {};
mem = {};
conntrack = { };
cpu = { };
disk = { };
diskio = { };
internal = { };
interrupts = { };
kernel = { };
kernel_vmstat = { };
linux_sysctl_fs = { };
mem = { };
net = {
ignore_protocol_stats = true;
};
netstat = {};
nstat = {};
processes = {};
swap = {};
system = {};
netstat = { };
nstat = { };
processes = { };
swap = { };
system = { };
systemd_units = {
unittype = "service";
};
temp = {};
wireguard = {};
temp = { };
wireguard = { };
ping = mkIfNotEmpty (concatLists (flip mapAttrsToList globals.monitoring.ping (
name: pingCfg:
optionals (elem pingCfg.network cfg.availableMonitoringNetworks) (
concatLists (forEach ["hostv4" "hostv6"] (
attr:
optional (pingCfg.${attr} != null) {
interval = "1m";
method = "native";
urls = [pingCfg.${attr}];
ipv4 = attr == "hostv4";
ipv6 = attr == "hostv6";
tags = {
inherit name;
inherit (pingCfg) network;
ip_version =
if attr == "hostv4"
then "v4"
else "v6";
};
fieldinclude = [
"percent_packet_loss"
"average_response_ms"
];
}
))
ping = mkIfNotEmpty (
concatLists (
flip mapAttrsToList globals.monitoring.ping (
name: pingCfg:
optionals (elem pingCfg.network cfg.availableMonitoringNetworks) (
concatLists (
forEach
[
"hostv4"
"hostv6"
]
(
attr:
optional (pingCfg.${attr} != null) {
interval = "1m";
method = "native";
urls = [ pingCfg.${attr} ];
ipv4 = attr == "hostv4";
ipv6 = attr == "hostv6";
tags = {
inherit name;
inherit (pingCfg) network;
ip_version = if attr == "hostv4" then "v4" else "v6";
};
fieldinclude = [
"percent_packet_loss"
"average_response_ms"
];
}
)
)
)
)
)));
)
);
http_response = mkIfNotEmpty (concatLists (flip mapAttrsToList globals.monitoring.http (
name: httpCfg:
optional (elem httpCfg.network cfg.availableMonitoringNetworks) {
interval = "1m";
urls = toList httpCfg.url;
method = "GET";
response_status_code = httpCfg.expectedStatus;
response_string_match = mkIf (httpCfg.expectedBodyRegex != null) httpCfg.expectedBodyRegex;
insecure_skip_verify = httpCfg.skipTlsVerification;
follow_redirects = true;
tags = {
inherit name;
inherit (httpCfg) network;
};
}
)));
http_response = mkIfNotEmpty (
concatLists (
flip mapAttrsToList globals.monitoring.http (
name: httpCfg:
optional (elem httpCfg.network cfg.availableMonitoringNetworks) {
interval = "1m";
urls = toList httpCfg.url;
method = "GET";
response_status_code = httpCfg.expectedStatus;
response_string_match = mkIf (httpCfg.expectedBodyRegex != null) httpCfg.expectedBodyRegex;
insecure_skip_verify = httpCfg.skipTlsVerification;
follow_redirects = true;
tags = {
inherit name;
inherit (httpCfg) network;
};
}
)
)
);
dns_query = mkIfNotEmpty (concatLists (flip mapAttrsToList globals.monitoring.dns (
name: dnsCfg:
optional (elem dnsCfg.network cfg.availableMonitoringNetworks) {
interval = "1m";
servers = [dnsCfg.server];
domains = [dnsCfg.domain];
record_type = dnsCfg.record-type;
tags = {
inherit name;
inherit (dnsCfg) network;
};
}
)));
dns_query = mkIfNotEmpty (
concatLists (
flip mapAttrsToList globals.monitoring.dns (
name: dnsCfg:
optional (elem dnsCfg.network cfg.availableMonitoringNetworks) {
interval = "1m";
servers = [ dnsCfg.server ];
domains = [ dnsCfg.domain ];
record_type = dnsCfg.record-type;
tags = {
inherit name;
inherit (dnsCfg) network;
};
}
)
)
);
net_response = mkIfNotEmpty (concatLists (flip mapAttrsToList globals.monitoring.tcp (
name: tcpCfg:
optional (elem tcpCfg.network cfg.availableMonitoringNetworks) {
interval = "1m";
address = "${tcpCfg.host}:${toString tcpCfg.port}";
protocol = "tcp";
tags = {
inherit name;
inherit (tcpCfg) network;
};
fieldexclude = ["result_type" "string_found"];
}
)));
net_response = mkIfNotEmpty (
concatLists (
flip mapAttrsToList globals.monitoring.tcp (
name: tcpCfg:
optional (elem tcpCfg.network cfg.availableMonitoringNetworks) {
interval = "1m";
address = "${tcpCfg.host}:${toString tcpCfg.port}";
protocol = "tcp";
tags = {
inherit name;
inherit (tcpCfg) network;
};
fieldexclude = [
"result_type"
"string_found"
];
}
)
)
);
}
// optionalAttrs config.services.smartd.enable {
sensors = {};
sensors = { };
smart = {
attributes = true;
path_nvme = config.security.elewrap.telegraf-nvme.path;
@ -272,16 +301,19 @@ in {
};
}
// optionalAttrs config.services.nginx.enable {
nginx.urls = ["http://localhost/nginx_status"];
nginx.urls = [ "http://localhost/nginx_status" ];
}
// optionalAttrs (config.networking.wireless.enable || config.networking.wireless.iwd.enable) {
wireless = {};
wireless = { };
};
};
};
services.nginx.virtualHosts = mkIf config.services.nginx.enable {
localhost.listenAddresses = ["127.0.0.1" "[::1]"];
localhost.listenAddresses = [
"127.0.0.1"
"[::1]"
];
localhost.locations."= /nginx_status".extraConfig = ''
allow 127.0.0.0/8;
allow ::1;
@ -303,13 +335,14 @@ in {
systemd.services.telegraf = {
path = [
# Make sensors refer to the correct wrapper
(mkIf cfg.scrapeSensors
(pkgs.writeShellScriptBin "sensors" config.security.elewrap.telegraf-sensors.path))
(mkIf cfg.scrapeSensors (
pkgs.writeShellScriptBin "sensors" config.security.elewrap.telegraf-sensors.path
))
];
serviceConfig = {
ExecStartPre = mkAfter [
(
pkgs.writeShellScript "pre-start-token" (lib.concatLines (
(pkgs.writeShellScript "pre-start-token" (
lib.concatLines (
lib.flip lib.mapAttrsToList config.meta.telegraf.secrets (
key: secret: ''
${lib.getExe pkgs.replace-secret} \
@ -318,11 +351,11 @@ in {
/var/run/telegraf/config.toml
''
)
))
)
)
))
];
# For wireguard statistics
AmbientCapabilities = ["CAP_NET_ADMIN"];
AmbientCapabilities = [ "CAP_NET_ADMIN" ];
RestartSec = "60"; # Retry every minute
};
};