From 0bf113fa254d3a8387a4022d9eac59f22f064cdb Mon Sep 17 00:00:00 2001 From: Pim Kunis Date: Sat, 25 Nov 2023 21:00:21 +0100 Subject: [PATCH 1/2] WIP: nixos-anywhere for virtual machines --- .gitignore | 2 + README.md | 2 +- bootstrap.sh | 8 ++- configuration.nix | 2 +- flake.nix | 2 + machines/default.nix | 67 +++++++++++++++++++ modules/custom/ssh-certificates.nix | 10 ++- modules/disk-config.nix | 1 - terraform/main.tf | 36 ++++++++++ terraform/modules/README.md | 7 ++ .../modules/debian/files/cloud_init.cfg.tftpl | 15 +++++ terraform/modules/debian/files/get_cert.sh | 17 +++++ .../modules/debian/files/network_config.cfg | 9 +++ terraform/modules/debian/main.tf | 54 +++++++++++++++ terraform/modules/debian/variables.tf | 13 ++++ terraform/modules/setup/main.tf | 44 ++++++++++++ 16 files changed, 282 insertions(+), 7 deletions(-) create mode 100644 terraform/main.tf create mode 100644 terraform/modules/README.md create mode 100644 terraform/modules/debian/files/cloud_init.cfg.tftpl create mode 100755 terraform/modules/debian/files/get_cert.sh create mode 100644 terraform/modules/debian/files/network_config.cfg create mode 100644 terraform/modules/debian/main.tf create mode 100644 terraform/modules/debian/variables.tf create mode 100644 terraform/modules/setup/main.tf diff --git a/.gitignore b/.gitignore index 92b2793..aab1529 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,3 @@ .direnv +.terraform.lock.hcl +.terraform diff --git a/README.md b/README.md index c8e0376..7ee81e5 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ Additionally, it deploys an age identity, which is later used for decrypting sec 1. Make sure your have a [Secret service](https://www.gnu.org/software/emacs/manual/html_node/auth/Secret-Service-API.html) running (such as Keepassxc) that provides the age identity. 2. Ensure you have root SSH access to the server. -3. Run nixos-anywhere: `./bootstrap.sh ` +3. Run nixos-anywhere: `./bootstrap.sh ` ## Deployment diff --git a/bootstrap.sh b/bootstrap.sh index 0633b1d..66f2449 100755 --- a/bootstrap.sh +++ b/bootstrap.sh @@ -4,9 +4,11 @@ IFS=$'\n\t' servername="${1-}" -if [ -z "$servername" ] +hostname="${2-}" + +if [ -z "$servername" ] || [ -z "$hostname" ] then - echo "Usage: $0 SERVERNAME" + echo "Usage: $0 SERVERNAME HOSTNAME" exit 1 fi @@ -40,4 +42,4 @@ secret-tool lookup age-identity "$servername" > "$temp/root/age_ed25519" chmod 600 "$temp/root/age_ed25519" # Install NixOS to the host system with our age identity -nix run github:numtide/nixos-anywhere -- --extra-files "$temp" --flake ".#${servername}" "root@${servername}.hyp" +nix run github:numtide/nixos-anywhere -- --extra-files "$temp" --flake ".#${servername}" "root@${hostname}" diff --git a/configuration.nix b/configuration.nix index 950b605..8e3c389 100644 --- a/configuration.nix +++ b/configuration.nix @@ -1,7 +1,7 @@ { pkgs, config, lib, modulesPath, ... }: { imports = [ (modulesPath + "/installer/scan/not-detected.nix") - ./modules/disk-config.nix + # ./modules/disk-config.nix ./modules/custom ./modules/uptimed.nix ]; diff --git a/flake.nix b/flake.nix index aa1690b..58bf87f 100644 --- a/flake.nix +++ b/flake.nix @@ -41,6 +41,8 @@ pkgs-unstable.deploy-rs pkgs.openssl pkgs.postgresql_15 + pkgs-unstable.opentofu + pkgs.cdrtools ]; }; diff --git a/machines/default.nix b/machines/default.nix index fad2e3d..116441c 100644 --- a/machines/default.nix +++ b/machines/default.nix @@ -8,12 +8,79 @@ dataDisk.enable = true; ssh = { + useCertificates = true; hostCert = builtins.readFile ./jefke_host_ed25519-cert.pub; userCert = builtins.readFile ./jefke_user_ed25519-cert.pub; }; terraformDatabase.enable = true; }; + + disko.devices = { + disk = { + vdb = { + device = "/dev/nvme0n1"; + type = "disk"; + content = { + type = "gpt"; + partitions = { + ESP = { + type = "EF00"; + size = "500M"; + content = { + type = "filesystem"; + format = "vfat"; + mountpoint = "/boot"; + }; + }; + root = { + end = "-4G"; + content = { + type = "filesystem"; + format = "btrfs"; + mountpoint = "/"; + }; + }; + swap = { size = "100%"; }; + }; + }; + }; + }; + }; + }; + }; + + bancomart = { + name = "bancomart"; + hostname = "bancomart.dmz"; + + specificConfig = { + disko.devices = { + disk = { + vda = { + device = "/dev/vda"; + type = "disk"; + content = { + type = "gpt"; + partitions = { + boot = { + size = "1M"; + type = "EF02"; # for grub MBR + }; + root = { + size = "100%"; + content = { + type = "filesystem"; + format = "ext4"; + mountpoint = "/"; + }; + }; + }; + }; + }; + }; + }; + }; }; } diff --git a/modules/custom/ssh-certificates.nix b/modules/custom/ssh-certificates.nix index 456ff21..d4c5a9d 100644 --- a/modules/custom/ssh-certificates.nix +++ b/modules/custom/ssh-certificates.nix @@ -7,6 +7,14 @@ in { options = { custom = { ssh = { + useCertificates = lib.mkOption { + type = lib.types.bool; + default = false; + description = '' + Whether to use certificates at all. + ''; + }; + hostCert = lib.mkOption { type = lib.types.str; description = '' @@ -42,7 +50,7 @@ in { }; }; - config = { + config = lib.mkIf cfg.useCertificates { services.openssh = { extraConfig = '' HostCertificate ${hostCert} diff --git a/modules/disk-config.nix b/modules/disk-config.nix index bbef13a..b02df2f 100644 --- a/modules/disk-config.nix +++ b/modules/disk-config.nix @@ -31,4 +31,3 @@ }; }; } - diff --git a/terraform/main.tf b/terraform/main.tf new file mode 100644 index 0000000..39bee8b --- /dev/null +++ b/terraform/main.tf @@ -0,0 +1,36 @@ +terraform { + backend "pg" { + schema_name = "testje" + conn_str = "postgresql://terraform@jefke.hyp/terraformstates" + } + + required_providers { + libvirt = { + source = "dmacvicar/libvirt" + version = "0.7.1" # https://github.com/dmacvicar/terraform-provider-libvirt/issues/1040 + } + } +} + +# https://libvirt.org/uri.html#libssh-and-libssh2-transport +provider "libvirt" { + # alias = "jefke" + uri = "qemu+ssh://root@jefke.hyp/system?known_hosts=/etc/ssh/ssh_known_hosts" +} + +module "setup_jefke" { + source = "./modules/setup" + # providers = { + # libvirt = libvirt.jefke + # } +} + +module "bancomart" { + source = "./modules/debian" + name = "bancomart" + ram = 2048 + storage = 10 + # providers = { + # libvirt = libvirt.jefke + # } +} diff --git a/terraform/modules/README.md b/terraform/modules/README.md new file mode 100644 index 0000000..70e2b7d --- /dev/null +++ b/terraform/modules/README.md @@ -0,0 +1,7 @@ +# tf-modules + +Terraform modules we use for the virtual machines in our home network. +These are all personalized and probably of little use outside our network. +The modules are currently: +- `debian`: Personalized Debian VM using Terraform's `libvirt` provider +- `invariants`: Invariants for our home network we use in multiple places. diff --git a/terraform/modules/debian/files/cloud_init.cfg.tftpl b/terraform/modules/debian/files/cloud_init.cfg.tftpl new file mode 100644 index 0000000..070e8fa --- /dev/null +++ b/terraform/modules/debian/files/cloud_init.cfg.tftpl @@ -0,0 +1,15 @@ +#cloud-config +hostname: "${hostname}" +manage_etc_hosts: true +disable_root: false + +ssh_authorized_keys: +- "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOodpLr+FDRyKyHjucHizNLVFHZ5AQmE9GmxMnOsSoaw pimkunis@thinkpadpim" +- "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAINUZp4BCxf7uLa1QWonx/Crf8tYZ5MKIZ+EuaBa82LrV user@user-laptop" + +ssh_pwauth: false + +# TODO: Do we need this? +runcmd: +- dhclient -r +- dhclient diff --git a/terraform/modules/debian/files/get_cert.sh b/terraform/modules/debian/files/get_cert.sh new file mode 100755 index 0000000..b6a6f85 --- /dev/null +++ b/terraform/modules/debian/files/get_cert.sh @@ -0,0 +1,17 @@ +#!/bin/bash +set -euo pipefail +IFS=$'\n\t' + +eval "$(jq -r '@sh "PUBKEY=\(.pubkey) HOST=\(.host) CAHOST=\(.cahost) CASCRIPT=\(.cascript) CAKEY=\(.cakey)"')" + +# TODO: Can this be done more eye-pleasingly? +set +e +CERT=$(ssh -o ConnectTimeout=3 -o ConnectionAttempts=1 root@$CAHOST '"'"$CASCRIPT"'" host "'"$CAKEY"'" "'"$PUBKEY"'" "'"$HOST"'".dmz') +retval=$? +set -e + +if [ retval -neq 0 ]; then + CERT="" +fi + +jq -n --arg cert "$CERT" '{"cert":$cert}' diff --git a/terraform/modules/debian/files/network_config.cfg b/terraform/modules/debian/files/network_config.cfg new file mode 100644 index 0000000..b7c4ff2 --- /dev/null +++ b/terraform/modules/debian/files/network_config.cfg @@ -0,0 +1,9 @@ +version: 2 +ethernets: + ens: + match: + name: ens* + dhcp4: true + routes: + - to: 0.0.0.0/0 + via: 192.168.30.1 diff --git a/terraform/modules/debian/main.tf b/terraform/modules/debian/main.tf new file mode 100644 index 0000000..d9574a5 --- /dev/null +++ b/terraform/modules/debian/main.tf @@ -0,0 +1,54 @@ +terraform { + required_providers { + libvirt = { + source = "dmacvicar/libvirt" + } + } +} + +resource "libvirt_volume" "os" { + name = "${var.name}.qcow2" + pool = "disks" + size = 1024 * 1024 * 1024 * var.storage + base_volume_name = "debian-bookworm.qcow2" + base_volume_pool = "images" + + lifecycle { + replace_triggered_by = [ + libvirt_cloudinit_disk.main.id + ] + } +} + +resource "libvirt_cloudinit_disk" "main" { + name = "${var.name}.iso" + pool = "cloudinit" + user_data = templatefile("${path.module}/files/cloud_init.cfg.tftpl", { + hostname = var.name + }) + network_config = file("${path.module}/files/network_config.cfg") +} + +resource "libvirt_domain" "main" { + name = var.name + memory = var.ram + vcpu = 4 + autostart = true + + disk { + volume_id = libvirt_volume.os.id + } + + network_interface { + bridge = "bridgedmz" + hostname = var.name + } + + cloudinit = libvirt_cloudinit_disk.main.id + + lifecycle { + replace_triggered_by = [ + libvirt_cloudinit_disk.main.id + ] + } +} diff --git a/terraform/modules/debian/variables.tf b/terraform/modules/debian/variables.tf new file mode 100644 index 0000000..5d25fb4 --- /dev/null +++ b/terraform/modules/debian/variables.tf @@ -0,0 +1,13 @@ +variable "name" { + type = string +} + +variable "ram" { + type = number + description = "In MiB" +} + +variable "storage" { + type = number + description = "In GiB" +} diff --git a/terraform/modules/setup/main.tf b/terraform/modules/setup/main.tf new file mode 100644 index 0000000..c58be12 --- /dev/null +++ b/terraform/modules/setup/main.tf @@ -0,0 +1,44 @@ +terraform { + required_providers { + libvirt = { + source = "dmacvicar/libvirt" + } + } +} + +resource "libvirt_pool" "images" { + name = "images" + type = "dir" + path = "/var/lib/libvirt/pools/images" +} + +resource "libvirt_pool" "cloudinit" { + name = "cloudinit" + type = "dir" + path = "/var/lib/libvirt/pools/cloudinit" +} + +resource "libvirt_pool" "disks" { + name = "disks" + type = "dir" + path = "/var/lib/libvirt/pools/disks" +} + +resource "libvirt_volume" "debian_bookworm" { + name = "debian-bookworm.qcow2" + pool = libvirt_pool.images.name + source = "https://cloud.debian.org/images/cloud/bookworm/daily/latest/debian-12-generic-amd64-daily.qcow2" +} + +resource "libvirt_network" "bridgedmz" { + name = "bridgedmz" + mode = "bridge" + bridge = "bridgedmz" + dhcp { + enabled = false + } + dns { + enabled = false + } + autostart = true +} -- 2.45.2 From 7e9637c984d1c09b4db6f8cd39be7c398c386c79 Mon Sep 17 00:00:00 2001 From: Pim Kunis Date: Wed, 29 Nov 2023 17:21:18 +0100 Subject: [PATCH 2/2] manage atlas --- machines/atlas_host_ed25519-cert.pub | 1 + machines/atlas_user_ed25519-cert.pub | 1 + machines/default.nix | 39 ++++++++++++++++++--------- secrets/atlas_host_ed25519.age | Bin 0 -> 663 bytes secrets/atlas_user_ed25519.age | Bin 0 -> 712 bytes secrets/secrets.nix | 9 +++++++ terraform/main.tf | 4 +-- 7 files changed, 39 insertions(+), 15 deletions(-) create mode 100644 machines/atlas_host_ed25519-cert.pub create mode 100644 machines/atlas_user_ed25519-cert.pub create mode 100644 secrets/atlas_host_ed25519.age create mode 100644 secrets/atlas_user_ed25519.age diff --git a/machines/atlas_host_ed25519-cert.pub b/machines/atlas_host_ed25519-cert.pub new file mode 100644 index 0000000..44e70c7 --- /dev/null +++ b/machines/atlas_host_ed25519-cert.pub @@ -0,0 +1 @@ +ssh-ed25519-cert-v01@openssh.com AAAAIHNzaC1lZDI1NTE5LWNlcnQtdjAxQG9wZW5zc2guY29tAAAAIH4CQGHwWytKnkn7lYjT6G1NyPzINvfroZgwCLoOLO74AAAAIOMoSSEqM4VUBWUeFweJbqK9z7Ygp7fkX22hyWmgCNg8AAAAAAAAAAAAAAACAAAACWF0bGFzLmh5cAAAAA0AAAAJYXRsYXMuaHlwAAAAAAAAAAD//////////wAAAAAAAAAAAAAAAAAAADMAAAALc3NoLWVkMjU1MTkAAAAgXNGQfd38pUlCi6zBj8Myl6dZsMVU6cjdW63TFHR7W1sAAABTAAAAC3NzaC1lZDI1NTE5AAAAQAYModSEVNG06xvAcRn8XFeCp/iXFeqVcbtfT1NmmMkyIgybkXhJyHjp89BPg0zeAaoScFx8Xpsdd8CsxTeP+QU= root@atlas diff --git a/machines/atlas_user_ed25519-cert.pub b/machines/atlas_user_ed25519-cert.pub new file mode 100644 index 0000000..660f82a --- /dev/null +++ b/machines/atlas_user_ed25519-cert.pub @@ -0,0 +1 @@ +ssh-ed25519-cert-v01@openssh.com AAAAIHNzaC1lZDI1NTE5LWNlcnQtdjAxQG9wZW5zc2guY29tAAAAIItpNkjaH8o51VKydwHYbbLxXMtf4euzojFKPxz+XqdwAAAAIG1vJNH1p8l8HlmYMT/vHGTjEnIul7ORQhutNnKiXlgqAAAAAAAAAAAAAAABAAAACWF0bGFzLmh5cAAAABsAAAAJYXRsYXMuaHlwAAAACmh5cGVydmlzb3IAAAAAAAAAAP//////////AAAAAAAAAIIAAAAVcGVybWl0LVgxMS1mb3J3YXJkaW5nAAAAAAAAABdwZXJtaXQtYWdlbnQtZm9yd2FyZGluZwAAAAAAAAAWcGVybWl0LXBvcnQtZm9yd2FyZGluZwAAAAAAAAAKcGVybWl0LXB0eQAAAAAAAAAOcGVybWl0LXVzZXItcmMAAAAAAAAAAAAAADMAAAALc3NoLWVkMjU1MTkAAAAgdmt4SFL+swd8kHsh6cQR+TfzMKObJx75fYBbHNT83zUAAABTAAAAC3NzaC1lZDI1NTE5AAAAQIW4tC+FJA6bKFUfRVcHLWz1u3ZL/GRTWD2WCW4ApHq7no6ODeMwE10noNt/42mwYjFmjwR+cd9EuMyUErXmaw8= root@atlas diff --git a/machines/default.nix b/machines/default.nix index 116441c..bf1e41f 100644 --- a/machines/default.nix +++ b/machines/default.nix @@ -50,37 +50,50 @@ }; }; - bancomart = { - name = "bancomart"; - hostname = "bancomart.dmz"; + atlas = { + name = "atlas"; + hostname = "atlas.hyp"; specificConfig = { + custom = { + ssh = { + useCertificates = true; + hostCert = builtins.readFile ./atlas_host_ed25519-cert.pub; + userCert = builtins.readFile ./atlas_user_ed25519-cert.pub; + }; + }; + disko.devices = { disk = { - vda = { - device = "/dev/vda"; + vdb = { + device = "/dev/nvme0n1"; type = "disk"; content = { type = "gpt"; partitions = { - boot = { - size = "1M"; - type = "EF02"; # for grub MBR - }; - root = { - size = "100%"; + ESP = { + type = "EF00"; + size = "500M"; content = { type = "filesystem"; - format = "ext4"; + format = "vfat"; + mountpoint = "/boot"; + }; + }; + root = { + end = "-4G"; + content = { + type = "filesystem"; + format = "btrfs"; mountpoint = "/"; }; }; + swap = { size = "100%"; }; }; }; }; }; }; - }; }; } diff --git a/secrets/atlas_host_ed25519.age b/secrets/atlas_host_ed25519.age new file mode 100644 index 0000000000000000000000000000000000000000..36d8be2882b47a063fb3ca8d239c3b68f4f48a9a GIT binary patch literal 663 zcmV;I0%-kVXJsvAZewzJaCB*JZZ29J|JLoM^R8MXL4m>b7cxy zPd9W;b6I(KSv6HwRC#7cNKrL!Lqv9IH(_HrT1_-HX*dckEiE7`D>!6jYHc=1dP!zf zGIwTAc55?hI5uW!DSrC>E<2=zbfL0nfZjg$NashHTIWQ|roFcb2Id7d z#R*fYe2}`ChJ66uqjz`{`(G@#aq}_sP%4k}SN8ltP_UB#M6(Qrl+1y6j=p@TdQALc7|C%6~%^~*eS^_p{ z{wNEFLK<;nu-595>Mv2C=<$=>1}8*cpPXo-ij?zRO}!eB2TA6Jh@0@!_8R4;-Zv==7906hx%2a x#s!-hp&EU9Kb`JOgCHn#APlzRE?nK=vq)ri{hA3&lNyjUU%`;{`taSSf7bxJ9_9c5 literal 0 HcmV?d00001 diff --git a/secrets/atlas_user_ed25519.age b/secrets/atlas_user_ed25519.age new file mode 100644 index 0000000000000000000000000000000000000000..403104f9bc0171b1bf39c98e81f87b625882f0f9 GIT binary patch literal 712 zcmV;(0yq6(XJsvAZewzJaCB*JZZ2}kXl!U!MokJbG%!nPH)Bmu za!EySW=Jr0crZd|aY{*WcwtyicXmd2MNL_7K{RtWYi|lIJ|IP7Y&~5_EoX9NVRL05 zTxWGcI$U%hYAAF^UVBF%NOV$OD_S5Sbtg+{Yj8*ka7ArRVOn=Fa5;8lR5VaDWOH_D zL`zChL`PRuPC;`^WHdozXl+AvMm9`rQbK5AMK^Z}EiEk|YFcemWN0-vIA||WWNlMO zbVF!sYEf-&M`L6;O?E41R%vcBR(e!-OhtDJWd&k_>Gks>##pedACoQ4VoV4yF8{Jw3xU!g)Ys(?Kff{VD(yC)8P z`YE5|OT6d$ zx9K%l#gT$;12SwRhpFjZsSKCUPy8+;Og8DQ^w+Z2^KA6Xlg23mZZHQzsU94&D-#1V z^1-kKDBkVKqB=p5Fagu{G#@p0J2~ln<9Vx4T%m^JpNQeR)dH1_-hw<*9)GEZZdeI; zvW}lXH@Y9pHAJoQ0q;+yfN8*DSp?Kr*WEkXOjnTC2( zn`S~VNOT(UoZlb9g3UmP;GY0U+M~OZX3%6|AsPwslvUgLYtj-4+n>v>E7DO+`QY8R z2kkT)hY?*jeo=Hx^v-nAI-D$#Gs|>51p^IwpXn%j@(F}7x-7UcN{OfRf@%0(jyh=Z u3}$Pbq)pY`V2_;vHwkU+GKBxrZIoqp4mdrC$5rhJO?=@GB~0#UZHTP4vnx#i literal 0 HcmV?d00001 diff --git a/secrets/secrets.nix b/secrets/secrets.nix index ea13f5f..ffc6e09 100644 --- a/secrets/secrets.nix +++ b/secrets/secrets.nix @@ -12,6 +12,15 @@ let "postgresql_server.key.age" ]; }; + atlas = { + publicKeys = [ + "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIKZ1OGe8jLyc+72SFUnW4FOKbpqHs7Mym85ESBN4HWV7 pim@x260" + ]; + encryptedFiles = [ + "atlas_host_ed25519.age" + "atlas_user_ed25519.age" + ]; + }; }; in lib.attrsets.mergeAttrsList (builtins.map ({ publicKeys, encryptedFiles }: lib.attrsets.mergeAttrsList (builtins.map diff --git a/terraform/main.tf b/terraform/main.tf index 39bee8b..5fb5315 100644 --- a/terraform/main.tf +++ b/terraform/main.tf @@ -28,8 +28,8 @@ module "setup_jefke" { module "bancomart" { source = "./modules/debian" name = "bancomart" - ram = 2048 - storage = 10 + ram = 4096 + storage = 25 # providers = { # libvirt = libvirt.jefke # } -- 2.45.2