This blog post recaps how to install NixOS with only a WiFi connection, how to manage the WiFi connection declaratively and how to encrypt the passphrase during deploy.
This is only really useful for a machine that will not move much. If you’re trying to configure WiFi
on a laptop, I would recommend using services.networkmanager
and configure the connection
manually.
- Why Encrypt the Passphrase
- Enable WiFi Manually
- Initial Install WiFi Configuration
- Deploy with Encrypted Secret
Why Encrypt the Passphrase
Like for any non-encrypted secret in NixOS, two issues arise if you put the passphrase in clear in the configuration:
- the passphrase will be stored in the nix store of both the build machine and the target machine
- and the passphrase will be stored in the git repo you use to manage your deploy.
The solution to the first issue is to use something like the deployment.keys
option that is
supported by most deployment tools.
The solution to the second issue is to encrypt the secret in the repo, and decrypt it on the target machine, after deploy. Multiple tools exist to handle this.
In this post, we will use sops-nix which provides a solution for both issues.
Enable WiFi Manually
A little aside before we start. If something goes wrong, you can always setup WiFi manually with the commands from the wpa_supplicant and dhcpcd Arch Linux wiki. The only difference with the wiki is you do not need to install the commands as they come with NixOS.
sudo wpa_supplicant -B \
-i wlan0 \
-c <(wpa_passphrase SSID PASSPHRASE)
touch dhcpcd.conf && \
sudo dhcpcd --config dhcpcd.conf
Initial Install WiFi Configuration
Assuming you just booted on NixOS for the first time on the target machine and you made some edits
to configuration.nix
, your next step is to run nixos-rebuild switch
. But first, you need a
working internet connection.
What is following looks like a convoluted way to set WiFi up but it sets us up nicely to be able to declaratively set the WiFi connection with an encrypted password later on.
In the machine’s configuration.nix
, add:
{
networking.wireless = enable = true;
environmentFile = "/run/secrets/MY_SSID_PSK";
networks = {
"MY_SSID" = {
psk = "@MY_SSID_PSK@";
};
};
};
Replace MY_SSID
with the name of the SSID you will be connecting to.
Then create the file /run/secrets/MY_SSID_PSK
with the following content:
MY_SSID_PSK=theactualpassphrase
Deploy with Encrypted Secret
Like we said earlier, we will use nix-sops to encrypt the secret at rest and during deploy.
A few prerequisites:
- You copied over the machine’s configuration.nix
locally which includes the networking.wireless
section we added earlier.
- You created a public/private key pair that allows you to ssh into the target machine.
Now, to actually encrypt the secret, we will follow the nix-sops readme file. The gist is:
Install the necessary packages to run the commands:
nix shell nixpkgs#ssh-to-age nixpkgs#sops
You need the latest
ssh-to-age
binary as the one provided in 21.11 does not have all the necessary arguments.Create an
age
secret from that public/private key pair used to connect to the target machine.ssh-to-age -private-key \ -i ~/.ssh/TARGET_HOSTNAME \ -o ~/.config/sops/age/TARGET_HOSTNAME.txt age-keygen -y ~/.config/sops/age/TARGET_HOSTNAME.txt
Use the output of that last command for
admin_nixos
later on.Also, replace
TARGET_HOSTNAME
with the actual hostname of the target machine.If the private key uses a passphrase, you’ll first need to export an environment variable with the passphrase:
read -s SSH_TO_AGE_PASSPHRASE export SSH_TO_AGE_PASSPHRASE
Get the
age
secret from the target machinessh-keyscan -t ed25519 TARGET_MACHINE_IP | \ ssh-to-age
Use the output of that command for
server_TARGET_HOSTNAME
later on.Note here I am using the IP of the target machine as
ssh-keyscan
was failing to retrieve anything with the hostname. I do not know why.Then fill in
.sops.yaml
with:keys: - &admin_nixos age1... - &server_TARGET_HOSTNAME age1... creation_rules: - path_regex: secrets/[^/]+\.yaml$ key_groups: - age: - *admin_nixos - *server_TARGET_HOSTNAME
That file should be living in your repository used for deploys.
A few replacements are needed in the file:
- Replace
age1...
string foradmin_nixos
with the value we obtained at step 2. - Replace
age1...
string forserver_TARGET_HOSTNAME
with the value we obtained at step 3. - Replace
TARGET_HOSTNAME
with the actual hostname of the target server.
- Replace
Create the encrypted secret file:
mkdir -p secrets
sops secrets/secrets.yaml
The content of the file should be the content of the file in /run/secrets
we created earlier:
MY_SSID_PSK=theactualpassphrase
- Wire up SOPS in the
configuration.nix
:
{
sops = defaultSopsFile = ./secrets/secrets.yaml;
age.sshKeyPaths = [ "/etc/ssh/ssh_host_ed25519_key" ];
age.keyFile = "/home/ME/.config/sops/age/TARGET_HOSTNAME.txt";
secrets."MY_SSID_PSK" = {};
};
Now, next time you will deploy, sops will use the secret file, send it over to the target machine
when deploying, decrypt the file and populate the content of /run/secrets/MY_SSID_PSK
.