Sops-Nix encrypted secrets
- Add sops to your
flake.nix
:
{
inputs.sops-nix.url = "github:Mic92/sops-nix";
inputs.sops-nix.inputs.nixpkgs.follows = "nixpkgs";
outputs = { self, nixpkgs, sops-nix }: {
# change `yourhostname` to your actual hostname
nixosConfigurations.yourhostname = nixpkgs.lib.nixosSystem {
# customize to your system
system = "x86_64-linux";
modules = [
./configuration.nix
sops-nix.nixosModules.sops
];
};
};
}
- Add
sops
andage
to yourenvironment.systemPackages
:
environment.systemPackages = [
pkgs.sops
pkgs.age
];
- Generate a key (This is your private key and MUST NEVER BE COMMITTED TO GIT OR SHARED):
mkdir -p ~/.config/sops/age
age-keygen -o ~/.config/sops/age/keys.txt
Copy the Public Keys Value, it will look something like this:
age-keygen -y ~/.config/sops/age/keys.txt
age12zlz6lvcdk6eqaewfylg35w0syh58sm7gh53q5vvn7hd7c6nngyseftjxl
- Create a
.sops.yaml
in the same directory as yourflake.nix
:
# .sops.yaml
keys:
# Your personal age public key (from age-keygen -y ~/.config/sops/age/keys.txt)
- &personal_age_key age12zlz6lvcdk6eqaewfylg35w0syh58sm7gh53q5vvn7hd7c6nngyseftjxl
# You can also use PGP keys if you prefer, but age is often simpler
# - &personal_pgp_key 0xDEADBEEFCAFE0123
creation_rules:
# This rule applies to any file named 'secrets.yaml' directly in the 'secrets/' directory
# or 'secrets/github-deploy-key.yaml' etc.
- path_regex: "secrets/.*\\.yaml$"
key_groups:
- age:
- *personal_age_key
# Add host keys for decryption on the target system
# sops-nix will automatically pick up the system's SSH host keys
# as decryption keys if enabled in your NixOS config.
# So you typically don't list them explicitly here unless you
# want to restrict it to specific fingerprints, which is rare.
# This part ensures your *personal* key can decrypt it.
Save it and move on.
- sops-nix's automatic decryption feature using system SSH host keys only works with ed25519 host keys for deriving Age decryption keys. Therefore, for system decryption, ensure your using ed25519 not rsa keys:
ssh-keygen -t ed25519 -C "your_email@example.com"
# for multiple keys run something like
ssh-keygen -t ed25519 -f ~/nix-book-deploy-key -C "deploy-key-nix-book-repo"
- Copy the PRIVATE key for each and add them to your secrets directory:
While in your flake directory:
mkdir secrets
sops secrets/github-deploy-key.yaml # For your github ssh key
The above command will open a default sops github-deploy-key.yaml
in your
$EDITOR
:
github_deploy_key_ed25519: |
-----BEGIN OPENSSH PRIVATE KEY-----
...
-----END OPENSSH PRIVATE KEY-----
github_deploy_key_ed25519_nix-book: |
-----BEGIN OPENSSH PRIVATE KEY-----
...
-----END OPENSSH PRIVATE KEY-----
Ensure sops can decrypt it:
sops -d secrets/github-deploy-key.yaml
The -----BEGIN
and the rest of the private key must be indented 2 spaces
❗ WARNING: Only ever enter your private keys through the
sops
command. If you forget and paste them in without thesops
command then rungit add
at any point, your git history will have contained an unencrypted secret which is a nono. Always use thesops
command when dealing with files in thesecrets
directory, save the file and inspect that it is encrypted on save. If not something went wrong with thesops
process, do not add it to Git. If you do, you will be required to rewrite your entire history which can be bad if you're collaborating with others.git-filter-repo
is one such solution that rewrites your history. Just keep this in mind. This happens because Git has a protection that stops you from doing stupid things.
Generate a hashedPassword:
mkpasswd -m SHA-512 -s
# Enter your chosen password and copy it
sops secrets/password-hash.yaml # For your `hashedPasswordFile`
password_hash: PasteGeneratedPasswordHere
sops -d secrets/password-hash.yaml
- Create a
sops.nix
and import it or add this directly to yourconfiguration.nix
:
My sops.nix
is located at ~/flake/hosts/hostname/sops.nix
and the secrets
directory is located at ~/flake/secrets
so the path from sops.nix
to
secrets/pasword-hash.yaml
would be ../../secrets/password-hash.yaml
# ~/flake/hosts/magic/sops.nix # magic is my hostname
# hosts/magic/ is also where my configuration.nix is
{...}: {
sops = {
defaultSopsFile = ../../.sops.yaml; # Or the correct path to your .sops.yaml
age.sshKeyPaths = ["/etc/ssh/ssh_host_ed25519_key"];
secrets = {
"password_hash" = {
sopsFile = ../../secrets/password-hash.yaml; # <-- Points to your password hash file
owner = "root";
group = "root";
mode = "0400";
};
"github_deploy_key_ed25519_nix-book" = {
sopsFile = ../../secrets/github-deploy-key.yaml;
key = "github_deploy_key_ed25519_nix-book";
owner = "root";
group = "root";
mode = "0400";
};
"github_deploy_key_ed25519" = {
sopsFile = ../../secrets/github-deploy-key.yaml;
key = "github_deploy_key_ed25519";
owner = "root";
group = "root";
mode = "0400";
};
};
};
}
And finally use the password-hash for your hashedPasswordFile
for your user,
my user is jr
so I added this:
# ... snip ...
users.users = {
# ${username} = {
jr = {
homeMode = "755";
isNormalUser = true;
# description = userVars.gitUsername;
hashedPasswordFile = config.sops.secrets.password_hash.path;
# ...snip...
- Rebuild your configuration and you should see something like this:
sops-install-secrets: Imported /etc/ssh/ssh_host_ed25519_key as age key with fingerprint age1smamkzrwpdxw63hrxxcq8kmejsm4olknsrg72vd0qtfpmlzlvnf8uws38mzuj
If all goes well you should now have a more reproducible setup that doesn't rely on file paths for your secrets.