commit bf2c813abd2f88090d2279fcd22b1fceaad3d97a Author: min Date: Sun Oct 13 16:16:39 2024 -0400 Initial commit diff --git a/.envrc b/.envrc new file mode 100644 index 0000000..f41a01c --- /dev/null +++ b/.envrc @@ -0,0 +1,4 @@ +if ! has nix_direnv_version || ! nix_direnv_version 3.0.5; then + source_url "https://raw.githubusercontent.com/nix-community/nix-direnv/3.0.5/direnvrc" "sha256-RuwIS+QKFj/T9M2TFXScjBsLR6V3A17YVoEW/Q6AZ1w=" +fi +use flake diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e43eb3b --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +/.direnv + +# files decrypted by vscode-sops +.decrypted~* + +/tmp diff --git a/.sops.yaml b/.sops.yaml new file mode 100644 index 0000000..8f4ffd7 --- /dev/null +++ b/.sops.yaml @@ -0,0 +1,22 @@ +keys: + - &min 78795D9EBD425CBB3E850BC45DF91852CB14CEFF + - &eidola age1uqxzduupzes3tgfrrlret0n6thyldmlef60nqfzk689lmg6yayvsqpwxj6 + - &silver age19yhycdgqczrvttszq97ccljh684x3r7f5dj4p0wdwqsrusqlcayse0vsh3 +creation_rules: + - path_regex: k8s/apps/.*/secrets/.*\.yaml$ + encrypted_regex: "^(data|stringData)$" + key_groups: + - pgp: + - *min + - path_regex: secrets/eidola\.yaml$ + key_groups: + - pgp: + - *min + age: + - *eidola + - path_regex: secrets/silver\.yaml$ + key_groups: + - pgp: + - *min + age: + - *silver diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..13ffdd2 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,12 @@ +{ + "nix.enableLanguageServer": true, + "nix.serverSettings": { + "nil": { + "formatting": { + "command": [ + "alejandra" + ] + } + } + }, +} \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..10862b2 --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ +# infra + +infrastructure-as-code repo for systems I manage diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..a804e0e --- /dev/null +++ b/flake.lock @@ -0,0 +1,293 @@ +{ + "nodes": { + "breeze": { + "inputs": { + "crane": "crane", + "flake-utils": "flake-utils", + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1726958203, + "narHash": "sha256-7Upw1xPlncrFVegoAD0+16/6IoWedszIWPinFO6bJ2g=", + "ref": "refs/heads/main", + "rev": "495aee464d1570f00069bc8ea44b708b2f96e438", + "revCount": 60, + "type": "git", + "url": "https://git.min.rip/min/breeze.git" + }, + "original": { + "type": "git", + "url": "https://git.min.rip/min/breeze.git" + } + }, + "crane": { + "locked": { + "lastModified": 1725409566, + "narHash": "sha256-PrtLmqhM6UtJP7v7IGyzjBFhbG4eOAHT6LPYOFmYfbk=", + "owner": "ipetkov", + "repo": "crane", + "rev": "7e4586bad4e3f8f97a9271def747cf58c4b68f3c", + "type": "github" + }, + "original": { + "owner": "ipetkov", + "repo": "crane", + "type": "github" + } + }, + "deploy-rs": { + "inputs": { + "flake-compat": "flake-compat", + "nixpkgs": [ + "nixpkgs" + ], + "utils": "utils" + }, + "locked": { + "lastModified": 1718194053, + "narHash": "sha256-FaGrf7qwZ99ehPJCAwgvNY5sLCqQ3GDiE/6uLhxxwSY=", + "owner": "serokell", + "repo": "deploy-rs", + "rev": "3867348fa92bc892eba5d9ddb2d7a97b9e127a8a", + "type": "github" + }, + "original": { + "owner": "serokell", + "repo": "deploy-rs", + "type": "github" + } + }, + "disko": { + "inputs": { + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1726842196, + "narHash": "sha256-u9h03JQUuQJ607xmti9F9Eh6E96kKUAGP+aXWgwm70o=", + "owner": "nix-community", + "repo": "disko", + "rev": "51994df8ba24d5db5459ccf17b6494643301ad28", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "disko", + "type": "github" + } + }, + "flake-compat": { + "flake": false, + "locked": { + "lastModified": 1696426674, + "narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=", + "owner": "edolstra", + "repo": "flake-compat", + "rev": "0f9255e01c2351cc7d116c072cb317785dd33b33", + "type": "github" + }, + "original": { + "owner": "edolstra", + "repo": "flake-compat", + "type": "github" + } + }, + "flake-parts": { + "inputs": { + "nixpkgs-lib": "nixpkgs-lib" + }, + "locked": { + "lastModified": 1726153070, + "narHash": "sha256-HO4zgY0ekfwO5bX0QH/3kJ/h4KvUDFZg8YpkNwIbg1U=", + "owner": "hercules-ci", + "repo": "flake-parts", + "rev": "bcef6817a8b2aa20a5a6dbb19b43e63c5bf8619a", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "flake-parts", + "type": "github" + } + }, + "flake-utils": { + "inputs": { + "systems": "systems" + }, + "locked": { + "lastModified": 1726560853, + "narHash": "sha256-X6rJYSESBVr3hBoH0WbKE5KvhPU5bloyZ2L4K60/fPQ=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "c1dfcf08411b08f6b8615f7d8971a2bfa81d5e8a", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "impermanence": { + "locked": { + "lastModified": 1725690722, + "narHash": "sha256-4qWg9sNh5g1qPGO6d/GV2ktY+eDikkBTbWSg5/iD2nY=", + "owner": "nix-community", + "repo": "impermanence", + "rev": "63f4d0443e32b0dd7189001ee1894066765d18a5", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "impermanence", + "type": "github" + } + }, + "min-rip": { + "flake": false, + "locked": { + "lastModified": 1726983284, + "narHash": "sha256-JNWGpZ0L6Eu5ZgZsW1tLVbO3ujGKcY1e6SbvUR66V9s=", + "ref": "refs/heads/main", + "rev": "d22d0e86beafd6a0de643ab0d28d40e761395132", + "revCount": 24, + "type": "git", + "url": "ssh://git@git.min.rip/min/min.rip.git" + }, + "original": { + "type": "git", + "url": "ssh://git@git.min.rip/min/min.rip.git" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1726838390, + "narHash": "sha256-NmcVhGElxDbmEWzgXsyAjlRhUus/nEqPC5So7BOJLUM=", + "owner": "nixos", + "repo": "nixpkgs", + "rev": "944b2aea7f0a2d7c79f72468106bc5510cbf5101", + "type": "github" + }, + "original": { + "owner": "nixos", + "ref": "nixos-24.05", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs-lib": { + "locked": { + "lastModified": 1725233747, + "narHash": "sha256-Ss8QWLXdr2JCBPcYChJhz4xJm+h/xjl4G0c0XlP6a74=", + "type": "tarball", + "url": "https://github.com/NixOS/nixpkgs/archive/356624c12086a18f2ea2825fed34523d60ccc4e3.tar.gz" + }, + "original": { + "type": "tarball", + "url": "https://github.com/NixOS/nixpkgs/archive/356624c12086a18f2ea2825fed34523d60ccc4e3.tar.gz" + } + }, + "nixpkgs-stable": { + "locked": { + "lastModified": 1725762081, + "narHash": "sha256-vNv+aJUW5/YurRy1ocfvs4q/48yVESwlC/yHzjkZSP8=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "dc454045f5b5d814e5862a6d057e7bb5c29edc05", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "release-24.05", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "breeze": "breeze", + "deploy-rs": "deploy-rs", + "disko": "disko", + "flake-parts": "flake-parts", + "impermanence": "impermanence", + "min-rip": "min-rip", + "nixpkgs": "nixpkgs", + "sops-nix": "sops-nix" + } + }, + "sops-nix": { + "inputs": { + "nixpkgs": [ + "nixpkgs" + ], + "nixpkgs-stable": "nixpkgs-stable" + }, + "locked": { + "lastModified": 1726524647, + "narHash": "sha256-qis6BtOOBBEAfUl7FMHqqTwRLB61OL5OFzIsOmRz2J4=", + "owner": "Mic92", + "repo": "sops-nix", + "rev": "e2d404a7ea599a013189aa42947f66cede0645c8", + "type": "github" + }, + "original": { + "owner": "Mic92", + "repo": "sops-nix", + "type": "github" + } + }, + "systems": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + }, + "systems_2": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + }, + "utils": { + "inputs": { + "systems": "systems_2" + }, + "locked": { + "lastModified": 1701680307, + "narHash": "sha256-kAuep2h5ajznlPMD9rnQyffWG8EM/C73lejGofXvdM8=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "4022d587cbbfd70fe950c1e2083a02621806a725", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..c2f119e --- /dev/null +++ b/flake.nix @@ -0,0 +1,68 @@ +{ + description = "k8s land"; + + inputs = { + nixpkgs.url = "github:nixos/nixpkgs/nixos-24.05"; + + flake-parts.url = "github:hercules-ci/flake-parts"; + + sops-nix.url = "github:Mic92/sops-nix"; + sops-nix.inputs.nixpkgs.follows = "nixpkgs"; + + disko.url = "github:nix-community/disko"; + disko.inputs.nixpkgs.follows = "nixpkgs"; + + deploy-rs.url = "github:serokell/deploy-rs"; + deploy-rs.inputs.nixpkgs.follows = "nixpkgs"; + + impermanence.url = "github:nix-community/impermanence"; + + min-rip.url = "git+ssh://git@git.min.rip/min/min.rip.git"; + min-rip.flake = false; + + breeze.url = "git+https://git.min.rip/min/breeze.git"; + breeze.inputs.nixpkgs.follows = "nixpkgs"; + }; + + outputs = inputs @ {self, ...}: + inputs.flake-parts.lib.mkFlake {inherit inputs;} { + flake = let + hosts = import ./nixos/hosts {inherit inputs;}; + in { + inherit (hosts) nixosConfigurations deploy; + }; + + systems = ["x86_64-linux"]; + + perSystem = { + pkgs, + system, + ... + }: { + devShells.default = pkgs.mkShell { + packages = with pkgs; [ + sops + ssh-to-age + # not included: age, gpg, pcscd, etc. + + deploy-rs + nixos-anywhere + + argocd + kubectl + kubernetes-helm + minikube + cilium-cli + hubble + + yamllint + + nil + alejandra + statix + deadnix + ]; + }; + }; + }; +} diff --git a/nixos/hosts/README.md b/nixos/hosts/README.md new file mode 100644 index 0000000..74b1195 --- /dev/null +++ b/nixos/hosts/README.md @@ -0,0 +1,31 @@ +# NixOS Hosts + +This directory contains configs for all NixOS hosts. + +## Installing a new machine + +✩ = on target machine, everything else is on your own device +1. ✩ If SB is desired, set a UEFI password and set Secure Boot to "Setup Mode". (This may be done by clearing Secure Boot keys on some boards) +2. ✩ Boot the [NixOS remote deploy image](https://github.com/nix-community/nixos-images/releases/tag/nixos-24.05) +3. Copy an appropriate config as a base +4. ``` + mkdir tempkeys + ../scripts/make_base_keys.sh + cd .. + ``` +5. Create a `secrets/NAME_OF_HOST.yaml` file. +6. Add the AGE key from `make_base_keys.sh` into `.sops.yaml`. Add necessary keys to `secrets/NAME_OF_HOST.yaml`. Make sure that the host's `secrets.nix` uses the proper YAML secrets file. +7. Ensure disk partitioning is correct. Make sure to change the rootfs disk in `disk-config.nix`. +8. Generate secure passwords, find their hashes with `mkpasswd -m sha-512`, and put them into the `root-pw` and `user-pw` properties of your , again ensuring there's no EOLs +9. Customize the config however you want +10. `./scripts/install.sh -c NAME_OF_HOST -k tempkeys root@IP_OF_HOST` +11. Delete the `tempkeys` folder if you're done with it +12. That is about it + +## Deploying changes remotely + +1. Make your changes +2. `nix flake check` +3. Commit them to Git +4. `deploy`, or if you're making breaking network changes where it's necessary, `deploy --magic-rollback false` +5. Hopefully they work! I have not worked out a way to do a manual rollback remotely yet diff --git a/nixos/hosts/default.nix b/nixos/hosts/default.nix new file mode 100644 index 0000000..66c7fee --- /dev/null +++ b/nixos/hosts/default.nix @@ -0,0 +1,49 @@ +{inputs, ...}: let + systems = { + eidola = import ./eidola {inherit inputs;}; + silver = import ./silver {inherit inputs;}; + }; + + inherit (inputs.nixpkgs) lib; + + makeNixosConfigurations = systems: + lib.mapAttrs + (name: system: + lib.nixosSystem { + inherit (system) system; + + modules = + system.modules + ++ [ + { + _module.args = { + inherit inputs; + }; + } + ] + ++ (import ../modules); + }) + systems; + + makeDeployRsNodes = systems: + lib.mapAttrs + (name: system: { + hostname = system.deployment.host; + + profiles.system = { + sshUser = system.deployment.user; + sshOpts = ["-p" "${toString system.deployment.port}"]; + remoteBuild = system.deployment.buildOnTarget; + + magicRollback = true; + + path = + inputs.deploy-rs.lib.${system.system}.activate.nixos + inputs.self.nixosConfigurations.${name}; + }; + }) + systems; +in { + nixosConfigurations = makeNixosConfigurations systems; + deploy.nodes = makeDeployRsNodes systems; +} diff --git a/nixos/hosts/eidola/configuration.nix b/nixos/hosts/eidola/configuration.nix new file mode 100644 index 0000000..8f2d856 --- /dev/null +++ b/nixos/hosts/eidola/configuration.nix @@ -0,0 +1,67 @@ +{ + config, + pkgs, + ... +}: { + imports = [ + ./hardware.nix + ./disk-config.nix + ./mounts.nix + ./secrets.nix + ]; + + networking.hostName = "eidola"; # Define your hostname. + time.timeZone = "America/New_York"; # Set your time zone. + + # Allow unfree packages (firmware) + nixpkgs.config.allowUnfree = true; + + # Basic networking + networking.networkmanager.enable = true; + networking.firewall.enable = true; + + # Locales + i18n.defaultLocale = "en_US.UTF-8"; + console = { + keyMap = "us"; + }; + + # Users - eidola & root + users.users = { + root.hashedPasswordFile = config.sops.secrets."root-pw".path; + + eidola = { + isNormalUser = true; + extraGroups = ["networkmanager" "wheel"]; + hashedPasswordFile = config.sops.secrets."user-pw".path; + openssh.authorizedKeys.keys = import ../../keys/ssh.nix; + }; + }; + + # Packages + environment.systemPackages = with pkgs; [ + rsync + git + vim + fastfetch + htop + ]; + environment.variables.EDITOR = "vim"; + + # Enable ssh server + services.openssh = { + enable = true; + settings.PasswordAuthentication = false; + settings.KbdInteractiveAuthentication = false; + }; + + # My modules + gen.system.hardening.disableSack = true; + gen.system.bootloader.luksSsh = { + enable = true; + port = 48722; + hostKeys = ["/persist/etc/secrets/initrd/ssh_host_ed25519_key"]; + }; + + system.stateVersion = "24.05"; +} diff --git a/nixos/hosts/eidola/default.nix b/nixos/hosts/eidola/default.nix new file mode 100644 index 0000000..c8b1e0a --- /dev/null +++ b/nixos/hosts/eidola/default.nix @@ -0,0 +1,23 @@ +{inputs, ...}: rec { + system = "x86_64-linux"; + + pkgs = import inputs.nixpkgs { + inherit system; + }; + + deployment = { + host = "192.168.1.159"; + user = "root"; + port = 22; + + buildOnTarget = false; + }; + + modules = [ + inputs.sops-nix.nixosModules.sops + inputs.disko.nixosModules.disko + inputs.impermanence.nixosModules.impermanence + inputs.breeze.nixosModules.${system}.breeze + ./configuration.nix + ]; +} diff --git a/nixos/hosts/eidola/disk-config.nix b/nixos/hosts/eidola/disk-config.nix new file mode 100644 index 0000000..44467ec --- /dev/null +++ b/nixos/hosts/eidola/disk-config.nix @@ -0,0 +1,94 @@ +{ + disko.devices = { + disk = { + main = { + type = "disk"; + device = "/dev/nvme0n1"; + content = { + type = "gpt"; + + partitions = { + boot = { + name = "boot"; + size = "1M"; + type = "EF02"; + }; + + esp = { + name = "ESP"; + type = "EF00"; + size = "1G"; + content = { + type = "filesystem"; + format = "vfat"; + mountpoint = "/boot"; + mountOptions = ["defaults"]; + }; + }; + + luks = { + size = "100%"; + content = { + type = "luks"; + name = "encrypted"; + extraOpenArgs = []; + settings = { + allowDiscards = true; + bypassWorkqueues = true; + }; + passwordFile = "/tmp/luks-pw"; + content = { + type = "lvm_pv"; + vg = "pool"; + }; + }; + }; + }; + }; + }; + }; + + nodev = { + "/" = { + fsType = "tmpfs"; + mountOptions = [ + "defaults" + "size=8G" + "mode=755" + ]; + }; + }; + + lvm_vg = { + pool = { + type = "lvm_vg"; + lvs = { + nix = { + size = "64G"; + content = { + type = "filesystem"; + format = "ext4"; + mountpoint = "/nix"; + mountOptions = [ + "defaults" + "noatime" + ]; + }; + }; + persist = { + size = "100%FREE"; + content = { + type = "filesystem"; + format = "ext4"; + mountpoint = "/persist"; + mountOptions = [ + "defaults" + "noatime" + ]; + }; + }; + }; + }; + }; + }; +} diff --git a/nixos/hosts/eidola/hardware.nix b/nixos/hosts/eidola/hardware.nix new file mode 100644 index 0000000..950cb3c --- /dev/null +++ b/nixos/hosts/eidola/hardware.nix @@ -0,0 +1,43 @@ +# Do not modify this file! It was generated by ‘nixos-generate-config’ +# and may be overwritten by future invocations. Please make changes +# to /etc/nixos/configuration.nix instead. +{ + config, + lib, + modulesPath, + ... +}: { + imports = [ + (modulesPath + "/installer/scan/not-detected.nix") + ]; + + boot = { + loader = { + efi.canTouchEfiVariables = true; + + timeout = 2; + systemd-boot = { + enable = true; + configurationLimit = 3; + }; + }; + + initrd = { + availableKernelModules = ["xhci_pci" "ahci" "nvme" "usbhid" "usb_storage" "sd_mod" "r8169"]; + kernelModules = []; + }; + kernelModules = ["kvm-amd"]; + extraModulePackages = []; + }; + + hardware.enableAllFirmware = true; + + # Enables DHCP on each ethernet and wireless interface. In case of scripted networking + # (the default) this is the recommended approach. When using systemd-networkd it's + # still possible to use this option, but it's recommended to use it in conjunction + # with explicit per-interface declarations with `networking.interfaces..useDHCP`. + # networking.useDHCP = lib.mkDefault false; + + nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux"; + hardware.cpu.amd.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware; +} diff --git a/nixos/hosts/eidola/mounts.nix b/nixos/hosts/eidola/mounts.nix new file mode 100644 index 0000000..4816c59 --- /dev/null +++ b/nixos/hosts/eidola/mounts.nix @@ -0,0 +1,27 @@ +{...}: { + environment.persistence."/persist" = { + hideMounts = true; + directories = [ + # "/etc/secureboot" + "/etc/ssh" + "/etc/secrets" + + "/var/log" + "/var/lib/systemd/coredump" + "/var/lib/nixos" + "/var/db/sudo" + ]; + files = [ + "/etc/machine-id" + ]; + }; + + fileSystems = { + "/".neededForBoot = true; + "/etc/ssh" = { + depends = ["/persist"]; + neededForBoot = true; + }; + "/persist".neededForBoot = true; # no further config is needed, disko handles the rest + }; +} diff --git a/nixos/hosts/eidola/secrets.nix b/nixos/hosts/eidola/secrets.nix new file mode 100644 index 0000000..3f0718f --- /dev/null +++ b/nixos/hosts/eidola/secrets.nix @@ -0,0 +1,9 @@ +{...}: { + sops = { + defaultSopsFile = ../../../secrets/eidola.yaml; + age.sshKeyPaths = ["/persist/etc/ssh/ssh_host_ed25519_key"]; + + secrets."root-pw" = {neededForUsers = true;}; + secrets."user-pw" = {neededForUsers = true;}; + }; +} diff --git a/nixos/hosts/silver/configuration.nix b/nixos/hosts/silver/configuration.nix new file mode 100644 index 0000000..2f287a1 --- /dev/null +++ b/nixos/hosts/silver/configuration.nix @@ -0,0 +1,99 @@ +{ + config, + pkgs, + ... +}: let + net = { + address = "107.152.41.67"; + prefixLength = 24; + subnet = "255.255.255.0"; + gateway = "107.152.41.1"; + interface = "eth0"; + }; +in { + imports = [ + ./hardware.nix + ./disk-config.nix + ./mounts.nix + ./secrets.nix + ./services + ]; + + networking.hostName = "silver"; # Define your hostname. + time.timeZone = "America/Chicago"; # Set your time zone. + + # Allow unfree packages (firmware) + nixpkgs.config.allowUnfree = true; + + # Basic networking + networking.networkmanager.enable = true; + networking.firewall.enable = true; + + # Networking - IP configuration + networking = { + enableIPv6 = false; + + defaultGateway = { + address = net.gateway; + inherit (net) interface; + }; + + interfaces.${net.interface} = { + useDHCP = false; + ipv4.addresses = [ + {inherit (net) address prefixLength;} + ]; + }; + }; + boot.kernelParams = [ + # Manual IP configuration for initrd + "ip=${net.address}::${net.gateway}:${net.subnet}::${net.interface}:off" + ]; + + # Locales + i18n.defaultLocale = "en_US.UTF-8"; + console = { + keyMap = "us"; + }; + + # Users - silver & root + users.users = { + root.hashedPasswordFile = config.sops.secrets."root-pw".path; + + silver = { + isNormalUser = true; + extraGroups = ["networkmanager" "wheel"]; + hashedPasswordFile = config.sops.secrets."user-pw".path; + openssh.authorizedKeys.keys = import ../../keys/ssh.nix; + }; + }; + + # Packages + environment.systemPackages = with pkgs; [ + rsync + git + vim + fastfetch + htop + speedtest-cli + ]; + environment.variables.EDITOR = "vim"; + + # Enable ssh server + services.openssh = { + enable = true; + settings.PasswordAuthentication = false; + settings.KbdInteractiveAuthentication = false; + ports = [12208]; + }; + + # My modules + gen.system.hardening.disableSack = true; + gen.system.bootloader.luksSsh = { + enable = true; + port = 48722; + hostKeys = ["/persist/etc/secrets/initrd/ssh_host_ed25519_key"]; + }; + + system.stateVersion = "24.05"; +} diff --git a/nixos/hosts/silver/default.nix b/nixos/hosts/silver/default.nix new file mode 100644 index 0000000..14ce09f --- /dev/null +++ b/nixos/hosts/silver/default.nix @@ -0,0 +1,23 @@ +{inputs, ...}: rec { + system = "x86_64-linux"; + + pkgs = import inputs.nixpkgs { + inherit system; + }; + + deployment = { + host = "min.rip"; + user = "root"; + port = 12208; + + buildOnTarget = false; + }; + + modules = [ + inputs.sops-nix.nixosModules.sops + inputs.disko.nixosModules.disko + inputs.impermanence.nixosModules.impermanence + inputs.breeze.nixosModules.${system}.breeze + ./configuration.nix + ]; +} diff --git a/nixos/hosts/silver/disk-config.nix b/nixos/hosts/silver/disk-config.nix new file mode 100644 index 0000000..e33e7d7 --- /dev/null +++ b/nixos/hosts/silver/disk-config.nix @@ -0,0 +1,94 @@ +{ + disko.devices = { + disk = { + main = { + type = "disk"; + device = "/dev/vda"; + content = { + type = "gpt"; + + partitions = { + bios = { + name = "bios"; + size = "1M"; + type = "EF02"; + }; + + boot = { + name = "boot"; + type = "8300"; + size = "1G"; + content = { + type = "filesystem"; + format = "vfat"; + mountpoint = "/boot"; + mountOptions = ["defaults"]; + }; + }; + + luks = { + size = "100%"; + content = { + type = "luks"; + name = "encrypted"; + extraOpenArgs = []; + settings = { + allowDiscards = true; + bypassWorkqueues = true; + }; + passwordFile = "/tmp/luks-pw"; + content = { + type = "lvm_pv"; + vg = "pool"; + }; + }; + }; + }; + }; + }; + }; + + nodev = { + "/" = { + fsType = "tmpfs"; + mountOptions = [ + "defaults" + "size=6G" + "mode=755" + ]; + }; + }; + + lvm_vg = { + pool = { + type = "lvm_vg"; + lvs = { + nix = { + size = "24G"; + content = { + type = "filesystem"; + format = "ext4"; + mountpoint = "/nix"; + mountOptions = [ + "defaults" + "noatime" + ]; + }; + }; + persist = { + size = "100%FREE"; + content = { + type = "filesystem"; + format = "ext4"; + mountpoint = "/persist"; + mountOptions = [ + "defaults" + "noatime" + ]; + }; + }; + }; + }; + }; + }; +} diff --git a/nixos/hosts/silver/hardware.nix b/nixos/hosts/silver/hardware.nix new file mode 100644 index 0000000..016bfa3 --- /dev/null +++ b/nixos/hosts/silver/hardware.nix @@ -0,0 +1,32 @@ +# Do not modify this file! It was generated by ‘nixos-generate-config’ +# and may be overwritten by future invocations. Please make changes +# to /etc/nixos/configuration.nix instead. +{ + lib, + modulesPath, + ... +}: { + imports = [ + (modulesPath + "/installer/scan/not-detected.nix") + (modulesPath + "/profiles/qemu-guest.nix") + ]; + + boot.initrd.availableKernelModules = ["virtio-pci"]; + + # Enables DHCP on each ethernet and wireless interface. In case of scripted networking + # (the default) this is the recommended approach. When using systemd-networkd it's + # still possible to use this option, but it's recommended to use it in conjunction + # with explicit per-interface declarations with `networking.interfaces..useDHCP`. + networking.useDHCP = lib.mkDefault false; + + boot.loader = { + timeout = 2; + grub = { + enable = true; + efiSupport = false; + enableCryptodisk = true; + }; + }; + + nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux"; +} diff --git a/nixos/hosts/silver/mounts.nix b/nixos/hosts/silver/mounts.nix new file mode 100644 index 0000000..81203a6 --- /dev/null +++ b/nixos/hosts/silver/mounts.nix @@ -0,0 +1,32 @@ +{...}: { + environment.persistence."/persist" = { + hideMounts = true; + directories = [ + # "/etc/secureboot" + "/etc/ssh" + "/etc/secrets" + + "/var/log" + "/var/lib/systemd/coredump" + "/var/lib/nixos" + "/var/lib/containers" + "/var/db/sudo" + + "/var/lib/acme" + + "/srv" + ]; + files = [ + "/etc/machine-id" + ]; + }; + + fileSystems = { + "/".neededForBoot = true; + "/etc/ssh" = { + depends = ["/persist"]; + neededForBoot = true; + }; + "/persist".neededForBoot = true; # no further config is needed, disko handles the rest + }; +} diff --git a/nixos/hosts/silver/secrets.nix b/nixos/hosts/silver/secrets.nix new file mode 100644 index 0000000..d759f5c --- /dev/null +++ b/nixos/hosts/silver/secrets.nix @@ -0,0 +1,9 @@ +{...}: { + sops = { + defaultSopsFile = ../../../secrets/silver.yaml; + age.sshKeyPaths = ["/persist/etc/ssh/ssh_host_ed25519_key"]; + + secrets."root-pw" = {neededForUsers = true;}; + secrets."user-pw" = {neededForUsers = true;}; + }; +} diff --git a/nixos/hosts/silver/services/breeze.nix b/nixos/hosts/silver/services/breeze.nix new file mode 100644 index 0000000..c3c1bd1 --- /dev/null +++ b/nixos/hosts/silver/services/breeze.nix @@ -0,0 +1,49 @@ +{config, ...}: let + httpIntPort = 14010; + dom = "picture.wtf"; +in { + sops.secrets."svc-breeze-upload_key" = { + owner = "breeze"; + group = "breeze"; + }; + + services.nginx = { + virtualHosts.${dom} = { + locations."/" = { + proxyPass = "http://127.0.0.1:${toString httpIntPort}"; + }; + forceSSL = true; + enableACME = true; + }; + }; + + systemd.tmpfiles.rules = [ + "d /srv/uploads 0750 breeze breeze - -" + ]; + + services.breeze = { + enable = true; + uploadKeyFile = config.sops.secrets."svc-breeze-upload_key".path; + + settings = { + engine = { + base_url = "https://${dom}"; + motd = "minish's image host, currently hosting %uplcount% files"; + max_upload_len = 2147483648; + max_temp_lifetime = 43200; + max_strip_len = 16777216; + + disk.save_path = "/srv/uploads"; + cache = { + max_length = 268435456; + upload_lifetime = 1800; + scan_freq = 60; + mem_capacity = 4294967296; + }; + }; + + http.listen_on = "127.0.0.1:${toString httpIntPort}"; + logger.level = "info"; + }; + }; +} diff --git a/nixos/hosts/silver/services/default.nix b/nixos/hosts/silver/services/default.nix new file mode 100644 index 0000000..75051e2 --- /dev/null +++ b/nixos/hosts/silver/services/default.nix @@ -0,0 +1,34 @@ +{...}: { + imports = [ + ./vcnotifier.nix + # ./nodemusicbot.nix + ./breeze.nix + ./min-rip.nix + ./gitea.nix + ./synapse.nix + ]; + + security.acme = { + acceptTerms = true; + defaults.email = "minishcontact@riseup.net"; + }; + + services.nginx = { + enable = true; + recommendedZstdSettings = true; + recommendedGzipSettings = true; + recommendedBrotliSettings = true; + recommendedTlsSettings = true; + recommendedOptimisation = true; + recommendedProxySettings = true; + + # proxy_http_version is already set to 1.1 by recommendedProxySettings + appendHttpConfig = '' + proxy_request_buffering off; + ''; + + clientMaxBodySize = "0"; + }; + networking.firewall.allowedTCPPorts = [22 80 443]; # exposed by nginx + networking.firewall.allowedUDPPorts = [443]; +} diff --git a/nixos/hosts/silver/services/gitea.nix b/nixos/hosts/silver/services/gitea.nix new file mode 100644 index 0000000..642ad2a --- /dev/null +++ b/nixos/hosts/silver/services/gitea.nix @@ -0,0 +1,68 @@ +{config, ...}: let + sshExposeIp = "0.0.0.0"; # TODO: change this to the public-facing IP for prod + sshIntPort = 14022; + httpIntPort = 14020; + dom = "git.min.rip"; +in { + services.nginx = { + virtualHosts.${dom} = { + locations."/" = { + proxyPass = "http://127.0.0.1:${toString httpIntPort}"; + }; + forceSSL = true; + enableACME = true; + }; + streamConfig = '' + upstream gitea { + server 127.0.0.1:${toString sshIntPort}; + } + + server { + listen ${sshExposeIp}:22; + proxy_timeout 20s; + proxy_pass gitea; + } + ''; # May not support IPv6, i'm unsure.. + }; + + # Auto-create directories we need + systemd.tmpfiles.rules = [ + "d /srv/gitea 0750 1000 1000 - -" + "d /srv/gitea/gitea 0750 1000 1000 - -" + "d /srv/gitea/runner 0750 1000 1000 - -" + ]; + + virtualisation.oci-containers.containers.gitea = { + image = "docker.io/gitea/gitea:1.21.4"; + environment = { + USER_UID = "1000"; + USER_GID = "1000"; + GITEA_WORK_DIR = "/data/gitea"; + GITEA_CUSTOM = "/data/gitea"; + GITEA_APP_INI = "/data/gitea/conf/app.ini"; + }; + volumes = [ + "/srv/gitea/gitea:/data" + "/etc/localtime:/etc/localtime:ro" + ]; + ports = [ + "${toString httpIntPort}:3000/tcp" + "${toString sshIntPort}:22/tcp" + ]; + }; + + sops.secrets."svc-gitea-runner-env" = {}; + + virtualisation.oci-containers.containers.gitea-runner = { + image = "docker.io/gitea/act_runner:0.2.6-dind-rootless"; + environment = { + GITEA_INSTANCE_URL = "https://${dom}/"; + DOCKER_HOST = "unix:///var/run/user/1000/docker.sock"; + }; + environmentFiles = [config.sops.secrets."svc-gitea-runner-env".path]; + volumes = [ + "/srv/gitea/runner:/data" + ]; + extraOptions = ["--privileged"]; + }; +} diff --git a/nixos/hosts/silver/services/min-rip.nix b/nixos/hosts/silver/services/min-rip.nix new file mode 100644 index 0000000..9c151c9 --- /dev/null +++ b/nixos/hosts/silver/services/min-rip.nix @@ -0,0 +1,24 @@ +{inputs, ...}: let + dom = "min.rip"; +in { + services.nginx.virtualHosts.${dom} = { + root = "${inputs.min-rip}"; + forceSSL = true; + enableACME = true; + + locations."/" = { + tryFiles = "$uri $uri/ =404"; + extraConfig = '' + add_header Cache-Control "max-age=15552000, must-revalidate"; + ''; + }; + + locations."/robots.txt" = { + priority = 10000; + return = ''200 "User-Agent: ia_archiver\nDisallow: /"''; + extraConfig = '' + add_header Content-Type text/plain; + ''; + }; + }; +} diff --git a/nixos/hosts/silver/services/nodemusicbot.nix b/nixos/hosts/silver/services/nodemusicbot.nix new file mode 100644 index 0000000..136c65e --- /dev/null +++ b/nixos/hosts/silver/services/nodemusicbot.nix @@ -0,0 +1,9 @@ +{config, ...}: { + sops.secrets."svc-nodemusicbot-env" = {}; + + virtualisation.oci-containers.containers.nodemusicbot = { + image = "git.min.rip/min/nodemusicbot:latest"; + extraOptions = ["--rm"]; + environmentFiles = [config.sops.secrets."svc-nodemusicbot-env".path]; + }; +} diff --git a/nixos/hosts/silver/services/synapse.nix b/nixos/hosts/silver/services/synapse.nix new file mode 100644 index 0000000..d65b0f5 --- /dev/null +++ b/nixos/hosts/silver/services/synapse.nix @@ -0,0 +1,103 @@ +{config, ...}: let + httpIntPort = 14030; + dbIntPort = 14032; + domHost = "mtx.min.rip"; + domDelegate = "min.rip"; + dir = "/srv/synapse"; + dirSynapse = "${dir}/synapse"; + dirDb = "${dir}/db"; +in { + services.nginx = { + virtualHosts.${domHost} = { + locations."/" = { + proxyPass = "http://127.0.0.1:${toString httpIntPort}"; + }; + forceSSL = true; + enableACME = true; + }; + virtualHosts.${domDelegate} = { + locations."/.well-known/matrix/client" = { + return = ''200 '{"m.homeserver": {"base_url": "https://${domHost}:443"}}' ''; + extraConfig = '' + default_type application/json; + add_header Access-Control-Allow-Origin *; + ''; + }; + locations."/.well-known/matrix/server" = { + return = ''200 '{"m.server": "${domHost}:443"}' ''; + extraConfig = '' + default_type application/json; + add_header Access-Control-Allow-Origin *; + ''; + }; + }; + }; + + # Auto-create directories we need + systemd.tmpfiles.rules = [ + "d ${dir} 0777 root root - -" + "d ${dirSynapse} 0750 224 224 - -" + "d ${dirDb} 0750 70 70 - -" + ]; + + virtualisation.oci-containers.containers.synapse-db = { + image = "docker.io/postgres:12-alpine"; + environment = { + POSTGRES_USER = "synapse"; + POSTGRES_PASSWORD = "synapse"; + POSTGRES_INITDB_ARGS = "--encoding=UTF-8 --lc-collate=C --lc-ctype=C"; + }; + volumes = [ + "${dirDb}:/var/lib/postgresql/data" + ]; + ports = ["${toString dbIntPort}:5432/tcp"]; + # extraOptions = [ + # "--health-cmd" "pg_isready -U \${POSTGRES_USER}" + # "--health-interval=5s" + # "--health-retries=5" + # ]; + }; + + sops.secrets."svc-synapse-synapse-config" = { + owner = "matrix-synapse"; + group = "matrix-synapse"; + mode = "0664"; + }; + + services.matrix-synapse = { + enable = true; + withJemalloc = true; + dataDir = dirSynapse; + + extraConfigFiles = [config.sops.secrets."svc-synapse-synapse-config".path]; + settings = { + server_name = domDelegate; + listeners = [ + { + bind_addresses = ["127.0.0.1"]; + port = httpIntPort; + tls = false; + type = "http"; + x_forwarded = true; + resources = [ + { + names = ["client" "federation"]; + compress = false; + } + ]; + } + ]; + database = { + name = "psycopg2"; + args = { + user = "synapse"; + password = "synapse"; + database = "synapse"; + host = "127.0.0.1"; + port = dbIntPort; + }; + }; + report_stats = false; + }; + }; +} diff --git a/nixos/hosts/silver/services/vcnotifier.nix b/nixos/hosts/silver/services/vcnotifier.nix new file mode 100644 index 0000000..87224f0 --- /dev/null +++ b/nixos/hosts/silver/services/vcnotifier.nix @@ -0,0 +1,16 @@ +{config, ...}: { + sops.secrets."svc-vcnotifier-env" = {}; + + systemd.tmpfiles.rules = [ + "d /srv/vcnotifier 0750 root root - -" + ]; + + virtualisation.oci-containers.containers.vcnotifier = { + image = "ghcr.io/zyme-xd/vcping:latest"; + extraOptions = ["--rm"]; + environmentFiles = [config.sops.secrets."svc-vcnotifier-env".path]; + volumes = [ + "/srv/vcnotifier/data.json:/usr/src/app/dist/data.json" + ]; + }; +} diff --git a/nixos/keys/ssh.nix b/nixos/keys/ssh.nix new file mode 100644 index 0000000..c628a29 --- /dev/null +++ b/nixos/keys/ssh.nix @@ -0,0 +1,4 @@ +[ + "ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBBCZ7P/hl8DOMyTm5vGZuMrxBeSr2bmN2tp8zeiK+y/zq/fOi4rMIbfQif8KmaZ2UDTnpWj8DNfrPhfz6li1nzU=" + "sk-ssh-ed25519@openssh.com AAAAGnNrLXNzaC1lZDI1NTE5QG9wZW5zc2guY29tAAAAIPci/gIUGWdoiLXS8Nq8T6Fvh2Wtpxv6pnqyvbSWvzyoAAAABHNzaDo=" +] diff --git a/nixos/modules/README.md b/nixos/modules/README.md new file mode 100644 index 0000000..fa3a666 --- /dev/null +++ b/nixos/modules/README.md @@ -0,0 +1,5 @@ +# NixOS Modules + +This directory contains NixOS modules that are shared across hosts. + +* `modules/system` - automatically loaded on every host diff --git a/nixos/modules/default.nix b/nixos/modules/default.nix new file mode 100644 index 0000000..0bd30b4 --- /dev/null +++ b/nixos/modules/default.nix @@ -0,0 +1,3 @@ +[ + ./system +] diff --git a/nixos/modules/system/boot/default.nix b/nixos/modules/system/boot/default.nix new file mode 100644 index 0000000..b101c3c --- /dev/null +++ b/nixos/modules/system/boot/default.nix @@ -0,0 +1,5 @@ +{...}: { + imports = [ + ./luks-ssh.nix + ]; +} diff --git a/nixos/modules/system/boot/luks-ssh.nix b/nixos/modules/system/boot/luks-ssh.nix new file mode 100644 index 0000000..23a0d58 --- /dev/null +++ b/nixos/modules/system/boot/luks-ssh.nix @@ -0,0 +1,52 @@ +{ + config, + lib, + ... +}: let + baseCfg = config.gen.system.bootloader; + cfg = baseCfg.luksSsh; +in { + options.gen.system.bootloader.luksSsh = { + enable = lib.mkEnableOption "use boot process with luks unlock over ssh"; + port = lib.mkOption { + type = lib.types.port; + description = "port for ssh server to listen on"; + }; + hostKeys = lib.mkOption { + type = lib.types.listOf lib.types.path; + description = "paths of host keys for the ssh server to use"; + }; + }; + + config = lib.mkIf cfg.enable { + # ### Use systemd-boot ### + # boot.loader = { + # efi.canTouchEfiVariables = true; + + # timeout = 2; + # systemd-boot = { + # enable = true; + # configurationLimit = 3; + # }; + # }; + + ### LUKS unlock through SSH ### + boot.initrd = { + network = { + enable = true; + flushBeforeStage2 = true; + + ssh = { + enable = true; + authorizedKeys = import ../../../keys/ssh.nix; + inherit (cfg) hostKeys port; + }; + + postCommands = '' + # Automatically ask for the password on SSH login + echo 'cryptsetup-askpass || echo "Unlock was successful; exiting SSH session" && exit 1' >> /root/.profile + ''; + }; + }; + }; +} diff --git a/nixos/modules/system/default.nix b/nixos/modules/system/default.nix new file mode 100644 index 0000000..2b91dc0 --- /dev/null +++ b/nixos/modules/system/default.nix @@ -0,0 +1,33 @@ +{pkgs, ...}: { + imports = [ + ./hardening.nix + ./limits.nix + ./networking.nix + ./boot + ]; + + # Ensure root login is available on every machine (if ssh is enabled) + users.users.root.openssh.authorizedKeys.keys = import ../../keys/ssh.nix; + + # Speed up the build a little bit, these aren't really needed + documentation = { + enable = false; + info.enable = false; + man.enable = false; + doc.enable = false; + nixos.enable = false; + }; + + # Immutable users + users.mutableUsers = false; + + ### Nix settings ### + nix = { + # Make sure flakes are enabled + settings.experimental-features = ["nix-command" "flakes"]; + + # Use our nixpkgs on legacy Nix tools. + # This way I don't have to `nix-channel --update` to use `nix-shell` + nixPath = ["nixpath=${pkgs.path}"]; + }; +} diff --git a/nixos/modules/system/hardening.nix b/nixos/modules/system/hardening.nix new file mode 100644 index 0000000..379ba9f --- /dev/null +++ b/nixos/modules/system/hardening.nix @@ -0,0 +1,60 @@ +{ + config, + lib, + ... +}: let + cfg = config.gen.system.hardening; +in { + options.gen.system.hardening = { + hardenBpf = lib.mkEnableOption "place heavier restrictions on BPF"; + fullRpFilter = lib.mkEnableOption "enable full reverse path filtering. breaks dynamic routing, probably"; + ignoreIcmpEcho = lib.mkEnableOption "ignore icmp echos. obviously, this makes pings unresponsive"; + disableSack = lib.mkEnableOption "disable tcp sack"; + disableConsole = lib.mkEnableOption "disable console. not recommended for test machines"; + }; + + config = { + ### Sysctls ### + boot.kernel.sysctl = + { + "kernel.kptr_restrict" = 1; + "kernel.dmesg_restrict" = 1; + "kernel.printk" = "3 3 3 3"; + "dev.tty.ldisc_autoload" = 0; + "vm.unprivileged_userfaultfd" = 0; + "kernel.kexec_load_disabled" = 1; + "kernel.sysrq" = 0; # ignore sysrq key + "kernel.perf_event_paranoid" = 3; + "net.ipv4.tcp_rfc1337" = 1; # drop RSTs during time-wait state + } + // lib.mkIf cfg.ignoreIcmpEcho { + "net.ipv4.icmp_echo_ignore_all" = 1; + } + // lib.mkIf cfg.hardenBpf { + "kernel.unprivileged_bpf_disabled" = 1; + "net.core.bpf_jit_harden" = 2; + } + // lib.mkIf cfg.fullRpFilter { + "net.ipv4.conf.all.rp_filter" = 1; + "net.ipv4.conf.default.rp_filter" = 1; + } + // lib.mkIf cfg.disableSack { + "net.ipv4.tcp_sack" = 0; + "net.ipv4.tcp_dsack" = 0; + "net.ipv4.tcp_fack" = 0; + }; + + ### Security options ### + security.protectKernelImage = true; + + ### Disable emergency access ### + systemd.enableEmergencyMode = false; + boot.initrd.systemd.emergencyAccess = false; + + ### Disable tty login ### + console = { + earlySetup = true; + enable = !cfg.disableConsole; + }; + }; +} diff --git a/nixos/modules/system/limits.nix b/nixos/modules/system/limits.nix new file mode 100644 index 0000000..437b041 --- /dev/null +++ b/nixos/modules/system/limits.nix @@ -0,0 +1,13 @@ +{...}: { + boot.kernel.sysctl."net.core.rmem_max" = 2500000; + boot.kernel.sysctl."fs.inotify.max_user_instances" = 1024; + + security.pam.loginLimits = [ + { + domain = "*"; + type = "soft"; + item = "nofile"; + value = "655360"; + } + ]; +} diff --git a/nixos/modules/system/networking.nix b/nixos/modules/system/networking.nix new file mode 100644 index 0000000..4eb31e4 --- /dev/null +++ b/nixos/modules/system/networking.nix @@ -0,0 +1,3 @@ +{...}: { + networking.nameservers = ["1.1.1.1" "1.0.0.1"]; +} diff --git a/scripts/install.sh b/scripts/install.sh new file mode 100755 index 0000000..1b80346 --- /dev/null +++ b/scripts/install.sh @@ -0,0 +1,71 @@ +#!/usr/bin/env bash + +# fail on errors +set -e + +die() { + echo >&2 "$@" + exit 1 +} + +# parse args +POSITIONAL_ARGS=() +while [[ $# -gt 0 ]]; do + case $1 in + -c|--nixos-config) + NAME="$2" + shift # past argument + shift # past value + ;; + -k|--key-dir) + KEYDIR="$2" + shift # past argument + shift # past value + ;; + *) + POSITIONAL_ARGS+=("$1") # save positional arg + shift # past argument + ;; + esac +done + +# check args +[ ! -f "$KEYDIR/host.pub" ] && die "host pubkey missing!" +[ ! -f "$KEYDIR/host" ] && die "host privkey missing!" +[ ! -f "$KEYDIR/host_initrd.pub" ] && die "host pubkey (initrd) missing!" +[ ! -f "$KEYDIR/host_initrd" ] && die "host privkey (initrd) missing!" +[ ! -f "$KEYDIR/luks-pw" ] && die "luks pw missing!" + +# temp work dir +temp=$(mktemp -d) +cleanup() { + rm -rf "$temp" +} +trap cleanup EXIT + +# prepare host keys +echo "Preparing host keys.." +dir="$temp/persist/etc/ssh" +install -d -m755 "$dir" +cp "$KEYDIR/host" "$dir/ssh_host_ed25519_key" +cp "$KEYDIR/host.pub" "$dir/ssh_host_ed25519_key.pub" +chmod 600 "$dir/ssh_host_ed25519_key" + +# prepare host keys (initrd) +echo "Preparing host keys.. (initrd)" +dir="$temp/persist/etc/secrets/initrd" +install -d -m755 "$dir" +cp "$KEYDIR/host" "$dir/ssh_host_ed25519_key" +cp "$KEYDIR/host.pub" "$dir/ssh_host_ed25519_key.pub" +chmod 600 "$dir/ssh_host_ed25519_key" + +# nixos-anywhere +echo "Starting install.." +nixos-anywhere \ + --disk-encryption-keys "/tmp/luks-pw" "$KEYDIR/luks-pw" \ + --extra-files "$temp" \ + --flake .#$NAME \ + "${POSITIONAL_ARGS[@]}" + +echo -e "Finished install.\n" \ + "Make sure to delete the SSH host keys from here if you are done with them." diff --git a/scripts/make_base_keys.sh b/scripts/make_base_keys.sh new file mode 100755 index 0000000..679f8da --- /dev/null +++ b/scripts/make_base_keys.sh @@ -0,0 +1,36 @@ +#!/usr/bin/env bash + +# fail on errors +set -e + +die() { + echo >&2 "$@" + exit 1 +} + +# set up target folder +P="$1" +[[ -z "$P" || -d "$P" ]] && die "specify a non-existent path as a first argument" + +mkdir "$P" +pushd "$P" >/dev/null + +# host keys +echo "Generating SSH host keys.." +ssh-keygen -t ed25519 -f ./host -q -N "" -C "" + +# host pubkey -> age key +echo "AGE key is: $(cat ./host.pub | ssh-to-age)" + +# host keys (initrd) +echo "Generating SSH host keys.. (initrd)" +ssh-keygen -t ed25519 -f ./host_initrd -q -N "" -C "" + +# luks pw +echo "Generating LUKS password file.." +echo -n "$(openssl rand -base64 24)" > ./luks-pw + +# we are done +popd >/dev/null +echo "Finished generating keys." \ + "Delete them or put them somewhere else once you're done with them." diff --git a/scripts/rekey.sh b/scripts/rekey.sh new file mode 100755 index 0000000..1565bcd --- /dev/null +++ b/scripts/rekey.sh @@ -0,0 +1,12 @@ +#!/usr/bin/env bash + +SCRIPT_DIR="$(dirname "$0")" +ROOT_DIR="$(realpath "$SCRIPT_DIR/..")" + +pushd "$ROOT_DIR" > /dev/null + +rekey_dir() { + find $1 | xargs -i sops updatekeys -y {} +} + +rekey_dir "secrets/*.yaml" diff --git a/secrets/eidola.yaml b/secrets/eidola.yaml new file mode 100644 index 0000000..2bfcfb1 --- /dev/null +++ b/secrets/eidola.yaml @@ -0,0 +1,42 @@ +root-pw: ENC[AES256_GCM,data:g/dIT5d5w+FCAbxgGRJoMISgVTySEqXoBCV/jopu9Cgm4db9zAFWzZ7kUqOr8IQpEpCXyguYClIGExt0SztbRze8YPu9NilcUmYH7QmI+8oaEanYkvwpT5jyBU/M2eG0U9pMzcGI6hl2Ew==,iv:2HmGvFkRrnwYi5gjB4Na/ZayGoCFEsM4TDoqKlzhZUg=,tag:NLuval5PJ6AnDLvPGVvm7w==,type:str] +user-pw: ENC[AES256_GCM,data:gr+Dis3c5NWLWnfJG4eJUxwt574R3n40djeK68hukMNPx0qwGRAT5a7UQ5doxtDBgafcH1uCgqrsWwEmy9H5dS6WfLMivE5Uy213EcEk3YNUwI9d5vbdcbCcXWvPsyCu6sxS3x731EVVYA==,iv:4AHzVLoJD95d2UwwEAwxWP0G2gekHahBt4hDDA9ZSx0=,tag:03L3Ql070mt3oDV5YdrETg==,type:str] +sops: + kms: [] + gcp_kms: [] + azure_kv: [] + hc_vault: [] + age: + - recipient: age1uqxzduupzes3tgfrrlret0n6thyldmlef60nqfzk689lmg6yayvsqpwxj6 + enc: | + -----BEGIN AGE ENCRYPTED FILE----- + YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBPOUhRbm95NndkQmRnc0VX + RFpaYVpoaE1mcDVuVTFYZ0hFbFBDalozM0RJCnVsUTZ2alJsY2s4TTJwRmQvYzBB + VDQ5SUVUWnRhdGN0elNybElpMmhsRzAKLS0tIC9DZGNnbU80Z3FTU1ZRMlZlUVMv + dVh4dFgrcWxtMFdUVVZTTm4rczVLaE0KBhCAwRHxtedfNZapyR3lbkxaiWxZR5lW + SQMhh9sUTnc/4B6StOhZEn+S7bVSRjPgvn9F+W7nCzcq/fpRYTcWvw== + -----END AGE ENCRYPTED FILE----- + lastmodified: "2024-08-23T20:15:55Z" + mac: ENC[AES256_GCM,data:l/9IHeMTgA7hzF2EEcWW+wkKa4eRWCRLAmdee371qhipLzgJMKrme+qK2RkJd2txVIgz7m7FJG4HWEo4hVpjvcloY1H0U86dJndwKwGKYTmJPdcEH3HQgKVcx8b5pdkww1g98vnLfY/jwbMBkx3CrPliJw86QVglkmWWHR6W92w=,iv:cYlpkLN4PwHghbRn6KIWgUGEymdbFBsnUZ8xUBgif5g=,tag:jqLa5Nl74DUYFqDpuQPfUA==,type:str] + pgp: + - created_at: "2024-09-02T19:43:07Z" + enc: |- + -----BEGIN PGP MESSAGE----- + + hQIMAwAAAAAAAAAAAQ/6AsaPEjdfJlZs1ktcZ/zingDBMEzmjk7ZCN7kO1luTMFs + F7OaxAuyTlJNHU90ihJO94Gbtw0wqLewltl9nfNvHVSAWFtrZL2ReWGaAd5tS827 + b91tDiifCQ+AWYrBMO0Bmd0RkgOQvfc3FJbOzxhb1mdzhCXKGbOCjOD41A3/nadK + xaaBHv1dGhW+YYvtibgUWfntSEa/drgB0REjGwbuLqvlPL2I/aP51edYyGqok8V/ + +2dWRggxzm4st2jWngb7jpKSweEckOXki9Fu35bVOeV0/x6/iMQ+6f2OObHKu6pi + OB3kes6m5hsSWPgL0s9WbrKNpyrYeFhxzweGfm4KG8hZqU9aytf6Efo4a/dKin8j + DQBeFk3TxDgEzy9s+4xq6zq9yJeud4knd0cM82Qkghg/96E/M/RUf1l9MkbuDXCU + i6HPcDv7yMMPDexLWBD4HN2Kr418TJ8rtqb1lESWhjl7LlFQeXqD9mvy6tF9URZE + /L2ez4RCFZL4EAD345+gA584hmmmfqfXi7WrQZiA1EAZRcfcTm3sQlyHKMHkT5jr + oiFXxvG5hwjrfe9ipqhQWvFn06x5PielOUy9bfG0uza9/o9AEtQWdyMOIGFqm00o + 04Xfv7PblknudezGKEbAOexZRDwZTGPOSNU1WCSguSUrEwX+mFKCtxD1MCxgowTS + XgFdTAk8t/pctB4QBBVUNdN36fU8Iah+K6+T658aQjHOCcIziBWUikHtD4yT/emu + BSqn41/VbLfyCosyXuOjqtMYg6evKRsVtqMx1GnInOHTjw2juOV1ZklVZNk8fzs= + =AZ8U + -----END PGP MESSAGE----- + fp: 78795D9EBD425CBB3E850BC45DF91852CB14CEFF + unencrypted_suffix: _unencrypted + version: 3.8.1 diff --git a/secrets/silver.yaml b/secrets/silver.yaml new file mode 100644 index 0000000..6fc2aa7 --- /dev/null +++ b/secrets/silver.yaml @@ -0,0 +1,47 @@ +root-pw: ENC[AES256_GCM,data:VCaP0E5A/UV1LLHotZK2B6AcfXZtQhi13t0wguv8tfoljwSbzcuYIhOMDIbL0BLPcaJRA7S//LhCDmQszfs/NNH5bcRBIoxPjUjKJ+V3ip/mEFWj1LypMIOWlKt1jOAtdMpmbK3uygGUNg==,iv:SOsaoox+J3rPTmw1MUJDwbFxexPVUYoIMfJo7YRTBvw=,tag:6R+Z1gsX37zgLyMz//wZvw==,type:str] +user-pw: ENC[AES256_GCM,data:5qJ/TLLdHyQVTftN882UJZ/FPAbHUGQkw1eXqajCt2Aw2wca5D7lWhwccix79Ws1DCK/w7cQjn48ys20faODQbcXO0TkQl6GYPqD+z3j2HETWuFgZqh0yW3pl/L1S5w4Jk2bALEJ7FXthg==,iv:9BETCLg4b2uE4QezblY6Krofvuieaqd3Cbk3ucpR58E=,tag:BeyHB28CqxiZa+KWjnv5Fw==,type:str] +svc-nodemusicbot-env: ENC[AES256_GCM,data:XoTn7WuFbfs8P+MvoMLfwpvUJ4IGGRMhdG1HXdmXGiI9s6ZTlipnIL70MYlih5kKn/wSBR2QDd9i6AErbz3hDUAkCh0tBuiZTDuSctUU0X2PCnrBnbg=,iv:ayrHgGO0zCl7apVKjMGI1MbtkN8V3j6dT0Mv07/KoYQ=,tag:TdAussU7bBg+jxpLufR1sw==,type:str] +svc-vcnotifier-env: ENC[AES256_GCM,data:8DwT17Aosvu7/Q2ecbir/t9HOtanPlFeBgLOzxtcv2BpCIGTEHqbVk9pegKQKc7lGhj5OrVg4HvNnQNEdEu5fLqB2XpMV8ltS7PL1wEz,iv:CfnXvb2wSRwQAURSLUrV4jofGnFOE6PQan7KPPhERjI=,tag:ve1Dh+63N4B6W7ZtvbDCFA==,type:str] +svc-breeze-upload_key: ENC[AES256_GCM,data:qNNH4/Q0rk2lsMImzpVe54+DbSAOiGjo,iv:rX9zvcPt6qSbPs6sKYO0T8EVaHU/u9QDoT/ISHdQSV4=,tag:kivJyeJGtuBP0l54qJ0t9w==,type:str] +svc-synapse-synapse-config: ENC[AES256_GCM,data:r8ZYi67CfftGheassCFiLOVcFUho+sNNe0XCkyQETHT6Q/w2jqO9eAVA2EDJyK4Vk3S4MP6ppcGxwocMmTYzkAjmtwf6a7GzUyh14+Lj5VTybvIKOze0wuLlsEUUYgU=,iv:HTnPaS5/ZvdJIMKiTfPffZmemp5IGTo/mIWrpafk/Fk=,tag:2HusbhzmxqsTMz5/78WCRA==,type:str] +svc-gitea-runner-env: ENC[AES256_GCM,data:M2hV8YM03dcBcgpJqbpiW6RGlhDvkfF/ExF+J1GF+39GnOsBWwPKteM5EAUB2Wrl/zRFifgfNLLdYgSEWhJsT1cBLhI3vwE5,iv:9/nvC3sS6XcLxgeKrEg/AaFhptXCm3uvGgSUMAz4p5Y=,tag:A1MnoJP6aekXuWHhlONnkw==,type:str] +sops: + kms: [] + gcp_kms: [] + azure_kv: [] + hc_vault: [] + age: + - recipient: age19yhycdgqczrvttszq97ccljh684x3r7f5dj4p0wdwqsrusqlcayse0vsh3 + enc: | + -----BEGIN AGE ENCRYPTED FILE----- + YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB1emJhUTZTcVU1c3hSMjJK + MFcwOUYxbUV5ZUtJck9WSEpZS2Y0UFMrNTJRCjE1U1RNeUI4VUE0aURvamd6WHFF + NGE4RWExanUxelI5MjI3dmV2WjYyNDAKLS0tIGprYlhpd0ZrN3E2ZnRBZWZibGFy + Z1dZRXNCRkQ5cktZRGNpUXJaWHhrYTQKXQ1VOLDgptLJ8JKSBF8CWzyEGHnlbB+4 + 6nZlCHid4AFPRdAZ7cgEvJViBTSV05NOWE0pKYO3WZyWVKysfBKtgg== + -----END AGE ENCRYPTED FILE----- + lastmodified: "2024-10-13T07:04:09Z" + mac: ENC[AES256_GCM,data:/Mn3G6qHRPSZ1vt1ks30EYZ7UxhjmC7hdkZCl0ifipEfrl//zcsgtB96Q0V/35JWPVcVVoirLJsUmMcJZaevjAgIBys9jIjLgw5AN5R9QhVdRJ25tp/qX/JlKHuj9IVOM7n9hzVjauJYoWy6ftSeTmzyWoqTJrKvF6etaU4AUYs=,iv:Wcfr3sbVqOo7JTMH4kooLFDSQGTTV6ZMnKcWJqF6gK0=,tag:Dp9fEDTN4ko1YZp4O4EtWg==,type:str] + pgp: + - created_at: "2024-10-13T01:11:54Z" + enc: |- + -----BEGIN PGP MESSAGE----- + + hQIMAwAAAAAAAAAAAQ//Z6FAOpeM+H/nYQ/FIlcX/E87PxSwtpPoQJMcnBMaqTQL + vpedSVDHuvdNexJfT0cOj2Va8aguhMQcgGo+vaoKtMWXQhJYf/WsTMS3F0UJLHwZ + AvuH62F8C3EPPxFrrhP/bQeADtki7CDRzQuE9wLXjCT8KwY4bfFXD8NDA/9Rp+k7 + mU5hOADqS6LBhaB55fFZ/e8MaCinezADDIDrz8VkTDzaRiU5g5VDdZnocCIp2Pz6 + umlswmJXPic2BkbH4ALAfA/v8lsLXqi5ZnMkvwzc1YoACLKsJ0TexMXE3UgTvNb9 + PzxDI1JzC+9RpGSBgy6uH2hnYNfgZ9S72Ha3vGR82k7WdEAQ4JzbD/WZNllb4aXw + oTx/XSyyLoUK1o66nV1KJRFsw9yhc4Af43lafur5cQp5snSJyYg6BKc+K25eRBYK + yBV0NdF9sRZlNd+FiIjj00iKJYXJ1YclPxb5NJ8OMhKYLR52migfj3F2WUj9sjRE + bKdXtjeRup2HhiCSS3pLlcaTSE7VaxCyaYbMT4GhZI4uFMt0WSrFHE3A1e/bk+GI + 6v43SnCnzHX2o8ZPs6Oz7o6/HvhRLg0qarczqsEZbRVB8HFn0GrTLHSG743RFN73 + Jyhy7/KXv1pqb049VNL7ya5+ZEEFgHAjYkEkkBUKYFcnRCwh7liJ7vTkwWfbRR3S + XgG1ClrtfTSCzEHrYE3FbDC05Bjaep1YiYkWEzHT7rAeFw1YkLZGPUuLcm55owHs + jCGCk1j97DdK5lJyJVuSoNspg57DOW6CsQaho1pKmu3c/OWx0le64sU9Sg4aWGo= + =TIIJ + -----END PGP MESSAGE----- + fp: 78795D9EBD425CBB3E850BC45DF91852CB14CEFF + unencrypted_suffix: _unencrypted + version: 3.8.1