How to Force Specific Source IP for Outbound Connections on Linux Multi-IP Host


1 views

When a Linux server has multiple IP addresses assigned to the same interface (via aliases or secondary IPs), the kernel may unpredictably select source addresses for outbound connections. This causes critical issues when:

  • Email servers require matching forward/reverse DNS
  • Firewalls whitelist specific source IPs
  • Applications depend on consistent outbound identity

The Linux kernel selects source IPs based on:

1. The route table's src attribute
2. Strong host model (newer kernels)
3. Weak host model (older kernels)
4. First available IP when no explicit rule exists

In your case, the 255.255.255.255 netmask creates host routes that complicate source selection. While restarting networking may temporarily fix it, we need deterministic solutions.

Method 1: Policy Routing

Add rules to force specific source IPs:

# Create custom routing table
echo "200 custom_table" >> /etc/iproute2/rt_tables

# Add route for primary IP
ip route add default via 81.169.180.1 dev eth0 src 81.169.180.51 table custom_table

# Create policy rule
ip rule add from 81.169.180.51 lookup custom_table

# Make persistent (Debian)
cat > /etc/network/if-up.d/policy_routing <

Method 2: Bind Before Connect

For application-level control (Python example):

import socket

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind(('81.169.180.51', 0))  # Bind to specific source IP
s.connect(('example.com', 80))

Method 3: sysctl Parameters

Adjust kernel behavior:

# Prefer primary IP for outbound
sysctl -w net.ipv4.conf.eth0.route_localnet=1
sysctl -w net.ipv4.conf.all.route_localnet=1

# Make persistent
echo "net.ipv4.conf.eth0.route_localnet=1" >> /etc/sysctl.conf
echo "net.ipv4.conf.all.route_localnet=1" >> /etc/sysctl.conf

Verify outbound IP with:

curl --interface 81.169.180.51 ifconfig.me
# Or for SMTP specifically:
telnet mail.example.com 25
EHLO yourdomain.com

The SMTP banner should show your correct source IP in received headers.

For Postfix specifically, enforce the HELO IP:

# /etc/postfix/main.cf
smtp_bind_address = 81.169.180.51
smtp_helo_name = mail.yourdomain.com

When your Linux server has multiple IP addresses assigned to the same interface, the kernel's default behavior for selecting source IPs can cause issues. Particularly for email servers where reverse DNS must match the sending IP, this becomes critical.

# Current interface configuration example
eth0: 81.169.180.51/32
eth0:0: 85.214.157.120/32

The key lies in the routing table's source-based routing capabilities. Your current setup shows:

# ip route show
default via 81.169.180.1 dev eth0
81.169.180.1 dev eth0 scope link

We'll use Linux's advanced routing capabilities to enforce source IP selection:

# Create a custom routing table
echo "200 custom" >> /etc/iproute2/rt_tables

# Add rules to use specific source IPs
ip rule add from 81.169.180.51 lookup custom
ip route add default via 81.169.180.1 dev eth0 table custom

# For the secondary IP (optional)
ip rule add from 85.214.157.120 lookup main

For Debian systems, add to /etc/network/interfaces:

post-up ip rule add from 81.169.180.51 lookup custom
post-up ip route add default via 81.169.180.1 dev eth0 table custom

Verify the source IP selection with curl:

curl --interface 81.169.180.51 ifconfig.me
curl --interface 85.214.157.120 ifconfig.me

For application-level control (like Postfix):

# In postfix main.cf
smtp_bind_address = 81.169.180.51

Use these commands to troubleshoot:

ip route get 8.8.8.8 from 81.169.180.51
ip rule list
tcpdump -i eth0 -nn -v src host 81.169.180.51

For DHCP-assigned interfaces, use dhclient hooks:

# /etc/dhcp/dhclient-enter-hooks.d/source_routing
case $reason in
    BOUND|RENEW|REBIND)
        ip rule add from 81.169.180.51 lookup custom
        ip route add default via 81.169.180.1 dev eth0 table custom
        ;;
esac