Agenix : option not found

Giving agenix a whirl as I’d like to configure Wireguard and encrypt my private key.

The Problem

When I try to test out this configuration I’m informed…

[root@crow:/etc/nixos]# nixos-rebuild test 
error:
       … while calling the 'seq' builtin
         at /nix/store/wj2qla569hnxwqfc26imv5hqbxc1rc27-source/lib/modules.nix:334:18:
          333|         options = checked options;
          334|         config = checked (removeAttrs config [ "_module" ]);
             |                  ^
          335|         _module = checked (config._module);

       … while calling the 'throw' builtin
         at /nix/store/wj2qla569hnxwqfc26imv5hqbxc1rc27-source/lib/modules.nix:310:18:
          309|                     ''
          310|             else throw baseMsg
             |                  ^
          311|         else null;

       error: The option `"/etc/nixos/secrets/vpn.age"' does not exist. Definition values:
       - In `/nix/store/1gdvh8939wpzb6frd05qf0231qsaz89h-source/secrets/secrets.nix':
           {
             publicKeys = [
               "ssh-ed25519 ######################################################## neil@crow"
               "ssh-ed25519 ######################################################## root@crow"
             ];
           ...

Config

/etc/nixos/secrets/secrets.nix

let
  system1 = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAID3c2j/3kpyUU7YVgtaSNSLB3g9EXSHv7C8mEyHCUq18 root@crow";
  systems = [ system1 ];


  user_crow = "ssh-ed25519 #################################################### neil@crow";
  users = [ neil_kimura ];
in {
  "/etc/nixos/secrets/vpn.age".publicKeys = [ user_crow system1 ];
}

/etc/nixos/flake.nix

{
  description = "NixOS configuration";
  inputs = {
    nixpkgs.url = "github:nixos/nixpkgs/nixos-24.11";
    nixos-hardware.url = "github:NixOS/nixos-hardware";
    agenix.url = "github:ryantm/agenix";
    # agenix.inputs.nixpkgs.follows = "nixppkgs";
    home-manager.url = "github:nix-community/home-manager";
    home-manager.inputs.nixpkgs.follows = "nixpkgs";
  };

  outputs = inputs@{ nixpkgs, home-manager, nixos-hardware, agenix, ... }:
  let
    system = "x86_64-linux";
    pkgs = import nixpkgs {
      inherit system;
      config = { allowUnfree = true; };
    };
  in {
    nixosConfigurations = {
      crow = nixpkgs.lib.nixosSystem {
        inherit pkgs system;
        modules = [
          ./configuration.nix
          nixos-hardware.nixosModules.lenovo-thinkpad-t490
          agenix.nixosModules.default
          home-manager.nixosModules.home-manager
          { environment.systemPackages = [ agenix.packages.${system}.default ]; }
          {
            home-manager.useGlobalPkgs = true;
            home-manager.useUserPackages = true;
            home-manager.users.neil = import ./home.nix;
            # Optionally, use home-manager.extraSpecialArgs to pass
            # arguments to home.nix
          }
           }
         ]; # end modules
       }; # end crow
     }; # end nixosConfigurations
   }; # end in
 }

/etc/nixos/configuration.nix

{ config, lib, pkgs, inputs, ... }:

{
  imports =
    [ # Include the results of the hardware scan.
      ./hardware-configuration.nix
      ./audio.nix
      ./bluetooth.nix
      ./secrets/secrets.nix
      ./xfce.nix
    ];
  ...
 # agenix
 age = {
     vpn = {
       file = ./secrets/vpn.age;
       owner = "root";
       group = "root";
     }; # end vpn
   }; # end secrets
 }; # end age

  # Networking                                                                                                                                            
  networking = {
    hostName = "crow"; # Define your hostname.
    networkmanager = {
      enable = true;
    }; # end networkmanager
                                                                                                                                                          
    wireguard.interfaces = {
      # "wg0" is the network interface name. You can name the interface arbitrarily.
      wg0 = {
        # Determines the IP address and subnet of the client's end of the tunnel interface.
        ips = [ "10.100.0.12/24" ];
        listenPort = 51820; # to match firewall allowedUDPPorts (without this wg uses random port numbers)
                                                                                                                                                          
        # Path to the private key file.
        #                                                                                                                                                 
        # Note: The private key can also be included inline via the privateKey option,
        # but this makes the private key world-readable; thus, using privateKeyFile is
        # recommended.
        privateKeyFile = config.age.secrets.vpn.path;
                                                                                                                                                          
        peers = [
          # For a client configuration, one peer entry for the server will suffice.
          {
            # Public key of the server (not a file path).
            publicKey = "########################################################";                                                                                   
                                                                                                                                                          
            # Forward all the traffic via VPN.
            allowedIPs = [ "0.0.0.0/0" "::/0" ];
            # Or forward only particular subnets
            #allowedIPs = [ "10.100.0.1" "91.108.12.0/22" ];
                                                                                                                                                          
            # Set this to the server IP and port.                                                                                                         
            endpoint = "152.228.170.148:51820"; # ToDo: route to endpoint not automatically configured https://wiki.archlinux.org/index.php/WireGuard#Loop
_routing https://discourse.nixos.org/t/solved-minimal-firewall-setup-for-wireguard-client/7577                                                            
                                                                                                                                                          
          # Send keepalives every 25 seconds. Important to keep NAT tables alive.                                                                         
          persistentKeepalive = 25;                                                                                                                       
        }                                                                                                                                                 
      ];                                                                                                                                                  
    };                                                                                                                                                    
    }; # end wireguard.interfaces                                                                                                                         
  };                                                                                                                                                      
}
 ...

Question

Clearly I’ve not specified something correctly but am not yet familiar enough with the Nix language to work out where I’ve gone wrong.

Suggestions/pointers very much welcome.

Your error mentions “ovh-vpn.age”, your config does not, are you sure that’s actually the code and error?

Anyway, the error is more or less accurate, there’s no option called "/etc/whatever" at the top-level.

Thanks for the quick response @waffle8946

That is me failing to edit out the actual file names to more generic vpn.age, corrected (I should probably just stick with copy and pasting verbatim, bar secrets!)

Anyway, the error is more or less accurate, there’s no option called "/etc/whatever" at the top-level.

Thanks, although I don’t understand what I’ve done wrong here.

What is the “top-level” here? The file exists and is encrypted…

# l /etc/nixos/secrets/vpn.age 
-rw-r--r-- 1 root root 367 Dec 25 12:46 /etc/nixos/secrets/vpn.age
[root@crow:/etc/nixos]# bat secrets/vpn.age 
───────┬────────────────────────────────────────────────────────────────────────────────
       │ File: secrets/ovh-vpn.age
───────┼────────────────────────────────────────────────────────────────────────────────
   1   │ age-encryption.org/v1
   2   │ -> ssh-ed25519 /21a1w x2zoFx6O8cBOzpVu3KE0d0OHLO2jI993bBswMaXFORI
   3   │ 7XHgLcB8400NU8NaCqMIhk/GI5yQxBk8T32IinUl2zE
   4   │ -> ssh-ed25519 yglNfw N4LfgseYoQgkkRVRMYgVe+rLfOcGDrE+E/e96cxLi1Y
   5   │ u1T1YXM6CsDyFFRcp2q2U0zY6it0oFrtUJYwykESV6g
   6   │ --- TeUXjsDa5vzeimtJKbWVPK9Mtq8MdMdmhds3uQpKM8I
   7   │ ��&�w�X�֯�lTz���5�^^�,��e�@�@��-���=�^\��a��Sd��@�%�^P)b����},��-�gz�oc�N�0V�
───────┴────────────────────────────────────────────────────────────────────────────────

But I don’t think that is what you mean by top-level, its perhaps something in the configurations options that should be defining this top-level, but I don’t know where?

In the tutorial I’ve failed to follow these are defined as the filenames with .age extension in quotes. Whether I use the the full-path or the relative path (the vpn.age sits adjacent to secrets.nix) I get the same error.

Its quite likely I’m missing something really obvious as I’m pretty new to NixOS.

In that case, remove this line. The readme says to not import the secrets.nix into your config.

Bingo! Thanks, I’d completely misunderstood that statement about it not being imported and thought it therefore needed importing as most other .nixfiles with configuration options need pulling in. :man_facepalming:

Thanks for getting me back on track, very much appreciated. :+1: