at 23.11-pre 5.2 kB view raw
1{ config, lib, pkgs, ... }: 2 3with lib; 4 5let 6 cfg = config.services.powerdns-admin; 7 8 configText = '' 9 ${cfg.config} 10 '' 11 + optionalString (cfg.secretKeyFile != null) '' 12 with open('${cfg.secretKeyFile}') as file: 13 SECRET_KEY = file.read() 14 '' 15 + optionalString (cfg.saltFile != null) '' 16 with open('${cfg.saltFile}') as file: 17 SALT = file.read() 18 ''; 19in 20{ 21 options.services.powerdns-admin = { 22 enable = mkEnableOption (lib.mdDoc "the PowerDNS web interface"); 23 24 extraArgs = mkOption { 25 type = types.listOf types.str; 26 default = [ ]; 27 example = literalExpression '' 28 [ "-b" "127.0.0.1:8000" ] 29 ''; 30 description = lib.mdDoc '' 31 Extra arguments passed to powerdns-admin. 32 ''; 33 }; 34 35 config = mkOption { 36 type = types.str; 37 default = ""; 38 example = '' 39 BIND_ADDRESS = '127.0.0.1' 40 PORT = 8000 41 SQLALCHEMY_DATABASE_URI = 'postgresql://powerdnsadmin@/powerdnsadmin?host=/run/postgresql' 42 ''; 43 description = lib.mdDoc '' 44 Configuration python file. 45 See [the example configuration](https://github.com/ngoduykhanh/PowerDNS-Admin/blob/v${pkgs.powerdns-admin.version}/configs/development.py) 46 for options. 47 ''; 48 }; 49 50 secretKeyFile = mkOption { 51 type = types.nullOr types.path; 52 example = "/etc/powerdns-admin/secret"; 53 description = lib.mdDoc '' 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. 57 ''; 58 }; 59 60 saltFile = mkOption { 61 type = types.nullOr types.path; 62 example = "/etc/powerdns-admin/salt"; 63 description = lib.mdDoc '' 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. 67 ''; 68 }; 69 }; 70 71 config = mkIf cfg.enable { 72 systemd.services.powerdns-admin = { 73 description = "PowerDNS web interface"; 74 wantedBy = [ "multi-user.target" ]; 75 after = [ "networking.target" ]; 76 77 environment.FLASK_CONF = builtins.toFile "powerdns-admin-config.py" configText; 78 environment.PYTHONPATH = pkgs.powerdns-admin.pythonPath; 79 serviceConfig = { 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"; 89 90 AmbientCapabilities = "CAP_NET_BIND_SERVICE"; 91 BindReadOnlyPaths = [ 92 "/nix/store" 93 "-/etc/resolv.conf" 94 "-/etc/nsswitch.conf" 95 "-/etc/hosts" 96 "-/etc/localtime" 97 ] 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 102 DeviceAllow = ""; 103 # Implies ProtectSystem=strict, which re-mounts all paths 104 #DynamicUser = true; 105 LockPersonality = true; 106 MemoryDenyWriteExecute = true; 107 NoNewPrivileges = true; 108 PrivateDevices = true; 109 PrivateMounts = true; 110 # Needs to start a server 111 #PrivateNetwork = true; 112 PrivateTmp = true; 113 PrivateUsers = true; 114 ProcSubset = "pid"; 115 ProtectClock = true; 116 ProtectHome = 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 131 SystemCallFilter = [ 132 "@system-service" 133 "~@privileged @resources @keyring" 134 # These got removed by the line above but are needed 135 "@setuid @chown" 136 ]; 137 TemporaryFileSystem = "/:ro"; 138 # Does not work well with the temporary root 139 #UMask = "0066"; 140 }; 141 }; 142 143 users.groups.powerdnsadmin = { }; 144 users.users.powerdnsadmin = { 145 description = "PowerDNS web interface user"; 146 isSystemUser = true; 147 group = "powerdnsadmin"; 148 }; 149 }; 150 151 # uses attributes of the linked package 152 meta.buildDocsInSandbox = false; 153}