Kickstarting RHEL7 Net-Installs
September 12, 2016Nobody ever said it’d be easy, but then again nobody ever said it’d be this hard.
The theory is simple enough, right? Put an ISO in your server, edit the Linux command-line, hit enter, and fifteen to thirty minutes later, you have a server running, configured just so.™ In reality, getting Kickstarts to actually work and to do what you need is pretty hard.
One of the most difficult parts of Kickstart is in testing your Kickstarts. Sure, there’s ksvalidate, but that only validates that your syntax is right. Fat-fingered a package name? It could break the entire install, and as I learned firsthand, it can easily waste your entire day. Those ten minutes waiting for the RAM to initialize, the RAID firmware to load, entering the BIOS and selecting your boot option, then finally carefully typing in your command-line… well, they add up real fast.
Let’s make testing these things easier, and then we’ll provide a fully functional net-install Kickstart.
Testing Kickstarts Using Packer
If you haven’t used Packer, now’s a great time to start. Packer is a tool that can be used to build AMIs, VirtualBox images, and much more. We’ll use it to make a VirtualBox image, just to prove to ourselves that our Kickstart actually works.
packer-centos7.json:
{
"variables": {
"output_directory": "output-virtualbox-iso",
"gui_scale_factor": "1",
"boot_wait": "5s",
"headless": "false"
},
"builders": [
{
"type": "virtualbox-iso",
"guest_os_type": "RedHat_64",
"http_directory": "srv",
"boot_command": [
"<tab> linux ks=http://{{ .HTTPIP }}:{{ .HTTPPort }}/kickstart.ks<enter><wait>"
],
"boot_wait": "{{ user `boot_wait` }}",
"disk_size": 32000,
"output_directory": "{{ user `output_directory` }}",
"iso_url": "https://mirrors.kernel.org/centos/7.2.1511/isos/x86_64/CentOS-7-x86_64-NetInstall-1511.iso",
"iso_checksum": "9ed9ffb5d89ab8cca834afce354daa70a21dcb410f58287d6316259ff89758f5",
"iso_checksum_type": "sha256",
"guest_additions_path": "/tmp/VBoxGuestAdditions.iso",
"headless": "{{ user `headless` }}",
"vboxmanage": [
["modifyvm", "{{.Name}}", "--memory", "2048"],
["setextradata", "{{.Name}}", "GUI/ScaleFactor", "{{ user `gui_scale_factor` }}"]
],
"shutdown_command": "echo 'root' | sudo -S poweroff",
"ssh_username": "root",
"ssh_password": "lol",
"ssh_wait_timeout": "20m",
"ssh_pty": "true"
}
]
}
To summarize, we create a set of variables for configuring some things dependent on your host operating system. For
instance, the gui_scale_factor
is a huge help on high DPI displays, and it can be set to 2
by adding it to the
build command like so:
packer build -var gui_scale_factor=2 packer-centos7.json
In builders
, we tell Packer that we’d like to have one build of type
virtualbox-iso
, and inform VirtualBox that
this is going to be a RedHat 64bit guest.
Next, Packer does some magic for us and spins up a HTTP server serving out the content in the srv
directory. We then
tell Packer to wait for five seconds (or whatever the boot_wait
command-line variable is set to) on boot, and then
Packer will type in the kernel command-line in the VM for us.
More info on the boot_command
is available in the Packer docs.
In our case, we tell Packer to load our Kickstart file using the ks
command-line parameter. Most of the rest of the
Packer config file should be pretty self-explanatory:
- Download and verify the checksum of the given ISO image, in our case CentOS 7 over HTTPS.
- Tweak some VirtualBox VM settings.
- Mount the VirtualBox guest additions ISO in the guest at a given path.
- Tell Packer to try acquiring SSH to the guest for 20 minutes before giving up. (This has to do with other types of provisioning, which isn’t so important to us in this example)
Now that we’ve got a Packer build file, let’s dive into Kickstart.
Kickstart Your Engines
Before we get to the Kickstart, we’ll need to generate a root (or user) passphrase in a crypted format. The following Python script will do the trick:
scripts/gen-crypted-passphrase.py:
#!/usr/bin/env python3
from crypt import crypt
from getpass import getpass
from random import SystemRandom ; random = SystemRandom()
from string import ascii_lowercase, ascii_uppercase, digits
salt_chars = ascii_lowercase + ascii_uppercase + digits
# generate a SHA-512 passphrase from user input with a 16 byte random salt
passphrase = crypt(getpass(), "$6${}".format(''.join([random.choice(salt_chars) for i in range(16)])))
# print the output
print(passphrase)
Generate a password, my output was
$6$jtOb5fRIV3KNBxk9$lc39iSR0F2SXftduF1dLwR.PNng2PHmQ/WYzTvb699tZXxFDh/Kte4sGqlFtUHK8sA2QKrzCegb6XymzZdqbD1
for the horrible password lol
. SHA-512 is a hash function and not a KDF, but it’s the best we have for now,
at least until scrypt support is implemented and standardized.
Finally, the Kickstart:
srv/kickstart.ks:
# text installer
text
# install from net boot
install
url --url https://mirrors.kernel.org/centos/7.2.1511/os/x86_64/
# restart after finished
reboot
# run the Setup Agent on first boot
firstboot --enable
# localization
keyboard --vckeymap=us --xlayouts='us'
lang en_US.UTF-8
# this is necessary to prevent x installation
skipx
# configure auth method
auth --enableshadow --passalgo=sha512
# root password
rootpw --iscrypted "$6$jtOb5fRIV3KNBxk9$lc39iSR0F2SXftduF1dLwR.PNng2PHmQ/WYzTvb699tZXxFDh/Kte4sGqlFtUHK8sA2QKrzCegb6XymzZdqbD1"
# system services
services --enabled="chronyd,sshd"
# timezone
timezone UTC --isUtc --ntpservers=0.centos.pool.ntp.org,1.centos.pool.ntp.org,2.centos.pool.ntp.org,3.centos.pool.ntp.org
# bootloader configuration
bootloader --append=" crashkernel=auto" --location=mbr --boot-drive=sda
# Partitioning
# ------------
# only operate on /dev/sda
ignoredisk --only-use=sda
# remove all partitions and recreate the partition table on device sda
clearpart --all --initlabel --drives=sda
part / --fstype="xfs" --ondisk=sda --grow
part /boot --fstype="ext4" --ondisk=sda --size=512
part swap --fstype="swap" --ondisk=sda --size=4096
# Networking
# ----------
network --device=eth0 --bootproto=dhcp
%packages
@^minimal
@core
chrony
kexec-tools
%end
%addon com_redhat_kdump --enable --reserve-mb='auto'
%end
%post --interpreter /bin/bash
set -ex
# install puppetlabs repository
rpm -ivh https://yum.puppetlabs.com/puppetlabs-release-pc1-el-7.noarch.rpm
# install puppet
yum install -y puppet-agent
%end
I’m not going to walk line-by-line through this Kickstart file, as the docs do a pretty good job of explaining everything.
The most critical lines are:
# text installer
text
# install from net boot
install
url --url https://mirrors.kernel.org/centos/7.2.1511/os/x86_64/
# restart after finished
reboot
The text
clause will cause the installer to run from a tmux session, making it easy to get shell and poke around if
need be. install
tells the installer to, well, install, and the url
clause tells it where to attempt the
installation from. If you pass cdrom
instead of url
with a netinstall ISO, installation will fail with an unhelpful
message.
On a real server, it’s important that networking works so that the server can download the Kickstart file and fetch packages from the repositories. If you are so unfortunate as to not have DHCP in your datacenter (I feel your pain in a very personal way), the kernel command-line you need will likely look like this:
ip=10.0.0.4 gateway=10.0.0.1 netmask=255.255.255.0 nameserver=10.0.0.3 ks=http://10.0.0.5/kickstart.ks
Pass in your own static IP address, gateway, netmask, and nameserver to get off and running. The documentation covers all of the boot parameters.