Sops failing to start

you are right, i added one extra on the gmail_main.nix file
this is the file tree filtered for the files:

❯ tree
.
├── flake.lock
├── flake.nix
├── modules
│   ├── home
│   │   ├── default.nix
│   │   ├── email
│   │   │   ├── default.nix
│   │   │   └── gmail_main.nix
├── secrets
│   ├── email.yaml
│   └── README.mb

there is a new error:

error: A definition for option `accounts.email.accounts.gmail_main.address' is not of type `string matching the pattern .*@.*'. Definition values:
       - In `/nix/store/4ab82kbq8jmklcgaj7vq4kj896b53y98-source/modules/home/email/gmail_main.nix': "/home/felipepinto/.config/sops-nix/secrets/email/gmail_main/address"

i double checked the mail.yaml file and the address seems alright

The contents of the secret isn’t relevant.

The option only sees a string that represents a path in the filesystem.

The option strictly requires a string that is a valid email address (by a loose definition).

And it will use that email address it sees in the string for the operations.

TLDRv this option doesn’t support reading from a file

2 Likes

Maybe a more detailed description on how sops works helps to understand: sops decrypts your password during activation (e.g. during boot, as a hm activation script or when you run stand alone hm). For each secret you use sops creates a file containing that password. That file is only readable by the specific user (can be changed via options). This file can then be read by services or programs hence you use the sops.*.path attribute to tell the program where to find that password file. You cannot however use information directly (like you did) during eval time because the result would end up in the store defeating the purpose of a secret. If a program/service cannot read passwords from a file you can use templates for example to generate env files or you might find a way to cat the file content ‘into’ the program.

I hope this helps.

Ok, with this much i understand im doing something wrong.
im not sure how to apply the template in this case tho. how would you suggest?
the email.yaml will be the only file to store information refered here
should belong to the user felipepinto
and the structure of the file was setup this way so far:

email:
  realName: your name
  domain:
    email_tag:
      address: address@domain.com
      pass: password123
  gmail:
    main:
      address: address@gmail.com
      pass: otherpass123

do you suggest any changes to this file?


from what i understand the templates is a way for sops to write files with secrets, correct me if im wrong but if i want to create an acounts.email.accounts in a nix file to which i want to insert the secrets to be configured in the system. seems like the correct approach was to use the sops.secrets.*.path no?

The content of the .yaml file can be chosen arbitrarily. You just reference “path” in your nix configuration.

What you then want to look out for is a way to get the password from the file (see my previous answer) into your program. This can be done via a command or the program can read the password from the file directly. For e-mail-accounts you could look at accounts.email.accounts.<name>.passwordCommand.

You could try something passwordCommand = "cat ${config.sops.secrets."email/gmail/main/pass"}.path" in your email configuration (didn’t try, so might need to be adapted it a bit more but I hope you get the idea).

i was already using that password command, to facilitate lemme grab the latest (still not working) version of that file:

{ lib, config, pkgs, ... }:
let
  cfg_ = config.modules.email;
  cfg = cfg_.accounts.gmail.main;
  sopsSecret = { inherit (cfg_.secrets) sopsFile owner; };
in
{
  options.modules.email.accounts.gmail.main.enable = 
    lib.mkEnableOption "config main gmail account";

  config = lib.mkIf cfg.enable {

    sops.secrets = {
      "email/gmail/main/address"   = sopsSecret;
      "email/gmail/main/pass"      = sopsSecret;
      "email/gmail/main/signature" = sopsSecret;
    };

    accounts.email.accounts.gmail_main = let
      realName = config.sops.secrets."email/realName".path;
      address   = config.sops.secrets."email/gmail/main/address".path;
      pass      = config.sops.secrets."email/gmail/main/pass".path;
      signature = {
        text = config.sops.secrets."email/gmail/main/signature".path;
        showSignature = "append";
      };
    in {
      inherit address realName signature;
      userName = address;
      passwordCommand = "${pkgs.coreutils}/bin/cat ${pass}";
      # gpg
      gpg.key = config.programs.git.signing.key;
      gpg.signByDefault = true;
      # settings
      primary = true;
      msmtp.enable = true;
      notmuch.enable = true;
      mbsync.enable = true;
      mbsync.create = "maildir";
      imap.host = "gmail.com";
      smtp.host = "gmail.com";
    };
  };
}

its imported by this module:

{ lib, config, ... }:
let
  cfg = config.modules.email;
in
{
  imports = [
    ./gmail_main.nix
  ];

  options.modules.email = {
    enable = lib.mkEnableOption "enable email home module";
    secrets = {
      sopsFile = lib.mkOption {
        type = lib.types.path;
        default = ../../secrets/email.yaml;
        description = ''
          path to sops file with email info
          schema should follow: 
          ${../../secrets/email_schema.txt}
        '';
      };
      owner = lib.mkOption {
        type = lib.types.str;
        default = config.home.username;
        description = ''owner of the secrets'';
      };
    };
  };

  config = lib.mkIf cfg.enable {
    modules.email.accounts = {
      gmail.main.enable = lib.mkDefault true;
    };

    programs = {
      mbsync.enable = true;
      msmtp.enable = true;
      notmuch = {
        enable = true;
        hooks = {
          preNew = "mbsync --all";
        };
      };
      neomutt = {
        enable = true;
        vimKeys = true;
        sidebar = {
          enable = true;
        };
        sort = "threads";
      };
    };
  };
}

What does ‘still not working’ mean? Any helpful error messages?

And you are also still trying to access sops data directly from your config which will not work (see several posts that mention this above)

A more personal note: I would rather start of without using any fancy options and get a most basic version running. After that I would start refactoring to use options and such. This would help analyzing your code.

I figured out i could use builtins.readFile to get the file contents so the last iteration of the file looks like

{ lib, config, pkgs, ... }:
let
  cfgEmail = config.modules.email;
  cfg = cfgEmail.accounts.gmail_main;
in
{
  options.modules.email.accounts.gmail_main.enable = 
    lib.mkEnableOption "config main gmail account";

  config = lib.mkIf cfg.enable {

    # cfgEmail.secrets = { sopsFile = ./path/to/secrets/yaml; };

    sops.secrets = let
      # sopsSecrets = secret: cfgEmail.secrets // {
      #   path = "%r/email/gmail/main/${secret}";
      # };
    in {
      # "email/gmail/main/address"   = sopsSecrets "address";
      # "email/gmail/main/pass"      = sopsSecrets "pass";
      # "email/gmail/main/signature" = sopsSecrets "signature";
      "email/gmail/main/address"   = cfgEmail.secrets;
      "email/gmail/main/pass"      = cfgEmail.secrets;
      "email/gmail/main/signature" = cfgEmail.secrets;
    };

    accounts.email.accounts.gmail_main = let
      address = builtins.readFile config.sops.secrets."email/gmail/main/address".path;
      signature = {
        text  = builtins.readFile config.sops.secrets."email/gmail/main/signature".path;
        showSignature = "append";
      };
      passwordCommand = "cat ${config.sops.secrets."email/gmail/main/pass".path}";
    in {
      inherit address signature passwordCommand;
      inherit (cfgEmail) realName;

      userName = address;

      # settings
      primary = true;
      notmuch.enable = true;
    };
  };
}

still tho im getting the error saying that home manager cant access /home

       … while evaluating the option `home.file."/home/felipepinto/.config/notmuch/default/config".text':

       … while evaluating definitions from `/nix/store/60wy1fqj9scwnvb48cq4y5r1mfmyvyb5-source/modules/misc/xdg.nix':

       … while evaluating the option `xdg.configFile."notmuch/default/config".text':

       … while evaluating definitions from `/nix/store/60wy1fqj9scwnvb48cq4y5r1mfmyvyb5-source/modules/programs/notmuch.nix':

       … while evaluating the option `accounts.email.accounts.gmail_main.address':

       … while evaluating definitions from `/nix/store/y905im4lvh5c0wz3pfrknsfgz2q5cy0h-source/modules/home/email/gmail_main.nix':

       (stack trace truncated; use '--show-trace' to show the full, detailed trace)

       error: access to absolute path '/home' is forbidden in pure evaluation mode (use '--impure' to override)

i tried to address this by adding to the secrets the path %r/email/gmail/main/${secret} path to which it just dislikes saying it not absolute

       … while evaluating the option `xdg.configFile."notmuch/default/config".text':

       … while evaluating definitions from `/nix/store/60wy1fqj9scwnvb48cq4y5r1mfmyvyb5-source/modules/programs/notmuch.nix':

       … while evaluating the option `accounts.email.accounts.gmail_main.address':

       … while evaluating definitions from `/nix/store/s0048sx1g95csx6wqpmqnjqchq0jpggj-source/modules/home/email/gmail_main.nix':

       (stack trace truncated; use '--show-trace' to show the full, detailed trace)

       error: string '%r/email/gmail/main/address' doesn't represent an absolute path

one thing i noticed is that both errors comes when evaluating notmuch

Again this cannot work!!!

I try one more time and then I’m out.

Steps that are done:

  1. Run home-manager switch
  2. Evaluation of your config: config.sops.secrets."email/gmail/main/address".path becomes /home/user/somepath/somefile (this depends on whether it is a NixOS module or a hm one) especially when you run it the first time the file does not even exist
  3. During evaluation the encrypted sops yaml is copied to /nix/store
  4. Evaluation is done now (current config will not be changed anymore!)
  5. During activation (e.g. during boot) sops decrypts the sops yaml and stores each password/information in a separate file (e.g /home/user/somepath/somefile) as specified in the config.You can change how and where in your config but in the end it will always be a file on disk.
  6. Now and only now programs (not config) can read the secrets from the file (or you can cat them somewhere).

This is the reason why you cannot and never should try to reference any information stored in your secrets file from your configuration.

2 Likes

Maybe check this out if you’re determined to use secrets at evaluation time: GitHub - Mic92/sops-nix: Atomic secret provisioning for NixOS based on sops

Why is a file on disk considered safer than a file on disk in the store? Is because read permissions?

Not sure what you mean by “file on disk”, if you mean /run/secrets, AFAIK /run should actually be a tmpfs and not on disk. And yes the read permissions are more locked-down.

1 Like

I was not aware of that detail about run folder. Is that nixos specific or is it cross platform? On Darwin doe example

I do not know about darwin, though under Linux /run being a thing, and it being tmpfs or other RAM only is standard.

1 Like