- Add the migrations directory to the package - Add postgres support to the package - Add a service for powerdns-admin Co-authored-by: Zhaofeng Li <hello@zhaofeng.li>main
parent
040d6eb51a
commit
368b22d09b
@ -0,0 +1,149 @@ |
||||
{ config, lib, pkgs, ... }: |
||||
|
||||
with lib; |
||||
|
||||
let |
||||
cfg = config.services.powerdns-admin; |
||||
|
||||
configText = '' |
||||
${cfg.config} |
||||
'' |
||||
+ optionalString (cfg.secretKeyFile != null) '' |
||||
with open('${cfg.secretKeyFile}') as file: |
||||
SECRET_KEY = file.read() |
||||
'' |
||||
+ optionalString (cfg.saltFile != null) '' |
||||
with open('${cfg.saltFile}') as file: |
||||
SALT = file.read() |
||||
''; |
||||
in |
||||
{ |
||||
options.services.powerdns-admin = { |
||||
enable = mkEnableOption "the PowerDNS web interface"; |
||||
|
||||
extraArgs = mkOption { |
||||
type = types.listOf types.str; |
||||
default = [ ]; |
||||
example = literalExpression '' |
||||
[ "-b" "127.0.0.1:8000" ] |
||||
''; |
||||
description = '' |
||||
Extra arguments passed to powerdns-admin. |
||||
''; |
||||
}; |
||||
|
||||
config = mkOption { |
||||
type = types.str; |
||||
default = ""; |
||||
example = '' |
||||
BIND_ADDRESS = '127.0.0.1' |
||||
PORT = 8000 |
||||
SQLALCHEMY_DATABASE_URI = 'postgresql://powerdnsadmin@/powerdnsadmin?host=/run/postgresql' |
||||
''; |
||||
description = '' |
||||
Configuration python file. |
||||
See <link xlink:href="https://github.com/ngoduykhanh/PowerDNS-Admin/blob/v${pkgs.powerdns-admin.version}/configs/development.py">the example configuration</link> |
||||
for options. |
||||
''; |
||||
}; |
||||
|
||||
secretKeyFile = mkOption { |
||||
type = types.nullOr types.path; |
||||
example = "/etc/powerdns-admin/secret"; |
||||
description = '' |
||||
The secret used to create cookies. |
||||
This needs to be set, otherwise the default is used and everyone can forge valid login cookies. |
||||
Set this to null to ignore this setting and configure it through another way. |
||||
''; |
||||
}; |
||||
|
||||
saltFile = mkOption { |
||||
type = types.nullOr types.path; |
||||
example = "/etc/powerdns-admin/salt"; |
||||
description = '' |
||||
The salt used for serialization. |
||||
This should be set, otherwise the default is used. |
||||
Set this to null to ignore this setting and configure it through another way. |
||||
''; |
||||
}; |
||||
}; |
||||
|
||||
config = mkIf cfg.enable { |
||||
systemd.services.powerdns-admin = { |
||||
description = "PowerDNS web interface"; |
||||
wantedBy = [ "multi-user.target" ]; |
||||
after = [ "networking.target" ]; |
||||
|
||||
environment.FLASK_CONF = builtins.toFile "powerdns-admin-config.py" configText; |
||||
environment.PYTHONPATH = pkgs.powerdns-admin.pythonPath; |
||||
serviceConfig = { |
||||
ExecStart = "${pkgs.powerdns-admin}/bin/powerdns-admin --pid /run/powerdns-admin/pid ${escapeShellArgs cfg.extraArgs}"; |
||||
ExecStartPre = "${pkgs.coreutils}/bin/env FLASK_APP=${pkgs.powerdns-admin}/share/powerdnsadmin/__init__.py ${pkgs.python3Packages.flask}/bin/flask db upgrade -d ${pkgs.powerdns-admin}/share/migrations"; |
||||
ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID"; |
||||
ExecStop = "${pkgs.coreutils}/bin/kill -TERM $MAINPID"; |
||||
PIDFile = "/run/powerdns-admin/pid"; |
||||
RuntimeDirectory = "powerdns-admin"; |
||||
User = "powerdnsadmin"; |
||||
Group = "powerdnsadmin"; |
||||
|
||||
AmbientCapabilities = "CAP_NET_BIND_SERVICE"; |
||||
BindReadOnlyPaths = [ |
||||
"/nix/store" |
||||
"-/etc/resolv.conf" |
||||
"-/etc/nsswitch.conf" |
||||
"-/etc/hosts" |
||||
"-/etc/localtime" |
||||
] |
||||
++ (optional (cfg.secretKeyFile != null) cfg.secretKeyFile) |
||||
++ (optional (cfg.saltFile != null) cfg.saltFile); |
||||
CapabilityBoundingSet = "CAP_NET_BIND_SERVICE"; |
||||
# ProtectClock= adds DeviceAllow=char-rtc r |
||||
DeviceAllow = ""; |
||||
# Implies ProtectSystem=strict, which re-mounts all paths |
||||
#DynamicUser = true; |
||||
LockPersonality = true; |
||||
MemoryDenyWriteExecute = true; |
||||
NoNewPrivileges = true; |
||||
PrivateDevices = true; |
||||
PrivateMounts = true; |
||||
# Needs to start a server |
||||
#PrivateNetwork = true; |
||||
PrivateTmp = true; |
||||
PrivateUsers = true; |
||||
ProcSubset = "pid"; |
||||
ProtectClock = true; |
||||
ProtectHome = true; |
||||
ProtectHostname = true; |
||||
# Would re-mount paths ignored by temporary root |
||||
#ProtectSystem = "strict"; |
||||
ProtectControlGroups = true; |
||||
ProtectKernelLogs = true; |
||||
ProtectKernelModules = true; |
||||
ProtectKernelTunables = true; |
||||
ProtectProc = "invisible"; |
||||
RestrictAddressFamilies = [ "AF_INET" "AF_INET6" "AF_UNIX" ]; |
||||
RestrictNamespaces = true; |
||||
RestrictRealtime = true; |
||||
RestrictSUIDSGID = true; |
||||
SystemCallArchitectures = "native"; |
||||
# gunicorn needs setuid |
||||
SystemCallFilter = [ |
||||
"@system-service" |
||||
"~@privileged @resources @keyring" |
||||
# These got removed by the line above but are needed |
||||
"@setuid @chown" |
||||
]; |
||||
TemporaryFileSystem = "/:ro"; |
||||
# Does not work well with the temporary root |
||||
#UMask = "0066"; |
||||
}; |
||||
}; |
||||
|
||||
users.groups.powerdnsadmin = { }; |
||||
users.users.powerdnsadmin = { |
||||
description = "PowerDNS web interface user"; |
||||
isSystemUser = true; |
||||
group = "powerdnsadmin"; |
||||
}; |
||||
}; |
||||
} |
@ -0,0 +1,117 @@ |
||||
# Test powerdns-admin |
||||
{ system ? builtins.currentSystem |
||||
, config ? { } |
||||
, pkgs ? import ../.. { inherit system config; } |
||||
}: |
||||
|
||||
with import ../lib/testing-python.nix { inherit system pkgs; }; |
||||
with pkgs.lib; |
||||
let |
||||
defaultConfig = '' |
||||
BIND_ADDRESS = '127.0.0.1' |
||||
PORT = 8000 |
||||
''; |
||||
|
||||
makeAppTest = name: configs: makeTest { |
||||
name = "powerdns-admin-${name}"; |
||||
meta = with pkgs.lib.maintainers; { |
||||
maintainers = [ Flakebi zhaofengli ]; |
||||
}; |
||||
|
||||
nodes.server = { pkgs, config, ... }: mkMerge ([ |
||||
{ |
||||
services.powerdns-admin = { |
||||
enable = true; |
||||
secretKeyFile = "/etc/powerdns-admin/secret"; |
||||
saltFile = "/etc/powerdns-admin/salt"; |
||||
}; |
||||
# It's insecure to have secrets in the world-readable nix store, but this is just a test |
||||
environment.etc."powerdns-admin/secret".text = "secret key"; |
||||
environment.etc."powerdns-admin/salt".text = "salt"; |
||||
environment.systemPackages = [ |
||||
(pkgs.writeShellScriptBin "run-test" config.system.build.testScript) |
||||
]; |
||||
} |
||||
] ++ configs); |
||||
|
||||
testScript = '' |
||||
server.wait_for_unit("powerdns-admin.service") |
||||
server.wait_until_succeeds("run-test", timeout=10) |
||||
''; |
||||
}; |
||||
|
||||
matrix = { |
||||
backend = { |
||||
mysql = { |
||||
services.powerdns-admin = { |
||||
config = '' |
||||
${defaultConfig} |
||||
SQLALCHEMY_DATABASE_URI = 'mysql://powerdnsadmin@/powerdnsadmin?unix_socket=/run/mysqld/mysqld.sock' |
||||
''; |
||||
}; |
||||
systemd.services.powerdns-admin = { |
||||
after = [ "mysql.service" ]; |
||||
serviceConfig.BindPaths = "/run/mysqld"; |
||||
}; |
||||
|
||||
services.mysql = { |
||||
enable = true; |
||||
package = pkgs.mariadb; |
||||
ensureDatabases = [ "powerdnsadmin" ]; |
||||
ensureUsers = [ |
||||
{ |
||||
name = "powerdnsadmin"; |
||||
ensurePermissions = { |
||||
"powerdnsadmin.*" = "ALL PRIVILEGES"; |
||||
}; |
||||
} |
||||
]; |
||||
}; |
||||
}; |
||||
postgresql = { |
||||
services.powerdns-admin = { |
||||
config = '' |
||||
${defaultConfig} |
||||
SQLALCHEMY_DATABASE_URI = 'postgresql://powerdnsadmin@/powerdnsadmin?host=/run/postgresql' |
||||
''; |
||||
}; |
||||
systemd.services.powerdns-admin = { |
||||
after = [ "postgresql.service" ]; |
||||
serviceConfig.BindPaths = "/run/postgresql"; |
||||
}; |
||||
|
||||
services.postgresql = { |
||||
enable = true; |
||||
ensureDatabases = [ "powerdnsadmin" ]; |
||||
ensureUsers = [ |
||||
{ |
||||
name = "powerdnsadmin"; |
||||
ensurePermissions = { |
||||
"DATABASE powerdnsadmin" = "ALL PRIVILEGES"; |
||||
}; |
||||
} |
||||
]; |
||||
}; |
||||
}; |
||||
}; |
||||
listen = { |
||||
tcp = { |
||||
services.powerdns-admin.extraArgs = [ "-b" "127.0.0.1:8000" ]; |
||||
system.build.testScript = '' |
||||
curl -sSf http://127.0.0.1:8000/ |
||||
''; |
||||
}; |
||||
unix = { |
||||
services.powerdns-admin.extraArgs = [ "-b" "unix:/run/powerdns-admin/http.sock" ]; |
||||
system.build.testScript = '' |
||||
curl -sSf --unix-socket /run/powerdns-admin/http.sock http://somehost/ |
||||
''; |
||||
}; |
||||
}; |
||||
}; |
||||
in |
||||
with matrix; { |
||||
postgresql = makeAppTest "postgresql" [ backend.postgresql listen.tcp ]; |
||||
mysql = makeAppTest "mysql" [ backend.mysql listen.tcp ]; |
||||
unix-listener = makeAppTest "unix-listener" [ backend.postgresql listen.unix ]; |
||||
} |
Loading…
Reference in new issue