How to 1312 Blogs, part 1: provisioning a dedicated server with NixOS
Documenting here how the server supporting 1312 Blogs (and a few other blog sites) was created.
So I'm using nixos-anywhere
to provision NixOS to my hosted dedicated servers. nixos-anywhere
purpose is to transform any linux machine with ssh access into a NixOS machine.
For this nixos-anywhere
only need a description of the disk layout you want to end-up with. More precisely, it uses disko to support declarative disk partitioning.
My declarative partitioning look like this:
disko.nix
{
disko.devices = {
disk = {
sda = {
type = "disk";
device = "/dev/sda";
content = {
type = "gpt";
partitions = {
BOOT = {
size = "1M";
type = "EF02"; # for grub MBR
};
ESP = {
size = "1000M";
type = "EF00";
content = {
type = "filesystem";
format = "ext4";
mountpoint = "/boot";
};
};
luks = {
size = "100%";
content = {
type = "luks";
name = "crypted";
askPassword = true;
content = {
type = "filesystem";
format = "ext4";
mountpoint = "/";
};
};
};
};
};
};
};
};
}
So only 2 partitions, /boot
and an encrypted /
.
Now I don't have physical access to the machine, so to decrypt the root partition we will bundle an ssh server into the initrd
, as explained in this blog post by Carlos Vaz or this one by Matthias Totschnig:
flake.nix
{
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-24.11";
disko = {
url = "github:nix-community/disko";
inputs.nixpkgs.follows = "nixpkgs";
};
};
outputs = { nixpkgs, disko, ... }: {
nixosConfigurations = {
server = nixpkgs.lib.nixosSystem rec {
system = "x86_64-linux";
modules = [
disko.nixosModules.disko
./disko.nix
./configuration.nix
{
boot.initrd.network = {
# Make sure you have added the kernel module for your network driver to `boot.initrd.availableKernelModules`
enable = true;
ssh = {
enable = true;
# To prevent ssh clients from freaking out because a different host key is used,
# a different port for ssh is useful (assuming the same host has also a regular sshd running)
port = 2222;
hostKeys = [ "/etc/ssh/initrd-ssh-host-key" ];
# public ssh key used for login
authorizedKeys = [
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPEvEVBYFc/u2vRUaiQgV/t4aA6tqhlvNj/OrUkHa1Pz"
];
};
# ask for secret key on login
postCommands = ''
cat <<'EOF' > /root/.profile
echo "Enter encrypted volume passphrase"
read -s pass
echo "$pass" > /crypt-ramfs/passphrase && exit
EOF
'';
};
}
];
};
};
};
}
To go one setup further, one could use Tailscale ssh instead.
Then we run nixos-anywhere
with the necessary secrets, as per documentation:
temp=$(mktemp -d)
cleanup() {
rm -rf "$temp"
}
trap cleanup EXIT
install -d -m755 "$temp/etc/ssh"
ssh-keygen -t ed25519 -N "" -f "$temp/etc/ssh/initrd-ssh-host-key"
chmod 600 "$temp/etc/ssh/initrd-ssh-host-key"
read -s my_disk_encryption_password
nix run github:nix-community/nixos-anywhere -- \
--extra-files "$temp" \
--disk-encryption-keys /tmp/luks.key <(echo $my_disk_encryption_password) \
--flake '.#server' ubuntu@nsxxxxx.ip-xx-xx-xx-xx.eu
Note: beware that this method does not prevent an attacker with physical access to intercept you password by forcing the machine to reboot into a crafted shell under attacker control. To prevent this you would need to setup UEFI Secure Boot with lanzaboote (that means having access to the machine BIOS, which must have reliable TPM support).
disclaimer: I am not a professional security expert / cryptographer.
Still,
All Crypted-disks Are Beautiful