2
0
Fork 1
mirror of https://github.com/elitak/nixos-infect synced 2025-03-14 13:46:59 +00:00
This commit is contained in:
jr_blue_551 2025-01-05 20:47:23 +00:00 committed by GitHub
commit e5418736be
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 205 additions and 667 deletions

348
README.md
View file

@ -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:**
*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`.
```bash
curl https://raw.githubusercontent.com/jr551/nixos-infect/master/nixos-infect | NIX_CHANNEL=nixos-25.05 bash -x
```
## 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:
> **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.
## Hoster Notes
### 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
```
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|
| 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 |
### Hetzner cloud
Hetzner cloud works out of the box.
When creating a server provide the following yaml as "Cloud config":
```yaml
#cloud-config
## Contributing
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
```
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.
#### 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|
## Disclaimer
### InterServer VPS
**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 | 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|
## License
This project is licensed under the [MIT License](./LICENSE).
### Tencent Cloud Lighthouse
Tencent Cloud Lighthouse **Hong Kong** Region Works out of the box.
## Acknowledgements
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
```
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
```
#!/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
```
#### 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.

View file

@ -1,157 +1,186 @@
#! /usr/bin/env bash
#!/usr/bin/env bash
# More info at: https://github.com/elitak/nixos-infect
# NixOS Installation Script
# More info: https://github.com/elitak/nixos-infect
set -e -o pipefail
set -euo pipefail
autodetectProvider() {
if [ -e /etc/hetzner-build ]; then
# 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
}
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
# Lightsail config is not like the others
if [ "$PROVIDER" = "lightsail" ]; then
makeLightsailConf
return 0
fi
# NB <<"EOF" quotes / $ ` in heredocs, <<EOF does not
mkdir -p /etc/nixos
# Prevent grep for sending error code 1 (and halting execution) when no lines are selected : https://www.unix.com/man-page/posix/1P/grep
local IFS=$'\n'
for trypath in /root/.ssh/authorized_keys /home/$SUDO_USER/.ssh/authorized_keys $HOME/.ssh/authorized_keys; do
[[ -r "$trypath" ]] \
&& keys=$(sed -E 's/^[^#].*[[:space:]]((sk-ssh|sk-ecdsa|ssh|ecdsa)-[^[:space:]]+)[[:space:]]+([^[:space:]]+)([[:space:]]*.*)$/\1 \3\4/' "$trypath") \
&& [[ ! -z "$keys" ]] \
&& break
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
local network_import=""
[[ -n "$doNetConf" ]] && network_import="./networking.nix # generated at runtime by nixos-infect"
cat > /etc/nixos/configuration.nix << EOF
{ ... }: {
imports = [
./hardware-configuration.nix
$network_import
$NIXOS_IMPORT
];
if [[ -z "$ESP" ]]; then
echo "ERROR: No ESP mount point found."
exit 1
fi
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";
for uuid in /dev/disk/by-uuid/*; do
if [[ "$(readlink -f "$uuid")" == "$ESP" ]]; then
ESP="$uuid"
return
fi
done
echo "ERROR: ESP UUID not found."
exit 1
}
EOF
if isEFI; then
bootcfg=$(cat << EOF
boot.loader.grub = {
efiSupport = true;
efiInstallAsRemovable = true;
device = "nodev";
};
fileSystems."/boot" = { device = "$esp"; fsType = "vfat"; };
EOF
)
is_efi() {
[[ -d /sys/firmware/efi ]]
}
is_x86_64() {
[[ "$(uname -m)" == "x86_64" ]]
}
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
}
EOF
[[ -n "$doNetConf" ]] && makeNetworkingConf || true
# Fix SSH host key permissions
chmod 600 /etc/ssh/ssh_host_*_key
}
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";
}
EOF
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\"; } ];"
}
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|')
remove_swap() {
swapoff -a
rm -vf /tmp/nixos-infect.*.swp
}
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=""
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
}
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/' )
make_network_conf() {
local eth0_name eth1_name
eth0_name=$(ip -o link show | awk -F': ' '/^[0-9]+: /{print $2}' | head -n1)
if [[ "$eth0_name" = eth* ]]; then
# 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 "")
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"/')
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 '<nixpkgs/nixos>' \
-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}