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:
parent
deca311c68
commit
7ccd7856ee
162 changed files with 4750 additions and 3718 deletions
|
@ -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}" ];
|
||||
});
|
||||
}
|
||||
|
|
|
@ -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" ];
|
||||
};
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
# ];
|
||||
};
|
||||
}));
|
||||
})
|
||||
);
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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`";
|
||||
});
|
||||
);
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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 = [ ];
|
||||
};
|
||||
};
|
||||
};
|
||||
});
|
||||
}
|
||||
);
|
||||
};
|
||||
};
|
||||
};
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
}
|
||||
'';
|
||||
});
|
||||
}
|
||||
);
|
||||
}
|
||||
];
|
||||
}
|
||||
|
|
|
@ -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
|
||||
;
|
||||
}
|
||||
);
|
||||
};
|
||||
);
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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";
|
||||
}
|
||||
];
|
||||
|
|
|
@ -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 = ''
|
||||
|
|
|
@ -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
|
||||
};
|
||||
};
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue