powerdns-admin: fix and add module

- 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
Flakebi 2 years ago committed by Sebastian Neubauer
parent 040d6eb51a
commit 368b22d09b
  1. 7
      nixos/doc/manual/from_md/release-notes/rl-2205.section.xml
  2. 2
      nixos/doc/manual/release-notes/rl-2205.section.md
  3. 1
      nixos/modules/module-list.nix
  4. 149
      nixos/modules/services/web-apps/powerdns-admin.nix
  5. 1
      nixos/tests/all-tests.nix
  6. 117
      nixos/tests/powerdns-admin.nix
  7. 15
      pkgs/applications/networking/powerdns-admin/default.nix

@ -36,6 +36,13 @@
<link linkend="opt-services.filebeat.enable">services.filebeat</link>.
</para>
</listitem>
<listitem>
<para>
<link xlink:href="https://github.com/ngoduykhanh/PowerDNS-Admin">PowerDNS-Admin</link>,
a web interface for the PowerDNS server. Available at
<link xlink:href="options.html#opt-services.powerdns-admin.enable">services.powerdns-admin</link>.
</para>
</listitem>
</itemizedlist>
</section>
<section xml:id="sec-release-22.05-incompatibilities">

@ -12,6 +12,8 @@ In addition to numerous new and upgraded packages, this release has the followin
- [filebeat](https://www.elastic.co/guide/en/beats/filebeat/current/filebeat-overview.html), a lightweight shipper for forwarding and centralizing log data. Available as [services.filebeat](#opt-services.filebeat.enable).
- [PowerDNS-Admin](https://github.com/ngoduykhanh/PowerDNS-Admin), a web interface for the PowerDNS server. Available at [services.powerdns-admin](options.html#opt-services.powerdns-admin.enable).
## Backward Incompatibilities {#sec-release-22.05-incompatibilities}
- `pkgs.ghc` now refers to `pkgs.targetPackages.haskellPackages.ghc`.

@ -1026,6 +1026,7 @@
./services/web-apps/plantuml-server.nix
./services/web-apps/plausible.nix
./services/web-apps/pgpkeyserver-lite.nix
./services/web-apps/powerdns-admin.nix
./services/web-apps/matomo.nix
./services/web-apps/moinmoin.nix
./services/web-apps/openwebrx.nix

@ -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";
};
};
}

@ -381,6 +381,7 @@ in
postgresql = handleTest ./postgresql.nix {};
postgresql-wal-receiver = handleTest ./postgresql-wal-receiver.nix {};
powerdns = handleTest ./powerdns.nix {};
powerdns-admin = handleTest ./powerdns-admin.nix {};
power-profiles-daemon = handleTest ./power-profiles-daemon.nix {};
pppd = handleTest ./pppd.nix {};
predictable-interface-names = handleTest ./predictable-interface-names.nix {};

@ -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 ];
}

@ -1,4 +1,4 @@
{ lib, stdenv, fetchFromGitHub, mkYarnPackage, writeText, python3 }:
{ lib, stdenv, fetchFromGitHub, mkYarnPackage, nixosTests, writeText, python3 }:
let
version = "0.2.3";
@ -23,8 +23,8 @@ let
pythonDeps = with python.pkgs; [
flask flask_assets flask_login flask_sqlalchemy flask_migrate flask-seasurf flask_mail flask-sslify
mysqlclient sqlalchemy
configobj bcrypt requests ldap pyotp qrcode dnspython
mysqlclient psycopg2 sqlalchemy
cffi configobj cryptography bcrypt requests ldap pyotp qrcode dnspython
gunicorn python3-saml pyopenssl pytz cssmin jsmin authlib bravado-core
lima pytimeparse pyyaml
];
@ -91,6 +91,7 @@ in stdenv.mkDerivation rec {
postPatch = ''
rm -r powerdnsadmin/static powerdnsadmin/assets.py
sed -i "s/id:/'id':/" migrations/versions/787bdba9e147_init_db.py
'';
installPhase = ''
@ -100,7 +101,7 @@ in stdenv.mkDerivation rec {
wrapPythonPrograms
mkdir -p $out/share $out/bin
cp -r powerdnsadmin $out/share/powerdnsadmin
cp -r migrations powerdnsadmin $out/share/
ln -s ${assets} $out/share/powerdnsadmin/static
ln -s ${assetsPy} $out/share/powerdnsadmin/assets.py
@ -114,6 +115,12 @@ in stdenv.mkDerivation rec {
runHook postInstall
'';
passthru = {
# PYTHONPATH of all dependencies used by the package
pythonPath = python3.pkgs.makePythonPath pythonDeps;
tests = nixosTests.powerdns-admin;
};
meta = with lib; {
description = "A PowerDNS web interface with advanced features";
homepage = "https://github.com/ngoduykhanh/PowerDNS-Admin";

Loading…
Cancel
Save