Mic92 / sops-nix

Atomic secret provisioning for NixOS based on sops
MIT License
1.55k stars 141 forks source link

`sops.template` docs with systemd DynamicUser #412

Open johnjameswhitman opened 1 year ago

johnjameswhitman commented 1 year ago

Thanks for adding sops.template -- it really simplified getting a key into my inadyn service.

Along the way I learned that you can pass credentials into a systemd unit that runs w/ DynamicUser = true. The key bit in the example below is LoadCredential = "inadyn.conf:${config.sops.templates."inadyn.conf".path}";, which exposes the template to the unit at ${CREDENTIALS_DIRECTORY}/inadyn.conf.

Wonder if it'd be worth updating the docs with these bits since I think it's pretty common to set the DynamicUser for better security? If not, figure at least having an example in this issue could be helpful to others in the future.

  sops = {
    defaultSopsFile = ./secrets.yaml;
    age.sshKeyPaths = [ "/etc/ssh/ssh_host_ed25519_key" ];
    secrets."inadyn/some_tld" = { };
    templates."inadyn.conf" = {
      content = ''
        custom namecheap { 
          username    = some.tld
          password    = ${config.sops.placeholder."inadyn/some_tld}
          ddns-server = dynamicdns.park-your-domain.com 
          ddns-path   = "/update?domain=%u&password=%p&host=%h" 
          hostname    = "some.tld"
        }
      '';
    };
  };

  systemd.timers.inadyn = {
    description = "Sync inadyn every hour";
    wantedBy = [ "default.target" ];
    timerConfig = {
      OnBootSec = "60m";
      OnUnitActiveSec = "60m";
    };
  };

  systemd.services.inadyn = {
    description = "Syncs home IP to dynamic DNS.";
    requires = [ "network-online.target" ];
    serviceConfig = {
      DynamicUser = true;
      Type = "oneshot";
      ProtectSystem = "strict";
      ProtectHome = true;
      PrivateUsers = true;
      PrivateTmp = true;
      LoadCredential = "inadyn.conf:${config.sops.templates."inadyn.conf".path}";
      CacheDirectory= "inadyn";
      ExecStart = ''
        ${pkgs.inadyn}/bin/inadyn \
          --foreground \
          --syslog \
          --once \
          --cache-dir ''${CACHE_DIRECTORY} \
          --config ''${CREDENTIALS_DIRECTORY}/inadyn.conf
      '';
    };
  };
Mic92 commented 12 months ago

Does systemd replace ${CREDENTIALS_DIRECTORY}/inadyn.conf in ExecStart? Didn't knew that.

johnjameswhitman commented 12 months ago

Yep -- Here are a few key snippets from the systemd docs:

The data is accessible from the unit's processes via the file system, at a read-only location that (if possible and permitted) is backed by non-swappable memory. The data is only accessible to the user associated with the unit, via the User=/DynamicUser= settings (as well as the superuser).

The LoadCredential= setting takes a textual ID to use as name for a credential plus a file system path, separated by a colon.

In order to reference the path a credential may be read from within a ExecStart= command line use "${CREDENTIALS_DIRECTORY}/mycred", e.g. "ExecStart=cat ${CREDENTIALS_DIRECTORY}/mycred".

Apparently this came out with v247 which released in 2020, so it's relatively recent.

Note: Systemd ran into an issue loading the credential when the name of my sops.template lead with a / (e.g. /etc/inadyn.conf instead of just inadyn.conf), I think because the resulting file path included a double-slash //.