
Photo · Wikimedia Commons
The CIA Triad
Information security is built on three pillars: Confidentiality, Integrity, and Availability. On this server, that means encrypted tunnels (HTTPS), restrictive firewall rules, and monitoring that tells us the site is actually up — not just assuming it is.
Layers of Defense
SSL/TLS Encryption
Using Let's Encrypt to ensure that sensitive data remains encrypted while in transit between the client and the Nginx server.
SSH Hardening
Disabling password authentication in favor of Public Key Infrastructure (PKI) to stop brute-force bots in their tracks.
Rate Limiting
Public-facing endpoints like form submissions are throttled at the Nginx layer to blunt scripted abuse before it reaches the app.
fail2ban
Watches auth and web server logs, and temporarily bans IPs that show repeated failed login or probing behavior.
Real Hardening Configs
Textbook, standard-practice configuration — safe to publish and safe to run. Test every SSH change in a second session before closing your first one.
Key-only authentication, root login locked to keys only (not disabled entirely, so recovery via console still works), and a short grace period so half-open connections don't pile up.
# /etc/ssh/sshd_config.d/99-hardening.conf
PasswordAuthentication no
PermitRootLogin prohibit-password
PubkeyAuthentication yes
KbdInteractiveAuthentication no
X11Forwarding no
MaxAuthTries 3
LoginGraceTime 20
AllowTcpForwarding no
ClientAliveInterval 300
ClientAliveCountMax 2
# Always validate before reloading — a typo here can lock you out
sshd -t && systemctl reload ssh
A shared snippet included in every server block, so every site on the box gets the same baseline browser-side protections for free.
# /etc/nginx/snippets/security-headers.conf
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Permissions-Policy "geolocation=(), microphone=(), camera=()" always;
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains" always;
# included inside each server{} block with:
# include snippets/security-headers.conf;
The full annotated version lives on the Linux hub — this is the SSH-focused core of it.
# /etc/fail2ban/jail.local
[DEFAULT]
bantime = 1h
findtime = 10m
maxretry = 5
ignoreip = 127.0.0.1/8 ::1
[sshd]
enabled = true
port = ssh
filter = sshd
maxretry = 4
bantime = 24h
Installing fail2ban and never enabling the service is a more common gap than people expect — systemctl is-active fail2ban is a five-second sanity check worth running after any provisioning script.
Suricata & IDS on OPNsense
A firewall decides what traffic is allowed through. An intrusion detection system reads the traffic that was allowed through and decides whether it looks malicious — two different jobs, both needed. Here's the real breakdown, from a production deployment running this exact stack.
A single custom rule looks like this — alert-only here, but the same syntax with drop instead of alert is what inline blocking actually uses:
# /usr/local/etc/suricata/rules/local.rules
alert tcp any any -> $HOME_NET 22 (msg:"Repeated SSH auth attempts"; flow:to_server; \
flags:S; threshold:type threshold, track by_src, count 10, seconds 60; \
classtype:attempted-recon; sid:1000001; rev:1;)
And the interface Suricata actually listens on has to be explicitly bound — on OPNsense that's the WAN interface, set to inline (IPS) mode rather than the default passive (IDS) mode:
# /usr/local/etc/suricata/suricata.yaml
af-packet:
- interface: igb0 # WAN — inline, not a mirrored/SPAN copy
threads: auto
cluster-type: cluster_flow
defrag: yes
use-mmap: yes
ips: yes # inline blocking mode, not alert-only
This is one piece of a larger open-source perimeter build — firewall, VPN, and IDS together. Full architecture and diagram in the Open-Source Enterprise Networking case study.
Case Study: Hardening This Very Server
Before this design system went live, the box hosting it went through a real hardening pass — the same investigate-before-you-fix process used on every client engagement. Five findings came out of it, closed one at a time:
The pattern that held across all five: don't patch an exposure until you know exactly who's been using it and how. Read the logs first, confirm what legitimately depends on the current behavior, then design the fix so nothing breaks on the way to being secure. The full write-up, with more incidents from other engagements, is on the Network Engineering & Hardening case study.
Layered Defense
No single control is enough on its own — encryption, authentication, rate limiting, and monitoring each cover a different failure mode. That's the same layered approach behind the remote IT & security services offered on this site.