blog.iankulin.com

blog.iankulin.com

Bruno asserts

After ChatGPT flagged that mdserver might allow path traversal attacks, I used Bruno, an under-development Postman/Insomnia alternative, to check whether Express was sanitizing URLs properly. ChatGPT generated pass and fail URL examples, I wrote asserts for each in Bruno, and the whole collection can be run at once. A side benefit is that requests are stored as version-controllable text committed alongside the rest of the code...

Displaying markdown as HTML

This is a write-up of a small weekend project of mine: a Node.js/Express server that serves markdown files from a directory as HTML, using middleware to intercept requests for .md files and convert them with the Showdown library. I added a simple string-replacement template so the output is well-formed HTML with a proper page title. The code is on GitHub, with hardening and a possible Docker containerization left as future work...

Ansible playbook to start Proxmox hosts

A follow-up to my previous post on tagging Proxmox guests, showing how to use Ansible to automatically start VMs and LXC containers before running apt updates. I cover installing the proxmoxer Python library, my inventory and vault setup, and the differences between starting VMs by name with the proxmox_kvm module and containers by VMID with the proxmox module. I include a complete example playbook...

Proxmox tags to solve a problem

I use Proxmox’s tagging system to mark which VMs and containers need to be powered on before running my weekend Ansible update script on a dev server. My walkthrough covers applying tags in the web GUI, customizing colors via Tag Style Override, the three display styles available, and sorting machines by tag in the Datacenter search view...

apt update - BADSIG 871920D1991BC93C

My weekend Ansible apt upgrade run failed on one Ubuntu host with a BADSIG GPG signature error on the Ubuntu jammy-updates repository. I trace the problem to my Apt Cacher NG cache and fix it by deleting the cached Ubuntu repository data, forcing fresh index files to be downloaded. I leave the underlying cause unresolved, though it appears to affect only that one host...

We need to talk about Bruno

After Insomnia forced account creation and my saved API requests disappeared, I switched to Bruno, a free and open-source API client available for Mac, Windows, and Linux, plus a CLI and VS Code plugins. Bruno stores request collections as human-readable text files, making them easy to commit to source control alongside code. The writeup includes my first impressions after an afternoon of use...

Tailscale keys expire

My weekend Ansible update run flagged three machines as unreachable, and the culprit turned out to be expired Tailscale keys, which expire by default every 180 days. Re-authenticating remote machines is awkward because force-reauth drops any SSH session running over Tailscale, while reauthorization from the admin page only grants temporary access. Ways around the problem include disabling key expiry per machine in the admin console, and I also note that expiry dates aren’t easily visible in the CLI or admin page...

New Project Routine

A step-by-step walkthrough of my routine for starting new Node/Express SSR web projects with HTMX, covering directory setup, npm and git initialization, GitHub repo creation, and my ongoing dilemma about committing htmx.min.js versus using a CDN. In the second half, I describe an Express starter skeleton with EJS views and partials, basic routing with 404 handling, and some starter CSS...

Getting Tailscale working in LXC containers

When I ran Tailscale inside an unprivileged LXC container on Proxmox, it failed at tailscale up with a “no backend” error, because the container lacked access to the TUN/TAP device Tailscale needs. My fix was to stop the container, add a device allow rule and a bind mount entry for /dev/net/tun to its LXC config file, then start it and run tailscale up again...

Certbot - adding more virtual hosts

A walkthrough of how I add a new domain as a virtual host to an NGINX server already managed by Certbot, showing that rerunning certbot –nginx lets me expand the existing combined certificate to cover the new domain. I also cover migrating domains with existing Porkbun-issued certificates to the same server and folding them into the combined certificate, ending with a note on revoking the now-redundant ones...

Certbot & Let's Encrypt are great

I explain why I switched from manually downloading SSL certificates from PorkBun every 90 days to using Let’s Encrypt and Certbot, prompted in part by limitations with managing Australian .au domains. It covers installing Certbot on Ubuntu with NGINX, how it reads site configs to obtain and install certificates, and the systemd timer that renews them automatically...

BOINC in an LXC container

My homelab post about BOINC, the successor to SETI@home, running in an LXC container. After an earlier setup attempt ended with limited success and doubt about the configuration, a pulsing CPU during a weekend maintenance routine showed the client was actually processing World Community Grid work. I also float the idea of using BOINC as a workload to experiment with Kubernetes...

Solved DNS Issues - Proxmox, LXC, Ubuntu, Tailscale

While setting up an Ubuntu 20.04 LXC on Proxmox to run a TP-Link Omada controller, I found DNS resolution failing because Proxmox copies the host’s Tailscale-based resolv.conf into containers, and the container has no route to the Tailscale DNS address. In this post, I cover the diagnosis of why Debian templates behaved differently, plus two fixes: specifying DNS explicitly in the Proxmox GUI, or creating a .pve-ignore.resolv.conf file so Proxmox stops overwriting the container’s settings...

Caching APT updates

I set up APT Cacher NG in an unprivileged LXC container to cache Debian package downloads for machines on my homelab LAN, avoiding repeated fetching of the same updates. My walkthrough covers installing and configuring the server (including enabling HTTPS passthrough), pointing client machines at the cache via a proxy config file pushed out with Ansible, and checking the built-in report page for cache statistics...

Installing service with Ansible

I deploy a small Go monitoring endpoint to my homelab servers as a systemd service, using Ansible. I cover the basics of a systemd .service file, including target dependencies like After=network.target, and an Ansible playbook that copies the executable and service file into place, restarting the service via handlers only when files change. I pull my sudo credentials from an encrypted Ansible vault...

Simple API endpoint in Go

A small Go utility exposes a lightweight JSON endpoint on each node or VM indicating whether RAM and disk usage are okay. Uptime Kuma watches the endpoint with its HTTP keyword check and sends an ntfy push notification when a machine gets into trouble. I include brief impressions of writing in Go and link to the GitHub repo...

Problems backing up LXC to NFS in Proxmox

I found that backing up an unprivileged LXC container on Proxmox to an NFS share can fail with a permission denied error when the backup process tries to create a temporary file on the remote share. The fix is to edit /etc/vzdump.conf on my Proxmox node and set tmpdir to /tmp, so the temporary file is built locally before being copied to the share...

Use VS Code to work on remote files

I walk through using VS Code’s Remote-SSH extension to edit files on a remote server with a full editor instead of terminal tools like nano or vim. I explain that the plugin works by installing VS Code Server on the remote machine and connecting over SSH, then I cover setup: installing the extension, adding a host via the Remote Explorer, saving the SSH config, and connecting in a new window...

Disable SSH root logins

My freshly spun-up Ubuntu VPS starts receiving SSH brute-force login attempts against root within minutes, at roughly one attempt every 10 seconds. I cover disabling root SSH login by creating a sudo user, setting PermitRootLogin no in sshd_config, restarting sshd, and watching out for included config files in sshd_config.d that can override settings. I end with the idea of logging what passwords the bots actually try...

Lightweight Web Servers

After a couple of homelab incidents that Uptime Kuma didn’t catch, including a vanished USB mount and an NVMe drive filling up, I consider adding custom checks for things like disk space, memory use, and mount status via a small metrics endpoint. Weighing Node/Express against Python, C, and Golang, I conclude that C’s benchmark edge isn’t worth the hassle for endpoints polled only every few minutes...