Description
This rule verifies that no DigitalOcean Cloud Firewall permits MySQL (TCP port 3306) from an unrestricted source. Cloud Firewalls are DigitalOcean's equivalent of a security group; an unrestricted source is 0.0.0.0/0 (All IPv4) or ::/0 (All IPv6).
The rule enumerates every firewall's inbound_rules and flags any rule whose protocol is TCP, whose ports include 3306, and whose sources include 0.0.0.0/0 or ::/0. This applies to self-managed MySQL/MariaDB running on Droplets; DigitalOcean Managed Databases are governed by their own trusted-source settings rather than Cloud Firewalls.
Remediation narrows the source of the port-3306 rule to the specific application server IPs, a resource tag, or the VPC/private network, and removes the unrestricted rule once connectivity through the scoped path is confirmed.
Inbound TCP rules on port 3306 with source 0.0.0.0/0
Inbound TCP rules on port 3306 with source ::/0
Port-range or 'all' rules encompassing port 3306 from an unrestricted source
Rationale
Exposing a database port directly to the internet bypasses every application-layer control and puts the database engine's own authentication on the front line. Automated scanners routinely sweep for open port 3306 and attempt to log in with default or weak credentials, or exploit known MySQL vulnerabilities. A successful connection can lead to data theft, ransom, or destruction.
Databases should only be reachable from the application tier that needs them. Scoping the inbound rule to specific app-server addresses, a tag, or the private VPC network ensures the database is never directly addressable from the public internet.
Direct database exposure sidesteps all application-layer protection
Open 3306 is a constant target for credential and vulnerability scans
Private-network / tag scoping keeps the DB reachable only by the app tier
Impact
Pros:
Removes the database from public reachability
Forces access through the application tier or private network
Reduces exposure to database brute-force and CVE exploitation
Cons:
Requires knowing the exact app-server IPs, tags, or VPC range
A missed source can break application-to-database connectivity
External tooling that connected directly must move behind the allowed path
Default Value
DigitalOcean firewalls suggest only an SSH (port 22) inbound rule on creation and deny all other inbound traffic by default, so any port-3306 exposure was configured deliberately.
Pre-Requisite
Control Panel access with permission to view and edit firewalls
A DigitalOcean API token or doctl authenticated for API/CLI checks
Knowledge of the application server IPs, resource tag, or VPC/private network that must reach MySQL
Remediation
Test Plan
Using the DigitalOcean Control Panel:
Sign in to cloud.digitalocean.com and go to Networking, then Firewalls.
Open each firewall and select the Rules tab.
Under Inbound Rules, look for a MySQL rule or any Custom TCP rule whose Ports include 3306.
Inspect the Sources for that rule and note whether it lists 'All IPv4' and/or 'All IPv6'.
Pass: No inbound rule allowing TCP port 3306 has a source of All IPv4 (0.0.0.0/0) or All IPv6 (::/0).
Fail: At least one inbound rule allows TCP port 3306 from All IPv4 or All IPv6.
Using doctl or the API:
Enumerate firewalls and inspect inbound rules for port 3306 open to an unrestricted source.
doctl compute firewall list --format ID,Name,InboundRules
curl -s -X GET -H "Authorization: Bearer $DIGITALOCEAN_TOKEN" "https://api.digitalocean.com/v2/firewalls" | jq '.firewalls[] | {id, name, offending: [.inbound_rules[] | select(.protocol=="tcp" and (.ports=="3306" or .ports=="0") and (.sources.addresses // [] | any(.=="0.0.0.0/0" or .=="::/0")))]}'
Pass: the offending array is empty for every firewall. Fail: any firewall returns an inbound TCP rule covering port 3306 with a source of 0.0.0.0/0 or ::/0.
Implementation Steps
Using the DigitalOcean Control Panel:
In Networking, then Firewalls, open the firewall and select the Rules tab.
Click Add inbound rule, choose MySQL (or Custom TCP port 3306), and set Sources to the specific app-server IPs, a resource tag, or the VPC/private network range.
Save the rule and confirm the application can still reach the database through the scoped path.
Find the original port-3306 rule whose source is All IPv4 / All IPv6, open its … (More) menu, and choose Delete rule.
Repeat for every firewall exposing MySQL to an unrestricted source.
Using doctl or the API:
Add a scoped port-3306 rule, then remove the unrestricted one.
doctl compute firewall add-rules <firewall-id> --inbound-rules "protocol:tcp,ports:3306,address:10.10.0.0/16"
doctl compute firewall remove-rules <firewall-id> --inbound-rules "protocol:tcp,ports:3306,address:0.0.0.0/0"
curl -s -X POST -H "Authorization: Bearer $DIGITALOCEAN_TOKEN" -H "Content-Type: application/json" -d '{"inbound_rules":[{"protocol":"tcp","ports":"3306","sources":{"addresses":["10.10.0.0/16"]}}]}' "https://api.digitalocean.com/v2/firewalls/<firewall-id>/rules"
curl -s -X DELETE -H "Authorization: Bearer $DIGITALOCEAN_TOKEN" -H "Content-Type: application/json" -d '{"inbound_rules":[{"protocol":"tcp","ports":"3306","sources":{"addresses":["0.0.0.0/0","::/0"]}}]}' "https://api.digitalocean.com/v2/firewalls/<firewall-id>/rules"
Backout Plan:
If application connectivity breaks, temporarily re-add the prior rule with doctl compute firewall add-rules <firewall-id> --inbound-rules "protocol:tcp,ports:3306,address:0.0.0.0/0" (or the equivalent POST /rules) while you correct the allowed sources.
Re-apply the scoped rule and remove the unrestricted rule once the correct app-server sources or VPC range are confirmed.