1 { config, lib, pkgs, ... }:
6 cfg = config.services.powerdns-admin;
11 + optionalString (cfg.secretKeyFile != null) ''
12 with open('${cfg.secretKeyFile}') as file:
13 SECRET_KEY = file.read()
15 + optionalString (cfg.saltFile != null) ''
16 with open('${cfg.saltFile}') as file:
21 options.services.powerdns-admin = {
22 enable = mkEnableOption "the PowerDNS web interface";
24 extraArgs = mkOption {
25 type = types.listOf types.str;
27 example = literalExpression ''
28 [ "-b" "127.0.0.1:8000" ]
31 Extra arguments passed to powerdns-admin.
39 BIND_ADDRESS = '127.0.0.1'
41 SQLALCHEMY_DATABASE_URI = 'postgresql://powerdnsadmin@/powerdnsadmin?host=/run/postgresql'
44 Configuration python file.
45 See [the example configuration](https://github.com/ngoduykhanh/PowerDNS-Admin/blob/v${pkgs.powerdns-admin.version}/configs/development.py)
50 secretKeyFile = mkOption {
51 type = types.nullOr types.path;
52 example = "/etc/powerdns-admin/secret";
54 The secret used to create cookies.
55 This needs to be set, otherwise the default is used and everyone can forge valid login cookies.
56 Set this to null to ignore this setting and configure it through another way.
61 type = types.nullOr types.path;
62 example = "/etc/powerdns-admin/salt";
64 The salt used for serialization.
65 This should be set, otherwise the default is used.
66 Set this to null to ignore this setting and configure it through another way.
71 config = mkIf cfg.enable {
72 systemd.services.powerdns-admin = {
73 description = "PowerDNS web interface";
74 wantedBy = [ "multi-user.target" ];
75 after = [ "networking.target" ];
77 environment.FLASK_CONF = builtins.toFile "powerdns-admin-config.py" configText;
78 environment.PYTHONPATH = pkgs.powerdns-admin.pythonPath;
80 ExecStart = "${pkgs.powerdns-admin}/bin/powerdns-admin --pid /run/powerdns-admin/pid ${escapeShellArgs cfg.extraArgs}";
81 # Set environment variables only for starting flask database upgrade
82 ExecStartPre = "${pkgs.coreutils}/bin/env FLASK_APP=${pkgs.powerdns-admin}/share/powerdnsadmin/__init__.py SESSION_TYPE= ${pkgs.python3Packages.flask}/bin/flask db upgrade -d ${pkgs.powerdns-admin}/share/migrations";
83 ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
84 ExecStop = "${pkgs.coreutils}/bin/kill -TERM $MAINPID";
85 PIDFile = "/run/powerdns-admin/pid";
86 RuntimeDirectory = "powerdns-admin";
87 User = "powerdnsadmin";
88 Group = "powerdnsadmin";
90 AmbientCapabilities = "CAP_NET_BIND_SERVICE";
98 ++ (optional (cfg.secretKeyFile != null) cfg.secretKeyFile)
99 ++ (optional (cfg.saltFile != null) cfg.saltFile);
100 CapabilityBoundingSet = "CAP_NET_BIND_SERVICE";
101 # ProtectClock= adds DeviceAllow=char-rtc r
103 # Implies ProtectSystem=strict, which re-mounts all paths
105 LockPersonality = true;
106 MemoryDenyWriteExecute = true;
107 NoNewPrivileges = true;
108 PrivateDevices = true;
109 PrivateMounts = true;
110 # Needs to start a server
111 #PrivateNetwork = true;
117 ProtectHostname = true;
118 # Would re-mount paths ignored by temporary root
119 #ProtectSystem = "strict";
120 ProtectControlGroups = true;
121 ProtectKernelLogs = true;
122 ProtectKernelModules = true;
123 ProtectKernelTunables = true;
124 ProtectProc = "invisible";
125 RestrictAddressFamilies = [ "AF_INET" "AF_INET6" "AF_UNIX" ];
126 RestrictNamespaces = true;
127 RestrictRealtime = true;
128 RestrictSUIDSGID = true;
129 SystemCallArchitectures = "native";
130 # gunicorn needs setuid
133 "~@privileged @resources @keyring"
134 # These got removed by the line above but are needed
137 TemporaryFileSystem = "/:ro";
138 # Does not work well with the temporary root
143 users.groups.powerdnsadmin = { };
144 users.users.powerdnsadmin = {
145 description = "PowerDNS web interface user";
147 group = "powerdnsadmin";
151 # uses attributes of the linked package
152 meta.buildDocsInSandbox = false;