From 2f93570ac45bc9cdf358d0ffeb3bb672ee68a614 Mon Sep 17 00:00:00 2001 From: Jeltz Date: Thu, 27 Feb 2025 21:49:48 +0100 Subject: [PATCH] indico: use files for passwords Signed-off-by: Jeltz --- modules/indico.nix | 26 ++++++------- pkgs/python-vars-with-env/codegen.py | 54 +++++++++++++++++++++++++++ pkgs/python-vars-with-env/default.nix | 53 ++++++++++++++++++++++++++ 3 files changed, 120 insertions(+), 13 deletions(-) create mode 100755 pkgs/python-vars-with-env/codegen.py create mode 100644 pkgs/python-vars-with-env/default.nix diff --git a/modules/indico.nix b/modules/indico.nix index f4939ef..4069991 100644 --- a/modules/indico.nix +++ b/modules/indico.nix @@ -8,8 +8,8 @@ let cfg = config.services.indico; - pythonFmt = pkgs.formats.pythonVars { }; yamlFmt = pkgs.formats.yaml {}; + pythonFmt = pkgs.callPackage ../pkgs/python-vars-with-env { }; indico = pkgs.callPackage ../pkgs/indico { }; pythonEnv = pkgs.python3.withPackages (ps: [ (ps.toPythonModule indico) @@ -41,7 +41,7 @@ let ldapConfig = { uri = cfg.ldap.uri; bind_dn = cfg.ldap.bindDN; - bind_password = cfg.ldap.bindPassword; + bind_password = pythonFmt.mkReadFile cfg.ldap.bindPasswordFile; timeout = 30; verify_cert = true; page_size = 1500; @@ -72,7 +72,7 @@ let CELERY_BROKER = cfg.celeryBrokerRedis; USE_PROXY = true; BASE_URL = cfg.baseUrl; - SECRET_KEY = cfg.secretKey; + SECRET_KEY = pythonFmt.mkReadFile cfg.secretKeyFile; LOGGING_CONFIG_FILE = loggingFile; NO_REPLY_EMAIL = cfg.email.noReply; SUPPORT_EMAIL = cfg.email.support; @@ -80,7 +80,7 @@ let PUBLIC_SUPPORT_EMAIL = cfg.email.publicSupport; SMTP_SERVER = [ cfg.email.smtp.host cfg.email.smtp.port ]; SMTP_LOGIN = cfg.email.smtp.login; - SMTP_PASSWORD = cfg.email.smtp.password; + SMTP_PASSWORD = pythonFmt.mkReadFile cfg.email.smtp.passwordFile; SMTP_USE_TLS = cfg.email.smtp.useTLS; } // lib.optionalAttrs (cfg.ldap != null) { AUTH_PROVIDERS = { @@ -169,16 +169,16 @@ in { description = "SMTP login."; }; - password = lib.mkOption { - type = lib.types.str; - description = "SMTP password."; + passwordFile = lib.mkOption { + type = lib.types.path; + description = "SMTP password file."; }; }; }; - secretKey = lib.mkOption { - type = lib.types.str; - description = "Secret key."; + secretKeyFile = lib.mkOption { + type = lib.types.path; + description = "Secret key file."; }; baseUrl = lib.mkOption { @@ -285,9 +285,9 @@ in { description = "LDAP server bind DN."; }; - bindPassword = lib.mkOption { - type = lib.types.str; - description = "LDAP server bind password."; + bindPasswordFile = lib.mkOption { + type = lib.types.path; + description = "LDAP server bind password file."; }; uid = lib.mkOption { diff --git a/pkgs/python-vars-with-env/codegen.py b/pkgs/python-vars-with-env/codegen.py new file mode 100755 index 0000000..d957128 --- /dev/null +++ b/pkgs/python-vars-with-env/codegen.py @@ -0,0 +1,54 @@ +from argparse import ArgumentParser +from pathlib import Path +from json import load + + +# Indico raises a warning for every non prefixed local variable, so we add a +# prefix to each import and declared function +PROLOGUE = ( + "import functools as _functools\n" + "@_functools.cache\n" + "def _read_file(name):\n" + " with open(name) as f:\n" + " return name\n" +) + + +def make_value(value): + match value["_pyType"]: + case "str" | "int" | "bool" | "null": + return repr(value["_value"]) + case "list": + items = [make_value(i) for i in value["_value"]] + return f"[{','.join(items)}]" + case "dict": + items = [ + f"{repr(k)}: {make_value(v)}" + for k, v in value["_value"].items() + ] + return f"{{{','.join(items)}}}" + case "read-file": + return f"_read_file({repr(value['_value'])})" + case _: + raise ValueError("Unknown data type") + + +def mk_vars(vars): + if not all(str.isidentifier(n) for n in vars.keys()): + raise ValueError("At least one variable identifier is invalid") + eqs = [f"{n} = {make_value(v)}" for n, v in vars.items()] + return PROLOGUE + "\n".join(eqs) + + +def main(): + parser = ArgumentParser() + parser.add_argument("json", type=Path) + + args = parser.parse_args() + + with args.json.open() as fd: + print(mk_vars(load(fd))) + + +if __name__ == "__main__": + main() diff --git a/pkgs/python-vars-with-env/default.nix b/pkgs/python-vars-with-env/default.nix new file mode 100644 index 0000000..3bfd25d --- /dev/null +++ b/pkgs/python-vars-with-env/default.nix @@ -0,0 +1,53 @@ +{ + lib, + pkgs, + ... +}: + +rec { + _mkPy = type: value: { + _pyType = type; + _value = value; + }; + + _isPy = value: lib.isAttrs value && lib.hasAttr "_pyType" value; + + _mkValue = value: + if _isPy value then + value + else if lib.isStringLike value then + _mkPy "str" value + else if lib.isInt value then + _mkPy "int" value + else if lib.isBool value then + _mkPy "bool" value + else if value == null then + _mkPy "null" null + else if lib.isList value then + _mkPy "list" (lib.map _mkValue value) + else if lib.isAttrs value then + _mkPy "dict" (lib.mapAttrs (_: _mkValue) value) + else + abort "unsupported type"; + + _mkVars = lib.mapAttrs (_: _mkValue); + + mkReadFile = path: + _mkPy "read-file" (if lib.isStringLike path then + path + else + abort "invalid path type"); + + generate = name: value: + pkgs.callPackage ({ runCommand, python3, black }: + runCommand name { + nativeBuildInputs = [ python3 black ]; + value = builtins.toJSON (_mkVars value); + codegen = builtins.readFile ./codegen.py; + passAsFile = [ "codegen" "value" ]; + preferLocalBuild = true; + } '' + python3 $codegenPath $valuePath > $out + black $out + '') { }; +}