forked from QubesOS/qubes-remote-support
-
Notifications
You must be signed in to change notification settings - Fork 0
/
qubes-remote-support-receiver-start
executable file
·377 lines (316 loc) · 14.7 KB
/
qubes-remote-support-receiver-start
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
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
#!/bin/bash
set -e
vm_name="$2"
if [ "$vm_name" = "" ]; then
vm_name="sys-whonix"
fi
dom0_check_dependencies() {
## Packaging should make sure these packages are installed in dom0 before proceeding.
#sudo qubes-dom0-update torsocks socat openssh-server x2goserver
command -v torsocks >/dev/null || { echo "ERROR: missing torsocks" >&2 ; exit 101; };
command -v socat >/dev/null || { echo "ERROR: missing socat" >&2 ; exit 102; };
command -v sshd >/dev/null || { echo "ERROR: missing sshd" >&2 ; exit 103; };
## x2goserver is optional.
}
no_root_check() {
if [ "$(id -u)" = "0" ]; then
echo "ERROR: Do not run $0 as root / with sudo!" >&2
exit 100
fi
}
start_info() {
echo "INFO: Starting Qubes Remote Support Receiver."
echo "INFO: This tool is supposed to be run by those who wish to receive remote support."
echo "INFO: Setting up... This will take a moment..."
}
dom0_copy_from_vm() {
qvm-run --user root --pass-io "$1" "cat $2" > "$3"
}
preparation() {
temp_dir="$(mktemp --directory)"
mkdir -p "$temp_dir/keys"
mkdir -p ~/.qubes-remote-support
}
colors() {
if [ "$TERM" = "" ]; then
return 0
fi
## Thanks to:
## http://mywiki.wooledge.org/BashFAQ/037
## Variables for terminal requests.
[[ -t 2 ]] && {
export alt=$( tput smcup || tput ti ) # Start alt display
export ealt=$( tput rmcup || tput te ) # End alt display
export hide=$( tput civis || tput vi ) # Hide cursor
export show=$( tput cnorm || tput ve ) # Show cursor
export save=$( tput sc ) # Save cursor
export load=$( tput rc ) # Load cursor
export bold=$( tput bold || tput md ) # Start bold
export stout=$( tput smso || tput so ) # Start stand-out
export estout=$( tput rmso || tput se ) # End stand-out
export under=$( tput smul || tput us ) # Start underline
export eunder=$( tput rmul || tput ue ) # End underline
export reset=$( tput sgr0 || tput me ) # Reset cursor
export blink=$( tput blink || tput mb ) # Start blinking
export italic=$( tput sitm || tput ZH ) # Start italic
export eitalic=$( tput ritm || tput ZR ) # End italic
[[ $TERM != *-m ]] && {
export red=$( tput setaf 1|| tput AF 1 )
export green=$( tput setaf 2|| tput AF 2 )
export yellow=$( tput setaf 3|| tput AF 3 )
export blue=$( tput setaf 4|| tput AF 4 )
export magenta=$( tput setaf 5|| tput AF 5 )
export cyan=$( tput setaf 6|| tput AF 6 )
}
export white=$( tput setaf 7|| tput AF 7 )
export default=$( tput op )
export eed=$( tput ed || tput cd ) # Erase to end of display
export eel=$( tput el || tput ce ) # Erase to end of line
export ebl=$( tput el1 || tput cb ) # Erase to beginning of line
export ewl=$eel$ebl # Erase whole line
export draw=$( tput -S <<< ' enacs
smacs
acsc
rmacs' || { \
tput eA; tput as;
tput ac; tput ae; } ) # Drawing characters
export back=$'\b'
} 2>/dev/null ||:
}
dom0_exit_handler() {
systemctl --user stop socat-9050 &>/dev/null || true
systemctl --user reset-failed socat-9050 &>/dev/null || true
}
dom0_run_socat() {
## Running this function early to allow socat to be launched into the background.
## By Qubes default, dom0 is non-networked.
## Use socat in combination with torsocks to allow running wormhole directly
## from Qubes dom0. This is to make VMs untrusted. I.e. a compromised VM
## should not get access to dom0 SSH server keys.
## In other words, allow running "torsocks wormhole" in dom0.
## (As executed by qubes-remote-support-receiver-wormhole-helper.)
##
## Listen with socat on port 9050.
## (9050 is the default Tor listening port. I.e. if Tor was installed, it would
## listen on that port. This is being simulated.)
## torsocks with its default configuration will use port 9050.
##
## We could use any dom0 port other than 9050.
## For that we would have to use something like this:
## TORSOCKS_CONF_FILE=/path/to/qubes-remote-support-torsocks-config torsocks
## But since nothing should be listening on port 9050 in dom0 the choice of
## this port is appropriate at a small expense of breakage if Tor was
## installed in dom0 which really should not be the case in dom0 and
## certainty is not the case in default configuration.
##
## Port number 9122 is an arbitrarily chosen unused Tor SocksPort inside Whonix-Gateway.
##
## Using bash with 'program-name &' followed by 'pid=$!' and writing that to a pid file
## is prone to race conditions. To avoid having to manually maintain a state, a pid file,
## systemd-run is being used.
## Terminate possibly running leftover processes from a previous run in case a previous
## run of this script was terminated with sigkill thorugh a kernel OOM due to an unrelated
## issue. This is to avoid race conditions.
systemctl --user stop socat-9050 &>/dev/null || true
systemctl --user reset-failed socat-9050 &>/dev/null || true
## Suppress output "Running as unit: socat-9050.service" to avoid confusing Qubes remote support gui.
systemd-run --user --unit socat-9050 socat TCP-LISTEN:9050,reuseaddr,fork "EXEC:qrexec-client -d $vm_name user\:\'\''/etc/qubes-rpc/qubes.ConnectTCP 9122\'\''" &>/dev/null
systemctl --user --no-pager --no-block status socat-9050 >/dev/null
## socat TCP-LISTEN:9050,reuseaddr,fork 'EXEC:qrexec-client -d sys-whonix user\:\'\''/etc/qubes-rpc/qubes.ConnectTCP 9122\'\'''
## Give socat sufficient time to set up listener.
## Hardcoded wait.A non-hardcoded, event based a protocol where socat signals readiness similar to
## systemd-notify would be overly complex.
sleep 2
## Symptom, shell output if socat is already listening:
## 2020/09/30 06:50:40 socat[4168765] E bind(5, {AF=2 0.0.0.0:9050}, 16): Address already in use
}
dom0_rpc_policy_setup() {
local append_string
if ! test -d /etc/qubes-rpc/policy ; then
sudo --non-interactive mkdir -p /etc/qubes-rpc/policy
fi
if ! test -f /etc/qubes-rpc/policy/qubes.ConnectTCP+22 ; then
sudo --non-interactive touch /etc/qubes-rpc/policy/qubes.ConnectTCP+22
fi
test -r /etc/qubes-rpc/policy/qubes.ConnectTCP+22
append_string="$vm_name dom0 allow,target=dom0"
if grep --quiet "$append_string" /etc/qubes-rpc/policy/qubes.ConnectTCP+22 ; then
true "INFO: /etc/qubes-rpc/policy/qubes.ConnectTCP+22 added modified."
else
echo "$append_string" | sudo --non-interactive tee --append /etc/qubes-rpc/policy/qubes.ConnectTCP+22 >/dev/null
fi
## Debugging.
#cat /etc/qubes-rpc/policy/qubes.ConnectTCP+22
}
dom0_sshd_hardening() {
local old new file_name
if ! test -e /etc/ssh/sshd_config ; then
echo "ERROR: file /etc/ssh/sshd_config does not exist!" >&2
exit 1
fi
## dom0 minimal sshd configuration hardening
old="PasswordAuthentication yes"
new="PasswordAuthentication no"
file_name="/etc/ssh/sshd_config"
sudo --non-interactive sed -i "s/$old/$new/g" "$file_name"
}
check_vm_installed() {
qvm-check "$vm_name" 2>/dev/null
}
start_vm() {
if qvm-check --running "$vm_name" 2>/dev/null ; then
true "INFO: already running, ok."
else
## Would exit non-zero if already running.
true "INFO: Not yet running, starting..."
qvm-start "$vm_name" 2>/dev/null
fi
}
vm_setup() {
## --pass-io is optional but useful for gathering debug output.
qvm-run --user root --pass-io "$vm_name" "systemctl start tor" >/dev/null
qvm-run --user root --pass-io "$vm_name" "systemctl restart qubes-whonix-remote-support.service" >/dev/null
qvm-run --user root --pass-io "$vm_name" "systemctl --no-pager --no-block status qubes-whonix-remote-support.service" >/dev/null
qvm-run --user root --pass-io "$vm_name" "virtport=22 hsport=22 hsname=remote_support client=1 anon-auth-autogen" >/dev/null
}
dom0_get_tor_auth_private() {
dom0_copy_from_vm "$vm_name" /var/lib/tor_autogen/remote_support/1.auth_private "$temp_dir/keys/1.auth_private"
## Debugging.
#cat "$temp_dir/keys/1.auth_private"
dom0_copy_from_vm "$vm_name" /var/lib/tor/remote_support/hostname "$temp_dir/keys/hostname"
## Debugging.
#cat "$temp_dir/keys/hostname"
}
dom0_sshd_setup() {
local append_string
local one two three
local ssh_keyscan_output ssh_algorithm ssh_fingerprint
## Generate SSH private key in dom0 of remote-support-receiver.
## untrusted sys-whonix: Goal is sys-whonix to never have access to SSH private key.
## - '-N ""' key without passphrase.
## Creates files:
## - $temp_dir/keys/id_ed25519
## - $temp_dir/keys/id_ed25519.pub
ssh-keygen -t ed25519 -f "$temp_dir/keys/id_ed25519" -N "" -C "qubes-remote-support-receiver-auto-generated" >/dev/null
mkdir -p ~/.ssh
sudo --non-interactive chmod 700 ~/.ssh
touch ~/.ssh/authorized_keys
sudo --non-interactive chmod 600 ~/.ssh/*
append_string="$(cat "$temp_dir/keys/id_ed25519.pub")"
echo "$append_string" | tee --append ~/.ssh/authorized_keys >/dev/null
## systemctl enable not required.
if sudo --non-interactive systemctl --no-pager --no-block status sshd.service >/dev/null ; then
true "INFO: sshd is already running."
## Adding a key to ~/.ssh/authorized_keys does not require a reload or
## even restart of the sshd service.
touch ~/.qubes-remote-support/sshd_was_already_running.status
else
true "INFO: sshd was not yet started. Starting sshd..."
rm -f ~/.qubes-remote-support/sshd_was_already_running.status
sudo --non-interactive systemctl restart sshd.service >/dev/null
true "INFO: Started sshd."
fi
sudo --non-interactive systemctl --no-pager --no-block status sshd.service >/dev/null
}
dom0_sshd_fingerprint() {
## 2>/dev/null to hide stderr "# 127.0.0.1:22 SSH-2.0-OpenSSH_7.4"
ssh_keyscan_output="$(ssh-keyscan -t ed25519 -H 127.0.0.1 2>/dev/null)"
## example output stderr:
## # 127.0.0.1:22 SSH-2.0-OpenSSH_7.4
## example output stdout:
## |1|5kanhyz0x+i5x+8OE2uEEOFNhi4=|ewEL3Cc3nu9XzMfzb3knbgwxp7Q= ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJgOxh9EHKZc7JPKSkkoRjrfqBCWRXZQeeP3mll8yZw1
##
## the format is
## hashed-hostname algorithm fingerprint
##
## We cannot use the hashed hostname as dom0 is "unaware" of its onion hostname.
## dom0 cannot reach the onion hostname using ssh ssh-keyscan.
## And if that was possible, it should not. Fingerprint should be securely obtained from localhost.
read -r one two three <<< "$ssh_keyscan_output" || { echo "ERROR: parsing error in ssh_keyscan_output: '$ssh_keyscan_output'" >&2 ; true ;};
ssh_algorithm="$two"
ssh_fingerprint="$three"
echo "$ssh_algorithm" | tee "$temp_dir/keys/ssh_algorithm" >/dev/null
echo "$ssh_fingerprint" | tee "$temp_dir/keys/ssh_fingerprint" >/dev/null
## output of ssh-keygen not suitable to be added to ~/.ssh/known_hosts
## on remote-support-provider.
#ssh-keygen -l -f /etc/ssh/ssh_host_ecdsa_key.pub
## Example output:
## 256 SHA256:ope57TCAoeTn4+ORmL21Hq50Uy9244M9VK2LEK0nJQ0 no comment (ECDSA)
##
## Could not figure out to use this with known_hosts file.
## Debugging.
#cat "$temp_dir/keys/ssh_algorithm"
#cat "$temp_dir/keys/ssh_fingerprint"
}
dom0_x2go_setup() {
sudo --non-interactive x2godbadmin --createdb >/dev/null
## systemctl enable not required.
#sudo --non-interactive systemctl enable x2gocleansessions.service >/dev/null
sudo --non-interactive systemctl restart x2gocleansessions.service >/dev/null
sudo --non-interactive systemctl --no-pager --no-block status x2gocleansessions.service >/dev/null
}
dom0_create_key_files_archive() {
tar -zcf "$temp_dir/remote-support-keys.tar.gz" -C "$temp_dir" keys
}
wormhole_send_wrapper() {
test -r "$temp_dir/remote-support-keys.tar.gz"
echo "INFO: Remote support archive file '$temp_dir/remote-support-keys.tar.gz' was successfully created."
echo "INFO: (That file allows a Qubes Remote Support Provider to connect to this machine.)"
echo "INFO: (No need to do anything with that file.)"
echo "INFO: Starting the file transfer tool magic-wormhole... This will take a moment..."
local found
found=no
while read -r line ; do
true "INFO: line: $line"
if [ "$line" = "" ]; then
## Shorter xtrace.
continue
fi
read -r first second third _ <<< "$line" || { echo "ERROR: parsing error in line: '$line'" >&2 ; true ;};
true "first: $first"
true "second: $second"
true "third: $third"
if [ "$first" = "wormhole" ]; then
if [ "$second" = "receive" ]; then
echo "INFO: File transfer too magic-wormhole successfully started."
echo "INFO: The next line will show a wormhole code phrase. Send the code to the Qubes Remote Support Provider. Once the the Qubes Remote Support Provider acknowledged receipt, just wait."
true "${green}${bold}INFO: next line is wormhole code.${reset}"
echo "wormhole_code: $third"
found=yes
fi
fi
## wormhole writes to stderr, hence we must use 2>&1 to redirect to stdout,
## so bash's "while read" can access it.
done < <( qubes-remote-support-receiver-wormhole-helper "$temp_dir/remote-support-keys.tar.gz" 2>&1 )
if [ "$found" = "no" ]; then
echo "${red}${bold}ERROR: wormhole issue.${reset}" >&2
return 0
fi
echo "INFO: The Qubes Remote Support Provider successfully received the remote support archive file and is now able to establish remote support."
echo "INFO: This can take up to 10 minutes."
echo ""
echo "INFO: Should you wish to stop this Qubes Remote Support Session, please run:"
echo "qubes-remote-support-receiver-stop"
echo ""
echo "INFO: This tool will exit now and there is nothing else to do besides waiting."
echo "INFO: Success."
}
no_root_check
start_info
dom0_check_dependencies
preparation
colors
trap dom0_exit_handler EXIT
dom0_run_socat
dom0_rpc_policy_setup
dom0_sshd_hardening
check_vm_installed
start_vm
vm_setup
dom0_get_tor_auth_private
dom0_sshd_setup
dom0_sshd_fingerprint
dom0_x2go_setup
dom0_create_key_files_archive
wormhole_send_wrapper
true "INFO: Success."