What to avoid
So, you use Yocto to build an image for your embedded device. You tweak the image and distribution settings to get the features you need, and other developers use the SDK built by Yocto to create and build the User Interface and other applications.
As product release is getting closer, it’s then time to harden the image to meet its security objectives. You reduce the kernel configuration to its minimum, you remove bootloader shell access, you remove text editors and other convenient utilities, and even remove root user access.
But then, making and testing becomes more complicated than before. You have to temporarily restore debugging settings to make it easier to test the new features, instead of being locked out of your own system. And then remove these settings, to generate a new production image again. All this back and forth is error prone, as there’s the risk to leave some debugging settings in the production image.
Two images solution

What about defining two image variants, one for production and one for development? This way, the development settings are well isolated from the production ones, and you can refine each of them progressively without compromising the other.
Yocto makes this very easy to do. All you have to do is require
the shared definitions. You could also use the include
statement, but it doesn’t fail when the file to include doesn’t exist.
Here are example recipes files:
recipes-core/images/image-prod.bb |
---|
DESCRIPTION = "A small image to demonstrate securing a Yocto generated system" # Packages to skip installing |
recipes-core/images/image-common.bb |
---|
require recipes-core/images/core-image-base.bb IMAGE_INSTALL += "lighttpd" :# Create mount point for data partition image_create_data_dir() { |
recipes-core/images/image-dev.bb |
---|
DESCRIPTION = "The debugging variant of the secure demo image" |
Two distributions too
Similarly, each type of image can be associated to a specific distribution setting, one for production, and one for development.
This way, the “production” distro can use compiler settings that harden the generated binaries. You don’t want to use such settings in the “development” distro, because they will make your binaries harder to debug.
conf/distro/distro-prod.conf |
---|
DISTRO = "distro-prod" require distro-common.conf # Compiler hardening settings # Build systemd with systemd-firstboot # Unnecessary distro features |
conf/distro/distro-common.conf |
---|
INIT_MANAGER = "systemd" |
conf/distro/distro-dev.conf |
---|
DISTRO = "distro-dev" require distro-common.conf |
Adding production testing tools
Then, what if you want to use tools like security scanners, like kernel-hardening-checker or lynis?
You cannot really add them to the “development” image, because they will probably flag false positives with development tools or settings not present in the production image. You shouldn’t add them to the “production” image either, as they may pull extra dependencies, such as a Python interpreter or a compiler, and could increase the attack surface.
A solution is to introduce yet another image, a “production testing” one. It is built with the same settings as the production image, but just adds some extra security scanning and production image testing tools.

Here’s what this new image could look like:
recipes-core/images/image-prod-test.bb |
---|
DESCRIPTION = "A small image to demonstrate securing a Yocto generated system - With added test tools" |
What to remember
If you’re using Yocto to develop your embedded system, this article suggests to introduce three different images:
- A development image: containing all the applications in your system, plus a few extra features to make your life easier as a developer, such as your favorite text editor, an SSH server with password-less root access or debugging tools. This image will be compiled with debugger friendly settings.
- A production image: containing only the applications and features strictly needed in your system. The kernel, bootloader and userspace applications have been hardened, to reduce the attack surface. Of course, debugging and console access features are limited to what’s strictly necessary for supporting users having issues.
- A production testing image: very similar to the production image, it just adds security scanning and production testing tools that you don’t want to leave in the production image, because they could help attackers to compromise your system.
The main benefit of this approach is that you can continuously refine your production image, without any interference from settings or changes that you have to introduce for development or debugging purposes.
See our Securing Yocto Built Systems presentation for further details and techniques for hardening your systems. If you’re new to Yocto, also check out our freely available Yocto Project and OpenEmbedded training course materials. You may also be interested in our consulting services.