Hardening an Oracle Cloud instance in 30 minutes
A freshly provisioned Oracle Cloud VM is reachable from the entire internet within seconds of creation. Here is the sequence I run before deploying anything on it.
An Oracle Cloud Always Free instance ships with two stacked firewalls — the
network Security List and system-level iptables — plus an Ubuntu image that,
by default, leaves INPUT at ACCEPT for most of what the Security List lets
through. Knowing which of the two blocks what saves you an hour of debugging a
port that looks “open” but never answers.
Threat model, in three lines
What hits a public IP in the minutes after it goes live:
- automated, continuous SSH brute-forcing from botnets;
- systematic port scanning, high ports included;
- HTTP discovery (
/.env,/.git/config,/wp-login.php) the moment a web service responds.
Nothing targeted — just opportunistic background noise. The good news: generic measures handle generic noise.
1. Remove password authentication
Best effort-to-payoff measure by a wide margin. Brute-forcing only works if there is a password to find.
sudo install -d -m 755 /etc/ssh/sshd_config.d
sudo tee /etc/ssh/sshd_config.d/10-hardening.conf > /dev/null <<'CONF'
PermitRootLogin no
PasswordAuthentication no
KbdInteractiveAuthentication no
PubkeyAuthentication yes
MaxAuthTries 3
LoginGraceTime 20
AllowUsers ubuntu
X11Forwarding no
CONF
sudo sshd -t && sudo systemctl reload ssh
The sshd -t before reload is not decoration: it validates the config. A typo
can otherwise make sshd refuse to restart — and you lose your only access.
Always keep a second SSH session open while editing
sshd_config. If the new config locks you out, the already-established session is your way back in.
2. Understand the two firewalls
The Security List lives in the OCI console; until a port is allowed there as
Ingress, the packet never reaches the VM. Meanwhile Oracle’s Ubuntu image
ships with preloaded iptables rules — the classic trap: the Security List
allows the port, but iptables still rejects it. Insert your rules before
the trailing REJECT:
sudo iptables -I INPUT 6 -p tcp --dport 80 -m state --state NEW -j ACCEPT
sudo iptables -I INPUT 7 -p tcp --dport 443 -m state --state NEW -j ACCEPT
sudo netfilter-persistent save
3. Shrink the exposed surface
sudo ss -tulpn | grep LISTEN
Anything listening on 0.0.0.0 without reason should move to 127.0.0.1 or be
stopped. And beware Docker: -p 8080:80 publishes on all interfaces and
bypasses iptables. Bind to loopback when a service needn’t be public:
ports:
- "127.0.0.1:8080:80" # not "8080:80"
4. Verify, don’t assume
From an outside machine:
nmap -Pn -p- --min-rate 1000 <PUBLIC_IP>
ssh -o PreferredAuthentications=password -o PubkeyAuthentication=no ubuntu@<IP>
The last command must fail immediately. If it prompts for a password, your SSH config was not reloaded.
What this does not cover
This hardens network exposure and system access. It says nothing about application security: an injection flaw will ride in through the very 443 you just legitimately opened. HTTP headers, CSP and input validation are a separate effort — and that is where most actually-exploited vulnerabilities live.