push sheeet
Some checks failed
Periodic Merges (6h) / master → staging-nixos (push) Failing after 12m50s
Periodic Merges (6h) / master → staging-next (push) Failing after 12m54s
Periodic Merges (24h) / merge-base(master,staging) → haskell-updates (push) Failing after 11m54s
Periodic Merges (6h) / staging-next → staging (push) Failing after 12m13s
Periodic Merges (24h) / staging-next-25.05 → staging-25.05 (push) Failing after 13m24s
Periodic Merges (24h) / release-25.05 → staging-next-25.05 (push) Failing after 14m28s

This commit is contained in:
Dark Steveneq
2025-10-09 14:15:47 +02:00
commit 646b892680
49168 changed files with 5897842 additions and 0 deletions

View File

@@ -0,0 +1,93 @@
{
config,
pkgs,
lib,
utils,
...
}:
let
cfg = config.services.docuum;
inherit (lib)
mkIf
mkEnableOption
mkOption
getExe
types
optionals
concatMap
;
in
{
options.services.docuum = {
enable = mkEnableOption "docuum daemon";
threshold = mkOption {
description = "Threshold for deletion in bytes, like `10 GB`, `10 GiB`, `10GB` or percentage-based thresholds like `50%`";
type = types.str;
default = "10 GB";
example = "50%";
};
minAge = mkOption {
description = "Sets the minimum age of images to be considered for deletion.";
type = types.nullOr types.str;
default = null;
example = "1d";
};
keep = mkOption {
description = "Prevents deletion of images for which repository:tag matches the specified regex.";
type = types.listOf types.str;
default = [ ];
example = [ "^my-image" ];
};
deletionChunkSize = mkOption {
description = "Removes specified quantity of images at a time.";
type = types.int;
default = 1;
example = 10;
};
};
config = mkIf cfg.enable {
assertions = [
{
assertion = config.virtualisation.docker.enable;
message = "docuum requires docker on the host";
}
];
systemd.services.docuum = {
after = [ "docker.socket" ];
requires = [ "docker.socket" ];
wantedBy = [ "multi-user.target" ];
path = [ config.virtualisation.docker.package ];
environment.HOME = "/var/lib/docuum";
serviceConfig = {
DynamicUser = true;
StateDirectory = "docuum";
SupplementaryGroups = [ "docker" ];
ExecStart = utils.escapeSystemdExecArgs (
[
(getExe pkgs.docuum)
"--threshold"
cfg.threshold
"--deletion-chunk-size"
cfg.deletionChunkSize
]
++ (concatMap (keep: [
"--keep"
keep
]) cfg.keep)
++ (optionals (cfg.minAge != null) [
"--min-age"
cfg.minAge
])
);
};
};
};
}

View File

@@ -0,0 +1,54 @@
{
config,
pkgs,
lib,
...
}:
let
cfg = config.services.meshcentral;
configFormat = pkgs.formats.json { };
configFile = configFormat.generate "meshcentral-config.json" cfg.settings;
in
with lib;
{
options.services.meshcentral = with types; {
enable = mkEnableOption "MeshCentral computer management server";
package = mkPackageOption pkgs "meshcentral" { };
settings = mkOption {
description = ''
Settings for MeshCentral. Refer to upstream documentation for details:
- [JSON Schema definition](https://github.com/Ylianst/MeshCentral/blob/master/meshcentral-config-schema.json)
- [simple sample configuration](https://github.com/Ylianst/MeshCentral/blob/master/sample-config.json)
- [complex sample configuration](https://github.com/Ylianst/MeshCentral/blob/master/sample-config-advanced.json)
- [Old homepage with documentation link](https://www.meshcommander.com/meshcentral2)
'';
type = types.submodule {
freeformType = attrsOf configFormat.type;
};
example = {
settings = {
WANonly = true;
Cert = "meshcentral.example.com";
TlsOffload = "10.0.0.2,fd42::2";
Port = 4430;
};
domains."".certUrl = "https://meshcentral.example.com/";
};
};
};
config = mkIf cfg.enable {
services.meshcentral.settings.settings.autoBackup.backupPath =
lib.mkDefault "/var/lib/meshcentral/backups";
systemd.services.meshcentral = {
wantedBy = [ "multi-user.target" ];
serviceConfig = {
ExecStart = "${cfg.package}/bin/meshcentral --datapath /var/lib/meshcentral --configfile ${configFile}";
DynamicUser = true;
StateDirectory = "meshcentral";
CacheDirectory = "meshcentral";
};
};
};
meta.maintainers = [ ];
}

View File

@@ -0,0 +1,163 @@
{
config,
pkgs,
lib,
...
}:
let
cfg = config.services.oxidized;
in
{
options.services.oxidized = {
enable = lib.mkEnableOption "the oxidized configuration backup service";
package = lib.mkPackageOption pkgs "oxidized" { };
user = lib.mkOption {
type = lib.types.str;
default = "oxidized";
description = ''
User under which the oxidized service runs.
'';
};
group = lib.mkOption {
type = lib.types.str;
default = "oxidized";
description = ''
Group under which the oxidized service runs.
'';
};
dataDir = lib.mkOption {
type = lib.types.path;
default = "/var/lib/oxidized";
description = "State directory for the oxidized service.";
};
configFile = lib.mkOption {
type = lib.types.nullOr lib.types.path;
example = lib.literalExpression ''
pkgs.writeText "oxidized-config.yml" '''
---
debug: true
use_syslog: true
input:
default: ssh
ssh:
secure: true
interval: 3600
model_map:
dell: powerconnect
hp: procurve
source:
default: csv
csv:
delimiter: !ruby/regexp /:/
file: "/var/lib/oxidized/.config/oxidized/router.db"
map:
name: 0
model: 1
username: 2
password: 3
pid: "/var/lib/oxidized/.config/oxidized/pid"
rest: 127.0.0.1:8888
retries: 3
# ... additional config
''';
'';
description = ''
Path to the oxidized configuration file.
'';
};
routerDB = lib.mkOption {
type = lib.types.nullOr lib.types.path;
default = null;
example = lib.literalExpression ''
pkgs.writeText "oxidized-router.db" '''
hostname-sw1:powerconnect:username1:password2
hostname-sw2:procurve:username2:password2
# ... additional hosts
'''
'';
description = ''
Path to the file/database which contains the targets for oxidized.
'';
};
};
config = lib.mkIf cfg.enable {
users.groups.${cfg.group} = { };
users.users.${cfg.user} = {
description = "Oxidized service user";
group = cfg.group;
home = cfg.dataDir;
createHome = true;
isSystemUser = true;
};
systemd.tmpfiles.settings."10-oxidized" = {
"${cfg.dataDir}" = {
d = {
mode = "0750";
user = cfg.user;
group = cfg.group;
};
};
"${cfg.dataDir}/.config" = {
d = {
mode = "0750";
user = cfg.user;
group = cfg.group;
};
};
"${cfg.dataDir}/.config/oxidized" = {
d = {
mode = "0750";
user = cfg.user;
group = cfg.group;
};
};
}
// lib.optionalAttrs (cfg.configFile != null) {
"${cfg.dataDir}/.config/oxidized/config" = {
"L+" = {
argument = "${cfg.configFile}";
user = cfg.user;
group = cfg.group;
};
};
}
// lib.optionalAttrs (cfg.routerDB != null) {
"${cfg.dataDir}/.config/oxidized/router.db" = {
"L+" = {
argument = "${cfg.routerDB}";
user = cfg.user;
group = cfg.group;
};
};
};
systemd.services.oxidized = {
wantedBy = [ "multi-user.target" ];
after = [ "network.target" ];
serviceConfig = {
ExecStart = lib.getExe cfg.package;
User = cfg.user;
Group = cfg.group;
UMask = "0077";
NoNewPrivileges = true;
Restart = "always";
WorkingDirectory = cfg.dataDir;
KillSignal = "SIGKILL";
PIDFile = "${cfg.dataDir}/.config/oxidized/pid";
};
};
};
}

View File

@@ -0,0 +1,282 @@
{
config,
lib,
pkgs,
...
}:
let
cfg = config.services.pgadmin;
_base = with lib.types; [
int
bool
str
];
base =
with lib.types;
oneOf (
[
(listOf (oneOf _base))
(attrsOf (oneOf _base))
]
++ _base
);
formatAttrset =
attr:
"{${
lib.concatStringsSep "\n" (
lib.mapAttrsToList (key: value: "${builtins.toJSON key}: ${formatPyValue value},") attr
)
}}";
formatPyValue =
value:
if builtins.isString value then
builtins.toJSON value
else if value ? _expr then
value._expr
else if builtins.isInt value then
toString value
else if builtins.isBool value then
(if value then "True" else "False")
else if builtins.isAttrs value then
(formatAttrset value)
else if builtins.isList value then
"[${lib.concatStringsSep "\n" (map (v: "${formatPyValue v},") value)}]"
else
throw "Unrecognized type";
formatPy =
attrs:
lib.concatStringsSep "\n" (
lib.mapAttrsToList (key: value: "${key} = ${formatPyValue value}") attrs
);
pyType =
with lib.types;
attrsOf (oneOf [
(attrsOf base)
(listOf base)
base
]);
in
{
options.services.pgadmin = {
enable = lib.mkEnableOption "PostgreSQL Admin 4";
port = lib.mkOption {
description = "Port for pgadmin4 to run on";
type = lib.types.port;
default = 5050;
};
package = lib.mkPackageOption pkgs "pgadmin4" { };
initialEmail = lib.mkOption {
description = "Initial email for the pgAdmin account";
type = lib.types.str;
};
initialPasswordFile = lib.mkOption {
description = ''
Initial password file for the pgAdmin account. Minimum length by default is 6.
Please see `services.pgadmin.minimumPasswordLength`.
NOTE: Should be string not a store path, to prevent the password from being world readable
'';
type = lib.types.path;
};
minimumPasswordLength = lib.mkOption {
description = "Minimum length of the password";
type = lib.types.int;
default = 6;
};
emailServer = {
enable = lib.mkOption {
description = ''
Enable SMTP email server. This is necessary, if you want to use password recovery or change your own password
'';
type = lib.types.bool;
default = false;
};
address = lib.mkOption {
description = "SMTP server for email delivery";
type = lib.types.str;
default = "localhost";
};
port = lib.mkOption {
description = "SMTP server port for email delivery";
type = lib.types.port;
default = 25;
};
useSSL = lib.mkOption {
description = "SMTP server should use SSL";
type = lib.types.bool;
default = false;
};
useTLS = lib.mkOption {
description = "SMTP server should use TLS";
type = lib.types.bool;
default = false;
};
username = lib.mkOption {
description = "SMTP server username for email delivery";
type = lib.types.nullOr lib.types.str;
default = null;
};
sender = lib.mkOption {
description = ''
SMTP server sender email for email delivery. Some servers require this to be a valid email address from that server
'';
type = lib.types.str;
example = "noreply@example.com";
};
passwordFile = lib.mkOption {
description = ''
Password for SMTP email account.
NOTE: Should be string not a store path, to prevent the password from being world readable
'';
type = lib.types.path;
};
};
openFirewall = lib.mkEnableOption "firewall passthrough for pgadmin4";
settings = lib.mkOption {
description = ''
Settings for pgadmin4.
[Documentation](https://www.pgadmin.org/docs/pgadmin4/development/config_py.html)
'';
type = pyType;
default = { };
};
};
config = lib.mkIf (cfg.enable) {
networking.firewall.allowedTCPPorts = lib.mkIf (cfg.openFirewall) [ cfg.port ];
services.pgadmin.settings = {
DEFAULT_SERVER_PORT = cfg.port;
PASSWORD_LENGTH_MIN = cfg.minimumPasswordLength;
SERVER_MODE = true;
UPGRADE_CHECK_ENABLED = false;
}
// (lib.optionalAttrs cfg.openFirewall {
DEFAULT_SERVER = lib.mkDefault "::";
})
// (lib.optionalAttrs cfg.emailServer.enable {
MAIL_SERVER = cfg.emailServer.address;
MAIL_PORT = cfg.emailServer.port;
MAIL_USE_SSL = cfg.emailServer.useSSL;
MAIL_USE_TLS = cfg.emailServer.useTLS;
MAIL_USERNAME = cfg.emailServer.username;
SECURITY_EMAIL_SENDER = cfg.emailServer.sender;
});
systemd.services.pgadmin = {
wantedBy = [ "multi-user.target" ];
after = [ "network.target" ];
requires = [ "network.target" ];
# we're adding this optionally so just in case there's any race it'll be caught
# in case postgres doesn't start, pgadmin will just start normally
wants = [ "postgresql.target" ];
path = [
config.services.postgresql.package
pkgs.coreutils
pkgs.bash
];
preStart = ''
# NOTE: this is idempotent (aka running it twice has no effect)
# Check here for password length to prevent pgadmin from starting
# and presenting a hard to find error message
# see https://github.com/NixOS/nixpkgs/issues/270624
PW_FILE="$CREDENTIALS_DIRECTORY/initial_password"
PW_LENGTH=$(wc -m < "$PW_FILE")
if [ $PW_LENGTH -lt ${toString cfg.minimumPasswordLength} ]; then
echo "Password must be at least ${toString cfg.minimumPasswordLength} characters long"
exit 1
fi
(
# Email address:
echo ${lib.escapeShellArg cfg.initialEmail}
# file might not contain newline. echo hack fixes that.
PW=$(cat "$PW_FILE")
# Password:
echo "$PW"
# Retype password:
echo "$PW"
) | ${cfg.package}/bin/pgadmin4-cli setup-db
'';
restartTriggers = [
"/etc/pgadmin/config_system.py"
];
serviceConfig = {
User = "pgadmin";
DynamicUser = true;
LogsDirectory = "pgadmin";
StateDirectory = "pgadmin";
ExecStart = "${cfg.package}/bin/pgadmin4";
LoadCredential = [
"initial_password:${cfg.initialPasswordFile}"
]
++ lib.optional cfg.emailServer.enable "email_password:${cfg.emailServer.passwordFile}";
AmbientCapabilities = "";
CapabilityBoundingSet = "";
LockPersonality = true;
MemoryDenyWriteExecute = true;
NoNewPrivileges = true;
PrivateDevices = true;
PrivateMounts = true;
PrivateTmp = true;
ProtectClock = true;
ProtectControlGroups = true;
ProtectHome = true;
ProtectHostname = true;
ProtectKernelLogs = true;
ProtectKernelModules = true;
ProtectKernelTunables = true;
ProtectSystem = "full";
RemoveIPC = true;
RestrictAddressFamilies = [
"AF_UNIX"
"AF_INET"
"AF_INET6"
];
RestrictNamespaces = true;
RestrictRealtime = true;
RestrictSUIDSGID = true;
SystemCallArchitectures = "native";
UMask = 27;
};
};
users.users.pgadmin = {
isSystemUser = true;
group = "pgadmin";
};
users.groups.pgadmin = { };
environment.etc."pgadmin/config_system.py" = {
text =
lib.optionalString cfg.emailServer.enable ''
import os
with open(os.path.join(os.environ['CREDENTIALS_DIRECTORY'], 'email_password')) as f:
pw = f.read()
MAIL_PASSWORD = pw
''
+ formatPy cfg.settings;
mode = "0600";
user = "pgadmin";
group = "pgadmin";
};
};
}

View File

@@ -0,0 +1,63 @@
{
config,
pkgs,
lib,
...
}:
let
cfg = config.services.salt.master;
fullConfig = lib.recursiveUpdate {
# Provide defaults for some directories to allow an immutable config dir
# Default is equivalent to /etc/salt/master.d/*.conf
default_include = "/var/lib/salt/master.d/*.conf";
# Default is in /etc/salt/pki/master
pki_dir = "/var/lib/salt/pki/master";
} cfg.configuration;
in
{
options = {
services.salt.master = {
enable = lib.mkEnableOption "Salt configuration management system master service";
configuration = lib.mkOption {
type = lib.types.attrs;
default = { };
description = "Salt master configuration as Nix attribute set.";
};
};
};
config = lib.mkIf cfg.enable {
environment = {
# Set this up in /etc/salt/master so `salt`, `salt-key`, etc. work.
# The alternatives are
# - passing --config-dir to all salt commands, not just the master unit,
# - setting a global environment variable,
etc."salt/master".source = pkgs.writeText "master" (builtins.toJSON fullConfig);
systemPackages = with pkgs; [ salt ];
};
systemd.services.salt-master = {
description = "Salt Master";
wantedBy = [ "multi-user.target" ];
after = [ "network.target" ];
path = with pkgs; [
util-linux # for dmesg
];
serviceConfig = {
ExecStart = "${pkgs.salt}/bin/salt-master";
LimitNOFILE = 16384;
Type = "notify";
NotifyAccess = "all";
};
restartTriggers = [
config.environment.etc."salt/master".source
];
};
};
meta.maintainers = with lib.maintainers; [ Flakebi ];
}

View File

@@ -0,0 +1,66 @@
{
config,
pkgs,
lib,
...
}:
let
cfg = config.services.salt.minion;
fullConfig = lib.recursiveUpdate {
# Provide defaults for some directories to allow an immutable config dir
# NOTE: the config dir being immutable prevents `minion_id` caching
# Default is equivalent to /etc/salt/minion.d/*.conf
default_include = "/var/lib/salt/minion.d/*.conf";
# Default is in /etc/salt/pki/minion
pki_dir = "/var/lib/salt/pki/minion";
} cfg.configuration;
in
{
options = {
services.salt.minion = {
enable = lib.mkEnableOption "Salt configuration management system minion service";
configuration = lib.mkOption {
type = lib.types.attrs;
default = { };
description = ''
Salt minion configuration as Nix attribute set.
See <https://docs.saltstack.com/en/latest/ref/configuration/minion.html>
for details.
'';
};
};
};
config = lib.mkIf cfg.enable {
environment = {
# Set this up in /etc/salt/minion so `salt-call`, etc. work.
# The alternatives are
# - passing --config-dir to all salt commands, not just the minion unit,
# - setting aglobal environment variable.
etc."salt/minion".source = pkgs.writeText "minion" (builtins.toJSON fullConfig);
systemPackages = with pkgs; [ salt ];
};
systemd.services.salt-minion = {
description = "Salt Minion";
wantedBy = [ "multi-user.target" ];
after = [ "network.target" ];
path = with pkgs; [
util-linux
];
serviceConfig = {
ExecStart = "${pkgs.salt}/bin/salt-minion";
LimitNOFILE = 8192;
Type = "notify";
NotifyAccess = "all";
};
restartTriggers = [
config.environment.etc."salt/minion".source
];
};
};
}