First, install Nix, then:
$ nix shell nixpkgs#git nixpkgs#home-manager $ git clone <this repo> $ home-manager --switch --extra-experimental-features "nix-command flakes" init --flake .#
The --impure
flag might need to be given as well. The above instructions are
only for the very first time using this.
config/
: contains stuff that should be placed under~/.config
but without using the HM way of doing things.legacy/
: contains scripts and configurations of tools used in the past, not used by any configuration at the moment but that might be useful if I decide to start using them again.nix/
: all nix code organized on this directory, home-manager and nixos configuration live here.resources/
: mainly icons and fonts, I could add also wallpapers or other stuff here in the future.secrets/
: declares encrypted secrets usingsops
integrated into the configuration withsops-nix
.
There are two solutions in place to manage secrets, whenever possible sops
is
recommended.
This approach only works for programs that need a secret at run-time provided via CLI arguments, configuration files or environment variables, if you need a secret to pass into a nix option somewhere (e.g. kagi search token configured by url using nix) then using sops won’t work (easily), for those cases you can look into nix run nixpkgs#sops -- secrets/<profile>/secrets.yaml
Note that adding or removing keys into the .sops.yaml
file requires
re-encrypting the secrets file, so one might need to edit it even though we do
no wish to change its contents just trigger re-encryption of the secrets in
the file.
On the nix side, secrets have to be “declared” under the sops.secrets
option, see
jgutierrez/secrets.nix for an example on how to declare and access secrets.
The important think is that the attribute name declaring the secret must match
the key of that secret used in the secrets/<profile>/secrets.yaml
file to be
mapped correctly.
That module also contains an example on how to use the secrets to source them
into environment variables, note that using home.sessionVariables
won’t work
because when the configuration is evaluated the file containing the secret
value isn’t available yet and even if it was the secret would then leak into
the /nix/store
because in that case it would be used as the value to a Nix
option meaning it would end-up rendered in plain text in the derivation that
builds our shell environment.
In order to start using this mechanism:
- Add your encryption GPG key ID under
keys
in the.sops.yaml
configuration file. And refer to it in the list ofkey_groups.pgp
keys under thecreation_rules
section. - Create your secrets file in
secrets/<your profile>/secrets.yaml
using sops as instructed above. - Add the secret as an object, that is a YAML key value pair, just remember only the value will be encrypted so do not include sensitive data as part of the key.
- Declare your secrets in your Home-Manager configuration under the
sops.secrets
option. - The secret will be decrypted and written to a file at run-time, the path to
this file is under the
config.sops.secrets.<declared secret>.path
option. Make your program read the secret from there or load it to an environment variable accordingly.
The above will work provided that your configuration imports the workstation
or minimal
configuration modules these include the presets/sops
preset which
configures Home-Manager appropriately.
Nix itself is configured by home-manager with the following options set:
{
nix.settings = {
# The `nix-plugins` package makes nix configuration to expose a new `extra-builtins-file` setting
plugin-files = "${pkgs.nix-plugins}/lib/nix/plugins";
# After the plugin is bootstraped this option is visible and we add "extra" builtins to our nix
extra-builtins-file = "${inputs.self}/nix/extra-builtins.nix";
};
}
By using nix-plugins
we’re able to use the exec
function to do impure things
during evaluation time (see nix/extra-builtins.nix
and nix/decrypt.sh
), such
as decrypting a GPG secrets file in secrets/<profile>/sensitive.nix.gpg
. When using this
we must be very careful to not break Nix’s assumptions (immutability,
observable side-effects, etc).
This is loaded in a sensitive
attribute for every profile in the flake.nix
using:
sensitive = builtins.extraBuiltins.readEncrypted ./secrets/<profile>/sensitive.nix.gpg;
Any profile has this sensitive
attribute set in scope, so sensitive values can
be accessed by attribute, e.g. profile.sensitive.kagiAuthToken
.
Note that the sensitive.nix.gpg
file needs to be decrypted and re-encrypted if
one adds a new key to the gpg keyring (in case multiple machines with
different keys have to access sensitive values under the same profile). Albeit
in that case it would be simpler to use different profiles per machine.
Also note that values stored in this way are not really secrets, they are
decrypted at evaluation time and injected into some nix expression which
stores them as plain text somewhere in the /nix/store
as part of the
derivation that needs them. If this is a concern look at the approach
described above which doesn’t have this shortcomings Managing secrets with
=sops= .
Note that in order to bootstrap this mechanism into a configuration that
attempts to use this for the first time it is necessary to apply the
configuration twice, first to load the nix plugin and a second time to load
the extra-builtins-file
option values. Only then we should refer to a
sensitive value within our configuration, doing it any sooner will result in
an error as that would mean that either the plugin hasn’t been loaded or if
the plugin has been loaded then the extra-builtins
haven’t been defined yet.
So to setup this for the first time:
- Make sure you have a GPG encryption key loaded into your keyring, i.e.
gpg --list-secret-keys
. - Add your GPG encrypted sensitive file in
secrets/<profile>/sensitive.nix.gpg
it can be created with standard GPG options or use Emacs support for GPG files (which triggers when editing files with the.gpg
extension). The file must be a Nix attribute set of key value pairs. - Activate your configuration to install and configure the necessary
nix-plugins
so that yournix.conf
file has theplugin-files
setting enabled. - Activate your configuration a second time to enable the necessary
extra-builtins
(likereadEncrypted
), this would only work if thenix-plugins
are loaded by the previous step. - Finally refer to sensitive values within your configuration, these would be
available under the
profile.sensitive
option, i.e.profile.sensitive.bank_account
.
It may be possible that unknowingly you have applied the Nix configuration
multiple times before without actually declaring senstive values, in that case
most-likely you can skip the double activation, to ensure this is the case it
is enough to inspect the nix.conf
file and see if the extra-builtins
option is there.
Browser extension settings aren’t declared in the configuration (yet) these are sometimes lost, it is suggested to evaluate the following tridactyl settings to restore familiar bindings for colemak users:
:unbindurl youtube\.com/watch\?v=.* f :unbindurl youtube\.com/watch\?v=.* j :unbindurl youtube\.com/watch\?v=.* l :unbindurl lichess.org f :bind k scrollline 10 :bind h scrollline -10 :bind j scrollpx -50 :bind l scrollpx 50 :bind J tabprev :bind L tabnext :bind H forward :bind K back
- Make sure code is properly formatted:
nix fmt
. - Make sure configuration checks are green:
nix flake check
.