-
Notifications
You must be signed in to change notification settings - Fork 201
/
nixos-infect
executable file
·244 lines (215 loc) · 7.85 KB
/
nixos-infect
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
#! /usr/bin/env bash
# More info at: https://github.com/elitak/nixos-infect
set -e -o pipefail
makeConf() {
# Skip everything if main config already present
[[ -e /etc/nixos/configuration.nix ]] && return 0
# NB <<"EOF" quotes / $ ` in heredocs, <<EOF does not
mkdir -p /etc/nixos
local IFS=$'\n'; keys=($(grep -vE '^[[:space:]]*(#|$)' /root/.ssh/authorized_keys))
cat > /etc/nixos/configuration.nix << EOF
{ ... }: {
imports = [
./hardware-configuration.nix
./networking.nix # generated at runtime by nixos-infect
$NIXOS_IMPORT
];
boot.cleanTmpDir = true;
networking.hostName = "$(hostname)";
networking.firewall.allowPing = true;
services.openssh.enable = true;
users.users.root.openssh.authorizedKeys.keys = [$(for key in "${keys[@]}"; do echo -n "
\"$key\""; done)
];
}
EOF
# If you rerun this later, be sure to prune the filesSystems attr
cat > /etc/nixos/hardware-configuration.nix << EOF
{ ... }:
{
imports = [ <nixpkgs/nixos/modules/profiles/qemu-guest.nix> ];
boot.loader.grub.device = "/dev/$disk";
fileSystems."/" = { device = "/dev/${disk}1"; fsType = "ext4"; };
}
EOF
# 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|')
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|')
gateway6=$(ip -6 route show dev "$eth1_name" | grep default | sed -r 's|default via ([0-9a-f:]+).*|\1|' || true)
interfaces1=<< EOF
$eth1_name = {
ip4 = [$(for a in "${eth1_ip4s[@]}"; do echo -n "
$a"; done)
];
ip6 = [$(for a in "${eth1_ip6s[@]}"; do echo -n "
$a"; done)
];
EOF
extraRules1="ATTR{address}==\"${ether1}\", NAME=\"eth0\""
else
interfaces1=""
extraRules1=""
fi
nameservers=($(grep ^nameserver /etc/resolv.conf | cut -f2 -d' '))
cat > /etc/nixos/networking.nix << EOF
{ ... }: {
# This file was populated at runtime with the networking
# details gathered from the active system.
networking = {
nameservers = [$(for a in "${nameservers[@]}"; do echo -n "
\"$a\""; done)
];
defaultGateway = "${gateway}";
defaultGateway6 = "${gateway6}";
interfaces = {
$eth0_name = {
ip4 = [$(for a in "${eth0_ip4s[@]}"; do echo -n "
$a"; done)
];
ip6 = [$(for a in "${eth0_ip6s[@]}"; do echo -n "
$a"; done)
];
};
$interfaces1
};
};
services.udev.extraRules = ''
ATTR{address}=="${ether0}", NAME="eth0"
$extraRules1
'';
}
EOF
#! /usr/bin/env bash
# NB put your semi-sensitive (not posted to github) configuration in a separate
# file and include it via this customConfig() function. e.g.:
# customConfig() {
# cat > /etc/nixos/custom.nix << EOF
# { config, lib, pkgs, ... }: {
# }
# EOF
# }
#
# then you can add the files in configuration.nix's imports above and run something like:
# cat customConfig nixos-infect | root@targethost bash
if [[ "$(type -t customConfig)" == "function" ]]; then customConfig; fi
}
makeSwap() {
# TODO check currently available swapspace first
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() {
for swapFile in /tmp/nixos-infect.*.swp
do
swapoff -v "$swapFile"
rm -vf "$swapFile"
done
}
prepareEnv() {
# $disk is used in makeConf()
for disk in vda sda; do [[ -e /dev/$disk ]] && break; done
# DigitalOcean doesn't seem to set USER while running user data
export USER="root"
export HOME="/root"
# Use adapted wget if curl is missing
which curl || { \
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; }
# 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
}
req() {
type "$1" > /dev/null 2>&1 || which "$1" > /dev/null 2>&1
}
checkEnv() {
# Perform some easy fixups before checking
which dnf && dnf install -y perl-Digest-SHA # Fedora 24
which bzcat || (which yum && yum install -y bzip2) \
|| (which apt-get && apt-get install bzip2) \
|| true
[[ "$(whoami)" == "root" ]] || { echo "ERROR: Must run as root"; return 1; }
req curl || req wget || { echo "ERROR: Missing both curl and wget"; return 1; }
req bzcat || { echo "ERROR: Missing bzcat"; 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 || { echo "ERROR: Missing cut"; return 1; }
}
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
curl https://nixos.org/nix/install | $SHELL
# shellcheck disable=SC1090
source ~/.nix-profile/etc/profile.d/nix.sh
[[ -z "$NIX_CHANNEL" ]] && NIX_CHANNEL="nixos-17.03"
nix-channel --remove nixpkgs
nix-channel --add "https://nixos.org/channels/$NIX_CHANNEL" nixos
nix-channel --update
export NIXOS_CONFIG=/etc/nixos/configuration.nix
nix-env --set \
-I nixpkgs=$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
# 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
rm -rf /boot.bak
mv -v /boot /boot.bak
/nix/var/nix/profiles/system/bin/switch-to-configuration boot
}
prepareEnv
checkEnv
makeConf
makeSwap # smallest (512MB) droplet needs extra memory!
infect
removeSwap
reboot