Skip to main content

Posts

Showing posts with the label Experiment

Qubesify My Daily Driver Part 3: VM Management, Nested Compositors, and Containment

In Part 2 , I got a basic proof-of-concept running: a single headless microVM booting a minimal Debian kernel and forwarding Firefox back to my daily-driver desktop (“Viewer VM”) over VSOCK using Waypipe and PipeWire. It proved the concept, but it was essentially a static, single-instance test rig. To actually live in this setup like I used to in Qubes OS, I needed to turn this prototype into a scalable, multi-domain system. In this post, I’ll walk through the next iteration of the architecture: building a centralized VM lifecycle manager, isolating VMs under dedicated host users, scaling dynamic TAP networks, and running the Waypipe viewer inside a confined nested compositor container. 1. Domain Modeling & Host VM Management In Qubes OS, workloads are categorized into Disposable VMs (DispVMs) for ephemeral tasks and AppVMs for persistent user data, both derived from centralized Template VMs . While Qubes relies on Template VMs to maintain packages across domains, I chose ...

Qubesify My Daily Driver Part 2: Headless Micro VM

I decided to take the plunge into micro VMs. My goal? To set up a headless micro VM capable of running graphical programs remotely. As a first milestone, I wanted to get Firefox running and smoothly playing videos. (See Part 1 for a breakdown of why I passed on other isolation methods.) Overview At a high level, the concept is simple: I click an icon, and Firefox opens seamlessly on my screen while actually running securely in a VM. This setup is similar to disposable VMs in Qubes OS. When the program closes, the VM is destroyed, leaving absolutely zero trace on the disk. To pull this off, I needed to boot a micro VM with a minimal kernel and disk image, and seamlessly forward both graphics and audio to my daily-driver main VM. The Kernel Unlike standard VMs, micro VMs do not support PCI devices. Instead, they rely on different protocols (like virtio_mmio and virtio_blk ), which the kernel must support natively. Crucially, these drivers must be compiled directly into the kernel...

Isolating Graphical Software (Part 1)

I’ve really been missing the experience of Qubes OS, where all programs are properly isolated. However, I can’t install it on my daily driver because I use that machine for gaming. Instead, I’ve been exploring ways to approximate that isolation on a standard Linux setup. Firefox Profiles Let’s start with the browser. There are many privacy and security benefits to separating profiles. My general workflow is as follows: I create separate .desktop files to run Firefox with different profiles. Each file uses a unique --name and StartupWMClass= . This prevents the icons from stacking together on the GNOME Panel. I use ImageMagick to tint the icons so I can tell the profiles apart at a glance: magick input.png -colorspace gray -fill "#cc0000" -tint 100 output.png The “new” Firefox profile manager didn’t work well for me. For example, I couldn’t set a default profile for opening external URLs. I eventually had to switch back to the old profile management style. However, whil...

My Experience with libvirt

My daily driver setup has been working exceptionally well. If you missed them, you can check out the previous posts for details: Part 1 Part 2 Part 3 Recently, I decided to harden my setup by writing custom AppArmor profiles and nftables rules. During my research, libvirt kept popping up in tutorials. In fact, most “QEMU/KVM” guides simply assume you are using it. Initially, I decided against using libvirt because I wasn’t a fan of its design philosophy. However, I kept hearing that it can automatically generate AppArmor profiles and firewall rules for each VM. Furthermore, this article highlighted several things that libvirt genuinely simplifies. Intrigued, I decided to dive in and get some first-hand experience. The plan was to migrate my existing VM setup (which relies on bare QEMU scripts) to libvirt to see if it was a good fit. I specifically wanted to evaluate the parts of libvirt I was previously skeptical about: Reliance on a highly privileged daemon. Configuration stored...

Writing Sudoku Solvers

After writing a Nonogram solver, I decided to tackle a Sudoku solver to practice Rust. My goal wasn't just to support classic Sudoku rules, but also to handle variants like Thermometer, Arrow, and Cage etc. 1. Brute Force It is fairly easy to write a brute force or backtracking algorithm. This approach is sufficient for most classic Sudoku puzzles, but it becomes unbearably slow as soon as variant rules are introduced. I considered this step a warmup—a baseline to improve upon. 2. Constraint Propagation Here, I tried to introduce "logical thinking" to the algorithm. I used  u16  as a bitmask to represent the possible values of a cell. Whenever a cell's state changes (due to guessing, backtracking, or propagation), the algorithm consults all constraints to eliminate impossible candidates. While Nonogram is technically an NP-Complete problem, in practice, my constraint-propagation solver (without guessing) can solve almost all puzzles found online. I’ve only seen one ex...

An Adventure with Qubes OS

I've been experimenting with Qubes OS on my new laptop and wanted to share some notes on the experience. Hardware Overall, Qubes OS works quite well on my hardware. Aside from typical issues like deep sleep, speaker performance, and touchpad scroll speed, the experience has been smooth. I particularly like that I can boot directly from a microSD card. This allowed me to move the  /boot  partition to the card while completely disabling USB access in  dom0  for better security. Detached  /boot  and LUKS Header Moving  /boot  and the LUKS header to a microSD card is a fun project, but it has some drawbacks: I have to remember to mount  /boot  before updating  dom0 . The system won't shut down properly if I forget to unmount  /boot . Testing Qubes OS 4.3 rc3 I decided to test the Qubes OS 4.3 rc3 release by performing an in-place upgrade. Unfortunately, the system failed to boot afterward. dracut  Issues After the upgrade, the...

A Journey into Podman: Notes on My First Adventure

For the last few days, I've been experimenting with Podman. My goal was to get a feel for the setup, create a minimal yet scalable environment for a few containers, and identify potential problems early on. Here are my notes from this experience. [Updates 2025-09-21] Added more networking options and other information. Quadlet Quadlet  allows you to define containers, networks and more using a syntax similar to systemd. This includes helpful features like drop-in overrides and templates. The framework is tightly integrated with systemd, and Quadlet actually generates real systemd units. This means I can directly write systemd options in my Quadlet files. One of the biggest benefits I've found is how easy Quadlet makes it to set up socket activation. This allows me to place some containers in an internal network or even without a network at all. Hardening Defaults Let's say I have a group of Systemd and Quadlet units, all named in the format of  xyz-* . My goal is to define ...

gVisor: A Fresh Look at Container Security

My original plan was to stabilize my  VM pipeline  before deploying containers using a hardened stack of Podman, QEMU, SELinux, and user namespaces ( --userns=auto ). However, the pipeline's complexity grew, requiring script rewrites and schema redesigns, and the process took much longer than anticipated. In the meantime, an interesting alternative has captured my attention:  gVisor . It occupies a unique space between traditional SELinux policies and full-blown virtual machines, offering a compelling set of trade-offs. What is gVisor? At its core, gVisor is an application kernel, written in the memory-safe language Go, that provides an additional layer of isolation between containerized applications and the host operating system. It's essentially a user-space implementation of the Linux kernel's system call interface. The security model is explained  here . gVisor in Practice gVisor provides an OCI-compliant runtime called  runsc , which can be almost transpare...

mkosi: First Impressions

I stumbled upon the Gentoo wiki page for systemd-nspawn , which in turn led me to nspawn.org , mkosi , and later systemd-sysupdate . mkosi quickly caught my eye because it's almost exactly what I wanted to build myself, as mentioned in a previous post . So, I decided to spend my "sysadmin fun quota" on it. Overview mkosi is similar to docker build or podman build , but it's designed for creating full OS images. It focuses on development and testing. For example, much like nix-shell , mkosi can quickly launch a sandboxed shell with a specific distribution and selected packages installed. The systemd project itself uses mkosi for testing across different distros. The re-introduction article  is a great read. Speed Note that this is by no means a rigid benchmark. My setup is an SSD with LUKS and an ext4 filesystem (without reflink support). Building Container Images mkosi is pretty fast. A simple mkosi command creates a fresh Debian image. I used the --incrementa...