Skip to main content

Secrets Management

Managing secrets with SOPS-Nix.

NixOSInstall

GenerateNixOS

Public

Subset Keysof flake.nix

# flake.nix

{
  inputs = {
    sops-nix = {
      url = "github:Mic92/sops-nix";
      inputs.nixpkgs.follows = "nixpkgs";
    };

  };

  outputs = inputs@{
    sops-nix,
    ...
  }: {

   nixosConfigurations = {
   };
  };
}

Create module for target machines

AGE (Using SSH Host Keys)
SOPS

nix-shell# -pmodules/nixos/secrets.nix

{ config, lib, pkgs, sops-nix, ... }:

let
  isEd25519 = k: k.type == "ed25519";
  getKeyPath = k: k.path;
  keys = builtins.filter isEd25519 config.services.openssh.hostKeys;
in
{
  imports = [
    sops-nix.nixosModules.sops
  ];

  environment.systemPackages = with pkgs; [
    age
    gnupg
    pinentry.out
    ssh-to-age
    --run 'ssh-keyscan servername | ssh-to-age'pgp
    #sops
  OR];

  locallysops nix-shell= -p{
    ssh-to-ageage.sshKeyPaths --run= 'catmap /etc/ssh/ssh_host_ed25519_key.pubgetKeyPath |keys;
  ssh-to-age'};
}
GPG (Using SSH Host Keys)
ssh user@server "sudo cat /etc/ssh/ssh_host_rsa_key" | nix-shell -p ssh-to-pgp --run "ssh-to-pgp -o server.asc"

# OR locally

nix-shell -p ssh-to-pgp --run "ssh-to-pgp -i /etc/ssh/ssh_host_rsa_key -o $(hostname).asc"

Home-Manager

Install

# flake.nix

{
  inputs = {
    ...

    sops-nix = {
      url = "github:mic92/sops-nix";
      inputs.nixpkgs.follows = "nixpkgs";
    };
    
    ...
   
   outputs = { ..., sops-nix, ...} :
   
     imports = [
       sops-nix.homeManagerModules.sops
     ];
     
    ...
 };

Create SOPS module.

# modules/cli/sops.nix
{ pkgs, config, specialArgs, ...}:
let
  inherit(specialArgs) username;
in
{
  home = {
    packages = with pkgs;
      [
        age
        gnupg
        pinentry.out
        ssh-to-age
        ssh-to-pgp
        sops
      ];
  };

  systemd.user.services.sops-nix.Unit.After = [ "sops-nix.service" ];

  sops.defaultSopsFile = ../../secrets/example.yaml;
  sops.age.keyFile = "/home/${username}/.config/sops/age/keys.txt";
  sops.secrets.example_key = {};

Generate Public Keys for target machines

AGE (Using SSH Host Keys)

ssh-keyscan servername | ssh-to-age

# OR locally

cat /etc/ssh/ssh_host_ed25519_key.pub | ssh-to-age

GPG (Using SSH Host Keys)

ssh user@server "sudo cat /etc/ssh/ssh_host_rsa_key" | ssh-to-pgp -o server.asc

# OR locally

ssh-to-pgp -i /etc/ssh/ssh_host_rsa_key -o $(hostname).asc

Generate Private+Public Keypair

If creating this for NixOS, use sudo Choose one..one, recommend using an existing SSH ed25519 key.

AGE

  • Need pkgs.age

mkdir -p ~/.config/sops/age
age-keygen -o ~/.config/sops/age/keys.txt

Get Public Key:

age-keygen -y ~/.config/sops/age/keys.txt
AGE (Convert exsiting SSH ed25519 key)

  • Need pkgs.age

mkdir -p ~/.config/sops/age
nix-shell -p ssh-to-age --run "ssh-to-age -private-key -i ~/.ssh/id_ed25519 > ~/.config/sops/age/keys.txt"
age-keygen -y ~/.config/sops/age/keys.txt
GPG Key

  • Need some variant of pkgs.pinentry

gpg --full-generate-key

Get Public Key:

gpg --list-secret-keys

Create SOPS configuration

NixOS

Filling in the user public key, and the groups for a standard set of secrets to be found within the secrets folder.

# ./src/nixos/.sops.yaml
keys:
  - &user_dave age1upzm9um3qljxlmxcg8vl35d7eyeqtnsfcnqlh3wtnj46dhfzwyrqa80avw
  - &host_machine age1wq5xj5mwv9xk4tp26cxc4xqjq9xd9hwqv0zeemawl2cc8sarmqesw366dh
creation_rules:
  - path_regex: secrets/[^/]+\.(yaml|json|env|ini)$
    key_groups:
    - age:
      - *user_dave
      - *host_machine
      

Home Manager

Filling in the user public key, and the groups for a standard set of secrets to be found within the secrets folder.

# ./config/home-manager/.sops.yaml
keys:
  - &user_dave age1upzm9um3qljxlmxcg8vl35d7eyeqtnsfcnqlh3wtnj46dhfzwyrqa80avw
  - &host_beefhost_machine age1wq5xj5mwv9xk4tp26cxc4xqjq9xd9hwqv0zeemawl2cc8sarmqesw366dh
creation_rules:
  - path_regex: secrets/[^/]+\.yaml$(yaml|json|env|ini)$
    key_groups:
    - age:
      - *user_dave
      - *host_beefhost_machine
      

Create new secrets file

nix-shell -p sops --run "sops secrets/github.yaml"yaml

# Inside the file put the key and value:

gh_token: 12345678910

Upon saving this will encrypt the secret. If you execute it again it will decrypt the file to revise.

Configure Home Manager

# sops.example.nix
{ pkgs,
...}:
{
  home = {
    packages = with pkgs;
      [
        age
        gnupg
        pinentry.out
        ssh-to-age
        ssh-to-pgp
        sops
      ];
  };

  sops.defaultSopsFile = ../../secrets/example.yaml;
  sops.age.keyFile = "/home/$USER/.config/sops/age/keys.txt";

  sops.secrets.example_key = {};

  sops.secrets.gh_token = {
    sopsFile = ../../secrets/github.yaml ;
    path = "%r/gh-token" ;
    owner = dave.name ;
    group = dave.group ;
  };
}

Activate

ActivateNixOS

Upon generating new NixOS configuration the secrets will appear in /run/secrets/

[root@hostname:/]# ls -l /run/secrets/*
-r-------- 1 root root 46 Jul 19 09:21 /run/secrets/hostname
-r-------- 1 root root 25 Jul 19 09:21 /run/secrets/common
cat /run/secrets/common
supersecretpassword

Home Manager

Upon performing home-manager switch the secrets will appear in $XDG_RUNTIME_DIR/

[~/] $ ls -l $XDG_RUNTIME_DIR/secrets/
.r-------- dave users 13 B Fri Jun 23 16:29:52 2023  example_key
.r-------- dave users 40 B Fri Jun 23 16:29:52 2023  gh_token

[~/] $ cat $XDG_RUNTIME_DIR/secrets/gh_token
12345678910

Usage and Tips

Use in configuration

Environment example
programs.bash.initExtra = ''
  export GITHUB_TOKEN=$(cat /$XDG_RUNTIME_DIR/secrets/gh_token)
'';   

Nix Configuration

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

  services.example = {
    ...
    username = "dave";
    password = config.sops.secrets.example_key;
  };
};
'';   

Reencrypting secrets

If ever adding or removing keys from .sops.yaml then you will need to reencrypt your secrets.

Use sops updatekeys path/to/secrets.yaml to re-encrypt the secrets contained in that .yaml file with all the keys that the file should use.