diff --git a/bin/deploy b/bin/deploy new file mode 100755 index 0000000000000000000000000000000000000000..01101cadc87734a53139cca18211c86c5a10bfcf --- /dev/null +++ b/bin/deploy @@ -0,0 +1,147 @@ +#!/usr/bin/env zsh + +set -e +# fail on undeclared variable +set -u +set -o pipefail + +readonly cmdname=$(basename $0) + + +nixos_system_file=$MACHINES_PATH/nixos.nix + +# This script cannot run without the nixos configuration entry point +if [[ ! -f "$nixos_system_file" ]] +then + print -P "%B%F{red}Error: %F{orange}nixos.nix%F{red} not found%b%f" >&2 + exit 2 +fi + + +source $DOTFILES_PATH/bin/util.zsh + +usage() { + print "Usage: $cmdname [--via via_hostname] <hostname> [switch|boot|reboot|test|dry-activate|build]" >&2 +} + +positional=() +via_hostname="" +while [[ $# -gt 0 ]] +do + case "$1" in + --help|-h) + usage + exit 0 + ;; + --via) + via_hostname="$2" + shift + ;; + *) + positional+=("$1") + ;; + esac + shift +done + +if [[ ${#positional[@]} -ne 1 && ${#positional[@]} -ne 2 ]] +then + print "Invalid number of arguments." >&2 + usage + exit 2 +fi + +readonly hostname="${positional[1]}" +if [[ ${#positional[@]} -ge 2 ]] +then + readonly original_operation="${positional[2]}" +else + # default operation + readonly original_operation=switch +fi + +if [[ -z "$via_hostname" ]] +then + via_hostname="$hostname" +fi + +operation=$original_operation +set_profile="" +reboot="" + +if [[ "$operation" = "switch" || "$operation" = "boot" ]] +then + set_profile=1 +elif [[ "$operation" = "reboot" ]] +then + operation="boot" + set_profile=1 + reboot=1 +elif [[ "$operation" = "test" || "$operation" = "dry-activate" || "$operation" = "build" ]] +then + # pass +else + print_error "Invalid operation: $operation" + usage + exit 2 +fi + +if [[ "$(hostname)" = "$hostname" ]] +then + readonly is_target_host=1 +else + readonly is_target_host="" +fi + + +readonly local_temp_dir=$(mktemp --tmpdir --directory phoenix-deploy.XXXXXXXXXX) +trap "rm -rf $local_temp_dir" EXIT INT HUP TERM + +print_info "Building target system configuration" +nix build --file "$nixos_system_file" --argstr hostname "$hostname" --out-link "$local_temp_dir/nixos-config-$hostname" +readonly nixos_config_path=$(realpath "$local_temp_dir/nixos-config-$hostname") + +if [[ "$operation" = "build" ]] +then + print_info "Build completed" + print $nixos_config_path + exit 0 +fi + +print_info "Deploying target system configuration" +if [[ "$is_target_host" ]] +then + # local deploy + + if [[ -n "$set_profile" ]] + then + sudo nix-env --profile /nix/var/nix/profiles/system --set $nixos_config_path + fi + sudo $nixos_config_path/bin/switch-to-configuration $operation + sync + + if [[ -n "$reboot" ]] + then + sudo systemctl reboot + fi +else + # remote deploy + + nix copy --file "$nixos_system_file" --argstr hostname "$hostname" --to "ssh://root@$via_hostname" + + # The manual way to do it (this is in theory also supported by nixos-rebuild by using '-I') + + if [[ -n "$set_profile" ]] + then + ssh root@$via_hostname "nix-env --profile /nix/var/nix/profiles/system --set $nixos_config_path" + fi + ssh root@$via_hostname "$nixos_config_path/bin/switch-to-configuration $operation && sync" + + if [[ -n "$reboot" ]] + then + ssh root@$via_hostname "systemctl reboot" + fi +fi + + +print_info "Update completed" diff --git a/bin/format.nix b/bin/format.nix new file mode 100644 index 0000000000000000000000000000000000000000..4d8cf59231366cf1224bda58f9b5dc5e5da7741c --- /dev/null +++ b/bin/format.nix @@ -0,0 +1,243 @@ +# This script formats a host, mounts the partitions, runs nixos-generate-config +# and then returns a summary that can be used to specialize the system +# configuration for the machine. + +{ pkgs ? import <nixpkgs> {}, hostname, template }: + +with builtins; with pkgs; +let + zsh-bin = "${zsh}/bin/zsh"; + realpath-bin = "${coreutils}/bin/realpath"; + lsblk-bin = "${utillinux}/bin/lsblk"; + blkid-bin = "${utillinux}/bin/blkid"; + blkdiscard-bin = "${utillinux}/bin/blkdiscard"; + sfdisk-bin = "${utillinux}/bin/sfdisk"; + mkswap-bin = "${utillinux}/bin/mkswap"; + swapon-bin = "${utillinux}/bin/swapon"; + mount-bin = "${utillinux}/bin/mount"; + umount-bin = "${utillinux}/bin/umount"; + cryptsetup-bin = "${cryptsetup}/bin/cryptsetup"; + pvcreate-bin = "${lvm2}/bin/pvcreate"; + lvcreate-bin = "${lvm2}/bin/lvcreate"; + vgcreate-bin = "${lvm2}/bin/vgcreate"; + mkfs-fat-bin = "${dosfstools}/bin/mkfs.fat"; + mkfs-ext4-bin = "${e2fsprogs}/bin/mkfs.ext4"; + mkfs-btrfs-bin = "${btrfsProgs}/bin/mkfs.btrfs"; + btrfs-bin = "${btrfsProgs}/bin/btrfs"; + fzf-bin = "${fzf}/bin/fzf"; + jq-bin = "${jq}/bin/jq"; + + swap = (if template ? swap then template.swap else "8G"); + luks = template.luks; + +in +assert (typeOf luks) == "bool"; +assert (typeOf swap) == "string"; +{ + format = writeScriptBin "format_${hostname}" '' + #!${zsh-bin} + set -e + set -u + set -o pipefail + set -x + + source ${./util.zsh} + + cmdname=$0 + usage() { + print "Usage: $cmdname <CONFIG_FILE> <OUTPUT_FILE> [<MESSAGE_PROGRAM>]" >&2 + } + + print_info() { + if [[ $# -ge 1 ]] + then + print -P "%B%F{blue}$1%b%f" >&2 + else + print >&2 + fi + } + + print_warning() { + print -P "%B%F{yellow}$1%b%f" >&2 + } + + print_error() { + print -P "%B%F{red}$1%b%f" >&2 + } + + if [ "$1" = "--help" -o "$1" = "-h" ] + then + usage + exit 0 + fi + + if [ $# -ne 2 -a $# -ne 3 ] + then + print "Invalid number of arguments." >&2 + usage + exit 2 + fi + + config_file="$1" + output_file="$2" + message_program="$3" + + # Before doing anything that could fail: + # Set up tmpdir controlled by this script (and trap to remove it) and move the + # installation config there to make sure it is deleted if the script fails in + # any way. + temp_dir=$(mktemp --tmpdir --directory install.nix.XXXXXXXXXX) + trap "rm -rf $temp_dir" EXIT INT HUP TERM + mv $config_file $temp_dir/config + config_file=$temp_dir/config + + block_device=$(${jq-bin} --raw-output .blockDevice $config_file) + if [ "$block_device" = null ] + then + block_device=$(${lsblk-bin} --nodeps --output PATH,NAME,SIZE,TYPE,MODEL,VENDOR | ${fzf-bin} --layout=reverse --header-lines=1 --nth=1 | awk '{print $1;}') + fi + + ${if luks then '' + luks_keyfile=$temp_dir/luksKey + luks_key=$(${jq-bin} -e --raw-output .luksKey $config_file) + print -n "$luks_key" > $luks_keyfile + '' else "" } + + if [ ! -b "$block_device" ] + then + print_info "error: $block_device is not a block device." + exit 1 + fi + + print_info "Selected block device: $block_device" + + stable_block_device=$(for i in /dev/disk/by-id/*; do [ "$(${realpath-bin} "$i")" = "$(${realpath-bin} "$block_device")" ] && echo "$i" && return; done) + + print_info "Stable block device name is: $stable_block_device" + print_info + + print_info "Printing current layout:" + print_info "$(${lsblk-bin} --output name,size,type,mountpoint,model,vendor "$block_device")" + print_info + + + print_info "You are about to install the configuration for host '${hostname}' to $block_device (this is executed on host '$(hostname)')." + if read -q "?Do you want to wipe all data on $block_device? (y/n) " + then + print >&2 + else + print >&2 + exit 3 + fi + + if [ -x $message_program ] + then + $message_program & + fi + + print_info "Discarding disk contents..." + if ${blkdiscard-bin} $block_device + then + ssd=true + else + ssd=false + print_warning "Discard failed" + fi + + print_info "Creating partition table for bootloader ${template.bootloader}" + + ${if template.bootloader == "efi" then '' + ${sfdisk-bin} "$block_device" <<EOF + label: gpt + start=2048, size=512MiB, type=C12A7328-F81F-11D2-BA4B-00A0C93EC93B, name="esp" + type=0FC63DAF-8483-4772-8E79-3D69D8477DE4, name="system" + EOF + esp_partition="$block_device"1 + system_partition="$block_device"2 + '' else if template.bootloader == "bios" then '' + ${sfdisk-bin} "$block_device" <<EOF + label: gpt + size=1MiB, type=21686148-6449-6E6F-744E-656564454649, name="bios_grub" + size=512MiB, type=0FC63DAF-8483-4772-8E79-3D69D8477DE4, name="boot" + type=0FC63DAF-8483-4772-8E79-3D69D8477DE4, name="system" + EOF + esp_partition="$block_device"2 + system_partition="$block_device"3 + '' else abort "Invalid bootloader configured in template: ${template.bootloader}" } + + print_info "Creating partitions..." + + ${mkfs-fat-bin} -F32 -n ESP "$esp_partition" + + ${if luks then '' + ${cryptsetup-bin} --batch-mode --key-file $luks_keyfile luksFormat --type luks2 $system_partition + + luks_partition_uuid=$(${blkid-bin} --match-tag UUID --output value $system_partition) + if [[ -z $luks_partition_uuid ]] + then + print_error "Cound not detect uuid of luks partition" >&2 + exit 1 + fi + + crypt_volume_name=cryptvol_${hostname} + + if $ssd + then + ${cryptsetup-bin} --batch-mode --key-file $luks_keyfile --allow-discards --persistent open $system_partition $crypt_volume_name + else + ${cryptsetup-bin} --batch-mode --key-file $luks_keyfile open $system_partition $crypt_volume_name + fi + + rm $luks_keyfile + + lvm_partition=/dev/mapper/$crypt_volume_name + '' else '' + lvm_partition=$system_partition + ''} + vg_name=vg_${hostname} + + ${pvcreate-bin} $lvm_partition + ${vgcreate-bin} $vg_name $lvm_partition + + ${lvcreate-bin} --size "${swap}" --name swap --yes $vg_name + swap_partition="/dev/$vg_name/swap" + ${mkswap-bin} -L swap $swap_partition + ${swapon-bin} $swap_partition + + ${lvcreate-bin} --extents "100%FREE" --name btrfs --yes $vg_name + root_partition="/dev/$vg_name/btrfs" + ${mkfs-btrfs-bin} -L "btrfs_${hostname}" "$root_partition" + + mount_point=/mnt + + # Create subvolumes + ${mount-bin} -o noatime,compress=zstd:1 $root_partition $mount_point + ${btrfs-bin} subvolume create $mount_point/${hostname} + ${btrfs-bin} subvolume create $mount_point/${hostname}/nix + ${umount-bin} $mount_point + + # Remount + ${mount-bin} -o subvol=/${hostname},noatime,compress=zstd:2 $root_partition $mount_point + + mkdir -p $mount_point/boot + ${mount-bin} -o noatime $esp_partition $mount_point/boot + + print_info "Generating NixOS hardware config..." + nixos-generate-config --root $mount_point + + print_info "Writing output..." + > "$output_file" <<EOF + { + "installedBlockDevice": "$stable_block_device", + "luks": ${toJSON luks}, + ${if luks then '' + "luksPartitionUuid": "$luks_partition_uuid", + '' else ""} + "ssd": $ssd, + "bootloader": "${template.bootloader}" + } + EOF + + print_info "Installation stage 1 completed" + ''; +} \ No newline at end of file diff --git a/bin/install b/bin/install new file mode 100755 index 0000000000000000000000000000000000000000..ed02a0a3c15349cbf78d63c30a308578af105bb5 --- /dev/null +++ b/bin/install @@ -0,0 +1,106 @@ +#!/usr/bin/env nix-shell +#!nix-shell -i zsh -p zsh -p nix + +set -e +set -u +set -o pipefail + +source $DOTFILES_PATH/bin/util.zsh + +cmdname=$(basename $0) +usage() { + print "Usage: $cmdname <via_host> <hostname>" >&2 +} + +if [[ $# -ge 1 ]] +then + if [[ "$1" = "--help" || "$1" = "-h" ]] + then + usage + exit 0 + fi +fi + +if [ $# -ne 2 ] +then + print "Invalid number of arguments." >&2 + usage + exit 2 +fi + +via_host="$1" +hostname="$2" + + +nixos_system_file=$MACHINES_PATH/nixos.nix + +# This script cannot run without the nixos configuration entry point +if [[ ! -f "$nixos_system_file" ]] +then + print -P "%B%F{red}Error: %F{orange}nixos.nix%F{red} not found%b%f" >&2 + exit 2 +fi + + +local_temp_dir=$(mktemp --tmpdir --directory install-via.XXXXXXXXXX) +trap "rm -rf $local_temp_dir" EXIT INT HUP TERM + +# Pre-build installation helper +nix build --file $DOTFILES_PATH/bin/format.nix --argstr hostname "$hostname" --arg template "(import $MACHINES_PATH).machineTemplates.$hostname" --out-link "$local_temp_dir/format_$hostname" + +if ! ssh -o VisualHostKey=yes root@$via_host true +then + print "Cannot connect to host '$via_host'" >&2 + exit 1 +fi + +if ! nix ping-store --store ssh://root@$via_host +then + print "Cannot connect to nix store on '$via_host'" >&2 + exit 1 +fi + +local_config_file=$local_temp_dir/config + +# Prepare config +#luks_key=$(pass hosts/$hostname/luks) +luks_key="" +> $local_config_file <<EOF +{ + "blockDevice": null, + "luksKey": "$luks_key" +} +EOF +luks_key="" + +nix copy --file $DOTFILES_PATH/bin/format.nix --argstr hostname "$hostname" --arg template "(import $MACHINES_PATH).machineTemplates.$hostname" --to ssh://root@$via_host + +remote_temp_dir=$(ssh root@$via_host mktemp --tmpdir --directory install-via.XXXXXXXXXX) +# copy install-helper and config +scp -r $local_temp_dir/* root@$via_host:$remote_temp_dir/ +scp $DOTFILES_PATH/bin/message root@$via_host:$remote_temp_dir/ + +# -t: Force pseudo-terminal allocation +ssh -t root@$via_host "$remote_temp_dir/format_$hostname/bin/format_$hostname" "$remote_temp_dir/config" "$remote_temp_dir/output.json" "$remote_temp_dir/message" +scp "root@$via_host:$remote_temp_dir/output.json" "$MACHINES_PATH/machines/$hostname/install-result.json" + +# TODO: ensure the working directory is set correctly +print_info "Gathering hardware information..." +scp root@$via_host:/mnt/etc/nixos/hardware-configuration.nix "$MACHINES_PATH/machines/$hostname/" + +print_info "Building target system configuration..." +nix build --file "$nixos_system_file" --argstr hostname "$hostname" --out-link "$local_temp_dir/nixos-config-$hostname" + +print_info "Deploying target system configuration..." +nix copy --file "$nixos_system_file" --argstr hostname "$hostname" --to ssh://root@$via_host +nixos_config_path=$(realpath "$local_temp_dir/nixos-config-$hostname") + +# TODO: merge commands +ssh root@$via_host "nixos-install --system $nixos_config_path && sync" +ssh root@$via_host mkdir --mode u=rwx,g=,o= --parents /mnt/secrets/passwords + +# TODO: get host-specific password +scp -r notThePassword root@$via_host:/mnt/secrets/passwords/root +scp -r notThePassword root@$via_host:/mnt/secrets/passwords/jens + +print_info "Installation completed" diff --git a/bin/install-mounted b/bin/install-mounted new file mode 100755 index 0000000000000000000000000000000000000000..cd37bb4bd6d6f4f48b9a11bad611c7905c9fdcbb --- /dev/null +++ b/bin/install-mounted @@ -0,0 +1,47 @@ +#!/usr/bin/env nix-shell +#!nix-shell --pure -i zsh -p zsh -p nix + +set -e + +source util.zsh + +cmdname=$(basename $0) +usage() { + print "Usage: $cmdname <via_host> <hostname>" >&2 +} + +if [ "$1" = "--help" -o "$1" = "-h" ] +then + usage + exit 0 +fi + +if [ $# -ne 2 ] +then + print "Invalid number of arguments." >&2 + usage + exit 2 +fi + +via_host="$1" +hostname="$2" + +local_temp_dir=$(mktemp --tmpdir --directory install-via.XXXXXXXXXX) +trap "rm -rf $local_temp_dir" EXIT INT HUP TERM + +print_info "Building target system configuration..." +nix build --file ../nixos.nix --argstr hostname "$hostname" --out-link "$local_temp_dir/nixos-config-$hostname" + +print_info "Deploying target system configuration..." +nix copy --file ../nixos.nix --argstr hostname "$hostname" --to ssh://root@$via_host +nixos_config_path=$(realpath "$local_temp_dir/nixos-config-$hostname") + +# TODO: merge commands +ssh root@$via_host "nixos-install --system $nixos_config_path && sync" +ssh root@$via_host mkdir --mode u=rwx,g=,o= --parents /mnt/secrets/passwords + +# TODO: get host-specific password +#scp -r notThePassword root@$via_host:/mnt/secrets/passwords/root +#scp -r notThePassword root@$via_host:/mnt/secrets/passwords/jens + +print_info "Installation completed" diff --git a/bin/message b/bin/message new file mode 100755 index 0000000000000000000000000000000000000000..8a57bcf39327f4e23718d34ecba360feae636941 --- /dev/null +++ b/bin/message @@ -0,0 +1,36 @@ +#!/usr/bin/env zsh + +message="We are experiencing technical difficulties +Please stand by..." + +tty_num=13 + +tty="/dev/tty$tty_num" + +# Change tty +chvt "$tty_num" + +reset() { + # Clear screen + printf "\e[3J" > $tty + # Reset cursor position + printf "\033[0;0H" > $tty +} + +reset +sleep 1 + +# print character char by char +for (( i=0; i<${#message}; i++ )) +do + print -n "${message:$i:1}" > $tty + sleep 0.1 +done + +# keep screen clear of error messages +while true +do + sleep 10 + reset + print -n "${message}" > $tty +done diff --git a/bin/sshd-allow-connection b/bin/sshd-allow-connection new file mode 100644 index 0000000000000000000000000000000000000000..2f536792840beae3398b59e17638946a6d51eddf --- /dev/null +++ b/bin/sshd-allow-connection @@ -0,0 +1,8 @@ +#!/bin/sh + +mkdir -p /root/.ssh +>> /root/.ssh/authorized_keys <EOF +ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOgQ0W/0pH5A0G8eyx2CQCQshsUb/23f2/mWyPWIvEXT jens@serenity +EOF + +systemctl start sshd.service \ No newline at end of file diff --git a/bin/util.zsh b/bin/util.zsh new file mode 100644 index 0000000000000000000000000000000000000000..ef03cdf45563d05c602ec7b9843b9963ef24f9b9 --- /dev/null +++ b/bin/util.zsh @@ -0,0 +1,11 @@ +print_info() { + print -P "%B%F{blue}$1%b%f" >&2 +} + +print_warning() { + print -P "%B%F{yellow}$1%b%f" >&2 +} + +print_error() { + print -P "%B%F{red}$1%b%f" >&2 +} diff --git a/nixos/channels/nixos-19.09/channel.json b/nixos/channels/nixos-19.09/channel.json new file mode 100644 index 0000000000000000000000000000000000000000..8d34a51f9dffafbea4f8eaf5d85f97d26d0c2dbd --- /dev/null +++ b/nixos/channels/nixos-19.09/channel.json @@ -0,0 +1,7 @@ +{ + "url": "https://github.com/NixOS/nixpkgs-channels/", + "rev": "093faad9684796975520d9d88503e76ab539b8ef", + "date": "2019-12-03T20:28:01-05:00", + "sha256": "0v3an5f5anvqqfpihp9sgrhnzv68qvjihq16mjhfycglsz758z6p", + "fetchSubmodules": false +} diff --git a/nixos/channels/nixos-19.09/default.nix b/nixos/channels/nixos-19.09/default.nix new file mode 100644 index 0000000000000000000000000000000000000000..5370bfc19d0091eebcaed5d41ddd8cec55d5a8f4 --- /dev/null +++ b/nixos/channels/nixos-19.09/default.nix @@ -0,0 +1,11 @@ +with builtins; + +let + + channelDef = fromJSON ( readFile ./channel.json ); + +in fetchGit { + name = "nixpkgs-19.09"; + ref = "nixos-19.09"; + inherit (channelDef) url rev; +} diff --git a/nixos/channels/nixos-19.09/update b/nixos/channels/nixos-19.09/update new file mode 100755 index 0000000000000000000000000000000000000000..4f11e78f2a296002755460aea74d3d00cd857c46 --- /dev/null +++ b/nixos/channels/nixos-19.09/update @@ -0,0 +1,4 @@ +#!/usr/bin/env nix-shell +#! nix-shell -i sh -p nix-prefetch-git + +nix-prefetch-git --rev refs/heads/nixos-19.09 --no-deepClone https://github.com/NixOS/nixpkgs-channels/ > channel.json diff --git a/nixos/channels/nixos-unstable/channel.json b/nixos/channels/nixos-unstable/channel.json new file mode 100644 index 0000000000000000000000000000000000000000..2cf445c584d713d67bf8bfec8eb045afe298f95c --- /dev/null +++ b/nixos/channels/nixos-unstable/channel.json @@ -0,0 +1,7 @@ +{ + "url": "https://github.com/NixOS/nixpkgs-channels/", + "rev": "e89b21504f3e61e535229afa0b121defb52d2a50", + "date": "2019-11-19T07:59:43-05:00", + "sha256": "0jqcv3rfki3mwda00g66d27k6q2y7ca5mslrnshfpbdm7j8ya0kj", + "fetchSubmodules": false +} diff --git a/nixos/channels/nixos-unstable/default.nix b/nixos/channels/nixos-unstable/default.nix new file mode 100644 index 0000000000000000000000000000000000000000..405068dad704e892473024b3509994493056fcb8 --- /dev/null +++ b/nixos/channels/nixos-unstable/default.nix @@ -0,0 +1,11 @@ +with builtins; + +let + + channelDef = fromJSON ( readFile ./channel.json ); + +in fetchGit { + name = "nixpkgs-unstable"; + ref = "nixos-unstable"; + inherit (channelDef) url rev; +} diff --git a/nixos/channels/nixos-unstable/update b/nixos/channels/nixos-unstable/update new file mode 100755 index 0000000000000000000000000000000000000000..747d5855ce4a1de6a7563e3dea050b035307da8e --- /dev/null +++ b/nixos/channels/nixos-unstable/update @@ -0,0 +1,4 @@ +#!/usr/bin/env nix-shell +#! nix-shell -i sh -p nix-prefetch-git + +nix-prefetch-git --rev refs/heads/nixos-unstable --no-deepClone https://github.com/NixOS/nixpkgs-channels/ > channel.json diff --git a/nixos/configuration.nix b/nixos/configuration.nix new file mode 100644 index 0000000000000000000000000000000000000000..1ee064937fb4b104e572cfa078f3653a595cb1b0 --- /dev/null +++ b/nixos/configuration.nix @@ -0,0 +1,39 @@ +# This is the entry point for my NixOS configuration. +{ name, path, channel }: +{ lib, config, pkgs, ... }: + +let + installResult = builtins.fromJSON (builtins.readFile (path + "/install-result.json")); + dotfilesConfig = import (path + "/dotfiles.nix"); + layerImports = map (l: ./layers + "/${l}.nix") dotfilesConfig.layers; +in +{ + imports = [ + ./modules + (path + "/configuration.nix") + (path + "/hardware-configuration.nix") + ] ++ layerImports; + + nixpkgs.config = { + packageOverrides = ( import ./pkgs ) { inherit lib config; } ; + }; + + # Pin channel in nix path + nix.nixPath = [ "nixpkgs=${channel}" ]; + + # Bootloader + boot.loader.systemd-boot.enable = (installResult.bootloader == "efi"); + boot.loader.efi.canTouchEfiVariables = (installResult.bootloader == "efi"); + boot.loader.grub.enable = (installResult.bootloader == "grub"); + boot.loader.grub.device = installResult.installedBlockDevice; + + # Default hostname ist machine directory name + networking.hostName = lib.mkDefault name; + + boot.initrd.luks.devices = if installResult.luks then { + cryptvol = { + device = "/dev/disk/by-uuid/" + installResult.luksPartitionUuid; + allowDiscards = true; + }; + } else {}; +} diff --git a/nixos/default.nix b/nixos/default.nix index a5de97f8056395f1752e168efd8f31c90e469f2e..c338ccc4c04aaf239d347c02d594387b7268a5fc 100644 --- a/nixos/default.nix +++ b/nixos/default.nix @@ -1,16 +1,54 @@ -# This is the entry point for my NixOS configuration. -{ layers ? [] }: -{ lib, config, pkgs, ... }: +# entry point for machine configurations: +# (import <repo-path> { machinesDir=./machines }).<netname>.configurations.<hostname> +{ machinesDir }: + +with builtins; let - layerImports = map (l: ./layers + "/${l}.nix") layers; + defaultChannel = (import channels/nixos-unstable); + + # helpers :: { *: ? } + helpers = import ./helpers.nix; + + # channelsDir :: path + channelsDir = ./channels; + # allChannels :: { *: path } + allChannels = with helpers; keysToAttrs (channelname: import (channelsDir + "/${channelname}")) (readFilterDir (filterAnd [(not filterDirHidden) filterDirDirs]) channelsDir); + # getMachineChannel :: string -> path + getMachineChannel = { name, path }: + let + channelFile = path + "/channel.nix"; + in + if (pathExists channelFile) + then (import channelFile) allChannels + else defaultChannel; + # machineChannels :: { *: path } + machineChannels = withMachines getMachineChannel; + + machinesDirContents = readDir machinesDir; + machineNames = filter (p: machinesDirContents.${p} == "directory") (attrNames machinesDirContents); + withMachines = lambda: listToAttrs (map (m: {name = m; value = lambda { name = m; path = (machinesDir + "/${m}"); }; }) machineNames); + mkMachineConfig = { name, path }: ( + import ./configuration.nix { + inherit name path; + channel = machineChannels.${name}; + } + ); + mkNixosSystemDerivation = { name, path }: + let + channel = machineChannels.${name}; + configuration = mkMachineConfig { inherit name path; }; + # Importing <nixpkgs/nixos> results in a nixos system closure + nixos = import "${channel}/nixos" { + system = "x86_64-linux"; + inherit configuration; + }; + in + nixos.system; in { - imports = [ - ./modules - ] ++ layerImports; - - nixpkgs.config = { - packageOverrides = ( import ./pkgs ) { inherit lib config; } ; - }; -} + configurations = withMachines mkMachineConfig; + nixosSystemDerivations = withMachines mkNixosSystemDerivation; + machineTemplates = withMachines ({name, path}: import (path + /template.nix)); + channels = machineChannels; +} \ No newline at end of file diff --git a/nixos/helpers.nix b/nixos/helpers.nix new file mode 100644 index 0000000000000000000000000000000000000000..ffc8b9e224841659814f9560749e08a880f270c0 --- /dev/null +++ b/nixos/helpers.nix @@ -0,0 +1,102 @@ +rec { + + # id :: a -> a + id = value: value; + + # not :: Bool -> Bool + # not :: (a -> Bool) -> a -> Bool + not = value: with builtins; + if isBool value then + ! value + else if isFunction value then + x: ! (value x) + else + throw ("value is a " + typeOf value + " while a Boolean or a Function was expected"); + + + # filterAnd :: [ (a -> Bool) ] -> a -> Bool + filterAnd = lambdas: value: with builtins; + all (lambda: lambda value) lambdas; + + + # filterOr :: [ (a -> Bool) ] -> a -> Bool + filterOr = lambdas: value: with builtins; + any (lambda: lambda value) lambdas; + + + # readFilterDir :: ({name:String, path:Path, type:String, ...} -> Bool) -> Path -> [ String ] + readFilterDir = lambda: path: with builtins; + let + dirContents = readDir path; + filterFunc = name: lambda rec { + inherit name; + path = path + "/${name}"; + type = dirContents.${name}; + }; + in filter filterFunc (attrNames dirContents); + + + # filterDirHidden :: {name:String, ...} -> Bool + filterDirHidden = { name, ... }: + (builtins.substring 0 1 name) == "."; + + + # filterDirDirs :: {type:String, ...} -> Bool + filterDirDirs = { type, ... }: + type == "directory"; + + + # filterDirFiles :: {type:String, ...} -> Bool + filterDirFiles = { type, ... }: + type == "regular"; + + + # filterDirSymlinks :: {type:String, ...} -> Bool + filterDirSymlinks = { type, ... }: + type == "symlink"; + + # keysToAttrs :: ( String -> a ) -> [ String ] -> { *: a } + keysToAttrs = lambda: strings: + builtins.listToAttrs (map (k: { + name = k; + value = lambda k; + }) strings); + + # isMaybe :: a -> Bool + isMaybe = m: with builtins; + if not isAttrs m then false else + if not (hasAttr "isMaybe") m then false else + if not isBool m.isMaybe then false else + m.isMaybe; + + # Just :: a -> Maybe a + Just = a: {isMaybe = true; hasValue = true; value = a;}; + + # Nothing :: Maybe a + Nothing = {isMaybe = true; hasValue = false;}; + + # maybe :: b -> (a -> b) -> Maybe a -> b + maybe = default: transform: m: + if not isMaybe m then + throw "maybe: ${m} is not a Maybe." + else + if m.hasValue then + transform m.value + else + default + ; + + # maybeToList :: Maybe a -> [ a ] + maybeToList = m: + if not isMaybe m then + throw "maybeToList: ${m} is not a Maybe." + else if m.hasValue then + [ m.value ] + else + []; + + # toExistingPath :: Path -> Maybe Path + toExistingPath = path: with builtins; + if pathExists path then Just path else Nothing; + +}