From e6866b54e65484901945a11b5660e7aa61bbaf75 Mon Sep 17 00:00:00 2001 From: virchau13 Date: Sun, 21 Nov 2021 02:12:22 +0800 Subject: [PATCH] docs: make README.md clearer This makes several changes to the README to make reading it clearer. - General grammar, capitalization, and punctuation fixes. - Change the usage example into collapsible sections so the README is navigable. - Merge steps 2a/2b and steps 3a/3b into steps 2 and 3, since they share a lot in common. - Use age examples for .sops.yaml, instead of just GPG fingerprints. - Make sure there is only one consistent example throughout the entirety of the usage example. - Make the age/GPG/SSH trichotomy less confusing. - Adds a source for the "GnuPG is not great software" claim. --- README.md | 480 +++++++++++++++++++++++++++--------------------------- 1 file changed, 242 insertions(+), 238 deletions(-) diff --git a/README.md b/README.md index 5da95fc..c2c333f 100644 --- a/README.md +++ b/README.md @@ -2,49 +2,50 @@ ![sops-nix logo](https://github.com/Mic92/sops-nix/releases/download/assets/logo.gif "Logo of sops-nix") -Atomic secret provisioning for NixOS based on [sops](https://github.com/mozilla/sops). +Atomic, declarative, and reproducible secret provisioning for NixOS based on [sops](https://github.com/mozilla/sops). ## How it works -Sops-nix decrypts secrets [sops files](https://github.com/mozilla/sops#2usage) -on the target machine to files specified in the NixOS configuration at -activation time. It also adjusts file permissions/owner/group. It uses either -age or GPG keys for decryption, where both types can be derived from ssh host -keys. In future we will also support cloud key management APIs such as AWS -KMS, GCP KMS, Azure Key Vault or Hashicorp's vault. +Secrets are decrypted from [`sops` files](https://github.com/mozilla/sops#2usage) during +activation time. The secrets are access-controlled by by full declarative configuration of their users, permissions, and groups. +It can use `age`, GPG, or SSH keys for decryption. +In the future, we will also support cloud key management APIs such as AWS +KMS, GCP KMS, Azure Key Vault and Hashicorp Vault. ## Features -- Compatible with all NixOS deployment frameworks: [NixOps](https://github.com/NixOS/nixops), nixos-rebuild, [krops](https://github.com/krebs/krops/), [morph](https://github.com/DBCDK/morph), [nixus](https://github.com/Infinisil/nixus) -- Version-control friendly: Since all files are encrypted they can directly committed to version control. The format is readable in diffs and there are also ways of showing [git diffs in cleartext](https://github.com/mozilla/sops#showing-diffs-in-cleartext-in-git) -- Works well in teams: sops-nix comes with nix-shell hooks that allows quickly import multiple people to import all used keys. +- Compatible with all NixOS deployment frameworks: [NixOps](https://github.com/NixOS/nixops), nixos-rebuild, [krops](https://github.com/krebs/krops/), [morph](https://github.com/DBCDK/morph), [nixus](https://github.com/Infinisil/nixus), etc. +- Version-control friendly: Since all files are encrypted they can directly committed to version control without worry. Diffs of the secrets are readable, and [can be shown in cleartext](https://github.com/mozilla/sops#showing-diffs-in-cleartext-in-git). +- CI friendly: Since sops files can be added to the Nix store without leaking secrets, a machine definition can be built as a whole from a repository, without needing to rely on external secrets or services. +- Works well in teams: sops-nix comes with `nix-shell` hooks that allows multiple people to quickly import all keys. The cryptography used in sops is designed to be scalable: Secrets are only encrypted once with a master key instead of each machine/developer key. -- CI friendly: Since sops files can be added to the Nix store without leaking secrets, a machine definition can be built as a whole. -- Atomic upgrades: New secrets are written to a new directory which replaces the old directory in an atomic step. -- Rollback support: If sops files are added to Nix store, old secrets can be rolled back. This is optional. -- Fast: Unlike solutions implemented by NixOps, krops and morph there is no extra step required to upload secrets -- Different storage formats: Secrets can be stored in YAML, JSON or binary. -- Minimize configuration errors: sops files are checked against the configuration at evaluation time. +- Atomic upgrades: New secrets are written to a new directory which replaces the old directory atomically. +- Rollback support: If sops files are added to the Nix store, old secrets can be rolled back. This is optional. +- Fast time-to-deploy: Unlike solutions implemented by NixOps, krops and morph, no extra steps are required to upload secrets. +- A variety of storage formats: Secrets can be stored in YAML, JSON or binary. +- Minimizes configuration errors: sops files are checked against the configuration at evaluation time. ## Demo -There is a configuration.nix example in the [deployment step](#5-deploy) of our usage example. +There is a `configuration.nix` example in the [deployment step](#deploy-example) of our usage example. ## Supported encryption methods -sops-nix supports two basic ways of encryption, gnupg and age. Gnupg is based -on gnupg (duh) and encrypts against gnupg public keys. Private gnupg keys may -be used to decrypt the secrets on the target machine. The tool `ssh-to-pgp` can -be used to derive a gnupg key from a ssh (host) key in RSA format. +sops-nix supports two basic ways of encryption, GPG and age. + +GPG is based on [GnuPG](https://gnupg.org/) and encrypts against GPG public keys. Private GPG keys may +be used to decrypt the secrets on the target machine. The tool [`ssh-to-pgp`](https://github.com/Mic92/ssh-to-pgp) can +be used to derive a GPG key from a SSH (host) key in RSA format. The other method is age which is based on [age](https://github.com/FiloSottile/age). -A tool is provided with sops-nix that can convert ssh host or user keys in ed25519 +The tool ([`ssh-to-age`](https://github.com/Mic92/ssh-to-age)) can convert SSH host or user keys in Ed25519 format to age keys. ## Usage example -### 1. Install nix-sops +
+1. Install nix-sops Choose one of the following methods: @@ -55,7 +56,7 @@ Choose one of the following methods: $ niv add Mic92/sops-nix ``` - Then add the following to your configuration.nix in the `imports` list: + Then add the following to your `configuration.nix` in the `imports` list: ```nix { @@ -72,7 +73,7 @@ $ nix-channel --add https://github.com/Mic92/sops-nix/archive/master.tar.gz sops $ nix-channel --update ``` - Then add the following to your configuration.nix in the `imports` list: + Then add the following to your `configuration.nix` in the `imports` list: ```nix { @@ -82,7 +83,7 @@ $ nix-channel --update #### fetchTarball - Add the following to your configuration.nix: + Add the following to your `configuration.nix`: ``` nix { @@ -120,7 +121,7 @@ If you use experimental nix flakes support: outputs = { self, nixpkgs, sops-nix }: { # change `yourhostname` to your actual hostname nixosConfigurations.yourhostname = nixpkgs.lib.nixosSystem { - # change to your system: + # customize to your system system = "x86_64-linux"; modules = [ ./configuration.nix @@ -131,14 +132,28 @@ If you use experimental nix flakes support: } ``` -### 2a. Generate a GPG key for yourself +
-This is only needed when you plan to use the gnupg encryption. -When using age, you can skip to step 2b instead. +
+2. Generate a key for yourself -First generate yourself [a GPG key](https://docs.github.com/en/github/authenticating-to-github/generating-a-new-gpg-key) or use nix-sops -conversion tool to convert an existing ssh key (we only support RSA keys right now): +This key will be used for you to edit secrets. +You can generate yourself a key: +```console +# for age +$ mkdir -p ~/.config/sops/age +$ age-keygen -o ~/.config/sops/age/keys.txt +# to convert an ssh ed25519 key to an age key +$ 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" +# for GPG >= version 2.1.17 +$ gpg --full-generate-key +# for GPG < 2.1.17 +$ gpg --default-new-key-algo rsa4096 --gen-key +``` + +Or you can use the `ssh-to-pgp` tool to get a GPG key from an SSH key: ```console $ nix-shell -p gnupg -p ssh-to-pgp --run "ssh-to-pgp -private-key -i $HOME/.ssh/id_rsa | gpg --import --quiet" 2504791468b153b8a3963cc97ba53d1919c5dfd4 @@ -146,39 +161,57 @@ $ nix-shell -p gnupg -p ssh-to-pgp --run "ssh-to-pgp -private-key -i $HOME/.ssh/ $ nix-shell -p ssh-to-pgp --run "ssh-to-pgp -i $HOME/.ssh/id_rsa -o $USER.asc" 2504791468b153b8a3963cc97ba53d1919c5dfd4 ``` - -If you get: - +(Note that `ssh-to-pgp` currently only supports RSA keys.) +If you get the following, ```console ssh-to-pgp: failed to parse private ssh key: ssh: this private key is passphrase protected ``` - -then your ssh key is encrypted with your password and you need to create an unencrypted copy temporarily: - +then your SSH key is encrypted with your password and you will need to create an unencrypted copy temporarily. ```console $ cp $HOME/.ssh/id_rsa /tmp/id_rsa $ ssh-keygen -p -N "" -f /tmp/id_rsa $ nix-shell -p gnupg -p ssh-to-pgp --run "ssh-to-pgp -private-key -i /tmp/id_rsa | gpg --import --quiet" +$ rm /tmp/id_rsa ``` -The hex string printed here is your GPG fingerprint that can written to your [`.sops.yaml`](https://github.com/mozilla/sops#using-sops-yaml-conf-to-select-kms-pgp-for-new-files) in the root of your configuration directory or repository. +You can also use your existing SSH key as an age key. -```yaml -# This example uses yaml anchors which allows to name keys -# and re-use for multiple keys in a flexible way. -# Also see https://github.com/Mic92/dotfiles/blob/master/nixos/.sops.yaml -# for a more complex example -keys: - - &admin 2504791468b153b8a3963cc97ba53d1919c5dfd4 -creation_rules: - - path_regex: secrets/[^/]+\.yaml$ - key_groups: - - pgp: - - *admin +
+ How to find the age public key + +If you generated an age key, the age public key can be found via `age-keygen -y $PATH_TO_KEY`: +```console +$ age-keygen -y ~/.config/sops/age/keys.txt +age12zlz6lvcdk6eqaewfylg35w0syh58sm7gh53q5vvn7hd7c6nngyseftjxl ``` -If you have generated a GnuPG key directly you can get your fingerprint like this: +Otherwise, you can convert an existing SSH key into an age public key: +```console +$ nix-shell -p ssh-to-age --run "ssh-to-age < ~/.ssh/id_ed25519.pub" +# or +$ nix-shell -p ssh-to-age --run "ssh-add -L | ssh-to-age" +``` +If you get this, + +```console +failed to parse ssh private key: ssh: this private key is passphrase protected +``` + +then your SSH key is encrypted with your password and you need to create an unencrypted copy temporarily: + +```console +$ cp $HOME/.ssh/id_ed25519 /tmp/id_ed25519 +$ ssh-keygen -p -N "" -f /tmp/id_ed25519 +$ nix-shell -p ssh-to-age --run "ssh-to-age -private-key -i /tmp/id_ed25519 > ~/.config/sops/age/keys.txt" +``` + +
+ +
+ How to find the GPG fingerprint of a key + +Invoke this command and look for your key: ```console $ gpg --list-secret-keys /tmp/tmp.JA07D1aVRD/pubring.kbx @@ -189,103 +222,95 @@ uid [ unknown] root ``` The fingerprint here is `9F89C5F69A10281A835014B09C3DC61F752087EF`. +
- -### 2b. Generate a SSH and age key for yourself - -This is only needed when you plan to use the age encryption. -When using gnupg, you need to go back to step 2a. - -sops-nix in age mode requires you to have an age key. -You can generate one like this: - -``` console -$ mkdir -p ~/.config/sops/age -$ age-keygen -o ~/.config/sops/age/keys.txt +Your age public key or GPG fingerprint can written to your [`.sops.yaml`](https://github.com/mozilla/sops#using-sops-YAML-conf-to-select-kms-pgp-for-new-files) in the root of your configuration directory or repository: +```yaml +# This example uses YAML anchors which allows reuse of multiple keys +# without having to repeat yourself. +# Also see https://github.com/Mic92/dotfiles/blob/master/nixos/.sops.yaml +# for a more complex example. +keys: + - &admin_alice 2504791468b153b8a3963cc97ba53d1919c5dfd4 + - &admin_bob age12zlz6lvcdk6eqaewfylg35w0syh58sm7gh53q5vvn7hd7c6nngyseftjxl +creation_rules: + - path_regex: secrets/[^/]+\.yaml$ + key_groups: + - pgp: + - *admin_alice + - age: + - *admin_bob ``` -If you have an ssh key in `ed25519` format (i.e. if it was generated `ssh-keygen -t ed25519`) -you can also convert to an age key: +
+
+3. Get a public key for your target machine + +The easiest way to add new machines is using SSH host keys (this requires OpenSSH to be enabled). + +If you are using age, the `ssh-to-age` tool can be used to convert any SSH public key to the age format: ```console -$ 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" +$ nix-shell -p ssh-to-age --run 'ssh-keyscan my-server.com | ssh-to-age' +age1rgffpespcyjn0d8jglk7km9kfrfhdyev6camd3rck6pn8y47ze4sug23v3 +$ nix-shell -p ssh-to-age --run 'cat /etc/ssh/ssh_host_ed25519_key.pub | ssh-to-age' +age1rgffpespcyjn0d8jglk7km9kfrfhdyev6camd3rck6pn8y47ze4sug23v3 ``` -Converting the public key to the age format works like this: -```console -$ nix-shell -p ssh-to-age --run " ssh-to-age < ~/.ssh/id_ed25519.pub " -``` - -or like this - -``` console -$ nix-shell -p ssh-to-age --run "ssh-add -L | ssh-to-age" -``` - -If you get: - -```console -failed to parse ssh private key: ssh: this private key is passphrase protected -``` - -then your ssh key is encrypted with your password and you need to create an unencrypted copy temporarily: - -```console -$ cp $HOME/.ssh/id_ed25519 /tmp/id_ed25519 -$ ssh-keygen -p -N "" -f /tmp/id_ed25519 -$ nix-shell -p ssh-to-age --run "ssh-to-age -private-key -i /tmp/id_ed25519 > ~/.config/sops/age/keys.txt" -``` - -### 3a. Get a PGP Public key for your machine - -The easiest way to add new hosts is using ssh host keys (requires openssh to be enabled). -Since sops does not natively supports ssh keys yet, nix-sops supports a conversion tool -to store them as gpg keys. +For GPG, since sops does not natively support SSH keys yet, nix-sops supports a conversion tool (`ssh-to-pgp`) +to store them as GPG keys: ```console $ ssh root@server01 "cat /etc/ssh/ssh_host_rsa_key" | nix-shell -p ssh-to-pgp --run "ssh-to-pgp -o server01.asc" # or with sudo $ ssh youruser@server01 "sudo cat /etc/ssh/ssh_host_rsa_key" | nix-shell -p ssh-to-pgp --run "ssh-to-pgp -o server01.asc" 0fd60c8c3b664aceb1796ce02b318df330331003 -# Or just read them locally (or in a ssh session) +# or just read them locally/over ssh $ nix-shell -p ssh-to-pgp --run "ssh-to-pgp -i /etc/ssh/ssh_host_rsa_key -o server01.asc" 0fd60c8c3b664aceb1796ce02b318df330331003 ``` -Also the hex string here is the fingerprint of your server's gpg key that can be exported append to `.sops.yaml`: +The output of these commands is the identifier for the server's key, which can be added to your `.sops.yaml`: ```yaml keys: - - &admin 2504791468b153b8a3963cc97ba53d1919c5dfd4 - - &server 0fd60c8c3b664aceb1796ce02b318df330331003 + - &admin_alice 2504791468b153b8a3963cc97ba53d1919c5dfd4 + - &admin_bob age12zlz6lvcdk6eqaewfylg35w0syh58sm7gh53q5vvn7hd7c6nngyseftjxl + - &server_azmidi 0fd60c8c3b664aceb1796ce02b318df330331003 + - &server_nosaxa age1rgffpespcyjn0d8jglk7km9kfrfhdyev6camd3rck6pn8y47ze4sug23v3 creation_rules: - path_regex: secrets/[^/]+\.yaml$ key_groups: - pgp: - - *admin - - *server + - *admin_alice + - *server_azmidi + - age: + - *admin_bob + - *server_nosaxa + - path_regex: secrets/azmidi/[^/]+\.yaml$ + key_groups: + - pgp: + - *admin_alice + - *server_azmidi + - age: + - *admin_bob ``` -If you prefer having a separate GnuPG key, see [Use with GnuPG instead of ssh keys](#use-with-gnupg-instead-of-ssh-keys). +If you prefer having a separate GPG key, see [Use with GPG instead of SSH keys](#use-with-GPG-instead-of-SSH-keys). -### 3b. Get a age Public key for your machine +
-The `ssh-to-age` tool is used to convert any ssh public key to the age format. -This way you can convert any key: -```console -$ nix-shell -p ssh-to-age --run 'ssh-keyscan my-server.com | ssh-to-age' -$ nix-shell -p ssh-to-age --run 'cat /etc/ssh/ssh_host_ed25519_key.pub | ssh-to-age' -``` - -### 4. Create a sops file +
+4. Create a sops file To create a sops file you need write a `.sops.yaml` as described above. -When using gnupg you also need to import your personal gpg key -(and your colleagues) and your servers into your gpg key chain. -sops-nix automates importing gpg keys with a hook for nix-shell allowing public -keys to be shared via version control (i.e. git): +When using GnuPG you also need to import your personal GPG key +(and your colleagues) and your servers into your GPG key chain. + +
+sops-nix can automate the import of GPG keys with a hook for nix-shell, allowing public +keys to be shared via version control (i.e. git). ```nix # shell.nix @@ -319,7 +344,7 @@ mkShell { # $ (unset GNUPGHOME; gpg --armor --export-secret-key 0000000000000000000000000000000000000000) | gpg --import #sopsCreateGPGHome = true; # To use a different directory for gpg dirs set sopsGPGHome - #sopsGPGHome = "${toString ./.}/../gnupg"; + #sopsGPGHome = "${toString ./.}/../GnuPG"; nativeBuildInputs = [ (pkgs.callPackage sops-nix {}).sops-import-keys-hook @@ -327,7 +352,7 @@ mkShell { } ``` -Our directory structure looks like this: +A valid directory structure for this might look like: ```console $ tree . @@ -339,134 +364,111 @@ $ tree . │   └── mic92.asc ``` -After that you can open a new file with sops +
+ +After configuring `.sops.yaml`, you can open a new file with sops: ```console -$ nix-shell -p sops --run "sops secrets.yaml" +$ nix-shell -p sops --run "sops secrets/example.yaml" ``` -This will start your configured editor -In our example we put the following content in it: - -``` -example-key: example-value -``` - -Nesting the key results in the creation of directories. -These directories will be owned by root:keys and have permissions 0751. +This will start your configured editor located at the `$EDITOR` environment variable. +An example secret file might be: ```yaml +# Files must always have a string value +example-key: example-value +# Nesting the key results in the creation of directories. +# These directories will be owned by root:keys and have permissions 0751. myservice: my_subdir: - my_secret: example value + my_secret: password1 ``` -As a result when saving the file the following content will be in it: +An example result when saving this file could be: ``` -example-key: ENC[AES256_GCM,data:7QIOMLd2kZkeVVpH0Q==,iv:ROh+J59ZM6BtjZLhRj1Ylk6ROEvsiX6/UR8obHX8YcQ=,tag:QOiFoHKyGFBkhr9lcWBB3Q==,type:str] +example-key: ENC[AES256_GCM,data:AB8XMyid4P7mXdjj+A==,iv:RRsZC+V+3w22pOi/2TCjBYn/0OYsNGCu5CT1ZBSKGi0=,tag:zT5mlujrSuA6KKxLKL8CMQ==,type:str] +#ENC[AES256_GCM,data:59QWbzCQCP7kLdhyjFOZe503MgegN0kv505PBNHwjp6aYztDHwx2N9+A1Bz6G/vWYo+4LpBo8/s=,iv:89q3ZXgM1wBUg5G29ROor3VXrO3QFGCvfwDoA3+G14M=,tag:hOSnEZ6DKycnF37LCXOjzg==,type:comment] +#ENC[AES256_GCM,data:kUuJCkDE9JT9C+kdNe0CSB3c+gmgE4We1OoX4C1dWeoZCw/o9/09CzjRi9eOBUEL0P1lrt+g6V2uXFVq4n+M8UPGUAbRUr3A,iv:nXJS8wqi+ephoLynm9Nxbqan0V5dBstctqP0WxniSOw=,tag:ALx396Z/IPCwnlqH//Hj3g==,type:comment] +myservice: + my_subdir: + my_secret: ENC[AES256_GCM,data:hcRk5ERw60G5,iv:3Ur6iH1Yu0eu2otcEv+hGRF5kTaH6HSlrofJ5JXvewA=,tag:hpECXFnMhGNnAxxzuGW5jg==,type:str] sops: kms: [] gcp_kms: [] azure_kv: [] - lastmodified: '2020-07-13T09:09:14Z' - mac: ENC[AES256_GCM,data:BCwTBxaW6qINVfixC32EEYrlqPvGz47wF+o/vNPqcwed1HPwZezlNy7Z4NFLbRcCLAELyeMqkJ+fi9XCWvnT3UvfwB45COpz/xZphURt3gyCVOyd9mT/s9cJ1O9vNy5iKblqCae2X0CTKee/GxJ0G725LDOL4r+oHM1+WWEInWo=,iv:S43qegidSqcaUaDjvQpEQj/qvF/OZcW32Yo05CfyTUs=,tag:npj5auJXZrg7jQwYSjC6Vg==,type:str] - pgp: - - created_at: '2020-07-13T08:34:30Z' - enc: | - -----BEGIN PGP MESSAGE----- - - hQIMAysxjfMwMxADAQ//SyBLvbpyuoTGCZCtoJyaFzZ+vCKWZaD7dCZEURRyNKFV - 87wZyNO/rwtA1jP64Smqy0q2R8iZfoN0v5oVvtj2y5wFECs8Q5nONCVP4rs9nTRK - n46w0v2UE2GqIWStFE7Mpv11qdZaMDoNGXq+n6s/uA2mwSYIVvzcWwhKvyKrMNrd - iOlfCKl4QTaGgGupZqmT2S00AEMJzY5lohvtzAC1TlnXGXhetDyCHtkoN/NKZDU7 - m7j1/pvlIwxTQKeA3FKuxDJDYk+p3+W/EgwEchYDzjo+5A529J/tuIfXWBOF7BAV - ZiVVWISTahky/ioOMatNBAttu0lBGlSkovkbqIVsbTG7nF1wzGdToCxZmwQveEj7 - 0N8ZzocDkOXqS71LW+X2HYSeywxNUbg/S6MrHrZN8MOp5qnGztm8yrKW2gDDe+Nl - nqJJ4lGg5CbODoDmhbPPof9tmWkykFmQSqmkjs4pcomcNthmcQvPVy75pnXEN9Wo - 0cDRnHtgROCJLqfv1AsXWkSxtmZRMMQ1yKJIPVFUHSPodgAoTyA81sHi66RypDOV - KezX6sW8UuTZ7q1oPcJFpaaHrpIHDn+bqPGMfhu4NVXFusdb7MPxtxlKflhTdc8B - xzlrB6+LdnCaeN+KqB6DOvmiPP3nC91zflO1SpMY3yUOnTFDKZG7wnVjidyIuMvS - UAHk6rhsBEJleAn5f4AuBVWtWLuvS4t1g9Lhci3833f7XNp+GFNy05UOsmUo9upr - cgqaa2teuy2cbUtzS6gLBbcMA7SEs5MDYHjq6le/pwKv - =ZYPM - -----END PGP MESSAGE----- - fp: 0FD60C8C3B664ACEB1796CE02B318DF330331003 - - created_at: '2020-07-13T08:34:30Z' - enc: | - -----BEGIN PGP MESSAGE----- - - hQIMAysxjfMwMxADARAAqbkG7+WZIDDHNjFp4mcabdGcKaTenJmAQKJjk4vnAWZD - 5Y6yInTldxldsFNvPcVmjZp/nM1otyH0MEHrurl5LX+BuUj8hRIE0ZFnNU0hNmyd - toiwTE4GF1/otYFOPb9WnhDt+g6Y0ORuV/ZMSvP8PIu5/UnTeCkbZR/VudOvUq/m - qF013M3q7UKssW4aReO2goFEhLjm8GfWksCuiGYKoHdJKzFAPYNhoxnxU3n43Oxp - wz7QYFI0aA7RLZph70WjUNBun5+y4UyEJ8uNZ+cgVBeHQLqVdFUuejdzWK0d79Mr - 5D9fxgSsPMz7yUMMdPl0T4rrAsZ977pftI9+JofqMN+u9UzUJwfTjnbCxlob39/t - bfORkanzU8BNUCxpHyyqau921AUtfcqV9Y9Hf+qwxgVRVKgfETOqN376A1nhrYsf - Mhvmcsk/rDssiRSIu11/mZwifcpALnS8WgO5tK+e/454ANqsiEdSRVogWBTzcIIs - trm/6kwsTl7COzK0ThUKIb6aOfb910JQKaYq93qWqF1fceIf49Ubz9NVZc80J0an - OiAaVGS0IOGI1ua8zciY7m+rr1BlrqJFtUm7hd8C9fMaF8YdB2SXgW8/HPGL8uTd - f9ASg9TMSxhr7wjdqWp4EXXxdB6p4FXai9XBbgAJ2tKcS6AV6QmRVMoITZ7uZpvS - UAG4nIgey9A57C8DSnt5zVPtxAsjDNiMubLUnHzTEJEJyQH5j2E41teujycOOAye - I/UHMfpxSgrFfS8JJHYrJO0JQq/maBZi/VzZCl/G3IMn - =Xls9 - -----END PGP MESSAGE----- - fp: 0FD60C8C3B664ACEB1796CE02B318DF330331003 - - created_at: '2020-07-13T08:34:30Z' - enc: | - -----BEGIN PGP MESSAGE----- - - hQEMA5w9xh91IIfvAQf+I1FDo7rglcA6EF7jmQ0pq9FwYR/Dd9+4pu4mxUofQawj - YsXPToVvyOKFrs1BZzW3Idyn5U/oXnkPN0qNK30DKir/wCt9OBqHHuhlo80OR2nS - G2ZvHOJKEW3W5Hs2yT1e1MQxznI1lGFrsj6xgZAnKtK3Y6iy48XZ9pTw4Fxjkixw - NppHtYrMj30mwV9XFAer0EfGlV2AIi70xBZ2inYAzPU2SpLEEoGyztjIeSS4VfhQ - fnKSx3UjlVIix65s2ky0JqbL1wI+FPKNt2hWupW+M7en8BJ5VfAcbU7n0ZuQnaFx - YPErw3agfhw1bNnqXh0y5aZ9sswt/Jy+IRkMJHLcqNJQAREdKgGmkW8wO2dngYYL - IwLyChHJfcSnixboVcW5CIbfmIbOdgfEk2tdSiX1tJIA6qeeJz+D8UbR47nIdIw2 - ZoID5dEUiDgikopjdqWk+zk= - =43hf - -----END PGP MESSAGE----- - fp: 9F89C5F69A10281A835014B09C3DC61F752087EF + hc_vault: [] + age: + - recipient: age12zlz6lvcdk6eqaewfylg35w0syh58sm7gh53q5vvn7hd7c6nngyseftjxl + enc: | + -----BEGIN AGE ENCRYPTED FILE----- + YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB1dFYvSTRHa3IwTVpuZjEz + SDZZQnc5a0dGVGEzNXZmNEY5NlZDbVgyNVU0Clo3ZC9MRGp4SHhLUTVCeWlOUUxS + MEtPdW4rUHhjdFB6bFhyUXRQTkRpWjAKLS0tIDVTbWU2V3dJNUZrK1A5U0c5bkc0 + S3VINUJYc3VKcjBZbHVqcGJBSlVPZWcKqPXE01ienWDbTwxo+z4dNAizR3t6uTS+ + KbmSOK1v61Ri0bsM5HItiMP+fE3VCyhqMBmPdcrR92+3oBmiSFnXPA== + -----END AGE ENCRYPTED FILE----- + - recipient: age18jtffqax5v0t6ehh4ypaefl4mfhcrhn6ek3p80mhfp9psx6pd35qew2ww3 + enc: | + -----BEGIN AGE ENCRYPTED FILE----- + YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBzT3FxcDEzaFRQOVFpNkg2 + Skw4WEIxZzNTWkNBaDRhcUN2ejY4QTAwTERvCkx2clIzT2wyaFJZcjl0RkFXL2p6 + enhqVEZ3ZkNKUU5jTlUxRC9Lb090TzAKLS0tIDBEaG00RFJDZ3ZVVjBGUWJkRHdQ + YkpudG43eURPVWJUejd3Znk5Z29lWlkK0cIngn2qdmiOE5rHOHxTRcjfZYuY3Ej7 + Yy7nYxMwTdYsm/V6Lp2xm8hvSzBEIFL+JXnSTSwSHnCIfgle5BRbug== + -----END AGE ENCRYPTED FILE----- + lastmodified: "2021-11-20T16:21:10Z" + mac: ENC[AES256_GCM,data:5ieT/yv1GZfZFr+OAZ/DBF+6DJHijRXpjNI2kfBun3KxDkyjiu/OFmAbsoVFY/y6YCT3ofl4Vwa56Veo3iYj4njgxyLpLuD1B6zkMaNXaPywbAhuMho7bDGEJZHrlYOUNLdBqW2ytTuFA095IncXE8CFGr38A2hfjcputdHk4R4=,iv:UcBXWtaquflQFNDphZUqahADkeege5OjUY38pLIcFkU=,tag:yy+HSMm+xtX+vHO78nej5w==,type:str] + pgp: [] unencrypted_suffix: _unencrypted - version: 3.5.0 + version: 3.7.1 ``` -### 5. Deploy +
-If you derived your server public key from ssh, all you need in your configuration.nix is: +
+5. Deploy + +If you derived your server public key from SSH, all you need in your `configuration.nix` is: ```nix { imports = [ ]; # This will add secrets.yml to the nix store # You can avoid this by adding a string to the full path instead, i.e. - # sops.defaultSopsFile = "/root/.sops/secrets.yaml"; - sops.defaultSopsFile = ./secrets.yaml; - sops.secrets.example-key = {}; - # This is using ssh keys in the age format: + # sops.defaultSopsFile = "/root/.sops/secrets/example.yaml"; + sops.defaultSopsFile = ./secrets/example.yaml; + # This will automatically import SSH keys as age keys sops.age.sshKeyPaths = [ "/etc/ssh/ssh_host_ed25519_key" ]; # This is using an age key that is expected to already be in the filesystem sops.age.keyFile = "/var/lib/sops-nix/key.txt"; # This will generate a new key if the key specified above does not exist sops.age.generateKey = true; + # This is the actual specification of the secrets. + sops.secrets.example-key = {}; sops.secrets."myservice/my_subdir/my_secret" = {}; } ``` -On `nixos-rebuild switch` this will make the key accessible -via `/run/secrets/example-key`: +On `nixos-rebuild switch` this will make the keys accessible +via `/run/secrets/example-key` and `/run/secrets/myservice/my_subdir/my_secret`: ```console $ cat /run/secrets/example-key example-value +$ cat /run/secrets/myservice/my_subdir/my_secret +password1 ``` -`/run/secrets` is a symlink to `/etc/secret.d/1`: +`/run/secrets` is a symlink to `/run/secrets.d/{number}`: ```console $ ls -la /run/secrets lrwxrwxrwx 16 root 12 Jul 6:23  /run/secrets -> /run/secrets.d/1 ``` +
+ ## Set secret permission/owner and allow services to access it By default secrets are owned by `root:root`. Furthermore @@ -476,9 +478,9 @@ the parent directory `/run/secrets.d` is only owned by ``` console $ ls -la /run/secrets.d/1 total 24 -drwxr-x--- 2 root keys 0 Jul 18 15:35 . -drwxr-x--- 3 root keys 0 Jul 18 15:35 .. --r-------- 1 root root 20 Jul 18 15:35 borgbackup +drwxr-x--- 2 root keys 0 Jul 12 6:23 . +drwxr-x--- 3 root keys 0 Jul 12 6:23 .. +-r-------- 1 root root 20 Jul 12 6:23 example-secret ``` The secrets option has further parameter to change secret permission. @@ -486,8 +488,8 @@ Consider the following nixos configuration example: ```nix { - # Permission modes are in octal representation, - # the digits reprsent: user|group|owner + # Permission modes are in octal representation (same as chmod), + # the digits represent: user|group|owner # 7 - full (rwx) # 6 - read and write (rw-) # 5 - read and execute (r-x) @@ -525,8 +527,9 @@ For login or system users this can be done like this: } ``` -The following example configures secrets for buildkite, a CI agent -the service needs a token and a ssh private key to function: +
+This example configures secrets for buildkite, a CI agent; +the service needs a token and a SSH private key to function. ```nix { pkgs, config, ... }: @@ -555,7 +558,9 @@ the service needs a token and a ssh private key to function: } ``` -## Restarting/reloading systemd units +
+ +## Restarting/reloading systemd units on secret change **With NixOS 21.11**, it is possible to restart or reload units when a secret changes or is newly initialized. This behaviour can be configured per-secret: @@ -591,10 +596,10 @@ lrwxrwxrwx 1 root root 40 Jul 19 22:36 /var/lib/hass/secrets.yaml -> /run/secret ## Setting a user's password -sops-nix has to run after users were created by NixOS. +sops-nix has to run after NixOS creates users (in order to specify what users own a secret.) This means that it's not possible to set `users.users..passwordFile` to any secrets managed by sops-nix. To work around this issue, it's possible to set `neededForUsers = true` in a secret. -This will cause the secret to be decrypted to `/run/secrets-for-users` instead of `/run/secrets` before NixOS creates the users. +This will cause the secret to be decrypted to `/run/secrets-for-users` instead of `/run/secrets` before NixOS creates users. As users are not created yet, it's not possible to set an owner for these secrets. ```nix @@ -610,12 +615,12 @@ As users are not created yet, it's not possible to set an owner for these secret ## Different file formats -At the moment we support the following file formats: YAML, JSON, binary +At the moment we support the following file formats: YAML, JSON, and binary. NOTE: At the moment we do not support nested data structures that -sops support. This might change in the future: +sops support. This might change in the future. -We support the following yaml: +We support the following YAML: ```yaml key: 1 @@ -635,8 +640,8 @@ nix-sops allows to specify multiple sops files in different file formats: imports = [ ]; # The default sops file used for all secrets can be controlled using `sops.defaultSopsFile` sops.defaultSopsFile = ./secrets.yaml; - # If you use something different from yaml, you can also specify it here: - #sops.defaultSopsFormat = "yaml"; + # If you use something different from YAML, you can also specify it here: + #sops.defaultSopsFormat = "YAML"; sops.secrets.github_token = { # The sops file can be also overwritten per secret... sopsFile = ./other-secrets.json; @@ -658,7 +663,7 @@ Than put in the following content: ```yaml github_token: 4a6c73f74928a9c4c4bc47379256b72e598e2bd3 -# multi-line strings in yaml start with an | +# multi-line strings in YAML start with an | ssh_key: | -----BEGIN OPENSSH PRIVATE KEY----- b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW @@ -674,10 +679,10 @@ You can include it like this in your `configuration.nix`: ```nix { sops.defaultSopsFile = ./secrets.yaml; - # yaml is the default - #sops.defaultSopsFormat = "yaml"; + # YAML is the default + #sops.defaultSopsFormat = "YAML"; sops.secrets.github_token = { - format = "yaml"; + format = "YAML"; # can be also set per secret sopsFile = ./secrets.yaml; }; @@ -706,7 +711,7 @@ You can include it like this in your `configuration.nix`: ```nix { sops.defaultSopsFile = ./secrets.json; - # yaml is the default + # YAML is the default sops.defaultSopsFormat = "json"; sops.secrets.github_token = { format = "json"; @@ -746,7 +751,7 @@ It can be decrypted again like this: $ sops -d krb5.keytab > /tmp/krb5.keytab ``` -This is how it can be included in your configuration.nix: +This is how it can be included in your `configuration.nix`: ```nix { @@ -757,9 +762,9 @@ This is how it can be included in your configuration.nix: } ``` -## Use with GnuPG instead of ssh keys +## Use with GPG instead of SSH keys -If you prefer having a separate GnuPG key, sops-nix also comes with a helper tool: +If you prefer having a separate GPG key, sops-nix also comes with a helper tool, `sops-init-gpg-key`: ```console $ nix-shell -p sops-init-gpg-key @@ -788,22 +793,22 @@ EOF fingerprint: E4CA86768F176AEB6C01554153AF8D7F149613B1 ``` -In this case you need to make upload the gpg key directory `/tmp/newkey` to your server. +In this case, you must upload the GPG key directory `/tmp/newkey` onto the server. If you uploaded it to `/var/lib/sops` than your sops configuration will look like this: ```nix { # Make sure that `/var/lib/sops` is owned by root and is not world-readable/writable sops.gnupg.home = "/var/lib/sops"; - # disable import host ssh keys + # disable importing host ssh keys sops.gnupg.sshKeyPaths = []; } ``` -However be aware that this will also run gnupg on your server including the -gnupg daemon. Gnupg is in general not great software and might break in +However be aware that this will also run GnuPG on your server including the +GnuPG daemon. [GnuPG is in general not great software](https://latacora.micro.blog/2019/07/16/the-pgp-problem.html) and might break in hilarious ways. If you experience problems, you are on your own. If you want a -more stable and predictable solution go with ssh keys or one of the KMS services. +more stable and predictable solution go with SSH keys or one of the KMS services. ## Share secrets between different users @@ -830,7 +835,7 @@ example the `drone` secret is exposed as `/run/secrets/drone-server` for If you have used [pass](https://www.passwordstore.org) before i.e. in [krops](https://github.com/krebs/krops) than you can use the following one-liner -to convert all your secrets to a yaml structure. +to convert all your secrets to a YAML structure: ```console $ for i in *.gpg; do echo "$(basename $i .gpg): |\n$(pass $(dirname $i)/$(basename $i .gpg)| sed 's/^/ /')"; done @@ -838,11 +843,10 @@ $ for i in *.gpg; do echo "$(basename $i .gpg): |\n$(pass $(dirname $i)/$(basena Copy the output to the editor you have opened with sops. -## Realworld Examples +## Real-world examples My [personal configuration](https://github.com/Mic92/dotfiles/tree/master/nixos) makes extensive usage of sops-nix. Each host has a [secrets](https://github.com/Mic92/dotfiles/tree/master/nixos/eve/secrets) directory containing secrets for the host. -You can also check out [nix-community infrastructure repository](https://github.com/nix-community/infra). ## Known limitations @@ -850,9 +854,9 @@ You can also check out [nix-community infrastructure repository](https://github. sops-nix does not fully support initrd secrets. This is because `nixos-rebuild switch` installs -the bootloader before running sops-nix activation hook. -At the moment it is be possible to run `nixos-rebuild test` -before `nixos-rebuild switch` to provision initrd secrets key -before the initrd secrets are built. -In future we hope to extend nixos to allow keys to be +the bootloader before running sops-nix's activation hook. +As a workaround, it is possible to run `nixos-rebuild test` +before `nixos-rebuild switch` to provision initrd secrets +before actually using them in the initrd. +In the future, we hope to extend NixOS to allow keys to be provisioned in the bootloader install phase.