mirror of
https://github.com/NixOS/nixpkgs.git
synced 2025-11-10 17:54:53 +01:00
334 lines
11 KiB
Nix
334 lines
11 KiB
Nix
{
|
|
config,
|
|
lib,
|
|
pkgs,
|
|
...
|
|
}:
|
|
|
|
let
|
|
cfg = config.services.nipap;
|
|
iniFmt = pkgs.formats.ini { };
|
|
|
|
configFile = iniFmt.generate "nipap.conf" cfg.settings;
|
|
|
|
defaultUser = "nipap";
|
|
defaultAuthBackend = "local";
|
|
dataDir = "/var/lib/nipap";
|
|
|
|
defaultServiceConfig = {
|
|
WorkingDirectory = dataDir;
|
|
User = cfg.user;
|
|
Group = config.users.users."${cfg.user}".group;
|
|
Restart = "on-failure";
|
|
RestartSec = 30;
|
|
};
|
|
|
|
escapedHost = host: if lib.hasInfix ":" host then "[${host}]" else host;
|
|
in
|
|
{
|
|
options.services.nipap = {
|
|
enable = lib.mkEnableOption "global Neat IP Address Planner (NIPAP) configuration";
|
|
|
|
user = lib.mkOption {
|
|
type = lib.types.str;
|
|
description = "User to use for running NIPAP services.";
|
|
default = defaultUser;
|
|
};
|
|
|
|
settings = lib.mkOption {
|
|
description = ''
|
|
Configuration options to set in /etc/nipap/nipap.conf.
|
|
'';
|
|
|
|
default = { };
|
|
|
|
type = lib.types.submodule {
|
|
freeformType = iniFmt.type;
|
|
|
|
options = {
|
|
nipapd = {
|
|
listen = lib.mkOption {
|
|
type = lib.types.str;
|
|
default = "::1";
|
|
description = "IP address to bind nipapd to.";
|
|
};
|
|
port = lib.mkOption {
|
|
type = lib.types.port;
|
|
default = 1337;
|
|
description = "Port to bind nipapd to.";
|
|
};
|
|
|
|
foreground = lib.mkOption {
|
|
type = lib.types.bool;
|
|
default = true;
|
|
description = "Remain in foreground rather than forking to background.";
|
|
};
|
|
debug = lib.mkOption {
|
|
type = lib.types.bool;
|
|
default = false;
|
|
description = "Enable debug logging.";
|
|
};
|
|
|
|
db_host = lib.mkOption {
|
|
type = lib.types.str;
|
|
default = "";
|
|
description = "PostgreSQL host to connect to. Empty means use UNIX socket.";
|
|
};
|
|
db_name = lib.mkOption {
|
|
type = lib.types.str;
|
|
default = cfg.user;
|
|
defaultText = defaultUser;
|
|
description = "Name of database to use on PostgreSQL server.";
|
|
};
|
|
};
|
|
|
|
auth = {
|
|
default_backend = lib.mkOption {
|
|
type = lib.types.str;
|
|
default = defaultAuthBackend;
|
|
description = "Name of auth backend to use by default.";
|
|
};
|
|
auth_cache_timeout = lib.mkOption {
|
|
type = lib.types.int;
|
|
default = 3600;
|
|
description = "Seconds to store cached auth entries for.";
|
|
};
|
|
};
|
|
};
|
|
};
|
|
};
|
|
|
|
authBackendSettings = lib.mkOption {
|
|
description = ''
|
|
auth.backends options to set in /etc/nipap/nipap.conf.
|
|
'';
|
|
|
|
default = {
|
|
"${defaultAuthBackend}" = {
|
|
type = "SqliteAuth";
|
|
db_path = "${dataDir}/local_auth.db";
|
|
};
|
|
};
|
|
|
|
type = lib.types.submodule {
|
|
freeformType = iniFmt.type;
|
|
};
|
|
};
|
|
|
|
nipapd = {
|
|
enable = lib.mkEnableOption "nipapd server";
|
|
package = lib.mkPackageOption pkgs "nipap" { };
|
|
|
|
database.createLocally = lib.mkOption {
|
|
type = lib.types.bool;
|
|
default = true;
|
|
description = "Create a nipap database automatically.";
|
|
};
|
|
};
|
|
|
|
nipap-www = {
|
|
enable = lib.mkEnableOption "nipap-www server";
|
|
package = lib.mkPackageOption pkgs "nipap-www" { };
|
|
|
|
xmlrpcURIFile = lib.mkOption {
|
|
type = lib.types.nullOr lib.types.path;
|
|
default = null;
|
|
description = "Path to file containing XMLRPC URI for use by web UI - this is a secret, since it contains auth credentials. If null, it will be initialized assuming that the auth database is local.";
|
|
};
|
|
|
|
workers = lib.mkOption {
|
|
type = lib.types.int;
|
|
default = 4;
|
|
description = "Number of worker processes for Gunicorn to fork.";
|
|
};
|
|
umask = lib.mkOption {
|
|
type = lib.types.str;
|
|
default = "0";
|
|
description = "umask for files written by Gunicorn, including UNIX socket.";
|
|
};
|
|
|
|
unixSocket = lib.mkOption {
|
|
type = lib.types.nullOr lib.types.str;
|
|
default = null;
|
|
description = "Path to UNIX socket to bind to.";
|
|
example = "/run/nipap/nipap-www.sock";
|
|
};
|
|
host = lib.mkOption {
|
|
type = lib.types.nullOr lib.types.str;
|
|
default = "::";
|
|
description = "Host to bind to.";
|
|
};
|
|
port = lib.mkOption {
|
|
type = lib.types.nullOr lib.types.port;
|
|
default = 21337;
|
|
description = "Port to bind to.";
|
|
};
|
|
};
|
|
};
|
|
|
|
config = lib.mkIf cfg.enable (
|
|
lib.mkMerge [
|
|
{
|
|
systemd.tmpfiles.rules = [
|
|
"d '${dataDir}' - ${cfg.user} ${config.users.users."${cfg.user}".group} - -"
|
|
];
|
|
|
|
environment.etc."nipap/nipap.conf" = {
|
|
source = configFile;
|
|
};
|
|
|
|
services.nipap.settings = lib.attrsets.mapAttrs' (name: value: {
|
|
name = "auth.backends.${name}";
|
|
inherit value;
|
|
}) cfg.authBackendSettings;
|
|
|
|
services.nipap.nipapd.enable = lib.mkDefault true;
|
|
services.nipap.nipap-www.enable = lib.mkDefault true;
|
|
|
|
environment.systemPackages = [
|
|
cfg.nipapd.package
|
|
];
|
|
}
|
|
(lib.mkIf (cfg.user == defaultUser) {
|
|
users.users."${defaultUser}" = {
|
|
isSystemUser = true;
|
|
group = defaultUser;
|
|
home = dataDir;
|
|
};
|
|
users.groups."${defaultUser}" = { };
|
|
})
|
|
(lib.mkIf (cfg.nipapd.enable && cfg.nipapd.database.createLocally) {
|
|
services.postgresql = {
|
|
enable = true;
|
|
extensions = ps: with ps; [ ip4r ];
|
|
ensureUsers = [
|
|
{
|
|
name = cfg.user;
|
|
}
|
|
];
|
|
ensureDatabases = [ cfg.settings.nipapd.db_name ];
|
|
};
|
|
|
|
systemd.services.postgresql.serviceConfig.ExecStartPost =
|
|
let
|
|
sqlFile = pkgs.writeText "nipapd-setup.sql" ''
|
|
CREATE EXTENSION IF NOT EXISTS ip4r;
|
|
|
|
ALTER SCHEMA public OWNER TO "${cfg.user}";
|
|
ALTER DATABASE "${cfg.settings.nipapd.db_name}" OWNER TO "${cfg.user}";
|
|
'';
|
|
in
|
|
[
|
|
''
|
|
${lib.getExe' config.services.postgresql.finalPackage "psql"} -d "${cfg.settings.nipapd.db_name}" -f "${sqlFile}"
|
|
''
|
|
];
|
|
})
|
|
(lib.mkIf cfg.nipapd.enable {
|
|
systemd.services.nipapd =
|
|
let
|
|
pkg = cfg.nipapd.package;
|
|
in
|
|
{
|
|
description = "Neat IP Address Planner";
|
|
after = [
|
|
"network.target"
|
|
"systemd-tmpfiles-setup.service"
|
|
]
|
|
++ lib.optional (cfg.settings.nipapd.db_host == "") "postgresql.target";
|
|
requires = lib.optional (cfg.settings.nipapd.db_host == "") "postgresql.target";
|
|
wantedBy = [ "multi-user.target" ];
|
|
preStart = lib.optionalString (cfg.settings.auth.default_backend == defaultAuthBackend) ''
|
|
# Create/upgrade local auth database
|
|
umask 077
|
|
${pkg}/bin/nipap-passwd create-database >/dev/null 2>&1
|
|
${pkg}/bin/nipap-passwd upgrade-database >/dev/null 2>&1
|
|
'';
|
|
serviceConfig = defaultServiceConfig // {
|
|
KillSignal = "SIGINT";
|
|
ExecStart = ''
|
|
${pkg}/bin/nipapd \
|
|
--auto-install-db \
|
|
--auto-upgrade-db \
|
|
--foreground \
|
|
--no-pid-file
|
|
'';
|
|
};
|
|
};
|
|
})
|
|
(lib.mkIf cfg.nipap-www.enable {
|
|
assertions = [
|
|
{
|
|
assertion =
|
|
cfg.nipap-www.xmlrpcURIFile == null -> cfg.settings.auth.default_backend == defaultAuthBackend;
|
|
message = "If no XMLRPC URI secret file is specified, then the default auth backend must be in use to automatically generate credentials.";
|
|
}
|
|
];
|
|
|
|
# Ensure that _something_ exists in the [www] group.
|
|
services.nipap.settings.www = lib.mkDefault { };
|
|
|
|
systemd.services.nipap-www =
|
|
let
|
|
pkg = cfg.nipap-www.package;
|
|
in
|
|
{
|
|
description = "Neat IP Address Planner web server";
|
|
after = [
|
|
"network.target"
|
|
"systemd-tmpfiles-setup.service"
|
|
]
|
|
++ lib.optional cfg.nipapd.enable "nipapd.service";
|
|
wantedBy = [ "multi-user.target" ];
|
|
environment = {
|
|
PYTHONPATH = pkg.pythonPath;
|
|
};
|
|
serviceConfig = defaultServiceConfig;
|
|
script =
|
|
let
|
|
bind =
|
|
if cfg.nipap-www.unixSocket != null then
|
|
"unix:${cfg.nipap-www.unixSocket}"
|
|
else
|
|
"${escapedHost cfg.nipap-www.host}:${toString cfg.nipap-www.port}";
|
|
generateXMLRPC = cfg.nipap-www.xmlrpcURIFile == null;
|
|
xmlrpcURIFile = if generateXMLRPC then "${dataDir}/www_xmlrpc_uri" else cfg.nipap-www.xmlrpcURIFile;
|
|
in
|
|
''
|
|
test -f "${dataDir}/www_secret" || {
|
|
umask 0077
|
|
${pkg.python}/bin/python -c "import secrets; print(secrets.token_hex())" > "${dataDir}/www_secret"
|
|
}
|
|
export FLASK_SECRET_KEY="$(cat "${dataDir}/www_secret")"
|
|
|
|
# Ensure that we have an XMLRPC URI.
|
|
${
|
|
if generateXMLRPC then
|
|
''
|
|
test -f "${dataDir}/www_xmlrpc_uri" || {
|
|
umask 0077
|
|
www_password="$(${pkg.python}/bin/python -c "import secrets; print(secrets.token_hex())")"
|
|
${cfg.nipapd.package}/bin/nipap-passwd add --username nipap-www --password "''${www_password}" --name "User account for the web UI" --trusted
|
|
|
|
echo "http://nipap-www@${defaultAuthBackend}:''${www_password}@${escapedHost cfg.settings.nipapd.listen}:${toString cfg.settings.nipapd.port}" > "${xmlrpcURIFile}"
|
|
}
|
|
''
|
|
else
|
|
""
|
|
}
|
|
export FLASK_XMLRPC_URI="$(cat "${xmlrpcURIFile}")"
|
|
|
|
exec "${pkg.gunicorn}/bin/gunicorn" \
|
|
--preload --workers ${toString cfg.nipap-www.workers} \
|
|
--pythonpath "${pkg}/${pkg.python.sitePackages}" \
|
|
--bind ${bind} --umask ${cfg.nipap-www.umask} \
|
|
"nipapwww:create_app()"
|
|
'';
|
|
};
|
|
})
|
|
]
|
|
);
|
|
|
|
meta.maintainers = with lib.maintainers; [ lukegb ];
|
|
}
|