From 08a59e89c397de0911cf2a22d010536eeeaa6a2f Mon Sep 17 00:00:00 2001 From: Slair1 Date: Tue, 19 Jun 2018 14:19:26 -0500 Subject: [PATCH] Source NAT option on Private Gateway (#2681) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Using Source NAT option on Private Gateway does not work This fixes #2680 ## Description When you use the Source NAT feature of Private Gateways on a VPC. This should Source NAT all traffic from CloudStack VMs going towards IPs reachable through Private Gateways. This change in this PR, stops adding the Source CIDR to SNAT rules. This should be discussed/reviewed, but i can see no reason why the Source CIDR is needed. There can only be one SNAT IP per interface, except for Static (one-to-one) NATs, which still work with this change in place. The outbound interface is what matters in the rule. ##### SUMMARY There is a bug in the Private Gateway functionality, when Source NAT is enabled for the Private Gateway. When the SNAT is added to iptables, it has the source CIDR of the private gateway subnet. Since no VMs live in that private gateway subnet, the SNAT doesn’t work. ##### STEPS TO REPRODUCE Below is an example: - VMs have IP addresses in the 10.0.0.0/24 subnet. - The Private Gateway address is 10.101.141.2/30 In the outputs below, the SOURCE field for the new SNAT (eth3) only matches if the source is 10.101.141.0/30. Since the VM has an IP address in 10.0.0.0/24, the VMs don’t get SNAT’d as they should when talking across the private gateway. The SOURCE should be set to ANYWHERE. ##### BEFORE ADDING PRIVATE GATEWAY ~~~ Chain POSTROUTING (policy ACCEPT 1 packets, 52 bytes) pkts bytes target prot opt in out source destination 2 736 SNAT all -- any eth2 10.0.0.0/24 anywhere to:10.0.0.1 16 1039 SNAT all -- any eth1 anywhere anywhere to:46.99.52.18 ~~~ ##### EXPECTED RESULTS ~~~ Chain POSTROUTING (policy ACCEPT 0 packets, 0 bytes) pkts bytes target prot opt in out source destination 0 0 SNAT all -- any eth3 anywhere anywhere to:10.101.141.2 2 736 SNAT all -- any eth2 anywhere anywhere to:10.0.0.1 23 1515 SNAT all -- any eth1 anywhere anywhere to:46.99.52.18 ~~~ ##### ACTUAL RESULTS ~~~ Chain POSTROUTING (policy ACCEPT 0 packets, 0 bytes) pkts bytes target prot opt in out source destination 0 0 SNAT all -- any eth3 10.101.141.0/30 anywhere to:10.101.141.2 2 736 SNAT all -- any eth2 10.0.0.0/24 anywhere to:10.0.0.1 23 1515 SNAT all -- any eth1 anywhere anywhere to:46.99.52.18 ~~~ ## Types of changes - [ ] Breaking change (fix or feature that would cause existing functionality to change) - [ ] New feature (non-breaking change which adds functionality) - [X] Bug fix (non-breaking change which fixes an issue) - [ ] Enhancement (improves an existing feature and functionality) - [ ] Cleanup (Code refactoring and cleanup, that may add test cases) ## GitHub Issue/PRs Fixes: #2680 ## Screenshots (if appropriate): ## How Has This Been Tested? ## Checklist: - [x] I have read the [CONTRIBUTING](https://github.com/apache/cloudstack/blob/master/CONTRIBUTING.md) document. - [x] My code follows the code style of this project. - [ ] My change requires a change to the documentation. - [ ] I have updated the documentation accordingly. Testing - [ ] I have added tests to cover my changes. - [ ] All relevant new and existing integration tests have passed. - [ ] A full integration testsuite with all test that can run on my environment has passed. --- systemvm/debian/opt/cloud/bin/configure.py | 9 +++------ systemvm/debian/opt/cloud/bin/cs/CsAddress.py | 7 ++++--- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/systemvm/debian/opt/cloud/bin/configure.py b/systemvm/debian/opt/cloud/bin/configure.py index e023a9fb94e..c8e3ff6e504 100755 --- a/systemvm/debian/opt/cloud/bin/configure.py +++ b/systemvm/debian/opt/cloud/bin/configure.py @@ -937,12 +937,9 @@ class CsForwardingRules(CsDataBag): self.fw.append(["filter", "", "-A FORWARD -i %s -o eth0 -d %s -m state --state NEW -j ACCEPT " % (device, rule["internal_ip"])]) - # Configure the hairpin nat - self.fw.append(["nat", "front", - "-A PREROUTING -d %s -i eth0 -j DNAT --to-destination %s" % (rule["public_ip"], rule["internal_ip"])]) - - self.fw.append(["nat", "front", "-A POSTROUTING -s %s -d %s -j SNAT -o eth0 --to-source %s" % - (self.getNetworkByIp(rule['internal_ip']), rule["internal_ip"], self.getGuestIp())]) + # Configure the hairpin snat + self.fw.append(["nat", "front", "-A POSTROUTING -s %s -d %s -j SNAT -o %s --to-source %s" % + (self.getNetworkByIp(rule['internal_ip']), rule["internal_ip"], self.getDeviceByIp(rule["internal_ip"]), self.getGuestIp())]) class IpTablesExecutor: diff --git a/systemvm/debian/opt/cloud/bin/cs/CsAddress.py b/systemvm/debian/opt/cloud/bin/cs/CsAddress.py index 3212dff71ee..10b6d3cfdbe 100755 --- a/systemvm/debian/opt/cloud/bin/cs/CsAddress.py +++ b/systemvm/debian/opt/cloud/bin/cs/CsAddress.py @@ -476,9 +476,10 @@ class CsIP: self.fw.append(["", "front", "-A NETWORK_STATS_%s -o %s -s %s" % ("eth1", "eth1", guestNetworkCidr)]) - self.fw.append(["nat", "front", - "-A POSTROUTING -s %s -o %s -j SNAT --to-source %s" % - (guestNetworkCidr, self.dev, self.address['public_ip'])]) + if self.address["source_nat"]: + self.fw.append(["nat", "front", + "-A POSTROUTING -o %s -j SNAT --to-source %s" % + (self.dev, self.address['public_ip'])]) if self.get_type() in ["public"]: self.fw.append(