Skip to content

Commit 6323b81

Browse files
committed
tests: add test for password-less emergency console
It shouldn't prompt for the password, as the root account is locked. Related to QubesOS/qubes-core-agent-linux#526
1 parent bbfea7a commit 6323b81

File tree

1 file changed

+122
-0
lines changed

1 file changed

+122
-0
lines changed

qubes/tests/integ/misc.py

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,128 @@ def test_010_os_metadata(self):
5454
self.assertEqual(tpl.features.get("os-distribution"), "kali")
5555
self.assertEqual(tpl.features.get("os-distribution-like"), "debian")
5656

57+
def test_110_rescue_console(self):
58+
self.loop.run_until_complete(self._test_110_rescue_console())
59+
60+
async def _test_110_rescue_console(self):
61+
self.testvm = self.app.add_new_vm(
62+
"AppVM", label="red", name=self.make_vm_name("vm")
63+
)
64+
await self.testvm.create_on_disk()
65+
self.testvm.kernelopts = "emergency"
66+
# avoid qrexec timeout
67+
self.testvm.features["qrexec"] = ""
68+
self.app.save()
69+
await self.testvm.start()
70+
# call admin.vm.Console via qrexec-client so it sets all the variables
71+
console_proc = await asyncio.create_subprocess_exec(
72+
"qrexec-client",
73+
"-d",
74+
"dom0",
75+
f"DEFAULT:QUBESRPC admin.vm.Console dom0 name {self.testvm.name}",
76+
stdin=subprocess.PIPE,
77+
stdout=subprocess.PIPE,
78+
)
79+
try:
80+
await asyncio.wait_for(
81+
self._interact_emergency_console(console_proc), 120
82+
)
83+
finally:
84+
with contextlib.suppress(ProcessLookupError):
85+
console_proc.terminate()
86+
await console_proc.communicate()
87+
88+
async def _interact_emergency_console(
89+
self, console_proc: asyncio.subprocess.Process
90+
):
91+
emergency_mode_found = False
92+
whoami_typed = False
93+
while True:
94+
try:
95+
line = await asyncio.wait_for(
96+
console_proc.stdout.readline(), 30
97+
)
98+
except TimeoutError:
99+
break
100+
if b"emergency mode" in line:
101+
emergency_mode_found = True
102+
if emergency_mode_found and b"Press Enter" in line:
103+
console_proc.stdin.write(b"\n")
104+
await console_proc.stdin.drain()
105+
# shell prompt doesn't include newline, so the top loop won't
106+
# progress on it
107+
while True:
108+
try:
109+
line2 = await asyncio.wait_for(
110+
console_proc.stdout.read(128), 5
111+
)
112+
except TimeoutError:
113+
break
114+
if b"bash" in line2 or b"root#" in line2:
115+
break
116+
console_proc.stdin.write(b"echo $USER\n")
117+
await console_proc.stdin.drain()
118+
whoami_typed = True
119+
if whoami_typed and b"root" in line:
120+
return
121+
if whoami_typed:
122+
self.fail("Calling whoami failed, but emergency console started")
123+
if emergency_mode_found:
124+
self.fail("Emergency mode started, but didn't got shell")
125+
self.fail("Emergency mode not found")
126+
127+
def test_111_rescue_console_initrd(self):
128+
if "minimal" in self.template:
129+
self.skipTest(
130+
"Test not relevant for minimal template - booting "
131+
"in-vm kernel not supported"
132+
)
133+
self.loop.run_until_complete(self._test_111_rescue_console_initrd())
134+
135+
async def _test_111_rescue_console_initrd(self):
136+
self.testvm = self.app.add_new_vm(
137+
qubes.vm.standalonevm.StandaloneVM,
138+
name=self.make_vm_name("vm"),
139+
label="red",
140+
)
141+
self.testvm.kernel = None
142+
self.testvm.features.update(self.app.default_template.features)
143+
await self.testvm.clone_disk_files(self.app.default_template)
144+
self.app.save()
145+
146+
await self.testvm.start()
147+
await self.testvm.run_for_stdio(
148+
"echo 'GRUB_CMDLINE_LINUX=\"$GRUB_CMDLINE_LINUX rd.emergency\"' >> "
149+
"/etc/default/grub",
150+
user="root",
151+
)
152+
await self.testvm.run_for_stdio(
153+
"update-grub2 || grub2-mkconfig -o /boot/grub2/grub.cfg",
154+
user="root",
155+
)
156+
await self.testvm.shutdown(wait=True)
157+
158+
# avoid qrexec timeout
159+
self.testvm.features["qrexec"] = ""
160+
await self.testvm.start()
161+
# call admin.vm.Console via qrexec-client so it sets all the variables
162+
console_proc = await asyncio.create_subprocess_exec(
163+
"qrexec-client",
164+
"-d",
165+
"dom0",
166+
f"DEFAULT:QUBESRPC admin.vm.Console dom0 name {self.testvm.name}",
167+
stdin=subprocess.PIPE,
168+
stdout=subprocess.PIPE,
169+
)
170+
try:
171+
await asyncio.wait_for(
172+
self._interact_emergency_console(console_proc), 60
173+
)
174+
finally:
175+
with contextlib.suppress(ProcessLookupError):
176+
console_proc.terminate()
177+
await console_proc.communicate()
178+
57179
@unittest.skipUnless(
58180
spawn.find_executable("xdotool"), "xdotool not installed"
59181
)

0 commit comments

Comments
 (0)