// DevOps
A new server isn't a blank slate. It's an unlocked door.
Published on 2026-09-22
A few days ago I investigated a production server compromise. Not a theoretical one — a real one, with a Monero miner, a C2 agent and a backdoor. The attack was completely automated. No one was specifically hunting that server — a bot continuously scans the entire internet, tries open databases with default passwords and does its job.
From the server appearing on the network to the first attack it took less than six hours. To a successful compromise — less than a day.
Three conditions that made it possible: PostgreSQL was exposed, the password was postgres, the firewall was not enabled. That’s it. Nothing else was required.
Why this happens even to experienced people
There is a certain psychology around a new server. You just spun it up, everything works, the application responds — and you move on to the next task. Security feels like something that can be set up later. Later — when there is time. Later — before production. Later — after the release.
Later never comes. Or it does, but only in the form of top showing /tmp/mysql at 400% CPU.
A new server on the internet is not a blank slate. It’s an unlocked door in a house that already stands on a busy street. Scanners find it within hours, not days.
What actually happened
The attacker found an open port 5432 on the internet. They ran a brute force. The password postgres — that’s not even brute forcing, it’s the first line in any dictionary. They gained superuser access to PostgreSQL.
Next comes a legitimate PostgreSQL feature — COPY FROM PROGRAM. It allows a superuser to execute an arbitrary shell command directly from an SQL query. The attacker passed a bash script to it encoded in base64, which downloaded and launched a dropper.
But the smartest part was the persistence mechanism. Event triggers were added to the database — triggers that fire on every DDL operation (CREATE TABLE, ALTER, DROP…). On each such event they quietly recreated a superuser role with a password known to the attacker. So even if an admin noticed and removed the malicious role — the next migration or CREATE INDEX would automatically restore it.
Elegant and truly nasty.
Three rules that cut off 99% of such attacks
I’m intentionally not writing “ten rules” or a “complete checklist”. Not because the rest isn’t important — but because these three rules close off the majority of automated attacks that actually happen in the wild.
Databases must not be accessible from the internet. PostgreSQL, MySQL, Redis, MongoDB — none of these are designed for direct external access. In a docker-compose.yml the line "5432:5432" for a database is almost always a mistake. The application will reach the database inside the Docker network by hostname. Externally that port is needed by no one except attackers.
If you need access for development — bind only to localhost:
ports:
- "127.0.0.1:5432:5432"For production — remove ports entirely.
Default passwords are not passwords. postgres, password, root, admin — these are the first lines in any brute-force dictionary. A scanner will try them in seconds, not hours. Generate a password at deploy time:
POSTGRES_PASSWORD=$(openssl rand -base64 32)Or use secret management: HashiCorp Vault, AWS Secrets Manager, Doppler. The main thing — never commit passwords to the repository and don’t leave default values in .env.
The firewall must be enabled from the first minute. Not before release. Not after configuration. From the first minute after creating the server. The correct model — deny everything, open only what is necessary:
ufw default deny incoming
ufw default allow outgoing
ufw allow ssh
ufw enableAfter that, open specific ports as needed. Not the other way around.
Monitoring you should add
The three rules above are about prevention. But if you also want to see what’s happening, add minimal logging to PostgreSQL. By default it doesn’t write successful connections or DDL statements. That means you have no visibility into what is happening with the database.
In postgresql.conf:
log_connections = on
log_disconnections = on
log_statement = 'ddl'And a simple cron script to check for anomalies:
#!/bin/bash
SUPERUSERS=$(psql -U postgres -t -c "SELECT count(*) FROM pg_user WHERE usesuper = true AND usename != 'postgres';")
if [ "$SUPERUSERS" -gt 0 ]; then
echo "ALERT: unauthorized superusers detected" | mail -s "Security Alert" admin@example.com
fiThis is already enough to notice something suspicious before top does.
Why this matters more than it seems
According to Shodan, there are over 800,000 PostgreSQL instances open on the internet. A significant portion use default or weak passwords. Nobody is hacking them manually — these are fully automated scanners running around the clock. They do not pick victims by business size or data value. They just go down the list of open ports.
Your server is no exception. It’s already on that list. The only question is whether there’s a lock on the door.
Protection against this doesn’t require complex technologies, expensive tools, or deep security expertise. It requires three things you can do in ten minutes on first logging into the server. Just don’t postpone them.
More about each defensive layer — in the “Protecting a Linux server” series: UFW firewall, Fail2ban, CrowdSec and audit with Lynis.
// Reviews
Related reviews
I came with an expensive request to configure a VPS server, but during the consultation Mikhail suggested a much simpler, more affordable solution. In the end I saved time and money. Mikhail — a true expert who works for the client's result, not for the fee. I recommend him!
I came with an expensive request to configure a VPS server, but during the consultation Mikhail suggested a much simpler and more cost-effective solution. In the end I saved budget and time. Mikhail — a true expert who …
VPS setup, server setup
2026-05-12 · ★ 5/5
Excellent work! Set up the server very quickly, installed the control panel, and configured the IP. Definitely recommend!
Excellent work! Very quickly set up the server, installed the panel, configured the IP I can definitely recommend it!
Everything was excellent; helped promptly and professionally. Thank you — I recommend them to the community.
Everything's great, helped promptly and professionally, thank you, I recommend it to the community
VPS setup, server setup
2026-04-16 · ★ 5/5
There were several issues concerning both the technical side and overall understanding. Mikhail responded quickly, resolved the technical problems, and helped me understand them — many thanks. I'm satisfied with the result.
There were several issues concerning both the technical side and overall understanding. Mikhail responded quickly to the request, helped sort things out and resolved the technical problems and helped clarify …
VPS setup, server setup
2026-02-18 · ★ 5/5
Everything was done quickly and efficiently. I recommend.
Everything was done quickly and efficiently. I recommend.
VPS setup, server setup
2026-01-17 · ★ 5/5
Everything went well; the contractor responded quickly to questions and helped resolve the issue. Thanks!
Everything went well, the contractor responded quickly to questions and helped resolve the issue. Thank you!
VPS setup, server setup
2025-12-16 · ★ 5/5
// Contact
Need help?
Get in touch with me and I'll help solve the problem
I reply within one business day (03:00-13:00 GMT)
Или оставьте заявку здесь:
// Related