The Consent Dongle: Zero‑Trust Client Servers with Fedora CoreOS

Updated September 9, 2026

I deploy Linux servers for clients many of which are not technical and I don’t want them to worry about learning technical concepts like SSH keys, firewall rules, or package managers. I want to deploy for them a server that works, stays secure, and doesn’t require them to think about it.

But they also deserve control over who can access their machine and confidence that their machine and data are their own.

This post describes the system I’ve built to give them exactly that: Fedora CoreOS, provisioned automatically from a USB stick, connected to me via WireGuard, and protected by a physical consent dongle—the same USB they used to install the OS.

When they plug in the USB, I can get in to provide support, when they unplug it, I’m locked out. Just a physical “key” to control access and a zero-trust system.


The Goal: Remote Access Without Compromising Client Privacy

I wanted to set up a system where:

  • SSH is not exposed to the internet: this is a constant attack target.
  • Port forwarding is not necessary: potentially confusing for clients and can bring in security concerns with open ports.
  • There is no permanent VPN access: I can only reach the client’s server when they give explicit permission.
  • The process is simple: the client simply boots from a USB drive and then uses that drive as a physical consent dongle.
  • Our connection is secure: I have an encrypted tunnel to the client for setup and support.
  • The system is reproducible: a single configuration file allows me to set up the entire system.

Fedora CoreOS and Wireguard made all of this possible.


The Process Overview

Here’s the architecture:

  1. The client receives or creates a custom USB stick containing a customized Fedora CoreOS installer.
  2. They boot from it. The OS installs itself automatically to their hard drive.
  3. The same USB becomes the consent dongle. After installation, the USB has only one job: to act as a physical key. Plug it in, and the WireGuard tunnel comes up. Unplug it, and the tunnel drops.
  4. I connect through WireGuard from my laptop and SSH in to provide installation, configuration, and support.
  5. CoreOS updates itself atomically in the background. If an update breaks something, the client can reboot and roll back easily.

The client never touches a terminal. They never configure a firewall. They never run updates. They just plug in a USB stick when they need my help.


Part 1: The WireGuard Server (Alpine Linux + Docker)

I run a WireGuard server on an Alpine Linux box in my own server. It acts as the hub for all client connections.

Setup

Assuming Docker is already running on your Alpine server:

# Install userspace tools
doas apk add wireguard-tools

# Load the kernel module
doas modprobe wireguard
echo wireguard | doas tee -a /etc/modules-load.d/wireguard.conf

Create the Docker Compose stack:

mkdir -p ~/docker/wireguard && cd ~/docker/wireguard
nano docker-compose.yml
services:
  wireguard:
    image: linuxserver/wireguard:latest
    container_name: wireguard
    cap_add:
      - NET_ADMIN
      - SYS_MODULE
    environment:
      - PUID=1000
      - PGID=1000
      - TZ=America/Edmonton #replace with your timezone
      - SERVERURL=my.domain.com #replace with your domain
      - SERVERPORT=51820
      - PEERS=99
      - PEERDNS=1.1.1.1 #replace with your choses dns server
      - INTERNAL_SUBNET=10.99.0.0/24
      - ALLOWEDIPS=0.0.0.0/0
    volumes:
      - ./config:/config
      - /lib/modules:/lib/modules:ro
    ports:
      - 51820:51820/udp
    sysctls:
      - net.ipv4.ip_forward=1
      - net.ipv4.conf.all.src_valid_mark=1
    restart: unless-stopped
    networks:
      - wg_net

networks:
  wg_net:
    driver: bridge

Start the container:

docker compose up -d

Restricting Client Access

I don’t want client servers to reach each other—or my own LAN. So I add iptables rules to isolate them.

Edit ~/docker/wireguard/config/wg_confs/wg0.conf and replace the PostUp/PostDown lines:

PostUp = iptables -I FORWARD 1 -i %i -m iprange --src-range 10.99.0.12-10.99.0.100 -d 10.99.0.1 -j ACCEPT ; iptables -I FORWARD 2 -i %i -m iprange --src-range 10.99.0.12-10.99.0.100 -d 10.99.0.0/24 -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT ; iptables -I FORWARD 3 -i %i -m iprange --src-range 10.99.0.12-10.99.0.100 -j DROP ; iptables -A FORWARD -i %i -j ACCEPT ; iptables -A FORWARD -o %i -j ACCEPT ; iptables -t nat -A POSTROUTING -o eth+ -j MASQUERADE
PostDown = iptables -D FORWARD -i %i -m iprange --src-range 10.99.0.12-10.99.0.100 -j DROP ; iptables -D FORWARD -i %i -m iprange --src-range 10.99.0.12-10.99.0.100 -d 10.99.0.0/24 -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT ; iptables -D FORWARD -i %i -m iprange --src-range 10.99.0.12-10.99.0.100 -d 10.99.0.1 -j ACCEPT ; iptables -D FORWARD -i %i -j ACCEPT ; iptables -D FORWARD -o %i -j ACCEPT ; iptables -t nat -D POSTROUTING -o eth+ -j MASQUERADE

Restart the container:

docker compose restart wireguard

Peer Access Tiers

  • peer1–peer10 (IPs 10.99.0.2–10.99.0.11): Admin peers. Full access to the network and other peers.
  • peer11–peer99 (IPs 10.99.0.12–10.99.0.100): Client servers. Can only reach the WireGuard server itself and accept connections initiated by admins.

This means even if a client server is compromised, it cannot reach my infrastructure or other clients.


Part 2: My Admin Laptop

My laptop is an admin peer with IP 10.99.0.3.

sudo dnf install wireguard-tools

I copy the generated peer config from the Alpine server:

scp user@<machine ip>:~/docker/wireguard/config/peer2/peer2.conf ~/
sudo mv ~/peer2.conf /etc/wireguard/wg0.conf
sudo chmod 600 /etc/wireguard/wg0.conf

Then connect:

sudo wg-quick up wg0
sudo wg show

And optionally enable auto‑start:

sudo systemctl enable wg-quick@wg0

Part 3: Building the Custom Fedora CoreOS ISO

This is the really cool part. I create a customized ISO that installs CoreOS automatically and configures everything before first boot.

Install Needed Tools

sudo dnf install butane coreos-installer mkpasswd

Generate WireGuard Keys for the Client

On my laptop:

wg genkey | tee client_private.key | wg pubkey > client_public.key
wg genpsk > client_preshared.key

Create the Butane Configuration

Here’s the template I use. Replace the placeholders with actual values.

variant: fcos
version: 1.7.0
passwd:
  users:
    - name: user1
      ssh_authorized_keys:
        - <ssh key>
      password_hash: <password hash>
      home_dir: /home/user1
      groups:
        - docker
        - wheel
      shell: /bin/bash

storage:
  files:
    - path: /etc/wireguard/wg0.conf
      mode: 0600
      contents:
        inline: |
          [Interface]
          Address = 10.99.0.12
          PrivateKey = CLIENT_PRIVATE_KEY
          ListenPort = 51820

          PostUp = iptables -I INPUT 1 -s 10.99.0.0/24 -j ACCEPT
          PreDown = iptables -D INPUT -s 10.99.0.0/24 -j ACCEPT

          [Peer]
          PublicKey = SERVER_PUBLIC_KEY
          PresharedKey = CLIENT_PRESHARED_KEY
          AllowedIPs = 10.99.0.1/32, 10.99.0.3/32
          Endpoint = MY.DOMAIN.COM:51820
          PersistentKeepalive = 25

    - path: /etc/udev/rules.d/99-wireguard-killswitch.rules
      mode: 0644
      contents:
        inline: |
          ACTION=="add", SUBSYSTEM=="block", KERNEL=="sd*[0-9]", ENV{ID_FS_LABEL}=="fedora-core*", RUN+="/usr/bin/systemctl start wg-quick@wg0"
          ACTION=="remove", SUBSYSTEM=="block", KERNEL=="sd*[0-9]", ENV{ID_FS_LABEL}=="fedora-core*", RUN+="/usr/bin/systemctl stop wg-quick@wg0"

systemd:
  units:
    - name: wg-quick@wg0.service
      enabled: false

Convert and Customize the ISO

butane --pretty --strict config.bu > config.ign

wget https://builds.coreos.fedoraproject.org/prod/streams/stable/builds/44.20260802.3.1/x86_64/fedora-coreos-44.20260802.3.1-live-iso.x86_64.iso #replace with current latest iso

coreos-installer iso customize \
    --dest-device /dev/sda \
    --dest-ignition config.ign \
    --dest-console ttyS0,115200n8 \
    --dest-console tty0 \
    -o clientinstall.iso fedora-coreos-44.20260802.3.1-live-iso.x86_64.iso #replace with downloaded iso

The resulting clientinstall.iso will wipe /dev/sda and install CoreOS automatically when booted. If client needs a different disk, adjust accordingly.

Warning: The customized ISO will automatically wipe the disk specified in the Butane file. Make sure the target machine doesn’t have any other important data on that disk.


The client receives or creates the USB stick with instructions:

“Plug this into your server and turn it on. Wait for it to finish. When you need my help, plug it in again and tell me. If it’s plugged in, I have access—if it’s unplugged I do not.”

That’s the entire user manual.

After Installation

Once CoreOS is installed and the machine reboots, the USB is no longer bootable—but it’s still the consent key.

  • Plugged in: The udev rule starts WireGuard. I can SSH in from my laptop.
  • Unplugged: WireGuard stops. Access is severed immediately.

The client holds physical control over my access. That’s the zero‑trust model.

I do this on first login to ensure the USB can never accidentally reinstall the OS:

lsblk -o NAME,LABEL,SIZE,MODEL

Identify the USB, then:

sudo fdisk /dev/sdX
# Create new DOS partition table, new primary partition, write
sudo mkfs.vfat -I -F 32 -n "fedora-core" /dev/sdX

The label fedora-core matches the udev rule, so the drive still works as the consent dongle—but it can no longer boot.


Part 5: Security Considerations

This system is built on a few key principles:

1. Zero Trust

The client doesn’t have to trust me to “do the right thing.” They hold the physical key. No USB plugged in, no access. Period.

2. Minimal Attack Surface

The server has:

  • No open ports (WireGuard connects out)
  • No public exposure of any kind
  • A minimal, locked-down core OS that updates atomically with minimal packages and a container-first design

3. Segmented Access

Client servers can’t reach each other or my LAN. Even if one is compromised, the rest are isolated.

4. Reproducibility

The entire system is defined in a Butane file and a customized ISO. If the server dies or the USB is lost, I can recreate everything in minutes.


Part 6: What If Something Goes Wrong?

I walk the client through creating a new one. Any USB with a label starting fedora-core will work as a consent key once CoreOS is installed.

An update breaks something

Fedora CoreOS keeps the previous image by design. The client reboots and selects the previous image from the boot menu. No complicated recovery process.

The client wants to revoke my access permanently

They unplug the USB and throw it away. That’s it. I no longer have access of any kind.


Final Thoughts

This system gives my clients something important: security without complexity. They don’t need to understand SSH, firewalls, or VPNs. They need to understand one thing: “Plug in the USB when I need help.”

And when they unplug it, they know—with absolute certainty—that I’m locked out.

All together it creates a server that is simple, secure, and private.


Want a Hand With Your Setup?
This is exactly the kind of system I build for my clients. If you’d like a zero‑trust server like this—or help with any Linux project—take a look at my support plans.
Take a look at my support plans →



Related Posts

Tracking Cookies: What Are They and Should You Be Concerned?

Tracking Cookies: What Are They and Should You Be Concerned?

You may have come across the term internet cookies or tracking cookies especially with a recent focus on online privacy, online security, and concerns with being tracked. Well what are cookies?

Read More
Choosing a Linux Distro: Server Edition

Choosing a Linux Distro: Server Edition

Updated August 22, 2026

So, you’ve decided to build a home server—a reliable, always-on machine for media streaming, hosting services, or running containers. Choosing which distro to use can be a difficult decision with so many options, each with its own set of advantages and trade-offs.

Read More
The Cuts: Excluded Distros I Left Out (And Why)

The Cuts: Excluded Distros I Left Out (And Why)

Updated September 23, 2026

My main guide, “How to Choose a Linux Distro: The Complete Guide”, is intentionally tight.
Every distribution that made the final cut earned its place by filling a distinct niche, being actively maintained, and providing a reliable experience—especially for newcomers.

Read More