-
Notifications
You must be signed in to change notification settings - Fork 8
/
Copy pathlf-rhel.cfg
496 lines (415 loc) · 17.5 KB
/
lf-rhel.cfg
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
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
# MAIN KICKSTART
# Author: Linuxfabrik GmbH, Zurich, Switzerland
# Contact: info (at) linuxfabrik (dot) ch
# https://www.linuxfabrik.ch/
# License: The Unlicense, see LICENSE file.
# This kickstart file consists of two parts.
# * The main part, which is quite minimal,
# * and a dynamic part (/tmp/dynamic.ks) that gets written during the %pre script.
# The %pre script starts by parsing some options from the kernel cmdline and
# detecting the RHEL version.
# It then fills /tmp/dynamic.ks depending on these variables.
# The order of the commands is relatively compatible with the output of
# https://access.redhat.com/labs/kickstartconfig/
# See the README for more details.
# System language
lang en_US.UTF-8
# Keyboard layouts
keyboard us
# System timezone
timezone Europe/Zurich --utc
# Shutdown after installation
shutdown
# Use text mode install and CDROM installation media
text
cdrom
# This command is required when performing an unattended installation on a system with previously
# initialized disks.
zerombr
# Automatically creates partitions required by your hardware platform, eg /boot/efi or biosboot
reqpart
# Network information
network --hostname=localhost.localdomain
# note: do not set any other network options. dhcp is the default anyway, and setting them here disregards the ip and nameserver settings on the kernel command line
# network --bootproto=dhcp --device=link --activate --onboot=on
# Do not configure the X Window System
skipx
# Disable the Setup Agent on first boot
firstboot --disable
# State of SELinux on the installed system
selinux --enforcing
# Firewalling should be done later on by the admin. Required for the cloud
firewall --disable
# System services
services --disabled="kdump" --enabled="NetworkManager,sshd"
# Disable kdump by default, frees up some memory
%addon com_redhat_kdump --disable
%end
%include /tmp/dynamic.ks
%pre --logfile /tmp/kickstart.install.pre.log --erroronfail
# fedora doesn't have platform-python and rhel8 only has platform-python, so we'll have to
# detect what we have
PYTHON=$(ls /usr/libexec/platform-python /usr/bin/python3 2> /dev/null | head -n1)
cat << EOT > /tmp/pre-script.py
#!$PYTHON
import os
import re
import stat
import sys
def isblockdevice(path):
return os.path.exists(path) and stat.S_ISBLK(os.stat(path).st_mode)
lfkeys = [
'ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDuYw1auj8Lo6l5Ie8H7q419pKNjD3LSDZpFLNI0KehO [email protected]',
'ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDaWaDOEGqhZy1XD3a0IJKPvuB0wz2Yzc7Y8b2E91PPDSfi2wczUjZ9T2f8J7fw46i6O8dUFdsle8HePlVt1f6P+SPr84KIvte4sCXPGjHO7UlP+0biPl1FJbe+LU6akGVgAhc37CTn7h10COim5TpIdmPfn8A1y0Y8G3GNTVELSC4GG6rJLgme++OTkNlenH+L7KSobQE1v+MS4mRjrg+qitgPzBv1VgTWJff4d3vdEtb9zwVdzZzyXcdP5/nWH54iDaZawLsyvPXG2VDQq9bUn4CbZ+/ldrVg+yi8Y5RlSLFlbaX2XKnqf8mC3fpszXrmZ93d/idvCLDQ2ijV1FQzYLNg9nstV6VHCek1+g0u3oQ17CyRRCuxSRf3kDagO/+FMGwIliQTSX8rTx0epYzj4vUKA0nYIHXhwklUTG2PFNgJ1Nfxllqblij/PbFfoJCCp+st7/ewYYiclV6jrAg01/bqAvrdS8PW6JQpTIeuJRZhkrvCxgzDsuYE+EqTHxyBs7Uxu9D8QvgZEYiwuClRE92xPNmuEk/BDpX9s8mcXtamp57AUESRFWPFUZoDFuHRjHz56lXJ0UIfDR7XDZHumdQRt6jh7Sj0YGVN3Es9UIqka9dZRiXLdZ9q/C0IReZKtcKDSIn/xB1Kt2qAIRLpSG3IX8JmONOa1h/Gns1NKw== [email protected]',
'ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIM5DxWzuUlSfdHHE1c4oQ2cbC0TyjXkVCuNKBJvn7TvX [email protected]',
]
lftype='minimal'
lfdisk = None
for guess in ['vda', 'sda', 'nvme0n1']:
if isblockdevice('/dev/{}'.format(guess)):
lfdisk = guess
break
print('Linuxfabrik Kickstart: Set kernel cmdline arguments')
with open('/proc/cmdline', 'r') as file:
cmdline = file.read()
for item in cmdline.split(" "):
if item.split("=")[0] == "lftype":
lftype=item.split("=")[1].strip()
elif item.split("=")[0] == "lfdisk":
lfdisk=item.split("=")[1].strip()
if not lfdisk:
print('Linuxfabrik Kickstart: Lfdisk is not given and no disk to install to could be guessed. Aborting...')
sys.exit(1)
print('Linuxfabrik Kickstart: Lftype: {}, lfdisk: {}'.format(lftype, lfdisk))
print('Linuxfabrik Kickstart: Set bootloader')
# * console=ttyS0,115200n8:
# required for cloud images.
# see https://docs.openstack.org/image-guide/openstack-images.html#ensure-image-writes-boot-log-to-console
# * no_timer_check:
# see https://opendev.org/openstack/diskimage-builder/commit/1ec93f43a8736171cb78382fd6184f03c001771b
# * net.ifnames=0:
# for cloud images, 'eth0' _is_ the predictable device name
# * audit=1 audit_backlog_limit=8192:
# required by CIS
# * vsyscall=none:
# due to https://www.stigviewer.com/stig/red_hat_enterprise_linux_8/2020-11-25/finding/V-230278
bootloader_cis = 'bootloader --location=mbr --boot-drive={} --append="audit=1 audit_backlog_limit=8192 vsyscall=none"'.format(lfdisk)
bootloader_cloud = 'bootloader --location=mbr --boot-drive={} --append="console=tty0 console=ttyS0,115200n8 no_timer_check net.ifnames=0 vsyscall=none"'.format(lfdisk)
bootloader_cloud_cis = 'bootloader --location=mbr --boot-drive={} --append="console=tty0 console=ttyS0,115200n8 no_timer_check net.ifnames=0 audit=1 audit_backlog_limit=8192 vsyscall=none"'.format(lfdisk)
bootloader_minimal = 'bootloader --location=mbr --boot-drive={} --append="vsyscall=none"'.format(lfdisk)
print('Linuxfabrik Kickstart: Set packages')
packages = [
'@core',
'-plymouth', # No need for plymouth. Also means anaconda won't put rhgb/quiet on kernel command line
]
packages_cis = []
packages_cloud = [
'cloud-init',
'cloud-utils-growpart',
'dhcp-client',
'qemu-guest-agent',
'rng-tools',
'tuned',
'-aic94xx-firmware',
'-alsa-firmware',
'-alsa-lib',
'-alsa-tools-firmware',
'-biosdevname',
'-iprutils',
'-ivtv-firmware',
'-iwl100-firmware',
'-iwl1000-firmware',
'-iwl105-firmware',
'-iwl135-firmware',
'-iwl2000-firmware',
'-iwl2030-firmware',
'-iwl3160-firmware',
'-iwl3945-firmware',
'-iwl4965-firmware',
'-iwl5000-firmware',
'-iwl5150-firmware',
'-iwl6000-firmware',
'-iwl6000g2a-firmware',
'-iwl6000g2b-firmware',
'-iwl6050-firmware',
'-iwl7260-firmware',
'-langpacks-*',
'-langpacks-en',
'-libertas-sd8686-firmware',
'-libertas-sd8787-firmware',
'-libertas-usb8388-firmware',
]
packages_cloud_cis = packages_cloud
packages_minimal = []
print('Linuxfabrik Kickstart: Set partitioning schema')
part_cis = [
'logvol / --fstype="xfs" --size=4096 --vgname=rl --name=root',
'logvol /home --fstype="xfs" --size=1024 --vgname=rl --name=home --fsoptions="nodev,nosuid"',
'logvol /tmp --fstype="xfs" --size=1024 --vgname=rl --name=tmp --fsoptions="nodev,noexec,nosuid"',
'logvol /var --fstype="xfs" --size=4096 --vgname=rl --name=var --fsoptions="nodev,nosuid"',
'logvol /var/log --fstype="xfs" --size=2048 --vgname=rl --name=var_log --fsoptions="nodev,noexec,nosuid"',
'logvol /var/log/audit --fstype="xfs" --size=512 --vgname=rl --name=var_log_audit --fsoptions="nodev,noexec,nosuid"',
'logvol /var/tmp --fstype="xfs" --size=1024 --vgname=rl --name=var_tmp --fsoptions="nodev,noexec,nosuid"',
'logvol swap --fstype="swap" --recommended --vgname=rl --name=swap',
]
part_cloud = [
'logvol / --fstype="xfs" --size=1024 --vgname=rl --name=root --grow',
'logvol swap --fstype="swap" --recommended --vgname=rl --name=swap',
]
part_cloud_cis = part_cis
part_minimal = part_cloud
print('Linuxfabrik Kickstart: Define users')
users_cis = [
{
'name': 'linuxfabrik',
'cmd': 'user --name=linuxfabrik --groups=wheel --password=password --plaintext',
'keys': lfkeys,
},
{
'name': 'root',
'cmd': 'rootpw --plaintext --lock password',
'keys': [],
},
]
users_cloud = [
{
'name': 'linuxfabrik',
'cmd': 'user --name=linuxfabrik --groups=wheel --lock',
'keys': [],
},
{
'name': 'root',
'cmd': 'rootpw --plaintext --lock password',
'keys': [],
},
]
users_cloud_cis = users_cloud
users_minimal = users_cis
print('Linuxfabrik Kickstart: Create post scripts')
post_cis = '''
# allow password-less sudo
cat > /etc/sudoers.d/linuxfabrik << EOF
%linuxfabrik ALL=(ALL) NOPASSWD: ALL
EOF
'''
post_cloud = '''
# setup systemd to boot to the right runlevel
echo "Setting default runlevel to multiuser text mode"
rm -f /etc/systemd/system/default.target
ln -s /lib/systemd/system/multi-user.target /etc/systemd/system/default.target
# this is installed by default but we don't need it in virt
# Commenting out the following for =1234504
# rpm works just fine for removing this, no idea why yum can't cope
echo "Removing linux-firmware package"
rpm --erase linux-firmware
# Remove firewalld; was supposed to be optional in F18+, but is pulled in
# in install/image building.
echo "Removing firewalld"
# FIXME! clean_requirements_on_remove is the default with yum, but may
# not work when package was installed by Anaconda instead of command line.
# Also -- check if this is still even needed with new anaconda -- disabled
# firewall should _not_ pull in this package.
# yum -C -y remove "firewalld*" --setopt="clean_requirements_on_remove=1"
yum --cacheonly -y erase "firewalld*"
# Another one needed at install time but not after that, and it pulls
# in some unneeded deps (like, newt and slang)
echo "Removing authconfig"
yum --cacheonly -y erase authconfig
echo "Removing avahi"
yum --cacheonly -y remove avahi\\*
echo "Installing cloud-init cloud-utils-growpart rng-tools tuned"
# these tools always fail to install in the %packages section, so try this here
yum -y install cloud-init cloud-utils-growpart rng-tools tuned
# Since the scriptlets from yum install are running in chroot, they can't do a daemon-reload
# ("Running in chroot, ignoring command 'daemon-reload'"), so a "systemctl enable --now" would
# fail. "systemctl enable" is enough here.
systemctl enable cloud-init
systemctl enable cloud-init-local
systemctl enable cloud-config
systemctl enable cloud-final
systemctl enable rngd
echo "Getty fixes"
# although we want console output going to the serial console, we don't
# actually have the opportunity to login there. FIX.
# we don't really need to auto-spawn _any_ gettys.
sed --in-place 's/^#NAutoVTs=.*/NAutoVTs=0/' /etc/systemd/logind.conf
echo "Network fixes"
# initscripts don't like this file to be missing.
# and https://bugzilla.redhat.com/show_bug.cgi?id=1204612
cat > /etc/sysconfig/network << EOF
NETWORKING=yes
NOZEROCONF=yes
DEVTIMEOUT=10
EOF
echo "Truncate /etc/resolv.conf"
# Anaconda is writing an /etc/resolv.conf from the install environment.
# The system should start out with an empty file, otherwise cloud-init
# will try to use this information and may error:
# https://bugs.launchpad.net/cloud-init/+bug/1670052
truncate --size=0 /etc/resolv.conf
echo "Disable predictable network interface names"
# For cloud images, 'eth0' _is_ the predictable device name, since
# we don't want to be tied to specific virtual (!) hardware
rm -f /etc/udev/rules.d/70*
ln -s /dev/null /etc/udev/rules.d/80-net-name-slot.rules
echo 'Set generic localhost names (/etc/hosts)'
cat > /etc/hosts << EOF
127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4
::1 localhost localhost.localdomain localhost6 localhost6.localdomain6
EOF
# Because memory is scarce resource in most cloud/virt environments,
# and because this impedes forensics, we are differing from the Fedora
# default of having /tmp on tmpfs.
# However, there is no need to disable the mount, as we are already overwriting the options
# for /tmp above.
# Masking the mount now would lead to a missing /tmp.
# echo "Disabling tmpfs for /tmp."
# systemctl mask tmp.mount
echo 'Set tuned profile to virtual-guest'
echo 'virtual-guest' > /etc/tuned/active_profile
echo 'Make sure firstboot does not start'
echo 'RUN_FIRSTBOOT=NO' > /etc/sysconfig/firstboot
echo 'Adjust sudoers for the linuxfabrik user'
cat > /etc/sudoers.d/linuxfabrik << EOF
%linuxfabrik ALL=(ALL) NOPASSWD: ALL
EOF
sed --in-place 's/name: cloud-user/name: linuxfabrik/g' /etc/cloud/cloud.cfg
echo 'Cleaning up old yum repodata'
yum clean all
echo 'Increase DHCP client retry/timeouts'
# change dhcp client retry/timeouts to resolve =6866
cat >> /etc/dhcp/dhclient.conf << EOF
timeout 300;
retry 60;
EOF
echo 'Adjust console output in /etc/default/grub and regenerate grub config'
# If init script messages need to be seen on the serial console as well, it should be made
# the primary by swapping the order of the console parameters:
sed --in-place 's/console=ttyS0,115200n8 console=tty0/console=tty0 console=ttyS0,115200n8/' /etc/default/grub
if [ -d /sys/firmware/efi/ ]; then
grub_config_file=$(find /boot/efi -name grub.cfg)
else
grub_config_file=$(find /boot/ -name grub.cfg)
fi
if [ -z "$grub_config_file" ]; then
echo 'Could not find a grub.cfg in /boot. Skipping grub2-mkconfig'
else
grub2-mkconfig --output=$grub_config_file
fi
echo "Removing random-seed so it's not the same in every image"
rm -f /var/lib/systemd/random-seed
echo 'Remove /etc/machine-id'
# https://git.resf.org/sig_core/kickstarts/src/branch/r8/Rocky-8-GenericCloud-LVM.ks
cat /dev/null > /etc/machine-id
echo 'Remove various other log / cache files'
rm -f /root/.wget-hsts
rm -rf /root/anaconda-ks.cfg
rm -rf /root/install.log
rm -rf /root/install.log.syslog
rm -rf /var/lib/yum/*
rm -rf /var/log/anaconda*
rm -rf /var/log/yum.log
rm -rf /var/tmp/*
find /var/log -type f -exec truncate --size=0 {} \\;
export HISTSIZE=0
cat /dev/null > ~/.bash_history
history -c
echo 'Fixing SELinux contexts'
touch /var/log/cron
touch /var/log/boot.log
mkdir -p /var/cache/yum
# ignore return code because UEFI systems with vfat filesystems
# that don't support selinux will give us errors like
# "Could not set context for /boot/efi/EFI/BOOT/BOOTX64.EFI: Operation not supported"
/usr/sbin/fixfiles -R -a restore || true
'''
post_cloud_cis = post_cloud
post_minimal = post_cis
print('Linuxfabrik Kickstart: Act on cmdargs & discovery for {}'.format(lftype))
if lftype == 'cloud':
bootloader = bootloader_cloud
packages += packages_cloud
part = part_cloud
post = post_cloud
users = users_cloud
elif lftype == 'cloud-cis':
bootloader = bootloader_cloud_cis
packages += packages_cloud_cis
part = part_cloud_cis
post = post_cloud_cis
users = users_cloud_cis
elif lftype == 'cis':
bootloader = bootloader_cis
packages += packages_cis
part = part_cis
post = post_cis
users = users_cis
elif lftype == 'minimal':
bootloader = bootloader_minimal
packages += packages_minimal
part = part_minimal
post = post_minimal
users = users_minimal
# version specifics
with open('/etc/redhat-release') as file:
rhel_version = int(re.findall('[0-9]{1,2}', file.read())[0])
print('Linuxfabrik Kickstart: Detected OS version {}'.format(rhel_version))
if rhel_version == 7:
try:
packages.remove('dhcp-client') # does not exist on RHEL7
except ValueError:
pass # means it is already removed
print('Linuxfabrik Kickstart: Write dynamic.ks')
dynamic = []
keypost = []
dynamic.append('# System users')
for user in users:
dynamic.append(user['cmd'])
for key in user['keys']:
if rhel_version == 7: # sshkey only exists for RHEL8+. instead manually add the keys
if user['name'] == 'root':
keypost.append('mkdir -m0700 /root/.ssh')
keypost.append('echo "{}" >> /root/.ssh/authorized_keys'.format(key))
keypost.append('restorecon -F /root/.ssh/authorized_keys')
else:
keypost.append('mkdir -m0700 /home/{}/.ssh'.format(user['name']))
keypost.append('echo "{}" >> "/home/{}/.ssh/authorized_keys"'.format(key, user['name']))
keypost.append('chown -R {}: /home/{}/.ssh'.format(user['name'], user['name']))
keypost.append('restorecon -F "/home/{}/.ssh/authorized_keys"'.format(user['name']))
else:
dynamic.append('sshkey --user={} "{}"'.format(user['name'], key))
dynamic.append('# Only touch $lfdisk. This setting is also respected by zerombr (confirmed on RHEL8 and 9)')
dynamic.append('ignoredisk --only-use={}'.format(lfdisk))
dynamic.append('# System bootloader configuration')
dynamic.append(bootloader)
dynamic.append('# Do not remove any partitions, but initializes a disk (or disks) by creating a default disk label')
dynamic.append('clearpart --all --drives={} --initlabel --disklabel gpt'.format(lfdisk))
dynamic.append('# Partitioning')
dynamic.append('volgroup rl --pesize=4096 pv.0')
dynamic.append('part pv.0 --fstype=lvmpv --ondisk={} --size=1 --grow'.format(lfdisk))
dynamic.append('part /boot --fstype=xfs --ondisk={} --size=1024 --asprimary'.format(lfdisk))
dynamic.extend(part)
dynamic.append('%packages')
dynamic.extend(packages)
dynamic.append('%end')
dynamic.append('%post --erroronfail')
dynamic.append(post)
dynamic.append('%end\n')
# actually write to file
with open('/tmp/dynamic.ks', 'w') as file:
file.write('\n'.join(dynamic))
if rhel_version == 7:
with open('/usr/share/anaconda/post-scripts/70-install-ssh-keys.ks', 'a') as file:
file.write('%post\n')
file.write('\n'.join(keypost))
file.write('%end\n')
print('Linuxfabrik Kickstart: Wrote dynamic kickstart to /tmp/dynamic.ks')
EOT
$PYTHON /tmp/pre-script.py
%end
%post --nochroot
root_mount=$(awk '/rl-root/{print $2}' /proc/mounts)
echo "Copying /tmp/dynamic.ks to $root_mount/root/"
cp /tmp/dynamic.ks $root_mount/root/
[ -f /usr/share/anaconda/post-scripts/70-install-ssh-keys.ks ] && cp /usr/share/anaconda/post-scripts/70-install-ssh-keys.ks $root_mount/root/
%end