How to set an agenix secret as an environment variable in systemd service?

I need to set an environment variable to an agenix secret in a systemd service definition.

  systemd.services.backend = {
    enable = true;
    description = "backend";
    environment = {
      LOAD_BALANCER_IP = "my-ip";
      DB_NAME = "mydb";
      PGPASSWORD = "${pkgs.coreutils}/bin/cat ${ config.age.secrets.secret.path}";
    };
    serviceConfig = {
      Type = "simple";
      ExecStart = "${backend.packages.x86_64-linux.default}/bin/backend_server";
    };
    wantedBy = [ "multi-user.target" ];
  };

That doesn’t work, since it doesn’t apply the cat command, but just points to the binary in the nix store for cat. How can I get the content of the file that the agenix secret points to in this context? And would doing this be secure?

Systemd provides dedicated credentials machinery for this case.

If you absolutely want to use the standard environment approach, you could pass config.age.secrets.secret.path as EnvironmentFile, provided the secret material is formatted as an environment file (FOO=bar)

1 Like

It exists, but it doesn’t play very nicely with sops-nix/agenix yet.

The environmentfile is probably among the better options, but best would be seeing if the binary can be made to read from the file directly upstream. If you control the source (as it looks like…) that should be pretty easy, can even throw in clever memory management that discards the password when it’s no longer used.

Thanks to both of you.

I did end up reading from the file directly.

I still have a question about environmentFile, since I need this to include the password for the prometheus prostgres exporter.

My secret is of the form DATA_SOURCE_NAME=postgresql://username:password@localhost:5432/postgres?sslmode=disable, and

  services.prometheus.exporters.postgres = {
    enable = true;
    environmentFile = "${config.age.secrets.secret2.path}";
    user = "postgres-exporter";
    listenAddress = "0.0.0.0";
    port = 9187;
    extraFlags = [ ];
    openFirewall = true;
  };

But DATA_SOURCE_NAME is still set to the default value:

[root@nixos:~]# systemctl cat prometheus-postgres-exporter
# /etc/systemd/system/prometheus-postgres-exporter.service
[Unit]
After=network.target

[Service]
Environment="DATA_SOURCE_NAME=user=postgres database=postgres host=/run/postgresql sslmode=disable"
Environment="LOCALE_ARCHIVE=/nix/store/wkb8skirx4zmrnsw6dcxz07p926kz32p-glibc-locales-2.39-52/lib/locale/locale-archive"
Environment="PATH=/nix/store/cnknp3yxfibxjhila0sjd1v3yglqssng-coreutils-9.5/bin:/nix/store/5my5b6mw7h9hxqknvggjla1ci165ly21-f>"
Environment="TZDIR=/nix/store/fn1y6zydm7mgxrm7b08h1w1c9qkrzk8r-tzdata-2024a/share/zoneinfo"
CapabilityBoundingSet=
DeviceAllow=
DynamicUser=false
EnvironmentFile=/run/agenix/secret2

How can I make environmentFile override the default value for DATA_SOURCE_NAME?

According to man systemd.exec whatever is inside your EnvironmentFile should override whatever is defined in Environment directives:

Settings from these files override settings made with Environment=.
If the same variable is set twice from these files, the files will
be read in the order they are specified and the later setting will
override the earlier setting.

So, this should not be an issue that the service still generates the default.

1 Like

Ok thanks. The exporter doesn’t work, so I thought this might be the reason.

You could temporarily override the ExecStart/script of the service to print the variable if you want to make sure.

Prometheus exporters can be a pain to debug, in either case.