What is the best way to use SOPS secrets in a systemd service?

Hey!

Podman has excellent way to inject SOPS secrets with the environmentFiles directive:

virtualisation.oci-containers.containers.proxy = {
    image = "my-proxy-image";

    environmentFiles = [
      config.sops.secrets.proxy_env.path
    ];

    environment = {
      HTTPPROXY = "on";
      HTTPPROXY_LOG = "on";
    };

    ports = [
      "127.0.0.1:8888:8888/tcp"
    ];
  };

Is there anything similiar available in systemd.services.<name>? What is the de facto way to insert secrets in them?

You could simply use EnvironmentFile like you would with podman?
See systemd.exec

Thanks!

I tried to look for that and it seems it doesn’t exist as a key in systemd.service.<name>.environmentFile: NixOS Search

I tried this

systemd.services.phoenix-web = {
    description = "Phoenix web app";
    wantedBy = ["multi-user.target"];
    after = ["postgresql.service"];
    script = ''
      ${self.packages.${pkgs.system}.default}/bin/migrate
      ${self.packages.${pkgs.system}.default}/bin/server
    '';
    serviceConfig = {
      User = user;
      WorkingDirectory = "${dataDir}";
      Group = user;
    };

    environmentFile = [
      cfg.secrets_env_path
    ];

    environment = {
      RELEASE_COOKIE = self.rev or self.dirtyRev or "this-is-not-a-distributed-node";

      # Disable Erlang's distributed features
      RELEASE_DISTRIBUTION = "none";
      # Additional safeguard, in case `RELEASE_DISTRIBUTION=none` ever
      # stops disabling the start of EPMD.
      ERL_EPMD_ADDRESS = "127.0.0.1";
    };
  };
};

And it throws me an error:

error: The option `systemd.services.phoenix-web.environmentFile' does not exist. Definition values:
       - In `/nix/store/1bghv5frv1gxn4i49vr54v51j80245yv-source/flake.nix':
           [
             "/run/secrets/production_secrets"
           ]

The EnvironmentFile can be put in the attribute set of systemd.services.<name>.serviceConfig (NixOS Search).

Some module have wrappers for this, if you grep the nixpkgs source code for environmentFile you can spot them :man_detective:

1 Like

Thanks for the fast answers! Using this indeed solved my problem:

serviceConfig = {
  EnvironmentFile = cfg.secrets_env_path;
};
2 Likes

That works, but it’s hardly the “best” way. The systemd manual actively discourages using env variables:

Note that environment variables are not suitable for passing secrets (such as passwords, key material, …) to service processes. Environment variables set for a unit are exposed to unprivileged clients via D-Bus IPC, and generally not understood as being data that requires protection. Moreover, environment variables are propagated down the process tree, including across security boundaries (such as setuid/setgid executables), and hence might leak to processes that should not have access to the secret data. Use LoadCredential=, LoadCredentialEncrypted= or SetCredentialEncrypted= (see below) to pass data to unit processes securely.

I believe EnvironmentFile= is not exposed via D-Bus, but the rest still applies.

Use systemd credentials instead.

5 Likes

Not using envvars requires that applications support to not use them.

Especially in the field of “cloud native” they often are the only option to provide any kind of configuration or secrets.

3 Likes