-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathubuntu-24-minimal.py
executable file
·141 lines (127 loc) · 6.2 KB
/
ubuntu-24-minimal.py
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
#!/usr/bin/python3
import argparse
import pathlib
import subprocess
import tempfile
__author__ = "Trent W. Buck"
__copyright__ = "Copyright © 2020 Trent W. Buck"
__license__ = "expat"
__doc__ = """ build the simplest Debian Live image that can boot
This uses mmdebstrap to do the heavy lifting;
it can run entirely without root privileges.
It emits a USB key disk image that contains a bootable EFI ESP,
which in turn includes a UKI (kernel/ramdisk/cmdline) and filesystem.squashfs.
NOTE: this is the simplest config possible.
It lacks CRITICAL SECURITY AND DATA LOSS packages, such as amd64-microcode and smartd.
NOTE: This makes a "unified kernel image" (there is NO bootloader).
The kernel command line is hard-coded into EFI/BOOT/BOOTX64.EFI.
You cannot change it at boot time (e.g. to add "console=ttyS0").
At time of writing, the host system needs:
apt install mmdebstrap apt-cacher-ng parted mtools qemu-kvm
"""
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument('output_file', nargs='?', default=pathlib.Path('filesystem.img'), type=pathlib.Path)
parser.add_argument('--boot-test', action='store_true')
args = parser.parse_args()
filesystem_img_size = '1G' # big enough to include filesystem.squashfs + about 64M of bootloader, kernel, and ramdisk.
esp_offset = 1024 * 1024 # 1MiB
esp_label = 'UEFI-ESP' # max 8 bytes for FAT32
sources_str = """
Types: deb
URIs: http://archive.ubuntu.com/ubuntu
Suites: noble noble-backports noble-security noble-updates
Components: main restricted universe multiverse
Signed-By:
-----BEGIN PGP PUBLIC KEY BLOCK-----
.
mQINBFufwdoBEADv/Gxytx/LcSXYuM0MwKojbBye81s0G1nEx+lz6VAUpIUZnbkq
dXBHC+dwrGS/CeeLuAjPRLU8AoxE/jjvZVp8xFGEWHYdklqXGZ/gJfP5d3fIUBtZ
HZEJl8B8m9pMHf/AQQdsC+YzizSG5t5Mhnotw044LXtdEEkx2t6Jz0OGrh+5Ioxq
X7pZiq6Cv19BohaUioKMdp7ES6RYfN7ol6HSLFlrMXtVfh/ijpN9j3ZhVGVeRC8k
KHQsJ5PkIbmvxBiUh7SJmfZUx0IQhNMaDHXfdZAGNtnhzzNReb1FqNLSVkrS/Pns
AQzMhG1BDm2VOSF64jebKXffFqM5LXRQTeqTLsjUbbrqR6s/GCO8UF7jfUj6I7ta
LygmsHO/JD4jpKRC0gbpUBfaiJyLvuepx3kWoqL3sN0LhlMI80+fA7GTvoOx4tpq
VlzlE6TajYu+jfW3QpOFS5ewEMdL26hzxsZg/geZvTbArcP+OsJKRmhv4kNo6Ayd
yHQ/3ZV/f3X9mT3/SPLbJaumkgp3Yzd6t5PeBu+ZQk/mN5WNNuaihNEV7llb1Zhv
Y0Fxu9BVd/BNl0rzuxp3rIinB2TX2SCg7wE5xXkwXuQ/2eTDE0v0HlGntkuZjGow
DZkxHZQSxZVOzdZCRVaX/WEFLpKa2AQpw5RJrQ4oZ/OfifXyJzP27o03wQARAQAB
tEJVYnVudHUgQXJjaGl2ZSBBdXRvbWF0aWMgU2lnbmluZyBLZXkgKDIwMTgpIDxm
dHBtYXN0ZXJAdWJ1bnR1LmNvbT6JAjgEEwEKACIFAlufwdoCGwMGCwkIBwMCBhUI
AgkKCwQWAgMBAh4BAheAAAoJEIcZINGZG8k8LHMQAKS2cnxz/5WaoCOWArf5g6UH
beOCgc5DBm0hCuFDZWWv427aGei3CPuLw0DGLCXZdyc5dqE8mvjMlOmmAKKlj1uG
g3TYCbQWjWPeMnBPZbkFgkZoXJ7/6CB7bWRht1sHzpt1LTZ+SYDwOwJ68QRp7DRa
Zl9Y6QiUbeuhq2DUcTofVbBxbhrckN4ZteLvm+/nG9m/ciopc66LwRdkxqfJ32Cy
q+1TS5VaIJDG7DWziG+Kbu6qCDM4QNlg3LH7p14CrRxAbc4lvohRgsV4eQqsIcdF
kuVY5HPPj2K8TqpY6STe8Gh0aprG1RV8ZKay3KSMpnyV1fAKn4fM9byiLzQAovC0
LZ9MMMsrAS/45AvC3IEKSShjLFn1X1dRCiO6/7jmZEoZtAp53hkf8SMBsi78hVNr
BumZwfIdBA1v22+LY4xQK8q4XCoRcA9G+pvzU9YVW7cRnDZZGl0uwOw7z9PkQBF5
KFKjWDz4fCk+K6+YtGpovGKekGBb8I7EA6UpvPgqA/QdI0t1IBP0N06RQcs1fUaA
QEtz6DGy5zkRhR4pGSZn+dFET7PdAjEK84y7BdY4t+U1jcSIvBj0F2B7LwRL7xGp
SpIKi/ekAXLs117bvFHaCvmUYN7JVp1GMmVFxhIdx6CFm3fxG8QjNb5tere/YqK+
uOgcXny1UlwtCUzlrSaP
=9AdM
-----END PGP PUBLIC KEY BLOCK-----
"""
network_config_str = """
# live-config doesn't support systemd-networkd yet (only ifupdown), AND
# Ubuntu 20.04 doesn't support ifupdown anymore.
# As a minimal workaround, hard-code a minimal .network.
[Match]
Type=ether
Name=en*
[Network]
DHCP=yes
"""
with tempfile.TemporaryDirectory(prefix='debian-live-bullseye-amd64-minimal.') as td_str:
td = pathlib.Path(td_str)
(td / 'live').mkdir()
(td / 'EFI/BOOT').mkdir(parents=True)
sources_path = td / 'ubuntu.sources'
sources_path.write_text(sources_str)
network_config_path = td / '50-FIXME.network'
network_config_path.write_text(network_config_str)
subprocess.check_call(
['mmdebstrap', 'noble', 'live/filesystem.squashfs',
sources_path, # mmdebstrap default sources were being weird (FIXME: remove)
'--aptopt=DPkg::Inhibit-Shutdown 0;', # https://bugs.debian.org/1061094
'--mode=unshare',
'--variant=apt',
'--aptopt=Acquire::http::Proxy "http://localhost:3142"',
'--aptopt=Acquire::https::Proxy "DIRECT"',
'--dpkgopt=force-unsafe-io',
'--include=linux-image-generic init initramfs-tools live-boot netbase',
'--include=dbus-broker', # https://bugs.debian.org/814758
'--include=live-config iproute2 keyboard-configuration locales sudo user-setup',
f'--customize-hook=copy-in "{network_config_path.name}" /etc/systemd/network/',
'--customize-hook=systemctl --root="$1" enable systemd-networkd',
# FIXME: once the host OS runs Debian 13, move this to the host.
'--include=systemd-boot systemd-ukify',
'--customize-hook=chroot $1 /lib/systemd/ukify build --linux=/boot/vmlinuz --initrd=/boot/initrd.img --cmdline=boot=live',
'--customize-hook=download /vmlinuz.unsigned.efi EFI/BOOT/BOOTX64.EFI'],
cwd=td)
# Create a raw disk image with GPT and one FAT32 EFI ESP partition.
# Copy EFI/BOOT/BOOTX64.EFI and live/filesystem.squashfs into the ESP.
# NOTE: We use gross legacy tools "mtools" because
# it doesn't need root (unlike kpartx/losetup/mount) and
# it is lightweight (unlike guestfish).
subprocess.check_call(
['truncate', args.output_file,
'--size', filesystem_img_size])
subprocess.check_call(
['parted', '--script', '--align=optimal', args.output_file,
'mklabel gpt',
f'mkpart {esp_label} {esp_offset}b 100%',
'set 1 esp on'])
subprocess.check_call( # ≈ mkfs.vfat
['mformat', '-i', f'{args.output_file}@@{esp_offset}',
'-F', '-v', esp_label])
subprocess.check_call( # ≈ mount, cp, umount
['mcopy', '-i', f'{args.output_file.resolve()}@@{esp_offset}',
'-vspm',
'EFI', 'live', # source dirs
'::'], # destdir is root of fs
cwd=td)
# NOTE: this invocation is concise, NOT efficient!
if args.boot_test:
subprocess.check_call([
'kvm', '-m', '1G', '-bios', 'OVMF.fd', '-hda', args.output_file])