Skip to content

saminaltamimi/portable_services

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Portable Services

A collection of systemd portable services and system extensions (sysext) for Linux, built with reproducibility and cryptographic verification in mind.

Overview

This repository provides secure, self-contained, cryptographically-signed portable services and system extensions for various applications. Each service is built as a minimal, isolated root filesystem packaged into a dm-verity signed raw image.

Available Services

  • chrony - Network Time Protocol (NTP) client/server
  • unbound - Validating DNS resolver
  • tailscale - WireGuard-based VPN mesh network
  • podman - Daemonless container engine
  • apparmor - Mandatory Access Control (MAC) using Linux Security Modules
  • hardened_malloc - Security-focused memory allocator (sysext)

Features

  • dm-verity signing - Cryptographic verification of root filesystem integrity
  • Reproducible builds - Deterministic build process for verifiable outputs
  • Minimal footprint - Stripped-down images with only essential dependencies
  • Arch Linux base - Built from official Arch Linux containers
  • Network-isolated signing - Private keys never exposed to network
  • Parallel builds - Build all services concurrently

Prerequisites

  • Linux system with systemd
  • Podman or Docker
  • Just command runner (just)
  • OpenSSL (for key generation)
  • sudo access (for privileged operations)

Quick Start

1. Generate Signing Keys

First, generate the dm-verity signing keys (stored in _shared/ and gitignored):

just gen-verity-keys

2. Build a Service

Build a single portable service:

just build chrony

Or build all services in parallel:

just build-all

3. Sign a Service

Sign a built service with dm-verity:

just sign chrony

Or sign all built services:

just sign-all

4. Build and Sign in One Step

Package a service (build + sign):

just package chrony

Or package everything:

just package-all

Usage

Managing Services

# List all available commands
just list

# Check status of all services
just status

# Clean a specific service
just clean chrony

# Clean everything
just clean-all

Deploying Portable Services

After building and signing, deploy with portablectl:

# Attach the portable service
sudo portablectl attach /path/to/chrony.raw --enable

# Start the service
sudo systemctl start chrony

# Check status
sudo systemctl status chrony

# Detach when done
sudo portablectl detach chrony

Deploying System Extensions

For sysext images like hardened_malloc:

# Copy to system extension directory
sudo cp hardened_malloc/outraw/hardened_malloc.raw /var/lib/extensions/

# Refresh extensions
sudo systemd-sysext refresh

# Verify
sudo systemd-sysext status

Architecture

Build Process

Each service follows this workflow:

  1. Build Stage (Containerfile)

    • Creates a minimal /portable or /sysext root filesystem
    • Installs only required runtime dependencies
    • Strips documentation, caches, and unnecessary files
    • Copies service-specific configuration from rootfs/
  2. Signing Stage (_shared/sign.Containerfile)

    • Calculates optimal partition sizes
    • Creates dm-verity partitions using systemd-repart
    • Generates cryptographic root hash
    • Signs with private key (network disabled during key operations)
    • Produces .raw, .roothash, and .roothash.p7s files

Directory Structure

.
├── Justfile                      # Build automation recipes
├── _shared/                      # Shared signing infrastructure
│   ├── sign.Containerfile        # Common signing logic
│   ├── verity.crt                # Public signing certificate (gitignored)
│   └── verity.key                # Private signing key (gitignored)
├── chrony/                       # Example service
│   ├── Containerfile             # Build definition
│   ├── rootfs/                   # Service-specific files
│   ├── outdir/                   # Extracted portable tree
│   └── outraw/                   # Signed .raw image + signatures
└── ...                           # Other services

Output Files

After building and signing, each service produces:

  • outdir/ - Unsigned portable/sysext tree (for inspection)
  • outraw/<service>.raw - Signed raw disk image
  • outraw/<service>.roothash - dm-verity root hash
  • outraw/<service>.roothash.p7s - PKCS#7 signature

Configuration

Environment Variables

  • CONTAINER_RUNTIME - Container runtime to use (default: auto-detect podman/docker)
  • BUILD_ELEVATE - Privilege escalation command (default: sudo)

Customization

To customize a service:

  1. Modify <service>/Containerfile to adjust packages or build logic
  2. Add configuration files to <service>/rootfs/ (overlaid onto /portable)
  3. Rebuild: just build <service>
  4. Re-sign: just sign <service>

Security Considerations

  • Private keys (_shared/verity.key) are gitignored and should never be committed
  • Network isolation - Signing operations run with --network=none to prevent key exfiltration
  • Reproducibility - Builds should be deterministic for verifiable supply chain
  • Minimal attack surface - Services include only runtime dependencies
  • dm-verity - Read-only root filesystem with cryptographic integrity verification

Troubleshooting

Build fails with "No /portable or /sysext found"

Ensure your Containerfile creates either /portable or /sysext directory and populates it.

Signing fails with "Verity keys not found"

Run just gen-verity-keys first to generate signing keys.

portablectl doesn't recognize the service

Ensure the service has:

  • Proper systemd unit files in /portable/usr/lib/systemd/system/
  • Correct naming convention for portable services
  • os-release or extension-release metadata

Image size too small

The signing process auto-calculates sizes based on content. If builds fail with size errors, check the partition calculation logic in _shared/sign.Containerfile.

Contributing

To add a new service:

  1. Create a new directory: mkdir -p <service>/rootfs
  2. Add a Containerfile following the existing patterns
  3. Add service configuration to <service>/rootfs/
  4. Build and test: just package <service>

License

This repository's build infrastructure is provided as-is. Individual services are subject to their respective upstream licenses.

References

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors