Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Index

This is my practical knowledge base. The intended audience is mostly me, but some notes are written in a tutorial-like format.

I can make no guarantees that any of the content here will work for others. If you break things after following something here, you’re the one responsible.

Anyway, you may find something useful here after all. But still, reader beware 🤨.

Apache

Install mod-evasive

sudo apt install libapache2-mod-evasive

Add mod_evasive to VirtualHost

<IfModule mod_evasive20.c>
    DOSHashTableSize 3097
    DOSPageCount 2
    DOSSiteCount 60
    DOSPageInterval 1
    DOSSiteInterval 1
    DOSBlockingPeriod 60
    DOSEmailNotify <hyperreal@moonshadow.dev
</IfModule>

Restart apache2.

sudo systemctl restart apache2.service

AppArmor

Setup AppArmor on CachyOS

Install apparmor.

sudo pacman -S apparmor apparmor.d

Add the following line to /etc/limine-entry-tool.conf. This should enable AppArmor and auditd at boot time.

KERNEL_CMDLINE[default]+=lsm=landlock,lockdown,yama,integrity,apparmor,bpf audit=1

Update the limine config.

sudo limine-update

Enable apparmor.service and auditd.service on boot.

sudo systemctl enable apparmor.service
sudo systemctl enable auditd.service

Reboot.

Check to see if AppArmor is enabled.

aa-enabled

Arch Linux

Pacman cheatsheet

ActionCommand
Clean pacman cachesudo pacman -Sc
Upgrade specific package(s)sudo pacman -U <pkg0> <pkg1> ... <pkgN>
Remove all files from pacman cachesudo pacman -Scc
Remove specific packagessudo pacman -R <pkg0> <pkg1> ... <pkgN>
Display info on a given sync db packagesudo pacman -Si <pkg>
Search sync db for names or desc matching regexpsudo pacman -Ss <regexp>
List all locally installed native packagessudo pacman -Qn
List all locally installed foreign packagessudo pacman -Qm
Display info on a given packagesudo pacman -Qi <pkg>
Search locally installed packages for name/descsudo pacman -Qs <regexp>
Refresh all package databasessudo pacman -Syy
List orphan packagessudo pacman -Qdt
Remove orphan packagessudo pacman -Rs $(pacman -Qtdq)
Refresh the package file databasessudo pacman -Fy
Query the files database for given packagesudo pacman -F <pkg>
List all files owned by given packagesudo pacman -Ql <pkg>
Search for packages that own given filesudo pacman -Qo <file>

Armbian

Disable armbian-ramlog

Edit /etc/default/armbian-ramlog:

ENABLED=false

Comment out the entry in /etc/cron.d/armbian-truncate-logs:

#  0,15,30,45 * * * * root /usr/lib/armbian/armbian-truncate-logs

Comment out the entry in /etc/cron.daily/armbian-ram-logging:

#/usr/lib/armbian/armbian-ramlog write >/dev/null 2>&1

Restart the server:

sudo systemctl reboot

Atop

Get lowest memfree for given analysis date

atopsar \
    -r /var/log/atop/atop_20240703 \
    -m \
    -R 1 \
    | awk 'NR<7{print $0;next}{print $0| "sort -k 3,4"}' \
    | head -11

Get top 3 mem procs for given analysis date

atopsar \
    -G \
    -r /var/log/atop/atop_20240710

Identify top 5 most frequently executed procs during logging period

atop \
    -r /var/log/atop/atop_20241123 \
    -P PRG \
    | grep -oP "(?<=\()[[:alnum:]]{1,}(?=\))" \
    | sort \
    | uniq -c \
    | sort -k1rn \
    | head -5

Count num of times a proc has been detected during logging period

atop \
    -r /var/log/atop/atop_20241123 \
    -P PRG \
    | egrep "docker" \
    | awk '{print $5}' \
    | uniq -c -w5

Generate a chart of the num of instances of proc during logging period

atop \
    -r /var/log/atop/atop_20241123 \
    -P PRG \
    | egrep "docker" \
    | awk '{print $5}' \
    | uniq -c -w8 \
    | \
    gnuplot -e \
        "set terminal dumb 80 20; \
        unset key; \
        set style data labels; \
        set xdata time; \
        set xlabel 'Time'; \
        set ylabel 'docker'; \
        set timefmt '%H:%M:%S'; \
        plot '-' using 2:1:ytic(1) with histeps"

Generate PNG chart of num of instances of proc during logging period

atop \
    -r /var/log/atop/atop_20241123 \
    -P PRG \
    | egrep "docker" \
    | awk '{print $5}' \
    | uniq -c -w8 \
    | \
    gnuplot -e \
        "set title 'Process Count'; \
        set offset 1,1,1,1; \
        set autoscale xy; \
        set mxtics; \
        set mytics; \
        set style line 12 lc rgb '#ddccdd' lt 1 lw 1.5; \
        set style line 13 lc rgb '#ddccdd' lt 1 lw 0.5; \
        set grid xtics mxtics ytics mytics \
        back ls 12, ls 13; \
        set terminal png size 1920,1080 enhanced font \
        '/usr/share/fonts/liberation/LiberationSans-Regular.ttf,10'; \
        set output 'plot_$(date '+%Y-%m-%d_%H:%M:%S')_${RANDOM}.png'; \
        set style data labels; \
        set xdata time;
        set xlabel 'Time' font \
        '/usr/share/fonts/liberation/LiberationSans-Regular.ttf,8'; \
        set ylabel 'Count' font \
        '/usr/share/fonts/liberation/LiberationSans-Regular.ttf,8'; \
        set timefmt '%H:%M:%S'; \
        plot '-' using 2:1 with histeps"

Identify top 10 most frequently executed binaries from /sbin or /usr/sbin during logging period

for i in $(atop \
    -r /var/log/atop/atop_20241123 \
    -P PRG \
    | grep -oP "(?<=\()[[:alnum:]]{1,}(?=\))" \
    | sort \
    | uniq -c \
    | sort -k1rn \
    | head -10); do
    which "${i}" 2>/dev/null | grep sbin;
done

Identify disks with over 90% activity during logging period

atopsar \
    -r /var/log/atop/atop_20241123 \
    -d \
    | egrep '^[0-9].*|(9[0-9]|[0-9]{3,})%'

Identify procs responsible for most disk I/O during logging period

atopsar \
    -r /var/log/atop/atop_20241123 \
    -D \
    | sed 's/\%//g' \
    | awk -v k=50 '$4 > k || $8 > k || $12 > k' \
    | sed -r 's/([0-9]{1,})/%/5;s/([0-9]{1,})/%/7;s/([0-9]{1,})/%/9'

Identify periods of heavy swap activity during logging period

atopsar \
    -r /var/log/atop/atop_20241123 \
    -s \
    | awk -v k=1000 '$2 > k || $3 > k || $4 > k'

Identify procs consuming > half of all CPUs

(( k = $(grep -c proc /proc/cpuinfo) / 2 * 100 ))
atopsar \
    -r /var/log/atop/atop_20241123 \
    -P \
    | sed 's/\%//g' \
    | awk -v k=$k '$4 > k || $8 > k || $12 > k' \
    | sed -r 's/([0-9]{1,})/%/5;s/([0-9]{1,})/%/7;s/([0-9]{1,})/%/9'

Identify time of peak mem utilization

atopsar \
    -r /var/log/atop/atop_20241123 \
    -m \
    -R 1 \
    | awk 'NR<7{print $0;next}{print $0| "sort -k 3,3"}' \
    | head -15

Bash

sed 'Nq;d' file.txt

Prepend text to beginning of file

echo "new content" | cat - file.txt > temp
mv temp file.txt
sed -i '1s/^/new content\n/' file.txt

Remove lines containing only whitespace

sed -i '/^\s*$/d' file.txt

Delete nth line from file

# N is the target line
sed -i 'Nd' file.txt

Replace an entire line in file by line number

# N is the target line
sed -i 'Ns/.*/replacement-line/' file.txt

Heredoc

cat << EOF > file.txt
The current working directory is $PWD.
Your are logged in as $(whoami).
EOF

Plain-print the difference between two files

Suppose we have two files: packages.fedora and packages.

packages.fedora

autossh
bash-completion
bat
bc
borgmatic
bzip2
cmake
curl
diff-so-fancy
diffutils
dnf-plugins-core

packages

bash-completion
bc
bzip2
curl
diffutils
dnf-plugins-core

To plain-print the lines that exist in packages.fedora but not in packages:

comm -23 <(sort packages.fedora) <(sort packages)

Output:

autossh
bat
borgmatic
cmake
diff-so-fancy
  • The comm command compares two sorted files line by line.
  • The -23 flag is shorthand for -2 and -3.
  • -2 suppresses column 2 (lines unique to packages).
  • -3 suppresses column 3 (lines that appear in both files).

Split large text file into smaller files with equal number of lines

split -l 60 bigfile.txt prefix-

Loop through lines of file

while read line; do
    echo "$line";
done </path/to/file.txt

Use grep to find URLs from HTML file

cat urls.html | grep -Eo "(http|https)://[a-zA-Z0-9./?=_%:-]*"

Use Awk to print the first line of ps aux output followed by each grepped line

To find all cron processes with ps aux:

ps aux | awk 'NR<2{print $0;next}{print $0 | grep "cron"}' | grep -v "awk"

Btrfs

Create systemd.mount unit for Btrfs on external HDD

Get the UUID of the Btrfs partition:

sudo blkid -s UUID -o value /dev/sda1

d3b5b724-a57a-49a5-ad1d-13ccf3acc52f

Edit /etc/systemd/system/mnt-internet_archive.mount:

[Unit]
Description=internet_archive Btrfs subvolume
DefaultDependencies=yes

[Mount]
What=/dev/disk/by-uuid/d3b5b724-a57a-49a5-ad1d-13ccf3acc52f
Where=/mnt/internet_archive
Type=btrfs
Options=subvol=@internet_archive,compress=zstd:1

[Install]
WantedBy=multi-user.target

Note that the name of the unit file, e.g., mnt-internet_archive.mount, must correspond to the Where=/mnt/internet_archive directive, such that the filesystem path separator / in the Where directive is replaced by an en dash in the unit file name.

Reload the daemons and enable the mount unit:

sudo systemctl daemon-reload
sudo systemctl enable --now mnt-internet_archive.mount

Setup encrypted external drive for backups

Prepare the external drive

sudo cryptsetup --type luks2 -y -v luksFormat /dev/sda1
sudo cryptsetup -v luksOpen /dev/sda1 cryptbackup
sudo mkfs.btrfs /dev/mapper/cryptbackup
sudo mkdir /srv/backup
sudo mount -o noatime,compress=zstd:1 /dev/mapper/cryptbackup /srv/backup
sudo restorecon -Rv /srv/backup

Setup /etc/crypttab

sudo blkid -s UUID -o value /dev/sda1 | sudo tee -a /etc/crypttab

Add the following line to /etc/crypttab:

cryptbackup UUID=<UUID of /dev/sda1> none discard

Setup /etc/fstab

sudo blkid -s UUID -o value /dev/mapper/cryptbackup | sudo tee -a /etc/fstab

Add the following line to /etc/fstab:

UUID=<UUID of /dev/mapper/cryptbackup> /srv/backup btrfs compress=zstd:1,nofail 0 0

Reload the daemons:

sudo systemctl daemon-reload

Mount the filesystems:

sudo mount -av

Btrfs backup script

#!/usr/bin/env bash

LOGFILE="/var/log/btrfs-backup.log"
SNAP_DATE=$(date '+%Y-%m-%d_%H%M%S')

# Check if device is mounted
if ! grep "/srv/backup" /etc/mtab >/dev/null; then
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] Backup device is not mounted." | tee -a "$LOGFILE"
    notify-send -i computer-fail "Backup device is not mounted"
    exit 1
fi

create_snapshot() {
    if ! btrfs subvolume snapshot -r "$1" "${1}/.snapshots/$2-$SNAP_DATE" >/dev/null; then
        echo "[$(date '+%Y-%m-%d %H:%M:%S')] Error creating snapshot of $1" | tee -a "$LOGFILE"
        notify-send -i computer-fail "Error creating snapshot of $1"
        exit 1
    else
        echo "[$(date '+%Y-%m-%d %H:%M:%S')] Create snapshot of $1: OK" | tee -a "$LOGFILE"
    fi
}

send_snapshot() {
    mkdir -p "/srv/backup/$SNAP_DATE"
    if ! btrfs send -q "${1}/.snapshots/$2-$SNAP_DATE" | btrfs receive -q "/srv/backup/$SNAP_DATE"; then
        echo "[$(date '+%Y-%m-%d %H:%M:%S')] Error sending snapshot of $1 to /srv/backup" | tee -a "$LOGFILE"
        notify-send -i computer-fail "Error sending snapshot of $1 to /srv/backup"
        exit 1
    else
        echo "[$(date '+%Y-%m-%d %H:%M:%S')] Send snapshot of $1 to /srv/backup: OK" | tee -a "$LOGFILE"
    fi
}

# Create root and home snapshots
create_snapshot "/" "root"
create_snapshot "/home" "home"

# Send root and home snapshots
send_snapshot "/" "root"
send_snapshot "/home" "home"

Move/copy the script to /etc/cron.daily/btrfs-backup.

CachyOS

Gaming

Resources:

DLSS

Edit /etc/environment.

PROTON_DLSS_UPGRADE=1
PROTON_DLSS_INDICATOR=1

Fix possible RTX 4000+ performance issues

Edit /etc/environment.

PROTON_NVIDIA_LIBS_NO_32BIT=1

Increase maximum shader cache size (NVIDIA)

Edit /etc/environment.

__GL_SHADER_DISK_CACHE_SIZE=12000000000

Reboot the system.

scx_bpfland

Source: https://wiki.cachyos.org/configuration/sched-ext

Edit /etc/scx_loader.toml.

default_sched = "scx_bpfland" # Edit this line to the scheduler you want scx_loader to start at boot
default_mode = "Auto" # Possible values: "Auto", "Gaming", "LowLatency", "PowerSave".

Enable or restart the scx_loader.service.

sudo systemctl enable --now scx_loader.service
sudo systemctl restart scx_loader.service

Caddy

IP whitelist

irc.hyperreal.coffee {
    @me {
    client_ip 1.2.3.4
    }
    handle @me {
    reverse_proxy localhost:9000
    }
    respond "You are attempting to access protected resources!" 403
}

Reverse proxy for qBittorrent over Tailscale

hostname.tailnet.ts.net:8888 {
    reverse_proxy localhost:8080 {
    header_up Host localhost:8080
    header_up X-Forwarded-Host {host}:{hostport}
    header_up -Origin
    header_up -Referer
    }
}

Cgit

Dependencies

  • xcaddy package from releases page.

Install caddy-cgi.

xcaddy build --with github.com/aksdb/caddy-cgi/v2

Install remaining dependencies:

sudo apt install gitolite3 cgit python-is-python3 python3-pygments python3-markdown docutils-common groff

Configuration

Make a git user.

sudo adduser --system --shell /bin/bash --group --disabled-password --home /home/git git

Configure gitolite for the git user in ~/.gitolite.rc.

UMASK => 0027,
GIT_CONFIG_KEYS => 'gitweb.description gitweb.owner gitweb.homepage gitweb.category',

Add caddy user to the git group.

sudo usermod -aG git caddy

Configure cgit in /etc/cgitrc.

#
# cgit config
# see cgitrc(5) for details

css=/cgit/cgit.css
logo=/cgit/cgit.png
favicon=/cgit/favicon.ico

enable-index-links=1
enable-commit-graph=1
enable-log-filecount=1
enable-log-linecount=1
enable-git-config=1

branch-sort=age
repository-sort=name

clone-url=https://git.hyperreal.coffee/$CGIT_REPO_URL git://git.hyperreal.coffee/$CGIT_REPO_URL ssh://git@git.hyperreal.coffee:$CGIT_REPO_URL

root-title=hyperreal.coffee Git repositories
root-desc=Source code and configs for my projects

##
## List of common mimetypes
##
mimetype.gif=image/gif
mimetype.html=text/html
mimetype.jpg=image/jpeg
mimetype.jpeg=image/jpeg
mimetype.pdf=application/pdf
mimetype.png=image/png
mimetype.svg=image/svg+xml

# Enable syntax highlighting
source-filter=/usr/lib/cgit/filters/syntax-highlighting.py

# Format markdown, rst, manpages, text files, html files, and org files.
about-filter=/usr/lib/cgit/filters/about-formatting.sh

##
### Search for these files in the root of the default branch of repositories
### for coming up with the about page:
##
readme=:README.md
readme=:README.org

robots=noindex, nofollow

section=personal-config

repo.url=doom-emacs-config
repo.path=/home/git/repositories/doom-emacs-config.git
repo.desc=My Doom Emacs config

DietPi

systemd-logind

Install libpam-systemd:

sudo apt install -y libpam-systemd

Unmask and enable systemd-logind:

sudo systemctl unmask systemd-logind
sudo systemctl enable systemd-logind
sudo systemctl reboot

DRM

nix-shell

Install libgourou in nix-shell.

nix-shell -p libgourou

Docker

docker run \
    -v "${PATH_TO_ADOBE_CREDS}:/home/libgourou/.adept" \
    -v "$(pwd):/home/libgourou/files" \
    --rm \
    bcliang/docker-libgourou \
    <name_of_adept_metafile.acsm>

Extract PDF or EPUB from ACSM file

Register the device with Adobe username and password.

adept_activate -u user -p password

Download the ACSM file. Make sure the ACSM file is in the current working directory.

acsmdownloader -f Dragon_Age_The_Missing_1.acsm

The downloaded file requires a password to open. Remove the DRM from the files.

find . -type f -name "Dragon_Age_The_Missing*.pdf" -exec adept_remove {} \;

Fail2Ban

Configure fail2ban on Linux with firewalld

sudo cp -v /etc/fail2ban/jail.conf /etc/fail2ban/jail.local
sudo nvim /etc/fail2ban/jail.local
bantime = 1h
findtime = 1h
maxretry = 5
sudo cp -v /etc/fail2ban/jail.d/00-firewalld.conf /etc/fail2ban/jail.d/00-firewalld.local
sudo nvim /etc/fail2ban/jail.d/sshd.local
[sshd]
enabled = true

bantime = 1d
maxretry = 3
sudo systemctl restart fail2ban.service
sudo fail2ban-client status

Configure fail2ban on FreeBSD with PF

sudo pkg install -y py311-fail2ban

Edit /usr/local/etc/fail2ban/jail.local.

[DEFAULT]
bantime = 86400
findtime = 3600
maxretry = 3
banaction = pf

[sshd]
enabled = true

Enable and start fail2ban.

sudo sysrc fail2ban_enable="YES"
sudo service fail2ban start

# If not enabled already:
sudo sysrc pf_enable="YES"
sudo service pf start

Configure /etc/pf.conf.

table <fail2ban> persist

set skip on lo0

block in all
block in quick from <fail2ban>
...

Check and reload PF rules.

sudo pfctl -nf /etc/pf.conf
sudo pfctl -f /etc/pf.conf

Fedora Atomic

Access USB serial device in container

Create a udev rule on the host for all usb serial devices. Set OWNER to your main user.

cat << EOF | sudo tee /etc/udev/rules.d/50-usb-serial.rules
SUBSYSTEM=="tty", SUBSYSTEMS=="usb-serial", OWNER="jas"
EOF

Reload udev.

sudo udevadm control --reload-rules
sudo udevadm trigger

The serial device should now be owned by your user.

ls -l /dev/ttyUSB0
crw-rw----. 1 jas dialout 188, 0 Mar 15 11:09 /dev/ttyUSB0

You can now run minicom inside the toolbox container.

distrobox enter default
minicom -D /dev/ttyUSB0

Fedora

Repo priorities and exclusions

Terra repositories (Fyra Labs)

Prioritize the fdk-aac-free package from upstream by excluding the fdk-aac from Terra:

sudo dnf config-manager setopt "*terra*".exclude="fdk-aac"

Set Terra repositories to priority 3:

sudo dnf config-manager setopt "*terra*".priority=3

RPMFusion repositories

Set all RPMFusion repositories priorities to 5:

sudo dnf config-manager setopt "*rpmfusion*".priority=5

Exclude mesa-* packages from RPMFusion:

sudo dnf config-manager setopt "*rpmfusion*".exclude="mesa-*"

Fedora repositories

Exclude mesa-* packages from upstream Fedora repositories:

sudo dnf config-manager setopt "*fedora*".exclude="mesa-*"

All overrides can be viewed in /etc/dnf/repos.override.d/99-config_manager.repo.

Firewalld

Allow connections only from tailnet

Create a new zone for the tailscale0 interface.

sudo firewall-cmd --permanent --new-zone=tailnet
sudo firewall-cmd --permanent --zone=tailnet --add-interface=tailscale0
sudo firewall-cmd --reload

Add services and ports to the tailnet zone.

sudo firewall-cmd --permanent --zone=tailnet --add-service={http,https,ssh}
sudo firewall-cmd --permanent --zone=tailnet --add-port=9100/tcp
sudo firewall-cmd --reload

Ensure the public zone does not have any interfaces or sources.

sudo firewall-cmd --permanent --zone=public --remove-interface=eth0
sudo firewall-cmd --reload

The firewall should now only allow traffic coming from the tailnet interface.

FreeBSD

USB 3.1 Type-C to RJ45 Gigabit Ethernet adapter

The Amazon Basics Aluminum USB 3.1 Type-C to RJ45 Gigabit Ethernet Adapter works well with FreeBSD 14.1-RELEASE. It uses the AX88179 chipset from ASIX Electronics Corp.

Install the ports tree

Checkout the FreeBSD source code.

sudo git clone -o freebsd -b releng/14.1 https://git.FreeBSD.org/src.git /usr/src

Checkout the ports tree.

sudo git clone --depth 1 https://git.FreeBSD.org/ports.git -b 2024Q3 /usr/ports

To switch to a different quarterly branch:

sudo git -C /usr/ports switch 2024Q4

drm-61-kmod

Install from the ports tree.

cd /usr/ports/graphics/drm-61-kmod
sudo make install clean

Alternatively, for Alderlake GPUs:

sudo pkg install drm-kmod

Edit /etc/rc.conf.

kld_list="i915kms"

Add user to video group:

sudo pw groupmod video -m jas

Mount filesystem in single-user mode

When booted into single-user mode:

fsck
mount -u /
mount -a -t zfs
zfs mount -a

You should now be able to edit files, add/remove packages, etc.

Mount encrypted zroot in LiveCD

Boot into the LiveCD environment.

mkdir /tmp/mnt
geli attach /dev/nda0p4
zpool import -f -R /tmp/mnt zroot
zfs mount zroot/ROOT/default

The root directory ofthe zroot, zroot/ROOT/default, is labeled to not be automounted when imported, hence the need for the last command.

Setup Podman (FreeBSD >= 14)

The following is a condensed version of the guide found at CloudSpinx: Install Podman and run Containers in FreeBSD 14.

sudo pkg install podman-suite
sudo mount -t fdescfs fdesc /dev/fd

Add the following line to /etc/fstab:

fdesc /dev/fd fdescfs rw 0 0

Enable the Podman service.

sudo sysrc podman_enable="YES"

Container networking requires a NAT to allow the container network’s packets to reach the host’s network. Copy the sample pf.conf for Podman.

sudo cp -v /usr/local/etc/containers/pf.conf.sample /etc/pf.conf

Change v4egress_if and v6egress_if to the host’s main network interface in /etc/pf.conf.

v4egress_if="igc0"
v6egree_if="igc0"

Enable and start PF.

sudo sysrc pf_enable="YES"
sudo service pf start

FreeBSD >= 13.3 has support for rerouting connections from the host to services inside the container. To enable this, load the PF kernel module, then use sysctl to activate PF support for this rerouting.

echo 'pf_load="YES"' | sudo tee -a /boot/loader.conf
sudo kldload pf
sudo sysctl net.pf.filter_local=1
echo 'net.pf.filter_local=1' | sudo tee -a /etc/sysctl.conf.local
sudo service pf restart

The rerouting rules will only work if the destination address is localhost. Ensure the following exists in /etc/pf.conf.

nat-anchor "cni-rdr/*"

Container images and related state is stored in /var/db/containers. Create a ZFS dataset for this with the mountpoint set to that directory.

sudo zfs create -o mountpoint=/var/db/containers zroot/containers

If the system is not using ZFS, change storage.conf to use the vfs storage driver.

sudo sed -I .bak -e 's/driver = "zfs"/driver = "vfs"/' /usr/local/etc/containers/storage.conf

If there are any errors caused by the /var/db/containers/storage database, remove it.

sudo rm -rfv /var/db/containers/storage

Note: Podman can only be run with root privileges on FreeBSD at this time.

Enable the Linux service.

sudo sysrc linux_enable="YES"
sudo service linux start

To run Linux containers, add the --os=linux argument to Podman commands.

sudo podman run --os=linux ubuntu /usr/bin/cat "/etc/os-release"

Everything should work as expected.

Install Linux VM in Bhyve

Based on How to install Linux VM on FreeBSD using bhyve and ZFS, but condensed and collated for my use-case.

Setting up the network interfaces

Make the tap device UP by default in /etc/sysctl.conf.

echo "net.link.tap.up_on_open=1" >> /etc/sysctl.conf
sysctl net.link.tap.up_on_open=1

Load the kernel modules needed for bhyve.

kldload vmm
kldload nmdm

Make sure the modules are loaded at boot time.

echo 'vmm_load="YES"' >> /boot/loader.conf
echo 'nmdm_load="YES"' >> /boot/loader.conf
echo 'if_tap_load="YES"' >> /boot/loader.conf
echo 'if_bridge_load="YES"' >> /boot/loader.conf

Create the bridge and tap device. If you already have a bridge created, use that instead. We’ll assume this is the case, and the bridge is called igb0bridge.

ifconfig bridge create

If a bridge is already created and the main network interface igc0 is attached to it, the following command is not necessary.

ifconfig igb0bridge addm igc0

Create tap interface and attach it to the igb0bridge.

ifconfig tap0 create
ifconfig igb0bridge addm tap0

If there wasn’t a bridge already being used for jails, then /etc/rc.conf should contain the following:

cloned_interfaces="igb0bridge tap0"
ifconfig_igb0bridge="addm igc0 addm tap0 up"

If there was already a bridge used for jails, then /etc/rc.conf should contain the following:

cloned_interfaces="igb0bridge tap0"
ifconfig_igb0bridge="inet 10.0.0.8/24 addm igc0 addm tap0 up"

Setting up ZFS volumes for Linux Bhyve VM

zfs create -V128G -o volmode=dev zroot/debianvm

Downloading the Debian installer ISO

cd /tmp/
DEBIAN_VERSION=12.10.0
wget "https://cdimage.debian.org/debian-cd/current/amd64/iso-cd/debian-${DEBIAN_VERSION}-amd64-netinst.iso"

Installing Debian in VM

Install the grub-bhyve binary to allow booting of non-FreeBSD guest OSes.

pkg install grub2-bhyve bhyve-firmware

Install Debian by running bhyve with the netinstall iso image and the zvol attached.

bhyve -c 4 -m 8G -w -H \
  -s 0,hostbridge \
  -s 3,ahci-cd,/tmp/debian-12.10.0-amd64-netinst.iso \
  -s 4,virtio-blk,/dev/zvol/zroot/debianvm \
  -s 5,virtio-net,tap0 \
  -s 29,fbuf,tcp=0.0.0.0:5900,w=800,h=600,wait \
  -s 30,xhci,tablet \
  -s 31,lpc \
  -l com1,stdio \
  -l bootrom,/usr/local/share/uefi-firmware/BHYVE_UEFI.fd \
  debianvm

When the command runs, use a remote VNC view to connect to and start the netinstall iso.

The following step is required to boot from UEFI.

Run the Debian installer with desired configuration. When you reach the “Finish the installation” stage, select “Go Back”, then select “Execute a shell”. Once in the shell, run the following commands:

mkdir /target/boot/efi/EFI/BOOT/
cp -v /target/boot/efi/EFI/debian/grubx64.efi /target/boot/efi/EFI/BOOT/bootx64.efi
exit

Now continue with “Finish the installation”.

Booting Debian bhyve VM

The instance of the virtual machine needs to be destroyed before it can be started again.

bhyvectl --destroy --vm=debianvm

Boot the Debian VM.

bhyve -c 4 -m 8G -w -H \
  -s 0,hostbridge \
  -s 4,virtio-blk,/dev/zvol/zroot/debianvm \
  -s 5,virtio-net,tap0 \
  -s 29,fbuf,tcp=0.0.0.0:5900,w=1024,h=768 \
  -s 30,xhci,tablet \
  -s 31,lpc \
  -l com1,stdio \
  -l bootrom,/usr/local/share/uefi-firmware/BHYVE_UEFI.fd \
  debianvm

Starting the Debian VM on boot with a shell script

#!/bin/sh
# Name: startdebianvm
# Purpose: Simple script to start my Debian 10 VM using bhyve on FreeBSD
# Author: Vivek Gite {https://www.cyberciti.biz} under GPL v2.x+
-------------------------------------------------------------------------
# Lazy failsafe (not needed but I will leave them here)
ifconfig tap0 create
ifconfig em0bridge addm tap0
if ! kldstat | grep -w vmm.ko
then
 kldload -v vmm
fi
if ! kldstat | grep -w nmdm.ko
then
 kldload -v nmdm
fi
bhyve -c 1 -m 1G -w -H \
-s 0,hostbridge \
-s 4,virtio-blk,/dev/zvol/zroot/debianvm \
-s 5,virtio-net,tap0 \
-s 29,fbuf,tcp=0.0.0.0:5900,w=1024,h=768 \
-s 30,xhci,tablet \
-s 31,lpc -l com1,stdio \
-l bootrom,/usr/local/share/uefi-firmware/BHYVE_UEFI.fd \
debianvm

Create a crontab entry:

@reboot /path/to/startdebianvm

Installing a Linux jail

Create the ZFS datasets for the base jail and Linux jail.

sudo zfs create naspool/jails/debian
sudo zfs create naspool/jails/14.2-RELEASE

Download the base userland system for FreeBSD.

fetch https://download.freebsd.org/ftp/releases/amd64/amd64/14.2-RELEASE/base.txz

Extract the base userland into the base jail’s directory.

sudo tar -xf base.txz -C /jails/14.2-RELEASE --unlink

Copy DNS and timezone files.

sudo cp -v /etc/resolv.conf /jails/14.2-RELEASE/etc/resolv.conf
sudo cp -v /etc/localtime /jails/14.2-RELEASE/etc/localtime

Update the base jail to the latest patch level.

sudo freebsd-update -b /jails/14.2-RELEASE/ fetch intall

Create a ZFS snapshot from the base jail.

sudo zfs snapshot naspool/jails/14.2-RELEASE@base

Clone the base jail to create a thin jail for the Linux distribution.

sudo zfs clone naspool/jails/14.2-RELEASE@base naspool/jails/debian

Enable the Linux ABI.

sudo sysrc linux_enable="YES"
sudo service linux start

Run the jail command with a quick configuration.

sudo jail -cm \
  name=debian \
  host.hostname="debian" \
  path="/jails/debian" \
  interface="igc0" \
  ip4.addr="10.0.0.21" \
  exec.start="/bin/sh /etc/rc" \
  exec.stop="/bin/sh /etc/rc.shutdown" \
  mount.devfs \
  devfs_ruleset=11 \
  allow.mount \
  allow.mount.devfs \
  allow.mount.fdescfs \
  allow.mount.procfs \
  allow.mount.linprocfs \
  allow.mount.linsysfs \
  allow.mount.tmpfs \
  enforce_statfs=1

Access the jail.

sudo jexec -u root debian

Install the debootstrap program and prepare the Debian environment.

pkg install debootstrap
debootstrap bookworm /compat/debian

When the process finishes, stop the jail from the host system.

sudo service jail onestop debian

Add an entry in /etc/jail.conf for the Debian jail.

debian {
  # STARTUP/LOGGING
  exec.start = "/bin/sh /etc/rc";
  exec.stop = "/bin/sh /etc/rc.shutdown";
  exec.consolelog = "/var/log/jail_console_${name}.log";

  # PERMISSIONS
  allow.raw_sockets;
  exec.clean;
  mount.devfs;
  devfs_ruleset = 11;

  # HOSTNAME/PATH
  host.hostname = "${name}";
  path = "/jails/${name}";

  # NETWORK
  ip4.addr = 10.0.0.21;
  interface = igc0;

  # MOUNT
  mount += "devfs       $path/compat/debian/dev       devfs     rw 0 0";
  mount += "tmpfs       $path/compat/debian/dev/shm   tmpfs     rw,size=1g,mode=1777 0 0";
  mount += "fdescfs     $path/compat/debian/dev/fd    fdescfs   rw,linrdlnk 0 0";
  mount += "linprocfs   $path/compat/debian/proc      linprocfs rw 0 0";
  mount += "linsysfs    $path/compat/debian/sys       linsysfs  rw 0 0";
  mount += "/tmp        $path/compat/debian/tmp       nullfs    rw 0 0";
  mount += "/home       $path/compat/debian/home      nullfs    rw 0 0";
}

Start the jail.

sudo service jail start debian

The Debian environment can be accessed with the following command:

sudo jexec debian chroot /compat/debian /bin/bash

Internet Archive

Install Python CLI client

pipx install internetarchive

Use Python client to download torrent files from given collection

In qBittorrent, ensure “Automatically add torrents from” > Monitored Folder is set the /mnt/torrent_files and the Override save path is Default save path.

Get itemlist from collection.

ia search --itemlist "collection:bbsmagazine" | tee bbsmagazine.txt

Download torrent files from each item using parallel.

cat bbsmagazine.txt | parallel 'ia download --format "Archive BitTorrent" --destdir=/mnt/torrent_files {}'

Move .torrent files from their directories to /mnt/torrent_files.

find /mnt/torrent_files -type f -name "*.torrent" -exec mv {} /mnt/torrent_files \;

Note: .torrent files will be removed from /mnt/torrent_files by qBittorrent once they are added to the instance.

Remove empty directories.

find /mnt/torrent_files -maxdepth 1 -mindepth 1 -type d -delete

Kernel

Basic hardening

Disable coredumps

Edit /etc/security/limits.conf and append the following lines:

* hard core 0
* soft core 0

Edit /etc/sysctl.d/9999-disable-core-dump.conf:

fs.suid_dumpable=0
kernel.core_pattern=|/bin/false

Create and edit /etc/systemd/coredump.conf.d/custom.conf:

sudo mkdir /etc/systemd/coredump.conf.d/
sudo nvim /etc/systemd/coredump.conf.d/custom.conf
[Coredump]
Storage=none
ProcessSizeMax=0

Reload the daemons.

sudo systemctl daemon-reload

Edit /etc/systemd/system.conf. Make sure DefaultLimitCORE is commented out.

#DefaultLimitCORE=infinity
sudo systemctl daemon-reexec

Libvirt

Setup bridged networking with systemd-networkd

Edit /etc/systemd/network/br0.netdev.

[NetDev]
Name=br0
Kind=bridge

Edit /etc/systemd/network/br0.network.

[Match]
Name=br0

[Network]
Address=10.0.0.96/24
Gateway=10.0.0.1
Broadcast=10.0.0.255

Edit /etc/systemd/network/eth0.network.

[Match]
Name=eth0

[Network]
Bridge=br0

Restart systemd-networkd and verify.

sudo systemctl restart systemd-networkd.service
sudo networkctl status br0
sudo brctl show br0 # requires bridge-utils package

Edit bridge.xml.

<!-- bridge.xml -->

<network
  <name>br0</name>
  <forward mode="bridge"/>
  <bridge name="br0"/>
</network>

Define, start, and verify the network.

sudo virsh net-define bridge.xml
sudo virsh net-start br0
sudo virsh net-autostart br0
sudo virsh net-list --all

Lutris

Add game-performance

  • Go to Preferences -> Global options.
  • Enable Advanced mode.
  • Scroll down to Command prefix.
  • Add game-performance to command prefix.

LVM

Add disk to LVM volume

Create a new physical volume on the new disk.

sudo pvcreate /dev/vdb
sudo lvmdiskscan -l

Add the newly created physical volume to an existing logical volume.

sudo vgextend almalinux /dev/vdb

Extend the /dev/almalinux/root to create a total 1000 GB.

sudo lvm lvextend -l +100%FREE /dev/almalinux/root

Grow the filesystem of the root volume.

# ext4
sudo resize2fs -p /dev/mapper/almalinux-root

# xfs
sudo xfs_growfs /

Neovim Cheatsheet

NERD Commenter

ModeKeybindAction
normalSPACE + ccNERDCommenterComment
normalSPACE + c + SPACENERDCommenterToggle
normalSPACE + cmNERDCommenterMinimal
normalSPACE + cnNERDCommenterNested
normalSPACE + c$NERDCommenterToEOL
normalSPACE + ciNERDCommenterInvert
normalSPACE + csNERDCommenterSexy
normalSPACE + cyNERDCommenterYank
normalSPACE + cANERDCommenterAppend
normalSPACE + clNERDCommenterAlignLeft
normalSPACE + cbNERDCommenterAlignBoth
normalSPACE + cuNERDCommenterUncomment
normalSPACE + caNERDCommenterAltDelims
ModeKeybindAction
normalSPACE + sh[S]earch [H]elp
normalSPACE + sk[S]earch [K]eymaps
normalSPACE + sf[S]earch [F]iles
normalSPACE + ss[S]earch [S]elect Telescope
normalSPACE + sw[S]earch current [W]ord
normalSPACE + sg[S]earch by [G]rep
normalSPACE + sd[S]earch [D]iagnostics
normalSPACE + sr[S]earch [R]esume
normalSPACE + s.[S]earch Recent Files (“.” for repeat)
normalSPACE + SPACE[ ] Find existing buffers
normalSPACE + /[/] Fuzzily search in current buffer
normalSPACE + s/[S]earch [/] in Open Files
normalSPACE + sn[S]earch [N]eovim files
normalESCnohlsearch

Editing

ModeKeybindAction
normalsaAdd surrounding
normalsdDelete surrounding
normalsrReplace surrounding
normalsfFind right surrounding
normalsFFind left surrounding
normalshHighlight surrounding
normalShift-oAdd empty line below cursor
normalShift-OAdd empty line above curosr
normalSPACE + fFormat buffer
insertArrowsTraverse completion menu
insertCTRL-ySelect completion

Diagnostics

ModeKeybindAction
normal]dJump to next diagnostic in current buffer
normal]DJump to last diagnostic in current buffer
normal[dJump to previous diagnostic in current buffer
normal[DJump to first diagnostic in current buffer
normalSPACE + qOpen diagnostic [Q]uickfix list
normalCTRL-w + dShow diagnostics under cursor

Splits and tabs

ModeKeybindAction
command:spSplit window horizontally
command:vspSplit window vertically
command:tabeCreate new tab
command:tabcClose the current tab
command:tabnGo to next tab
command:tabpGo to previous tab

Networking

Disable IPv6 on Debian

Edit /etc/sysctl.conf.

net.ipv6.conf.all.disable_ipv6 = 1
net.ipv6.conf.default.disable_ipv6 = 1
net.ipv6.conf.lo.disable_ipv6 = 1

Apply the changes.

sudo sysctl -p

Disable IPv6 on Fedora

sudo grubby --args=ipv6.disable=1 --update-kernel=ALL
sudo grub2-mkconfig -o /boot/grub2/grub.cfg

Rename network interface when using systemd-networkd

Create a udev rule at /etc/udev/rules.d/70-my-net-names.rules.

SUBSYSTEM=="net", ACTION=="add", ATTR{address}=="your-mac-address", NAME="wlan0"

Using 70-my-net-name.rules as the filename ensures the rule is ordered before /usr/lib/udev/rules.d/80-net-setup-link.rules.

Connecting to WiFi network using systemd-networkd and wpa_supplicant

Create a file at /etc/wpa_supplicant/wpa_supplicant-wlan0.conf. Use wpa_passphrase to hash the passphrase.

wpa_passphrase your-ssid your-ssid-passphrase | sudo tee -a /etc/wpa_supplicant/wpa_supplicant-wlan0.conf

Edit /etc/wpa_supplicant/wpa_supplicant-wlan0.conf.

ctrl_interface=/var/run/wpa_supplicant
ctrl_interface_group=0
update_config=1

network={
  ssid="your-ssid"
  psk="your-hashed-ssid-passphrase"
  key_mgmt=WPA-PSK
  proto=WPA2
  scan_ssid=1
}

Create a file at /etc/systemd/network/25-wlan.network.

[Match]
Name=wlan0

[Network]
DHCP=ipv4

Enable and start the network services.

sudo systemctl enable --now wpa_supplicant@wlan0.service
sudo systemctl restart systemd-networkd.service
sudo systemctl restart wpa_supplicant@wlan0.service

Check the interface status with ip a.

Use tailnet DNS and prevent DNS leaks

After the above WiFi interface is setup, disable IPv6 as per the above sections, and enable the Tailscale service.

sudo systemctl enable --now tailscaled.service
sudo tailscale up

Edit /etc/systemd/network/25-wlan-network again, and add the following contents.

[Match]
Name=wlan0

[Network]
DHCP=ipv4
DNS=100.100.100.100
DNSSEC=allow-downgrade

[DHCPv4]
UseDNS=no

This will tell the wlan0 interface to use Tailscale’s MagicDNS, along with DNSSEC if it is available, and not to get the nameservers from the DHCPv4 connection.

NFS

Setup NFS server on Debian

sudo apt install -y nfs-kernel-server nfs-common

Configure NFSv4 in /etc/default/nfs-common.

NEED_STATD="no"
NEED_IDMAPD="yes"

Configure NFSv4 in /etc/default/nfs-kernel-server. Disable NFSv2 and NFSv3.

RPCNFSDOPTS="-N 2 -N 3"
RPCMOUNTDOPTS="--manage-gids -N 2 -N 3"
sudo systemctl restart nfs-server.service

Configure Firewalld.

sudo firewall-cmd --zone=public --permanent --add-service=nfs
sudo firewall-cmd --reload

Setup pseudo filesystem and exports.

sudo mkdir /shared
sudo chown -R nobody:nogroup /shared

Add exported directory to /etc/exports.

/shared <IP address of client>(rw,no_root_squash,no_subtree_check,crossmnt,fsid=0)

Create the NFS table.

sudo exportfs -a

Setup NFS client on Debian

sudo apt install -y nfs-common

Create shared directory.

sudo mkdir -p /mnt/shared

Mount NFS exports. We’ll assume the IP address of the NFS server is 10.0.0.64.

sudo mount.nfs4 10.0.0.64:/ /mnt/shared

Note that the 10.0.0.64:/ is relative to the exported directory. So /mnt/shared on the client is /shared on the server. If you try to mount with mount -t nfs 10.0.0.64:/shared /mnt/shared you will get a no such file or directory error.

Edit /etc/fstab.

10.0.0.64:/ /mnt/shared nfs4 soft,intr,rsize=8192,wsize=8192
sudo systemctl daemon-reload
sudo mount -av

Setup NFS server on FreeBSD

Edit /etc/rc.conf.

nfs_server_enable="YES"
nfs_server_flags="-u -t -n 4"
rpcbind_enable="YES"
mountd_flags="-r"
mountd_enable="YES"

Edit /etc/exports.

/data1 -alldirs -mapall=user1 host1 host2 host3
/data2 -alldirs -maproot=user2 host2

Start the services.

sudo service rpcbind start
sudo service nfsd start
sudo service mountd start

After making changes to the exports file, you need to restart NFS for the changes to take effect.

kill -HUP `cat /var/run/mountd.pid`

Setup NFS client on FreeBSD

Edit /etc/rc.conf.

nfs_client_enable="YES"
nfs_client_flags="-n 4"
rpc_lockd_enable="YES"
rpc_statd_enable="YES"

Mount NFS share on client with systemd

Create a file at /etc/systemd/system/mnt-backup.mount.

[Unit]
Description=borgbackup NFS share from FreeBSD
DefaultDependencies=no
Conflicts=umount.target
After=network-online.target remote-fs.target
Before=umount.target

[Mount]
What=10.0.0.119:/coffeeNAS/borgbackup/repositories
Where=/mnt/backup
Type=nfs
Options=defaults,vers=3

[Install]
WantedBy=multi-user.target

Nmap

Target specification

ExampleDescription
nmap 192.168.1.1Scan a single IP
nmap 192.168.1.1 192.168.2.1Scan specific IPs
nmap 192.168.1.1-254Scan a range
nmap scanme.nmap.orgScan a domain
nmap 192.168.1.0/24Scan using CIDR notation
nmap -iL targets.txtScan targets from a file
nmap -iR 100Scan 100 random hosts
nmap -exclude 192.168.1.1Exclude listed hosts

Nmap scan techniques

ExampleDescription
nmap 192.168.1.1 -sSTCP SYN port scan (default)
nmap 192.168.1.1 -sTTCP connect port scan
nmap 192.168.1.1 -sUUDP port scan
nmap 192.168.1.1 -sATCP ACK port scan
nmap 192.168.1.1 -sWTCP Window port scan
nmap 192.168.1.1 -sMTCP Maimon port scan

Host discovery

ExampleDescription
nmap 192.168.1.1-3 -sLNo scan. List targets only
nmap 192.168.1.1/24 -snDisable port scanning. Host discovery only.
nmap 192.168.1.1-5 -PnDisable host discovery. Port scan only.
nmap 192.168.1.1-5 -PS22-25,80TCP SYN discovery on ports 22-25, 80 (Port 80 by default)
nmap 192.168.1.1-5 -PA22-25,80TCP ACK discovery on ports 22-25, 80 (Port 80 by default)
nmap 192.168.1.1-5 -PU53UDP discovery on port 53. (Port 40125 by default)
nmap 192.168.1.1-1/24 -PRARP discovery on local network
nmap 192.168.1.1 -nNever do DNS resolution

Port specification

ExampleDescription
nmap 192.168.1.1 -p 21Port scan for port 21
nmap 192.168.1.1 -p 21-100Port scan for range 21-100
nmap 192.168.1.1 -p U:53,T:21-25,80Port scan multiple TCP and UDP ports
nmap 192.168.1.1 -p-Port scan all ports
nmap 192.168.1.1 -p http,httpsPort scan from service name
nmap 192.168.1.1 -FFast port scan (100 ports)
nmap 192.168.1.1 -top-ports 2000Port scan the top 2000 ports
nmap 192.168.1.1 -p-65535Leaving off the initial port in range makes the scan start at port 1
nmap 192.168.1.1 -p0-Leaving off the end port in range makes the scan go through to port 65535

Service and version detection

ExampleDescription
nmap 192.168.1.1 -sVAttempts to determine version of the service running on port.
nmap 192.168.1.1 -sV -version-intensity 8Intensity level 0-9. Higher number increases possibility of correctness.
nmap 192.168.1.1 -sV -version-lightEnable light mode. Lower possibility of correctness. Faster.
nmap 192.168.1.1 -sV -version-allEnable intensity level 9. Higher possibility of correctness. Slower.
nmap 192.168.1.1 -AEnable OS detection, version detection, script scanning, and traceroute.

OS detection

ExampleDescription
nmap 192.168.1.1 -ORemote OS detection using TCP/IP stack fingerprinting
nmap 192.168.1.1 -O -osscan-limitIf at least one open and one closed TCP port are not found it will not try OS detection against host.
nmap 192.168.1.1 -P -osscan-guessMakes nmap guess more aggressively.
nmap 192.168.1.1 -O -max-os-tries 1Set the maximum number of OS detection tries
nmap 192.168.1.1 -AEnables OS detection, version detection, script scanning, and traceroute.

Timing and performance

ExampleDescription
nmap 192.168.1.1 -T0Paranoid (0) IDS evasion
nmap 192.168.1.1 -T1Sneaky (1) IDS evasion
nmap 192.168.1.1 -T2Polite (2) slows down the scan to use less bandwidth and use less target machine resources.
nmap 192.168.1.1 -T3Normal (3) which is default speed.
nmap 192.168.1.1 -T4Aggressive (4) speed scans. Assumes you are on a reasonably fast and reliable network.
nmap 192.168.1.1 -T5Insane (5) speed scan. Assumes you are on an extraordinarily fast network.

Timing and performance switches

ExampleDescription
-host-timeout 1s; -host-timeout 4m;Give up on target after this long.
-min-rtt-timeout/max-rtt-timeout/initial-rtt-timeout 4m;Specifies probe round trip time.
-min-hostgroup/max-hostgroup 50Parallel host scan group sizes
-min-parallelism/max-parallelism 10Probe parallelization
-max-retries 3Specify the max number of port scan probe retransmissions.
-min-rate 100Send packets to no slower than 100 per second
-max-rate 100Send packets no faster than 100 per second

NSE scripts

ExampleDescription
nmap 192.168.1.1 -sCScan with default NSE scripts. Useful and safe.
nmap 192.168.1.1 -script defaultScan with default NSE scripts.
nmap 192.168.1.1 -script=bannerScan with single script. Example banner.
nmap 192.168.1.1 -script=http*Scan with a wildcard. Example http.
nmap 192.168.1.1 -script=http,bannerScan with two scripts. http and banner.
nmap 192.168.1.1 -script "not intrusive"Scan default, but remove intrusive scripts.
nmap -script snmp-sysdescr -script-args snmpcommunity=admin 192.168.1.1NSE script with arguments

Useful NSE script examples

ExampleDescription
nmap -Pn -script=http-sitemap-generator scanme.nmap.orghttp site map generator
nmap -n -Pn -p 80 -open -sV -vvv -script banner,http-title -iR 1000Fast search for random web servers
nmap -Pn -script=dns-brute domain.comBrute forces DNS hostnames guessing subdomains
nmap -n -Pn -vv -O -sV -script smb-enum*,smb-ls,smb-mbenum,smb-os-discovery,smb-s*,smb-vuln*,smbv2* -vv 192.168.1.1Safe SMB scripts to run
nmap -script whois* domain.comWhois query
nmap -p80 -script http-unsafe-output-escaping scanme.nmap.orgDetect cross site scripting vulnerabilities
nmap -p80 -script http-sql-injection scanme.nmap.orgCheck for SQL injections

Firewall/IDS evasion and spoofing

ExampleDescription
nmap 192.168.1.1 -fRequested scan (including ping scans) use tiny fragmented IP packets. Harder for packet filters.
nmap 192.168.1.1 -mtu 32Set your own offset size
nmap -D 192.168.1.101,192.168.1.102,192.168.1.103Send scans from spoofed IPs
nmap -D decoy-ip1,decoy-ip2,your-own-ipSame as above
nmap -S www.microsoft.com www.facebook.comScan Facebook from Microsoft (-e eth0 -Pn may be required
nmap -g 53 192.168.1.1Use given source port number
nmap -proxies http://192.168.1.1:8080,http://192.168.1.2:8080,http://192.168.1.2:8080Relay connections through HTTP/SOCKS4 proxies
nmap -data-length 200 192.168.1.1Appends random data to sent packets

Output

ExampleDescription
nmap 192.168.1.1 -oN normal.fileNormal output to the file normal.file
nmap 192.168.1.1 -oX xml.fileXML output to the file xml.file
nmap 192.168.1.1 -oG grep.fileGrepable output to the file grep.file
nmap 192.168.1.1 -oA resultsOutput in the three major formats at once
nmap 192.168.1.1 -oG -Grepable output to screen. -oN, -oX also usable.
nmap 192.168.1.1 -oN file.txt -append-outputAppend a scan to a previous scan file
nmap 192.168.1.1 -vIncrease verbosity level (use -vv or more)
nmap 192.168.1.1 -dIncrease debugging level (use -dd or more)
nmap 192.168.1.1 -reasonDisplay the reason a port is in a particular state, same output as -vv
nmap 192.168.1.1 -openOnly show open (or possibly open) ports
nmap 192.168.1.1 -T4 -packet-traceShow all packets sent and received
nmap -iflistShows the host interfaces and routes
nmap -resume results.fileResume a scan from results.file

Helpful nmap output examples

Scan for web servers and grep to show IPs running web servers

nmap -p80 -sV -oG - -open 192.168.1.1/24 | grep open

Generate a list of IPs of live hosts

nmap -iR 10 -n -oX out.xml | grep "Nmap" | cut -d " " -f5 > live-hosts.txt

Append IP to the list of live hosts

nmap -iR 10 -n -oX out2.xml | grep "Nmap" | cut -d "" -f5 >> live-hosts.txt

Compare output of two scan results

ndiff scan.xml scan2.xml

Convert nmap xml files to html files

xsltproc nmap.xml -o nmap.html

Other useful nmap commands

ExampleDescription
nmap -iR 10 -PS22-25,80,113,1050,35000 -v -snDiscovery only on ports X, no port scan
nmap 192.168.1.1-1/24 -PR -sn -vvARP discovery only on local network, no port scan
nmap -iR 10 -sn -tracerouteTraceroute to random targets, no port scan
nmap 192.168.1.1-50 -sL -dns-server 192.168.1.1Query the internal DNS for hosts, list targets only
nmap 192.168.1.1 --packet-traceShow the details of the packets that are sent and received during a scan and capture the traffic

Orange Pi 5+

Disable blinky LEDs

Edit /etc/udev/rules.d/led_control.rules.

SUBSYSTEM=="leds", KERNEL=="blue_led", ACTION=="add", ATTR{trigger}="none"
SUBSYSTEM=="leds", KERNEL=="green_led", ACTION=="add", ATTR{trigger}="none"
SUBSYSTEM=="leds", KERNEL=="mmc0::", ACTION=="add", ATTR{trigger}="none"

Reboot the system.

Packet Tracer

Fix GUI issues with KDE Plasma dark theme

mkdir ~/.config-pt
cd ~/.config
cp -rf dconf gtk-3.0 gtk-4.0 xsettingsd ~/.config-pt
  • Right-click on Menu button.
  • Click Edit Applications.
  • Select Packet Tracer.
  • Add XDG_CONFIG_HOME=/home/jas/.config-pt to Environment variables.
  • Save.

Parallel

Pulling files from remote server with rsync

To transfer just the files:

ssh user@remote -- find /path/to/parent/directory -type f | parallel -v -j16 rsync -Havessh -aAXP user@remote:{} /local/path

To transfer the entire directory:

echo "/path/to/parent/directory" | parallel -v -j16 rsync -Havessh -aAXP user@remote:{} /local/path

Pushing files to remote server with rsync

To transfer just the files:

find /path/to/local/directory -type f | parallel -v -j16 -X rsync -aAXP /path/to/local/directory/{} user@remote:/path/to/dest/directory

Running the same command on multiple remote hosts

parallel --tag --nonall -S remote0,remote1,remote2 uptime

PostgreSQL

Change password for user

sudo -u user_name psql db_name
ALTER USER user_name WITH PASSWORD 'new_password';

Update password auth method to SCRAM

Edit /etc/postgresql/16/main/postgresql.conf.

password_encryption = scram-sha-256

Restart postgresql.service.

At this point, any services using the old MD5 auth method will fail to connect to their PostgreSQL databases.

Update the settings in /etc/postgresql/16/main/pg_hba.conf.

TYPE    DATABASE        USER            ADDRESS         METHOD
local   all             mastodon                        scram-sha-256
local   all             synapse_user                    scram-sha-256

Enter a psql shell and determine who needs to upgrade their auth method.

SELECT rolname, rolpassword ~ '^SCRAM-SHA-256\$' AS has_upgraded FROM pg_authid WHERE rolcanlogin;

\password username

Restart postgresql.service and all services using a PostgreSQL database.

Qcow2

Mount qcow2 image

Enable NBD on the host.

sudo modprobe nbd max_part=8

Connect qcow2 image as a network block device.

sudo qemu-nbd --connect=/dev/nbd0 /path/to/image.qcow2

Find the VM’s partitions.

sudo fdisk /dev/nbd0 -l

Mount the root partition from the VM.

sudo mount /dev/nbd0p3 /mnt/point

To unmount:

sudo umount /mnt/point
sudo qemu-nbd --disconnect /dev/nbd0
sudo rmmod nbd

Resize qcow2 image

Install guestfs-tools (required for virt-resize command).

sudo dnf install -y guestfs-tools
sudo apt install -y guestfs-tools libguestfs-tools

To resize qcow2 images, you’ll have to create a new qcow2 image with the size you want, then use virt-resize on the old qcow2 image to the new one.

You’ll need to know the root partition within the old qcow2 image.

Create a new qcow2 image with desired size.

qemu-img create -f qcow2 -o preallocation=metadata newdisk.qcow2 100G

Now resize the old one to the new one.

virt-resize --expand /dev/vda3 olddisk.qcow2 newdisk.qcow2

Once you boot into the new qcow2 image, you’ll probably have to adjust the size of the logical volume if it has LVM.

sudo lvresize -l +100%FREE /dev/mapper/sysvg-root

Then resize the XFS root partition within the logical volume.

sudo xfs_growfs /dev/mapper/sysvg-root

Qemu

Take snapshot of VM

sudo virsh domblklist vm1

sudo virsh snapshot-create-as \
    --domain vm1 \
    --name guest-state1 \
    --diskspec vda,file=/var/lib/libvirt/images/overlay1.qcow2 \
    --disk-only \
    --atomic \
    --quiesce

Ensure qemu-guest-agent is installed inside the VM. Otherwise omit the --quiesce flag, but when you restore the VM it will be as if the system had crashed. Not that big of a deal since the VM’s OS should flush required data and maintain consistency of its filesystem.

sudo rsync -avhW --progress /var/lib/libvirt/images/vm1.img /var/lib/libvirt/images/vm1-copy.img
sudo virsh blockcommit vm1 vda --active --verbose --pivot

Full disk backup of VM

Start the guest VM.

sudo virsh start vm1

Enumerate the disk(s) in use.

sudo virsh domblklist vm1

Begin the backup.

sudo virsh backup-begin vm1

Check the job status. “None” means the job has likely completed.

sudo virsh domjobinfo vm1

Check the completed job status.

sudo virsh domjobinfo vm1 --completed

Now we see the copy of the backup.

sudo ls -lash /var/lib/libvirt/images/vm1.qcow2*

Resident Evil HD

Installation

Resident Evil Director’s Cut

Extract the following files to %ProgramFiles(x86)%\Games Retro\Resident Evil Classic:

  • Biohazard.exe from Mediakite v1.01
  • ddraw.dll from Resident Evil Classic REbirth
  • All from Resident Evil HD mod by TeamX
  • All from Resident Evil Seamless HD Project v1.1

Resident Evil 2

Extract the following files to %ProgramFiles(x86)%\Games Retro\BIOHAZARD 2 PC:

  • ddraw.dll from Resident Evil 2 Classic REbirth
  • All from Resident Evil 2 HD mod by TeamX
  • All from Resident Evil 2 Seamless HD Project v2.0

Resident Evil 3: Nemesis

Extract the following files to %ProgramFiles(x86)%\Games Retro\BIOHAZARD 3 PC:

  • ddraw.dll from Resident Evil 3 Classic REbirth
  • All from Resident Evil 3 HD mod by TeamX
  • All from Resident Evil 3: Nemesis Seamless HD Project v2.0

Testing

Test each game by launching them with the following config changes:

  • Resolution 1280x960
  • RGB88 colors
  • Disable texture filtering

RetroPie

Bluetooth: protocol not available

sudo apt install pulseaudio-module-bluetooth

Add to /lib/systemd/system/bthelper@.service.

ExecStartPre=/bin/sleep 4
sudo systemctl start sys-subsystem-bluetooth-devices-hci0.device
sudo hciconfig hci0 down
sudo killall pulseaudio
systemctl --user enable --now pulseaudio.service
sudo systemctl restart bluetooth.service

Router

  • Ubuntu 24.04
  • Orange Pi 5 Plus
  • ISP router in bridge mode
  • Ethernet from ISP router -> Orange Pi 5 Plus WAN port
  • Ethernet from Orange Pi 5 Plus LAN port to switch

Install packages

sudo apt install -y neovim firewalld fail2ban atop htop python3-dev nmap tcpdump rsync rsyslog iptraf-ng iftop sysstat conntrack logwatch unattended-upgrades byobu

Register router as Tailnet node.

sudo systemctl enable --now tailscaled.service
sudo tailscale up

Netplan with DHCP WAN

Edit /etc/netplan/01-netcfg.yaml.

network:
    version: 2
        renderer: networkd
        ethernets:
            eth0:   # WAN interface (connected to internet)
                dhcp4: true
                dhcp6: false
                nameservers:
                    addresses:
                        - 9.9.9.9
                        - 149.112.112.112
            eth1:   # LAN interface (connected to local network)
                dhcp4: false
                dhcp6: false
                addresses:
                    - 10.0.2.1/24
                nameservers:
                    addresses:
                        - 9.9.9.9
                        - 149.112.112.112

Bridged LAN+WiFi AP

network:
    version: 2
    renderer: networkd
    ethernets:
        eth0:
            dhcp4: true
            dhcp6: false
            nameservers:
                addresses:
                    - 9.9.9.9
                    - 149.112.112.112
        eth1:
            dhcp4: false
            dhcp6: false
            addresses:
                - 10.0.2.1/24
            nameservers:
                addresses:
                    - 9.9.9.9
                    - 149.112.112.112
    wifis:
        wlan0:
            access-points:
                coffeenet:
                    auth:
                        key-management: psk
                        password: "password"
    bridges:
        br0:
            interfaces:
                - eth1
                - wlan0
            addresses:
                - 10.0.2.1/24
            nameservers:
                addresses:
                    - 9.9.9.9
                    - 149.112.112.112

Netplan with static IP

network:
    version: 2
    renderer: networkd
    ethernets:
        eth0: # WAN interface (connected to internet)
            addresses:
                - WAN public IP/prefix
            nameservers:
                addresses:
                    - 9.9.9.9
                    - 149.112.112.112
            routes:
                - to: default
                  via: WAN default gateway
                  metric: 100
        eth1:
            dhcp4: false
            dhcp6: false
            addresses:
                - 10.0.2.1/24
            nameservers:
                addresses:
                    - 9.9.9.9
                    - 149.112.112.112

Bridged LAN+WiFi AP

network:
    version: 2
    renderer: networkd
    ethernets:
        eth0:
            dhcp4: false
            dhcp6: false
            addresses:
                - WAN public IP
            nameservers:
                addresses:
                    - 9.9.9.9
                    - 149.112.112.112
            routes:
                - to: default
                  via: WAN default gateway
                  metric: 100
        eth1:
            dhcp4: false
            dhcp6: false
            addresses:
                - 10.0.2.1/24
            nameservers:
                addresses:
                    - 9.9.9.9
                    - 149.112.112.112
    wifis:
        wlan0:
            access-points:
                coffeenet:
                    auth:
                        key-management: psk
                        password: "password"
    bridges:
        br0:
            interfaces:
                - eth1
                - wlan0
            addresses:
                - 10.0.2.1/24
            nameservers:
                addresses:
                    - 9.9.9.9
            - 149.112.112.112

Apply the netplan settings.

sudo netplan apply

IP forwarding

echo "net.ipv4.ip_forward=1" | sudo tee -a /etc/sysctl.conf
sudo sysctl -p

Firewalld

sudo firewall-cmd --permanent --zone=home --add-interface=br0
sudo firewall-cmd --permanent --zone=home --add-service={ssh,dns,http,https,dhcp}
sudo firewall-cmd --permanent --zone=home --add-forward
sudo firewall-cmd --permanent --zone=external --add-interface=eth0
sudo firewall-cmd --permanent --zone=external --add-service=dhcpv6-client
sudo firewall-cmd --permanent --zone=external --add-forward

Create /etc/firewalld/policies/masquerade.xml to allow traffic to flow from LAN to WAN.

<?xml version="1.0" encoding="utf-8"?>
<policy target="ACCEPT">
<masquerade/>
<ingress-zone name="home"/>
<egress-zone name="external"/>
</policy>

Reload the firewalld configuration.

sudo firewall-cmd --reload

RSS

Source: Simple RSS, Atom, and JSON feed for your blog

RSS

<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>Example website title</title>
        <link>https://example.com</link>
        <description>Example website description.</description>
        <atom:link href="https://example.com/rss.xml" rel="self" type="application/rss+xml" />
        <item>
            <title>Post one</title>
            <link>https://example.com/posts-one</link>
            <description>Post one content.</description>
            <guid isPermaLink="true">https://example.com/posts-one</guid>
            <pubDate>Mon, 22 May 2023 13:00:00 -0600</pubDate>
        </item>
        <item>
            <title>Post two</title>
            <link>https://example.com/posts-two</link>
            <description>Post two content.</description>
            <guid isPermaLink="true">https://example.com/posts-two</guid>
            <pubDate>Mon, 15 May 2023 13:00:00 -0600</pubDate>
        </item>
    </channel>
</rss>

Atom

<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
    <id>http://example.com/</id>
    <title>Example website title</title>
    <updated>2023-05-22T13:00:00.000Z</updated>
    <author>
        <name>John Doe</name>
    </author>
    <link href="https://example.com/atom.xml" rel="self" type="application/rss+xml" />
    <subtitle>Example website description.</subtitle>
    <entry>
        <id>https://example.com/posts-one</id>
        <title>Post one</title>
        <link href="https://example.com/posts-one"/>
        <updated>2023-05-22T13:00:00.000Z</updated>
        <summary type="html">https://example.com/posts-one</summary>
        <content type="html">Post one content.</content>
    </entry>
    <entry>
        <id>https://example.com/posts-two</id>
        <title>Post two</title>
        <link href="https://example.com/posts-two"/>
        <updated>2023-05-15T13:00:00.000Z</updated>
        <summary type="html">https://example.com/posts-two</summary>
        <content type="html">Post two content.</content>
    </entry>
</feed>

JSON

{
  "version": "https://jsonfeed.org/version/1.1",
  "title": "Example website title",
  "home_page_url": "https://example.com",
  "feed_url": "https://example.com/feed.json",
  "description": "Example website description.",
  "items": [
    {
      "id": "https://example.com/posts-one",
      "url": "https://example.com/posts-one",
      "title": "Post one content.",
      "content_text": "Post one content.",
      "date_published": "2023-05-22T13:00:00.000Z"
    },
    {
      "id": "https://example.com/posts-two",
      "url": "https://example.com/posts-two",
      "title": "Post two content.",
      "content_text": "Post two content.",
      "date_published": "2023-05-15T13:00:00.000Z"
    }
  ]
}

Resources

Steam

Capturing and sharing Proton logs

  • Right-click on the game in Steam and select Properties.
  • Under Launch Options, add the following.
PROTON_LOG=1 PROTON_LOG_DIR=/home/jas/steam-proton-logs %command%

game-performance

To use CachyOS’s game-performance script to switch power profiles while a game is running, add the following to each Steam game’s Launch Options:

game-performance %command%

Combined with the Proton logging options:

PROTON_LOG=1 PROTON_LOG_DIR=/home/jas/steam-proton-logs game-performance %command%

Pre-caching shaders with Proton-CachyOS, -GE, -EM

Pre-caching of shaders is not needed on CachyOS, as Proton-CachyOS already contains all the necessary codecs.

  • Go to Steam Setttings -> Downloads
  • Go to the SHADER PRE-CACHING section
  • Disable Shader Pre-caching
  • Disable Allow background processing of Vulkan shaders

Systemd

Install systemd-boot on Debian

sudo mkdir /boot/efi/loader
printf "default systemd\ntimeout 5\neditor 1\n" | sudo tee /boot/efi/loader/loader.conf
sudo mkdir -p /boot/efi/loader/entries
sudo apt install -y systemd-boot
sudo bootctl install --path=/boot/efi

Check efibootmgr.

sudo efibootmgr

Output:
BootOrder: 0000,0001
Boot0000* Linux Boot Manager

Mount NFS share

See NFS

UFW

Allow ports only on specific interface

sudo ufw allow in on tailscale0 to any port 22 proto tcp

Weechat

Smart filter for JOIN, PART, QUIT

/set irc.look.smart_filter on
/filter add irc_smart * irc_smart_filter *