Using Agenix with DevShells

9 hours ago 1

Nix with Flakes allows you to easily centralize project environments and setup right in your repository. Often, your README will tell the reader "get an API key for FOOSERVICE from a teammate"

Just like NixOS modules and home-manager, we can use Agenix to encrypt and store secrets without exposing them to our git repository.

1. flake-parts

The easiest way to use Agenix with devShells is to use the agenix-shell module for flake-parts.

Let's start with a basic flake using flake-parts.

{ description = "Agenix example for devShells"; inputs = { nixpkgs.url = "github:nixos/nixpkgs?ref=nixos-unstable"; + flake-parts.url = "github:hercules-ci/flake-parts"; }; - outputs = { self, nixpkgs }: + outputs = { self, nixpkgs, flake-parts }: + flake-parts.lib.mkFlake {inherit inputs;} { + imports = []; + systems = ["x86_64-linux" "aarch64-linux" "aarch64-darwin" "x86_64-darwin"]; + perSystem = { pkgs, ... }: { + devShells.default = pkgs.mkShell { + packages = []; + }; + }; + } + }; }

2. Add Agenix and agenix-shell

Lets add flake-parts and the agenix-shell module.

{ description = "Agenix example for devShells"; inputs = { nixpkgs.url = "github:nixos/nixpkgs?ref=nixos-unstable"; flake-parts.url = "github:hercules-ci/flake-parts"; + agenix.url = "github:ryantm/agenix"; + agenix-shell.url = "github:aciceri/agenix-shell"; }; - outputs = { self, nixpkgs, flake-parts }: + outputs = { self, nixpkgs, flake-parts, agenix, agenix-shell }: flake-parts.lib.mkFlake {inherit inputs;} { - imports = []; + imports = [ + agenix-shell.flakeModules.default + ]; systems = ["x86_64-linux" "aarch64-linux" "aarch64-darwin" "x86_64-darwin"]; - perSystem = { pkgs, ... }: { + perSystem = { pkgs, config, lib ... }: { devShells.default = pkgs.mkShell { - packages = []; + packages = [agenix.packages.${system}.default]; + shellHook = '' + source ${lib.getExe config.agenix-shell.installationScript} + ''; }; }; } }; }

3. Configure your secrets recipients

You can reference the section we described last time.

This time, we're going to encrypt a file called api-keys.age.

4. Create your secrets files

Create your secrets files by running agenix -e <key-name>.age in the same directory as secrets.nix. Fill them in with the relevant values.

# service-a-api-key.age foo

5. Use your secrets

&lbrace; description = "Agenix example for devShells"; inputs = &lbrace; nixpkgs.url = "github:nixos/nixpkgs?ref=nixos-unstable"; flake-parts.url = "github:hercules-ci/flake-parts"; agenix.url = "github:ryantm/agenix"; agenix-shell.url = "github:aciceri/agenix-shell"; &rbrace;; outputs = &lbrace; self, nixpkgs, flake-parts, agenix, agenix-shell &rbrace;: flake-parts.lib.mkFlake &lbrace;inherit inputs;&rbrace; &lbrace; imports = [ agenix-shell.flakeModules.default ]; systems = ["x86_64-linux" "aarch64-linux" "aarch64-darwin" "x86_64-darwin"]; + agenix-shell = &lbrace; + secrets = &lbrace; + SERVICE_A_API_KEY.file = ./service-a-api-key.age; + SERVICE_B_API_KEY.file = ./service-b-api-key.age; + SERVICE_C_API_KEY.file = ./service-c-api-key.age; + &rbrace;; + &rbrace;; perSystem = &lbrace; pkgs, config, lib ... &rbrace;: &lbrace; devShells.default = pkgs.mkShell &lbrace; packages = [agenix.packages.$&lbrace;system&rbrace;.default]; shellHook = '' source $&lbrace;lib.getExe config.agenix-shell.installationScript&rbrace; ''; &rbrace;; &rbrace;; &rbrace; &rbrace;; &rbrace;

Your local project secrets are now available when you enter your devShell with nix develop.

Wrapping Up

Agenix seemed complicated to me at first, but once I got it set up up in the three usual scenarios (NixOS, home-manager, and devShells), its simplicity came to light.

I hope this series helps you take advantage of Agenix as well.

Full Example

flake.nix

&lbrace; description = "Agenix example for devShells"; inputs = &lbrace; nixpkgs.url = "github:nixos/nixpkgs?ref=nixos-unstable"; flake-parts.url = "github:hercules-ci/flake-parts"; agenix.url = "github:ryantm/agenix"; agenix-shell.url = "github:aciceri/agenix-shell"; &rbrace;; outputs = &lbrace; self, nixpkgs, flake-parts, agenix, agenix-shell &rbrace;: flake-parts.lib.mkFlake &lbrace;inherit inputs;&rbrace; &lbrace; imports = [ agenix-shell.flakeModules.default ]; systems = ["x86_64-linux" "aarch64-linux" "aarch64-darwin" "x86_64-darwin"]; agenix-shell = &lbrace; secrets = &lbrace; SERVICE_A_API_KEY.file = ./service-a-api-key.age; SERVICE_B_API_KEY.file = ./service-b-api-key.age; SERVICE_C_API_KEY.file = ./service-c-api-key.age; &rbrace;; &rbrace;; perSystem = &lbrace; pkgs, config, lib ... &rbrace;: &lbrace; devShells.default = pkgs.mkShell &lbrace; packages = [agenix.packages.$&lbrace;system&rbrace;.default]; shellHook = '' source $&lbrace;lib.getExe config.agenix-shell.installationScript&rbrace; ''; &rbrace;; &rbrace;; &rbrace; &rbrace;; &rbrace;

secrets.nix

let local = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIP55ETmYHSCjtvDZ/SDoHDTblYZPD2XDmObLMQvc+9xR mitchell@nixos"; remote = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAII5SFrZIaTh42TWQKSXeGRhBZ5CAvJWoJov+eiaUbwxa root@nixos"; in &lbrace; "service-a-api-key.age".publicKeys = [local remote]; "service-b-api-key.age".publicKeys = [local remote]; "service-c-api-key.age".publicKeys = [local remote]; &rbrace;
Read Entire Article