Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Uploads randomly truncated with TLS 1.3 #602

Open
asantoni opened this issue May 29, 2023 · 0 comments
Open

Uploads randomly truncated with TLS 1.3 #602

asantoni opened this issue May 29, 2023 · 0 comments

Comments

@asantoni
Copy link

I discovered a bug using rclone to sync some data over to a pyftpdlib-based application. Essentially, if TLS 1.3 is used, most uploads will randomly get truncated near the end.

For example, if we try uploading a file that is 29633874 bytes large, we can see it getting truncated sometimes:

$ rclone -P -vv --fast-list --ftp-disable-tls13 copy /usr/share/xyz/webroot/project/A.bin Project:/project
2023/05/29 19:24:31 DEBUG : 2 go routines active
2023/05/29 19:24:31 DEBUG : ftp://host:2121/project: closing 1 unused connections
root@lb-jh3py:~# rclone -P -vv --fast-list copy /usr/share/xyz/webroot/project/A.bin Project:"/project"
2023/05/29 19:25:21 DEBUG : rclone: Version "v1.62.2" starting with parameters ["rclone" "-P" "-vv" "--fast-list" "copy" "/usr/share/xyz/webroot/project/A.bin" "Project:/project"]
2023/05/29 19:25:21 DEBUG : Creating backend with remote "/usr/share/xyz/webroot/project/A.bin"
2023/05/29 19:25:21 DEBUG : Using config file from "/home/user/.config/rclone/rclone.conf"
2023/05/29 19:25:21 DEBUG : fs cache: adding new entry for parent of "/usr/share/xyz/webroot/project/A.bin", "/usr/share/xyz/webroot/project"
2023/05/29 19:25:21 DEBUG : Creating backend with remote "Projectproject:/project"
2023/05/29 19:25:21 DEBUG : ftp://host:2121/project: Connecting to FTP server
2023/05/29 19:25:21 DEBUG : ftp://host:2121/project: dial("tcp","host:2121")
2023/05/29 19:25:21 DEBUG : ftp://host:2121/project: > dial: conn=*fshttp.timeoutConn, err=<nil>
2023-05-29 19:25:22 DEBUG : A.bin: Need to transfer - File not found at Destination
2023-05-29 19:25:22 DEBUG : ftp://host:2121/project: dial("tcp","127.0.0.1:58205")
2023-05-29 19:25:22 DEBUG : ftp://host:2121/project: > dial: conn=*tls.Conn, err=<nil>
2023-05-29 19:25:23 ERROR : A.bin: corrupted on transfer: sizes differ 29633874 vs 29437952
2023-05-29 19:25:23 INFO  : A.bin: Removing failed copy
2023-05-29 19:25:23 ERROR : Attempt 1/3 failed with 1 errors and: corrupted on transfer: sizes differ 29633874 vs 29437952
2023-05-29 19:25:23 DEBUG : A.bin: Need to transfer - File not found at Destination
2023-05-29 19:25:24 DEBUG : ftp://host:2121/project: dial("tcp","127.0.0.1:58749")
2023-05-29 19:25:24 DEBUG : ftp://host:2121/project: > dial: conn=*tls.Conn, err=<nil>
2023-05-29 19:25:25 ERROR : A.bin: corrupted on transfer: sizes differ 29633874 vs 28487680
2023-05-29 19:25:25 INFO  : A.bin: Removing failed copy
2023-05-29 19:25:25 ERROR : Attempt 2/3 failed with 1 errors and: corrupted on transfer: sizes differ 29633874 vs 28487680
2023-05-29 19:25:25 DEBUG : A.bin: Need to transfer - File not found at Destination
2023-05-29 19:25:25 DEBUG : ftp://host:2121/project: dial("tcp","127.0.0.1:58468")
2023-05-29 19:25:25 DEBUG : ftp://host:2121/project: > dial: conn=*tls.Conn, err=<nil>
2023-05-29 19:25:27 ERROR : A.bin: corrupted on transfer: sizes differ 29633874 vs 29224960
2023-05-29 19:25:27 INFO  : A.bin: Removing failed copy
2023-05-29 19:25:27 ERROR : Attempt 3/3 failed with 1 errors and: corrupted on transfer: sizes differ 29633874 vs 29224960
Transferred:       84.783 MiB / 84.783 MiB, 100%, 16.957 MiB/s, ETA 0s
Errors:                 1 (retrying may help)
Elapsed time:         6.1s

The upload was truncated at 29224960 bytes (compared with original filesize of 29633874).

In the FTPS server with debug logging, we see:

May 29 19:25:26 server manage.py[3866644]: DEBUG:pyftpdlib:[debug] call: recv(), err: ssl-want-read (<TLS_DTPHandler(id=140092900063744, addr='127.0.0.1:59684', ssl=True, user='ftpuser', ssl-data=True, sending-file=<tempfile._TemporaryFileWrapper object at 0x7f69eb8b8940>, bytes-trans=24539136)>)
May 29 19:25:26 server manage.py[3866644]: DEBUG:pyftpdlib:[debug] call: recv(), err: ssl-want-read (<TLS_DTPHandler(id=140092900063744, addr='127.0.0.1:59684', ssl=True, user='ftpuser', ssl-data=True, sending-file=<tempfile._TemporaryFileWrapper object at 0x7f69eb8b8940>, bytes-trans=24866816)>)
May 29 19:25:26 server manage.py[3866644]: DEBUG:pyftpdlib:[debug] call: recv(), err: ssl-want-read (<TLS_DTPHandler(id=140092900063744, addr='127.0.0.1:59684', ssl=True, user='ftpuser', ssl-data=True, sending-file=<tempfile._TemporaryFileWrapper object at 0x7f69eb8b8940>, bytes-trans=27439104)>)
May 29 19:25:26 server manage.py[3866644]: DEBUG:pyftpdlib:[debug] call: recv(), err: ssl-want-read (<TLS_DTPHandler(id=140092900063744, addr='127.0.0.1:59684', ssl=True, user='ftpuser', ssl-data=True, sending-file=<tempfile._TemporaryFileWrapper object at 0x7f69eb8b8940>, bytes-trans=27504640)>)
May 29 19:25:26 server manage.py[3866644]: DEBUG:pyftpdlib:[debug] call: recv(), err: SysCallError(104, 'ECONNRESET') (<TLS_DTPHandler(id=140092900063744, addr='127.0.0.1:59684', ssl=True, user='ftpuser', ssl-data=True, sending-file=<tempfile._TemporaryFileWrapper object at 0x7f69eb8b8940>, bytes-trans=29224960)>)
May 29 19:25:26 server manage.py[3866644]: DEBUG:pyftpdlib:[debug] call: _do_ssl_shutdown() -> os.write, err: BrokenPipeError(32, 'Broken pipe') (<TLS_DTPHandler(id=140092900063744, addr='127.0.0.1:59684', ssl=True, user='ftpuser', ssl-data=True, sending-file=<tempfile._TemporaryFileWrapper object at 0x7f69eb8b8940>, bytes-trans=29224960)>)
May 29 19:25:26 server manage.py[3866644]: DEBUG:pyftpdlib:[debug] call: close() (<TLS_DTPHandler(id=140092900063744, addr='127.0.0.1:59684', ssl=True, user='ftpuser', ssl-data=True, sending-file=<tempfile._TemporaryFileWrapper object at 0x7f69eb8b8940>, bytes-trans=29224960)>)
May 29 19:25:26 server manage.py[3866644]: DEBUG:pyftpdlib:127.0.0.1:59684-[ftpuser] -> 226 Transfer complete.
May 29 19:25:26 server manage.py[3866644]: INFO:pyftpdlib:127.0.0.1:59684-[ftpuser] STOR /tmp/tmp8pdhfjfd completed=1 bytes=29224960 seconds=1.058

The FTP handler gets a ECONNRESET for some reason instead of a proper TLS 1.3 shutdown.

If we re-run the test and disable TLS 1.3 in rclone using the --ftp-disable-tls13 flag, the upload succeeds perfectly every time:

$ rclone -P -vv --fast-list --ftp-disable-tls13 copy /usr/share/xyz/webroot/project/A.bin Project:/project

Versions:

  • The rclone version tested is 1.62.2.
  • pyftdlib is version 1.5.7.
  • The OpenSSL version on the client (rclone) is unclear because it's a statically linked Go binary. (Not even sure if Go uses OpenSSL or if they use BoringSSL.)
  • OpenSSL on the server (pyftpdlib) is 1.1.1f-1ubuntu2.18.

Root Cause Speculation

It's quite possible there's an underlying bug here in pyopenssl or even OpenSSL:
openssl/openssl#10880

There's a closed bug in proftpd that looks the same as my debug log above:
proftpd/proftpd#959
And their workaround is here: proftpd/proftpd@6d6cde2

Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant