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"
}
},
"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": {
"inputs": {
"nixpkgs": [
@ -44,6 +67,26 @@
"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": {
"inputs": {
"systems": "systems"
@ -62,6 +105,24 @@
"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": {
"inputs": {
"nixpkgs": [
@ -114,35 +175,14 @@
"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": {
"inputs": {
"agenix": "agenix",
"blog": "blog",
"disko": "disko",
"flake-utils": "flake-utils_2",
"impermanence": "impermanence",
"nixpkgs": "nixpkgs",
"personal-blog": "personal-blog"
"nixpkgs": "nixpkgs"
}
},
"systems": {
@ -159,6 +199,21 @@
"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"
}
}
},
"root": "root",

View File

@ -3,6 +3,7 @@
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
flake-utils.url = "github:numtide/flake-utils";
impermanence = {
url = "github:nix-community/impermanence";
};
@ -10,69 +11,89 @@
url = "github:ryantm/agenix";
inputs.nixpkgs.follows = "nixpkgs";
};
personal-blog = {
url = "gitlab:blog/blog?host=gitlab.orion-technologies.io";
disko = {
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";
};
};
outputs = inputs @ { self, nixpkgs, impermanence, agenix, ... }:
outputs = inputs @ { self, nixpkgs, impermanence, agenix, disko, flake-utils, blog, ... }:
let
specialArgs = {
secrets = ./secrets;
persist-dir = "/nix/persist";
};
lib = import ./lib // nixpkgs.lib;
pkgs = nixpkgs.legacyPackages."x86_64-linux";
defaults = {
config = {
environment.persistence = {
"${specialArgs.persist-dir}" = {
environment.etc.machine-id.source = "/nix/persist/ephemeral/etc/machine-id";
environment.persistence.save = {
hideMounts = true;
persistentStoragePath = "/nix/persist/save";
};
environment.persistence.ephemeral = {
persistentStoragePath = "/nix/persist/ephemeral";
hideMounts = true;
directories = [
"/var/lib"
"/var/log"
"/etc/nixos"
"/opt"
"/persist"
];
files = [
"/etc/machine-id"
"/etc/ssh/ssh_host_ed25519_key"
"/etc/ssh/ssh_host_ed25519_key.pub"
"/etc/ssh/ssh_host_rsa_key"
"/etc/ssh/ssh_host_rsa_key.pub"
{ directory = "/persist"; user = "root"; group = "root"; mode = "0700"; }
];
};
};
age.identityPaths = [
"/persist/nix.key"
];
};
};
in
{
nixosConfigurations.orion = nixpkgs.lib.nixosSystem {
nixosConfigurations.luna =
let
hostname = "luna";
in
nixpkgs.lib.nixosSystem
{
system = "x86_64-linux";
specialArgs = specialArgs;
specialArgs = {
inherit self;
inherit flake-utils;
inherit inputs;
inherit hostname;
inherit lib;
inherit blog;
secrets = "${self}/secrets/${hostname}";
disk = "nvme0n1";
fqdn = "orion-technologies.io";
};
modules = [
{ _module.args = inputs; }
{
_module.args = { };
}
defaults
./hosts/orion
impermanence.nixosModules.impermanence
agenix.nixosModules.default
disko.nixosModules.disko
./hosts/${hostname}
];
};
nixosConfigurations.luna = nixpkgs.lib.nixosSystem {
system = "x86_64-linux";
specialArgs = specialArgs;
modules = [
{ _module.args = inputs; }
defaults
./hosts/luna
impermanence.nixosModules.impermanence
agenix.nixosModules.default
];
} // 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, ... }:
{
imports = [
./modules
./os
];
system.stateVersion = "23.11";
imports = (lib.recurseFilesInDirs [ ./os ./modules ] ".nix");
system.stateVersion = "24.05";
}

View File

@ -2,15 +2,4 @@
{
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;
};
};
imports = [
./gitlab.nix
];
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -3,8 +3,5 @@
services.fail2ban = {
enable = true;
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, ... }:
let
blog-host = "blog.orion-technologies.io";
in
{ config, pkgs, blog, fqdn, ... }:
{
services.nginx = {
enable = true;
@ -15,10 +12,10 @@ in
defaults.email = "price@orion-technologies.io";
};
services.nginx.virtualHosts."${blog-host}" = {
services.nginx.virtualHosts."blog.${fqdn}" = {
forceSSL = true;
enableACME = true;
root = personal-blog.packages.${pkgs.system}.default;
root = blog.packages.${pkgs.system}.default;
locations."/".index = "home.html";
};

View File

@ -1,20 +1,60 @@
{ ... }:
{ config, ... }:
{
services.openssh = {
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 = {
PasswordAuthentication = false;
PermitRootLogin = "prohibit-password";
PermitRootLogin = "no";
LogLevel = "VERBOSE";
KexAlgorithms = [
"curve25519-sha256"
"curve25519-sha256@libssh.org"
"diffie-hellman-group16-sha512"
"diffie-hellman-group18-sha512"
"sntrup761x25519-sha512@openssh.com"
"diffie-hellman-group-exchange-sha256"
];
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 = [
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 =
@ -10,6 +10,48 @@
initrd = {
availableKernelModules = [ "xhci_pci" "ahci" "nvme" "uas" "sd_mod" ];
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 = {
systemd-boot.enable = true;

View File

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

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, ... }:
{
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 -euo pipefail
local disk="${1}"
local encrypt_disk="${2}"
local host="${3}"
set -Eeuo pipefail
local boot_partition=""
local primary_partition=""
if [[ "${disk}" == *"nvme"* ]]; then
local boot_partition="${disk}p1"
local primary_partition="${disk}p2"
else
local boot_partition="${disk}1"
local primary_partition="${disk}2"
SCRIPT_DIR="$( cd -- "$(dirname "$0")" >/dev/null 2>&1 ; pwd -P )"
gen-system-key() {
local system="${1:?"No system provided to generate a key for!"}"
local priv_key_path="${2:?"No private key path provided!"}"
local key_file="out-key"
local priv_key
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
# 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"
local label_primary="NixOS-Primary"
local label_boot="NixOS-Boot"
pushd secrets >/dev/null
agenix -r -i "${priv_key_path}" 1>&2
git add . 1>&2
popd >/dev/null
swapoff -a >/dev/null 2>&1 || true
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))
printf "%s" "${priv_key}"
}
main() {
if ! running_as_root; then
printf "This script MUST be ran as root, exiting!\n"
local persist_dir="/mnt/nix/persist"
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
fi
local disk=""
local host=""
local encrypt_disk="no"
while :; do
case ${1} in
-h | -\? | --help)
usage # Display a usage synopsis.
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
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
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}"
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}"
cat <<- __EOS__
──────────────────────────────────────────
Finished Installing NixOS on Remote Host
==========================================
Host: "${conn}"
Flake: "${flake}"
──────────────────────────────────────────
__EOS__
}
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
keys = rec {
master = "age1yubikey1qfnj0k4mkzrn8ef5llwh2sv6hd7ckr0qml3n9hzdpz9c59ypvryhyst87k0";
orion-tech = {
luna = [
"age1jgwqs04tphuuklx4g3gjdg42mchagn2gu7sftknerh8y8l9n7v7s27wqgu"
master
root-dir = builtins.toString ./.;
lib = import ../lib;
master-keys = [
"age1yubikey1qfnj0k4mkzrn8ef5llwh2sv6hd7ckr0qml3n9hzdpz9c59ypvryhyst87k0"
];
hosts = {
luna =
let
secrets = "${root-dir}/luna";
in
[
"${secrets}/gitlab-runner-reg-config.age"
"${secrets}/root-hash-pw.age"
];
};
};
in
{
"gitlab-runner-reg-config.age".publicKeys = keys.orion-tech.luna;
}
(builtins.listToAttrs
(builtins.concatMap
(host:
(builtins.map
(secret: {
name = builtins.toString secret;
value = {
publicKeys = [ (import ./../hosts/${host}/pubkey.nix) ] ++ master-keys;
};
})
(builtins.getAttr host hosts)))
(builtins.attrNames hosts)))