diff --git a/flake.lock b/flake.lock index 5872398..ca825eb 100644 --- a/flake.lock +++ b/flake.lock @@ -206,11 +206,11 @@ ] }, "locked": { - "lastModified": 1693468174, - "narHash": "sha256-anRxrOKI9nJ/ss8CvvIA5lDAI05ke44PZI1F6I2Zk3g=", + "lastModified": 1693923568, + "narHash": "sha256-NTwQIDIbX5cu50FMt1RHyb9G6rWhoTIax+rx9zQ24wM=", "owner": "numtide", "repo": "srvos", - "rev": "28f5734829676013fe72454df923c50c12077c1c", + "rev": "b47df74261b0217336b034c03289a10d5191dbca", "type": "github" }, "original": { diff --git a/modules/flake-module.nix b/modules/flake-module.nix index 283c13d..14d571e 100644 --- a/modules/flake-module.nix +++ b/modules/flake-module.nix @@ -19,10 +19,9 @@ ./single-disk.nix ]; - hetzner-ex101.imports = [ - inputs.srvos.nixosModules.hardware-hetzner-online-intel - ./xfs-lvm-crypto-raid.nix - ./hetzner-ex101.nix + hetzner-ax102.imports = [ + inputs.srvos.nixosModules.hardware-hetzner-online-amd + ./zfs-crypto-raid.nix ./initrd-networking.nix ]; diff --git a/modules/hetzner-ex101.nix b/modules/hetzner-ex101.nix deleted file mode 100644 index cafb6eb..0000000 --- a/modules/hetzner-ex101.nix +++ /dev/null @@ -1,13 +0,0 @@ -{ pkgs, ... }: { - # Enable raid support specifically, this will disable srvos's - # systemd-initrd as well, which currently is not compatible with mdraid. - boot.swraid.enable = true; - systemd.services.mdmonitor.enable = false; - boot.loader.systemd-boot.enable = true; - boot.loader.efi.canTouchEfiVariables = true; - - # We are not limited by zfs, so we can use the latest kernel - boot.kernelPackages = pkgs.linuxPackages_latest; - - powerManagement.cpuFreqGovernor = "powersave"; -} diff --git a/modules/initrd-networking.nix b/modules/initrd-networking.nix index 64d2617..74e55f5 100644 --- a/modules/initrd-networking.nix +++ b/modules/initrd-networking.nix @@ -61,6 +61,7 @@ in boot.initrd.kernelModules = [ "e1000e" # older hetzner machines, 1 GbE nics "igc" # newer herzner machines, 2.5 GbE nics + "igb" # for debugging installation in vms "virtio_pci" "virtio_net" diff --git a/modules/zfs-crypto-raid.nix b/modules/zfs-crypto-raid.nix new file mode 100644 index 0000000..6614dcc --- /dev/null +++ b/modules/zfs-crypto-raid.nix @@ -0,0 +1,89 @@ +{ self, ... }: +let + mirrorBoot = idx: { + type = "disk"; + device = "/dev/nvme${idx}n1"; + content = { + type = "gpt"; + partitions = { + ESP = { + size = "1G"; + type = "EF00"; + content = { + type = "filesystem"; + format = "vfat"; + mountpoint = "/boot${idx}"; + }; + }; + zfs = { + size = "100%"; + content = { + type = "zfs"; + pool = "zroot"; + }; + }; + }; + }; + }; +in +{ + imports = [ + self.inputs.disko.nixosModules.disko + ]; + + networking.hostId = "8425e349"; + + boot.initrd.postDeviceCommands = '' + while ! test -f /tmp/decrypted; do + echo "wait for zfs to be decrypted" + sleep 1 + done + ''; + + boot.loader.grub = { + enable = true; + efiSupport = true; + efiInstallAsRemovable = true; + mirroredBoots = [ + { path = "/boot0"; devices = [ "nodev" ]; } + { path = "/boot1"; devices = [ "nodev" ]; } + ]; + }; + + disko.devices = { + disk = { + x = mirrorBoot "0"; + y = mirrorBoot "1"; + }; + zpool = { + zroot = { + type = "zpool"; + rootFsOptions = { + compression = "lz4"; + "com.sun:auto-snapshot" = "true"; + }; + datasets = { + "root" = { + type = "zfs_fs"; + options = { + mountpoint = "none"; + encryption = "aes-256-gcm"; + keyformat = "hex"; + keylocation = "file:///tmp/secret.key"; + }; + }; + "root/nixos" = { + type = "zfs_fs"; + options.mountpoint = "/"; + mountpoint = "/"; + }; + "root/home" = { + type = "zfs_fs"; + options.mountpoint = "/home"; + mountpoint = "/home"; + }; + }; + }; + }; + }; +} diff --git a/sops/secrets/zfs-key/groups/admins b/sops/secrets/zfs-key/groups/admins new file mode 120000 index 0000000..e5092e3 --- /dev/null +++ b/sops/secrets/zfs-key/groups/admins @@ -0,0 +1 @@ +../../../groups/admins \ No newline at end of file diff --git a/sops/secrets/zfs-key/machines/web01 b/sops/secrets/zfs-key/machines/web01 new file mode 120000 index 0000000..a3c776b --- /dev/null +++ b/sops/secrets/zfs-key/machines/web01 @@ -0,0 +1 @@ +../../../machines/web01 \ No newline at end of file diff --git a/sops/secrets/zfs-key/secret b/sops/secrets/zfs-key/secret new file mode 100644 index 0000000..55a7a2f --- /dev/null +++ b/sops/secrets/zfs-key/secret @@ -0,0 +1,32 @@ +{ + "data": "ENC[AES256_GCM,data:nHDRxFqSwEzz/hAlKstrGLhYgidpDMDwrPTLyTzvez83DuUHdfPWEWVASxkLOrZMm2Qyxt/3ylrKnY9zVakal9I=,iv:WHLk4020GGg8m9upuptB6IZrUcumOkS18K6eJ1Oj4/A=,tag:zW1LA4ymBAuind/mK/hEdA==,type:str]", + "sops": { + "kms": null, + "gcp_kms": null, + "azure_kv": null, + "hc_vault": null, + "age": [ + { + "recipient": "age17n64ahe3wesh8l8lj0zylf4nljdmqn28hvqns2g7hgm9mdkhlsvsjuvkxz", + "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBzNlVRVmtDZXVXV21icWpR\nZ1EvSTVBS05oOW1Kdlh5NzBQY1Fsa0EyVGdRCjZwc3ZLK1Q2d2RTTEZmSWQ5eTEr\naEk1VW9KdkdTQ1h6WDE4Mk1uZEFnNGsKLS0tIHhvWHlzeHdqeW10Q3VuMXRwQU91\nRk9COHRhMTlUR3VJaytDanZJNWsyVmMK+kanz0BW8TCjHw7322/fYTmOmK2Ru6rX\nPPh7vHnfXOuzbAS4Gt5Ci9v371WEMUloTltKQ2Jbmo9Fe/PwZTX4/Q==\n-----END AGE ENCRYPTED FILE-----\n" + }, + { + "recipient": "age17xuvz0fqtynzdmf8rfh4g3e46tx8w3mc6zgytrmuj5v9dhnldgxs7ue7ct", + "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSA0aUlZc0UvNUw4ZHpOSy95\nYTc2K0tzR01XWk9MclJVRFFBYnJrK0s3MW4wCkoyekJwNlM5SElQOEZQNnlFdG1Q\ndEhFbHVwUytCUFFXVXdjbGx2MkNvTm8KLS0tIG9aek9vaitBOHgrcThoY3BKRFlP\nUTBQM25Mc05IKzdzSDZWTDBWSmMzOWcKGXIYFBsz7HThc8Boy9uozkbtcuF6PaxP\n/3Sqgf+hhxPvqTt5JDPH+BdWURN40pdQ7PnpuepJRNYcCTVU8laq+g==\n-----END AGE ENCRYPTED FILE-----\n" + }, + { + "recipient": "age1eq0e6uhjj2tja8v338tkdz8ema2aw5anpuyaq2uru7rt4lq7msyqqut6m2", + "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBrSTdrQmJNZ1NWT1M3LzRT\nVk45WVYwNVByK1pUZVVFa0srZTRIRktScXpnCkJLWnhlK3V0eDJCM1psdzg3WkNw\nMnB6L0xDOHZPencvUGZaNXV4TjR1THMKLS0tIGliclFzOURrRFJXem1qajJMaS9k\nenNXTDJzeXhmQ1d0L2ZCNTF3WTNzZzQKEq5zzmi52CaK+ZfL7enSQ/n3VDmDwje4\nuIg5KwvhhOFdYtqkZFC5XJE6DeHqgjWaACcAPvYGDEOFKWaOVSbOxg==\n-----END AGE ENCRYPTED FILE-----\n" + }, + { + "recipient": "age1vphy2sr6uw4ptsua3gh9khrm2cqyt65t46tusmt44z98qa7q6ymq6prrdl", + "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBUOUhXV0tNdEF1ZGRBVW5L\nWURVRmRHUTdKMXk5bE9BS2VxbXI2ZWp0NnhjCmI3UEltS3U0RG95MmFFSmZMT2hy\nZTF4dVU5SThmNU9ibVMveHgrMWxJNDgKLS0tIEEyQUJUVkMzanJBcWRRYTlIb2Uy\nRko4WmVrM0tsMUxLV1puWW41SWJlUFUKx78ZKKNXf3q+fwdPDVGZc4gjmISUwMwa\n8Fde5g74UDiB9TAbAIYoiAyZLnPibpvJzE1SihTLNmnaJ46KEgtGWA==\n-----END AGE ENCRYPTED FILE-----\n" + } + ], + "lastmodified": "2023-09-05T13:34:30Z", + "mac": "ENC[AES256_GCM,data:Y8OTVsqwjxzPuSXUfPH/Dt6I5x77Y7wLXlP9r3qHnFwQsjEdaXx4ayijMgIf3aY72osRVgFOEDDTjV0RU/rMy5x/KFgaSAfH5CO2vATE3yBERGcURcReGC7OmQN+nGPD9A49CdY6RQyGOTdQ8aWCHc7dmPPY2fcWaLaE1TB76MI=,iv:acowBk0DDYvfhOCAuOsWxJS7w6pz3v40E9WSUZz3y5g=,tag:d4NuATRx2UGZKVZbPNrfng==,type:str]", + "pgp": null, + "unencrypted_suffix": "_unencrypted", + "version": "3.7.3" + } +} \ No newline at end of file diff --git a/targets/web01/configuration.nix b/targets/web01/configuration.nix index 6366879..0d70840 100644 --- a/targets/web01/configuration.nix +++ b/targets/web01/configuration.nix @@ -5,14 +5,14 @@ in { imports = [ self.nixosModules.web01 - self.nixosModules.hetzner-ex101 + self.nixosModules.hetzner-ax102 ]; networking.hostName = "web01"; - systemd.network.networks."10-uplink".networkConfig.Address = "2a01:4f9:3080:282a::1"; + systemd.network.networks."10-uplink".networkConfig.Address = "2a01:4f9:3080:418b::1"; users.users.root.openssh.authorizedKeys.keys = builtins.attrValues admins; - clan.networking.ipv4.address = "65.109.103.5"; - clan.networking.ipv4.gateway = "65.109.103.1"; + clan.networking.ipv4.address = "65.21.12.51"; + clan.networking.ipv4.gateway = "65.21.12.1"; clan.networking.ipv6.address = config.systemd.network.networks."10-uplink".networkConfig.Address; system.stateVersion = "23.05"; diff --git a/targets/web01/decrypt.sh b/targets/web01/decrypt.sh index 727a0ae..3f625d8 100755 --- a/targets/web01/decrypt.sh +++ b/targets/web01/decrypt.sh @@ -3,10 +3,7 @@ set -euox pipefail -HOST=clan.lol -temp=$(mktemp -d) -trap 'rm -rf $temp' EXIT -sops --extract '["cryptsetup_key"]' -d secrets.yaml > "$temp/secret.key" +HOST="clan.lol" while ! ping -4 -W 1 -c 1 "$HOST"; do sleep 1 @@ -15,4 +12,4 @@ while ! timeout 4 ssh -p 2222 "root@$HOST" true; do sleep 1 done -ssh -p 2222 "root@$HOST" "cat > /crypt-ramfs/passphrase" < "$temp/secret.key" +clan secrets get zfs-key | ssh -p 2222 "root@${HOST}" "zpool import -f -a; cat > /tmp/secret.key && zfs load-key -a && touch /tmp/decrypted" diff --git a/targets/web01/nixos-vars.json b/targets/web01/nixos-vars.json index baec0bb..fdfadd7 100644 --- a/targets/web01/nixos-vars.json +++ b/targets/web01/nixos-vars.json @@ -1 +1 @@ -{"ipv6_address":"2a01:4f9:3080:282a::1"} \ No newline at end of file +{"ipv6_address":"2a01:4f9:3080:418b::1"} \ No newline at end of file diff --git a/targets/web01/secrets.auto.tfvars.sops.json b/targets/web01/secrets.auto.tfvars.sops.json index 3a0f983..ff3a5f1 100644 --- a/targets/web01/secrets.auto.tfvars.sops.json +++ b/targets/web01/secrets.auto.tfvars.sops.json @@ -1,5 +1,5 @@ { - "hetznerdns_token": "ENC[AES256_GCM,data:QMMn/j2Lv0Mz/2PhaYQygBjxEoU6f6hL23D5DrderFo=,iv:lOeXBlx/Lb7adzK2SKDKELxXNjlDNWVWQtLp+Mn6YaI=,tag:zTBP/IFdum6T5zITk+WU9A==,type:str]", + "hetznerdns_token": "ENC[AES256_GCM,data:58TlsZWNJ569HfVCKoQKN0ea9XMLC0Y1+F9ltCmQP5A=,iv:uVvbmBuPce8VpCRxFWjOB5TIcUk3u66RflP54+GDPDw=,tag:Ah7zSDVst9WEogW+ejHRag==,type:str]", "sops": { "kms": null, "gcp_kms": null, @@ -19,8 +19,8 @@ "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBQS002SkJjSEZyL3FEbG8r\nWnM1VW9CTy9yaVR5VlJqcnQ0Y2hnZ29QWWxjCnowbnRwUFRabngwWDhWUnp2S0Vt\naytMNEp6UGtnRVJJYzZEOWdUWEIwRUEKLS0tIEI2MUhocHJHb25DaWxYeTFaRzhT\nSGpOd2xXTjVOSUtuSkF0Z2o1QkhNcW8KukbPUN8NsCgCSzoIMnj+WT4WqLcwQSj/\nXh7+7fyZ+PCgtfc0peT8qZ/4sP8XrkXML2G+AFlaTVP7tdFF22En2A==\n-----END AGE ENCRYPTED FILE-----\n" } ], - "lastmodified": "2023-07-13T15:46:42Z", - "mac": "ENC[AES256_GCM,data:TYlJZLdIvaWD96RQg5RnUJyNAR69bze0f0+Ai37BfA0G6VEWDZqvc537vRFk7dj4R8kYCe4q79w7yWmSt30UUZ+SXHSjVcUU9WijO4QprrUz/q4r9ezVZfQLe6disaUDdgsqhQvkQSh0AJ5eJtcr1uVChOViVfH/nk/FfJgUc7s=,iv:ulkInzkkD2ZG8uSQW3vrkAjVD1gWExtultU8zhs2+aU=,tag:bxNP152hKrLBh2zKeGM8KA==,type:str]", + "lastmodified": "2023-09-05T13:47:51Z", + "mac": "ENC[AES256_GCM,data:YI4Vx0lpqoqT9XrIlE6JfE55Cp0YIZzpl8c/hQHXdNJm6QX2qmN9ejtGMXG8DEQ3EAlnRDIFburKY0kZ+Dtv8mCzRVT4tiReWniSh20lT747ukX032PfhFsNYFOlhrd8rAV5GrVyklfo3mnu3pwduXyAo9RT+d9yOj8rGV3wsYk=,iv:fuX6BbTGaTcoaGV+3YbhqvKC8GQtVVKaUp8UvML1uAw=,tag:jT1ghf6j0sv19gxc13dy0Q==,type:str]", "pgp": null, "unencrypted_suffix": "_unencrypted", "version": "3.7.3" diff --git a/targets/web01/terraform.tf b/targets/web01/terraform.tf index 568c7cd..222c827 100644 --- a/targets/web01/terraform.tf +++ b/targets/web01/terraform.tf @@ -11,8 +11,8 @@ module "web01" { nixos_flake_attr = ".#web01" nixos_vars_file = "${path.module}/nixos-vars.json" hetznerdns_token = var.hetznerdns_token - ipv4_address = "65.109.103.5" - ipv6_address = "2a01:4f9:3080:282a::1" + ipv4_address = "65.21.12.51" + ipv6_address = "2a01:4f9:3080:418b::1" sops_secrets_file = "${abspath(path.module)}/secrets.yaml" tags = { Terraform = "true" diff --git a/targets/web01/terraform.tfstate b/targets/web01/terraform.tfstate index 1d1240c..b4df993 100644 --- a/targets/web01/terraform.tfstate +++ b/targets/web01/terraform.tfstate @@ -1,28 +1,196 @@ { - "data": "ENC[AES256_GCM,data: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,iv:T3Mq66G9YZFSc4KAIFsXi6PXuYRriLh7jRHmafdSWi4=,tag:9ypbBtUhx+lUZ/qcfxEnzA==,type:str]", - "sops": { - "kms": null, - "gcp_kms": null, - "azure_kv": null, - "hc_vault": null, - "age": [ - { - "recipient": "age17n64ahe3wesh8l8lj0zylf4nljdmqn28hvqns2g7hgm9mdkhlsvsjuvkxz", - "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBOZnl3VkpzVDBPR1JSVTZT\nWlcxWDNqaUY0dVd1ZTBQT3ptbWIvNzAwT0U4Cm5IM1l6U0JxYVhZSnlna1I3WUZ5\nSkU5OHBGY3g0QzBEQlo2ZTRNQ0duSkkKLS0tIElzMHoxR21Uak9TQUNsNXZ0ZkpG\nZmllTHk1Q3IwZHFuOWlXZ0wxQXE3ZDQKl8LVnKRze/dcfHBysb59FHgUBNRYlKhM\nyS/dKRT9uNPcWHolP8g/jK4H+hdYOt0rNM1UrTonumOHZmFSzyeTNg==\n-----END AGE ENCRYPTED FILE-----\n" - }, - { - "recipient": "age1eq0e6uhjj2tja8v338tkdz8ema2aw5anpuyaq2uru7rt4lq7msyqqut6m2", - "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBiczl0enRrTDVRaG0veU5l\nNjhDTndKWXl4U0lTemt2Mm1DRHVhczRHeVRZCjQyWGtLKzB3eFo2LzIzUWQ4dDB6\ncWducVk4VUtyalpEei9oN2Q3aktTTlUKLS0tIDVhTStGdGM3M2VmRVNaT1Qrb3o4\nYWNuV3hRa212Z0hKMXRLeDY3WmltL0kKk1kh8DDzgVR8FOnx/L/ZGWdBa8kuz8YQ\nrr0GeAc4zBycYXMXHKtgXgnJQJZaZbyJ3EgFkYXOxuRyj3sCI8H/BA==\n-----END AGE ENCRYPTED FILE-----\n" - }, - { - "recipient": "age1vphy2sr6uw4ptsua3gh9khrm2cqyt65t46tusmt44z98qa7q6ymq6prrdl", - "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSAwY2FJZnpSNld4QlhpekJB\nRzExc2hDSXI4czcwaGFIWmtrYTNDbUsrandFCkxMRHkrMW1xdzlWQUdxekh1R09o\naGNWQzdEZWJQYk8rbmgwYkFMaGhhYWsKLS0tIHZ4a2hob0JEbVpndHpTSklmazFN\nMGJTWUErVVA3dWRHYlV1Z1paOHdvVGcKCt4Q+Yg+GxvnjQURc4u3JO+n3XHlTzYY\njIgeuMNiVKHEGulTkWI7t0Sgax0C5SXiFbujdTkY9G/YFSWf2j4ojA==\n-----END AGE ENCRYPTED FILE-----\n" - } - ], - "lastmodified": "2023-07-18T14:57:41Z", - "mac": "ENC[AES256_GCM,data:rk8z4Uo21ARXXDk5tq9lBB2q/R4My/FNXzFoGEi5Vb0ToOYQ/2ZDbVNShzE0r4HXsCSLx/UdECOUz+nPE16BYr8wFgIOiNnw/3Oh9wo4/OzRJivZ43uwf1x9LClGbUpCNqq/YiTeskFierCFoE0lepnTCm+6vnIamAI5M0ClBQ4=,iv:Krn62ShWUCjgC8ubjzNChrHHPuM5zp69khH8ZL4M6BQ=,tag:jOlkOC4beWHFYjCDmQfl/g==,type:str]", - "pgp": null, - "unencrypted_suffix": "_unencrypted", - "version": "3.7.3" - } -} \ No newline at end of file + "version": 4, + "terraform_version": "1.5.6", + "serial": 65, + "lineage": "d10a0b8e-ffcf-9a76-59fd-bb1ce3100232", + "outputs": {}, + "resources": [ + { + "module": "module.web01", + "mode": "managed", + "type": "hetznerdns_record", + "name": "adsp", + "provider": "module.web01.provider[\"registry.terraform.io/timohirt/hetznerdns\"]", + "instances": [] + }, + { + "module": "module.web01", + "mode": "managed", + "type": "hetznerdns_record", + "name": "dkim", + "provider": "module.web01.provider[\"registry.terraform.io/timohirt/hetznerdns\"]", + "instances": [] + }, + { + "module": "module.web01", + "mode": "managed", + "type": "hetznerdns_record", + "name": "dmarc", + "provider": "module.web01.provider[\"registry.terraform.io/timohirt/hetznerdns\"]", + "instances": [] + }, + { + "module": "module.web01", + "mode": "managed", + "type": "hetznerdns_record", + "name": "matrix", + "provider": "module.web01.provider[\"registry.terraform.io/timohirt/hetznerdns\"]", + "instances": [] + }, + { + "module": "module.web01", + "mode": "managed", + "type": "hetznerdns_record", + "name": "server_a", + "provider": "module.web01.provider[\"registry.terraform.io/timohirt/hetznerdns\"]", + "instances": [] + }, + { + "module": "module.web01", + "mode": "managed", + "type": "hetznerdns_record", + "name": "server_aaaa", + "provider": "module.web01.provider[\"registry.terraform.io/timohirt/hetznerdns\"]", + "instances": [] + }, + { + "module": "module.web01", + "mode": "managed", + "type": "hetznerdns_record", + "name": "spf", + "provider": "module.web01.provider[\"registry.terraform.io/timohirt/hetznerdns\"]", + "instances": [] + }, + { + "module": "module.web01", + "mode": "managed", + "type": "local_file", + "name": "nixos_vars", + "provider": "provider[\"registry.terraform.io/hashicorp/local\"]", + "instances": [ + { + "schema_version": 0, + "attributes": { + "content": "{\"ipv6_address\":\"2a01:4f9:3080:418b::1\"}", + "content_base64": null, + "content_base64sha256": "UTjgeG7keeBIESTpDr/Cxd7C73CEQc20b9OcRSZFpgQ=", + "content_base64sha512": "dCAPFKN7HJlPec7pewTebG00aSugkZSlBj0dNkeTcdmmK9Xxvzq8BiVxAfD91ohje8D4ZBn3O9bUhG7lL2+x8w==", + "content_md5": "beb5745d8b813389169c7297c34fb9e9", + "content_sha1": "e6840ffbbd7003849a49f4cf931ba1a777b0fb7f", + "content_sha256": "5138e0786ee479e0481124e90ebfc2c5dec2ef708441cdb46fd39c452645a604", + "content_sha512": "74200f14a37b1c994f79cee97b04de6c6d34692ba09194a5063d1d36479371d9a62bd5f1bf3abc06257101f0fdd688637bc0f86419f73bd6d4846ee52f6fb1f3", + "directory_permission": "0777", + "file_permission": "600", + "filename": "./nixos-vars.json", + "id": "e6840ffbbd7003849a49f4cf931ba1a777b0fb7f", + "sensitive_content": null, + "source": null + }, + "sensitive_attributes": [] + } + ] + }, + { + "module": "module.web01.module.deploy.module.install", + "mode": "managed", + "type": "null_resource", + "name": "nixos-remote", + "provider": "provider[\"registry.terraform.io/hashicorp/null\"]", + "instances": [ + { + "schema_version": 0, + "attributes": { + "id": "1309623161403473713", + "triggers": { + "instance_id": "web01" + } + }, + "sensitive_attributes": [], + "dependencies": [ + "module.web01.module.deploy.module.partitioner-build.data.external.nix-build", + "module.web01.module.deploy.module.system-build.data.external.nix-build" + ] + } + ] + }, + { + "module": "module.web01.module.deploy.module.nixos-rebuild[0]", + "mode": "managed", + "type": "null_resource", + "name": "nixos-rebuild", + "provider": "provider[\"registry.terraform.io/hashicorp/null\"]", + "instances": [ + { + "schema_version": 0, + "attributes": { + "id": "3275000187934052681", + "triggers": { + "store_path": "/nix/store/rp6gxnndmh2ylalmawws2543nfrh7kq1-nixos-system-web01-23.11.20230903.d816b5a" + } + }, + "sensitive_attributes": [], + "dependencies": [ + "module.web01.module.deploy.module.install.null_resource.nixos-remote", + "module.web01.module.deploy.module.partitioner-build.data.external.nix-build", + "module.web01.module.deploy.module.system-build.data.external.nix-build" + ] + } + ] + }, + { + "module": "module.web01.module.deploy.module.partitioner-build", + "mode": "data", + "type": "external", + "name": "nix-build", + "provider": "provider[\"registry.terraform.io/hashicorp/external\"]", + "instances": [ + { + "schema_version": 0, + "attributes": { + "id": "-", + "program": [ + ".terraform/modules/web01.deploy/terraform/nix-build/nix-build.sh" + ], + "query": { + "attribute": ".#nixosConfigurations.web01.config.system.build.diskoScript", + "file": null + }, + "result": { + "out": "/nix/store/mgk3kyy4v98xb2iycsaggrs76abswzva-disko" + }, + "working_dir": null + }, + "sensitive_attributes": [] + } + ] + }, + { + "module": "module.web01.module.deploy.module.system-build", + "mode": "data", + "type": "external", + "name": "nix-build", + "provider": "provider[\"registry.terraform.io/hashicorp/external\"]", + "instances": [ + { + "schema_version": 0, + "attributes": { + "id": "-", + "program": [ + ".terraform/modules/web01.deploy/terraform/nix-build/nix-build.sh" + ], + "query": { + "attribute": ".#nixosConfigurations.web01.config.system.build.toplevel", + "file": null + }, + "result": { + "out": "/nix/store/rp6gxnndmh2ylalmawws2543nfrh7kq1-nixos-system-web01-23.11.20230903.d816b5a" + }, + "working_dir": null + }, + "sensitive_attributes": [] + } + ] + } + ], + "check_results": null +} diff --git a/terraform/web01/decrypt-ssh-secrets.sh b/terraform/web01/decrypt-ssh-secrets.sh new file mode 100755 index 0000000..7cae2cc --- /dev/null +++ b/terraform/web01/decrypt-ssh-secrets.sh @@ -0,0 +1,20 @@ +#!/usr/bin/env bash + +mkdir -p etc/ssh var/lib/secrets + +SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" + +umask 0177 +(cd "$SCRIPT_DIR" && clan secrets get initrd_ssh_key) > ./var/lib/secrets/initrd_ssh_key + +# restore umask +umask 0022 + +for keyname in ssh_host_rsa_key ssh_host_rsa_key.pub ssh_host_ed25519_key ssh_host_ed25519_key.pub; do + if [[ $keyname == *.pub ]]; then + umask 0133 + else + umask 0177 + fi + (cd "$SCRIPT_DIR" && clan secrets get "$keyname") >"./etc/ssh/$keyname" +done diff --git a/terraform/web01/decrypt-zfs-key.sh b/terraform/web01/decrypt-zfs-key.sh new file mode 100755 index 0000000..12ce67f --- /dev/null +++ b/terraform/web01/decrypt-zfs-key.sh @@ -0,0 +1,7 @@ +#!/usr/bin/env bash + +set -euo pipefail + +SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" +cd "$SCRIPT_DIR" +clan secrets get zfs-key diff --git a/terraform/web01/main.tf b/terraform/web01/main.tf index 81f3ed5..290db32 100644 --- a/terraform/web01/main.tf +++ b/terraform/web01/main.tf @@ -1,28 +1,18 @@ locals { } -resource "null_resource" "nixos-anywhere" { - triggers = { - instance_id = var.ipv4_address - } - connection { - type = "ssh" - user = "root" - host = var.ipv4_address - } - provisioner "remote-exec" { - # needed because kexec is broken - # https://github.com/numtide/nixos-anywhere/issues/136 - script = "${path.module}/nixosify.sh" - } - provisioner "local-exec" { - environment = { - HOST = var.ipv4_address - FLAKE_ATTR = var.nixos_flake_attr - SOPS_SECRETS_FILE = var.sops_secrets_file - } - command = "bash -x ${path.module}/install.sh" - } +module "deploy" { + source = "github.com/numtide/nixos-anywhere//terraform/all-in-one?ref=extra-files" + nixos_system_attr = ".#nixosConfigurations.web01.config.system.build.toplevel" + nixos_partitioner_attr = ".#nixosConfigurations.web01.config.system.build.diskoScript" + target_host = var.ipv4_address + instance_id = "web01" + debug_logging = true + extra_files_script = "${path.module}/decrypt-ssh-secrets.sh" + disk_encryption_key_scripts = [{ + path = "/tmp/secret.key" + script = "${path.module}/decrypt-zfs-key.sh" + }] } locals {