[
https://issues.apache.org/jira/browse/CLOUDSTACK-9339?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=15439991#comment-15439991
]
ASF GitHub Bot commented on CLOUDSTACK-9339:
--------------------------------------------
Github user jburwell commented on a diff in the pull request:
https://github.com/apache/cloudstack/pull/1659#discussion_r76494735
--- Diff: test/integration/smoke/test_multiple_public_ip_ranges.py ---
@@ -0,0 +1,887 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+""" BVT tests for network services on public IP's from different public IP
+ range than that of associated source NAT IP of the network. Each IP
associated
+ with network from a different public IP range results in a new public
+ interface on VR (eth3, eth4 etc) and iptable
+"""
+# Import Local Modules
+from marvin.codes import (FAILED, STATIC_NAT_RULE, LB_RULE,
+ NAT_RULE, PASS)
+from marvin.cloudstackTestCase import cloudstackTestCase
+from marvin.cloudstackException import CloudstackAPIException
+from marvin.cloudstackAPI import rebootRouter
+from marvin.sshClient import SshClient
+from marvin.lib.utils import cleanup_resources, get_process_status
+from marvin.lib.base import (Account,
+ VirtualMachine,
+ ServiceOffering,
+ NATRule,
+ PublicIPAddress,
+ StaticNATRule,
+ FireWallRule,
+ Network,
+ NetworkOffering,
+ LoadBalancerRule,
+ PublicIpRange,
+ Router,
+ VpcOffering,
+ VPC,
+ NetworkACL)
+from marvin.lib.common import (get_domain,
+ get_zone,
+ get_template,
+ list_hosts,
+ list_publicIP,
+ list_nat_rules,
+ list_routers,
+ list_virtual_machines,
+ list_lb_rules,
+ list_configurations,
+ verifyGuestTrafficPortGroups)
+from nose.plugins.attrib import attr
+from ddt import ddt, data
+# Import System modules
+import socket
+import time
+import logging
+
+_multiprocess_shared_ = True
+
+logger = logging.getLogger('TestNetworkOps')
+stream_handler = logging.StreamHandler()
+logger.setLevel(logging.DEBUG)
+logger.addHandler(stream_handler)
+
+class TestPortForwarding(cloudstackTestCase):
+
+ @classmethod
+ def setUpClass(cls):
+
+ testClient = super(TestPortForwarding, cls).getClsTestClient()
+ cls.apiclient = testClient.getApiClient()
+ cls.services = testClient.getParsedTestDataConfig()
+ cls.hypervisor = testClient.getHypervisorInfo()
+ # Get Zone, Domain and templates
+ cls.domain = get_domain(cls.apiclient)
+ cls.zone = get_zone(cls.apiclient, testClient.getZoneForTests())
+ cls.services["virtual_machine"]["zoneid"] = cls.zone.id
+ cls.services["zoneid"] = cls.zone.id
+ template = get_template(
+ cls.apiclient,
+ cls.zone.id,
+ cls.services["ostype"]
+ )
+ if template == FAILED:
+ assert False, "get_template() failed to return template with
description %s" % cls.services[
+ "ostype"]
+
+ # Create an account, network, VM and IP addresses
+ cls.account = Account.create(
+ cls.apiclient,
+ cls.services["account"],
+ admin=True,
+ domainid=cls.domain.id
+ )
+ cls.services["publiciprange"]["zoneid"] = cls.zone.id
+ cls.service_offering = ServiceOffering.create(
+ cls.apiclient,
+ cls.services["service_offerings"]["tiny"]
+ )
+ cls.virtual_machine = VirtualMachine.create(
+ cls.apiclient,
+ cls.services["virtual_machine"],
+ templateid=template.id,
+ accountid=cls.account.name,
+ domainid=cls.account.domainid,
+ serviceofferingid=cls.service_offering.id
+ )
+ cls._cleanup = [
+ cls.virtual_machine,
+ cls.account,
+ cls.service_offering
+ ]
+
+ def setUp(self):
+ self.apiclient = self.testClient.getApiClient()
+ self.cleanup = []
+ return
+
+ @classmethod
+ def tearDownClass(cls):
+ try:
+ cls.apiclient = super(
+ TestPortForwarding,
+ cls).getClsTestClient().getApiClient()
+ cleanup_resources(cls.apiclient, cls._cleanup)
+ except Exception as e:
+ raise Exception("Warning: Exception during cleanup : %s" % e)
+
+ def tearDown(self):
+ cleanup_resources(self.apiclient, self.cleanup)
+ return
+
+ @attr(tags=["advanced", "smoke"], required_hardware="true")
+ def test_port_forwarding_on_ip_from_non_src_nat_ip_range(self):
+ """Test for port forwarding on a IP which is in pubic IP range
different
+ from public IP range that has source NAT IP associated with
network
+ """
+
+ # Validate the following:
+ # 1. Create a new public IP range and dedicate to a account
+ # 2. Acquire a IP from new public range
+ # 3. create a port forwarding on acquired IP from new range
+ # 4. Create a firewall rule to open up the port
+ # 5. Test SSH works to the VM
+
+ self.public_ip_range = PublicIpRange.create(
+ self.apiclient,
+ self.services["publiciprange"]
+ )
+
+ self.debug("Dedicating Public IP range to the account");
+ dedicate_public_ip_range_response = PublicIpRange.dedicate(
+ self.apiclient,
+
self.public_ip_range.vlan.id,
+ account=self.account.name,
+
domainid=self.account.domainid
+ )
+ ip_address = PublicIPAddress.create(
+ self.apiclient,
+ self.account.name,
+ self.zone.id,
+ self.account.domainid,
+ self.services["virtual_machine"]
+ )
+ self.cleanup.append(ip_address)
+ self.cleanup.append(self.public_ip_range)
+ # Check if VM is in Running state before creating NAT and firewall
rules
+ vm_response = VirtualMachine.list(
+ self.apiclient,
+ id=self.virtual_machine.id
+ )
+
+ self.assertEqual(
+ isinstance(vm_response, list),
+ True,
+ "Check list VM returns a valid list"
+ )
+
+ self.assertNotEqual(
+ len(vm_response),
+ 0,
+ "Check Port Forwarding Rule is created"
+ )
+ self.assertEqual(
+ vm_response[0].state,
+ 'Running',
+ "VM state should be Running before creating a NAT rule."
+ )
+
+ # Open up firewall port for SSH
+ FireWallRule.create(
+ self.apiclient,
+ ipaddressid=ip_address.ipaddress.id,
+ protocol=self.services["natrule"]["protocol"],
+ cidrlist=['0.0.0.0/0'],
+ startport=self.services["natrule"]["publicport"],
+ endport=self.services["natrule"]["publicport"]
+ )
+
+ # Create PF rule
+ nat_rule = NATRule.create(
+ self.apiclient,
+ self.virtual_machine,
+ self.services["natrule"],
+ ip_address.ipaddress.id
+ )
+
+ try:
+ logger.debug("SSHing into VM with IP address %s with NAT IP
%s" %
+ (
+ self.virtual_machine.ipaddress,
+ ip_address.ipaddress.ipaddress
+ ))
+
self.virtual_machine.get_ssh_client(ip_address.ipaddress.ipaddress)
+ except Exception as e:
+ self.fail(
+ "SSH Access failed for %s: %s" %
+ (self.virtual_machine.ipaddress, e)
+ )
+
+ nat_rule.delete(self.apiclient)
+
+class TestStaticNat(cloudstackTestCase):
+
+ @classmethod
+ def setUpClass(cls):
+
+ testClient = super(TestStaticNat, cls).getClsTestClient()
+ cls.apiclient = testClient.getApiClient()
+ cls.services = testClient.getParsedTestDataConfig()
+ cls.hypervisor = testClient.getHypervisorInfo()
+ # Get Zone, Domain and templates
+ cls.domain = get_domain(cls.apiclient)
+ cls.zone = get_zone(cls.apiclient, testClient.getZoneForTests())
+ cls.services["virtual_machine"]["zoneid"] = cls.zone.id
+ cls.services["zoneid"] = cls.zone.id
+ template = get_template(
+ cls.apiclient,
+ cls.zone.id,
+ cls.services["ostype"]
+ )
+ if template == FAILED:
+ assert False, "get_template() failed to return template with
description %s" % cls.services[
+ "ostype"]
+
+ # Create an account, network, VM and IP addresses
+ cls.account = Account.create(
+ cls.apiclient,
+ cls.services["account"],
+ admin=True,
+ domainid=cls.domain.id
+ )
+ cls.services["publiciprange"]["zoneid"] = cls.zone.id
+ cls.service_offering = ServiceOffering.create(
+ cls.apiclient,
+ cls.services["service_offerings"]["tiny"]
+ )
+ cls.virtual_machine = VirtualMachine.create(
+ cls.apiclient,
+ cls.services["virtual_machine"],
+ templateid=template.id,
+ accountid=cls.account.name,
+ domainid=cls.account.domainid,
+ serviceofferingid=cls.service_offering.id
+ )
+ cls.defaultNetworkId = cls.virtual_machine.nic[0].networkid
+ cls._cleanup = [
+ cls.virtual_machine,
+ cls.account,
+ cls.service_offering
+ ]
+
+ def setUp(self):
+ self.apiclient = self.testClient.getApiClient()
+ self.cleanup = []
+ return
+
+ @classmethod
+ def tearDownClass(cls):
+ try:
+ cls.apiclient = super(
+ TestStaticNat,
+ cls).getClsTestClient().getApiClient()
+ cleanup_resources(cls.apiclient, cls._cleanup)
+ except Exception as e:
+ raise Exception("Warning: Exception during cleanup : %s" % e)
+
+ def tearDown(self):
+ cleanup_resources(self.apiclient, self.cleanup)
+ return
+
+ @attr(tags=["advanced", "smoke"], required_hardware="true")
+ def test_static_nat_on_ip_from_non_src_nat_ip_range(self):
+ """Test for static nat on a IP which is in pubic IP range different
+ from public IP range that has source NAT IP associated with
network
+ """
+
+ # Validate the following:
+ # 1. Create a new public IP range and dedicate to a account
+ # 2. Acquire a IP from new public range
+ # 3. Enable static NAT on acquired IP from new range
+ # 4. Create a firewall rule to open up the port
+ # 5. Test SSH works to the VM
+
+ self.public_ip_range = PublicIpRange.create(
+ self.apiclient,
+ self.services["publiciprange"]
+ )
+
+ self.debug("Dedicating Public IP range to the account");
+ dedicate_public_ip_range_response = PublicIpRange.dedicate(
+ self.apiclient,
+
self.public_ip_range.vlan.id,
+ account=self.account.name,
+
domainid=self.account.domainid
+ )
+ ip_address = PublicIPAddress.create(
+ self.apiclient,
+ self.account.name,
+ self.zone.id,
+ self.account.domainid,
+ self.services["virtual_machine"]
+ )
+ self.cleanup.append(ip_address)
+ self.cleanup.append(self.public_ip_range)
--- End diff --
Please see previous notes regarding extracting the dedication/creation of
public IP ranges to private utility method.
> Virtual Routers don't handle Multiple Public Interfaces
> -------------------------------------------------------
>
> Key: CLOUDSTACK-9339
> URL: https://issues.apache.org/jira/browse/CLOUDSTACK-9339
> Project: CloudStack
> Issue Type: Bug
> Security Level: Public(Anyone can view this level - this is the
> default.)
> Components: Virtual Router
> Affects Versions: 4.8.0
> Reporter: dsclose
> Labels: firewall, nat, router
>
> There are a series of issues with the way Virtual Routers manage multiple
> public interfaces. These are more pronounced on redundant virtual router
> setups. I have not attempted to examine these issues in a VPC context.
> Outside of a VPC context, however, the following is expected behaviour:
> * eth0 connects the router to the guest network.
> * In RvR setups, keepalived manages the guests' gateway IP as a virtual IP on
> eth0.
> * eth1 provides a local link to the hypervisor, allowing Cloudstack to issue
> commands to the router.
> * eth2 is the routers public interface. By default, a single public IP will
> be setup on eth2 along with the necessary iptables and ip rules to source-NAT
> guest traffic to that public IP.
> * When a public IP address is assigned to the router that is on a separate
> subnet to the source-NAT IP, a new interface is configured, such as eth3, and
> the IP is assigned to that interface.
> * This can result in eth3, eth4, eth5, etc. being created depending upon how
> many public subnets the router has to work with.
> The above all works. The following, however, is currently not working:
> * Public interfaces should be set to DOWN on backup redundant routers. The
> master.py script is responsible for setting public interfaces to UP during a
> keepalived transition. Currently the check_is_up method of the CsIP class
> brings all interfaces UP on both RvR. A proposed fix for this has been
> discussed on the mailing list. That fix will leave public interfaces DOWN on
> RvR allowing the keepalived transition to control the state of public
> interfaces. Issue #1413 includes a commit that contradicts the proposed fix
> so it is unclear what the current state of the code should be.
> * Newly created interfaces should be set to UP on master redundant routers.
> Assuming public interfaces should be default be DOWN on an RvR we need to
> accommodate the fact that, as interfaces are created, no keepalived
> transition occurs. This means that assigning an IP from a new public subnet
> will have no effect (as the interface will be down) until the network is
> restarted with a "clean up."
> * Public interfaces other than eth2 do not forward traffic. There are two
> iptables rules in the FORWARD chain of the filter table created for eth2 that
> allow forwarding between eth2 and eth0. Equivalent rules are not created for
> other public interfaces so forwarded traffic is dropped.
> * Outbound traffic from guest VMs does not honour static-NAT rules. Instead,
> outbound traffic is source-NAT'd to the networks default source-NAT IP. New
> connections from guests that are destined for public networks are processed
> like so:
> 1. Traffic is matched against the following rule in the mangle table that
> marks the connection with a 0x0:
> *mangle
> -A PREROUTING -i eth0 -m state --state NEW -j CONNMARK --set-xmark
> 0x0/0xffffffff
> 2. There are no "ip rule" statements that match a connection marked 0x0, so
> the kernel routes the connection via the default gateway. That gateway is on
> source-NAT subnet, so the connection is routed out of eth2.
> 3. The following iptables rules are then matched in the filter table:
> *filter
> -A FORWARD -i eth0 -o eth2 -j FW_OUTBOUND
> -A FW_OUTBOUND -j FW_EGRESS_RULES
> -A FW_EGRESS_RULES -j ACCEPT
> 4. Finally, the following rule is matched from the nat table, where the IP
> address is the source-NAT IP:
> *nat
> -A POSTROUTING -o eth2 -j SNAT --to-source 123.4.5.67
>
--
This message was sent by Atlassian JIRA
(v6.3.4#6332)