refactor(luna): massively overhaul luna to better handle opt-in state

Now uses BTRFS rollbacks instead of tmpfs
This commit is contained in:
Price Hiller 2023-12-07 09:05:24 -06:00
parent 79ee36db2d
commit 7a64899cee
Signed by: Price
SSH Key Fingerprint: SHA256:Y4S9ZzYphRn1W1kbJerJFO6GGsfu9O70VaBSxJO7dF8
31 changed files with 518 additions and 363 deletions

5
.envrc Normal file
View File

@ -0,0 +1,5 @@
# vim: ft=bash
if ! has nix_direnv_version || ! nix_direnv_version 2.5.1; then
source_url "https://raw.githubusercontent.com/nix-community/nix-direnv/2.5.1/direnvrc" "sha256-puRzug17Ed4JFS2wbpqa3k764QV6xPP6O3A/ez/JpOM="
fi
use flake

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
.direnv/
*.ignore

View File

@ -22,6 +22,29 @@
"type": "github" "type": "github"
} }
}, },
"blog": {
"inputs": {
"flake-utils": "flake-utils",
"nixpkgs": [
"nixpkgs"
]
},
"locked": {
"host": "gitlab.orion-technologies.io",
"lastModified": 1701591195,
"narHash": "sha256-cyU5Yv3vPaf9cfuqrScyObf8DA77KE+7HxF8WSmCEJA=",
"owner": "blog",
"repo": "blog",
"rev": "cbaf7ebe37d5e305991150a6b3aa9468cfa0a713",
"type": "gitlab"
},
"original": {
"host": "gitlab.orion-technologies.io",
"owner": "blog",
"repo": "blog",
"type": "gitlab"
}
},
"darwin": { "darwin": {
"inputs": { "inputs": {
"nixpkgs": [ "nixpkgs": [
@ -44,6 +67,26 @@
"type": "github" "type": "github"
} }
}, },
"disko": {
"inputs": {
"nixpkgs": [
"nixpkgs"
]
},
"locked": {
"lastModified": 1701905325,
"narHash": "sha256-lda63LmEIlDMeCgWfjr3/wb487XPllBByfrGRieyEk4=",
"owner": "nix-community",
"repo": "disko",
"rev": "1144887c6f4d2dcbb2316a24364ef53e25b0fcfe",
"type": "github"
},
"original": {
"owner": "nix-community",
"repo": "disko",
"type": "github"
}
},
"flake-utils": { "flake-utils": {
"inputs": { "inputs": {
"systems": "systems" "systems": "systems"
@ -62,6 +105,24 @@
"type": "github" "type": "github"
} }
}, },
"flake-utils_2": {
"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"
}
},
"home-manager": { "home-manager": {
"inputs": { "inputs": {
"nixpkgs": [ "nixpkgs": [
@ -114,35 +175,14 @@
"type": "github" "type": "github"
} }
}, },
"personal-blog": {
"inputs": {
"flake-utils": "flake-utils",
"nixpkgs": [
"nixpkgs"
]
},
"locked": {
"host": "gitlab.orion-technologies.io",
"lastModified": 1701591195,
"narHash": "sha256-cyU5Yv3vPaf9cfuqrScyObf8DA77KE+7HxF8WSmCEJA=",
"owner": "blog",
"repo": "blog",
"rev": "cbaf7ebe37d5e305991150a6b3aa9468cfa0a713",
"type": "gitlab"
},
"original": {
"host": "gitlab.orion-technologies.io",
"owner": "blog",
"repo": "blog",
"type": "gitlab"
}
},
"root": { "root": {
"inputs": { "inputs": {
"agenix": "agenix", "agenix": "agenix",
"blog": "blog",
"disko": "disko",
"flake-utils": "flake-utils_2",
"impermanence": "impermanence", "impermanence": "impermanence",
"nixpkgs": "nixpkgs", "nixpkgs": "nixpkgs"
"personal-blog": "personal-blog"
} }
}, },
"systems": { "systems": {
@ -159,6 +199,21 @@
"repo": "default", "repo": "default",
"type": "github" "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"
}
} }
}, },
"root": "root", "root": "root",

127
flake.nix
View File

@ -3,6 +3,7 @@
inputs = { inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
flake-utils.url = "github:numtide/flake-utils";
impermanence = { impermanence = {
url = "github:nix-community/impermanence"; url = "github:nix-community/impermanence";
}; };
@ -10,69 +11,89 @@
url = "github:ryantm/agenix"; url = "github:ryantm/agenix";
inputs.nixpkgs.follows = "nixpkgs"; inputs.nixpkgs.follows = "nixpkgs";
}; };
personal-blog = { disko = {
url = "gitlab:blog/blog?host=gitlab.orion-technologies.io"; url = "github:nix-community/disko";
inputs.nixpkgs.follows = "nixpkgs";
};
blog = {
type = "gitlab";
owner = "blog";
repo = "blog";
host = "gitlab.orion-technologies.io";
inputs.nixpkgs.follows = "nixpkgs"; inputs.nixpkgs.follows = "nixpkgs";
}; };
}; };
outputs = inputs @ { self, nixpkgs, impermanence, agenix, disko, flake-utils, blog, ... }:
outputs = inputs @ { self, nixpkgs, impermanence, agenix, ... }:
let let
specialArgs = { lib = import ./lib // nixpkgs.lib;
secrets = ./secrets; pkgs = nixpkgs.legacyPackages."x86_64-linux";
persist-dir = "/nix/persist";
};
defaults = { defaults = {
config = { config = {
environment.persistence = { environment.etc.machine-id.source = "/nix/persist/ephemeral/etc/machine-id";
"${specialArgs.persist-dir}" = { environment.persistence.save = {
hideMounts = true; hideMounts = true;
directories = [ persistentStoragePath = "/nix/persist/save";
"/var/lib" };
"/var/log" environment.persistence.ephemeral = {
"/etc/nixos" persistentStoragePath = "/nix/persist/ephemeral";
"/opt" hideMounts = true;
"/persist" directories = [
]; "/var/lib"
files = [ "/var/log"
"/etc/machine-id" "/etc/nixos"
"/etc/ssh/ssh_host_ed25519_key" { directory = "/persist"; user = "root"; group = "root"; mode = "0700"; }
"/etc/ssh/ssh_host_ed25519_key.pub" ];
"/etc/ssh/ssh_host_rsa_key"
"/etc/ssh/ssh_host_rsa_key.pub"
];
};
}; };
age.identityPaths = [
"/persist/nix.key"
];
}; };
}; };
in in
{ {
nixosConfigurations.orion = nixpkgs.lib.nixosSystem { nixosConfigurations.luna =
system = "x86_64-linux"; let
specialArgs = specialArgs; hostname = "luna";
modules = [ in
{ _module.args = inputs; } nixpkgs.lib.nixosSystem
defaults {
./hosts/orion system = "x86_64-linux";
impermanence.nixosModules.impermanence specialArgs = {
agenix.nixosModules.default inherit self;
]; inherit flake-utils;
}; inherit inputs;
nixosConfigurations.luna = nixpkgs.lib.nixosSystem { inherit hostname;
system = "x86_64-linux"; inherit lib;
specialArgs = specialArgs; inherit blog;
modules = [ secrets = "${self}/secrets/${hostname}";
{ _module.args = inputs; } disk = "nvme0n1";
defaults fqdn = "orion-technologies.io";
./hosts/luna };
impermanence.nixosModules.impermanence modules = [
agenix.nixosModules.default {
]; _module.args = { };
}; }
}; defaults
impermanence.nixosModules.impermanence
agenix.nixosModules.default
disko.nixosModules.disko
./hosts/${hostname}
];
};
} // flake-utils.lib.eachDefaultSystem (system:
let
pkgs = import nixpkgs {
inherit system;
overlays = [ agenix.overlays.default ];
};
in
{
devShells.default =
pkgs.mkShell
{
packages = with pkgs; [ age age-plugin-yubikey pkgs.agenix ];
shellHook = ''
export RULES="$PWD/secrets/secrets.nix"
'';
};
});
} }

View File

@ -1,9 +1,6 @@
{ config, lib, nixpkgs, ... }: { config, lib, nixpkgs, ... }:
{ {
imports = [ imports = (lib.recurseFilesInDirs [ ./os ./modules ] ".nix");
./modules system.stateVersion = "24.05";
./os
];
system.stateVersion = "23.11";
} }

View File

@ -2,15 +2,4 @@
{ {
time.timeZone = "America/Chicago"; time.timeZone = "America/Chicago";
imports = [
./services
./docker
./impermanence.nix
./nix.nix
./networking.nix
./programs.nix
./user.nix
./system.nix
];
} }

View File

@ -14,7 +14,4 @@
package = pkgs.docker_24; package = pkgs.docker_24;
}; };
}; };
imports = [
./gitlab.nix
];
} }

View File

@ -1,9 +1,12 @@
{ lib, config, specialArgs, ... }: { secrets, config, specialArgs, fqdn, ... }:
let let
gitlab_home = "/opt/gitlab"; gitlab_home = "/var/lib/gitlab";
hostname = "gitlab.orion-technologies.io"; gitlab_host = "gitlab.${fqdn}";
in in
{ {
environment.persistence.save.directories = [
gitlab_home
];
virtualisation.oci-containers.containers.gitlab = { virtualisation.oci-containers.containers.gitlab = {
image = "gitlab/gitlab-ee:latest"; image = "gitlab/gitlab-ee:latest";
autoStart = true; autoStart = true;
@ -18,7 +21,8 @@ in
]; ];
extraOptions = [ extraOptions = [
"--shm-size=256m" "--shm-size=256m"
"--hostname=${hostname}" "--hostname=${gitlab_host}"
"--pull=always"
]; ];
}; };
@ -26,11 +30,11 @@ in
2222 2222
]; ];
age.secrets.gitlab-runner-reg-config.file = specialArgs.secrets + "/gitlab-runner-reg-config.age"; age.secrets.gitlab-runner-reg-config.file = "${secrets}/gitlab-runner-reg-config.age";
services.gitlab-runner = { services.gitlab-runner = {
enable = true; enable = true;
services = { services = {
default = with lib; { default = {
registrationConfigFile = config.age.secrets.gitlab-runner-reg-config.path; registrationConfigFile = config.age.secrets.gitlab-runner-reg-config.path;
dockerImage = "alpine"; dockerImage = "alpine";
tagList = [ tagList = [
@ -41,7 +45,7 @@ in
}; };
}; };
services.nginx.virtualHosts."${hostname}" = { services.nginx.virtualHosts."${gitlab_host}" = {
locations."/".proxyPass = "http://127.0.0.1:8080"; locations."/".proxyPass = "http://127.0.0.1:8080";
forceSSL = true; forceSSL = true;
enableACME = true; enableACME = true;

View File

@ -1,4 +0,0 @@
{ ... }:
{
environment.persistence = { };
}

View File

@ -1,7 +1,6 @@
{ inputs, lib, pkgs, hostname, ... }: { inputs, lib, pkgs, hostname, ... }:
let let
hostname = "luna";
networks_dhcp_use_dns = "no"; networks_dhcp_use_dns = "no";
networks_dhcp = "ipv4"; networks_dhcp = "ipv4";
networks_multicast_dns = "no"; networks_multicast_dns = "no";

View File

@ -1,16 +1,13 @@
{ pkgs, ... }: { pkgs, ... }: {
{
nix = { nix = {
settings = { settings = {
experimental-features = [ "nix-command" "flakes" ]; experimental-features = [ "nix-command" "flakes" ];
auto-optimise-store = true; auto-optimise-store = true;
trusted-users = ["@wheel"]; trusted-users = [ "wheel" ];
}; };
gc = { gc = {
automatic = true; automatic = true;
dates = "weekly"; dates = "daily";
options = "--delete-older-than 7d";
}; };
}; };
} }

View File

@ -1,10 +1,7 @@
{ pkgs, ... }: { pkgs, ... }: {
{
nixpkgs.config.allowUnfree = true; nixpkgs.config.allowUnfree = true;
programs = { programs = {
zsh.enable = true;
neovim = { neovim = {
enable = true; enable = true;
defaultEditor = true; defaultEditor = true;
@ -13,11 +10,12 @@
environment.systemPackages = with pkgs; [ environment.systemPackages = with pkgs; [
vim vim
coreutils-full
nano
curl curl
wget
git git
jq jq
rsync rsync
rustc
cargo
]; ];
} }

View File

@ -1,8 +0,0 @@
{ ... }:
{
imports = [
./openssh.nix
./fail2ban.nix
./nginx.nix
];
}

View File

@ -3,8 +3,5 @@
services.fail2ban = { services.fail2ban = {
enable = true; enable = true;
maxretry = 10; maxretry = 10;
jails.DEFAULT.settings = {
port = "2200";
};
}; };
} }

View File

@ -0,0 +1,8 @@
{ ... }:
{
services.journald = {
extraConfig = ''
SystemMaxUse=100G
'';
};
}

View File

@ -1,7 +1,4 @@
{ config, pkgs, system, personal-blog, ... }: { config, pkgs, blog, fqdn, ... }:
let
blog-host = "blog.orion-technologies.io";
in
{ {
services.nginx = { services.nginx = {
enable = true; enable = true;
@ -15,10 +12,10 @@ in
defaults.email = "price@orion-technologies.io"; defaults.email = "price@orion-technologies.io";
}; };
services.nginx.virtualHosts."${blog-host}" = { services.nginx.virtualHosts."blog.${fqdn}" = {
forceSSL = true; forceSSL = true;
enableACME = true; enableACME = true;
root = personal-blog.packages.${pkgs.system}.default; root = blog.packages.${pkgs.system}.default;
locations."/".index = "home.html"; locations."/".index = "home.html";
}; };

View File

@ -1,20 +1,60 @@
{ ... }: { config, ... }:
{ {
services.openssh = { services.openssh = {
enable = true; enable = true;
# We set the hostkeys manually so they persist through reboots
hostKeys = [
{
path = (config.environment.persistence.ephemeral.persistentStoragePath + "/etc/ssh/ssh_host_ed25519_key");
type = "ed25519";
}
];
sftpFlags = [
"-f AUTHPRIV"
"-l INFO"
];
extraConfig = ''
AllowUsers price
'';
settings = { settings = {
PasswordAuthentication = false; PasswordAuthentication = false;
PermitRootLogin = "prohibit-password"; PermitRootLogin = "no";
LogLevel = "VERBOSE";
KexAlgorithms = [ KexAlgorithms = [
"curve25519-sha256" "curve25519-sha256"
"curve25519-sha256@libssh.org" "curve25519-sha256@libssh.org"
"diffie-hellman-group16-sha512" "diffie-hellman-group-exchange-sha256"
"diffie-hellman-group18-sha512" ];
"sntrup761x25519-sha512@openssh.com" Ciphers = [
"chacha20-poly1305@openssh.com"
"aes256-gcm@openssh.com"
"aes128-gcm@openssh.com"
"aes256-ctr"
"aes192-ctr"
"aes128-ctr"
];
Macs = [
"hmac-sha2-512-etm@openssh.com"
"hmac-sha2-256-etm@openssh.com"
"umac-128-etm@openssh.com"
]; ];
}; };
ports = [ ports = [
2200 2200
]; ];
banner = ''
Orion Technologies - Security Notice
UNAUTHORIZED ACCESS TO THIS DEVICE IS PROHIBITED
You must have written, explicit, authorized
permission to access or configure this device.
Unauthorized attempts and actions to access or use
this system may result in civil and/or criminal
penalties. All activities performed on this device
are logged and monitored.
'';
}; };
} }

View File

@ -1,12 +0,0 @@
{ pkgs, user, ... }:
{
users.users = {
root = {
openssh.authorizedKeys.keys = [
"no-touch-required sk-ssh-ed25519@openssh.com AAAAGnNrLXNzaC1lZDI1NTE5QG9wZW5zc2guY29tAAAAIJ9ODXLAIfGH/7VNobQsp5nwBvNoh+pQMEH7s2jkHpkqAAAACHNzaDpsdW5h"
];
initialPassword = "pass";
};
};
}

19
hosts/luna/modules/users.nix Executable file
View File

@ -0,0 +1,19 @@
{ pkgs, user, config, secrets, ... }:
{
age.secrets.root-pw.file = "${secrets}/root-hash-pw.age";
security.sudo.wheelNeedsPassword = false;
users.users = {
root = {
passwordFile = config.age.secrets.root-pw.path;
};
price = {
isNormalUser = true;
extraGroups = [ "wheel" ];
shell = pkgs.bash;
openssh.authorizedKeys.keys = [
"no-touch-required sk-ssh-ed25519@openssh.com AAAAGnNrLXNzaC1lZDI1NTE5QG9wZW5zc2guY29tAAAAIJ9ODXLAIfGH/7VNobQsp5nwBvNoh+pQMEH7s2jkHpkqAAAACHNzaDpsdW5h"
];
};
};
}

View File

@ -1,4 +1,4 @@
{ modulesPath, ... }: { modulesPath, pkgs, ... }:
{ {
imports = imports =
@ -10,6 +10,48 @@
initrd = { initrd = {
availableKernelModules = [ "xhci_pci" "ahci" "nvme" "uas" "sd_mod" ]; availableKernelModules = [ "xhci_pci" "ahci" "nvme" "uas" "sd_mod" ];
kernelModules = [ ]; kernelModules = [ ];
systemd = {
enable = true;
initrdBin = [ pkgs.libuuid pkgs.gawk ];
services.rollback = {
description = "Rollback btrfs root subvolume";
wantedBy = [ "initrd.target" ];
before = [ "sysroot.mount" ];
after = [ "initrd-root-device.target" ];
unitConfig.DefaultDependencies = "no";
serviceConfig.Type = "oneshot";
script = ''
mkdir -p /mnt
DISK_LABEL="NixOS-Primary"
ATTEMPTS=5
printf "Attempting to find disk with label '%s'\n" "$DISK_LABEL"
while ((ATTEMPTS > 0)); do
if findfs LABEL="$DISK_LABEL"; then
printf "Found disk!\n"
break;
fi
((ATTEMPTS--))
sleep 3
printf "Remaining disk discovery attempts: %s\n" "$ATTEMPTS"
done
mount -t btrfs -o subvol=/ $(findfs LABEL="$DISK_LABEL") /mnt
btrfs subvolume list -to /mnt/root \
| awk 'NR>2 { printf $4"\n" }' \
| while read subvol; do
printf "Removing Subvolume: %s\n" "$subvol";
btrfs subvolume delete "/mnt/$subvol"
done
printf "Removing /root subvolume\n"
btrfs subvolume delete /mnt/root
printf "Restoring base /root subvolume\n"
btrfs subvolume snapshot /mnt/root-base /mnt/root
umount /mnt
'';
};
};
}; };
loader = { loader = {
systemd-boot.enable = true; systemd-boot.enable = true;

View File

@ -1,11 +1,6 @@
{ modulesPath, ... }: { modulesPath, ... }:
{ {
imports = [ zramSwap.enable = true;
./boot.nix
./filesystem.nix
./hardware.nix
];
system.stateVersion = "23.11";
} }

View File

@ -0,0 +1,57 @@
{ disk ? "nvme0n1", ... }: {
disko.devices = {
disk.${disk} = {
type = "disk";
device = "/dev/${disk}";
content = {
type = "gpt";
partitions = {
esp =
let
label = "NixOS-Boot";
in
{
priority = 1;
size = "512M";
type = "EF00";
content = {
extraArgs = [ "-n ${label}" "-F 32" ];
type = "filesystem";
format = "vfat";
mountpoint = "/boot";
mountOptions = [
"defaults"
];
};
};
root =
let
label = "NixOS-Primary";
in
{
size = "100%";
content = {
type = "btrfs";
extraArgs = [ "-f" "--label ${label}" ];
postCreateHook = ''
MOUNT="$(mktemp -d)"
mount "/dev/disk/by-label/${label}" "$MOUNT" -o subvol=/
trap 'umount $MOUNT; rm -rf $MOUNT' EXIT
btrfs subvolume snapshot -r "$MOUNT/root" "$MOUNT/root-base"
'';
subvolumes = {
"/root" = {
mountpoint = "/";
};
"/nix" = {
mountpoint = "/nix";
mountOptions = [ "compress=zstd" "noatime" ];
};
};
};
};
};
};
};
};
}

View File

@ -1,25 +0,0 @@
{ config, lib, pkgs, modulesPath, ... }:
{
fileSystems = {
"/" = {
device = "none";
fsType = "tmpfs";
options = [ "defaults" "noatime" "mode=755" ];
};
"/boot" = {
device = "/dev/disk/by-label/NixOS-Boot";
fsType = "vfat";
options = [ "defaults" "noatime" ];
depends = [ "/" ];
};
"/nix" = {
device = "/dev/disk/by-label/NixOS-Primary";
fsType = "btrfs";
options = [ "subvol=@nix" "compress=zstd" "noatime" ];
};
};
zramSwap.enable = true;
}

View File

@ -1,4 +1,5 @@
{ lib, config, ... }: { lib, config, ... }:
{ {
hardware.cpu.intel.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware; hardware.cpu.intel.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware;
nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux";
} }

1
hosts/luna/pubkey.nix Normal file
View File

@ -0,0 +1 @@
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIF5AtCUEvm9pSi8iI4xH5wnJ6dR9tZZY7qPS4GLJbQAW luna"

View File

@ -1,179 +1,106 @@
#!/usr/env/bin bash #!/usr/bin/env bash
install() { set -Eeuo pipefail
set -euo pipefail
local disk="${1}"
local encrypt_disk="${2}"
local host="${3}"
local boot_partition="" SCRIPT_DIR="$( cd -- "$(dirname "$0")" >/dev/null 2>&1 ; pwd -P )"
local primary_partition=""
if [[ "${disk}" == *"nvme"* ]]; then gen-system-key() {
local boot_partition="${disk}p1" local system="${1:?"No system provided to generate a key for!"}"
local primary_partition="${disk}p2" local priv_key_path="${2:?"No private key path provided!"}"
else local key_file="out-key"
local boot_partition="${disk}1" local priv_key
local primary_partition="${disk}2" local pub_key
# Gen Key in a temp directory
pushd "$(mktemp -d)" >/dev/null
ssh-keygen -t ed25519 -f ./"${key_file}" -N '' -C "${system}" -q
priv_key="$(cat "${key_file}")"
pub_key="$(cat "${key_file}.pub")"
rm -f "${key_file}" "${key_file}.pub" >/dev/null
popd >/dev/null
# Update public key for system and rekey secrets
printf "Rekeying for '%s' secrets with new system key!\n" "${system}" 1>&2
local host_pubkey_path="${SCRIPT_DIR}/hosts/${system}/pubkey.nix"
if [[ -r "${host_pubkey_path}" ]]; then
local backup_pub_key_path
backup_pub_key_path="${host_pubkey_path}.$(date +'%Y-%d-%m_%H:%M:%S')"
printf "Backing up old public key file to '%s'!\n" "${backup_pub_key_path}" 1>&2
mv "${host_pubkey_path}" "${backup_pub_key_path}"
fi fi
# The size is large because I'd like to be able to hibernate my laptop in its entirety. I have 64 GB of ram. printf '"%s"' > "${host_pubkey_path}" "${pub_key}"
git add "${host_pubkey_path}" 1>&2
local label_crypt_luks="NixOS-Crypt" pushd secrets >/dev/null
local label_primary="NixOS-Primary" agenix -r -i "${priv_key_path}" 1>&2
local label_boot="NixOS-Boot" git add . 1>&2
popd >/dev/null
swapoff -a >/dev/null 2>&1 || true printf "%s" "${priv_key}"
umount /mnt/**/* >/dev/null 2>&1 || true
umount /mnt/* >/dev/null 2>&1 || true
umount /mnt >/dev/null 2>&1 || true
cryptsetup close enc >/dev/null 2>&1 || true
wipefs -a "${disk}" || true
### Partition The Disk
parted "${disk}" -- mklabel gpt
# Boot partition
parted -a optimal "${disk}" -- mkpart ESP fat32 1MiB 1GiB
parted "${disk}" -- set 1 boot on
mkfs.vfat "${boot_partition}"
fatlabel "${boot_partition}" "${label_boot}"
# Primary Partition
parted -a optimal "${disk}" -- mkpart "${label_primary}" 1Gib 100%
### Encrypt
if [[ "${encrypt_disk}" == "yes" ]]; then
cryptsetup --verify-passphrase -v luksFormat "${primary_partition}"
cryptsetup config "${primary_partition}" --label "${label_crypt_luks}"
# Have to decrypt it so we can actually get other things setup
local crypt_open_name="enc"
cryptsetup open "${primary_partition}" "${crypt_open_name}"
primary_partition="/dev/mapper/${crypt_open_name}"
fi
### BTRFS Setup
# Go ahead and make the unencrypted BTRFS
mkfs.btrfs -f -L "${label_primary}" "${primary_partition}"
# Mount it
mount -t btrfs "${primary_partition}" /mnt
# Create our subvolumes
btrfs subvolume create "/mnt/@nix"
umount /mnt
### Final Mountings
# Mount tmpfs to mnt
mount -t tmpfs -o mode=755 none /mnt
# Create our directories
mkdir /mnt/{"boot","nix"}
# Mount our boot partition
mount -t vfat -o defaults,noatime "${boot_partition}" /mnt/boot
# Mount our btrfs subvolumes individually with some btrfs options
# NOTE: On high performance NVME SSDs with a beefy CPU it may be worth considering ZLO compression instead of ZSTD. In
# many cases ZLO is more performant, especially when writing, than ZSTD while having a somewhat worse comrpession ratio.
# WARN: ZLO *may* be a good solution, it can be VERY slow on incompressible data. Something to keep in mind.
mount -t btrfs -o noatime,compress=zstd,subvol=@nix "${primary_partition}" /mnt/nix
# Persistence dir, dirs not to be wiped on reboot stored here
mkdir -p /mnt/nix/persist
# Actually install NixOS
nixos-install --flake "git+file:.#${host}"
# Finally, clone the flake into the persistent nixos config
git clone . /mnt/nix/persist/etc/nixos/
}
usage() {
cat <<-__EOF__
Usage: $(basename "${0}") -d "/dev/DISK_HERE" -s 32 -e
-h | -? | --help
Shows this menu.
Example:
$(basename "${0}") -h
-d <string> | --disk <string> [REQUIRED]
Path to a disk to install NixOS to.
Default: none
Example:
$(basename "${0}") -d /dev/vda
-e | --encrypt [OPTIONAL]
Enable disk encryption during install. Note, this will prompt you for the encryption password.
Default: disabled
Example:
$(basename "${0}") -e
__EOF__
}
running_as_root() {
((EUID == 0))
} }
main() { main() {
if ! running_as_root; then local persist_dir="/mnt/nix/persist"
printf "This script MUST be ran as root, exiting!\n" local flake_install_path="${persist_dir}/ephemeral/etc/nixos"
local system="${1:?"Provide system to build!"}"
local flake=".#${system}"
local conn="${2:?"Provide ssh connection string! (E.g. root@myhost)"}"
local priv_key_path="${3:?Provide path to private key}"
shift 3
if [[ ! -r "${priv_key_path}" ]]; then
printf "Unable a private key file at '%s'\n!" "${priv_key_path}" 1>&2
exit 1
elif [[ ! -r "${SCRIPT_DIR}/hosts/${system}" ]]; then
printf "Could not find a system named '%s' in '%s'!\n" "${system}" "${SCRIPT_DIR}/hosts" 1>&2
exit 1 exit 1
fi fi
cat <<-__EOS__
─────────────────────────────────
Installing NixOS on Remote Host
=================================
Host: "${conn}"
Flake: "${flake}"
─────────────────────────────────
__EOS__
printf "Generating system keys\n"
local new_sys_key
new_sys_key="$(gen-system-key "${system}" "${priv_key_path}")"
local nixos_anywhere_log
nixos_anywhere_log="$(nix run github:nix-community/nixos-anywhere -- --flake "${flake}" "${conn}" --stop-after-disko 2>&1 | tee >(cat >&2))"
local ssh_login_key="${nixos_anywhere_log##*$'\n'}"; ssh_login_key="${ssh_login_key#*\'}"; ssh_login_key="${ssh_login_key%\'*}"
local ssh_opts="-i ${ssh_login_key} -o GlobalKnownHostsFile=/dev/null -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no"
local ssh_cmd="ssh ${conn} ${ssh_opts}"
local system_key_dest="${persist_dir}/ephemeral/etc/ssh/ssh_host_ed25519_key"
printf "SSH Command: %s\n" "${ssh_cmd}"
eval "${ssh_cmd}" <<- __EOS__
mkdir -p "${persist_dir}/ephemeral/etc/ssh"
mkdir -p "${persist_dir}/save"
mkdir -p "${flake_install_path}"
printf "Putting new system key into place\n"
printf "%s\n" "${new_sys_key}" > "${system_key_dest}"
chmod 0600 "${system_key_dest}"
__EOS__
printf "Copying flake to system\n";
rsync -r "${SCRIPT_DIR}"/{*,.*} "${conn}:${flake_install_path}" -e "ssh ${ssh_opts}" --info=PROGRESS2
printf "Doing final install\n"
eval "${ssh_cmd}" <<- __EOS__
set -euo pipefail
cd "${flake_install_path}"
nix-env -f '<nixpkgs>' -iA git
sudo nixos-install --flake "git+file:${flake}" --no-root-password --no-channel-copy
reboot
__EOS__
printf "%s\n" "${ssh_cmd}"
local disk="" cat <<- __EOS__
local host="" ──────────────────────────────────────────
local encrypt_disk="no" Finished Installing NixOS on Remote Host
==========================================
while :; do Host: "${conn}"
case ${1} in Flake: "${flake}"
-h | -\? | --help) ──────────────────────────────────────────
usage # Display a usage synopsis. __EOS__
exit
;;
--) # End of all options.
break
;;
-d | --disk)
shift
disk="${1}"
if [[ ! -b "${disk}" ]]; then
printf "Unable to read disk '%s', check that it exists, is a block device, and that you have write and read permissions for it!" "${disk}"
exit 1
fi
printf "Setting installation disk to '%s'\n" "${disk}"
;;
-e | --encrypt)
encrypt_disk="yes"
printf "Enabled disk encryption\n"
;;
-H | --host)
shift
host="${1}"
printf "Set host to '%s'\n" "${host}"
;;
-?*)
printf 'Unknown option: %s\n' "$1" >&2
usage
exit 1
;;
*) # Default case: No more options, so break out of the loop.
break ;;
esac
shift
done
set -euo pipefail
if [[ -z "${disk}" ]]; then
printf "Value for 'disk' (-d) was not provided! See '--help' for usage!\n"
exit 1
fi
if [[ -z "${host}" ]]; then
printf "Value to 'host' (-H) was not provided! See '--help' for usage!\n"
exit 1
fi
read -r -s -n 1 -p "Press any key to begin NixOS installation to '${disk}' for host '${host}'!"
printf "\n"
printf "Installing in "
for i in {3..1}; do
printf "%s.." "${i}"
sleep 1
done
printf "\n"
install "${disk}" "${encrypt_disk}" "${host}"
} }
main "${@}" main "${@}"

32
lib/default.nix Normal file
View File

@ -0,0 +1,32 @@
# Some of these functions were taken from https://github.com/NixOS/nixpkgs/blob/master/lib/
rec {
hasSuffix =
suffix:
string:
let
lenSuffix = builtins.stringLength suffix;
lenString = builtins.stringLength string;
in
(
lenString >= lenSuffix && (builtins.substring (lenString - lenSuffix) lenString string) == suffix
);
recurseDir = dir:
let
dirContents = builtins.readDir dir;
in
(builtins.concatMap
(dirItem:
let
itemType = builtins.getAttr dirItem dirContents;
itemPath = dir + "/${dirItem}";
in
if itemType == "directory" then
(recurseDir itemPath)
else
[ itemPath ])
(builtins.attrNames dirContents));
recurseFilesInDir = dir: suffix:
(builtins.filter (file: hasSuffix "${suffix}" file) (recurseDir dir));
recurseFilesInDirs = dirs: suffix:
(builtins.concatMap (dir: (recurseFilesInDir dir "${suffix}")) dirs);
}

Binary file not shown.

View File

@ -0,0 +1,10 @@
age-encryption.org/v1
-> ssh-ed25519 4sH96w zBi3BGIf/OJza0ADpk264QCudT5mKzxcxgfxoNI9HSw
8Ys6frvd7UeXRXPpiClCJ+qoRHiE7K+TfQhbb1o34bk
-> piv-p256 rJs1HA Aw/GcBY+XMD/9++r819joYKKyIrf8gbpjgIgugLhBtBq
BTIRQ/6zl2HqJJrCGbeMwAwmp65EiU5jAs130/MoHOs
-> OKjJEPyZ-grease l {KDS$m oX;O="m
29gF4VbnR6zEbojhshhTvk2tYizn32MsCDsuutl+HCmVTNPvDIkWRSMPylfsZqLY
AMl5wcis0Spit7SKkNKJVCtL/39fXu5zgJU
--- VTqUeA4weWW1vN8qr5jsTgIgbU/3rvYQX8A0gPgYGus
ÝúÑ<1A>†IVÒ¬Þïº*HøŒ[Õ°/IEz<45>Ôù#—ûMiÒ®Õp0ÒßMð(áë´TS—y§±î§—º‡¢ DÄn¾…@ƒ [/R亱?l7 (Ì 7&˜R“ö<E2809C>8 D¥ JE5°(˜±¦¨bÍÊpón5/·w/çßâ°

Binary file not shown.

View File

@ -1,15 +1,29 @@
let let
keys = rec { root-dir = builtins.toString ./.;
master = "age1yubikey1qfnj0k4mkzrn8ef5llwh2sv6hd7ckr0qml3n9hzdpz9c59ypvryhyst87k0"; lib = import ../lib;
orion-tech = { master-keys = [
luna = [ "age1yubikey1qfnj0k4mkzrn8ef5llwh2sv6hd7ckr0qml3n9hzdpz9c59ypvryhyst87k0"
"age1jgwqs04tphuuklx4g3gjdg42mchagn2gu7sftknerh8y8l9n7v7s27wqgu" ];
master hosts = {
luna =
let
secrets = "${root-dir}/luna";
in
[
"${secrets}/gitlab-runner-reg-config.age"
"${secrets}/root-hash-pw.age"
]; ];
};
}; };
in in
(builtins.listToAttrs
{ (builtins.concatMap
"gitlab-runner-reg-config.age".publicKeys = keys.orion-tech.luna; (host:
} (builtins.map
(secret: {
name = builtins.toString secret;
value = {
publicKeys = [ (import ./../hosts/${host}/pubkey.nix) ] ++ master-keys;
};
})
(builtins.getAttr host hosts)))
(builtins.attrNames hosts)))