Security Hardening

Quick Reference

# Check encrypted volumes
lsblk -o NAME,SIZE,FSTYPE,MOUNTPOINT
dmsetup status

# SSH key types
ssh-keygen -t ed25519 -f ~/.ssh/id_ed25519_github -C "github"

# Secrets management
gopass list
gopass show path/to/secret

Disk Encryption Strategy

Multi-Volume LUKS Architecture

Separate encrypted volumes provide security segmentation:

Volume Size Contains Rationale

cryptroot

~250GB

System files, packages

OS can be wiped/reinstalled independently

crypthome

Remainder

User data, configs

Survives OS reinstalls

cryptdata

Separate drive

VMs, large data

Independent drive, separate passphrase option

Partition Layout

# Recommended layout (UEFI)
nvme0n1           # System drive
├─nvme0n1p1  512M  vfat   /boot/efi    # EFI System Partition
├─nvme0n1p2  2G    ext4   /boot        # Unencrypted boot
├─nvme0n1p3  250G  LUKS   cryptroot    # Encrypted root
└─nvme0n1p4  REST  LUKS   crypthome    # Encrypted home

nvme1n1           # Data drive (optional)
└─nvme1n1p1  ALL  LUKS   cryptdata     # Encrypted data

Creating LUKS Volumes

# Create LUKS2 container (root)
cryptsetup luksFormat --type luks2 \
    --cipher aes-xts-plain64 \
    --key-size 512 \
    --hash sha512 \
    --iter-time 5000 \
    /dev/nvme0n1p3

# Create LUKS2 container (home)
cryptsetup luksFormat --type luks2 /dev/nvme0n1p4

# Open volumes
cryptsetup open /dev/nvme0n1p3 cryptroot
cryptsetup open /dev/nvme0n1p4 crypthome

# Verify
ls /dev/mapper/
# cryptroot  crypthome

LUKS Best Practices

# Use strong passphrases (diceware recommended)
# Example: "correct-horse-battery-staple-quantum"

# Backup LUKS header (CRITICAL)
cryptsetup luksHeaderBackup /dev/nvme0n1p3 \
    --header-backup-file /secure/location/cryptroot.header

# Add backup key (recovery)
cryptsetup luksAddKey /dev/nvme0n1p3

# Check LUKS status
cryptsetup luksDump /dev/nvme0n1p3

# Benchmark encryption (choose best cipher)
cryptsetup benchmark

Btrfs Subvolume Architecture

Subvolume Strategy

Subvolume Mount Point Purpose

@

/

Root filesystem

@home

/home

User data (separate LUKS)

@snapshots

/.snapshots

Snapper snapshots

@var_log

/var/log

Logs (exclude from snapshots)

@data

/data

Large files, media

@vms

/var/lib/libvirt/images

VM disk images (CoW disabled)

Creating Subvolumes

# Mount root volume
mount /dev/mapper/cryptroot /mnt

# Create subvolumes
btrfs subvolume create /mnt/@
btrfs subvolume create /mnt/@snapshots
btrfs subvolume create /mnt/@var_log

# Unmount and remount with subvolume
umount /mnt
mount -o subvol=/@ /dev/mapper/cryptroot /mnt

# Create mount points
mkdir -p /mnt/{.snapshots,var/log,home,boot,boot/efi,data}

# Mount other subvolumes
mount -o subvol=/@snapshots /dev/mapper/cryptroot /mnt/.snapshots
mount -o subvol=/@var_log /dev/mapper/cryptroot /mnt/var/log

Optimal Mount Options

# /etc/fstab entries
# Root
UUID=xxxx-xxxx  /           btrfs  rw,noatime,compress=zstd:3,ssd,discard=async,space_cache=v2,subvol=/@          0 0

# Snapshots
UUID=xxxx-xxxx  /.snapshots btrfs  rw,noatime,compress=zstd:3,ssd,discard=async,space_cache=v2,subvol=/@snapshots 0 0

# Var log
UUID=xxxx-xxxx  /var/log    btrfs  rw,noatime,compress=zstd:3,ssd,discard=async,space_cache=v2,subvol=/@var_log   0 0

# Home (separate LUKS)
UUID=yyyy-yyyy  /home       btrfs  rw,noatime,compress=zstd:3,ssd,discard=async,space_cache=v2,subvol=/@home      0 0

Mount option explanations:

  • noatime - Don’t update access times (performance)

  • compress=zstd:3 - Transparent compression (good balance)

  • ssd - SSD optimizations

  • discard=async - Background TRIM

  • space_cache=v2 - Improved space allocation tracking

Disable CoW for VMs

# VM images don't benefit from CoW
chattr +C /var/lib/libvirt/images

# Or for existing directory with files
mkdir /var/lib/libvirt/images-new
chattr +C /var/lib/libvirt/images-new
mv /var/lib/libvirt/images/* /var/lib/libvirt/images-new/
rmdir /var/lib/libvirt/images
mv /var/lib/libvirt/images-new /var/lib/libvirt/images

Boot Configuration for Encryption

mkinitcpio Hooks

# /etc/mkinitcpio.conf
HOOKS=(base udev autodetect modconf kms keyboard keymap consolefont block encrypt btrfs filesystems fsck)

Hook order matters: * keyboard and keymap before encrypt - for password entry * encrypt before btrfs and filesystems - decrypt before mount

Kernel Parameters

# /boot/loader/entries/arch.conf (systemd-boot)
title   Arch Linux
linux   /vmlinuz-linux
initrd  /initramfs-linux.img
options cryptdevice=UUID=xxxx-xxxx:cryptroot root=/dev/mapper/cryptroot rootflags=subvol=/@ rw

# For multiple encrypted volumes, use /etc/crypttab
# /etc/crypttab
crypthome  UUID=yyyy-yyyy  none  luks
cryptdata  UUID=zzzz-zzzz  none  luks

Rebuild Initramfs

# After changing mkinitcpio.conf
sudo mkinitcpio -P

SSH Hardening

Post-Quantum Key Exchange

Modern SSH supports hybrid post-quantum cryptography:

# ~/.ssh/config (global defaults)
Host *
    # Post-quantum + classical hybrid KEX
    KexAlgorithms mlkem768x25519-sha256,sntrup761x25519-sha512@openssh.com,curve25519-sha256,curve25519-sha256@libssh.org

    # Strong ciphers only
    Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com,aes128-gcm@openssh.com

    # Encrypt-then-MAC only
    MACs hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.com,umac-128-etm@openssh.com

    # Host key algorithms
    HostKeyAlgorithms ssh-ed25519-cert-v01@openssh.com,ssh-ed25519,rsa-sha2-512,rsa-sha2-256
    PubkeyAcceptedAlgorithms ssh-ed25519-cert-v01@openssh.com,ssh-ed25519,rsa-sha2-512,rsa-sha2-256

    # Connection settings
    AddKeysToAgent yes
    IdentitiesOnly yes
    ServerAliveInterval 60
    ServerAliveCountMax 3

Key exchange algorithms explained: * mlkem768x25519-sha256 - ML-KEM (Kyber) post-quantum + X25519 hybrid * sntrup761x25519-sha512 - NTRU Prime post-quantum + X25519 hybrid * curve25519-sha256 - Classical fallback

Per-Service SSH Keys

Create separate keys for each service:

# Generate keys for each service
ssh-keygen -t ed25519 -f ~/.ssh/id_ed25519_github -C "github-$(hostname)"
ssh-keygen -t ed25519 -f ~/.ssh/id_ed25519_gitlab -C "gitlab-$(hostname)"
ssh-keygen -t ed25519 -f ~/.ssh/id_ed25519_gitea -C "gitea-$(hostname)"
ssh-keygen -t ed25519 -f ~/.ssh/id_ed25519_codeberg -C "codeberg-$(hostname)"
ssh-keygen -t ed25519 -f ~/.ssh/id_ed25519_bitbucket -C "bitbucket-$(hostname)"

# Directory structure
~/.ssh/
├── config
├── id_ed25519_github
├── id_ed25519_github.pub
├── id_ed25519_gitlab
├── id_ed25519_gitlab.pub
├── id_ed25519_gitea
├── id_ed25519_gitea.pub
├── id_ed25519_codeberg
├── id_ed25519_codeberg.pub
└── known_hosts

Host-Specific Configuration

# ~/.ssh/config

# === Git Services ===
Host github.com
    HostName github.com
    User git
    IdentityFile ~/.ssh/id_ed25519_github

Host gitlab.com
    HostName gitlab.com
    User git
    IdentityFile ~/.ssh/id_ed25519_gitlab

Host codeberg.org
    HostName codeberg.org
    User git
    IdentityFile ~/.ssh/id_ed25519_codeberg

Host gitea.example.com
    HostName gitea.example.com
    User git
    IdentityFile ~/.ssh/id_ed25519_gitea

# === Infrastructure ===
Host bastion
    HostName bastion.example.com
    User admin
    IdentityFile ~/.ssh/id_ed25519_infra
    ProxyJump none

Host internal-*
    User admin
    IdentityFile ~/.ssh/id_ed25519_infra
    ProxyJump bastion

# === Connection Multiplexing ===
Host *
    ControlMaster auto
    ControlPath ~/.ssh/sockets/%r@%h-%p
    ControlPersist 600
# Create socket directory
mkdir -p ~/.ssh/sockets
chmod 700 ~/.ssh/sockets

FIDO2/Hardware Key Support

# Generate resident key (stored on YubiKey)
ssh-keygen -t ed25519-sk -O resident -O verify-required \
    -f ~/.ssh/id_ed25519_sk_rk -C "yubikey-$(hostname)"

# Generate non-resident key (faster, key handle on disk)
ssh-keygen -t ed25519-sk -f ~/.ssh/id_ed25519_sk -C "yubikey-$(hostname)"

# Discover resident keys from hardware token
ssh-keygen -K

# Use in config
Host secure-server
    IdentityFile ~/.ssh/id_ed25519_sk_rk

SSH Permissions

# Fix permissions
chmod 700 ~/.ssh
chmod 600 ~/.ssh/config
chmod 600 ~/.ssh/id_*
chmod 644 ~/.ssh/*.pub
chmod 644 ~/.ssh/known_hosts

Secrets Management

gopass (Password Store)

# Install
sudo pacman -S gopass

# Initialize (uses GPG)
gopass init

# Or with age encryption
gopass init --crypto age

# Clone existing store
gopass clone git@github.com:user/password-store.git

Multi-Store Organization

# Structure secrets by domain
gopass/
├── personal/          # Personal accounts
│   ├── email/
│   ├── banking/
│   └── social/
├── work/              # Work credentials
│   ├── vpn
│   └── servers/
├── infra/             # Infrastructure secrets
│   ├── ssh/
│   ├── api-keys/
│   └── certificates/
└── wifi/              # WiFi passwords
    ├── home
    └── office

gopass Commands

# List all secrets
gopass list

# Show secret (copies to clipboard)
gopass show -c path/to/secret

# Insert new secret
gopass insert path/to/secret

# Generate password
gopass generate path/to/secret 32
gopass generate -s path/to/secret 32  # With symbols

# Edit secret
gopass edit path/to/secret

# Search
gopass find keyword
gopass search keyword

# Sync with git
gopass sync

Multiple Stores (Mounts)

# Add additional store
gopass mounts add work /path/to/work-store

# List mounts
gopass mounts

# Access mounted store
gopass show work/vpn/credentials

pass Alternative

# Standard Unix password store
sudo pacman -S pass

# Initialize
pass init GPG_ID

# Insert
pass insert email/work

# Show
pass show email/work

# Generate
pass generate -c email/new 20

# Tree view
pass

age-based Alternatives

# passage - pass with age encryption
yay -S passage-git

# Initialize
PASSAGE_DIR=~/.passage passage init

# Use like pass
passage insert secret
passage show secret

File Encryption

age (Simple Encryption)

# Install
sudo pacman -S age

# Generate key
age-keygen -o key.txt

# Encrypt with public key
age -r age1... -o secret.age secret.txt

# Encrypt with passphrase
age -p -o secret.age secret.txt

# Decrypt
age -d -i key.txt -o secret.txt secret.age
age -d -o secret.txt secret.age  # Passphrase prompt

GPG Encryption

# Symmetric encryption
gpg -c file.txt                    # Creates file.txt.gpg

# Asymmetric encryption
gpg -e -r recipient@email.com file.txt

# Decrypt
gpg -d file.txt.gpg > file.txt

# Sign file
gpg --sign file.txt
gpg --verify file.txt.gpg

Snapshot Security

Snapper for Btrfs

# Install
sudo pacman -S snapper snap-pac

# Create config for root
sudo snapper -c root create-config /

# Configure timeline
sudo nvim /etc/snapper/configs/root
# /etc/snapper/configs/root (key settings)
TIMELINE_CREATE="yes"
TIMELINE_CLEANUP="yes"
TIMELINE_MIN_AGE="1800"
TIMELINE_LIMIT_HOURLY="5"
TIMELINE_LIMIT_DAILY="7"
TIMELINE_LIMIT_WEEKLY="0"
TIMELINE_LIMIT_MONTHLY="0"
TIMELINE_LIMIT_YEARLY="0"
# Enable timers
sudo systemctl enable --now snapper-timeline.timer
sudo systemctl enable --now snapper-cleanup.timer

# List snapshots
sudo snapper -c root list

# Create manual snapshot
sudo snapper -c root create -d "before update"

# Rollback (from live USB)
sudo snapper -c root undochange 1..5

snap-pac Integration

# Automatically creates snapshots on pacman operations
sudo pacman -S snap-pac

# Before/after snapshots on every install/upgrade
# View with:
sudo snapper -c root list

Audit and Monitoring

Security Audit

# Lynis security audit
sudo pacman -S lynis
sudo lynis audit system

# Rootkit detection
sudo pacman -S rkhunter
sudo rkhunter --update
sudo rkhunter --check

# Check for weak permissions
find / -perm -4000 2>/dev/null  # SUID files
find / -perm -2000 2>/dev/null  # SGID files

Log Monitoring

# Failed login attempts
journalctl _SYSTEMD_UNIT=sshd.service | grep -i fail

# Authentication logs
journalctl -u systemd-logind --since today

# Kernel security messages
dmesg | grep -i security

Quick Reference

# === LUKS ===
cryptsetup luksFormat /dev/sdX          # Create
cryptsetup open /dev/sdX name           # Open
cryptsetup close name                   # Close
cryptsetup luksDump /dev/sdX            # Info
cryptsetup luksHeaderBackup /dev/sdX --header-backup-file backup.img

# === Btrfs ===
btrfs subvolume list /                  # List subvolumes
btrfs subvolume create /mnt/@name       # Create
btrfs subvolume delete /mnt/@name       # Delete
btrfs filesystem df /                   # Usage

# === SSH ===
ssh-keygen -t ed25519 -f ~/.ssh/key     # Generate key
ssh-keygen -t ed25519-sk -O resident    # Hardware key
ssh-add -l                              # List loaded keys
ssh-add ~/.ssh/key                      # Add key to agent

# === Secrets ===
gopass list                             # List secrets
gopass show -c path/to/secret           # Copy to clipboard
gopass generate path 32                 # Generate password

# === Snapshots ===
snapper -c root list                    # List snapshots
snapper -c root create -d "description" # Create snapshot
snapper -c root delete 5                # Delete snapshot

# === Audit ===
sudo lynis audit system                 # Security audit
sudo rkhunter --check                   # Rootkit check