Why Alpine Linux
The base distribution decision affects everything downstream: package availability, security posture, image size, and maintainability. Alpine Linux 3.19 was chosen over Debian and Ubuntu for several concrete reasons.
| Metric | Alpine 3.19 | Debian 12 | Ubuntu 22.04 |
|---|---|---|---|
| Base image size | ~5 MB | ~150 MB | ~77 MB |
| C library | musl (900 KB) | glibc (2.5 MB) | glibc (2.5 MB) |
| Init system | OpenRC | systemd | systemd |
| Package manager | apk | apt | apt |
| Shell utilities | BusyBox (1 MB) | GNU coreutils (~50 MB) | GNU coreutils (~50 MB) |
| Typical package count | ~230 | ~500+ | ~600+ |
Fewer packages means fewer potential CVEs. A 5 MB base image means less code to audit. For a security-focused distro, minimalism isn't a compromise — it's the entire point. Every package that isn't installed is a vulnerability that can't be exploited.
musl vs glibc
The C library is the most fundamental dependency in a Linux system. Every dynamically-linked binary depends on it. The choice between musl and glibc has cascading effects on security, size, and compatibility.
| Metric | musl libc | glibc |
|---|---|---|
| Size on disk | ~900 KB | ~2.5 MB |
| Source lines of code | ~100K | ~1.8M |
| Static linking | Clean, well-supported | Partial, discouraged |
| Binary compatibility | Some gaps (NSS, locale) | Maximum compatibility |
| Auditability | Feasible (one person can read it) | Impractical at scale |
Known Tradeoffs
musl has differences in DNS resolution behavior and lacks some glibc extensions. Some pre-compiled binaries assume glibc. For JefeLinux, this is an acceptable tradeoff because the target workloads (Python, Node.js, Docker containers) work well with musl, and containers can bring their own libc if needed.
100K lines of code can be meaningfully audited. 1.8M cannot. For a security-focused distro, code auditability at the C library level matters more than binary compatibility with legacy software.
OpenRC vs systemd
The init system is PID 1 — the first process that runs and the last to stop. Its attack surface directly impacts system security. This is a deliberate architectural choice, not a philosophical stance.
| Metric | OpenRC | systemd |
|---|---|---|
| Architecture | ~50 shell scripts | 1.4M+ lines of C |
| Scope | Init + service management | Init + logging + DNS + NTP + containers + ... |
| Log format | Plain text (syslog) | Binary journal |
| Dependencies | Dependency-based boot | Dependency-based boot |
| POSIX compliant | Yes | Linux-specific |
# /etc/init.d/jefe - JefeLinux service script
name="Jefe Application Manager"
command="/opt/jefe/bin/jefe-setup"
command_background="yes"
pidfile="/run/${RC_SVCNAME}.pid"
depend() {
need net
after docker
}
systemd does more, but for a security distro we want less — less code, less attack surface, more transparency. An admin can read every OpenRC init script on the system in an afternoon. That kind of auditability is impossible with systemd.
Defense-in-Depth Security
JefeLinux implements security at six independent layers. If any single layer is compromised, the remaining layers continue to protect the system. No single control is trusted to be sufficient on its own.
Layer 1: Kernel Parameters
kernel.randomize_va_space = 2 # Full ASLR
kernel.kptr_restrict = 2 # Hide kernel pointers
kernel.dmesg_restrict = 1 # Non-root can't read dmesg
kernel.lockdown = confidentiality # Kernel lockdown
net.ipv4.tcp_syncookies = 1 # SYN flood protection
net.ipv4.conf.all.rp_filter = 1 # Reverse path filtering
fs.protected_hardlinks = 1 # Hardlink protection
fs.protected_symlinks = 1 # Symlink protection
Layer 3: nftables Firewall
chain input {
type filter hook input priority 0; policy drop;
ct state established,related accept
iif "lo" accept
tcp dport 22 limit rate 3/minute accept
}
}
Layer 4: SSH Configuration
PermitRootLogin no
PasswordAuthentication no
PubkeyAuthentication yes
Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com
MaxAuthTries 3
Layer 5: Audit Rules
-w /etc/shadow -p wa -k shadow_changes
-w /etc/passwd -p wa -k passwd_changes
-a always,exit -F arch=b64 -S execve -k exec_log
Build Pipeline
JefeLinux is built through a 5-stage pipeline that produces a bootable ISO from source in 5-8 minutes. Each stage is isolated and can be individually cached or re-run. The pipeline runs inside Docker for reproducibility across platforms.
$ ./build.sh --target iso --arch x86_64
[Stage 1/5] Bootstrap... OK (28s)
[Stage 2/5] Packages... OK (3m42s)
[Stage 3/5] Security... OK (31s)
[Stage 4/5] Overlay... OK (8s)
[Stage 5/5] ISO Creation... OK (1m15s)
Total: 5m44s | Output: jefelinux-0.1.0-x86_64.iso (381 MB)
Build Interfaces
The build pipeline supports multiple interfaces for different environments:
| Interface | Platform | Use Case |
|---|---|---|
| build.sh | Linux native or Docker | Primary build script (390 lines) |
| build.ps1 | Windows via Docker | PowerShell wrapper for cross-platform support |
| Jenkins Pipeline | CI/CD server | Automated builds with Hyper-V VM deployment |
| Dockerfile.builder | Any (containerized) | Docker-in-Docker for reproducible builds |
Future Roadmap
JefeLinux v0.1.0 establishes the security foundation. The next phase focuses on boot-chain trust, data-at-rest encryption, and build reproducibility.
UEFI Secure Boot
Establish a full chain of trust from firmware through bootloader to kernel. Prevents boot-time rootkits by cryptographically verifying each stage before execution.
Full Disk Encryption (LUKS)
LUKS2-based encryption for data at rest. Protects against physical access attacks and ensures confidentiality when hardware is decommissioned or stolen.
Immutable Root Filesystem
Read-only root with overlayfs for mutable state. Prevents persistent modifications by attackers and enables atomic rollback on failed updates.
Yocto/OpenEmbedded Migration
Move from Alpine apk-based builds to Yocto for fully deterministic, offline-capable, bit-for-bit reproducible builds. Full control over every package from source.