This is another blog post about securing your Yocto built systems:
- Securing Yocto Built Systems overview presentation slides
- Yocto Security: Production and Development Images
- Yocto security: Kernel Hardening ⬅️
Introduction
The Linux kernel is the cornerstone and stronghold of a Linux based system. Unlike user-space applications which run with limited privileges, if it’s compromised, there is almost no limit to what an attacker can do.
While nothing is unbreakable, there are two types of settings you can change to make your kernel harder to compromise:
- Reducing the attack surface: compile the kernel with fewer features and drivers. Only keep what you need on your system. That’s by the way easier to do on an embedded system because you know exactly what programs are supposed to be executed.
- Using kernel hardening features: these features, developed along the years, make it harder to exploit not-yet-known vulnerabilities, or to attack a specific piece of code. See Kernel Self-Protection in the Kernel documentation for details.
In both cases, you’ll have to modify your kernel configuration and possibly the command line. The challenge is to find which settings to modify!
Kernel Hardening Checker
A very handy tool is Kernel Hardening Checker from Alexander Popov: https://github.com/a13xp0p0v/kernel-hardening-checker
Alexander has been working on kernel security and hardening at least since I first met him at an embedded Linux conference, around 2015 or even earlier! He knows this topic well for sure.
This tool is a Python script that looks at your kernel configuration (through /proc/config.gz
, otherwise through /boot/config-version
), at the kernel command line as well as at Sysctl settings, and looks for opportunities to enable or disable features to make the kernel harder to compromise.

Try it on your PC
kernel-hardening-checker
is very easy to try on your PC first:
$ git clone https://github.com/a13xp0p0v/kernel-hardening-checker.git
$ cd kernel-hardening-checker
$ ./bin/kernel-hardening-checker -a
You can see how “secure” the kernel on your desktop or server kernel is. You can blame (or praise) your distribution maintainers but keep in mind that a PC is an open platform meant to run and debug any types of programs, including the kernel. Unlike on embedded systems that have fixed functionality, and are usually not meant to be development platforms, a fully locked down kernel could restrict what you could do with your PC.
Anyway, in my case, I run my own kernel on my two PCs, and I’m trying to see how far I can go by following kernel-hardening-checker
‘s advice. I’m going slowly, one setting at a time, to avoid breaking something important without the ability to know what change caused the issue.
I’ll report on this effort later, in particular for people who want to harden their production x86 kernels. See on how to rebuild your own kernel on your PC or server. That’s surprisingly easy to do.
Using the tool on Yocto
kernel-hardening-checker
is easy to use on Yocto, provided you are on a version that knows how to build it.

It was added to the meta-openembedded repository in the Walnascar branch, which is still the latest stable release at the time of this writing (Oct. 2025).
You can also use the tool if you’re living on the edge and using the master
branch. This branch includes changes I sent to support the latest version (0.6.10.2
), which adds new options, new checks and support for the RISC-V architecture. Since then, the maintainers for this layer also accepted my patch to support this tool in the scarthgap branch.
So, you just need to add meta-oe
and meta-python
from meta-openembedded
to your layers (conf/bblayers.conf
) and add kernel-hardening-checker
to your image:
IMAGE_INSTALL += "kernel-hardening-checker"
After building and flashing your image, you can directly run the tool from a command line shell:
$ kernel-hardening-checker -a
On the right column, I’m sharing screenshots of the output of the tool on the BeagleBone Black board, for example.

Of course, not every hint will be relevant for your system, but I bet you will find many applicable changes that can be applied without impacting the functionality or performance of your embedded Linux system.
Another audit tool: Lynis
Lynis is another tool that you can use, not only on your GNU/Linux desktop and servers, but also on your Yocto built embedded Linux systems.
This tool runs a quite exhaustive audit on most aspects of your system which can impact its security. For example, it will flag unsafe file permissions, the presence of compilers in the filesystem, networking hazards and many more potential issues of all kinds.
All these features deserve a dedicated blog post or presentation. For the moment, let’s stay focused on the kernel hardening part of its report.
lynis
is available through the meta-security layer, for all supported versions: Kirkstone, Scarthgap and Walnascar. So, add lynis
to your image:

IMAGE_INSTALL += "lynis"
Then, run the tool on your board:
$ lynis audit system
As you can see, once again on BeagleBone Black, Lynis focuses on Sysctl configuration settings. This can complement the output of kernel-hardening-checker
.
Remove unnecessary kernel features
You’re not done yet. Paying attention to recommendations from tools like kernel-hardening-checker
and lynis
is not enough. You should also take time to remove the kernel features you’re not using in your system:
- Unused kernel drivers, not only built-in drivers, but also kernel modules, even the ones that are not loaded by default. If a module has known vulnerabilities, an attacker could try to get the system to load it.
- Unused filesystems, unused network protocols…
- Entire kernel subsystems. If you could only use built-in drivers, you could even remove loadable kernel modules support entirely.
The smaller your kernel, the smaller its attack surface too.
Unfortunately, I don’t know of any open-source tools that would help you to automatically identify unnecessary code. Some instrumentation based on ftrace
could however be used to identify all code paths used in a testing scenario, allowing to identify unused code, and the corresponding kernel configuration settings to stop compiling such code.
If you are interested in such a tool, and could find funding for it, such funding would allow me to spare enough time for such a development. This would be great for boot time reduction too. A smaller kernel is faster to load and initializes faster.
Anyway, my advice is to try removing kernel features one by one, to make it easy to identify the features that turn out to be needed in your system.
A good CI/CD and board testing pipeline will help a lot too, to quickly identify all unwanted side effects of the changes you make. Anyway, you can’t develop a real project without one, right?
Implementation details
Tweaking kernel configuration
The main aspect of hardening your kernel is changing its configuration settings.
Whatever the kernel recipe you’re using (linux-yocto
or another one), it’s very likely to be inheriting the kernel class. In this case, here’s what you can do to change the kernel configuration:
$ bitbake -c menuconfig linux-recipe
My advice: change kernel settings very progressively, ideally one at a time when you’re experimenting with configuration settings you have never tried before. They might break some programs or, a little more likely, negatively impact performance.
For example, the RANDOM_KMALLOC_CACHES
setting helps to harden the kernel against heap spraying attacks (see LWN.net articles for details: 1, 2). It doesn’t seem to have any noticeable negative impact on BeagleBone Black, for example:
Once you made your changes, save your new configuration as a defconfig
file (storing only kernel configuration settings differing from their default value):
$ bitbake -c savedefconfig linux-recipe

RANDOM_KMALLOC_CACHES
kernel configuration optionThen, you can add the resulting defconfig
file to your kernel recipe. You can use a .bbappend
file to add it:
FILESEXTRAPATHS:prepend := "${THISDIR}/files:"
SRC_URI += "\
file://defconfig \
"
If your kernel recipe is linux-yocto
, you can even use configuration fragments carrying only the configuration settings you wish to change. For BeagleBone Black again, I created the below recipes-kernel/linux/linux-yocto_%.bbappend
file:
FILESEXTRAPATHS:prepend := "${THISDIR}/files:" SRC_URI += "\ file://memory-hardening.cfg \ file://overlayfs.cfg \ "
Here are the contents of the memory-hardening.cfg
file:
CONFIG_RANDOM_KMALLOC_CACHES=y
CONFIG_SLAB_MERGE_DEFAULT=n
CONFIG_BUG_ON_DATA_CORRUPTION=y
You can use multiple configuration fragments, but I suspect that if you use too many of them, there may be conflicts between them. If this happens, you can still make your own defconfig
file as described earlier. Note that you can use configuration fragments together with a defconfig
file.
Two machine definitions
As you progress through the removal of unnecessary kernel features, and the addition of hardening features, you will see that your system will also get harder to debug. That’s why it will be necessary to have two MACHINE
variants in your project: the normal one associated to a debugging and development friendly kernel configuration, and the production-ready one with a hardened kernel configuration.
Having two different MACHINE
settings at the same time allows to start introducing hardening settings earlier in project development, without getting in the way of your normal application and system development tasks.
This takes our Yocto Security: Production and Development Images article to yet another level. We added 3 different images, 2 distributions, and now 2 different machines!

The production test image will typically be used to run tools like kernel-hardening-checker
and lynis
on your production image.
You may object that we could also have kept a single machine but introduced two kernel recipes, a normal one, and a hardened one. However, that’s overly complicated compared to using variable overrides in a single recipe.
For example, here’s how the recipes-kernel/linux-yocto_%.bbappend
file can handle different settings for two machines, beaglebone-yocto and beaglebone-yocto-prod
.
FILESEXTRAPATHS:prepend := "${THISDIR}/files:"
# Common source file names
SRC_URI += "\
file://defconfig
file://overlay.cfg \
"
# Machine specific source file names
SRC_URI:beaglebone-yocto-prod += "\
file://memory-hardening.cfg \
"
SRC_URI:beaglebone-yocto += \"
file://kernel-debugging.cfg \
"
By including different overrides in its search path, OpenEmbedded actually makes it possible to have two different files with the same name for different machines:
.
|-- files
| |-- beaglebone-yocto
| | |-- defconfig
| | `-- kernel-debugging.cfg
| |-- beaglebone-yocto-prod
| | |-- defconfig
| | `-- memory-hardening.cfg
| `-- overlayfs.cfg
`-- linux-yocto_%.bbappend
If you want to understand this better, check out our Yocto Project and OpenEmbedded training course materials.
Final thoughts
Given the big number of kernel settings to consider, kernel hardening represents substantial effort. However, at the scale of a project and even working on a entire series of similar products, this is still acceptable if done early and as a long term optimization task. The security benefits are worth the effort.
If all this looks a bit complicated to you, you may be interested in our Yocto Project and OpenEmbedded training course.
I would also be delighted to review your current Yocto implementation and help you improve it through Root Commit’s consulting services.