-
Notifications
You must be signed in to change notification settings - Fork 1
/
emergency
executable file
·125 lines (104 loc) · 2.87 KB
/
emergency
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
#!/usr/bin/env bash
set -euo pipefail
if [ "${#}" != 1 ]; then
(
echo "Usage: ${0} <connectTo>"
echo
echo 'connectTo is an SSH connection string'
) >&2
exit 1
fi
#####
# Variables
#####
connectTo="${1}"
sshDir="/run/user/$(id -u)/helsinki-ssh"
# shellcheck disable=SC2174
mkdir -m 0700 -p "${sshDir}"
sshFlags=(-o 'ControlMaster=auto' -o "ControlPath=${sshDir}/%r@%h:%p" -o 'ControlPersist=1h')
ssh="ssh ${connectTo} ${sshFlags[*]}"
myDir="$(dirname "${0}")"
#####
# kexec image
#####
nix build -f '<nixpkgs/nixos>' -I nixos-config="${myDir}/configuration.nix" config.system.build.kexec_tarball
#####
# Test connection and prepare
#####
if ! ${ssh} echo Connection successful; then
echo "Connection cannot be established"
exit 1
fi
if ! ${ssh} zgrep CONFIG_KEXEC=y /proc/config.gz; then
echo "Target system lacks kexec capabilities"
exit 1
fi
${ssh} apt install rsync || :
${ssh} mkdir -p /nix /tmp
${ssh} mount -t tmpfs tmpfs /tmp
${ssh} mkdir -p /tmp/work /tmp/nix
#####
# Networking
#####
# Serialize the current network state
ips="$(${ssh} ip a | awk -f "${myDir}/ips.awk")"
routes="$(${ssh} ip r | awk -f "${myDir}/routes.awk"
${ssh} ip -6 r | awk -f "${myDir}/routes.awk")"
nameservers="$(${ssh} grep ^nameserver /etc/resolv.conf | cut -d' ' -f2)"
# Handle systemd-resolved
nameservers="${nameservers/127.0.0.53/1.1.1.1}"
# Dump the network state if desired
if [ "${EMERGENCY_DUMP_NETWORK:-}" = 1 ]; then
echo "$ips" > emergency_ips
echo "$routes" > emergency_routes
echo "$nameservers" > emergency_nameservers
fi
# Deserialize the network state
{
# Do not set -e here which would fail if interfaces (e.g. VPN) are missing.
echo "set -x"
# IPs
while IFS=";" read -r interface interfaceIps; do
echo "ip l set ${interface} up"
for ip in $(echo "${interfaceIps}" | tr ';' '\n'); do
echo "ip a a ${ip} dev ${interface}"
done
done < <(echo "${ips}")
# Routes
allRoutes="$(while IFS=';' read -r net via dev; do
echo -n "ip r a $net"
[ -n "${via:-}" ] && echo -n " via ${via}"
[ -n "${dev:-}" ] && echo -n " dev ${dev}"
echo
done < <(echo "${routes}"))"
# Output per-link routes first
echo "${allRoutes}" | grep -v via
echo "${allRoutes}" | grep via
# Nameservers
echo '('
while IFS= read -r ns; do
echo "echo 'nameserver ${ns}'"
done < <(echo "${nameservers}")
echo ') > /etc/resolv.conf'
} | ${ssh} tee /tmp/ip-script > /dev/null
#####
# kexec
#####
# Copy and extract
rsync -P result/tarball/*.xz "${connectTo}:/tmp/emergency.tar.xz"
${ssh} tar xf /tmp/emergency.tar.xz -C /tmp
# Overlay the second /nix
${ssh} mount -t overlay overlay -o lowerdir=/nix,upperdir=/tmp/nix,workdir=/tmp/work /nix
# Here goes nothing
${ssh} /tmp/kexec
${ssh} -O exit
#####
# Wait for SSH to be available again
#####
sleep 10 # Ensure the old system is already shut down
echo -n "Attempting to connect..."
while ! ${ssh} echo Connection successful; do
sleep 5
echo -n "."
done
echo