ramar.work

Building Your Own Kernel

Running Arch on the new Tornado instance (see this article) has been worth the journey so far. To get up and running quickly, I stuck to using Arch Linux as my server’s distribution; but I ran into some problems with getting the kernel to work with my firewall.

The problem boiled down to installing some missing modules to allow iptables to function correctly, but I figured this was a good time to get my hands dirty and figure out if there were a better, long-term solution.

Why Build a Kernel?

The Arch distribution that is available with Tornado is a little out of date. This isn’t a massive problem because of Arch’s rolling release schedule (meaning that the distro will use a LiveCD or something simliar as a base and pull the latest packages from the internet to complete an initial install).

However, it appears that Tornado uses Xen for virtualization and might have been running a custom kernel to keep the boxes light on space and memory. When trying to run iptables for the first time, I got filter not available (I’m paraphrasing, since the actual error is not in front of me now). My guess is that the Tornado Arch kernels didn’t include these modules.

Get the Kernel

Arch and pretty much every other popular distribution includes a number of packages to enable switching between different kernels. Arch, for example, has packages which will download and install a hardened patch (linux-hardened), longterm (linux-lts). Documentation for that process is readily available here for Arch and only a quick Google search away for other distributions. I won’t be redundant and cover those here.

Our second option is to build a kernel from source via kernel.org. The process isn’t much more complicated, it just uses slightly more space. Let’s start off by grabbing a fresh copy from the kernel.org website. This could take a while depending on your connection speed, so have some coffee (or whiskey) ready:

[you@terminal]$ wget https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.19.tar.xz

Build It

Once the download is finished we can unpack it, change to the new directory and begin configuring the kernel.

[you@terminal]$ tar xJf linux-5.19.tar.xz && cd linux-5.19/

Once in the kernel source directory, there are a multitude of options we can use to build a kernel from scratch. I’ll list them here, but will only cover the first two.

make config - A question and answer based prompt to build a kernel.
make menuconfig - An ncurses based prompt with selectable options similar to a basic Linux distribution.
make xconfig - A configuration window using XWindows and QT5
make gconfig - A configuration window using GNOME desktop

The most powerful option (and easiest on mental resources) is the make menuconfig command. If you have done this once or twice and really need to go through every possible option, make config might work out for you too.

Before we can move ahead, however, both the config and menuconfig recipes require a couple of older GNU tools to run correctly. Make sure at the very least that the bison and flex packages are installed on your system. And if you are not familiar with compiling software from scratch, you’ll probably also need make and either gcc or clang. On Arch that process looks something like this:

[you@terminal]$ pacman -Syu flex bison make clang 

With those dependencies satisifed, we can go about creating our first configuration:

[you@terminal]$ make menuconfig

Pressing enter, your terminal should dump out a couple of lines of text:

[you@terminal]$ make menuconfig
  HOSTCC  scripts/basic/fixdep
  UPD     scripts/kconfig/mconf-cfg
  HOSTCC  scripts/kconfig/mconf.o
  HOSTCC  scripts/kconfig/lxdialog/checklist.o
  HOSTCC  scripts/kconfig/lxdialog/inputbox.o
  HOSTCC  scripts/kconfig/lxdialog/menubox.o
  HOSTCC  scripts/kconfig/lxdialog/textbox.o
  HOSTCC  scripts/kconfig/lxdialog/util.o
  HOSTCC  scripts/kconfig/lxdialog/yesno.o
  HOSTCC  scripts/kconfig/confdata.o
  HOSTCC  scripts/kconfig/expr.o
  LEX     scripts/kconfig/lexer.lex.c
  YACC    scripts/kconfig/parser.tab.[ch] 
  (bla bla bla...)

and eventually open a screen like the following:

'make menuconfig' in action

If you’ve been using Linux for a while or have installed something like FreeBSD, this menu will probably look familiar to you. You can move through and play with the options until you find a configuration that you are happy with.

Once this configuration is generated, you can proceed to actually build the kernel. Depending on your processor power, this will take a while. And even with a fast machine, building a kernel tends to be resource-intensive. So I would grab some more coffee (or whiskey) and pull up a 10 minute video on YouTube.

Build the Kernel (and any modules)

To build the kernel itself, we’ll just need to type make in the kernel directory and wait.

[you@terminal]$ make
    ...
  WRAP    arch/x86/include/generated/uapi/asm/socket.h
  WRAP    arch/x86/include/generated/uapi/asm/sockios.h
  WRAP    arch/x86/include/generated/uapi/asm/termbits.h
  WRAP    arch/x86/include/generated/uapi/asm/termios.h
  WRAP    arch/x86/include/generated/uapi/asm/types.h
  WRAP    arch/x86/include/generated/asm/early_ioremap.h
  WRAP    arch/x86/include/generated/asm/export.h
  WRAP    arch/x86/include/generated/asm/mcs_spinlock.h
  WRAP    arch/x86/include/generated/asm/irq_regs.h
  WRAP    arch/x86/include/generated/asm/kmap_size.h
  WRAP    arch/x86/include/generated/asm/local64.h
  WRAP    arch/x86/include/generated/asm/mmiowb.h
  WRAP    arch/x86/include/generated/asm/module.lds.h
  WRAP    arch/x86/include/generated/asm/platform-feature.h
  WRAP    arch/x86/include/generated/asm/rwonce.h
  WRAP    arch/x86/include/generated/asm/unaligned.h
  UPD     include/config/kernel.release
  UPD     include/generated/uapi/linux/version.h
  UPD     include/generated/utsrelease.h
  CC      scripts/mod/empty.o
  HOSTCC  scripts/mod/mk_elfconfig
  MKELF   scripts/mod/elfconfig.h
  HOSTCC  scripts/mod/modpost.o
  CC      scripts/mod/devicetable-offsets.s
  UPD     scripts/mod/devicetable-offsets.
    (output continued...)

If there are no issues (make will usually stop with an error), and you don’t need any specific external modules, you can go ahead and continue to the next step.

Install It

For systems using Lilo, you can just run make install and the new kernel will be appended to the bootloader options. For systems running Grub, it will be slightly more involved. I’ll break that down here in the next few paragraphs.

We’ll need to copy the image over to our boot directory.

[you@terminal]$ cp -v arch/x86/boot/bzImage /boot/vmlinuz-linux519-ramar-work

It will need an initramfs. This file is loaded into memory and used to instruct our CPU on how to boot into Linux.

The easiest way to generate a working version would be to make a copy of your system’s current preset file and simply rename it:

[you@terminal]$ cp /etc/mkinitcpio.d/linux.preset /etc/mkinitcpio.d/linux519ramar-work.preset

Once copied, we can instruct mkinitcpio to create an initial ramdisk using the new file as a preset:

[you@terminal]$ mkinitcpio -k linux519-ramar-work -g /boot/initramfs-linux519-ramar-work.img

Your terminal should spew a bunch of text similar to the following:

==> Building image from preset: /etc/mkinitcpio.d/linux519.preset: 'default'
  -> -k /boot/vmlinuz-5.19.0ramar-work \
        -c /etc/mkinitcpio.conf \
        -g /boot/initramfs-5.19.0ramar-work.img
==> Starting build: 5.19.0ramar-work
  -> Running build hook: [base]
  -> Running build hook: [udev]
  -> Running build hook: [autodetect]
  -> Running build hook: [modconf]
  -> Running build hook: [block]
  -> Running build hook: [filesystems]
  -> Running build hook: [keyboard]
  -> Running build hook: [fsck]
==> Creating zstd-compressed initcpio image: /boot/initramfs-5.19.0ramar-work.img
==> Image generation successful
==> Building image from preset: /etc/mkinitcpio.d/linux519.preset: 'fallback'
  -> -k /boot/vmlinuz-5.19.0ramar-work \
        -c /etc/mkinitcpio.conf \
        -g /boot/initramfs-5.19.0ramar-work-fallback.img \
        -S autodetect
==> Starting build: 5.19.0ramar-work
  -> Running build hook: [base]
  -> Running build hook: [udev]
  -> Running build hook: [modconf]
  -> Running build hook: [block]
  -> Running build hook: [filesystems]
  -> Running build hook: [keyboard]
  -> Running build hook: [fsck]
==> Generating module dependencies
==> Creating zstd-compressed initcpio image: \
    /boot/initramfs-5.19.0ramar-work-fallback.img
==> Image generation successful

When building a kernel, we’ll also have a file called System.map which lays out the addresses of syscalls in this version of Linux. This is not needed to boot.

[you@terminal]$ cp System.map /boot/System.map-linux519-ramar-work

Now we can go ahead and install our modules via make modules_install

[you@terminal]$ make modules_install

in which a successful run should look something like this:

  INSTALL /lib/modules/5.19.0ramar-work/kernel/drivers/thermal/intel/x86_pkg_temp_thermal.ko
  INSTALL /lib/modules/5.19.0ramar-work/kernel/fs/efivarfs/efivarfs.ko
  INSTALL /lib/modules/5.19.0ramar-work/kernel/net/ipv4/netfilter/iptable_nat.ko
  INSTALL /lib/modules/5.19.0ramar-work/kernel/net/netfilter/nf_log_syslog.ko
  INSTALL /lib/modules/5.19.0ramar-work/kernel/net/netfilter/xt_LOG.ko
  INSTALL /lib/modules/5.19.0ramar-work/kernel/net/netfilter/xt_MASQUERADE.ko
  INSTALL /lib/modules/5.19.0ramar-work/kernel/net/netfilter/xt_addrtype.ko
  INSTALL /lib/modules/5.19.0ramar-work/kernel/net/netfilter/xt_mark.ko
  INSTALL /lib/modules/5.19.0ramar-work/kernel/net/netfilter/xt_nat.ko
  DEPMOD  /lib/modules/5.19.0ramar-work

Our final step to installation, is notifying our bootloader that another kernel is available for use. Provided you are already using Linux and your system is already using Grub, there is no reason to reinstall the bootloader. You will, however, need a new configuration–since there are no facilities for Grub to detect new images at runtime.

[you@terminal]$ sudo grub-mkconfig -o /boot/grub/grub.cfg

Test It

Now we will need to reboot the system and test the new kernel. But wait, what if we should run into an issue?

In the future, I’ll be covering how to get fancy with your system’s boot process and debug issues with a kernel build.