Techne: A practical knowledge base
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 50
DOSPageInterval 1
DOSSiteInterval 1
DOSBlockingPeriod 60
DOSEmailNotify <hyperreal@moonshadow.dev>
</IfModule>
Restart apache2:
sudo systemctl restart apache2.service
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
| Argument | Description |
|---|---|
atopsar | atop’s system activity report |
-r /var/log/atop/atop_20240703 | Log file to use |
-m | Memory- and swap-occupation |
-R 1 | Summarize 1 sample into 1 sample. Log file contains samples of 10 minutes, so this will summarize each sample. -R 6 will summarize one sample per 60 minutes. |
| `awk ’NR<7{print $0;next}{print $0 | “sort -k 3,4”}’` |
head -11 | Get the top 11 lines of output |
Get top 3 memory processes for given analysis date
atopsar -G -r /var/log/atop/atop_20240710
Identify top-five most frequently executed process during logging period
atop -r /var/log/atop/atop_20241123 -P PRG | grep -oP "(?<=\()[[:alnum:]]{1,}(?=\))" | sort | uniq -c | sort -k1rn | head -5
Count the number of times a particular process 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 number of instances of a particular process 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 a PNG chart of the number of instances of a particular process during logging period
atop -r /var/log/atop/atop_20241123 -P PRG | 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,1280 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-ten 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 processes 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 logical volumes with high activity or high average queue during logging period
atopsar -r /var/log/atop/atop_20241123 -l -S | sed 's/\%//g' | awk -v k=50 -v j=100 '$3 > k || $8 > j' | sed -r 's/([0-9]{1,})/%/4'
Identify processes consuming more than half of all available CPUs during logging period
(( 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 memory utilization during logging period
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
Print nth line from file
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 should be the target line number
sed -i 'Nd' file.txt
Replace an entire line in file by line number
# N should be the target line number
sed -i 'Ns/.*/replacement-line/' file.txt
Heredoc
cat << EOF > file.txt
The current working directory is $PWD.
You 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 do not exist in packages:
comm -23 <(sort packages.fedora) <(sort packages)
Output:
autossh
bat
borgmatic
cmake
diff-so-fancy
- The
commcommand compares two sorted files line by line. - The
-23flag is shorthand for-2and-3. -2suppresses column 2 (lines unique topackages).-3suppresses 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
DefaultDependencies=yes | The mount unit automatically acquires Before=umount.target and Conflicts=umount.target. Local filesystems automatically gain After=local-fs-pre.target and Before=local-fs.target. Network mounts, such as NFS, automatically acquire After=remote-fs-pre.target network.target network-online.target and Before=remote-fs.target. |
Options=subvol=@internet_archive,compress=zstd:1 | Use the subvolume @internet_archive and use zstd compression level 1. |
Note that the name of the unit file, e.g.
mnt-internet_archive.mount, must correspond to theWhere=/mnt/internet_archivedirective, such that the filesystem path separator/in theWheredirective 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
Install Cgit with Caddy
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
Chimera Linux
Installation
Requirements
- UEFI
- LVM on LUKS with unencrypted
/boot
Disk partitioning
Use cfdisk to create the following partition layout.
| Partition Type | Size |
|---|---|
| EFI | +600M |
| boot | +900M |
| Linux | Remaining space |
Format the unencrypted partitions:
mkfs.vfat /dev/nvme0n1p1
mkfs.ext4 /dev/nvme0n1p2
Create LUKS on the remaining partition:
cryptsetup luksFormat /dev/nvme0n1p3
cryptsetup luksOpen /dev/nvme0n1p3 crypt
Create a LVM2 volume group for /dev/nvme0n1p3, which is located at /dev/mapper/crypt:
vgcreate chimera /dev/mapper/crypt
Create logical volumes in the volume group:
lvcreate --name swap -L 8G chimera
lvcreate --name root -l 100%FREE chimera
Create the filesystems for the logical volumes:
mkfs.ext4 /dev/chimera/root
mkswap /dev/chimera/swap
Create mount points for the chroot and mount the filesystems:
mkdir /media/root
mount /dev/chimera/root /media/root
mkdir /media/root/boot
mount /dev/nvme0n1p2 /media/root/boot
mkdir /media/root/boot/efi
mount /dev/nvme0n1p1 /media/root/boot/efi
Bootstrap
Chimera-bootstrap and chroot
chimera-bootstrap /media/root
chimera-chroot /media/root
Update the system:
apk update
apk upgrade --available
Install kernel, cryptsetup, and lvm2 packages:
apk add linux-stable cryptsetup-scripts lvm2
Fstab
genfstab / >> /etc/fstab
Crypttab
echo "crypt /dev/disk/by-uuid/$(blkid -s UUID -o value /dev/nvme0n1p3) none luks" > /etc/crypttab
Initramfs refresh
update-initramfs -c -k all
GRUB
apk add grub-x86_64-efi
grub-install --efi-directory=/boot/efi --target=x86_64-efi
Post-installation
passwd root
apk add zsh bash
useradd -c "Jeffrey Serio" -m -s /usr/bin/zsh -U jas
passwd jas
Add the following lines to /etc/doas.conf:
# Give jas access
permit nopass jas
Set hostname, timezone, and hwclock:
echo "falinesti" > /etc/hostname
ln -sf /usr/share/zoneinfo/America/Chicago /etc/localtime
echo localtime > /etc/hwclock
Xorg and Xfce4
apk add xserver-xorg xfce4
Reboot the machine.
Post-reboot
Login as jas. Run startxfce4. Connect to the internet via NetworkManager.
Ensure wireplumber and pipewire-pulse are enabled:
dinitctl enable wireplumber
dinitctl start wireplumber
dinitctl enable pipewire-pulse
dinitctl start pipewire-pulse
Install CPU microcode:
doas apk add ucode-intel
doas update-initramfs -c -k all
Install other packages
doas apk add chrony
doas dinitctl enable chrony
doas apk add ...
Containers
Self-hosted container registry with web UI
Source: https://github.com/Joxit/docker-registry-ui
Docker/Podman compose
services:
registry-ui:
image: joxit/docker-registry-ui:main
restart: always
ports:
- "127.0.0.1:4433:80"
environment:
- SINGLE_REGISTRY=true
- REGISTRY_TITLE=hyperreal's Container Registry
- DELETE_IMAGES=true
- SHOW_CONTENT_DIGEST=true
- NGINX_PROXY_PASS_URL=http://registry-server:5000
- SHOW_CATALOG_NB_TAGS=true
- CATALOG_MIN_BRANCHES=1
- CATALOG_MAX_BRANCHES=1
- TAGLIST_PAGE_SIZE=100
- REGISTRY_SECURED=false
- CATALOG_ELEMENTS_LIMIT=1000
container_name: registry-ui
registry-server:
image: registry:2.8.2
restart: always
environment:
REGISTRY_HTTP_HEADERS_Access-Control-Allow-Origin: '[http://aux-remote.carp-wyvern.ts.net]'
REGISTRY_HTTP_HEADERS_Access-Control-Allow-Methods: '[HEAD,GET,OPTIONS,DELETE]'
REGISTRY_HTTP_HEADERS_Access-Control-Allow-Credentials: '[true]'
REGISTRY_HTTP_HEADERS_Access-Control-Allow-Headers: '[Accept,Cache-Control]'
REGISTRY_HTTP_HEADERS_Access-Control-Expose-Headers: '[Docker-Content-Digest]'
REGISTRY_STORAGE_DELETE_ENABLED: 'true'
volumes:
- ./registry/data:/var/lib/registry
container_name: registry-server
Authorization and Authentication
For a public registry with authentication, the following headers are needed:
environment:
REGISTRY_HTTP_HEADERS_Access-Control-Allow-Headers: '[Authorization,Accept,Cache-Control]'
For a private registry without authentication, the following headers are needed:
environment:
REGISTRY_HTTP_HEADERS_Access-Control-Allow-Headers: '[Accept,Cache-Control]'
Caddy reverse proxy
Public registry
registry.hyperreal.coffee {
reverse_proxy localhost:4433
}
Private registry via Tailnet
aux-remote.carp-wyvern.ts.net {
reverse_proxy localhost:4433
}
Ensure the following is added to /etc/default/tailscaled:
TS_PERMIT_CERT_UID=caddy
The above will ensure Caddy receives SSL certs from the Tailscale daemon.
Debian
Install Debian with LUKS2 Btrfs and GRUB via Debootstrap
Source: https://gist.github.com/meeas/b574e4bede396783b1898c90afa20a30
- Use a Debian Live ISO
- Single LUKS2 encrypted partition
- Single Btrfs filesystem with @, @home, @swap, and other subvolumes
- Encrypted swapfile in Btrfs subvolume
- systemd-boot bootloader
- Optional removal of crypto keys from RAM during laptop suspend
- Optional configurations for laptops
Pre-installation setup
Boot into the live ISO, open a terminal, and become root. Install the needed packages.
sudo -i
apt update
apt install -y debootstrap cryptsetup arch-install-scripts
Create partitions.
cfdisk /dev/nvme0n1
- GPT partition table
- 512M
/dev/nvme0n1p1EFI System partition (EF00) - 100%+
/dev/nvme0n1p2Linux filesystem
mkfs.fat -F 32 -n EFI /dev/nvme0n1p1
cryptsetup -y -v --type luks2 luksFormat --label Debian /dev/nvme0n1p2
cryptsetup luksOpen /dev/nvme0n1p2 cryptroot
mkfs.btrfs /dev/mapper/cryptroot
Make Btrfs subvolumes.
mount /dev/mapper/cryptroot /mnt
btrfs subvolume create /mnt/@
btrfs subvolume create /mnt/@home
btrfs subvolume create /mnt/@swap
umount -lR /mnt
Re-mount subvolumes as partitions.
mount -t btrfs -o defaults,subvol=@,compress=zstd:1 /dev/mapper/cryptroot /mnt
mkdir -p /mnt/{boot,home}
mkdir /mnt/boot/efi
mount /dev/nvme0n1p1 /mnt/boot/efi
mount -t btrfs -o defaults,subvol=@home,compress=zstd:1 /dev/mapper/cryptroot /mnt/home
Setup swapfile.
mkdir -p /mnt/swap
mount -t btrfs -o subvol=@swap /dev/mapper/cryptroot /mnt/swap
touch /mnt/swap/swapfile
chmod 600 /mnt/swap/swapfile
chattr +C /mnt/swap/swapfile
btrfs property set ./swapfile compression none
dd if=/dev/zero of=/mnt/swap/swapfile bs=1M count=16384
mkswap /mnt/swap/swapfile
swapon /mnt/swap/swapfile
Base installation
Create a nested subvolume for /var/log under the @ subvolume. This will be automounted with @ so there is no need to add it to /etc/fstab. Nested subvolumes are not included in snapshots of the parent subvolume. Creating a nested subvolume for /var/log will ensure the log files remain untouched when we restore the rootfs from a snapshot.
mkdir -p /mnt/var
btrfs subvolume create /mnt/var/log
debootstrap --arch amd64 <suite> /mnt
Copy the mounted file systems table.
Bind the pseudo-filesystems for chroot.
mount --rbind /dev /mnt/dev
mount --rbind /sys /mnt/sys
mount -t proc proc /mnt/proc
Generate fstab.
genfstab -U /mnt >> /mnt/etc/fstab
Chroot into the new system.
cp -v /etc/resolv.conf /mnt/etc/
chroot /mnt
Configure the new installation
Set the timezone, locale, keyboard configuration, and console.
apt install -y locales
dpkg-reconfigure tzdata locales keyboard-configuration console-setup
Set the hostname.
echo 'hostname' > /etc/hostname
echo '127.0.1.1 hostname.localdomain hostname' >> /etc/hosts
Configure APT sources on /etc/apt/sources.list
deb https://deb.debian.org/debian <suite> main contrib non-free non-free-firmware
deb https://deb.debian.org/debian <suite>-updates main contrib non-free non-free-firmware
deb https://deb.debian.org/debian <suite>-backports main contrib non-free non-free-firmware
deb https://deb.debian.org/debian-security <suite>-security main contrib non-free non-free-firmware
Install essential packages.
apt update -t <suite>-backports
apt dist-upgrade -t <suite>-backports
apt install -y neovim linux-image-amd64 linux-headers-amd64 firmware-linux firmware-linux-nonfree sudo command-not-found systemd-timesyncd systemd-resolved cryptsetup cryptsetup-initramfs efibootmgr btrfs-progs grub-efi
Install desktop environment.
apt install task-gnome-desktop task-desktop task-ssh-server
If installing on a laptop:
sudo apt install -y task-laptop powertop
Create users and groups.
passwd root
adduser jas
echo "jas ALL=(ALL) NOPASSWD: ALL" | tee -a /etc/sudoers.d/jas
chmod 440 /etc/sudoers.d/jas
usermod -aG systemd-journal jas
Setting up the bootloader
Optional package for extra protection of suspended laptops.
apt install cryptsetup-suspend
Setup encryption parameters.
blkid -s UUID -o value /dev/nvme0n1p2
Edit /etc/crypttab.
cryptroot UUID=<uuid> none luks
Setup bootloader.
grub-install --target=x86_64-efi --efi-directory=/boot/efi --recheck --bootloader-id="Debian"
Edit /etc/default/grub.
GRUB_CMDLINE_LINUX_DEFAULT=""
GRUB_CMDLINE_LINUX=""
GRUB_ENABLE_CRYPTODISK=y
GRUB_TERMINAL=console
Update grub.
update-grub
Exit chroot and reboot.
exit
umount -lR /mnt
reboot
Emergency recovery from live ISO
sudo -i
cryptsetup luksOpen /dev/nvme0n1p2 cryptroot
mount -t btrfs -o defaults,subvol=@,compress=zstd:1 /dev/mapper/cryptroot /mnt
mount /dev/nvme0n1p1 /mnt/boot/efi
mount -t btrfs -o defaults,subvol=@home,compress=zstd:1 /dev/mapper/cryptroot /mnt/home
mount -t btrfs -o subvol=@swap /dev/mapper/cryptroot /mnt/swap
swapon /mnt/swap/swapfile
mount --rbind /dev /mnt/dev
mount --rbind /sys /mnt/sys
mount -t proc proc /mnt/proc
chroot /mnt
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 1000 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
Firewalld
Allow connections only from tailnet
Create a new zone for the tailscaled0 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
Install the ports tree
Source: Chapter 4. Installing Applications: Packages and Ports | FreeBSD Documentation Portal
Ensure the FreeBSD source code is checked out
sudo git clone -o freebsd -b releng/14.1 https://git.FreeBSD.org/src.git /usr/src
Check out 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 filesystems 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 of the 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, remote 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 else 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 the ZFS volumes for Linux bhyve VM
zfs create -V128G -o volmode=dev zroot/debianvm
Downloading 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 install
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 using the following command:
sudo jexec debian chroot /compat/debian /bin/bash
Internet Archive
Install Python command line client
pipx install internetarchive
Use Python client to download torrent files from given collection
Ensure “Automatically add torrents from” > Monitored Folder is set to /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_filesby qBittorrent once they are added to the instance.
Remove empty directories
find /mnt/torrent_files -maxdepth 1 -mindepth 1 -type d -delete
Kernel
Disable core dumps in Linux
limits.conf and sysctl
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
sudo sysctl -p /etc/sysctl.d/9999-disable-core-dump.conf
/bin/falseexits with a failure status code. The default value forkernel.core_patterniscoreon a Debian server and|/usr/lib/systemd/systemd-coredump %P %u %g %s %t %c %hon a Fedora desktop. These commands are executed upon crashes. In the case of/bin/false, nothing happens, and core dump is disabled.fs.suid_dumpable=0Any process that has changed privilege levels or is execute only will not be dumped. Other values include1, which is debug mode, and all processes dump core when possible. The current user owns the core dump, no security is applied.2, suidsafe mode, in which any Linux program that would generally not be dumped is dumped regardless, but only if thekernel.core_patternis sysctl is set to a valid program.
systemd
sudo mkdir /etc/systemd/coredump.conf.d/
sudo nvim /etc/systemd/coredump.conf.d/custom.conf
[Coredump]
Storage=none
ProcessSizeMax=0
Storage=noneandProcessSizeMax=0disables all coredump handling except for a log entry under systemd.
sudo systemctl daemon-reload
Edit /etc/systemd/system.conf. Make sure DefaultLimitCORE is commented out.
#DefaultLimitCORE=infinity
sudo systemctl daemon-reexec
Lutris
Add game-performance
- Go to Preferences -> Global options.
- Enable Advanced mode.
- Scroll down to Command prefix.
- Add
game-performanceto 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 1000GB:
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 /
MinIO
Bucket replication to remote MinIO instance
Use mcli to create aliases for the local and remote instances.
mcli alias set nas-local http://localhost:9000 username password
mcli alias set nas-remote http://ip.addr:9000 username password
Add configuration rule on source bucket for nas-local to nas-remote to replicate all operations in an active-active replication setup.
mcli replicate add nas-local/sourcebucket --remote-bucket nas-remote/targetbucket --priority 1
Show replication status.
mcli replicate status nas-local/sourcebucket
FreeBSD setup
Create a ZFS dataset to store MinIO data.
sudo zfs create naspool/minio_data
Install the MinIO package.
sudo pkg install -y minio
Configure the MinIO daemon settings in /etc/rc.conf.
minio_enable="YES"
minio_disks="/naspool/minio_data"
Set the required permissions on /naspool/minio_data.
sudo chown -R minio:minio /naspool/minio_data
sudo chmod u+rxw /naspool/minio_data
Start the MinIO daemon.
sudo service minio start
Check the logs for any important info.
sudo grep "minio" /var/log/messages
Neovim cheatsheet
NERDCommenter
| Mode | Keybind | Action |
|---|---|---|
| normal | SPACE + cc | NERDCommenterComment |
| normal | SPACE + c + SPACE | NERDCommenterToggle |
| normal | SPACE + cm | NERDCommenterMinimal |
| normal | SPACE + cn | NERDCommenterNested |
| normal | SPACE + c$ | NERDCommenterToEOL |
| normal | SPACE + ci | NERDCommenterInvert |
| normal | SPACE + cs | NERDCommenterSexy |
| normal | SPACE + cy | NERDCommenterYank |
| normal | SPACE + cA | NERDCommenterAppend |
| normal | SPACE + cl | NERDCommenterAlignLeft |
| normal | SPACE + cb | NERDCommenterAlignBoth |
| normal | SPACE + cu | NERDCommenterUncomment |
| normal | SPACE + ca | NERDCommenterAltDelims |
Search
| Mode | Keybind | Action |
|---|---|---|
| normal | SPACE + sh | [S]earch [H]elp |
| normal | SPACE + sk | [S]earch [K]eymaps |
| normal | SPACE + sf | [S]earch [F]iles |
| normal | SPACE + ss | [S]earch [S]elect Telescope |
| normal | SPACE + sw | [S]earch current [W]ord |
| normal | SPACE + sg | [S]earch by [G]rep |
| normal | SPACE + sd | [S]earch [D]iagnostics |
| normal | SPACE + sr | [S]earch [R]esume |
| normal | SPACE + s. | [S]earch Recent Files (“.” for repeat) |
| normal | SPACE + SPACE | [ ] Find existing buffers |
| normal | SPACE + / | [/] Fuzzily search in current buffer |
| normal | SPACE + s/ | [S]earch [/] in Open Files |
| normal | SPACE + sn | [S]earch [N]eovim files |
| normal | Esc | nohlsearch |
Movement
| Mode | Keybind | Action |
|---|---|---|
| normal | h | Move cursor left |
| normal | l | Move cursor right |
| normal | j | Move cursor down |
| normal | k | Move cursor up |
| normal | Ctrl-H | Move focus to the left window |
| normal | Ctrl-L | Move focus to the right window |
| normal | Ctrl-J | Move focus to the lower window |
| normal | Ctrl-K | Move focus to the upper window |
Editing
| Mode | Keybind | Action |
|---|---|---|
| normal | sa | Add surrounding |
| normal | sd | Delete surrounding |
| normal | sr | Replace surrounding |
| normal | sf | Find right surrounding |
| normal | sF | Find left surrounding |
| normal | sh | Highlight surrounding |
| normal | Shift-o | Add empty line below cursor |
| normal | Shift-O | Add empty line above cursor |
| normal | SPACE + f | Format buffer |
| insert | Arrows | Traverse completion menu |
| insert | Ctrl-Y | Select completion |
Diagnostics
| Mode | Keybind | Action |
|---|---|---|
| normal | ]d | Jump to next diagnostic in current buffer |
| normal | ]D | Jump to last diagnostic in current buffer |
| normal | [d | Jump to previous diagnostic in current buffer |
| normal | [D | Jump to first diagnostic in current buffer |
| normal | SPACE + q | Open diagnostic [Q]uickfix list |
| normal | Ctrl-W + d | Show diagnostics under cursor |
Splits and tabs
| Mode | Keybind | Action |
|---|---|---|
| command | :sp | Split window horizontally |
| command | :vsp | Split window vertically |
| command | :tabe | Create a new tab |
| command | :tabc | Close the current tab |
| command | :tabn | Go to next tab |
| command | :tabp | Go 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-names.rules as the filename ensures the rule is ordered /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/networkd/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.
Nextcloud
Migrating
Install dependencies.
sudo apt update
sudo apt dist-upgrade
sudo apt install apache2 mariadb-server libapache2-mod-php php-gd php-mysql php-curl php-mbstring php-intl php-gmp php-bcmath php-xml php-imagick php-zip php-apcu redis-server
Setup the database.
sudo mysql
CREATE USER 'nextcloud'@'localhost' IDENTIFIED BY 'password';
CREATE DATABASE IF NOT EXISTS nextcloud CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;
GRANT ALL PRIVILEGES ON nextcloud.* TO 'nextcloud'@'localhost';
FLUSH PRIVILEGES;
quit;
On the original machine, put Nextcloud into maintenance mode.
cd /var/www/nextcloud
sudo -u www-data php occ maintenance:mode --on
Wait 6-7 minutes for the sync clients to register the server is in maintenance mode before proceeding.
Stop the web server that runs Nextcloud.
sudo systemctl stop apache2.service
Copy over the Nextcloud directory to the new machine.
rsync -aAX /var/www/nextcloud root@new-machine:/var/www
Copy the PHP configurations to the new machine.
rsync -aAX /etc/php/8.2/apache2/ root@new-machine:/etc/php/8.2/apache2
rsync -aAX /etc/php/8.2/cli/ root@new-machine:/etc/php/8.2/cli
The PHP version on the new machine must match that from the original machine.
On the new machine, ensure /etc/php/8.2/mods-available/apcu.ini is configured correctly.
extension=apcu.so
apc.enable_cli=1
On the new machine, ensure permissions are set correctly on /var/www/nextcloud.
sudo chown -R www-data:www-data /var/www/nextcloud
On the original machine, dump the database.
mysql --single-transaction --default-character-set=utf8mb4 -h localhost -u nextcloud -p nextcloud > nextcloud-sqlbkp.bak
Copy the database backup to the new machine.
rsync -aAX nextcloud-sqlbkp.bak root@new-machine:/root
On the new machine, import the database backup.
mysql -h localhost -u nextcloud -p -e "DROP DATABASE nextcloud"
mysql -h localhost -u nextcloud -p -e "CREATE DATABASE nextcloud CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci"
mysql -h localhost -u nextcloud -p nextcloud < /root/nextcloud-sqlbkp.bak
On the new machine, ensure redis-server service is started.
sudo systemctl enable --now redis-server.service
On the new machine, run the following command to update the data-fingerprint:
cd /var/www/nextcloud
sudo -u www-data php occ maintenance:data-fingerprint
Ensure DNS records are changed to the new machine and the web server is running before taking Nextcloud out of maintenance mode.
On the new machine, take Nextcloud out of maintenance mode.
cd /var/www/nextcloud
sudo -u www-data php occ maintenance:mode --off
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
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.65:/is relative to the exported directory. So/mnt/sharedon the client is/sharedon the server. If you try to mount withmount -t nfs 10.0.0.64:/shared /mnt/sharedyou will get a no such file or directory error.
/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
| Example | Description |
|---|---|
nmap 192.168.1.1 | Scan a single IP |
nmap 192.168.1.1 192.168.2.1 | Scan specific IPs |
nmap 192.168.1.1-254 | Scan a range |
nmap scanme.nmap.org | Scan a domain |
nmap 192.168.1.0/24 | Scan using CIDR notation |
nmap -iL targets.txt | Scan targets from a file |
nmap -iR 100 | Scan 100 random hosts |
nmap -exclude 192.168.1.1 | Exclude listed hosts |
Nmap scan techniques
| Example | Description |
|---|---|
nmap 192.168.1.1 -sS | TCP SYN port scan (default) |
nmap 192.168.1.1 -sT | TCP connect port scan |
nmap 192.168.1.1 -sU | UDP port scan |
nmap 192.168.1.1 -sA | TCP ACK port scan |
nmap 192.168.1.1 -sW | TCP Window port scan |
nmap 192.168.1.1 -sM | TCP Maimon port scan |
Host discovery
| Example | Description |
|---|---|
nmap 192.168.1.1-3 -sL | No scan. List targets only |
nmap 192.168.1.1/24 -sn | Disable port scanning. Host discovery only. |
nmap 192.168.1.1-5 -Pn | Disable host discovery. Port scan only. |
nmap 192.168.1.1-5 -PS22-25,80 | TCP SYN discovery on ports 22-25, 80 (Port 80 by default) |
nmap 192.168.1.1-5 -PA22-25,80 | TCP ACK discovery on ports 22-25, 80 (Port 80 by default) |
nmap 192.168.1.1-5 -PU53 | UDP discovery on port 53. (Port 40125 by default) |
nmap 192.168.1.1-1/24 -PR | ARP discovery on local network |
nmap 192.168.1.1 -n | Never do DNS resolution |
Port specification
| Example | Description |
|---|---|
nmap 192.168.1.1 -p 21 | Port scan for port 21 |
nmap 192.168.1.1 -p 21-100 | Port scan for range 21-100 |
nmap 192.168.1.1 -p U:53,T:21-25,80 | Port scan multiple TCP and UDP ports |
nmap 192.168.1.1 -p- | Port scan all ports |
nmap 192.168.1.1 -p http,https | Port scan from service name |
nmap 192.168.1.1 -F | Fast port scan (100 ports) |
nmap 192.168.1.1 -top-ports 2000 | Port scan the top 2000 ports |
nmap 192.168.1.1 -p-65535 | Leaving 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
| Example | Description |
|---|---|
nmap 192.168.1.1 -sV | Attempts to determine version of the service running on port. |
nmap 192.168.1.1 -sV -version-intensity 8 | Intensity level 0-9. Higher number increases possibility of correctness. |
nmap 192.168.1.1 -sV -version-light | Enable light mode. Lower possibility of correctness. Faster. |
nmap 192.168.1.1 -sV -version-all | Enable intensity level 9. Higher possibility of correctness. Slower. |
nmap 192.168.1.1 -A | Enable OS detection, version detection, script scanning, and traceroute. |
OS detection
| Example | Description |
|---|---|
nmap 192.168.1.1 -O | Remote OS detection using TCP/IP stack fingerprinting |
nmap 192.168.1.1 -O -osscan-limit | If 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-guess | Makes nmap guess more aggressively. |
nmap 192.168.1.1 -O -max-os-tries 1 | Set the maximum number of OS detection tries |
nmap 192.168.1.1 -A | Enables OS detection, version detection, script scanning, and traceroute. |
Timing and performance
| Example | Description |
|---|---|
nmap 192.168.1.1 -T0 | Paranoid (0) IDS evasion |
nmap 192.168.1.1 -T1 | Sneaky (1) IDS evasion |
nmap 192.168.1.1 -T2 | Polite (2) slows down the scan to use less bandwidth and use less target machine resources. |
nmap 192.168.1.1 -T3 | Normal (3) which is default speed. |
nmap 192.168.1.1 -T4 | Aggressive (4) speed scans. Assumes you are on a reasonably fast and reliable network. |
nmap 192.168.1.1 -T5 | Insane (5) speed scan. Assumes you are on an extraordinarily fast network. |
Timing and performance switches
| Example | Description |
|---|---|
-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 50 | Parallel host scan group sizes |
-min-parallelism/max-parallelism 10 | Probe parallelization |
-max-retries 3 | Specify the max number of port scan probe retransmissions. |
-min-rate 100 | Send packets to no slower than 100 per second |
-max-rate 100 | Send packets no faster than 100 per second |
NSE scripts
| Example | Description |
|---|---|
nmap 192.168.1.1 -sC | Scan with default NSE scripts. Useful and safe. |
nmap 192.168.1.1 -script default | Scan with default NSE scripts. |
nmap 192.168.1.1 -script=banner | Scan 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,banner | Scan 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.1 | NSE script with arguments |
Useful NSE script examples
| Example | Description |
|---|---|
nmap -Pn -script=http-sitemap-generator scanme.nmap.org | http site map generator |
nmap -n -Pn -p 80 -open -sV -vvv -script banner,http-title -iR 1000 | Fast search for random web servers |
nmap -Pn -script=dns-brute domain.com | Brute 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.1 | Safe SMB scripts to run |
nmap -script whois* domain.com | Whois query |
nmap -p80 -script http-unsafe-output-escaping scanme.nmap.org | Detect cross site scripting vulnerabilities |
nmap -p80 -script http-sql-injection scanme.nmap.org | Check for SQL injections |
Firewall/IDS evasion and spoofing
| Example | Description |
|---|---|
nmap 192.168.1.1 -f | Requested scan (including ping scans) use tiny fragmented IP packets. Harder for packet filters. |
nmap 192.168.1.1 -mtu 32 | Set your own offset size |
nmap -D 192.168.1.101,192.168.1.102,192.168.1.103 | Send scans from spoofed IPs |
nmap -D decoy-ip1,decoy-ip2,your-own-ip | Same as above |
nmap -S www.microsoft.com www.facebook.com | Scan Facebook from Microsoft (-e eth0 -Pn may be required |
nmap -g 53 192.168.1.1 | Use given source port number |
nmap -proxies http://192.168.1.1:8080,http://192.168.1.2:8080 192.168.1.1 | Relay connections through HTTP/SOCKS4 proxies |
nmap -data-length 200 192.168.1.1 | Appends random data to sent packets |
Output
| Example | Description |
|---|---|
nmap 192.168.1.1 -oN normal.file | Normal output to the file normal.file |
nmap 192.168.1.1 -oX xml.file | XML output to the file xml.file |
nmap 192.168.1.1 -oG grep.file | Grepable output to the file grep.file |
nmap 192.168.1.1 -oA results | Output 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-output | Append a scan to a previous scan file |
nmap 192.168.1.1 -v | Increase verbosity level (use -vv or more) |
nmap 192.168.1.1 -d | Increase debugging level (use -dd or more) |
nmap 192.168.1.1 -reason | Display the reason a port is in a particular state, same output as -vv |
nmap 192.168.1.1 -open | Only show open (or possibly open) ports |
nmap 192.168.1.1 -T4 -packet-trace | Show all packets sent and received |
nmap -iflist | Shows the host interfaces and routes |
nmap -resume results.file | Resume a scan from results.file |
Helpful nmap output examples
| Example | Description |
|---|---|
| `nmap -p80 -sV -oG - -open 192.168.1.1/24 | grep open` |
| `nmap -iR 10 -n -oX out.xml | grep “Nmap” |
| `nmap -iR 10 -n -oX out2.xml | grep “Nmap” |
ndiff scan.xml scan2.xml | Compare the output of two scan results |
xsltproc nmap.xml -o nmap.html | Convert nmap xml files to html files |
Other useful nmap commands
| Example | Description |
|---|---|
nmap -iR 10 -PS22-25,80,113,1050,35000 -v -sn | Discovery only on ports X, no port scan |
nmap 192.168.1.1-1/24 -PR -sn -vv | ARP discovery only on local network, no port scan |
nmap -iR 10 -sn -traceroute | Traceroute to random targets, no port scan |
nmap 192.168.1.1-50 -sL -dns-server 192.168.1.1 | Query the internal DNS for hosts, list targets only |
nmap 192.168.1.1 --packet-trace | Show 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-ptto 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/dir
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 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 the size you want.
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
Target Source
-----------------------------------------------
vda /var/lib/libvirt/images/vm1.img
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 filesystems.
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
Target Source
-------------------------------------------------
vda /var/lib/libvirt/images/vm1.qcow2
Begin the backup.
sudo virsh backup-begin vm1
Backup started
Check the job status. “None” means the job has likely completed.
sudo virsh domjobinfo vm1
Job type: None
Check the completed job status.
sudo virsh domjobinfo vm1 --completed
Job type: Completed
Operation: Backup
Time elapsed: 182 ms
File processed: 39.250 MiB
File remaining: 0.000 B
File total: 39.250 MiB
Now we see the copy of the backup.
sudo ls -lash /var/lib/libvirt/images/vm1.qcow2*
15M -rw-r--r--. 1 qemu qemu 15M May 10 12:22 vm1.qcow2
21M -rw-------. 1 root root 21M May 10 12:23 vm1.qcow2.1620642185
RAID
Mounting RAID1 mirror
/dev/sda1/dev/sdb1
Assemble the RAID array.
sudo mdadm --assemble --run /dev/md0 /dev/sda1 /dev/sdb1
Mount the RAID device.
sudo mount /dev/md0 /mnt
Configure msmtp for mdmonitor.service (Ubuntu 24.04)
sudo apt install msmtp msmtp-mta
Edit /etc/msmtprc.
# Resend account
account resend
host smtp.resend.com
from admin@hyperreal.coffee
port 2587
tls on
tls_starttls on
tls_trust_file /etc/ssl/certs/ca-certificates.crt
auth on
user resend
password APIKEY GO HERE
syslog LOG_MAIL
Edit /etc/mdadm.conf.
MAILADDR hyperreal@moonshadow.dev
MAILFROM admin@hyperreal.coffee
PROGRAM msmtp
ARRAY ...
ARRAY ...
Rename sendmail and symlink msmtp to sendmail.
sudo mv /usr/sbin/sendmail /usr/sbin/sendmail.bak
sudo ln -s /usr/bin/msmtp /usr/sbin/sendmail
Send a test email.
sudo mdadm --monitor --scan --test --oneshot
Restart mdmonitor.service.
Resident Evil HD
Installation
- Download Resident Evil Classic Triple Pack PC from archive.org. This contains the Sourcenext versions of all three games.
- Install all three games using their installers.
- Download the following files:
- Biohazard PC CD-ROM Mediakite patch version 1.01
- Resident Evil Classic REbirth
- Resident Evil 2 Classic REbirth
- Resident Evil 3 Classic REbirth
- Biohazard Mediakite
- Resident Evil HD mod by TeamX
- Resident Evil 2 HD mod by TeamX
- Resident Evil 3 HD mod by TeamX
- Resident Evil Seamless HD Project v1.1
- Resident Evil 2 Seamless HD Project v2.0
- Resident Evil 3: Nemesis Seamless HD Project v2.0
- Open the Biohazard Mediakite disc image with 7zip and drag the JPN folder from the disc into
C:\Program Files (x86)\Games Retro\Resident Evil Classic
Resident Evil Director’s Cut
Extract the following files to %ProgramFiles(x86)%\Games Retro\Resident Evil Classic:
Biohazard.exefrom Mediakite v1.01ddraw.dllfrom 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.dllfrom 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.dllfrom 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 neovim firewalld fail2ban atop htop python3-dev nmap tcpdump rsync rsyslog iptraf-ng iftop sysstat conntrack logwatch unattended-upgrades byobu
curl -fsSL https://tailscale.com/install.sh | sh
Register router as Tailnet node.
sudo systemctl enable --now tailscaled.service
sudo tailscale up
Netplan with DHCP WAN
/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
RPM repository
Create an RPM repository
Install dependencies
sudo dnf install -y gnupg createrepo dnf-utils rpm-sign wget
Setup GnuPG
echo "%echo Generating a PGP key
Key-Type: RSA
Key-Length: 4096
Name-Real: Jeffrey Serio
Name-Email: hyperreal@moonshadow.dev
Expire-Date: 0
%no-ask-passphrase
%no-protection
%commit" > ~/hyperreal-pgp-key.batch
gpg --no-tty --batch --gen-key ~/hyperreal-pgp-key.batch
Export the public key.
gpg --armor --export "Jeffrey Serio" > ~/hyperreal-pgp-key.pub
Export the private key to back it up somewhere safe.
gpg --armor --export-secret-keys "Jeffrey Serio" > ~/hyperreal-pgp-key.sec
After backing it up, shred it from the working directory.
shred -xu ~/hyperreal-pgp.key.sec
Setup RPM signing
Replace E1933532750E9EEF with your key’s ID.
echo "%_signature gpg
%_gpg_name E1933532750E9EEF" > ~/.rpmmacros
Create a directory to serve the repository.
mkdir -p ~/rpm-repo/packages
Move RPM packages into the repo directory. Then sign them with the following command:
rpm --addsign ~/rpm-repo/packages/*.rpm
Create repo index
Once all the packages are signed, create the repository with the following command:
createrepo ~/rpm-repo/packages/
The above command will create a directory in the repo named repodata containing a file named repomd.xml.
Note that the
createrepomust be run against each directory in the repo containing .rpm files.
Now sign the repo metadata with the following command:
gpg --detach-sign --armor ~/rpm-repo/packages/repodata/repomd.xml
Create a .repo file
echo "[hyperreal-kernel-bazzite]
name=hyperreal kernel bazzite $releasever
baseurl=https://rpm.hyperreal.coffee/kernel-bazzite/fedora-$releasever/$basearch
enabled=1
gpgcheck=1
gpgkey=https://rpm.hyperreal.coffee/hyperreal-pgp-key.pub" > ~/rpm-repo/hyperreal-kernel-bazzite.repo
The RPM repository should now be ready to be served on a web server with ~/rpm-repo as the web root.
Example Caddy configuration
rpm.hyperreal.coffee {
root * /home/jas/rpm-repos/
file_server browse
}
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
- The RSS 2.0 Specification
- The Atom Syndication Format Specification
- The JSON Feed Version 1.1 Specification
- RSS and Atom Feed validator
- JSON Feed validator
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 and -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
Create a unit file at /etc/systemd/system/mnt-backup.mount. The name of the unit file must match the Where directive. Ex. Where=/mnt/backup –> mnt-backup.mount.
[Unit]
Description=borgbackup NFS share from TrueNAS (10.0.0.81)
DefaultDependencies=no
Conflicts=umount.target
After=network-online.target remote-fs.target
Before=umount.target
[Mount]
What=10.0.0.81:/mnt/coffeeNAS/backup
Where=/mnt/backup
Type=nfs
Options=defaults
[Install]
WantedBy=multi-user.target
Tailscale
Using Codeberg as an OIDC provider
These steps also apply to Gitea and Forgejo instances.
Requirements
- Use moonshadow.dev for the domain.
- Use hyperreal@moonshadow.dev for the email. This must be the primary email on your Codeberg, Gitea, or Forgejo account.
- Setup a web server to host the webfinger file at moonshadow.dev.
Webfinger
In the web root of the web server, create .well-known/webfinger.
{
"subject": "acct:hyperreal@moonshadow.dev",
"links": [
{
"rel": "http://openid.net/specs/connect/1.0/issuer",
"href": "https://codeberg.org"
}
]
}
Use the Webfinger lookup tool to make sure it is setup correctly.
Create an OAuth2 application on Codeberg
Go to User Settings -> Applications -> Manage OAuth2 applications.
| Application name | tailscale |
| Redirect URI | https://login.tailscale.com/a/oauth_response |
| Confidential client | Checked |
Click on Create. Copy and save the Client ID and Client secret that were generated.
Sign up with Tailscale
- Go to the Tailscale login page, and select “Sign up with OIDC”.
- Enter hyperreal@moonshadow.dev for the email.
- Choose Codeberg as the identity provider. (optional)
- Select “Get OIDC Issuer”.
- Enter the Client ID and Client secret saved from the OAuth2 application. Leave everything else as default, and make sure that “consent” is checked under Prompts.
- Click “Sign up with OIDC”, and you should be able to login to Tailscale and be redirected to your Tailscale admin console.
Resources
Torrenting
Setup a FreeBSD thick VNET jail for torrenting Anna’s Archive
Setup the VNET bridge
Create the bridge.
ifconfig bridge create
Attach the bridge to the main network interface. igc0 in this case. For some reason, the resulting bridge device is named igb0bridge, rather than bridge0.
ifconfig igb0bridge addm igc0
To make this persistent across reboots, add the following to /etc/rc.conf.
defaultrouter="10.0.0.1"
cloned_interfaces="igb0bridge"
ifconfig_igc0bridge="inet 10.0.0.8/24 addm igc0 up"
Create the classic (thick) jail
Create the ZFS datasets for the jails. We’ll use basejail as a template for subsequent jails.
zfs create -o mountpoint=/jails naspool/jails
zfs create naspool/jails/basejail
Use the bsdinstall utility to bootstrap the base system to the basejail.
export DISTRIBUTIONS="base.txz"
export BSDINSTALL_DISTSITE=https://download.freebsd.org/ftp/releases/amd64/14.2-RELEASE/
bsdinstall jail /jails/basejail
Run freebsd-update to update the base jail.
freebsd-update -b /jails/basejail fetch install
freebsd-update -b /jails/basejail IDS
We now snapshot the basejail and create a clone of this snapshot for the torrenting jail that we will use for Anna’s Archive.
zfs snapshot naspool/jails/basejail@`freebsd-version`
zfs clone naspool/jails/basejail@`freebsd-version` naspool/jails/torrenting
We now use the following configuration for /etc/jail.conf.
torrenting {
exec.consolelog = "/var/log/jail_console_${name}.log";
allow.raw_sockets;
exec.clean;
mount.devfs;
devfs_ruleset = 11;
path = "/jails/${name}";
host.hostname = "${name}";
vnet;
vnet.interface = "${epair}b";
$id = "127";
$ip = "10.0.0.${id}/24";
$gateway = "10.0.0.1";
$bridge = "igb0bridge";
$epair = "epair${id}";
exec.prestart = "/sbin/ifconfig ${epair} create up";
exec.prestart += "/sbin/ifconfig ${epair}a up descr jail:${name}";
exec.prestart += "/sbin/ifconfig ${bridge} addm ${epair}a up";
exec.start += "/sbin/ifconfig ${epair}b ${ip} up";
exec.start += "/sbin/route add default ${gateway}";
exec.start += "/bin/sh /etc/rc";
exec.stop = "/bin/sh /etc/rc.shutdown";
exec.poststop = "/sbin/ifconfig ${bridge} deletem ${epair}a";
exec.poststop += "/sbin/ifconfig ${epair}a destroy";
}
Now we create the devfs ruleset to enable access to devices under /dev inside the jail. Add the following to /etc/devfs.rules.
[devfsrules_jail_vnet=11]
add include $devfsrules_hide_all
add include $devfsrules_unhide_basic
add include $devfsrules_unhide_login
add include $devfsrules_jail
add path 'tun*' unhide
add path 'bpf*' unhide
Enable the jail utility in /etc/rc.conf.
sysrc jail_enable="YES"
sysrc jail_parallel_start="YES"
Start the jail service for torrenting jail.
service jail start torrenting
Setting up Wireguard inside the jail
Since we have the /dev/tun* devfs rule, we now need to install Wireguard inside the jail.
jexec -u root torrenting
pkg install wireguard-tools wireguard-go
Download a Wireguard configuration for ProtonVPN, and save it to /usr/local/etc/wireguard/wg0.conf.
Enable Wireguard to run when the jail boots up.
sysrc wireguard_enable="YES"
sysrc wireguard_interfaces="wg0"
Start the Wireguard daemon and make sure you are connected to it properly.
service wireguard start
curl ipinfo.io
The curl command should display the IP address of the Wireguard server defined in /usr/local/etc/wireguard/wg0.conf.
Setting up qBittorrent inside the jail
Install the qbittorrent-nox package.
pkg install -y qbittorrent-nox
Before running the daemon from /usr/local/etc/rc.d/qbittorrent, we must run the qbittorrent command from the shell so that we can see the default password generated for the web UI. For some reason it is not shown in any logs, and the qbittorrent nox manpage wrongly says the password is “adminadmin”.
pkg install -y sudo
sudo -u qbittorrent qbittorrent-nox --profile=/var/db/qbittorrent/conf --save-path=/var/db/qbittorrent/Downloads --confirm-legal-notice
Copy the password displayed after running the command. Login to the qBittorrent web UI at http://10.0.0.127:8080 with login admin and the password you copied. In the web UI, open the options menu and go over to the Web UI tab. Change the login password to your own. Save the options to close the menu.
Now press CTRL-c to stop the qbittorrent-nox process. Make the following changes to the torrenting jail’s /etc/rc.conf.
sysrc qbittorrent_enable="YES"
sysrc qbittorrent_flags="--confirm-legal-notice"
Enable the qBittorrent daemon.
service qbittorrent start
Go back to the web UI at http://10.0.0.127:8080. Go to the options menu and go over to the Advanced tab, which is the very last tab. Change the network interface to wg0.
Finding the forwarded port that the ProtonVPN server is using
Install the libnatpmp package.
Make sure that port forwarding is allowed on the server you’re connected to, which it should be if you enabled it while creating the Wireguard configuration on the ProtonVPN website. Run the natpmpc command against the ProtonVPN Wireguard gateway.
natpmpc -g 10.2.0.1
If the output looks like the following, you’re good.
initnatpmp() returned 0 (SUCCESS)
using gateway : 10.2.0.1
sendpublicaddressrequest returned 2 (SUCCESS)
readnatpmpresponseorretry returned 0 (OK)
Public IP address : 62.112.9.165
epoch = 58081
closenatpmp() returned 0 (SUCCESS)
Now create the UDP and TCP port mappings, then loop natpmpc so that it doesn’t expire.
while true ; do date ; natpmpc -a 1 0 udp 60 -g 10.2.0.1 && natpmpc -a 1 0 tcp 60 -g 10.2.0.1 || { echo -e "ERROR with natpmpc command \a" ; break ; } ; sleep 45 ; done
The port allocated for this server is shown on the line that says “Mapped public port XXXXX protocol UDP to local port 0 lifetime 60”. Port forwarding is now activated. Copy this port number and, in the qBittorrent web UI options menu, go to the Connections tab and enter it into the “Port used for incoming connections” box. Make sure to uncheck the “Use UPnP/NAT-PMP port forwarding from my router” box.
If the loop terminates, you’ll need to re-run this loop script each time you start a new port forwarding session or the port will only stay open for 60 seconds.
P2P NAT port forwarding script with supervisord
Install supervisord.
sudo pkg install -y py311-supervisor
Enable the supervisord service.
sudo sysrc supervisord_enable="YES"
Edit /usr/local/etc/supervisord.conf, and add the following to the bottom of the file.
[program:natpmpcd]
command=/usr/local/bin/natpmpcd
autostart=true
Add the following contents to a file at /usr/local/bin/natpmpcd.
#!/bin/sh
port=$(/usr/local/bin/natpmpc -a 1 0 udp 60 -g 10.2.0.1 | grep "Mapped public port" | awk '{print $4}')
echo $port | tee /usr/local/etc/natvpn_port.txt
while true; do
date
if ! /usr/local/bin/natpmpc -a 1 0 udp 60 -g 10.2.0.1 && /usr/local/bin/natpmpc -a 1 0 tcp 60 -g 10.2.0.1; then
echo "error Failure natpmpc $(date)"
break
fi
sleep 45
done
Ensure the script is executable with chmod +x /usr/local/bin/natpmpcd.
supervisord will start the above shell script automatically. Ensure supervisord service is started.
sudo service supervisord start
The script will print out the forwarded port number at /usr/local/etc/natvpn_port.txt.
cat /usr/local/etc/natvpn_port.txt
48565
UFW
Allow ports only on specific interface
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 *
Windows
- Download Windows 11 ISO from Microsoft and write to USB.
- Boot into Windows setup utility.
- Select Repair computer –> Troubleshoot –> Advanced –> Cmd prompt.
This procedure assumes the following:
- main disk is
disk 0 - EFI partition is
part 1 - Windows OS drive letter is
c:
The following command will format the old EFI partition, mount it to s:, and copy the boot files to it.
diskpart
> list disk
> sel disk 0
> list part
> sel part 1
> format fs=fat32 quick label=System
> list vol
> exit
mountvol S: /S
bcdboot c:\windows /s s: /f UEFI /v
exit