{"id":888,"date":"2014-06-25T06:10:20","date_gmt":"2014-06-25T06:10:20","guid":{"rendered":"http:\/\/poojanwagh.opalstacked.com\/techblog\/?p=888"},"modified":"2014-06-25T06:28:06","modified_gmt":"2014-06-25T06:28:06","slug":"making-duplicity-and-ncftpput-play-nice-with-vsftpd-ftp-daemon","status":"publish","type":"post","link":"https:\/\/tech.poojanblog.com\/blog\/unix-linux\/making-duplicity-and-ncftpput-play-nice-with-vsftpd-ftp-daemon\/","title":{"rendered":"Making duplicity and ncftpput play nice with vsftpd FTP daemon"},"content":{"rendered":"<p>I&#8217;ve been setting up yet another remote backup lately (<a title=\"Keeping FreeBSD TCP performance in the midst of a highly-buffered connection\" href=\"http:\/\/poojanwagh.opalstacked.com\/techblog\/unix-linux\/keeping-freebsd-tcp-performance-in-the-midst-of-a-highly-buffered-connection\/\">see associated problem here<\/a>). For this purpose (on Unix), the <a title=\"Duplicity backup project\" href=\"http:\/\/duplicity.nongnu.org\/\" target=\"_blank\">duplicity<\/a> solution looks ideal. However, I&#8217;ve tried it on a couple of lightweight FTP servers (a TL-WDR3600 and a Raspberry Pi) and neither of them work. I keep getting a &#8220;Permission Denied&#8221; message.<\/p>\n<p>I did quite a bit of investigation, and found that it&#8217;s related to the way that duplicity&#8217;s FTP backend, <a title=\"ncftp client\" href=\"http:\/\/www.ncftp.com\/ncftp\/\" target=\"_blank\">ncftp\/ncftpput<\/a> sends files. Or really, a quirk of vsftpd, where it won&#8217;t let you put a file with an absolute path (and more specifically a file with a directory name). I&#8217;ve seen other reports of this on the Internet <a title=\"RT-N56U bug report on broken vsftpd\" href=\"https:\/\/code.google.com\/p\/rt-n56u\/issues\/detail?id=293\" target=\"_blank\">here<\/a> and <a title=\"Duplicity bug report on broken FTP\" href=\"https:\/\/bugs.launchpad.net\/duplicity\/+bug\/486241\" target=\"_blank\">here<\/a>.<\/p>\n<p>Here&#8217;s a sample ncftpput debug log:<\/p>\n<p><code>2014-06-25 00:56:34\u00a0 331: Please specify the password.<br \/>\n2014-06-25 00:56:34\u00a0 Cmd: PASS xxxxxxxx<br \/>\n2014-06-25 00:56:34\u00a0 230: Login successful.<br \/>\n2014-06-25 00:56:34\u00a0 Cmd: PWD<br \/>\n2014-06-25 00:56:34\u00a0 257: \"\/\"<br \/>\n2014-06-25 00:56:34\u00a0 Logged in to 192.168.1.2 as Poojan.<br \/>\n2014-06-25 00:56:34\u00a0 Cmd: FEAT<br \/>\n2014-06-25 00:56:34\u00a0 211: Features:<br \/>\n2014-06-25 00:56:34\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 EPRT<br \/>\n2014-06-25 00:56:34\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 EPSV<br \/>\n2014-06-25 00:56:34\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 MDTM<br \/>\n2014-06-25 00:56:34\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 PASV<br \/>\n2014-06-25 00:56:34\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 REST STREAM<br \/>\n2014-06-25 00:56:34\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 SIZE<br \/>\n2014-06-25 00:56:34\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 TVFS<br \/>\n2014-06-25 00:56:34\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 UTF8<br \/>\n2014-06-25 00:56:34\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 End<br \/>\n2014-06-25 00:56:34\u00a0 Cmd: PWD<br \/>\n2014-06-25 00:56:34\u00a0 257: \"\/\"<br \/>\n2014-06-25 00:56:34\u00a0 Cmd: CWD folder1\/duplicity\/Public<br \/>\n2014-06-25 00:56:34\u00a0 250: Directory successfully changed.<br \/>\n2014-06-25 00:56:34\u00a0 Cmd: CWD \/<br \/>\n2014-06-25 00:56:34\u00a0 250: Directory successfully changed.<br \/>\n2014-06-25 00:56:34\u00a0 Cmd: TYPE I<br \/>\n2014-06-25 00:56:34\u00a0 200: Switching to Binary mode.<br \/>\n2014-06-25 00:56:34\u00a0 Cmd: PASV<br \/>\n2014-06-25 00:56:34\u00a0 227: Entering Passive Mode (192,168,1,2,225,89).<br \/>\n2014-06-25 00:56:34\u00a0 Cmd: STOR folder1\/duplicity\/Public\/duplicity-full.20140625T055614Z.vol1.difftar.gpg<br \/>\n2014-06-25 00:56:34\u00a0 550: Permission denied.<br \/>\n2014-06-25 00:56:34\u00a0 ncftpput folder1\/duplicity\/Public\/duplicity-full.20140625T055614Z.vol1.difftar.gpg: server said: Permission denied.<br \/>\n2014-06-25 00:56:34\u00a0 Cmd: QUIT<br \/>\n2014-06-25 00:56:34\u00a0 221: Goodbye.<br \/>\n<\/code><\/p>\n<p>However, I ran a few tests, and I found that if one <code>cd<\/code>&#8216;es to the directory and then puts (<code>STOR<\/code>&#8216;es) the file, everything works. My guess this is a chroot-style feature. So, I created a small Python script that will parse the arguments that duplicity sends, and inserts cd (to the target directory) command before uploading the file. This works well. Here&#8217;s the script:<\/p>\n<p><code><br \/>\n#!\/usr\/local\/bin\/python3<br \/>\nimport argparse<br \/>\nimport subprocess<br \/>\nimport sys<br \/>\nimport os<\/p>\n<p># ncftpput looks something like this:<br \/>\n# 'ncftpput -f \/tmp\/duplicity-yRftm3-tempdir\/mkstemp-PiYFTL-1 -F -t 30 -o useCLNT=0,useHELP_SITE=0\u00a0 -m -V -C '\/tmp\/duplicity-yRftm3-tempdir\/mktemp-sWc1AA-3' 'folder1\/duplicity\/Public\/duplicity-full.20140625T041147Z.vol1.difftar.gz<br \/>\nif __name__ == '__main__':<br \/>\n    parser = argparse.ArgumentParser(description='wrapper for ncftpput command, to allow absolute target paths on vsftpd')<br \/>\n    parser.add_argument('-f', dest='credentials_file')<br \/>\n    parser.add_argument('-F', action='store_true')<br \/>\n    parser.add_argument('-t', dest='timeout')<br \/>\n    parser.add_argument('-o', dest='ftp_options')<br \/>\n    parser.add_argument('-m', action='store_true')<br \/>\n    parser.add_argument('-C', action='store_true')<br \/>\n    parser.add_argument('-V', action='store_true')<br \/>\n    parser.add_argument('source')<br \/>\n    parser.add_argument('dest')<br \/>\n    args = parser.parse_args()<\/p>\n<p>    # print(args)<\/p>\n<p>    dest_dir = os.path.dirname(args.dest)<br \/>\n    dest_file = os.path.basename(args.dest)<\/p>\n<p>    #cmd_args = ['\/usr\/local\/bin\/ncftpput', '-d', 'ftp-debug.log', '-W', 'cd {0}'.format(dest_dir)] +\u00a0 sys.argv[1:-1] + [dest_file]<br \/>\n    cmd_args = ['\/usr\/local\/bin\/ncftpput', '-W', 'cd {0}'.format(dest_dir)] +\u00a0 sys.argv[1:-1] + [dest_file]<br \/>\n    subprocess.Popen(cmd_args)<br \/>\n<\/code><\/p>\n<p>The script is called <code>ncftpput<\/code>, and is placed in a directory called ~\/duplicity_bin. I then add this directory to my path before running duplicity. Here&#8217;s a shell script that does that:<\/p>\n<p><code><br \/>\n#!\/bin\/sh<br \/>\nexport FTP_PASSWORD=\"XXXYYYXXYXYZY\"<br \/>\nexport PATH=\"\/home\/Poojan\/duplicity_bin:$PATH\"<br \/>\nduplicity -v 9 --encrypt-key=DEADBEEF full \/tank\/Users\/Public ftp:\/\/Poojan@192.168.1.2\/folder1\/duplicity\/Public<br \/>\n<\/code>\n<\/p>\n<div class='wp_likes' id='wp_likes_post-888'><a class='like' href=\"javascript:wp_likes.like(888);\" title='' ><img data-recalc-dims=\"1\" decoding=\"async\" src=\"https:\/\/i0.wp.com\/tech.poojanblog.com\/blog\/wp-content\/plugins\/wp-likes\/images\/like.png\" alt='' border='0'\/><\/a><span class='text'>Be the first to like.<\/span><\/p>\n<div class='like' ><a href=\"javascript:wp_likes.like(888);\">Like<\/a><\/div>\n<div class='unlike' ><a href=\"javascript:wp_likes.unlike(888);\">Unlike<\/a><\/div>\n<\/div>\n","protected":false},"excerpt":{"rendered":"<p>I&#8217;ve been setting up yet another remote backup lately (see associated problem here). For this purpose (on Unix), the duplicity solution looks ideal. However, I&#8217;ve tried it on a couple of lightweight FTP servers (a TL-WDR3600 and a Raspberry Pi) and neither of them work. I keep getting a &#8220;Permission Denied&#8221; message. I did quite [&hellip;]<\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_jetpack_memberships_contains_paid_content":false,"footnotes":""},"categories":[10],"tags":[16,216,217,219,218,46],"class_list":["post-888","post","type-post","status-publish","format-standard","hentry","category-unix-linux","tag-backup","tag-duplicity","tag-ftp","tag-ncftp","tag-ncftpput","tag-python"],"jetpack_featured_media_url":"","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/tech.poojanblog.com\/blog\/wp-json\/wp\/v2\/posts\/888","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/tech.poojanblog.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/tech.poojanblog.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/tech.poojanblog.com\/blog\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/tech.poojanblog.com\/blog\/wp-json\/wp\/v2\/comments?post=888"}],"version-history":[{"count":11,"href":"https:\/\/tech.poojanblog.com\/blog\/wp-json\/wp\/v2\/posts\/888\/revisions"}],"predecessor-version":[{"id":909,"href":"https:\/\/tech.poojanblog.com\/blog\/wp-json\/wp\/v2\/posts\/888\/revisions\/909"}],"wp:attachment":[{"href":"https:\/\/tech.poojanblog.com\/blog\/wp-json\/wp\/v2\/media?parent=888"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/tech.poojanblog.com\/blog\/wp-json\/wp\/v2\/categories?post=888"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/tech.poojanblog.com\/blog\/wp-json\/wp\/v2\/tags?post=888"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}