Hey,
Today I’m doing pure sysadmin work and I’ve been asked to migrate several servers from an obsolete IP range 192.168.x to 10.x. Things were quite easy until I reached the internal mail server that can be used by hundreds field hardware as a relay server. Everybody is supposed to use DNS entry but I won’t trust that.
So my idea is to switch eth0 to the new network and keep a new eth1 in the old one to keep the service working and be able to log what’s using the obsolete address.
There’s just a little problem: if my default gateway is on eth0, any packet entering eth1 from a routed network (would work for what’s connected in the legacy local network) will be answered using the default gateway on eth0. That’s asymmetric routing and that just doesn’t work.
Okay, so how do I solve that ? With Shorewall of course ! The idea if to tag any packet entering eth1 with a different mark than the ones coming throught eth0 and provide different routing table for each mark. I’ll do this on CentOS today but it should be basically the same for any Linux system. Shorewall is usually available everywhere but you can try doing this by hand with “ip” and “iptables”. Looks like a lot of pain to me, though.
Having both address routed and working is a nice step but it’s pretty useless if I have no way to find out who’s using the obsolete address so we’ll use Shorewall to log these access and create a specific rsyslog/logrotate configuration to get a dedicated log.
First, we’ll change the network configuration to have both interfaces up with a default gateway only on the first interface (connected to the new network). The gateway will be later overridden by Shorewall but it’s always saner to have a default configuration working, even with limited feature.
So make sure to create proper ifcfg-eth0
and ifcfg-eth1
in /etc/sysconfig/network-scripts
and make sure to have only GATEWAY
defined on the new network. You should also make sure that the server is reachable on its new address and reachable on the old address with a machine directly connected to the legacy network.
Let’s continue with a very basic Shorewall configuration. yum -y install shorewall
and then make sure to have the three following files in /etc/shorewall
:
- interfaces – List of network adapter handled by Shorewall
- policy – Default firewall policies between each zone
- providers – This one is PBR specific, we’ll use this to mark packets
- rules – Overrides default policies with port/host rules
- shorewall.conf – Global settings
- zones – Map interfaces to firewall zones
If you miss one, copy it from /usr/share/shorewall/configfiles/
So let’s do a few adjustments in shorewall.conf
first:
IP_FORWARDING=No (No this machine SHOULD never be used a gateway between legacy and new network, we're not here to create security flows ;-))
DISABLE_IPV6=Yes (Sadly, there's no IPv6 here so it's better to let Shorewall4 kill the whole stack)
LOGTAGONLY=Yes (Change the way Shorewall generate log prefix, otherwise ours will be too long and get shortened)
Now defines the interfaces in rules
:
1 2 |
loc eth0 tcpflags,logmartians,nosmurfs,sourceroute=0 old eth1 tcpflags,logmartians,nosmurfs,sourceroute=0 |
And map them to IPv4 zones
:
1 2 3 |
fw firewall loc ipv4 old ipv4 |
fw is a default zone meaning “myself”.
And we create a default policy
allowing the machine itself to reach legacy and new network zones and blocking any incoming packets.
1 2 |
fw loc,old ACCEPT all all DROP info |
Finally we’ll add a set of default rules
to be at least, to SSH the server again
1 2 |
Ping(ACCEPT) loc fw SSH(ACCEPT) loc fw |
Just like in policy file, you can use loc,old if you want to permit ping and SSH from the old network too.
I’ll also add a few rules
to permit mail related services from the new zone:
1 2 3 |
SMTP(ACCEPT) loc fw Submission(ACCEPT) loc fw IMAP(ACCEPT) loc fw |
Okay, now we can enable and start Shorewall.
1 2 |
systemctl enable shorewall systemctl start shorewall |
Now we’ll ask Shorewall to mark packets differently according to the incoming interface. This will be done in providers
file.
1 2 |
NEW 1 1 - eth0 10.0.0.254 track OLD 2 2 - eth1 192.168.0.254 track |
Last column is the gateway to use on each network.
Let’s permit mail-related traffic from the legacy network but ask Shorewall to log these packets. Add the following in rules
file:
1 2 3 |
SMTP(ACCEPT):info:MailMigration old fw Submission(ACCEPT):info:MailMigration old fw IMAP(ACCEPT):info:MailMigration old fw |
Reload Shorewall and try to telnet tcp/25 from a routed network, both IPs are now working !
1 |
systemctl reload shorewall |
If you check /var/log/messages
you will see log like:
Jul 24 16:55:39 mailsrv kernel: Shorewall:MailMigration:ACCE IN=eth1 OUT= MAC=XXX SRC=192.168.55.4 DST=192.168.0.10 LEN=60 TOS=0x00 PREC=0x00 TTL=63 ID=22405 DF PROTO=TCP SPT=39474 DPT=25 WINDOW=29200 RES=0x00 SYN URGP=0 MARK=0x2
You can also check your routing tables with ip route show table.
ip route show table main shows no more default gateway
ip route show table 1 shows local route for eth0 network and default gateway of the new network. It’ll be used for packet tagged as 1.
ip route show table 2 shows local route for eth1 network and default gateway of the legacy network. It’ll be used for packet tagged as 2 (Note the log above with MARK=0x2).
Your server is now completely accessible from both networks and you can easily monitor the log file to find clients still using the legacy address. But we can make it a lot easier by asking rsyslog to create a separate log file with these specific messages:
Create /etc/rsyslog.d/mailmigration.conf
with the following content:
1 2 3 4 5 6 |
$FileOwner root $FileGroup root $Umask 0000 $FileCreateMode 0644 :msg, startswith, "Shorewall:MailMigration:" /var/log/mailmigration.log & stop |
And the associated logrotate /etc/logrotate.d/mailmigration
file to avoid having a single never ending file:
1 2 3 4 5 6 7 8 |
/var/log/mailmigration.log { missingok sharedscripts postrotate /bin/kill -HUP `cat /var/run/syslogd.pid 2> /dev/null` 2> /dev/null || true endscript } |
If you want to go further for a more automated way of handling this, I’d definitely suggest having a look at Rsyslog AMQP module to publish event to a RabbitMQ and write a quick Python consumer to parse and notify “Someone” (may I suggest calling some API to create an internal support ticket ?) using Pika. The “worker.py” file should be enough for testing, just try/except/ch.basic_nack your handler so the message goes back in queue in case of failure.