How I Built a Custom AI Security Agent to Harden My VPS
How I Built a Custom AI Security Agent to Harden My VPS
Securing a live production server can be a daunting task. While standard practices like moving your SSH off port 22 and setting up strong firewall rules drop the background noise of internet attacks to near zero, it doesn't hurt to have a second layer of defense.
Recently, I noticed suspicious Brazilian IPs scanning my server, which prompted me to build an active, AI-powered security agent. In this post, I will walk you through exactly what we built, how it works, and how it hardens the server against targeted attacks.
The Architecture of the AI Security Agent
The goal was simple: Create a silent background process that reads my server logs, filters out normal administrative noise, and uses OpenAI's GPT models to analyze failed login attempts in real-time. If it detects a legitimate threat, it traces the attacker's geolocation and pushes the data to a secure Next.js dashboard.
Here is the tech stack we used:
- Node.js & PM2: To run a lightweight background daemon directly on the Ubuntu VPS.
- OpenAI API: To analyze the log context and determine the threat level.
- Next.js & Tailwind CSS: For a secure, dark-mode command center to visualize the data.
Step 1: Tailing the Logs
The heart of the agent is a logWatcher.ts script. It uses the tail library to continuously monitor /var/log/auth.log (where Ubuntu logs all SSH login attempts).
To ensure we didn't waste money by sending every single log line to OpenAI, we implemented a strict Regex filtering system:
ignorePatterns: [
/session opened for user/i,
/session closed for user/i,
/CRON\[\d+\]:/i,
/sudo:.*COMMAND=/i,
/systemd\[\d+\]:/i
]
By filtering out normal cron jobs, valid SSH key logins, and local sudo commands, the agent stays completely silent until a legitimate failed authentication event occurs (like someone typing the wrong password for root).
Step 2: AI Threat Analysis
When a batch of suspicious logs is collected, the agent sends it to gpt-4o-mini with a strict JSON system prompt. The AI acts as a cybersecurity expert, categorizing the threat level as low, medium, or high.
If the AI flags an IP address as a threat, our agent immediately reaches out to the ip-api.com service to resolve the attacker's physical geolocation (e.g., "Los Angeles, United States").
Step 3: Secure Dashboard and Honeypots
Once the threat is analyzed, the agent pushes the payload securely via a REST API to my Next.js website.
To ensure my dashboard couldn't be viewed by the attackers, we secured it using Next.js Edge Middleware and HTTP Basic Authentication. We also added a token tracking badge to the UI so I can monitor exactly how much the OpenAI API is costing me in real-time.
As a final layer of defense, we set up Honeypot Routes on the Next.js site. We created fake /wp-admin and /.env endpoints. Any automated bot that tries to scrape those hidden URLs is instantly flagged as a HIGH threat and permanently logged on the dashboard!
Step 4: Navigating Linux Permissions
Deploying the system to a live production environment required careful orchestration of Linux file permissions.
Because PM2 runs as a standard user, it natively lacks permission to read /var/log/auth.log or write to directories owned by the www-data Nginx group. Rather than running the agent as root (which is dangerous), we handled this using strict Access Control Lists (ACLs) and group ownership:
- We added the PM2 user to the
admgroup to allow read-access to system logs. - We gave the Next.js API direct ownership of the
security-status.jsonfile to safely push data without breaking Nginx's web logging permissions.
Conclusion
By combining custom Linux logging with generative AI, we created a highly effective "silent alarm" for the VPS.
Changing the default SSH port handles 99% of the automated internet noise, but this AI agent serves as the perfect safety net. If a dedicated attacker ever runs a full port scan and finds the custom SSH port, the AI will immediately catch them, trace their location, and alert the dashboard before they can break in!

