From 42a5831e6235f815546411c48106381f2b2d1a8a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Kr=C3=BCger?= Date: Wed, 23 Feb 2022 14:37:29 +0100 Subject: [PATCH] nixos/pgadmin: init --- nixos/modules/module-list.nix | 1 + nixos/modules/services/admin/pgadmin.nix | 127 +++++++++++++++++++++++ 2 files changed, 128 insertions(+) create mode 100644 nixos/modules/services/admin/pgadmin.nix diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix index 2d9942a85b36..29fcc920f421 100644 --- a/nixos/modules/module-list.nix +++ b/nixos/modules/module-list.nix @@ -254,6 +254,7 @@ ./security/tpm2.nix ./services/admin/meshcentral.nix ./services/admin/oxidized.nix + ./services/admin/pgadmin.nix ./services/admin/salt/master.nix ./services/admin/salt/minion.nix ./services/amqp/activemq/default.nix diff --git a/nixos/modules/services/admin/pgadmin.nix b/nixos/modules/services/admin/pgadmin.nix new file mode 100644 index 000000000000..80b681454104 --- /dev/null +++ b/nixos/modules/services/admin/pgadmin.nix @@ -0,0 +1,127 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + pkg = pkgs.pgadmin4; + cfg = config.services.pgadmin; + + _base = with types; [ int bool str ]; + base = with types; oneOf ([ (listOf (oneOf _base)) (attrsOf (oneOf _base)) ] ++ _base); + + formatAttrset = attr: + "{${concatStringsSep "\n" (mapAttrsToList (key: value: "${builtins.toJSON key}: ${formatPyValue value},") attr)}}"; + + formatPyValue = value: + if builtins.isString value then builtins.toJSON value + else if value ? _expr then value._expr + else if builtins.isInt value then toString value + else if builtins.isBool value then (if value then "True" else "False") + else if builtins.isAttrs value then (formatAttrset value) + else if builtins.isList value then "[${concatStringsSep "\n" (map (v: "${formatPyValue v},") value)}]" + else throw "Unrecognized type"; + + formatPy = attrs: + concatStringsSep "\n" (mapAttrsToList (key: value: "${key} = ${formatPyValue value}") attrs); + + pyType = with types; attrsOf (oneOf [ (attrsOf base) (listOf base) base ]); +in +{ + options.services.pgadmin = { + enable = mkEnableOption "PostgreSQL Admin 4"; + + port = mkOption { + description = "Port for pgadmin4 to run on"; + type = types.port; + default = 5050; + }; + + initialEmail = mkOption { + description = "Initial email for the pgAdmin account."; + type = types.str; + }; + + initialPasswordFile = mkOption { + description = '' + Initial password file for the pgAdmin account. + NOTE: Should be string not a store path, to prevent the password from being world readable. + ''; + type = types.path; + }; + + openFirewall = mkEnableOption "firewall passthrough for pgadmin4"; + + settings = mkOption { + description = '' + Settings for pgadmin4. + Documentation. + ''; + type = pyType; + default= {}; + }; + }; + + config = mkIf (cfg.enable) { + networking.firewall.allowedTCPPorts = mkIf (cfg.openFirewall) [ cfg.port ]; + + services.pgadmin.settings = { + DEFAULT_SERVER_PORT = cfg.port; + SERVER_MODE = true; + } // (optionalAttrs cfg.openFirewall { + DEFAULT_SERVER = mkDefault "::"; + }); + + systemd.services.pgadmin = { + wantedBy = [ "multi-user.target" ]; + after = [ "network.target" ]; + requires = [ "network.target" ]; + # we're adding this optionally so just in case there's any race it'll be caught + # in case postgres doesn't start, pgadmin will just start normally + wants = [ "postgresql.service" ]; + + path = [ config.services.postgresql.package pkgs.coreutils pkgs.bash ]; + + preStart = '' + # NOTE: this is idempotent (aka running it twice has no effect) + ( + # Email address: + echo ${escapeShellArg cfg.initialEmail} + + # file might not contain newline. echo hack fixes that. + PW=$(cat ${escapeShellArg cfg.initialPasswordFile}) + + # Password: + echo "$PW" + # Retype password: + echo "$PW" + ) | ${pkg}/bin/pgadmin4-setup + ''; + + restartTriggers = [ + "/etc/pgadmin/config_system.py" + ]; + + serviceConfig = { + User = "pgadmin"; + DynamicUser = true; + LogsDirectory = "pgadmin"; + StateDirectory = "pgadmin"; + ExecStart = "${pkg}/bin/pgadmin4"; + }; + }; + + users.users.pgadmin = { + isSystemUser = true; + group = "pgadmin"; + }; + + users.groups.pgadmin = {}; + + environment.etc."pgadmin/config_system.py" = { + text = formatPy cfg.settings; + mode = "0600"; + user = "pgadmin"; + group = "pgadmin"; + }; + }; +}