This is an automated email from the ASF dual-hosted git repository.
weizhouapache pushed a commit to branch 4.22
in repository https://gitbox.apache.org/repos/asf/cloudstack.git
The following commit(s) were added to refs/heads/4.22 by this push:
new c7667d8c1fb VR: fix passive ftp through the virtual router on newer
system vm templates (#14330)
c7667d8c1fb is described below
commit c7667d8c1fb8ceb4ef5ee687fd85cc84a545c275
Author: Daman Arora <[email protected]>
AuthorDate: Fri Oct 16 02:24:07 2026 -0400
VR: fix passive ftp through the virtual router on newer system vm templates
(#14330)
* fix passive ftp through the virtual router on newer system vm templates
* attach the ftp helper when a port forward sends another public port to
port 21
* only attach the ftp helper on static nat and port forwarded public ips
---
systemvm/debian/opt/cloud/bin/configure.py | 17 +++++++
systemvm/debian/opt/cloud/bin/setup/common.sh | 4 +-
systemvm/debian/opt/cloud/bin/setup/router.sh | 2 +-
systemvm/debian/opt/cloud/bin/setup/vpcrouter.sh | 2 +-
systemvm/test/TestCsForwardingRules.py | 63 ++++++++++++++++++++++++
5 files changed, 85 insertions(+), 3 deletions(-)
diff --git a/systemvm/debian/opt/cloud/bin/configure.py
b/systemvm/debian/opt/cloud/bin/configure.py
index 24195a11bff..0575433ad00 100755
--- a/systemvm/debian/opt/cloud/bin/configure.py
+++ b/systemvm/debian/opt/cloud/bin/configure.py
@@ -1411,6 +1411,22 @@ class CsForwardingRules(CsDataBag):
self.forward_vpc(rule)
else:
self.forward_vr(rule)
+ self.forward_ftp_helper(rule)
+
+ def forward_ftp_helper(self, rule):
+ # Linux 6.0 no longer attaches the ftp helper after DNAT, so attach it
+ # to the public port that is forwarded to port 21
+ if rule["protocol"] != "tcp" or "any" in [rule["public_ports"],
rule["internal_ports"]]:
+ return
+ public_start = int(rule["public_ports"].split(":")[0])
+ internal_ports = rule["internal_ports"].split(":")
+ internal_start = int(internal_ports[0])
+ internal_end = int(internal_ports[-1])
+ if not internal_start <= 21 <= internal_end:
+ return
+ public_port = public_start + 21 - internal_start
+ self.fw.append(["raw", "", "-A PREROUTING -d %s/32 -p tcp -m tcp
--dport %s -j CT --helper ftp" %
+ (rule["public_ip"], public_port)])
def forward_vr(self, rule):
# Prefetch iptables variables
@@ -1555,6 +1571,7 @@ class CsForwardingRules(CsDataBag):
self.fw.append(["mangle", "front", "-A %s -d %s -j RETURN" %
(chain_name, static_route['network'])])
+ self.fw.append(["raw", "", "-A PREROUTING -d %s/32 -p tcp -m tcp
--dport 21 -j CT --helper ftp" % rule["public_ip"]])
self.fw.append(["nat", "front",
"-A PREROUTING -d %s/32 -j DNAT --to-destination %s" %
(rule["public_ip"], rule["internal_ip"])])
self.fw.append(["nat", "front",
diff --git a/systemvm/debian/opt/cloud/bin/setup/common.sh
b/systemvm/debian/opt/cloud/bin/setup/common.sh
index ef1576ab588..ef610c91407 100755
--- a/systemvm/debian/opt/cloud/bin/setup/common.sh
+++ b/systemvm/debian/opt/cloud/bin/setup/common.sh
@@ -164,7 +164,9 @@ enable_fwding() {
enable_passive_ftp() {
log_it "cloud: enabling passive FTP for guest VMs"
- echo "$1" > /proc/sys/net/netfilter/nf_conntrack_helper
+ # Linux 6.0 removed the nf_conntrack_helper sysctl, the ftp helper is
+ # attached by raw table rules for static nat and port forwarding
+ modprobe nf_nat_ftp
}
disable_rpfilter() {
diff --git a/systemvm/debian/opt/cloud/bin/setup/router.sh
b/systemvm/debian/opt/cloud/bin/setup/router.sh
index 5c72105f47a..dc4fb9d0a69 100755
--- a/systemvm/debian/opt/cloud/bin/setup/router.sh
+++ b/systemvm/debian/opt/cloud/bin/setup/router.sh
@@ -70,7 +70,7 @@ setup_router() {
disable_rpfilter_domR
enable_fwding 1
enable_rpsrfs 1
- enable_passive_ftp 1
+ enable_passive_ftp
restore_ipv6
# Only allow DNS service for current network
diff --git a/systemvm/debian/opt/cloud/bin/setup/vpcrouter.sh
b/systemvm/debian/opt/cloud/bin/setup/vpcrouter.sh
index 767f87848dd..cf88e6cac48 100755
--- a/systemvm/debian/opt/cloud/bin/setup/vpcrouter.sh
+++ b/systemvm/debian/opt/cloud/bin/setup/vpcrouter.sh
@@ -82,7 +82,7 @@ setup_vpcrouter() {
enable_vpc_rpsrfs 1
disable_rpfilter
enable_fwding 1
- enable_passive_ftp 1
+ enable_passive_ftp
cp /etc/iptables/iptables-vpcrouter /etc/iptables/rules.v4
cp /etc/vpcdnsmasq.conf /etc/dnsmasq.conf
cp /etc/cloud-nic.rules /etc/udev/rules.d/cloud-nic.rules
diff --git a/systemvm/test/TestCsForwardingRules.py
b/systemvm/test/TestCsForwardingRules.py
new file mode 100644
index 00000000000..50ec370ea9d
--- /dev/null
+++ b/systemvm/test/TestCsForwardingRules.py
@@ -0,0 +1,63 @@
+# 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.
+
+import unittest
+import mock
+from configure import CsForwardingRules
+
+
+class TestCsForwardingRules(unittest.TestCase):
+
+ def ftp_helper_rules(self, protocol, public_ports, internal_ports):
+ forwarding = CsForwardingRules.__new__(CsForwardingRules)
+ forwarding.fw = []
+ forwarding.forward_ftp_helper({"public_ip": "10.0.0.2", "internal_ip":
"10.1.1.10", "protocol": protocol,
+ "public_ports": public_ports,
"internal_ports": internal_ports})
+ return forwarding.fw
+
+ def test_ftp_helper_on_other_public_port(self):
+ self.assertEqual(self.ftp_helper_rules("tcp", "2121:2121", "21:21"),
+ [["raw", "", "-A PREROUTING -d 10.0.0.2/32 -p tcp -m
tcp --dport 2121 -j CT --helper ftp"]])
+
+ def test_ftp_helper_on_port_range(self):
+ self.assertEqual(self.ftp_helper_rules("tcp", "1020:1030", "20:30"),
+ [["raw", "", "-A PREROUTING -d 10.0.0.2/32 -p tcp -m
tcp --dport 1021 -j CT --helper ftp"]])
+
+ def test_ftp_helper_on_port_21(self):
+ self.assertEqual(self.ftp_helper_rules("tcp", "21:21", "21:21"),
+ [["raw", "", "-A PREROUTING -d 10.0.0.2/32 -p tcp -m
tcp --dport 21 -j CT --helper ftp"]])
+
+ def test_no_ftp_helper(self):
+ self.assertEqual(self.ftp_helper_rules("tcp", "2121:2121",
"2121:2121"), [])
+ self.assertEqual(self.ftp_helper_rules("udp", "2121:2121", "21:21"),
[])
+ self.assertEqual(self.ftp_helper_rules("tcp", "any", "any"), [])
+
+ @mock.patch.object(CsForwardingRules, 'getStaticRoutes', return_value=[])
+ @mock.patch.object(CsForwardingRules, 'getPrivateGatewayNetworks',
return_value=[])
+ @mock.patch.object(CsForwardingRules, 'getGuestIpByIp',
return_value="10.1.1.1")
+ @mock.patch.object(CsForwardingRules, 'getNetworkByIp',
return_value="10.1.1.0/24")
+ @mock.patch.object(CsForwardingRules, 'getDeviceByIp', return_value="eth2")
+ def test_ftp_helper_on_static_nat(self, *_):
+ forwarding = CsForwardingRules.__new__(CsForwardingRules)
+ forwarding.fw = []
+ forwarding.processStaticNatRule({"public_ip": "10.0.0.2",
"internal_ip": "10.1.1.10"})
+ self.assertIn(["raw", "", "-A PREROUTING -d 10.0.0.2/32 -p tcp -m tcp
--dport 21 -j CT --helper ftp"],
+ forwarding.fw)
+
+
+if __name__ == '__main__':
+ unittest.main()