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 +}