Skip to content

Commit c7667d8

Browse files
authored
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
1 parent 546569b commit c7667d8

5 files changed

Lines changed: 85 additions & 3 deletions

File tree

‎systemvm/debian/opt/cloud/bin/configure.py‎

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1411,6 +1411,22 @@ def processForwardRule(self, rule):
14111411
self.forward_vpc(rule)
14121412
else:
14131413
self.forward_vr(rule)
1414+
self.forward_ftp_helper(rule)
1415+
1416+
def forward_ftp_helper(self, rule):
1417+
# Linux 6.0 no longer attaches the ftp helper after DNAT, so attach it
1418+
# to the public port that is forwarded to port 21
1419+
if rule["protocol"] != "tcp" or "any" in [rule["public_ports"], rule["internal_ports"]]:
1420+
return
1421+
public_start = int(rule["public_ports"].split(":")[0])
1422+
internal_ports = rule["internal_ports"].split(":")
1423+
internal_start = int(internal_ports[0])
1424+
internal_end = int(internal_ports[-1])
1425+
if not internal_start <= 21 <= internal_end:
1426+
return
1427+
public_port = public_start + 21 - internal_start
1428+
self.fw.append(["raw", "", "-A PREROUTING -d %s/32 -p tcp -m tcp --dport %s -j CT --helper ftp" %
1429+
(rule["public_ip"], public_port)])
14141430

14151431
def forward_vr(self, rule):
14161432
# Prefetch iptables variables
@@ -1555,6 +1571,7 @@ def processStaticNatRule(self, rule):
15551571
self.fw.append(["mangle", "front", "-A %s -d %s -j RETURN" %
15561572
(chain_name, static_route['network'])])
15571573

1574+
self.fw.append(["raw", "", "-A PREROUTING -d %s/32 -p tcp -m tcp --dport 21 -j CT --helper ftp" % rule["public_ip"]])
15581575
self.fw.append(["nat", "front",
15591576
"-A PREROUTING -d %s/32 -j DNAT --to-destination %s" % (rule["public_ip"], rule["internal_ip"])])
15601577
self.fw.append(["nat", "front",

‎systemvm/debian/opt/cloud/bin/setup/common.sh‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,9 @@ enable_fwding() {
164164

165165
enable_passive_ftp() {
166166
log_it "cloud: enabling passive FTP for guest VMs"
167-
echo "$1" > /proc/sys/net/netfilter/nf_conntrack_helper
167+
# Linux 6.0 removed the nf_conntrack_helper sysctl, the ftp helper is
168+
# attached by raw table rules for static nat and port forwarding
169+
modprobe nf_nat_ftp
168170
}
169171

170172
disable_rpfilter() {

‎systemvm/debian/opt/cloud/bin/setup/router.sh‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ setup_router() {
7070
disable_rpfilter_domR
7171
enable_fwding 1
7272
enable_rpsrfs 1
73-
enable_passive_ftp 1
73+
enable_passive_ftp
7474
restore_ipv6
7575

7676
# Only allow DNS service for current network

‎systemvm/debian/opt/cloud/bin/setup/vpcrouter.sh‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ setup_vpcrouter() {
8282
enable_vpc_rpsrfs 1
8383
disable_rpfilter
8484
enable_fwding 1
85-
enable_passive_ftp 1
85+
enable_passive_ftp
8686
cp /etc/iptables/iptables-vpcrouter /etc/iptables/rules.v4
8787
cp /etc/vpcdnsmasq.conf /etc/dnsmasq.conf
8888
cp /etc/cloud-nic.rules /etc/udev/rules.d/cloud-nic.rules
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
# Licensed to the Apache Software Foundation (ASF) under one
2+
# or more contributor license agreements. See the NOTICE file
3+
# distributed with this work for additional information
4+
# regarding copyright ownership. The ASF licenses this file
5+
# to you under the Apache License, Version 2.0 (the
6+
# "License"); you may not use this file except in compliance
7+
# with the License. You may obtain a copy of the License at
8+
#
9+
# http://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# Unless required by applicable law or agreed to in writing,
12+
# software distributed under the License is distributed on an
13+
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
# KIND, either express or implied. See the License for the
15+
# specific language governing permissions and limitations
16+
# under the License.
17+
18+
import unittest
19+
import mock
20+
from configure import CsForwardingRules
21+
22+
23+
class TestCsForwardingRules(unittest.TestCase):
24+
25+
def ftp_helper_rules(self, protocol, public_ports, internal_ports):
26+
forwarding = CsForwardingRules.__new__(CsForwardingRules)
27+
forwarding.fw = []
28+
forwarding.forward_ftp_helper({"public_ip": "10.0.0.2", "internal_ip": "10.1.1.10", "protocol": protocol,
29+
"public_ports": public_ports, "internal_ports": internal_ports})
30+
return forwarding.fw
31+
32+
def test_ftp_helper_on_other_public_port(self):
33+
self.assertEqual(self.ftp_helper_rules("tcp", "2121:2121", "21:21"),
34+
[["raw", "", "-A PREROUTING -d 10.0.0.2/32 -p tcp -m tcp --dport 2121 -j CT --helper ftp"]])
35+
36+
def test_ftp_helper_on_port_range(self):
37+
self.assertEqual(self.ftp_helper_rules("tcp", "1020:1030", "20:30"),
38+
[["raw", "", "-A PREROUTING -d 10.0.0.2/32 -p tcp -m tcp --dport 1021 -j CT --helper ftp"]])
39+
40+
def test_ftp_helper_on_port_21(self):
41+
self.assertEqual(self.ftp_helper_rules("tcp", "21:21", "21:21"),
42+
[["raw", "", "-A PREROUTING -d 10.0.0.2/32 -p tcp -m tcp --dport 21 -j CT --helper ftp"]])
43+
44+
def test_no_ftp_helper(self):
45+
self.assertEqual(self.ftp_helper_rules("tcp", "2121:2121", "2121:2121"), [])
46+
self.assertEqual(self.ftp_helper_rules("udp", "2121:2121", "21:21"), [])
47+
self.assertEqual(self.ftp_helper_rules("tcp", "any", "any"), [])
48+
49+
@mock.patch.object(CsForwardingRules, 'getStaticRoutes', return_value=[])
50+
@mock.patch.object(CsForwardingRules, 'getPrivateGatewayNetworks', return_value=[])
51+
@mock.patch.object(CsForwardingRules, 'getGuestIpByIp', return_value="10.1.1.1")
52+
@mock.patch.object(CsForwardingRules, 'getNetworkByIp', return_value="10.1.1.0/24")
53+
@mock.patch.object(CsForwardingRules, 'getDeviceByIp', return_value="eth2")
54+
def test_ftp_helper_on_static_nat(self, *_):
55+
forwarding = CsForwardingRules.__new__(CsForwardingRules)
56+
forwarding.fw = []
57+
forwarding.processStaticNatRule({"public_ip": "10.0.0.2", "internal_ip": "10.1.1.10"})
58+
self.assertIn(["raw", "", "-A PREROUTING -d 10.0.0.2/32 -p tcp -m tcp --dport 21 -j CT --helper ftp"],
59+
forwarding.fw)
60+
61+
62+
if __name__ == '__main__':
63+
unittest.main()

0 commit comments

Comments
 (0)