aboutgitcodebugslistschat
path: root/doc/demo.sh
blob: ed71ffbe851a42a5499839b7d0a965119424c9ba (plain) (blame)
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
#!/bin/sh -e
#
# SPDX-License-Identifier: AGPL-3.0-or-later
#
# PASST - Plug A Simple Socket Transport
#
# demo.sh - Set up namespace with pasta, start qemu and passt, step by step
#
# Copyright (c) 2020-2022 Red Hat GmbH
# Author: Stefano Brivio <sbrivio@redhat.com>

# mbuto_profile() - Profile for https://mbuto.sh/, sourced, return after setting
mbuto_profile() {
	PROGS="${PROGS:-ash,dash,bash ip mount ls ln chmod insmod mkdir sleep
	       lsmod modprobe find grep mknod mv rm umount iperf3 dhclient cat
	       hostname chown socat dd strace ping killall sysctl wget,curl}"

	KMODS="${KMODS:- virtio_net virtio_pci}"

	LINKS="${LINKS:-
		 ash,dash,bash		/init
		 ash,dash,bash		/bin/sh}"

	DIRS="${DIRS} /tmp /sbin /var/log /var/run /var/lib"

	# shellcheck disable=SC2016
	FIXUP="${FIXUP}"'
		cat > /sbin/dhclient-script << EOF
#!/bin/sh

[ -n "\${new_interface_mtu}" ]       && ip link set dev \${interface} mtu \${new_interface_mtu}

[ -n "\${new_ip_address}" ]          && ip addr add \${new_ip_address}/\${new_subnet_mask} dev \${interface}
[ -n "\${new_routers}" ]             && for r in \${new_routers}; do ip route add default via \${r} dev \${interface}; done
[ -n "\${new_domain_name_servers}" ] && for d in \${new_domain_name_servers}; do echo "nameserver \${d}" >> /etc/resolv.conf; done
[ -n "\${new_domain_name}" ]         && echo "search \${new_domain_name}" >> /etc/resolf.conf
[ -n "\${new_domain_search}" ]       && (printf "search"; for d in \${new_domain_search}; do printf " %s" "\${d}"; done; printf "\n") >> /etc/resolv.conf
[ -n "\${new_ip6_address}" ]         && ip addr add \${new_ip6_address}/\${new_ip6_prefixlen} dev \${interface}
[ -n "\${new_dhcp6_name_servers}" ]  && for d in \${new_dhcp6_name_servers}; do echo "nameserver \${d}%\${interface}" >> /etc/resolv.conf; done
[ -n "\${new_dhcp6_domain_search}" ] && (printf "search"; for d in \${new_dhcp6_domain_search}; do printf " %s" "\${d}"; done; printf "\n") >> /etc/resolv.conf
[ -n "\${new_host_name}" ]           && hostname "\${new_host_name}"
exit 0
EOF

		chmod 755 /sbin/dhclient-script

		mkdir -p /etc/dhcp
		echo "timeout 3;" > /etc/dhcp/dhclient.conf

		ln -s /sbin /usr/sbin
		:> /etc/fstab

		echo
		echo "The guest is up and running. Networking is not configured yet:"
		echo
		echo "$ ip address show"
		echo
		ip address show
		echo
		echo "...the next step will take care of that."
		read x

		echo "$ ip link set dev eth0 up"
		ip link set dev eth0 up
		sleep 3
		echo "$ dhclient -4 -1 -sf /sbin/dhclient-script"
		dhclient -4 -1 -sf /sbin/dhclient-script
		sleep 2
		echo "$ dhclient -6 -1 -sf /sbin/dhclient-script"
		dhclient -6 -1 -sf /sbin/dhclient-script
		sleep 2
		echo
		echo "$ ip address show"
		ip address show
		echo
		echo "$ ip route show"
		ip route show
		echo
		echo "...done."
		read x

		echo "Checking connectivity..."
		echo
		echo "$ wget --no-check-certificate https://passt.top/ || curl -k https://passt.top/"
		wget --no-check-certificate https://passt.top/ || curl -k https://passt.top/
		echo "...done."
		read x

		echo "An interactive shell will start now. When you are done,"
		echo "use ^C to terminate the guest and exit the demo."
		echo

		sh +m
'
}

[ "${0##*/}" = "mbuto" ] && mbuto_profile && return 0

# cmd() - Show command being executed, then run it
# $@: Command and arguments
cmd() {
	echo "$" "$@"
	"$@"
}

# next() - Go to next step once a key is pressed, sets $KEY
next() {
	KEY="$(dd ibs=1 count=1 2>/dev/null)"
	echo
}

# cleanup() - Terminate pasta and passt, clean up, restore TTY settings
cleanup() {
	[ -f "${DEMO_DIR}/pasta.pid" ] && kill "$(cat "${DEMO_DIR}/pasta.pid")"
	[ -f "${DEMO_DIR}/passt.pid" ] && kill "$(cat "${DEMO_DIR}/passt.pid")"
	rm -rf "${DEMO_DIR}" 2>/dev/null
	[ -n "${STTY_BACKUP}" ] && stty "${STTY_BACKUP}"
}

# start_pasta_delayed() - Start pasta once $DEMO_DIR/pasta.wait is gone
start_pasta_delayed() {
	trap '' EXIT
	while [ -d "${DEMO_DIR}/pasta.wait" ]; do sleep 1; done
	cmd pasta --config-net -P "${DEMO_DIR}/pasta.pid" \
		"$(cat "${DEMO_DIR}/shell.pid")"
	echo
	echo "...pasta is running."
	exit 0
}

# start_mbuto_delayed() - Run mbuto once, and if, $DEMO_DIR/mbuto.wait is gone
start_mbuto_delayed() {
	trap '' EXIT
	while [ -d "${DEMO_DIR}/mbuto.wait" ]; do sleep 1; done
	cmd git -C "${DEMO_DIR}" clone git://mbuto.sh/mbuto
	echo
	cmd "${DEMO_DIR}/mbuto/mbuto" \
		-p "$(realpath "${0}")" -f "${DEMO_DIR}/demo.img"

	mkdir "${DEMO_DIR}/mbuto.done"
	exit 0
}

# into_ns() - Entry point and demo script to run inside new namespace
into_ns() {
	echo "We're in the new namespace now."
	next

	echo "Networking is not configured yet:"
	echo
	cmd ip link show
	echo
	cmd ip address show
	next

	echo "Let's run pasta(1) to configure networking and connect this"
	echo "namespace. Note that we'll run pasta(1) from outside this"
	echo "namespace, because it needs to implement the connection between"
	echo "this namespace and the initial (\"outer\") one."
	next

	echo "$$" > "${DEMO_DIR}/shell.pid"
	rmdir "${DEMO_DIR}/pasta.wait"
	next

	echo "Back to the new namespace, networking is configured:"
	echo
	cmd ip link show
	echo
	cmd ip address show
	next

	echo "and we can now start passt(1), to connect this namespace to a"
	echo "virtual machine. If you want to start a shell in this namespace,"
	echo "press 's' now. Exiting the shell will resume the script."
	next
	[ "${KEY}" = "s" ] && ${SHELL}

	cmd passt -P "${DEMO_DIR}/passt.pid"
	echo
	echo "...passt is running."
	next

	__arch="$(uname -m)"
	case ${__arch} in
	x86_64)
		__arch_supported=1
		__qemu_arch="qemu-system-x86_64 -M pc,accel=kvm:tcg"
		;;
	*)
		__arch_supported=0
		;;
	esac

	if [ "${__arch_supported}" -eq 1 ]; then
		echo "We're ready to start a virtual machine now. This script"
		echo "can download and use mbuto (https://mbuto.sh/) to build a"
		echo "basic initramfs image. Otherwise, press 's' to skip this"
		echo "step, and start an existing virtual machine yourself."
		echo "You'll need to use the qrap(1) wrapper, with qemu options"
		echo "as reported above."

		next
	else
		echo "This script doesn't know, yet, how to run a virtual"
		echo "machine on your architecture (${__arch}). Please start an"
		echo "existing virtual machine yourself, using the qrap(1)"
		echo "wrapper, with qemu options as reported above."
		echo
	fi

	if [ "${__arch_supported}" -eq 0 ] || [ "${KEY}" = "s" ]; then
		echo "Start a virtual machine now. Pressing any key here will"
		echo "terminate passt and pasta, and clean up."
		next

		exit 0
	fi

	rmdir "${DEMO_DIR}/mbuto.wait"
	while [ ! -d "${DEMO_DIR}/mbuto.done" ]; do sleep 1; done
	echo "The guest image is ready. The next step will start the guest."
	echo "Use ^C to terminate it."
	next

	cmd qrap 5 qemu-system-x86_64 -M pc,accel=kvm:tcg		    \
		-smp "$(nproc)" -m 1024					    \
		-nographic -serial stdio -nodefaults -no-reboot -vga none   \
		-initrd "${DEMO_DIR}/demo.img"				    \
		-kernel "/boot/vmlinuz-$(uname -r)" -append "console=ttyS0" \
		-net socket,fd=5 -net nic,model=virtio || :
}

STTY_BACKUP="$(stty -g)"
stty -icanon

trap cleanup EXIT INT
[ "${1}" = "into_ns" ] && into_ns && exit 0

DEMO_DIR="$(mktemp -d)"
mkdir "${DEMO_DIR}/pasta.wait"
mkdir "${DEMO_DIR}/mbuto.wait"

echo "This script sets up a network and user namespace using pasta(1), then"
echo "starts a virtual machine in it, connected via passt(1), pausing at every"
echo "step. Press any key to go to the next step."
next

echo "Let's create the network and user namespace, first. This could be done"
echo "with pasta(1) itself (just issue \`pasta\`), but for the sake of this"
echo "script we'll create it first with unshare(1), and run the next steps"
echo "of this script from there."
next

start_pasta_delayed &
start_mbuto_delayed &
DEMO_DIR="${DEMO_DIR}" cmd unshare -rUn "${0}" into_ns

exit 0