Sops-nix templates in config file

I’ve had an incredibly frustrating few days with sops-nix, trying to figure out how to use my secrets in a config file so I can auto mount a couple of samba shares.

The secrets are available in /run/secrets, but something like:

wallyPass = ''$(cat ${config.sops.secrets."wally/password".path})'';

just inserts the literal command (according to the generated /etc/fstab):

password=$(cat\040/run/secrets/wally/password)

The sops-nix documentation suggests that templates are the answer.

  # from configuration.nix
  sops = {
   defaultSopsFile = ./secrets/secrets.yaml;
   defaultSopsFormat = "yaml";
   secrets.main-keys = { };
   secrets."wally/username" = {
     owner = "me";
    };
    secrets."wally/password" = {
      owner = "me";
    };
    templates."system/networking.nix".content = ''
      wallyPass="${config.sops.placeholder."wally/password"}"
    '';
  };
#system/networking.nix
...
   options = let
      automount_opts = "x-systemd.automount,noauto,x-systemd.idle-timeout=60,x-systemd.device-timeout=5s,x-systemd.mount-timeout=5s,rw,uid=1000";

      in ["${automount_opts},username=me,password=${config.sops.templates."system/networking.nix".content}"];
  };

The docs say to use the .path attribute, but this again returns the literal “/run/secrets/wally/password”. Using the .content attribute, I get:

password=wallyPass="<SOPS:fc28915c29b77b24313xx8324b3xx82165f0be5454bd8f97c56daxx85cc561xx:PLACEHOLDER>"

in the generated fstab.

I’m about 5 days into nix, so forgive my ignorance, but I’m out of googles…I think I’m making this harder than it needs to be or have a fundamental misunderstanding of how this works.

I’m using flakes and home-manager, but I’m not sure if that comes into play here.

Any help would be appreciated.

You can’t insert just a password into a configuration file. You can either create an entire file using the .path way and have all the values in that file contained in your secrets file OR you can create a template in your configuration for a complete file and fill in the secret values using the template and placeholder idea. For either way, the service configuration option needs to be able to accept a file path.

For example - .path way. For the traefik systemd service you can set the path to a file containing the cloudflare DNS API token:

    sops.secrets.cf-api-token = {
      mode = "0440";
      owner = config.users.users.traefik.name;
      group = config.users.users.traefik.group;
    };
    systemd.services.traefik.environment = {
      CF_DNS_API_TOKEN_FILE = "${config.sops.secrets.cf-api-token.path}";
    };

# in secrets.yaml
cf-api-token: r-sty67lkljkwhret678734hjkdjshsdf

Another example of creating an entire file using the secrets file and .path would be setting a bunch of mail server variables in an environment file for vaultwarden:

sops.secrets.vaultwarden-env = {};
services.vaultwarden = {
  enable = true;
  ....
  environmentFile = "${config.sops.secrets.vaultwarden-env.path}";
};
# In secrets.yml
vaultwarden-env: |
    SMTP_PASSWORD=secretpassword!!
    SMTP_USERNAME=joe@email.com
    SMTP_FROM=noreply@email.com
    SMTP_HOST=smtp.hotmail.com
    SMTP_PORT=587
    SMTP_SECURITY=starttls

For my desktops where I want to generate a wireguard config file to import with networkmanager, I use a sops template which generates the entire file /etc/wireguard/wg0.conf:

sops.secrets.wg-private-key = {};
  sops.secrets.wg-preshared-key = {};
  sops.secrets.wg-address = {};
  sops.secrets.wg-allowed-ips = {};
  sops.templates."wg0.conf" = {
    content = ''
      [Interface]
      Address = ${config.sops.placeholder.wg-address}
      ListenPort = 51820
      PrivateKey = ${config.sops.placeholder.wg-private-key}
      DNS = 10.10.10.1

      [Peer]
      # pfsense
      PublicKey = gPdBkk+fw4z7YHjp7C1Hyy6f+jY7433E/RJu+aJ2w&ahj=
      PresharedKey = ${config.sops.placeholder.wg-preshared-key}
      AllowedIPs = ${config.sops.placeholder.wg-allowed-ips}
      Endpoint = domain.net:51820
      PersistentKeepalive = 25
    '';
    path = "/etc/wireguard/wg0.conf";
  };

I’m sorry I can’t explain it better, but I think this is why you can’t just use the contents of the secret and have to use the whole file.

Good luck!

1 Like

Thank you so much! I ended up using a systemd service to generate the samba credentials file and that works for this case. Your post will help for the rest. Thanks again for the help. Much appreciated.

Hi, needing a similar setup to @elvisthedj I found that systemd mount can take a credentials parameter pointing to a file which in this case will be a sops-nix template.

Initializing the needed secrets and creating the credentials template:

  sops.secrets.cifs_username = { };
  sops.secrets.cifs_password = { };
  sops.templates."cifs-credentials".content = ''
    username=${config.sops.placeholder.cifs_username}
    password=${config.sops.placeholder.cifs_password}
  '';

Setting up the mount (I’m using fileSystems instead of systemd.mounts but it would be the same options):

  fileSystems."/mnt/destination" = {
    device = "//CIFS_SERVER/source";
    fsType = "cifs";

    options = [
      "noauto"
      "x-systemd.automount"
      "credentials=${config.sops.templates."cifs-credentials".path}"
    ];
  };
1 Like