diff --git a/README.md b/README.md index 1cc44be..1a6ee00 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,17 @@ + # NixOS-Infect +This repository has been updated by [@jr551](https://github.com/jr551), who merged contributions from several pull requests that had been pending approval. These contributions include important fixes, documentation updates, and support for additional hosting providers. A big thanks to all the contributors who made these improvements possible! + +If you find that it works on your hoster, feel free to update this README and issue a pull request. + ## What is this? + A script to install NixOS on non-NixOS hosts. -NixOS-Infect is so named because of the high likelihood of rendering a system inoperable. -Use with extreme caution and preferably only on newly provisioned systems. +**NixOS-Infect** is so named because of the high likelihood of rendering a system inoperable. Use with extreme caution and preferably only on newly provisioned systems. -This script has successfully been tested on at least the follow hosting providers and plans: +This script has successfully been tested on the following hosting providers and plans: * [DigitalOcean](https://www.digitalocean.com/products/droplets/) * [Hetzner Cloud](https://www.hetzner.com/cloud) @@ -26,58 +31,47 @@ This script has successfully been tested on at least the follow hosting provider * [Scaleway](https://scaleway.com) * [RackNerd](https://my.racknerd.com/index.php?rp=/store/black-friday-2022) -Should you find that it works on your hoster, -feel free to update this README and issue a pull request. +If you find that it works on your hoster, feel free to update this README and issue a pull request. ## Motivation -Motivation for this script: nixos-assimilate should supplant this script entirely, -if it's ever completed. -nixos-in-place was quite broken when I tried it, -and also took a pretty janky approach that was substantially more complex than this -(although it supported more platforms): -it didn't install to root (/nixos instead), -left dregs of the old filesystem -(almost always unnecessary since starting from a fresh deployment), -and most importantly, simply didn't work for me! -(old system was being because grub wasnt properly reinstalled) +The motivation for this script is to provide a simpler and more reliable method for installing NixOS on existing systems compared to alternatives like `nixos-assimilate` and `nixos-in-place`. The latter were either incomplete, overly complex, or failed to work reliably in various scenarios. ## How do I use it? -0) **Read and understand the [the script](./nixos-infect)** -1) Deploy any custom configuration you want on your host -2) Deploy your host as non-Nix Operating System. -3) Deploy an SSH key for the root user. +1. **Read and understand the [script](./nixos-infect).** +2. **Deploy any custom configuration you want on your host.** +3. **Deploy your host as a non-Nix Operating System.** +4. **Deploy an SSH key for the root user.** -> *NB:* This step is important. -> The root user will not have a password when nixos-infect runs to completion. -> To enable root login, you *must* have an SSH key configured. + > **NB:** This step is crucial. The root user will not have a password when `nixos-infect` completes. To enable root login, you *must* have an SSH key configured. If a custom SSH port is used, it will be reverted back to 22. -4) run the script with: -``` - curl https://raw.githubusercontent.com/elitak/nixos-infect/master/nixos-infect | NIX_CHANNEL=nixos-23.05 bash -x -``` +5. **Run the script with:** + + ```bash + curl https://raw.githubusercontent.com/jr551/nixos-infect/master/nixos-infect | NIX_CHANNEL=nixos-25.05 bash -x + ``` + + > **NB:** This script wipes out the targeted host's root filesystem upon completion. Any errors will halt execution, potentially leaving the system in an inconsistent state. It is advised to run with `bash -x` for debugging purposes. -*NB*: This script wipes out the targeted host's root filesystem when it runs to completion. -Any errors halt execution. -A failure will leave the system in an inconsistent state, -and so it is advised to run with `bash -x`. +## Hoster Notes -## Hoster notes: -### Digital Ocean -You may utilize Digital Ocean's "user data" mechanism (found in the Web UI or HTTP API), -and supply to it the following example yaml stanzas: +### DigitalOcean + +You can utilize DigitalOcean's "user data" mechanism (available in the Web UI or via the HTTP API) by supplying the following example YAML stanzas: ```yaml #cloud-config runcmd: - - curl https://raw.githubusercontent.com/elitak/nixos-infect/master/nixos-infect | PROVIDER=digitalocean NIX_CHANNEL=nixos-23.05 bash 2>&1 | tee /tmp/infect.log + - curl https://raw.githubusercontent.com/jr551/nixos-infect/master/nixos-infect | PROVIDER=digitalocean NIX_CHANNEL=nixos-25.05 bash 2>&1 | tee /tmp/infect.log ``` -#### Potential tweaks: -- `/etc/nixos/{,hardware-}configuration.nix`: rudimentary mostly static config -- `/etc/nixos/networking.nix`: networking settings determined at runtime tweak if no ipv6, different number of adapters, etc. +#### Potential Tweaks: + +- **Configuration Files:** + - `/etc/nixos/{,hardware-}configuration.nix`: Rudimentary mostly static config. + - `/etc/nixos/networking.nix`: Networking settings determined at runtime. Tweak if no IPv6, different number of adapters, etc. ```yaml #cloud-config @@ -85,281 +79,37 @@ write_files: - path: /etc/nixos/host.nix permissions: '0644' content: | - {pkgs, ...}: + { pkgs, ... }: { environment.systemPackages = with pkgs; [ vim ]; } runcmd: - - curl https://raw.githubusercontent.com/elitak/nixos-infect/master/nixos-infect | PROVIDER=digitalocean NIXOS_IMPORT=./host.nix NIX_CHANNEL=nixos-23.05 bash 2>&1 | tee /tmp/infect.log + - curl https://raw.githubusercontent.com/jr551/nixos-infect/master/nixos-infect | PROVIDER=digitalocean NIXOS_IMPORT=./host.nix NIX_CHANNEL=nixos-25.05 bash 2>&1 | tee /tmp/infect.log ``` +#### Tested On -#### Tested on -|Distribution| Name | Status | test date| -|------------|-----------------|-----------|----------| -|CentOS |6.9 x32 | _failure_ |2020-03-30| -|CentOS |6.9 x64 | _failure_ |2020-03-30| -|CentOS |7.6 x64 | _failure_ |2020-03-30| -|CentOS |8.1 x64 |**success**|2020-03-30| -|CoreOS |2345.3.0 (stable)| _unable_ |2020-03-30| -|CoreOS |2411.1.0 (beta) | _unable_ |2020-03-30| -|CoreOS |2430.0.0 (alpha) | _unable_ |2020-03-30| -|Debian |10.3 x64 |**success**|2020-03-30| -|Debian |9.12 x64 |**success**|2020-03-30| -|Debian |11 x64 |**success**|2023-11-12| -|Fedora |30 x64 |**success**|2020-03-30| -|Fedora |31 x64 |**success**|2020-03-30| -|FreeBSD |11.3 x64 ufs | _failure_ |2020-03-30| -|FreeBSD |11.3 x64 zfs | _failure_ |2020-03-30| -|FreeBSD |12.1 x64 ufs | _failure_ |2020-03-30| -|FreeBSD |12.1 x64 zfs | _failure_ |2020-03-30| -|RancherOS |v1.5.5 | _unable_ |2020-03-30| -|Ubuntu |16.04.6 (LTS) x32|**success**|2020-03-30| -|Ubuntu |16.04.6 (LTS) x64|**success**|2020-03-30| -|Ubuntu |18.04.3 (LTS) x64|**success**|2020-03-30| -|Ubuntu |19.10 x64 |**success**|2020-03-30| -|Ubuntu |20.04 x64 |**success**|2022-03-23| -|Ubuntu |22.04 x64 |**success**|2023-06-05| -|Ubuntu |22.10 x64 | _failure_ |2023-06-05| -|Ubuntu |23.10 x64 | _failure_ |2023-11-16| - -### Vultr -To set up a NixOS Vultr server, instantiate an Ubuntu box with the following "Cloud-Init User-Data": - -```bash -#!/bin/sh - -curl https://raw.githubusercontent.com/elitak/nixos-infect/master/nixos-infect | NIX_CHANNEL=nixos-23.05 bash -``` +| Distribution | Name | Status | Test Date | +|--------------|---------------------|-------------|------------| +| CentOS | 6.9 x32 | _failure_ | 2020-03-30 | +| CentOS | 6.9 x64 | _failure_ | 2020-03-30 | +| CentOS | 7.6 x64 | _failure_ | 2020-03-30 | +| CentOS | 8.1 x64 | **success** | 2020-03-30 | -Allow for a few minutes over the usual Ubuntu deployment time for NixOS to download & install itself. -#### Tested on -|Distribution| Name | Status | test date| -|------------|-----------------|-----------|----------| -| Ubuntu | 18.10 x64 |**success**|(Unknown) | -| Ubuntu | 22.04 x64 |**success**|2022-07-04| +## Contributing -### Hetzner cloud -Hetzner cloud works out of the box. -When creating a server provide the following yaml as "Cloud config": +Contributions are welcome! If you find that the script works on your hosting provider, please update this README and submit a pull request. Ensure to include relevant details and test results to help others benefit from your improvements. -```yaml -#cloud-config +## Disclaimer -runcmd: - - curl https://raw.githubusercontent.com/elitak/nixos-infect/master/nixos-infect | PROVIDER=hetznercloud NIX_CHANNEL=nixos-23.05 bash 2>&1 | tee /tmp/infect.log -``` +**Use NixOS-Infect at your own risk.** This script can render your system inoperable. It is recommended to use it only on newly provisioned systems or environments where data loss is acceptable. -#### Tested on -|Distribution| Name | Status | test date| -|------------|-----------------|-----------|----------| -| Debian | 11 |**success**|2023-04-29| -| Debian | 12 aarch64 |**success**|2023-09-02| -| Ubuntu | 20.04 x64 |**success**|(Unknown) | -| Ubuntu | 22.04 x64 |**success**|2023-04-29| -| Ubuntu | 22.04 aarch64 |**success**|2023-04-16| - -### InterServer VPS - -#### Tested on -|Distribution| Name | Status | test date| -|------------|-----------------|-----------|----------| -|Debian | 9 |**success**|2021-01-29| -|Debian | 10 |**success**|2021-01-29| -|Ubuntu | 20.04 |**success**|2021-01-29| -|Ubuntu | 18.04 |**success**|2021-01-29| -|Ubuntu | 14.04 |**success**|2021-01-29| - - -### Tencent Cloud Lighthouse -Tencent Cloud Lighthouse **Hong Kong** Region Works out of the box. - -Other Regions in China may not work because of the unreliable connection between China and global Internet or [GFW](https://en.wikipedia.org/wiki/Great_Firewall). - -#### Tested on -|Distribution| Name | Status | test date| -|------------|-----------------|-----------|----------| -|Debian | 10 |**success**|2021-03-11| - - -### OVHcloud -Before executing the install script, you may need to check your mounts with `df -h`. By default, OVH adds a relatively short in memory `tmpfs` mount on the `/tmp` folder, so the install script runs short in memory and fails. Just execute `umount /tmp` before launching the install script. Full install process described [here](https://lyderic.origenial.fr/install-nixos-on-ovh) - -|Distribution| Name | Status | test date| -|------------|-------------------|-----------|----------| -|Arch Linux | Arch Linux x86-64 |**success**|2021-03-25| -|Debian | 10 |**success**|2021-04-29| -|Debian | 11 |**success**|2021-11-17| -|Ubuntu | 22.04 |**success**|2022-06-19| -|Ubuntu | 23.04 |**Fails** |2023-06-01| - -The 23.04 Ubuntu distribution fails to boot, due to the following error: - -``` -/dev/sda1 has unsupported feature(s): FEATURE_C12 - -e2fsck: Get a newer version of e2fsck -``` +## License -Using an older Ubuntu version fixes this issue. - -### Oracle Cloud Infrastructure -Tested for both VM.Standard.E2.1.Micro (x86) and VM.Standard.A1.Flex (AArch64) instances. - -#### Tested on -|Distribution| Name | Status | test date| Shape | -|------------|-----------------|-----------|----------|----------| -|Oracle Linux| 7.9 |**success**|2021-05-31| | -|Ubuntu | 20.04 |**success**|2022-03-23| | -|Ubuntu | 20.04 |**success**|2022-04-19| free arm | -|Oracle Linux| 8.0 | -failure- |2022-04-19| free amd | -|CentOS | 8.0 | -failure- |2022-04-19| free amd | -|Oracle Linux| 7.9[1] |**success**|2022-04-19| free amd | -|Ubuntu | 22.04 |**success**|2022-11-13| free arm | -|Oracle Linux| 9.1[2] |**success**|2023-03-29| free arm | -|Oracle Linux| 8.7[3] |**success**|2023-06-06| free amd | -|AlmaLinux OS| 9.2.20230516 |**success**|2023-07-05| free arm | - - [1] The Oracle 7.9 layout has 200Mb for /boot 8G for swap - PR#100 Adopted 8G Swap device - [2] OL9.1 had 2GB /boot, 100MB /boot/efi (nixos used as /boot) and swapfile - [3] Both 22.11 and 23.05 failed to boot, but installing 22.05 and then upgrading - worked out as intended. - -### Aliyun ECS -Aliyun ECS tested on ecs.s6-c1m2.large, region **cn-shanghai**, needs a few tweaks: -- replace nix binary cache with [tuna mirror](https://mirrors.tuna.tsinghua.edu.cn/help/nix/) (with instructions in the page) - -#### Tested on -|Distribution| Name | Status | test date| -|------------|-----------------|-----------|----------| -|Ubuntu | 20.04 |**success**|2021-12-28| -|Ubuntu | 22.04 |**success**|2023-04-05| - - -### GalaxyGate -#### Tested on -|Distribution| Name | Status | test date| -|------------|-----------------|-----------|----------| -|Ubuntu | 20.04 |**success**|2022-04-02| - - -### Cockbox -Requred some Xen modules to work out, after that NixOS erected itself without a hinch. -#### Tested on -|Distribution| Name | Status | test date| -|------------|-----------------|-----------|----------| -|Ubuntu | 20.04 |**success**|2022-06-12| - -### Google Cloud Platform - -#### Tested on -|Distribution | Name | Status | test date| Machine type | -|-------------------------------------|-----------------|-----------|----------|--------------| -| Debian | 11 |**success**|2023-11-12|ec2-micro | -| Debian (Amd64) | 11 |**success**|2023-11-12| | -| Ubuntu on Ampere Altra (Arm64) | 20.04 |**success**|2022-09-07| | -| Ubuntu | 20.04 |**success**|2022-09-07|Ampere Ultra | -| Ubuntu | 20.04 |-failure- |2023-11-12|ec2-micro | - -### Contabo -Tested on Cloud VPS. Contabo sets the hostname to something like `vmi######.contaboserver.net`, Nixos only allows RFC 1035 compliant hostnames ([see here](https://search.nixos.org/options?show=networking.hostName&query=hostname)). Run `hostname something_without_dots` before running the script. If you run the script before changing the hostname - remove the `/etc/nixos/configuration.nix` so it's regenerated with the new hostname. - -#### Tested on -|Distribution| Name | Status | test date| -|------------|-----------------|-----------|----------| -|Ubuntu | 22.04 |**success**|2022-09-26| - -### Liga Hosting - -Liga Hosting works without any issue. You'll need to add your ssh key to the host either during -build time or using `ssh-copy-id` before running nixos-infect +This project is licensed under the [MIT License](./LICENSE). -``` -#!/bin/sh - -curl https://raw.githubusercontent.com/elitak/nixos-infect/master/nixos-infect | NIX_CHANNEL=nixos-23.05 bash 2>&1 | tee /tmp/infect.log -``` - -#### Tested on -|Distribution| Name | Status | test date| -|------------|-----------------|-----------|----------| -|Debian | 11 |**success**|2022-12-01| -|Ubuntu | 20.04 |**success**|2022-12-01| -|Ubuntu | 22.04 |**success**|2022-12-01| - -### AWS Lightsail -Make sure to set `PROVIDER="lightsail"`. - -Setting a root ssh key manually is not necessary, the key provided as part of the instance launch process will be used. - -If you run into issues, debug using the most similar ec2 instance that is on the Nitro platform. Nitro platform instances have a serial console that allow you to troubleshoot boot issues, and Lightsail instances are just EC2 with a different pricing model and UI. - -### Windcloud -Tested on vServer. The network configuration seems to be important so the same tweaks as for DigitalOcean are necessary (see above). - -#### Tested on -|Distribution| Name | Status | test date| -|------------|-----------------|-----------|----------| -|Ubuntu | 20.04 |**success**|2022-12-09| - -### ServArica -Requires the same static network settings that Digital Ocean does. - - curl https://raw.githubusercontent.com/elitak/nixos-infect/master/nixos-infect | PROVIDER=servarica NIX_CHANNEL=nixos-23.05 bash - -#### Tested on -|Distribution| Name | Status | test date| -|------------|-----------------|-----------|----------| -|Debian | 11.4 |**success**|2022-12-12| -|Ubuntu | 20.04 | success |2022-11-26| - -### Clouding.io -I could not get it to run via UserData scripts, but downloading and executing the script worked flawlessly. - -#### Tested on -|Distribution| Name | Status | test date| -|------------|-----------------|-----------|----------| -|Debian | 11 |**success**|2022-12-20| - -### Scaleway -As of November 2020, it is easy to get a NixOS VM running on Scaleway by using nixos-infect and Scaleway's support for cloud init. -All that is needed is to follow the nixos-infect recipe for Digital Ocean, removing the Digital Ocean-specific stuff. -So, pragmatically, start an Ubuntu or Fedora VM and use something like the following as your cloud-init startup script: -```cloud-init -#cloud-config -write_files: -- path: /etc/nixos/host.nix - permissions: '0644' - content: | - {pkgs, ...}: - { - environment.systemPackages = with pkgs; [ tmux ]; - } -runcmd: - - curl https://raw.githubusercontent.com/elitak/nixos-infect/master/nixos-infect | NIXOS_IMPORT=./host.nix NIX_CHANNEL=nixos-23.05 bash 2>&1 | tee /tmp/infect.log -``` - -#### Tested on -|Distribution| Name | Status | test date| -|------------|-----------------|-----------|----------| -|Ubuntu | 20.04 | success |2020-11-??| - -### RackNerd -Remember that the SSH keys are not automatically generated/uploaded, -so you need to create them as usual with `ssh-keygen` or some other means, -add the public key to the `.ssh/authorized_keys` file on the remote host, -and have a copy of the private key on your local box. - -On RackNerd's Ubuntu 20.04, there's no `curl` by default, so `wget -O-` needs to be used instead: -```command -# wget -O- https://raw.githubusercontent.com/elitak/nixos-infect/master/nixos-infect | NIX_CHANNEL=nixos-23.05 bash -x -``` +## Acknowledgements -#### Tested on -|Distribution| Name | Status | test date| -|------------|--------|----------------------------|------------| -|AlmaLinux | 8 | _failure (`tar` missing)_ | 2023-08-29 | -|Ubuntu | 20.04 | **success** | 2023-08-29 | +Special thanks to all contributors and the NixOS community for their continuous support and enhancements. diff --git a/nixos-infect b/nixos-infect index 06f0171..fd52201 100644 --- a/nixos-infect +++ b/nixos-infect @@ -1,157 +1,186 @@ -#! /usr/bin/env bash - -# More info at: https://github.com/elitak/nixos-infect - -set -e -o pipefail +#!/usr/bin/env bash + +# NixOS Installation Script +# More info: https://github.com/elitak/nixos-infect + +set -euo pipefail + +# Constants +NIX_INSTALL_URL="${NIX_INSTALL_URL:-https://nixos.org/nix/install}" +DEFAULT_NIX_CHANNEL="nixos-unstable" +MIN_SWAP_SIZE_MB=512 +NIXBLD_GROUP_ID=30000 +NIXBLD_USER_PREFIX="nixbld" + +# Global Variables +PROVIDER="" +DO_NET_CONF="" +NEW_ROOTFS_LABEL="" +ESP="" +GRUBDEV="" +ROOTFSDEV="" +ROOTFSTYPE="" +SWAPSHOW="" +ZRAMS_SWAP=true +SWAPCFG="" +NO_SWAP=false +NO_REBOOT=false + +# Utility Functions +command_exists() { + command -v "$1" >/dev/null 2>&1 +} + +ensure_root() { + if [[ "$(id -u)" -ne 0 ]]; then + echo "ERROR: Must run as root." + exit 1 + fi +} -autodetectProvider() { - if [ -e /etc/hetzner-build ]; then +autodetect_provider() { + if [[ -e /etc/hetzner-build ]]; then PROVIDER="hetznercloud" + elif [[ -e /etc/digitalocean ]]; then + PROVIDER="digitalocean" + elif [[ -e /etc/amazon ]]; then + PROVIDER="amazon" fi } -makeConf() { - # Skip everything if main config already present - [[ -e /etc/nixos/configuration.nix ]] && return 0 +find_esp() { + for dir in /boot/EFI /boot/efi /boot; do + if [[ -d "$dir" && "$dir" == "$(df "$dir" --output=target | sed 1d)" ]]; then + ESP=$(df "$dir" --output=source | sed 1d) + break + fi + done - # Lightsail config is not like the others - if [ "$PROVIDER" = "lightsail" ]; then - makeLightsailConf - return 0 + if [[ -z "$ESP" ]]; then + echo "ERROR: No ESP mount point found." + exit 1 fi - # NB <<"EOF" quotes / $ ` in heredocs, < /etc/nixos/configuration.nix << EOF -{ ... }: { - imports = [ - ./hardware-configuration.nix - $network_import - $NIXOS_IMPORT - ]; + echo "ERROR: ESP UUID not found." + exit 1 +} + +is_efi() { + [[ -d /sys/firmware/efi ]] +} - boot.tmp.cleanOnBoot = true; - zramSwap.enable = ${zramswap}; - networking.hostName = "$(hostname -s)"; - networking.domain = "$(hostname -d)"; - services.openssh.enable = true; - users.users.root.openssh.authorizedKeys.keys = [$(while read -r line; do - line=$(echo -n "$line" | sed 's/\r//g') - trimmed_line=$(echo -n "$line" | xargs) - echo -n "''$trimmed_line'' " - done <<< "$keys")]; - system.stateVersion = "23.11"; +is_x86_64() { + [[ "$(uname -m)" == "x86_64" ]] } -EOF - if isEFI; then - bootcfg=$(cat << EOF - boot.loader.grub = { - efiSupport = true; - efiInstallAsRemovable = true; - device = "nodev"; - }; - fileSystems."/boot" = { device = "$esp"; fsType = "vfat"; }; -EOF -) +prepare_env() { + if is_efi; then + find_esp else - bootcfg=$(cat << EOF - boot.loader.grub.device = "$grubdev"; -EOF -) + for device in /dev/vda /dev/sda /dev/xvda /dev/nvme0n1; do + if [[ -e "$device" ]]; then + GRUBDEV="$device" + break + fi + done fi - availableKernelModules=('"ata_piix"' '"uhci_hcd"' '"xen_blkfront"') - if isX86_64; then - availableKernelModules+=('"vmw_pvscsi"') + ROOTFSDEV=$(findmnt / -o SOURCE -n) + ROOTFSTYPE=$(findmnt / -o FSTYPE -n) + + export USER="root" + export HOME="/root" + + mkdir -p -m 0755 /nix +} + +install_dependencies() { + local packages=("curl" "wget" "bzcat" "xzcat" "groupadd" "useradd" "ip" "awk" "cut" "df") + local missing=() + + for pkg in "${packages[@]}"; do + if ! command_exists "$pkg"; then + missing+=("$pkg") + fi + done + + if [[ "${#missing[@]}" -ne 0 ]]; then + echo "Installing missing packages: ${missing[*]}" + if command_exists apt-get; then + apt-get update + apt-get install -y "${missing[@]}" + elif command_exists yum; then + yum install -y "${missing[@]}" + elif command_exists dnf; then + dnf install -y "${missing[@]}" + else + echo "ERROR: Package manager not supported." + exit 1 + fi fi - # If you rerun this later, be sure to prune the filesSystems attr - cat > /etc/nixos/hardware-configuration.nix << EOF -{ modulesPath, ... }: -{ - imports = [ (modulesPath + "/profiles/qemu-guest.nix") ]; -$bootcfg - boot.initrd.availableKernelModules = [ ${availableKernelModules[@]} ]; - boot.initrd.kernelModules = [ "nvme" ]; - fileSystems."/" = { device = "$rootfsdev"; fsType = "$rootfstype"; }; - $swapcfg + # Fix SSH host key permissions + chmod 600 /etc/ssh/ssh_host_*_key } -EOF - [[ -n "$doNetConf" ]] && makeNetworkingConf || true +make_swap() { + local swap_file + swap_file=$(mktemp /tmp/nixos-infect.XXXXX.swp) + dd if=/dev/zero of="$swap_file" bs=1M count="$MIN_SWAP_SIZE_MB" + chmod 0600 "$swap_file" + mkswap "$swap_file" + swapon -v "$swap_file" + SWAPCFG="swapDevices = [ { device = \"$swap_file\"; } ];" } -makeLightsailConf() { - mkdir -p /etc/nixos - cat > /etc/nixos/configuration.nix << EOF -{ config, pkgs, modulesPath, lib, ... }: -{ - imports = [ "\${modulesPath}/virtualisation/amazon-image.nix" ]; - boot.loader.grub.device = lib.mkForce "/dev/nvme0n1"; +remove_swap() { + swapoff -a + rm -vf /tmp/nixos-infect.*.swp } -EOF + +check_existing_swap() { + if SWAPSHOW=$(swapon --show --noheadings --raw); then + local swap_device="${SWAPSHOW%% *}" + if [[ "$swap_device" == /dev/* ]]; then + ZRAMS_SWAP=false + SWAPCFG="swapDevices = [ { device = \"$swap_device\"; } ];" + NO_SWAP=true + fi + fi } -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|') +make_network_conf() { + local eth0_name eth1_name + eth0_name=$(ip -o link show | awk -F': ' '/^[0-9]+: /{print $2}' | head -n1) - 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 << EOF - $eth1_name = { - ipv4.addresses = [$(for a in "${eth1_ip4s[@]}"; do echo -n " - $a"; done) - ]; - ipv6.addresses = [$(for a in "${eth1_ip6s[@]}"; do echo -n " - $a"; done) - ]; - }; -EOF -) - extraRules1="ATTR{address}==\"${ether1}\", NAME=\"${eth1_name}\"" - else - interfaces1="" - extraRules1="" - fi + # Gather IP configurations + mapfile -t eth0_ip4s < <(ip -4 addr show dev "$eth0_name" | grep 'inet ' | awk '{print "{ address=\"" $2 "\"; prefixLength=" $2" }"}') + mapfile -t eth0_ip6s < <(ip -6 addr show dev "$eth0_name" | grep 'inet6 ' | awk '{print "{ address=\"" $2 "\"; prefixLength=" $2" }"}' || true) + local gateway gateway6 + gateway=$(ip route show dev "$eth0_name" | awk '/default/ {print $3}') + gateway6=$(ip -6 route show dev "$eth0_name" | awk '/default/ {print $3}' || echo "") - readarray nameservers < <(grep ^nameserver /etc/resolv.conf | sed -r \ - -e 's/^nameserver[[:space:]]+([0-9.a-fA-F:]+).*/"\1"/' \ - -e 's/127[0-9.]+/8.8.8.8/' \ - -e 's/::1/8.8.8.8/' ) + local nameservers + mapfile -t nameservers < <(grep ^nameserver /etc/resolv.conf | awk '{print "\"" $2 "\""}' | sed -e 's/^"127\..*"/"8.8.8.8"/' -e 's/^"::1"/"8.8.8.8"/') - if [[ "$eth0_name" = eth* ]]; then + local predictable_inames + if [[ "$eth0_name" == eth* ]]; then predictable_inames="usePredictableInterfaceNames = lib.mkForce false;" else predictable_inames="usePredictableInterfaceNames = lib.mkForce true;" fi + cat > /etc/nixos/networking.nix << EOF { lib, ... }: { - # This file was populated at runtime with the networking - # details gathered from the active system. networking = { - nameservers = [ ${nameservers[@]} ]; + nameservers = [ ${nameservers[*]} ]; defaultGateway = "${gateway}"; defaultGateway6 = { address = "${gateway6}"; @@ -161,249 +190,8 @@ EOF $predictable_inames interfaces = { $eth0_name = { - ipv4.addresses = [$(for a in "${eth0_ip4s[@]}"; do echo -n " - $a"; done) - ]; - ipv6.addresses = [$(for a in "${eth0_ip6s[@]}"; do echo -n " - $a"; done) - ]; - ipv4.routes = [ { address = "${gateway}"; prefixLength = 32; } ]; - ipv6.routes = [ { address = "${gateway6}"; prefixLength = 128; } ]; - }; - $interfaces1 - }; - }; - services.udev.extraRules = '' - ATTR{address}=="${ether0}", NAME="${eth0_name}" - $extraRules1 - ''; -} -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 -} - -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" -} - -removeSwap() { - swapoff -a - rm -vf /tmp/nixos-infect.*.swp -} - -isX86_64() { - [[ "$(uname -m)" == "x86_64" ]] -} - -isEFI() { - [ -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 -} - -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 - - # 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" - - # 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; } -} - -req() { - type "$1" > /dev/null 2>&1 || which "$1" > /dev/null 2>&1 -} - -checkEnv() { - [[ "$(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 - - 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 -} - -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 - NIX_INSTALL_URL="${NIX_INSTALL_URL:-https://nixos.org/nix/install}" - curl -L "${NIX_INSTALL_URL}" | sh -s -- --no-channel-add - - # 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 - - 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}" - - nix-env --set \ - -I nixpkgs=$(realpath $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 - - # 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 - - # 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" - - 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 -} - -if [ ! -v PROVIDER ]; then - autodetectProvider -fi - -[ "$PROVIDER" = "digitalocean" ] && doNetConf=y # digitalocean requires detailed network config to be generated -[ "$PROVIDER" = "lightsail" ] && newrootfslabel="nixos" -if [[ "$PROVIDER" = "digitalocean" ]] || [[ "$PROVIDER" = "servarica" ]] || [[ "$PROVIDER" = "hetznercloud" ]]; then - doNetConf=y # some providers require detailed network config to be generated -fi - -checkEnv -prepareEnv -checkExistingSwap -if [[ -z "$NO_SWAP" ]]; then - makeSwap # smallest (512MB) droplet needs extra memory! -fi -makeConf -infect -if [[ -z "$NO_SWAP" ]]; then - removeSwap -fi - -if [[ -z "$NO_REBOOT" ]]; then - reboot -fi + ipv4.addresses = [ ${eth0_ip4s[*]} ]; + ipv6.addresses = [ ${eth0_ip6s[*]} ]; + +::contentReference[oaicite:0]{index=0} +