blog.iankulin.com

Security

Detecting front-end client errors

Client-side JavaScript errors go unnoticed by keyword-based uptime checks like Uptime Kuma’s, which is how a broken weather widget slipped past monitoring on one of my sites. I walk through building Faultsy, a lightweight alternative to Sentry: a browser script captures uncaught errors and unhandled promise rejections, reports them to a small Node server via sendBeacon(), and exposes per-site error counts as JSON that Uptime Kuma can monitor. The project is on GitHub and still under active development...

CORS, What is it good for?

I explain the browser’s same-origin policy, covering what counts as an origin, why the restriction exists, and how it blocks JavaScript from reading cross-origin responses. I then introduce CORS as the mechanism servers use to permit specific origins via Access-Control-Allow-Origin headers...

Share files securely with Enclosed

I walk through self-hosting Enclosed on a VPS as a secure alternative to emailing password-protected zip files. I cover the Docker Compose setup behind Nginx Proxy Manager, along with configuring authenticated logins, a step the official docs leave under-explained, so only I can upload files while recipients download them without an account...

npm ERR! Exit handler never called!

My routine npm update, triggered by a GitHub security advisory, runs into the cryptic “Exit handler never called!” error. Alongside a refresher on what package-lock.json and npm update do, my fix is to confirm the lock file is at fault using npm install –no-package-lock, then delete it and regenerate it with a fresh npm install, followed by retesting and rebuilding any artifacts...

SSH login notification

How to get a phone notification whenever someone SSHs into a VPS, using a PAM hook and the Ntfy push notification service. I explain why SSH access is a risk worth monitoring on otherwise locked-down servers, then walk through editing the sshd PAM config, creating a script that sends a curl request to an Ntfy topic on login, and restarting sshd to test it...

Due Diligence on a Docker Image

When setting up self-hosted LimeSurvey, I find no official Docker image, so I work through how to decide which third-party container image to trust. I lay out rough criteria, such as known maintainers, popularity, update frequency, and inspectable build files, then compare several LimeSurvey images on Docker Hub and trace their provenance before settling on one...

Quick & Dirty auth with nginx & Node

I walk through protecting a Node/Express app with nginx basic auth, aimed at simple utilities on public servers: firewall off the app’s port so only nginx can reach it, configure nginx to proxy requests and require htpasswd credentials, and pass the authenticated username to the app via a request header. I also cover the approach’s limitations, such as plaintext passwords without SSL, no logout mechanism, and no brute-force protection...

Beginning Node App Security

I cover simple steps to secure a Node.js web app exposed to the public internet on a VPS, including putting it behind an Nginx reverse proxy with basic auth, enforcing HTTPS, and using Fail2ban to automatically ban brute-force attempts. I also cover restricting ports via a cloud firewall, disabling root SSH login, using SSH keys, keeping the system updated, and basic monitoring with Uptime Kuma...

User Sessions & Cookies in Node

In this tutorial, I build a user login and session management system from scratch in Node and Express, starting with a simple cookie-based session counter and progressively adding file-based persistence, user accounts, logout, and password authentication using bcrypt. I also cover input sanitisation, secure cookies, and enforcing HTTPS behind an NGINX proxy. The tutorial is aimed at readers with basic familiarity with Node and Express...

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...

Ansible with Secrets

This is a follow-up to my earlier Ansible playbook post, covering how to handle different sudo passwords across servers. I show how per-host inventory variables work, explain why storing ssh passwords in plaintext is risky, and walk through moving credentials into an external vars file before encrypting it with Ansible Vault, including the create, edit, and ask-vault-pass commands...

ssh key login on VPS

I walk through moving a server from password-based SSH logins to key-based authentication as a defence against brute force attacks. I cover generating an SSH key pair on a Mac, installing the public key on the target machine with ssh-copy-id, and disabling password authentication on Ubuntu 22 by adding PasswordAuthentication no to a config file in sshd_config.d and reloading the ssh daemon. I also note console login still works if something goes wrong...

Chinese Hackers Want to steal my Hello World container

I take a look at why leaving SSH password authentication enabled is risky, and why key-based auth with passwords disabled is a sensible setup for any internet-facing server. While reviewing server logs, I noticed sshd timeouts and suspicious kex_exchange_identification messages, and traced the IP address to a host in China flagged by others for brute force attacks...

sudo Incident Reports - where do they go?

After trying to run a docker command on a new home server and hitting the “user is not in the sudoers file” error, I look up the incident report in the journalctl logs, then explain how to properly gain sudo access. Instead of hand-editing /etc/sudoers, I add my user to the sudo group with usermod, covering why visudo exists and why I prefer sudo over logging in as root...

Mock Data

Needing mock student data for an iOS app without using real records, I try Mockaroo, a web-based test data generator with configurable field types and download options including JSON and a REST API. My writeup ends with a privacy caution that schemas saved on the service are publicly visible...