We Hardened Ubuntu 24.04 for Security Tools (And Broke Everything First)
We maintain HailBytes reNgine, a web recon platform. Wanted to deploy hardened golden images to AWS and Azure following industry benchmarks.
Should be simple. Run hardening script, create AMI, done.
Except our scanning tools immediately stopped working. 🔥
# Our "secure" kernel settings:
kernel.unprivileged_bpf_disabled = 1 # Block BPF
net.core.bpf_jit_harden = 2 # Maximum BPF hardening
# What our tools actually needed:
# BPF access. For packet capture. For scanning. For everything.
Here's the thing: security tools often need the exact privileges that security hardening removes. Fun paradox.
How Claude Code Helped
We used Claude Code to iterate on the hardening scripts. Three problems it helped us solve:
1. Azure VMs Were Detected as AWS
Both clouds use the same metadata IP. Our detection was just checking if the endpoint responded.
# This is wrong
METADATA_URL="http://169.254.169.254"
curl -s "$METADATA_URL/latest/meta-data/" && echo "AWS!"
The fix: actually validate the response format.
detect_cloud_provider() {
# Check Azure FIRST (it has a specific field)
local azure_check=$(curl -s -H "Metadata:true" \
"http://169.254.169.254/metadata/instance?api-version=2021-02-01" \
--connect-timeout 2 2>/dev/null)
if echo "$azure_check" | grep -q '"azEnvironment"'; then
echo "azure"
return
fi
# Then check AWS (validate instance ID format)
local aws_check=$(curl -s \
"http://169.254.169.254/latest/meta-data/instance-id" \
--connect-timeout 2 2>/dev/null)
if [[ "$aws_check" =~ ^i-[a-f0-9]+ ]]; then
echo "aws"
return
fi
echo "generic"
}
Azure returns JSON with azEnvironment. AWS returns plain text. Check the actual content, not just connectivity.
2. Ubuntu 24.04 Renamed the SSH Service
Worked fine on 22.04. Then:
systemctl restart sshd # "Unit sshd.service not found" 💀
Ubuntu 24.04 changed it from sshd to ssh. Cool. Cool cool cool.
if systemctl restart ssh 2>/dev/null || systemctl restart sshd 2>/dev/null; then
log "SSH hardened"
else
error "Failed to restart SSH"
fi
3. Finding the Right Security Tradeoffs
This was the real work. Hardening that doesn't break the app:
| Setting | CIS Says | What We Used | Why |
|---|---|---|---|
unprivileged_bpf_disabled |
1 (blocked) | 0 (allowed) | Scanning needs packet capture |
bpf_jit_harden |
2 (max) | 1 (moderate) | Performance matters |
| AppArmor | enforce all | complain for Docker | Containers need flexibility |
Each deviation is documented. Auditors will ask. Have your answers ready.
What You Get
# Auto-detect cloud provider
sudo ./harden_ubuntu_2404.sh
# Or force it
sudo ./harden_ubuntu_2404.sh --cloud azure
sudo ./harden_ubuntu_2404.sh --cloud aws
The script handles:
- SSH hardening (Ed25519, modern ciphers only)
- UFW firewall (ports 22, 443, 8082)
- Kernel hardening (ASLR, SYN cookies, anti-spoofing)
- Fail2Ban
- Auditd logging
- Auto security updates
- Still runs Docker and security tools
What I Learned
- Test on the actual target platform. Ubuntu version differences will get you.
- Cloud metadata APIs are tricky. Validate the response format, not just reachability.
- Document your security tradeoffs. Future you and your auditors will need this.
- AI assistants catch edge cases. Claude flagged the SSH rename before we hit production.
Grab It
git clone https://github.com/HailBytes/rengine-ng-rc
cd rengine-ng-rc/scripts
sudo ./harden_ubuntu_2404.sh --help
Works for any Ubuntu 24.04 server, not just reNgine.
How do you handle the security vs. functionality tradeoff? Would love to hear what's worked for you. Update incoming this week.
#security #devops #ubuntu #cloudcomputing #opensource
Top comments (0)