Agenix: Difference between revisions

imported>Onny
mNo edit summary
Layer-09 (talk | contribs)
m Translate tags
 
(18 intermediate revisions by 9 users not shown)
Line 1: Line 1:
[https://github.com/ryantm/agenix agenix] is a commandline tool for managing secrets encrypted with your existing SSH keys. The project also includes the NixOS module age for adding encrypted secrets into the Nix store and decrypting them.
<languages/>


== Installation ==
<translate>
<!--T:1-->
<strong>[https://github.com/ryantm/agenix Agenix]</strong><ref name="agenix">Ryan Mulligan and contributors, "agenix", GitHub Repository, Accessed October 2025. https://github.com/ryantm/agenix</ref> is a command-line tool for managing secrets in Nix configurations. It relies on existing SSH key pairs and integrates with the [https://age-encryption.org/ age] encryption tool to protect secrets while they reside in the Nix store and during system activation.<ref name="age">Filippo Valsorda and collaborators, "age", Official Website, Accessed October 2025. https://age-encryption.org/</ref><ref name="agenix-readme">Ryan Mulligan and contributors, "agenix README", GitHub Repository, Accessed October 2025. https://github.com/ryantm/agenix/blob/main/README.md</ref>


The following example describes an installation via [[Flakes]]. For further installation methods see the [https://github.com/ryantm/agenix upstream documentation].
== Features == <!--T:2-->


<!--T:3-->
* <strong>SSH-based encryption workflow:</strong> The `agenix` CLI encrypts raw secret files with user and host SSH public keys, allowing decryption wherever the matching private keys are present.<ref name="agenix-readme" />
* <strong>Reproducible storage model:</strong> Encrypted `.age` files can live in version control, enter the Nix store, and are decrypted during system activation to paths such as <code>/run/agenix</code>.<ref name="agenix-readme" />
* <strong>Declarative module support:</strong> NixOS and Home Manager modules ship with the project, so secrets can be imported, decrypted, and exposed declaratively alongside other configuration.<ref name="agenix-readme" />
* <strong>Minimal dependency footprint:</strong> Agenix depends on the `age` tool rather than GnuPG and intentionally omits templating features, keeping the code base small and auditable.<ref name="agenix-readme" /><ref name="comparison">NixOS Wiki Community, "Comparison of secret managing schemes", NixOS Wiki, Accessed October 2025. https://wiki.nixos.org/wiki/Comparison_of_secret_managing_schemes</ref>
== Installation == <!--T:4-->
<!--T:5-->
The following example describes installing [https://github.com/ryantm/agenix Agenix] with [[Flakes]].<ref name="agenix" /> For additional installation methods, consult the README.<ref name="agenix-readme" />
</translate>
<syntaxhighlight lang="nix">
<syntaxhighlight lang="nix">
{
{
Line 22: Line 35:
}
}
</syntaxhighlight>
</syntaxhighlight>
<translate>
<!--T:6-->
Replace <code>yourhostname</code> with the actual host name and <code>x86_64-linux</code> with the relevant system architecture.


Change <code>yourhostname</code> to your actual hostname and <code>x86_64-linux</code> to your system architecture.
<!--T:7-->
 
After adding the input, install the <code>agenix</code> client application with:
After that installing the agenix client application can be achieved like this
</translate>
 
<syntaxhighlight lang="nix">
<syntaxhighlight lang="nix">
{ config, pkgs, lib, inputs, ... }:{
{ config, pkgs, lib, inputs, ... }:{
Line 34: Line 49:
}
}
</syntaxhighlight>
</syntaxhighlight>
<translate>
== Configuration == <!--T:8-->
=== Choose a public/private key === <!--T:9-->
<!--T:10-->
Decide which [[SSH public key authentication|SSH public key]] should encrypt the secrets. The corresponding private key decrypts the secrets when rebuilding the system.
<!--T:11-->
If you deploy the configuration as the root user, the host key pairs generated by [[SSH]] are already available at:
<!--T:12-->
* <code>/etc/ssh/ssh_host_rsa_key</code> / <code>/etc/ssh/ssh_host_rsa_key.pub</code>
* <code>/etc/ssh/ssh_host_ed25519_key</code> / <code>/etc/ssh/ssh_host_ed25519_key.pub</code>
<!--T:13-->
For deployments triggered by another user, create a dedicated key pair with <code>ssh-keygen</code>. Such keys typically reside at:
<!--T:14-->
* <code>~/.ssh/id_rsa</code> / <code>~/.ssh/id_rsa.pub</code>
* <code>~/.ssh/id_ed25519</code> / <code>~/.ssh/id_ed25519.pub</code>


== Configuration ==
<!--T:15-->
Refer to [[SSH public key authentication]] for more information.


First create a directory where secrets are going to be stored. In this example we're creating the directory <code>secrets</code> inside the NixOS system configuration path <code>/etc/nixos</code>
=== Create the secrets === <!--T:16-->


<!--T:17-->
Create a directory that stores encrypted secrets. For example:
</translate>
<syntaxhighlight lang="bash">
<syntaxhighlight lang="bash">
# mkdir /etc/nixos/secrets
# mkdir /etc/nixos/secrets
</syntaxhighlight>
</syntaxhighlight>
 
<translate>
Inside the secrets directory we create a <code>secrets.nix</code> file which will be used by the agenix client to encrypt secrets for specific users and parts of the system
<!--T:18-->
 
Inside the secrets directory, create a <code>secrets.nix</code> file that defines the encryption rules. The following example gives <code>user1</code> and <code>system1</code> access to <code>secret1.age</code>:
</translate>
{{file|/etc/nixos/secrets/secrets.nix|nix|<nowiki>
{{file|/etc/nixos/secrets/secrets.nix|nix|<nowiki>
let
let
Line 57: Line 98:
}
}
</nowiki>}}
</nowiki>}}
<translate>
== Usage == <!--T:19-->


SSH public keys for a specific user or system can be generated with <code>ssh-keygen</code>, see [[SSH_public_key_authentication|this page]] for more information. Usually the public key of your user can be found in <code>~/.ssh/id_rsa.pub</code> and the system one in <code>/etc/ssh/ssh_host_rsa_key.pub</code>.
<!--T:20-->
 
Create a secret file and encrypt it by running:
== Usage ==
</translate>
 
Creating a secret file, which contents will be encrypted
 
<syntaxhighlight lang="bash">
<syntaxhighlight lang="bash">
# cd /etc/nixos/secrets
# cd /etc/nixos/secrets
# agenix -e secret1.age
# agenix -e secret1.age
</syntaxhighlight>
</syntaxhighlight>
<translate>
<!--T:21-->
The <code>agenix</code> command opens the default terminal editor. Enter the secret (for example, <code>password123</code>) and save the file. The <code>secret1.age</code> filename is defined in <code>secrets.nix</code>, allowing <code>agenix</code> to select the correct SSH keys.<ref name="agenix-readme" />


The agenix command will open your default terminal editor. Write in your secret, for example <code>password123</code>.
<!--T:22-->
 
To reference the secret inside a NixOS module, use a configuration similar to the following example:
The filename <code>secret1.age</code> is specified above in the agenix <code>secrets.nix</code> configuration. So agenix will know which keys to use for a specific user or system.
</translate>
 
To use and reference the secret inside your Nix configuration, an example would look like this
 
<syntaxhighlight lang="nix">
<syntaxhighlight lang="nix">
age.secrets.nextcloud = {
age.secrets.nextcloud = {
   file = /etc/nixos/secrets/secret1.age;
   file = ./secrets/secret1.age;
   owner = "nextcloud";
   owner = "nextcloud";
   group = "nextcloud";
   group = "nextcloud";
Line 83: Line 123:
services.nextcloud = {
services.nextcloud = {
   enable = true;
   enable = true;
   package = pkgs.nextcloud25;
   package = pkgs.nextcloud28;
   hostName = "localhost";
   hostName = "localhost";
   config.adminpassFile = config.age.secrets.nextcloud.path;
   config.adminpassFile = config.age.secrets.nextcloud.path;
};
};
</syntaxhighlight>
</syntaxhighlight>
<translate>
<!--T:23-->
In this case, the [[Nextcloud]] service reads its administrator password from the age-encrypted file, preventing the secret from appearing in the world-readable Nix store.<ref name="agenix-readme" />
<!--T:24-->
Secrets can also be deployed as files with specific permissions:<ref name="agenix-readme" />
</translate>
<syntaxhighlight lang="nix">
age.secrets = {
  netrc = {
    file = ./secrets/netrc.age;
    path = "/home/myuser/.netrc";
    owner = "myuser";
    group = "users";
    mode = "600";
  };
};
</syntaxhighlight>
<translate>
== Tips and tricks == <!--T:25-->
=== Replace in-place strings with secrets === <!--T:26-->
<!--T:27-->
Some modules do not yet support reading secrets from a file. Provide a placeholder string and replace it during activation.
</translate>
<syntaxhighlight lang="nix">
system.activationScripts."dex-user-secret" = ''
  secret=$(cat "${config.age.secrets.dex-user-password.path}")
  configFile=/run/dex/config.yaml
  ${pkgs.gnused}/bin/sed -i "s#@dex-user-password@#$secret#" "$configFile"
'';
</syntaxhighlight>
<translate>
=== Access secrets inside a container === <!--T:28-->
<!--T:29-->
Expose secrets to a container by binding the decrypted file into the container filesystem:
</translate>
<syntaxhighlight lang="nix">
containers.mycontainer.bindMounts."${config.agenix.secrets.mysecret.path}".isReadOnly = true;
</syntaxhighlight>
<translate>
<!--T:30-->
Another option is to import <code>agenix.nixosModules.default</code> directly inside the container and supply the required private key:
</translate>
<syntaxhighlight lang="nix">
{ agenix, ... }: {
  containers."mycontainer" = {
    # pass the private key to the container for agenix to decrypt the secret
    bindMounts."/etc/ssh/ssh_host_ed25519_key".isReadOnly = true;
    config = { config, lib, pkgs, ... }: {
      imports = [ agenix.nixosModules.default ];
      age.identityPaths = [ "/etc/ssh/ssh_host_ed25519_key" ];
      age.secrets."mysecret" = {
        file = ../secrets/mysecret.age;
        owner = "myuser";
      };
    };
  };
}
</syntaxhighlight>
<translate>
=== Use secrets in the initrd === <!--T:31-->
<!--T:32-->
Agenix provisions secrets during system activation, so they are unavailable while building the initrd. As a workaround, create the secret as a static file outside <code>/run/agenix</code> and reference it from <code>/etc/initrd-hostkey</code>.
</translate>
<syntaxhighlight lang="nix">
age.secrets.hostkey-initrd = {
  file = "${paths.agenix}/hostkey-initrd.age";
  path = "/etc/initrd-hostkey";
  symlink = false;
};
boot.initrd.network.ssh.hostKeys = [ "/etc/initrd-hostkey" ];
</syntaxhighlight>
<translate>
<!--T:33-->
Rebuild the system twice and reference <code>/etc/initrd-hostkey</code> only after the file exists.
=== Agenix with Impermanence === <!--T:34-->
<!--T:35-->
Systems that use [[Impermanence]] may regenerate host keys on each boot. Point <code>age.identityPaths</code> to the persistent locations of those keys:
</translate>
<syntaxhighlight lang="nix">
age.identityPaths = [
  "/persist/etc/ssh/ssh_host_ed25519_key"
  "/persist/etc/ssh/ssh_host_rsa_key"
];
</syntaxhighlight>
<translate>
== See also == <!--T:36-->
<!--T:37-->
* [[Comparison of secret managing schemes]] – Overview of alternative secret management approaches
* [https://age-encryption.org/ age] – Encryption tool that underpins Agenix secrets<ref name="age" />
* [[Home Manager]] – Declarative per-user configuration that can consume Agenix-managed secrets


Here, the service [[Nextcloud]] requires a password for the administrator account. In this case, the password is stored in an age-encrypted file, so no plaintext passwords will be copied into your world-readable Nix-store. We configure <code>owner</code> and <code>group</code> names to <code>nextcloud</code> so that the webservice has the permissions to read the password wile.
== References == <!--T:38-->


== See also ==
<references />
* [[Comparison of secret managing schemes]]


[[Category:Applications]]
[[Category:Applications]]
[[Category:Security]]
[[Category:Security]]
</translate>