diff --git a/nixos-infect b/nixos-infect index eb050ab..9a199d4 100644 --- a/nixos-infect +++ b/nixos-infect @@ -3,30 +3,21 @@ set -e -o pipefail makeConf() { - # Skip everything if main config already present [[ -e /etc/nixos/configuration.nix ]] && return 0 - # Lightsail config is not like the others if [ "$PROVIDER" = "lightsail" ]; then makeLightsailConf return 0 fi - # NB <<"EOF" quotes / $ ` in heredocs, < /etc/nixos/configuration.nix << EOF -{ config, pkgs, ... }: { +{ config, pkgs, ... }: + +{ imports = [ ./hardware-configuration.nix $network_import @@ -34,13 +25,14 @@ makeConf() { ]; nix.settings = { + # Enable flakes and new 'nix' command experimental-features = "nix-command flakes"; + # Deduplicate and optimize nix store auto-optimise-store = true; }; boot.loader.systemd-boot.enable = true; boot.loader.efi.canTouchEfiVariables = true; - boot.cleanTmpDir = true; zramSwap.enable = false; networking.hostName = "$(hostname -s)"; @@ -111,9 +103,8 @@ makeConf() { } EOF - if isEFI; then - bootcfg=$( - cat </etc/nixos/hardware-configuration.nix < /etc/nixos/hardware-configuration.nix << EOF { modulesPath, ... }: { imports = [ (modulesPath + "/profiles/qemu-guest.nix") ]; @@ -148,12 +138,12 @@ $bootcfg } EOF - [[ -n "$doNetConf" ]] && makeNetworkingConf || true + [[ -n "$doNetConf" ]] && makeNetworkingConf || true } makeLightsailConf() { - mkdir -p /etc/nixos - cat >/etc/nixos/configuration.nix < /etc/nixos/configuration.nix << EOF { config, pkgs, modulesPath, ... }: { imports = [ "\${modulesPath}/virtualisation/amazon-image.nix" ]; @@ -162,22 +152,21 @@ EOF } makeNetworkingConf() { - # XXX It'd be better if we used procfs for all this... - local IFS=$'\n' - eth0_name=$(ip address show | grep '^2:' | awk -F': ' '{print $2}') - eth0_ip4s=$(ip address show dev "$eth0_name" | grep 'inet ' | sed -r 's|.*inet ([0-9.]+)/([0-9]+).*|{ address="\1"; prefixLength=\2; }|') - eth0_ip6s=$(ip address show dev "$eth0_name" | grep 'inet6 ' | sed -r 's|.*inet6 ([0-9a-f:]+)/([0-9]+).*|{ address="\1"; prefixLength=\2; }|' || '') - gateway=$(ip route show dev "$eth0_name" | grep default | sed -r 's|default via ([0-9.]+).*|\1|') - gateway6=$(ip -6 route show dev "$eth0_name" | grep default | sed -r 's|default via ([0-9a-f:]+).*|\1|' || true) - ether0=$(ip address show dev "$eth0_name" | grep link/ether | sed -r 's|.*link/ether ([0-9a-f:]+) .*|\1|') + # XXX It'd be better if we used procfs for all this... + local IFS=$'\n' + eth0_name=$(ip address show | grep '^2:' | awk -F': ' '{print $2}') + eth0_ip4s=$(ip address show dev "$eth0_name" | grep 'inet ' | sed -r 's|.*inet ([0-9.]+)/([0-9]+).*|{ address="\1"; prefixLength=\2; }|') + eth0_ip6s=$(ip address show dev "$eth0_name" | grep 'inet6 ' | sed -r 's|.*inet6 ([0-9a-f:]+)/([0-9]+).*|{ address="\1"; prefixLength=\2; }|' || '') + gateway=$(ip route show dev "$eth0_name" | grep default | sed -r 's|default via ([0-9.]+).*|\1|') + gateway6=$(ip -6 route show dev "$eth0_name" | grep default | sed -r 's|default via ([0-9a-f:]+).*|\1|' || true) + ether0=$(ip address show dev "$eth0_name" | grep link/ether | sed -r 's|.*link/ether ([0-9a-f:]+) .*|\1|') - eth1_name=$(ip address show | grep '^3:' | awk -F': ' '{print $2}') || true - if [ -n "$eth1_name" ]; then - eth1_ip4s=$(ip address show dev "$eth1_name" | grep 'inet ' | sed -r 's|.*inet ([0-9.]+)/([0-9]+).*|{ address="\1"; prefixLength=\2; }|') - eth1_ip6s=$(ip address show dev "$eth1_name" | grep 'inet6 ' | sed -r 's|.*inet6 ([0-9a-f:]+)/([0-9]+).*|{ address="\1"; prefixLength=\2; }|' || '') - ether1=$(ip address show dev "$eth1_name" | grep link/ether | sed -r 's|.*link/ether ([0-9a-f:]+) .*|\1|') - interfaces1=$( - cat </etc/nixos/networking.nix < /etc/nixos/networking.nix << EOF { lib, ... }: { # This file was populated at runtime with the networking # details gathered from the active system. @@ -240,239 +229,204 @@ EOF } checkExistingSwap() { - SWAPSHOW=$(swapon --show --noheadings --raw) - zramswap=true - swapcfg="" - if [[ -n "$SWAPSHOW" ]]; then - SWAP_DEVICE="${SWAPSHOW%% *}" - if [[ "$SWAP_DEVICE" == "/dev/"* ]]; then - zramswap=false - swapcfg="swapDevices = [ { device = \"${SWAP_DEVICE}\"; } ];" - NO_SWAP=true - fi - fi + SWAPSHOW=$(swapon --show --noheadings --raw) + zramswap=true + swapcfg="" + if [[ -n "$SWAPSHOW" ]]; then + SWAP_DEVICE="${SWAPSHOW%% *}" + if [[ "$SWAP_DEVICE" == "/dev/"* ]]; then + zramswap=false + swapcfg="swapDevices = [ { device = \"${SWAP_DEVICE}\"; } ];" + NO_SWAP=true + fi + fi } makeSwap() { - swapFile=$(mktemp /tmp/nixos-infect.XXXXX.swp) - dd if=/dev/zero "of=$swapFile" bs=1M count=$((1 * 1024)) - chmod 0600 "$swapFile" - mkswap "$swapFile" - swapon -v "$swapFile" + swapFile=$(mktemp /tmp/nixos-infect.XXXXX.swp) + dd if=/dev/zero "of=$swapFile" bs=1M count=$((1*1024)) + chmod 0600 "$swapFile" + mkswap "$swapFile" + swapon -v "$swapFile" } removeSwap() { - swapoff -a - rm -vf /tmp/nixos-infect.*.swp + swapoff -a + rm -vf /tmp/nixos-infect.*.swp } isX86_64() { - [[ "$(uname -m)" == "x86_64" ]] + [[ "$(uname -m)" == "x86_64" ]] } isEFI() { - [ -d /sys/firmware/efi ] + [ -d /sys/firmware/efi ] } findESP() { - esp="" - for d in /boot/EFI /boot/efi /boot; do - [[ ! -d "$d" ]] && continue - [[ "$d" == "$(df "$d" --output=target | sed 1d)" ]] && - esp="$(df "$d" --output=source | sed 1d)" && - break - done - [[ -z "$esp" ]] && { - echo "ERROR: No ESP mount point found" - return 1 - } - for uuid in /dev/disk/by-uuid/*; do - [[ $(readlink -f "$uuid") == "$esp" ]] && echo $uuid && return 0 - done + esp="" + for d in /boot/EFI /boot/efi /boot; do + [[ ! -d "$d" ]] && continue + [[ "$d" == "$(df "$d" --output=target | sed 1d)" ]] \ + && esp="$(df "$d" --output=source | sed 1d)" \ + && break + done + [[ -z "$esp" ]] && { echo "ERROR: No ESP mount point found"; return 1; } + for uuid in /dev/disk/by-uuid/*; do + [[ $(readlink -f "$uuid") == "$esp" ]] && echo $uuid && return 0 + done } prepareEnv() { - # $esp and $grubdev are used in makeConf() - if isEFI; then - esp="$(findESP)" - else - for grubdev in /dev/vda /dev/sda /dev/xvda /dev/nvme0n1; do [[ -e $grubdev ]] && break; done - fi + # $esp and $grubdev are used in makeConf() + if isEFI; then + esp="$(findESP)" + else + for grubdev in /dev/vda /dev/sda /dev/xvda /dev/nvme0n1 ; do [[ -e $grubdev ]] && break; done + fi - # Retrieve root fs block device - # (get root mount) (get partition or logical volume) - rootfsdev=$(mount | grep "on / type" | awk '{print $1;}') - rootfstype=$(df $rootfsdev --output=fstype | sed 1d) + # Retrieve root fs block device + # (get root mount) (get partition or logical volume) + rootfsdev=$(mount | grep "on / type" | awk '{print $1;}') + rootfstype=$(df $rootfsdev --output=fstype | sed 1d) - # DigitalOcean doesn't seem to set USER while running user data - export USER="root" - export HOME="/root" + # DigitalOcean doesn't seem to set USER while running user data + export USER="root" + export HOME="/root" - # Nix installer tries to use sudo regardless of whether we're already uid 0 - #which sudo || { sudo() { eval "$@"; }; export -f sudo; } - # shellcheck disable=SC2174 - mkdir -p -m 0755 /nix + # Nix installer tries to use sudo regardless of whether we're already uid 0 + #which sudo || { sudo() { eval "$@"; }; export -f sudo; } + # shellcheck disable=SC2174 + mkdir -p -m 0755 /nix } fakeCurlUsingWget() { - # Use adapted wget if curl is missing - which wget && { - curl() { - eval "wget $( - ( - local isStdout=1 - for arg in "$@"; do - case "$arg" in - "-o") - echo "-O" - isStdout=0 - ;; - "-O") - isStdout=0 - ;; - "-L") ;; - *) - echo "$arg" - ;; - esac - done - [[ $isStdout -eq 1 ]] && echo "-O-" - ) | tr '\n' ' ' - )" - } - export -f curl - } + # Use adapted wget if curl is missing + which wget && { \ + curl() { + eval "wget $( + (local isStdout=1 + for arg in "$@"; do + case "$arg" in + "-o") + echo "-O"; + isStdout=0 + ;; + "-O") + isStdout=0 + ;; + "-L") + ;; + *) + echo "$arg" + ;; + esac + done; + [[ $isStdout -eq 1 ]] && echo "-O-" + )| tr '\n' ' ' + )" + }; export -f curl; } } req() { - type "$1" >/dev/null 2>&1 || which "$1" >/dev/null 2>&1 + type "$1" > /dev/null 2>&1 || which "$1" > /dev/null 2>&1 } checkEnv() { - [[ "$(whoami)" == "root" ]] || { - echo "ERROR: Must run as root" - return 1 - } + [[ "$(whoami)" == "root" ]] || { echo "ERROR: Must run as root"; return 1; } - # Perform some easy fixups before checking - # TODO prevent multiple calls to apt-get update - (which dnf && dnf install -y perl-Digest-SHA) || true # Fedora 24 - which bzcat || (which yum && yum install -y bzip2) || - (which apt-get && apt-get update && apt-get install -y bzip2) || - true - which xzcat || (which yum && yum install -y xz-utils) || - (which apt-get && apt-get update && apt-get install -y xz-utils) || - true - which curl || fakeCurlUsingWget || - (which apt-get && apt-get update && apt-get install -y curl) || - true + # Perform some easy fixups before checking + # TODO prevent multiple calls to apt-get update + (which dnf && dnf install -y perl-Digest-SHA) || true # Fedora 24 + which bzcat || (which yum && yum install -y bzip2) \ + || (which apt-get && apt-get update && apt-get install -y bzip2) \ + || true + which xzcat || (which yum && yum install -y xz-utils) \ + || (which apt-get && apt-get update && apt-get install -y xz-utils) \ + || true + which curl || fakeCurlUsingWget \ + || (which apt-get && apt-get update && apt-get install -y curl) \ + || true - req curl || req wget || { - echo "ERROR: Missing both curl and wget" - return 1 - } - req bzcat || { - echo "ERROR: Missing bzcat" - return 1 - } - req xzcat || { - echo "ERROR: Missing xzcat" - return 1 - } - req groupadd || { - echo "ERROR: Missing groupadd" - return 1 - } - req useradd || { - echo "ERROR: Missing useradd" - return 1 - } - req ip || { - echo "ERROR: Missing ip" - return 1 - } - req awk || { - echo "ERROR: Missing awk" - return 1 - } - req cut || req df || { - echo "ERROR: Missing coreutils (cut, df)" - return 1 - } + req curl || req wget || { echo "ERROR: Missing both curl and wget"; return 1; } + req bzcat || { echo "ERROR: Missing bzcat"; return 1; } + req xzcat || { echo "ERROR: Missing xzcat"; return 1; } + req groupadd || { echo "ERROR: Missing groupadd"; return 1; } + req useradd || { echo "ERROR: Missing useradd"; return 1; } + req ip || { echo "ERROR: Missing ip"; return 1; } + req awk || { echo "ERROR: Missing awk"; return 1; } + req cut || req df || { echo "ERROR: Missing coreutils (cut, df)"; return 1; } - # On some versions of Oracle Linux these have the wrong permissions, - # which stops sshd from starting when NixOS boots - chmod 600 /etc/ssh/ssh_host_*_key + # On some versions of Oracle Linux these have the wrong permissions, + # which stops sshd from starting when NixOS boots + chmod 600 /etc/ssh/ssh_host_*_key } infect() { - # Add nix build users - # FIXME run only if necessary, rather than defaulting true - groupadd nixbld -g 30000 || true - for i in {1..10}; do - useradd -c "Nix build user $i" -d /var/empty -g nixbld -G nixbld -M -N -r -s "$(which nologin)" "nixbld$i" || true - done - # TODO use addgroup and adduser as fallbacks - #addgroup nixbld -g 30000 || true - #for i in {1..10}; do adduser -DH -G nixbld nixbld$i || true; done + # Add nix build users + # FIXME run only if necessary, rather than defaulting true + groupadd nixbld -g 30000 || true + for i in {1..10}; do + useradd -c "Nix build user $i" -d /var/empty -g nixbld -G nixbld -M -N -r -s "$(which nologin)" "nixbld$i" || true + done + # TODO use addgroup and adduser as fallbacks + #addgroup nixbld -g 30000 || true + #for i in {1..10}; do adduser -DH -G nixbld nixbld$i || true; done - curl -L https://nixos.org/nix/install | sh -s -- --no-channel-add + curl -L https://nixos.org/nix/install | sh -s -- --no-channel-add - # shellcheck disable=SC1090 - source ~/.nix-profile/etc/profile.d/nix.sh + # shellcheck disable=SC1090 + source ~/.nix-profile/etc/profile.d/nix.sh - [[ -z "$NIX_CHANNEL" ]] && NIX_CHANNEL="nixos-23.05" - nix-channel --remove nixpkgs - nix-channel --add "https://nixos.org/channels/$NIX_CHANNEL" nixos - nix-channel --update + [[ -z "$NIX_CHANNEL" ]] && NIX_CHANNEL="nixos-23.05" + nix-channel --remove nixpkgs + nix-channel --add "https://nixos.org/channels/$NIX_CHANNEL" nixos + nix-channel --update - if [[ $NIXOS_CONFIG = http* ]]; then - curl $NIXOS_CONFIG -o /etc/nixos/configuration.nix - unset NIXOS_CONFIG - fi + if [[ $NIXOS_CONFIG = http* ]] + then + curl $NIXOS_CONFIG -o /etc/nixos/configuration.nix + unset NIXOS_CONFIG + fi - export NIXOS_CONFIG="${NIXOS_CONFIG:-/etc/nixos/configuration.nix}" + export NIXOS_CONFIG="${NIXOS_CONFIG:-/etc/nixos/configuration.nix}" - nix-env --set \ - -I nixpkgs=$HOME/.nix-defexpr/channels/nixos \ - -f '' \ - -p /nix/var/nix/profiles/system \ - -A system + nix-env --set \ + -I nixpkgs=$HOME/.nix-defexpr/channels/nixos \ + -f '' \ + -p /nix/var/nix/profiles/system \ + -A system - # Remove nix installed with curl | bash - rm -fv /nix/var/nix/profiles/default* - /nix/var/nix/profiles/system/sw/bin/nix-collect-garbage + # Remove nix installed with curl | bash + rm -fv /nix/var/nix/profiles/default* + /nix/var/nix/profiles/system/sw/bin/nix-collect-garbage - # Reify resolv.conf - [[ -L /etc/resolv.conf ]] && mv -v /etc/resolv.conf /etc/resolv.conf.lnk && cat /etc/resolv.conf.lnk >/etc/resolv.conf + # Reify resolv.conf + [[ -L /etc/resolv.conf ]] && mv -v /etc/resolv.conf /etc/resolv.conf.lnk && cat /etc/resolv.conf.lnk > /etc/resolv.conf - # Set label of root partition - if [ -n "$newrootfslabel" ]; then - echo "Setting label of $rootfsdev to $newrootfslabel" - e2label "$rootfsdev" "$newrootfslabel" - fi + # Set label of root partition + if [ -n "$newrootfslabel" ]; then + echo "Setting label of $rootfsdev to $newrootfslabel" + e2label "$rootfsdev" "$newrootfslabel" + fi - # Stage the Nix coup d'état - touch /etc/NIXOS - echo etc/nixos >>/etc/NIXOS_LUSTRATE - echo etc/resolv.conf >>/etc/NIXOS_LUSTRATE - echo root/.nix-defexpr/channels >>/etc/NIXOS_LUSTRATE - (cd / && ls etc/ssh/ssh_host_*_key* || true) >>/etc/NIXOS_LUSTRATE + # Stage the Nix coup d'état + touch /etc/NIXOS + echo etc/nixos >> /etc/NIXOS_LUSTRATE + echo etc/resolv.conf >> /etc/NIXOS_LUSTRATE + echo root/.nix-defexpr/channels >> /etc/NIXOS_LUSTRATE + (cd / && ls etc/ssh/ssh_host_*_key* || true) >> /etc/NIXOS_LUSTRATE - rm -rf /boot.bak - isEFI && umount "$esp" + rm -rf /boot.bak + isEFI && umount "$esp" - mv -v /boot /boot.bak || { - cp -a /boot /boot.bak - rm -rf /boot/* - umount /boot - } - if isEFI; then - mkdir -p /boot - mount "$esp" /boot - find /boot -depth ! -path /boot -exec rm -rf {} + - fi - /nix/var/nix/profiles/system/bin/switch-to-configuration boot + mv -v /boot /boot.bak || { cp -a /boot /boot.bak ; rm -rf /boot/* ; umount /boot ; } + if isEFI; then + mkdir -p /boot + mount "$esp" /boot + find /boot -depth ! -path /boot -exec rm -rf {} + + fi + /nix/var/nix/profiles/system/bin/switch-to-configuration boot } [ "$PROVIDER" = "digitalocean" ] && doNetConf=y # digitalocean requires detailed network config to be generated