How to use an agenix secret in msmtp

Hi, I’ve painted myself into a corner and I can’t seem to get out. I’ve created an age file for use with msmtp, but I can’t figure out how to actually use it. I don’t use flakes so everything I’m trying to do is within configuration.nix. Here’s what I’ve hobbled together so far:

  imports =
    [ # Include the results of the hardware scan.
      ./hardware-configuration.nix
      <home-manager/nixos>
      <agenix/modules/age.nix>
    ];
  age = {
    secrets = {
      msmtp.file = "/home/michael/.secrets/msmtp.age";
    };
    identityPaths = [ "/home/michael/.ssh/id_ed25519" ];
  };
  programs.msmtp = {
    enable = true;
    accounts = {
      default = {
        auth = true;
        tls = true;
        tls_starttls = false;
...
        passwordeval = "$(cat ${config.age.secrets.msmtp.path})";
      };
    };
  };

Previously, I had my password stored as plaintext, and the passwordeval line in the msmtp section looked like this:

passwordeval = "cat /home/michael/.secrets/smtp.txt";

Sending email worked just fine using that plaintext method, but I can’t seem to get it to work using the age file. All I want to do is keep my password secret using agenix and then call it up from within msmtp. Can anyone tell me what I’m doing wrong?

EDIT: The error I’m getting is:

cat: /run/agenix/msmtp: Permission denied
sendmail: cannot read output of '$(cat /run/agenix/msmtp)'

Can passwordeval do command substitution with “$()”? I use sops and the two differences for me is the command substitution and I use an absolute path to cat:

passwordeval = "${pkgs.coreutils-full}/bin/cat ${config.sops.secrets."email/password".path}";

Actually, I missed the permission denied error. I think msmtp makes a user, and secrets are only readable by root by default. You likely need to do this:

age = {
    secrets = {
      msmtp = { 
        file = "/home/michael/.secrets/msmtp.age";
        owner = "root";
        group = config.users.groups.sendmail.name;
        mode = "0440";
      };
    };
    identityPaths = [ "/home/michael/.ssh/id_ed25519" ];
  };

Thanks for the response! I have to admit I don’t understand the differences between ${} and $({}), so I’m probably grossly misusing it.

When I updated my passwordeval line to resemble yours, my error messages changed to:

/nix/store/ab5hdxr437ng76bayc5r3crfwm3yds9r-coreutils-full-9.3/bin/cat: /run/agenix/msmtp: Permission denied
sendmail: cannot read output of '/nix/store/ab5hdxr437ng76bayc5r3crfwm3yds9r-coreutils-full-9.3/bin/cat /run/agenix/msmtp'

I also added the extra age.secrets.msmtp details you recommended, but nixos-rebuild keeps throwing an error about the group attribute:

      error: attribute 'sendmail' missing
           90|         group = config.users.users.sendmail.group;
             |                 ^

For grins I edited the group line as:

       group = "root";

I made it through the rebuild that time, but I still got the same permissions errors as before.

I just figured it out. According to the NixOS wiki entry on msmtp:

Note that msmtp has no daemon and runs as the invoking user. If using passwordeval, the file must be readable by any user that wishes to send mail.

So that gave me the idea to change the owner and group declarations to:

        owner = "michael";
        group = "users";

And voila, it works. Thanks @ttamttam1 for pointing me in the right direction!

1 Like

Glad you figured it out! I must have made a sendmail group on my own when I set it up and forgot about it.
And for reference $() is a command substitution that happens at the shell level (or runtime). It replaces the contents with the output of the command. ${} is a nix thing where the contents get replaced with the nix expression at evaluation time. $() means nothing special in the nix expression language itself, but you’ll see it a lot because in shell scripts that are configured with nix.

1 Like