Skip to content

fix passive ftp through the virtual router on newer system vm templates - #14330

Open
Damans227 wants to merge 3 commits into
apache:mainfrom
Damans227:nl/issue-14319
Open

Damans227 wants to merge 3 commits into
apache:mainfrom
Damans227:nl/issue-14319

Conversation

@Damans227

@Damans227 Damans227 commented Oct 7, 2026 •

Copy link
Copy Markdown
Collaborator

Fixes #14319

Passive FTP to guest VMs behind the virtual router stopped working on the Debian 12 system VM template. The router turned on the FTP helper with a kernel setting that Linux 6.0 removed, so the helper never ran. The server's private IP was sent to the client and the data connection was dropped. This adds a rule that attaches the FTP helper to port 21 traffic, for both isolated network and VPC routers, and loads the FTP helper module at boot instead of writing to the removed setting. The rule is part of the router's normal firewall config, so it stays in place when rules are reapplied.

Test:

On a KVM lab with the 4.22.0 system VM template (kernel 6.1): create an isolated network, deploy a VM running vsftpd with anonymous login, get a public IP, enable static NAT to the VM and allow only TCP port 21 in the firewall. From outside the network, list a folder with passive mode, once using the IP from the 227 reply and once using the server's address:

curl -v --disable-epsv --no-ftp-skip-pasv-ip --list-only ftp://anonymous:x@10.0.52.84:21/pub/
curl -v --disable-epsv --ftp-skip-pasv-ip --list-only ftp://anonymous:x@10.0.52.84:21/pub/

Before: router recreated with the scripts from main. The 227 reply has the VM's private IP and both listings time out.

# iptables -t raw -S
-P PREROUTING ACCEPT
-P OUTPUT ACCEPT

--no-ftp-skip-pasv-ip:
< 227 Entering Passive Mode (10,1,1,158,36,234)
* Connecting to 10.1.1.158 (10.1.1.158) port 9450
curl: (28) Connection time-out

--ftp-skip-pasv-ip:
< 227 Entering Passive Mode (10,1,1,158,69,214)
* Connecting to 10.0.52.84 (10.0.52.84) port 17878
curl: (28) Connection time-out

After: router recreated with this change. The 227 reply has the public IP and both listings come back. The rule is still there after adding another firewall rule.

# iptables -t raw -S
-P PREROUTING ACCEPT
-P OUTPUT ACCEPT
-A PREROUTING -p tcp -m tcp --dport 21 -j CT --helper ftp

--no-ftp-skip-pasv-ip:
< 227 Entering Passive Mode (10,0,52,84,179,43)
* Connecting to 10.0.52.84 (10.0.52.84) port 45867
< 150 Here comes the directory listing.
< 226 Directory send OK.
hello.txt

--ftp-skip-pasv-ip:
< 227 Entering Passive Mode (10,0,52,84,180,212)
* Connecting to 10.0.52.84 (10.0.52.84) port 46292
< 150 Here comes the directory listing.
< 226 Directory send OK.
hello.txt

@Damans227

Copy link
Copy Markdown
Collaborator Author

@blueorangutan package

@blueorangutan

Copy link
Copy Markdown

@Damans227 a [SL] Jenkins job has been kicked to build packages. It will be bundled with no SystemVM templates. I'll keep you posted as I make progress.

@codecov

codecov Bot commented Oct 7, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 19.91%. Comparing base (7ed0cec) to head (47e05d4).
⚠️ Report is 2 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff            @@
##               main   #14330   +/-   ##
=========================================
  Coverage     19.91%   19.91%           
- Complexity    20198    20202    +4     
=========================================
  Files          6373     6373           
  Lines        577230   577230           
  Branches      70696    70696           
=========================================
+ Hits         114936   114965   +29     
+ Misses       449736   449697   -39     
- Partials      12558    12568   +10     
Flag Coverage Δ
uitests 3.71% <ø> (ø)
unittests 21.18% <ø> (+<0.01%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@blueorangutan

Copy link
Copy Markdown

Packaging result [SF]: ✔️ el8 ✔️ el9 ✔️ el10 ✔️ debian ✔️ suse15. SL-JID 19439

@Damans227
Damans227 marked this pull request as ready for review October 7, 2026 15:32
"-A POSTROUTING " +
"-p udp -m udp --dport 68 -j CHECKSUM --checksum-fill"])

self.fw.append(["raw", "", "-A PREROUTING -p tcp -m tcp --dport 21 -j CT --helper ftp"])

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what if the public port is not 21 ?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good point, a port forward like 2121 to the vm's port 21 missed it. those now get the helper on their public port too, in 949c903

"-A POSTROUTING " +
"-p udp -m udp --dport 68 -j CHECKSUM --checksum-fill"])

self.fw.append(["raw", "", "-A PREROUTING -p tcp -m tcp --dport 21 -j CT --helper ftp"])

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can this rule only apply on associated public ips for static nat ?

the rule for port forwarding looks good

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done, the port 21 rule now only goes on static nat public ips, and port forwards cover public port 21 themselves. in 47e05d4

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looks good now
@Damans227 can you run some tests and share the results ?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tested 47e05d4 on kvm with the 4.22.0 systemvm template. vsftpd vm with default passive settings, curl in passive mode from outside the zone, each router rebuilt with a clean restart. all six list the folder and the passive reply carries the public ip.

  • isolated, static nat, port 21: pass
  • isolated, port forward 21 to 21: pass
  • isolated, port forward 2121 to 21: pass
  • vpc tier, static nat, port 21: pass
  • vpc tier, port forward 21 to 21: pass
  • vpc tier, port forward 2121 to 21: pass

raw table on the isolated router (.185 static nat, .186 forward 21, .187 forward 2121). the source nat ip gets no rule and there is no catch-all port 21 rule:

-A PREROUTING -d 10.0.56.185/32 -p tcp -m tcp --dport 21 -j CT --helper ftp
-A PREROUTING -d 10.0.56.186/32 -p tcp -m tcp --dport 21 -j CT --helper ftp
-A PREROUTING -d 10.0.56.187/32 -p tcp -m tcp --dport 2121 -j CT --helper ftp

vpc router, same layout:

-A PREROUTING -d 10.0.56.188/32 -p tcp -m tcp --dport 21 -j CT --helper ftp
-A PREROUTING -d 10.0.56.189/32 -p tcp -m tcp --dport 21 -j CT --helper ftp
-A PREROUTING -d 10.0.56.190/32 -p tcp -m tcp --dport 2121 -j CT --helper ftp

the 2121 case replied with 227 Entering Passive Mode (10,0,56,187,41,171).

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Virtual Router: passive FTP to guest VMs is broken on Debian 12 based systemVM templates (FTP conntrack helper is never attached)

3 participants