1
0
Fork 0
mirror of https://github.com/Mic92/sops-nix.git synced 2024-12-14 11:57:52 +00:00
sops-nix/README.md

788 lines
26 KiB
Markdown
Raw Normal View History

2020-07-06 06:30:09 +00:00
# sops-nix
2021-08-29 14:04:06 +00:00
![sops-nix logo](https://github.com/Mic92/sops-nix/releases/download/assets/logo.gif "Logo of sops-nix")
2020-07-12 21:26:07 +00:00
Atomic secret provisioning for NixOS based on [sops](https://github.com/mozilla/sops).
2020-07-12 22:24:03 +00:00
## 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
2021-08-27 18:35:03 +00:00
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.
2020-07-12 22:24:03 +00:00
## Features
2020-07-14 10:52:22 +00:00
- 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)
2020-07-12 22:24:03 +00:00
- 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.
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.
2020-08-27 23:51:53 +00:00
- CI friendly: Since sops files can be added to the Nix store without leaking secrets, a machine definition can be built as a whole.
2020-07-12 22:24:03 +00:00
- 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
2020-07-19 18:31:45 +00:00
- 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.
2020-07-12 22:24:03 +00:00
## Demo
There is a configuration.nix example in the [deployment step](#5-deploy) of our usage example.
2021-08-27 18:35:03 +00:00
## 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.
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 to age keys.
2020-07-13 07:22:33 +00:00
## Usage example
### 1. Install nix-sops
2020-07-23 08:05:25 +00:00
Choose one of the following methods:
#### [niv](https://github.com/nmattia/niv) (Current recommendation)
First add it to niv:
2020-07-23 08:05:25 +00:00
```console
$ niv add Mic92/sops-nix
```
2021-01-26 12:58:19 +00:00
Then add the following to your configuration.nix in the `imports` list:
2020-07-23 08:05:25 +00:00
```nix
{
imports = [ "${(import ./nix/sources.nix).sops-nix}/modules/sops" ];
}
```
#### nix-channel
As root run:
2020-07-23 08:05:25 +00:00
```console
$ nix-channel --add https://github.com/Mic92/sops-nix/archive/master.tar.gz sops-nix
$ nix-channel --update
```
2021-01-26 12:58:19 +00:00
Then add the following to your configuration.nix in the `imports` list:
2020-07-23 08:05:25 +00:00
```nix
{
imports = [ <sops-nix/modules/sops> ];
}
```
2020-07-23 08:05:25 +00:00
#### fetchTarball
2020-07-19 22:24:44 +00:00
Add the following to your configuration.nix:
2020-07-23 08:05:25 +00:00
``` nix
{
imports = [ "${builtins.fetchTarball "https://github.com/Mic92/sops-nix/archive/master.tar.gz"}/modules/sops" ];
}
```
or with pinning:
2020-07-23 08:05:25 +00:00
```nix
{
imports = let
# replace this with an actual commit id or tag
commit = "298b235f664f925b433614dc33380f0662adfc3f";
in [
"${builtins.fetchTarball {
url = "https://github.com/Mic92/sops-nix/archive/${commit}.tar.gz";
# replace this with an actual hash
sha256 = "0000000000000000000000000000000000000000000000000000";
}}/modules/sops"
];
}
```
2020-07-23 08:05:25 +00:00
#### Flakes
If you use experimental nix flakes support:
``` nix
{
inputs.sops-nix.url = github:Mic92/sops-nix;
# optional, not necessary for the module
#inputs.sops-nix.inputs.nixpkgs.follows = "nixpkgs";
outputs = { self, nixpkgs, sops-nix }: {
# change `yourhostname` to your actual hostname
nixosConfigurations.yourhostname = nixpkgs.lib.nixosSystem {
# change to your system:
system = "x86_64-linux";
modules = [
./configuration.nix
sops-nix.nixosModules.sops
];
};
};
}
```
2021-08-27 18:35:03 +00:00
### 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.
2020-07-13 07:22:33 +00:00
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):
2021-02-01 11:12:20 +00:00
```console
$ nix-shell -p gnupg -p ssh-to-pgp --run "ssh-to-pgp -private-key -i $HOME/.ssh/id_rsa | gpg --import --quiet"
2020-07-13 08:12:47 +00:00
2504791468b153b8a3963cc97ba53d1919c5dfd4
# This exports the public key
2021-02-01 11:12:20 +00:00
$ nix-shell -p ssh-to-pgp --run "ssh-to-pgp -i $HOME/.ssh/id_rsa -o $USER.asc"
2020-07-13 07:22:33 +00:00
2504791468b153b8a3963cc97ba53d1919c5dfd4
```
If you get:
2021-02-01 11:12:20 +00:00
```console
2020-07-13 07:22:33 +00:00
ssh-to-pgp: failed to parse private ssh key: ssh: this private key is passphrase protected
```
2020-07-23 15:40:45 +00:00
then your ssh key is encrypted with your password and you need to create an unencrypted copy temporarily:
2020-07-13 07:22:33 +00:00
2021-02-01 11:12:20 +00:00
```console
2020-07-13 07:22:33 +00:00
$ cp $HOME/.ssh/id_rsa /tmp/id_rsa
$ ssh-keygen -p -N "" -f /tmp/id_rsa
2021-02-01 11:12:20 +00:00
$ nix-shell -p gnupg -p ssh-to-pgp --run "ssh-to-pgp -private-key -i /tmp/id_rsa | gpg --import --quiet"
2020-07-13 07:22:33 +00:00
```
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.
```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
2020-07-13 07:22:33 +00:00
```
2020-07-19 18:31:45 +00:00
If you have generated a GnuPG key directly you can get your fingerprint like this:
2020-07-13 07:22:33 +00:00
2021-02-01 11:12:20 +00:00
```console
$ gpg --list-secret-keys
2020-07-13 07:22:33 +00:00
/tmp/tmp.JA07D1aVRD/pubring.kbx
-------------------------------
sec rsa2048 1970-01-01 [SCE]
9F89C5F69A10281A835014B09C3DC61F752087EF
2020-07-13 07:22:33 +00:00
uid [ unknown] root <root@localhost>
```
The fingerprint here is `9F89C5F69A10281A835014B09C3DC61F752087EF`.
2020-07-13 07:22:33 +00:00
2021-08-27 18:35:03 +00:00
### 2a. 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 a `ed25519` key. If you don't already
have one, you can generate one using
```console
$ ssh-keygen -t ed25519
```
Converting it to the age format works like this:
```console
$ nix run -f default.nix sops-ssh-to-age -c sh -c 'ssh-add -L | sops-ssh-to-age'
```
### 3a. Get a PGP Public key for your machine
2020-07-12 22:24:03 +00:00
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.
2021-02-01 11:12:20 +00:00
```console
$ ssh root@server01 "cat /etc/ssh/ssh_host_rsa_key" | nix-shell -p ssh-to-pgp --run "ssh-to-pgp -o server01.asc"
2020-07-13 08:12:47 +00:00
# or with sudo
2021-02-01 11:12:20 +00:00
$ ssh youruser@server01 "sudo cat /etc/ssh/ssh_host_rsa_key" | nix-shell -p ssh-to-pgp --run "ssh-to-pgp -o server01.asc"
2020-07-13 07:22:33 +00:00
0fd60c8c3b664aceb1796ce02b318df330331003
# Or just read them locally (or in a ssh session)
2021-02-01 11:12:20 +00:00
$ nix-shell -p ssh-to-pgp --run "ssh-to-pgp -i /etc/ssh/ssh_host_rsa_key -o server01.asc"
2020-07-13 07:22:33 +00:00
0fd60c8c3b664aceb1796ce02b318df330331003
```
Also the hex string here is the fingerprint of your server's gpg key that can be exported append to `.sops.yaml`:
```yaml
keys:
- &admin 2504791468b153b8a3963cc97ba53d1919c5dfd4
- &server 0fd60c8c3b664aceb1796ce02b318df330331003
creation_rules:
- path_regex: secrets/[^/]+\.yaml$
key_groups:
- pgp:
- *admin
- *server
2020-07-13 07:22:33 +00:00
```
2020-07-19 18:31:45 +00:00
If you prefer having a separate GnuPG key, see [Use with GnuPG instead of ssh keys](#use-with-gnupg-instead-of-ssh-keys).
2020-07-13 07:22:33 +00:00
2021-08-27 18:35:03 +00:00
### 3b. Get a age Public key for your machine
The `sops-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 run -f default.nix sops-ssh-to-age -c sh -c 'ssh-keyscan my-server.com | sops-ssh-to-age'
$ nix run -f default.nix sops-ssh-to-age -c sh -c 'cat /etc/ssh/ssh_host_ed25519_key.pub | sops-ssh-to-age'
```
2020-07-13 07:22:33 +00:00
### 4. Create a sops file
2021-08-27 18:35:03 +00:00
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.
2020-07-13 07:22:33 +00:00
sops-nix automates importing gpg keys with a hook for nix-shell allowing public
keys to be shared via version control (i.e. git):
2020-07-13 07:22:33 +00:00
2021-02-01 11:12:20 +00:00
```nix
2020-07-13 07:22:33 +00:00
# shell.nix
with import <nixpkgs> {};
let
sops-nix = builtins.fetchTarball {
url = "https://github.com/Mic92/sops-nix/archive/master.tar.gz";
};
in
2020-07-13 07:22:33 +00:00
mkShell {
# imports all files ending in .asc/.gpg
2020-07-13 08:51:53 +00:00
sopsPGPKeyDirs = [
2020-07-13 07:22:33 +00:00
"./keys/hosts"
"./keys/users"
];
# Also single files can be imported.
2020-07-13 08:51:53 +00:00
#sopsPGPKeys = [
2020-07-13 07:22:33 +00:00
# "./keys/users/mic92.asc"
# "./keys/hosts/server01.asc"
#];
# This hook can also import gpg keys into its own seperate
# gpg keyring instead of using the default one. This allows
# to isolate otherwise unrelated server keys from the user gpg keychain.
# By uncommenting the following lines, it will set GNUPGHOME
# to .git/gnupg.
# Storing it inside .git prevents accedentially commiting private keys.
# After setting this option you will also need to import your own
# private key into keyring, i.e. using a a command like this
# (replacing 0000000000000000000000000000000000000000 with your fingerprint)
# $ (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";
2020-07-13 07:22:33 +00:00
nativeBuildInputs = [
(pkgs.callPackage sops-nix {}).sops-import-keys-hook
2020-07-13 07:22:33 +00:00
];
2020-07-12 22:24:03 +00:00
}
```
2020-07-13 07:22:33 +00:00
2020-07-13 08:51:53 +00:00
Our directory structure looks like this:
```console
2020-07-13 08:54:38 +00:00
$ tree .
.
├── keys
│   ├── hosts
│   │   └── server01.asc
│   └── users
│   └── mic92.asc
2020-07-13 08:51:53 +00:00
```
After that you can open a new file with sops
2021-02-01 11:12:20 +00:00
```console
$ nix-shell -p sops --run "sops secrets.yaml"
2020-07-13 08:51:53 +00:00
```
This will start your configured editor
In our example we put the following content in it:
```
2020-07-13 09:12:44 +00:00
example-key: example-value
2020-07-13 08:51:53 +00:00
```
NOTE: At the moment we do not support nested data structures that
sops support. This might change in the future. See also [Different file formats](#different-file-formats)
2020-07-13 08:51:53 +00:00
As a result when saving the file the following content will be in it:
```
2020-07-13 09:12:44 +00:00
example-key: ENC[AES256_GCM,data:7QIOMLd2kZkeVVpH0Q==,iv:ROh+J59ZM6BtjZLhRj1Ylk6ROEvsiX6/UR8obHX8YcQ=,tag:QOiFoHKyGFBkhr9lcWBB3Q==,type:str]
2020-07-13 08:51:53 +00:00
sops:
kms: []
gcp_kms: []
azure_kv: []
2020-07-13 09:12:44 +00:00
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]
2020-07-13 08:51:53 +00:00
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
unencrypted_suffix: _unencrypted
version: 3.5.0
```
### 5. Deploy
If you derived your server public key from ssh, all you need in your configuration.nix is:
```nix
{
imports = [ <sops-nix/modules/sops> ];
# 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;
2020-07-13 09:12:44 +00:00
sops.secrets.example-key = {};
2021-08-27 18:35:03 +00:00
# This is using ssh keys in the age format:
sops.age.sshKeyPaths = [ "/etc/ssh/ssh_host_ed25519_key" ];
2020-07-13 08:51:53 +00:00
}
```
On `nixos-rebuild switch` this will make the key accessible
2021-01-02 03:26:39 +00:00
via `/run/secrets/example-key`:
2020-07-13 07:22:33 +00:00
2020-07-13 08:51:53 +00:00
```console
2021-01-02 03:26:39 +00:00
$ cat /run/secrets/example-key
2020-07-13 09:12:44 +00:00
example-value
2020-07-13 07:22:33 +00:00
```
2020-07-13 08:51:53 +00:00
2021-01-02 03:26:39 +00:00
`/run/secrets` is a symlink to `/etc/secret.d/1`:
2020-07-13 08:51:53 +00:00
```console
$ ls -la /run/secrets
lrwxrwxrwx 16 root 12 Jul 6:23  /run/secrets -> /run/secrets.d/1
2020-07-13 07:22:33 +00:00
```
2020-07-13 08:51:53 +00:00
2020-07-21 11:29:42 +00:00
## Set secret permission/owner and allow services to access it
By default secrets are owned by `root:root`. Furthermore
the parent directory `/run/secrets.d` is only owned by
`root` and the `keys` group has read access to it:
``` 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
```
The secrets option has further parameter to change secret permission.
Consider the following nixos configuration example:
```nix
{
# Permission modes are in octal representation,
# the digits reprsent: user|group|owner
# 7 - full (rwx)
# 6 - read and write (rw-)
# 5 - read and execute (r-x)
# 4 - read only (r--)
# 3 - write and execute (-wx)
# 2 - write only (-w-)
# 1 - execute only (--x)
# 0 - none (---)
sops.secrets.example-secret.mode = "0440";
# Either a user id or group name representation of the secret owner
# It is recommended to get the user name from `config.users.<?name>.name` to avoid misconfiguration
sops.secrets.example-secret.owner = config.users.nobody.name;
# Either the group id or group name representation of the secret group
# It is recommended to get the group name from `config.users.<?name>.group` to avoid misconfiguration
sops.secrets.example-secret.group = config.users.nobody.group;
}
```
To access secrets each non-root process/service needs to be part of the keys group.
For systemd services this can be achieved as following:
```nix
{
systemd.services.some-service = {
serviceConfig.SupplementaryGroups = [ config.users.groups.keys.name ];
};
}
```
For login or system users this can be done like this:
```nix
{
users.users.example-user.extraGroups = [ config.users.groups.keys.name ];
}
2020-07-21 11:29:42 +00:00
```
The following example configures secrets for buildkite, a CI agent
the service needs a token and a ssh private key to function:
```nix
{ pkgs, config, ... }:
{
services.buildkite-agents.builder = {
enable = true;
tokenPath = config.sops.secrets.buildkite-token.path;
privateSshKeyPath = config.sops.secrets.buildkite-ssh-key.path;
runtimePackages = [
pkgs.gnutar
pkgs.bash
pkgs.nix
pkgs.gzip
pkgs.git
];
};
systemd.services.buildkite-agent-builder = {
serviceConfig.SupplementaryGroups = [ config.users.groups.keys.name ];
};
sops.secrets.buildkite-token.owner = config.users.buildkite-agent-builder.name;
sops.secrets.buildkite-ssh-key.owner = config.users.buildkite-agent-builder.name;
}
```
2020-07-13 08:51:53 +00:00
## Symlinks to other directories
Some services might expect files in certain locations.
Using the `path` option as symlink to this directory can
be created:
2020-07-23 07:53:05 +00:00
```nix
{
sops.secrets."home-assistant-secrets.yaml" = {
owner = "hass";
path = "/var/lib/hass/secrets.yaml";
};
}
```
2020-07-23 07:53:05 +00:00
```console
$ ls -la /var/lib/hass/secrets.yaml
lrwxrwxrwx 1 root root 40 Jul 19 22:36 /var/lib/hass/secrets.yaml -> /run/secrets/home-assistant-secrets.yaml
```
## Different file formats
At the moment we support the following file formats: YAML, JSON, binary
NOTE: At the moment we do not support nested data structures that
sops support. This might change in the future:
We support the following yaml:
```yaml
key: 1
```
but not:
```yaml
nested:
key: 1
```
nix-sops allows to specify multiple sops files in different file formats:
```nix
{
imports = [ <sops-nix/modules/sops> ];
# 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";
sops.secrets.github_token = {
# The sops file can be also overwritten per secret...
sopsFile = ./other-secrets.json;
# ... as well as the format
format = "json";
};
}
```
### YAML
Open a new file with sops ending in `.yaml`:
```console
$ sops secrets.yaml
```
Than put in the following content:
```yaml
github_token: 4a6c73f74928a9c4c4bc47379256b72e598e2bd3
# multi-line strings in yaml start with an |
ssh_key: |
-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
QyNTUxOQAAACDENhLwQI4v/Ecv65iCMZ7aZAL+Sdc0Cqyjkd012XwJzQAAAJht4at6beGr
egAAAAtzc2gtZWQyNTUxOQAAACDENhLwQI4v/Ecv65iCMZ7aZAL+Sdc0Cqyjkd012XwJzQ
AAAEBizgX7v+VMZeiCtWRjpl95dxqBWUkbrPsUSYF3DGV0rsQ2EvBAji/8Ry/rmIIxntpk
Av5J1zQKrKOR3TXZfAnNAAAAE2pvZXJnQHR1cmluZ21hY2hpbmUBAg==
-----END OPENSSH PRIVATE KEY-----
```
You can include it like this in your `configuration.nix`:
2020-07-23 07:51:18 +00:00
```nix
{
sops.defaultSopsFile = ./secrets.yaml;
# yaml is the default
#sops.defaultSopsFormat = "yaml";
sops.secrets.github_token = {
format = "yaml";
# can be also set per secret
sopsFile = ./secrets.yaml;
};
}
```
### JSON
Open a new file with sops ending in `.json`:
```console
$ sops secrets.json
```
Than put in the following content:
``` json
{
"github_token": "4a6c73f74928a9c4c4bc47379256b72e598e2bd3",
"ssh_key": "-----BEGIN OPENSSH PRIVATE KEY-----\\nb3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW\\nQyNTUxOQAAACDENhLwQI4v/Ecv65iCMZ7aZAL+Sdc0Cqyjkd012XwJzQAAAJht4at6beGr\\negAAAAtzc2gtZWQyNTUxOQAAACDENhLwQI4v/Ecv65iCMZ7aZAL+Sdc0Cqyjkd012XwJzQ\\nAAAEBizgX7v+VMZeiCtWRjpl95dxqBWUkbrPsUSYF3DGV0rsQ2EvBAji/8Ry/rmIIxntpk\\nAv5J1zQKrKOR3TXZfAnNAAAAE2pvZXJnQHR1cmluZ21hY2hpbmUBAg==\\n-----END OPENSSH PRIVATE KEY-----\\n"
}
```
You can include it like this in your `configuration.nix`:
2020-07-23 07:51:18 +00:00
```nix
{
sops.defaultSopsFile = ./secrets.json;
# yaml is the default
sops.defaultSopsFormat = "json";
sops.secrets.github_token = {
format = "json";
# can be also set per secret
sopsFile = ./secrets.json;
};
}
```
### Binary
Unlike the other two formats for binaries one file correspond to one secret.
2021-01-24 23:13:31 +00:00
This format allows to encrypt an arbitrary binary format that can't be put into
JSON/YAML files.
To encrypt an binary file use the following command:
``` console
$ sops -e /tmp/krb5.keytab > krb5.keytab
$ head krb5.keytab
{
"data": "ENC[AES256_GCM,data:bIsPHrjrl9wxvKMcQzaAbS3RXCI2h8spw2Ee+KYUTsuousUBU6OMIdyY0wqrX3eh/1BUtl8H9EZciCTW29JfEJKfi3ackGufBH+0wp6vLg7r,iv:TlKiOmQUeH3+NEdDUMImg1XuXg/Tv9L6TmPQrraPlCQ=,tag:dVeVvRM567NszsXKK9pZvg==,type:str]",
"sops": {
"kms": null,
"gcp_kms": null,
"azure_kv": null,
"lastmodified": "2020-07-06T06:21:06Z",
"mac": "ENC[AES256_GCM,data:ISjUzaw/5mNiwypmUrOk2DAZnlkbnhURHmTTYA3705NmRsSyUh1PyQvCuwglmaHscwl4GrsnIz4rglvwx1zYa+UUwanR0+VeBqntHwzSNiWhh7qMAQwdUXmdCNiOyeGy6jcSDsXUeQmyIWH6yibr7hhzoQFkZEB7Wbvcw6Sossk=,iv:UilxNvfHN6WkEvfY8ZIJCWijSSpLk7fqSCWh6n8+7lk=,tag:HUTgyL01qfVTCNWCTBfqXw==,type:str]",
"pgp": [
{
```
It can be decrypted again like this:
``` console
$ sops -d krb5.keytab > /tmp/krb5.keytab
```
This is how it can be included in your configuration.nix:
```nix
{
sops.secrets.krb5-keytab = {
format = "binary";
sopsFile = ./krb5.keytab;
};
}
```
2020-07-19 18:31:45 +00:00
## Use with GnuPG instead of ssh keys
2020-07-13 08:51:53 +00:00
2020-07-19 18:31:45 +00:00
If you prefer having a separate GnuPG key, sops-nix also comes with a helper tool:
2020-07-13 08:51:53 +00:00
2021-02-01 11:12:20 +00:00
```console
2020-07-13 08:51:53 +00:00
$ nix-shell -p sops-init-gpg-key
$ sops-init-gpg-key --hostname server01 --gpghome /tmp/newkey
You can use the following command to save it to a file:
cat > server01.asc <<EOF
-----BEGIN PGP PUBLIC KEY BLOCK-----
mQENBF8L/iQBCACroEaUfvPBMMorNepNQmideOtNztALejgEJ5wZmxabck+qC1Gb
NWe3tmvChXVHgL7DzodSUfX1PuIjTTeRr2clMXtISPFIsBlRQb4MiErZfsardITM
n4WScg8sTb4nnqEOJiRknwAhBryIjH8kkCXxKlYK67re281dIK4dKBMIolFADlyv
wyHurJ7NPpHxR2WXHcIqXX1DaT6RvGQvZHMpfctob8k/QD4CyV6QwG5IVACQ/tuC
bEUggrkGw+g+XdeieUfWbRsHM4C4pv8BNwA/EYD5d0eKI+rshSPoTT+hcGn8Uh8w
MVQ8PVs6jWMMOAF1JH/stoPr9Yha+TGbMRi5ABEBAAG0GHNlcnZlcjAxIDxyb290
QHNlcnZlcjAxPokBTgQTAQgAOBYhBOTKhnaPF2rrbAFVQVOvjX8UlhOxBQJfC/4k
AhsvBQsJCAcCBhUKCQgLAgQWAgMBAh4BAheAAAoJEFOvjX8UlhOx1XIH/jUOrSR2
wuoqFiHcqaDPgXmTVJk8QanVkmiP3tk0mz5rRKrDX2eX5GnHqYR4PfpjUYNzedQE
sGyTjl7+DvglWJ2Q8m3yD/9+1agBmeqEVQlKqwL6Sc3bI4WBwHaxwVDo/bNwMs0w
o8ngOs1jPd3LfQdfG/rE1NolpHm4LWqYj0D2zEGqozLXVBx2wiuwmm6OKX4U4EHR
UwKax+VZYA+J9oFDN+kOy/yR+bKnOvg5eyOv2ZrK5BKceSBhDTOclMIWTL2cGxcL
jsq4N7fobs4TbwFPxRUi/T9ldXi0LXeGhTl9stImTtj3bL+4Y734TipvB5UvzCDK
CkjjwEvD5MYdGDE=
=uvIf
-----END PGP PUBLIC KEY BLOCK-----
EOF
fingerprint: E4CA86768F176AEB6C01554153AF8D7F149613B1
```
In this case you need to make upload the gpg key directory `/tmp/newkey` to your 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
2021-08-27 11:35:53 +00:00
sops.gnupg.home = "/var/lib/sops";
# disable import host ssh keys
2021-08-27 11:35:53 +00:00
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
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.
2020-07-18 10:13:46 +00:00
## Share secrets between different users
Secrets can be shared between different users by creating different files
pointing to the same sops key but with different permissions. In the following
example the `drone` secret is exposed as `/run/secrets/drone-server` for
`drone-server` and as `/run/secrets/drone-agent` for `drone-agent`
```nix
{
sops.secrets.drone-server = {
owner = config.systemd.services.drone-server.serviceConfig.User;
key = "drone";
};
sops.secrets.drone-agent = {
owner = config.systemd.services.drone-agent.serviceConfig.User;
key = "drone";
};
}
```
2020-07-18 10:13:46 +00:00
## Migrate from pass/krops
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.
2020-07-18 10:13:46 +00:00
2020-07-23 08:05:25 +00:00
```console
2020-07-18 10:13:46 +00:00
$ for i in *.gpg; do echo "$(basename $i .gpg): |\n$(pass $(dirname $i)/$(basename $i .gpg)| sed 's/^/ /')"; done
```
Copy the output to the editor you have opened with sops.
## Realworld 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.
## Known limitations
### Restarting systemd services
Right now systemd services are not restarted automatically.
We want to implement this in future.
### Initrd secrets
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
provisioned in the bootloader install phase.