How to Configure IPTables for MySQL: Allow Outbound Connections to RDS While Blocking Inbound Requests


2 views

When working with MySQL on servers that also need to connect to external databases (like Amazon RDS), we face a specific firewall configuration dilemma: how to maintain local security while enabling necessary outbound access. The default DROP policy in IPTables creates the right security baseline, but we need precision rules for production database workflows.

Your existing configuration likely resembles:

iptables -P INPUT DROP
iptables -P FORWARD DROP
iptables -P OUTPUT ACCEPT

This explains why your initial attempt with --sport and --dport created a security hole - it was too permissive for incoming traffic.

For AWS RDS connections, we need stateful tracking rather than static port rules:

# Allow ESTABLISHED,RELATED connections (response traffic)
iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT

# Explicitly allow outbound MySQL connections
iptables -A OUTPUT -p tcp --dport 3306 -j ACCEPT

Since RDS endpoints can change IPs, we can leverage these approaches:

# Option 1: Allow all outbound MySQL (not recommended for production)
# iptables -A OUTPUT -p tcp --dport 3306 -j ACCEPT

# Option 2: Use AWS prefix lists (requires AWS CLI setup)
# aws ec2 describe-managed-prefix-lists --query "PrefixLists[?PrefixListName=="com.amazonaws.*.rds"].PrefixListId"
# Then reference the returned ID in your security groups

Here's a production-ready ruleset:

#!/bin/bash
# Flush existing rules
iptables -F

# Default policies
iptables -P INPUT DROP
iptables -P FORWARD DROP
iptables -P OUTPUT DROP

# Allow loopback
iptables -A INPUT -i lo -j ACCEPT
iptables -A OUTPUT -o lo -j ACCEPT

# Allow established connections
iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT

# Allow SSH (adjust port as needed)
iptables -A INPUT -p tcp --dport 22 -j ACCEPT

# Allow outbound HTTP/HTTPS
iptables -A OUTPUT -p tcp --dport 80 -j ACCEPT
iptables -A OUTPUT -p tcp --dport 443 -j ACCEPT

# Allow outbound MySQL specifically
iptables -A OUTPUT -p tcp --dport 3306 -j ACCEPT

# Allow DNS resolution
iptables -A OUTPUT -p udp --dport 53 -j ACCEPT
iptables -A OUTPUT -p tcp --dport 53 -j ACCEPT

# Log and drop everything else
iptables -A INPUT -j LOG --log-prefix "IPTABLES_INPUT_DROP: "
iptables -A INPUT -j DROP
iptables -A OUTPUT -j LOG --log-prefix "IPTABLES_OUTPUT_DROP: "
iptables -A OUTPUT -j DROP

Remember to:

  • Save rules with iptables-save > /etc/iptables.rules
  • Implement monitoring for IP changes if using specific IP whitelisting
  • Consider using AWS Security Groups in conjunction with IPTables

If connections fail:

# Check if the rule is hitting:
iptables -L -v -n

# Monitor live connections:
tcpdump -i eth0 port 3306

# Verify outbound connectivity:
nc -zv your-rds-endpoint.rds.amazonaws.com 3306

When managing MySQL server security, we often need to strike a balance between allowing outbound connections to remote databases (like Amazon RDS) while maintaining strict inbound connection controls. The default DROP policy isn't sufficient when we need selective outbound access.

The existing configuration has:

# Default policy for INPUT chain
iptables -P INPUT DROP

# Default policy for OUTPUT chain 
iptables -P OUTPUT ACCEPT

This blocks all incoming MySQL connections (port 3306) but doesn't properly handle outbound database connections.

The initial approach many try:

iptables -A INPUT -p tcp --sport 3306 -j ACCEPT
iptables -A OUTPUT -p tcp --dport 3306 -j ACCEPT

This creates security holes by:

  • Allowing incoming connections on port 3306
  • Not restricting destination IP addresses

The solution lies in using connection tracking and proper source/destination matching:

# Allow ESTABLISHED,RELATED connections
iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT

# Specifically allow outbound MySQL connections
iptables -A OUTPUT -p tcp --dport 3306 -j ACCEPT

For environments where the remote database IP changes:

# Create a chain for RDS connections
iptables -N RDS_OUT

# Allow DNS resolution (required for RDS hostnames)
iptables -A OUTPUT -p udp --dport 53 -j ACCEPT

# Main rule using hostname resolution
iptables -A OUTPUT -p tcp --dport 3306 -j RDS_OUT

Here's a production-ready configuration:

#!/bin/bash

# Flush existing rules
iptables -F

# Set default policies
iptables -P INPUT DROP
iptables -P FORWARD DROP
iptables -P OUTPUT ACCEPT

# Allow localhost traffic
iptables -A INPUT -i lo -j ACCEPT

# Allow established connections
iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT

# Allow SSH (adjust port as needed)
iptables -A INPUT -p tcp --dport 22 -j ACCEPT

# Allow outbound MySQL with connection tracking
iptables -A OUTPUT -p tcp --dport 3306 -m conntrack --ctstate NEW,ESTABLISHED -j ACCEPT

# Optional: Rate limiting for MySQL outbound
iptables -A OUTPUT -p tcp --dport 3306 -m limit --limit 5/min -j ACCEPT

For long-term management:

  • Create iptables scripts in /etc/network/if-pre-up.d/
  • Use iptables-save/restore for persistence
  • Consider implementing fail2ban for additional protection

When debugging connection issues:

# Check current rules with line numbers
iptables -L -n -v --line-numbers

# Monitor live connections
watch -n 1 'iptables -L OUTPUT -v -n --line-numbers'

For AWS RDS specifically, always verify your security group settings as they operate at a different network layer.