You’ve opened ports 465 and 587 in firewalld, but connections still fail. Learn why rich rules, zones, and service definitions matter. Fix your email server firewall once and for all.

You’ve run the command. You’ve double-checked it twice:

sudo firewall-cmd --list-ports
465/tcp 587/tcp

The ports are open. But when you try to connect from your email client or run nc -zv mail.server.com 465, you get:

Connection refused

Meanwhile, other ports like 993 (IMAPS) and 443 (HTTPS) work perfectly. What’s going on?

This is one of the most confusing issues for Linux system administrators. You’ve done everything right, yet something is still blocking your connections.

The answer lies in understanding firewalld zones, rich rules, and service definitions. Simply adding a port isn’t enough when other rules take precedence.

In this guide, I’ll demystify firewalld and show you exactly why your “open” ports are still blocked—and how to fix it permanently.


Understanding Firewalld Architecture

Firewalld uses multiple layers of rules. Think of it like airport security:

LayerFirewalld EquivalentPriority
Boarding pass checkRich rulesHighest
ID checkZone policiesMedium
Baggage scanService definitionsLower

Even if you have a valid boarding pass (open port), a rich rule can still reject you.

Key Concepts

ConceptDescriptionExample
ZoneA set of rules for a network interfacepublic, internal, trusted
ServicePredefined port/protocol combinationssmtp-submission (port 587)
Rich RuleComplex conditional rulesBlock specific IP ranges
Default TargetWhat happens if no rule matchesaccept, reject, drop

The Problem: How You Probably Opened Ports

Most tutorials tell you to do this:

sudo firewall-cmd --permanent --add-port=465/tcp
sudo firewall-cmd --permanent --add-port=587/tcp
sudo firewall-cmd --reload

This adds ports to the ports list, but rich rules or zone defaults can override them.


Diagnosing the Issue

Step 1: Check Your Active Zones

sudo firewall-cmd --get-active-zones

Example output:

public (default)
  interfaces: enp1s0
iredmail
  interfaces: enp6s0

This shows which zones are active on which network interfaces.

Step 2: List All Rules in Your Zone

sudo firewall-cmd --zone=public --list-all

Look for:

  • services: – Are smtp-submission and smtps listed?
  • ports: – Are 465/tcp and 587/tcp listed?
  • rich rules: – Any reject or drop rules?

Step 3: Check Rich Rules Specifically

sudo firewall-cmd --list-rich-rules

Example of problematic rich rules:

rule family="ipv4" source address="101.13.5.0/24" drop
rule family="ipv4" source address="66.132.195.0/24" drop

These drop entire IP ranges—and they apply before your port rules.

Step 4: Examine the Low-Level Rules

sudo nft list ruleset 2>/dev/null || sudo iptables -L -n

This shows the actual firewall rules being enforced.


The Fix: Proper Firewall Configuration

Solution 1: Use Services Instead of Ports (Recommended)

Remove the raw port definitions and use proper services:

# Remove raw ports
sudo firewall-cmd --permanent --remove-port=465/tcp
sudo firewall-cmd --permanent --remove-port=587/tcp

# Add proper services
sudo firewall-cmd --permanent --add-service=smtp-submission
sudo firewall-cmd --permanent --add-service=smtps

# Reload
sudo firewall-cmd --reload

Solution 2: Check What Services Actually Do

# View service definitions
sudo firewall-cmd --info-service=smtp-submission
sudo firewall-cmd --info-service=smtps

Expected output for smtp-submission:

smtp-submission
  ports: 587/tcp
  protocols:
  source-ports:
  modules:
  destination:

Solution 3: Add Your IP to Trusted Zone (Temporary)

For testing or if you have a static IP:

sudo firewall-cmd --permanent --zone=trusted --add-source=YOUR_IP_ADDRESS
sudo firewall-cmd --reload

Solution 4: Create a Custom Zone for Mail Services

# Create a dedicated mail zone
sudo firewall-cmd --permanent --new-zone=mail

# Add mail services
sudo firewall-cmd --permanent --zone=mail --add-service=smtp
sudo firewall-cmd --permanent --zone=mail --add-service=smtp-submission
sudo firewall-cmd --permanent --zone=mail --add-service=smtps
sudo firewall-cmd --permanent --zone=mail --add-service=imaps
sudo firewall-cmd --permanent --zone=mail --add-service=pop3s

# Assign your interface to this zone
sudo firewall-cmd --permanent --zone=mail --change-interface=enp1s0

# Reload
sudo firewall-cmd --reload

Solution 5: Remove Conflicting Rich Rules

If rich rules are blocking legitimate traffic:

# List all rich rules
sudo firewall-cmd --list-rich-rules

# Remove a specific rich rule (copy exactly from list)
sudo firewall-cmd --permanent --remove-rich-rule='rule family="ipv4" source address="101.13.5.0/24" drop'

# Reload
sudo firewall-cmd --reload

Understanding iRedMail’s Default Firewall Zones

iRedMail often creates multiple zones:

ZonePurposeTypical Interfaces
publicExternal trafficenp1s0 (WAN)
iredmailMail servicesenp6s0 (internal)
trustedTrusted IPsSources list

Your email ports might only be open in the iredmail zone, not public.

Check all zones:

for zone in $(sudo firewall-cmd --get-zones); do
    echo "=== Zone: $zone ==="
    sudo firewall-cmd --zone=$zone --list-all
done

Testing Your Fix

Test 1: Local Port Check

sudo ss -tlnp | grep -E "465|587"

Expected output:

LISTEN 0 100 0.0.0.0:465 0.0.0.0:* users:(("master",pid=xxx))
LISTEN 0 100 0.0.0.0:587 0.0.0.0:* users:(("master",pid=xxx))

Test 2: Remote Port Test (From another machine)

nc -zv mail.yourdomain.com 465
nc -zv mail.yourdomain.com 587

Expected output:

Connection to mail.yourdomain.com port 465 [tcp/urd] succeeded!
Connection to mail.yourdomain.com port 587 [tcp/submission] succeeded!

Test 3: Full SMTP Test

openssl s_client -connect mail.yourdomain.com:465 -crlf

Prevention: Firewall Audit Script

Create a script to regularly audit your firewall:

sudo nano /usr/local/bin/firewall-audit.sh
#!/bin/bash
# Firewall audit script for email servers

echo "=== Firewall Audit Report ==="
echo "Date: $(date)"
echo ""

echo "--- Active Zones ---"
sudo firewall-cmd --get-active-zones

echo ""
echo "--- Public Zone ---"
sudo firewall-cmd --zone=public --list-all

echo ""
echo "--- Required Services Check ---"
for service in smtp smtp-submission smtps imaps pop3s; do
    if sudo firewall-cmd --zone=public --query-service=$service 2>/dev/null; then
        echo "✅ $service"
    else
        echo "❌ $service MISSING"
    fi
done

echo ""
echo "--- Port Accessibility Tests ---"
for port in 25 465 587 993 995; do
    if ss -tlnp | grep -q ":$port "; then
        echo "✅ Port $port listening"
    else
        echo "❌ Port $port NOT listening"
    fi
done

Make it executable and run weekly:

sudo chmod +x /usr/local/bin/firewall-audit.sh
sudo crontab -e

Add: 0 9 * * 1 /usr/local/bin/firewall-audit.sh | mail -s "Firewall Audit" admin@yourdomain.com


Conclusion

Firewalld is powerful but complex. The key takeaways:

  1. Ports aren’t enough – Rich rules and zone defaults override them
  2. Use services--add-service=smtp-submission is better than --add-port=587/tcp
  3. Check all zones – Your interface might be in a different zone
  4. Rich rules apply first – They can block traffic even when ports are open

Your email server firewall is now properly configured. Connections to ports 465 and 587 will work from anywhere, as long as users authenticate.