Building Security-Hardened Embedded Systems with meta-sulka-distro

Yocto Project releases

Yocto Project Tutorial applies Maintenance
whinlatter (5.3) :test_fails: :test_works: development
walnascar (5.2) :test_fails: :test_works: stable
styhead (5.1) :test_fails: :test_fails: EOL
scarthgap (5.0) :test_works: :test_works: LTS

Currently only the scarthgap release is actively supported

A failure in the ‘tutorial applies’ column indicates that the instructions do not work without modification.

Note: This tutorial content is prone to change as the Sulka distribution is under active development. Configuration options, layer dependencies, and security policies may evolve. Always refer to the latest meta-sulka-distro documentation for the most current information.


Introduction

The meta-sulka-distro layer provides a security-hardened Yocto Project distribution designed for production IoT and edge devices. Unlike the default Poky reference distribution that prioritizes ease of use, Sulka implements a “secure by default” philosophy where all potentially dangerous features are disabled by default.

This tutorial will guide you through integrating meta-sulka-distro into your Yocto build to create embedded systems with security hardening, including restrictive firewall policies, disabled root access, and comprehensive security monitoring.

The techniques covered here apply to any embedded project requiring enhanced security posture, making it particularly valuable for industrial IoT, medical devices, and edge computing applications.

Prerequisites

  • Working Yocto Project build environment
  • Basic familiarity with bitbake and kas
  • Understanding of Linux networking and user management concepts

What you will learn

  • How to integrate meta-sulka-distro into existing Yocto builds
  • Configure security-hardened user management
  • Implement restrictive firewall policies with nftables
  • Enable security monitoring and file integrity checking

Step 1: Understanding Sulka’s Security Model

Before implementing Sulka, it’s important to understand its core security principles:

Deny by Default: All network traffic (including outgoing) is blocked by default
No Root Access: Root login is completely disabled
Service User Model: A single non-root user with sudo access for administration
Proactive Security Checks: Build-time validation prevents insecure configurations

Create a test directory for this tutorial:

mkdir sulka-tutorial && cd sulka-tutorial

Step 2: Adding meta-sulka-distro to Your Build

The meta-sulka-distro layer has specific dependencies that must be included in your build configuration.

Layer Dependencies

Add the following to your kas configuration file:

repos:
  meta-sulka-distro:
    url: https://codeberg.org/AltidSec/meta-sulka-distro.git
    commit: 64c1f5c0d484bd201e7f2f617040e2c5973d17cb

  meta-security:
    url: git://git.yoctoproject.org/meta-security.git
    commit: bc865c5276c2ab4031229916e8d7c20148dfbac3

  meta-openembedded:
    layers:
      meta-python:
      meta-networking:

Required Variables

The layer checks for essential security configuration during build:

local_conf_header:
  sulka: |
    DISTRO = "sulka"
    SULKA_SERVICEUSER_USERNAME = "mender"
    SULKA_SERVICEUSER_PASSWORD = "$encrypted_hash_here"

Step 3: Generating Secure User Credentials

Sulka requires a properly encrypted password for the service user account.

Creating the Password Hash

Use the mkpasswd utility to generate a secure hash:

# Install mkpasswd if not available
sudo apt-get install whois

# Generate password hash (replace 'your_password' with actual password)
mkpasswd -m yescrypt -s -R 8 your_password

Escaping for Yocto Configuration

The password hash contains dollar signs that must be escaped for BitBake:

# Generate and escape in one command
mkpasswd -m yescrypt -s -R 8 your_password | sed 's/\$/\\$/g'

Copy the resulting escaped hash to your configuration, which looks similar to this:

SULKA_SERVICEUSER_PASSWORD = "\\$y\\$jCT\\$odv3AoU98sQjAtgZRT1\\$xPty.dqoENrSyksek7i3"

Step 4: Understanding Sulka’s Security Components

Core Security Package Group

The packagegroup-sulka recipe defines the essential security components:

RDEPENDS:${PN} = "\
    aide \          # File integrity monitoring
    auditd \        # Security event logging
    nftables \      # Modern firewall framework
    nftables-configuration \
    sudo \          # Controlled privilege escalation
"

Firewall Configuration

Sulka includes several firewall templates in recipes-filter/nftables-configuration/files/:

  • nftables-drop-everything.conf (default - blocks all traffic)
  • nftables-allow-established-lo-outgoing.conf (allows established connections)
  • nftables-allow-established-lo-ssh-icmp-outgoing.conf (adds SSH and ICMP)

Security Validation

The variable-check.bbclass prevents common security misconfigurations:

insecure_features = [
    "debug-tweaks", 
    "empty-root-password", 
    "allow-empty-password", 
    "allow-root-login", 
    "serial-autologin-root"
]

Step 5: Customizing Security Policies

Selecting Firewall Templates

Choose an appropriate firewall template based on your connectivity requirements:

local_conf_header:
  firewall: |
    SULKA_NFTABLES_CONF = "nftables-allow-established-lo-ssh-icmp-outgoing.conf"

Enabling Graphics Support

Graphics support is disabled by default for minimal attack surface:

graphics: |
  SULKA_DISABLE_GRAPHICS = "0"  # Enable if needed

Sudo Configuration

Service user sudo access can be configured by modifying the template in recipes-extended/sudo/files/serviceuser.conf.

Step 6: Building Your Hardened Image

This example is a shortened version of the qemuarm64-sulka.yml, please review the full setup for additional information.

Complete Kas Configuration

Create sulka-hardened.yml:

header:
  version: 14
  includes:
  - kas/include/mender-full.yml

distro: sulka

repos:
  meta-sulka-distro:
    url: https://codeberg.org/AltidSec/meta-sulka-distro.git
    commit: 64c1f5c0d484bd201e7f2f617040e2c5973d17cb

  meta-security:
    url: git://git.yoctoproject.org/meta-security.git
    commit: bc865c5276c2ab4031229916e8d7c20148dfbac3

  meta-openembedded:
    layers:
      meta-python:
      meta-networking:

local_conf_header:
  sulka: |
    SULKA_SERVICEUSER_USERNAME = "mender"
    SULKA_SERVICEUSER_PASSWORD = "$your_escaped_hash"
    CORE_IMAGE_EXTRA_INSTALL:append = " packagegroup-sulka "

target:
  - core-image-minimal

Execute the Build

kas build sulka-hardened.yml

The build will fail immediately if any insecure IMAGE_FEATURES are detected.

Step 7: Testing Your Hardened System

Initial Boot Verification

  1. Boot your device using the generated image
  2. Attempt to login as root (should fail)
  3. Login using your configured service user credentials

Network Security Verification

Test the firewall configuration:

# Check firewall rules
sudo nft list ruleset

# Attempt outgoing connection (should fail with default config)
ping google.com

# Check network interfaces
ip addr show

Security Monitoring

Verify security components are active:

# Check audit daemon
sudo systemctl status auditd

# Initialize AIDE database
sudo aide --init

# Check file integrity
sudo aide --check

Troubleshooting

Build Fails with Security Errors

If you encounter security-related build failures:

  1. Remove insecure IMAGE_FEATURES:

    IMAGE_FEATURES:remove = "debug-tweaks empty-root-password"
    
  2. Check for conflicting configurations:
    Review your local.conf for any security-related overrides

Network Connectivity Issues

This is expected behavior! Configure the firewall to allow required traffic:

  1. Edit firewall configuration:

    sudo vi /etc/nftables.conf
    
  2. Restart nftables service:

    sudo systemctl restart nftables
    

Login Problems

Verify password configuration:

  1. Check password hash generation:

    mkpasswd -m yescrypt -s -R 8 test_password
    
  2. Verify escaping in configuration:
    Ensure all $ characters are escaped as \\$

Best Practices

  1. Use specific layer commits in production builds
  2. Document security policy decisions for compliance
  3. Keep service user credentials and API keys secure, make sure they do not leak out through accidental commits.

Further Reading