pyftpd-sink 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113
  1. #!/usr/bin/env python3
  2. import argparse
  3. import hashlib
  4. import os
  5. import pyftpdlib.authorizers
  6. import pyftpdlib.handlers
  7. import pyftpdlib.servers
  8. class SHA256Authorizer(pyftpdlib.authorizers.DummyAuthorizer):
  9. def validate_authentication(self, username, password, handler):
  10. # pyftpdlib/authorizers.py", line 110, in add_user
  11. # dic = {'pwd': str(password),
  12. # so we can not compare against .digest()
  13. password_hash = hashlib.sha256(password.encode()).hexdigest()
  14. if self.user_table[username]['pwd'] != password_hash.lower():
  15. raise pyftpdlib.authorizers.AuthenticationFailed()
  16. def serve(root_dir_path, username, password_sha256_hexdigest, control_port, passive_port):
  17. assert os.path.isdir(root_dir_path), root_dir_path
  18. authorizer = SHA256Authorizer()
  19. authorizer.add_user(
  20. username,
  21. password_sha256_hexdigest.lower(),
  22. homedir=root_dir_path,
  23. # https://pyftpdlib.readthedocs.io/en/latest/api.html#pyftpdlib.authorizers.DummyAuthorizer.add_user
  24. # e: change dir
  25. # m: mkdir
  26. # w: write
  27. perm='emw',
  28. msg_login='renshi ni hen gaoxing',
  29. msg_quit='zaijian',
  30. )
  31. handler = pyftpdlib.handlers.FTPHandler
  32. handler.authorizer = authorizer
  33. handler.banner = 'ni hao'
  34. # handler.masquerade_address = '192.168.1.1'
  35. handler.passive_ports = (passive_port,)
  36. server = pyftpdlib.servers.FTPServer((None, control_port), handler)
  37. # apparently requires +1 for unknown reasons
  38. server.max_cons = 1 + 1
  39. server.serve_forever()
  40. class EnvDefaultArgparser(argparse.ArgumentParser):
  41. def add_argument(self, *args, envvar=None, **kwargs):
  42. if envvar:
  43. envvar_value = os.environ.get(envvar, None)
  44. if envvar_value:
  45. kwargs['required'] = False
  46. kwargs['default'] = envvar_value
  47. super().add_argument(*args, **kwargs)
  48. def _init_argparser():
  49. argparser = EnvDefaultArgparser()
  50. argparser.add_argument(
  51. '--root', '--root-dir',
  52. metavar='path',
  53. dest='root_dir_path',
  54. default=os.getcwd(),
  55. help='default: current working directory',
  56. )
  57. argparser.add_argument(
  58. '--user', '--username',
  59. metavar='username',
  60. dest='username',
  61. required=True,
  62. envvar='FTP_USERNAME',
  63. help='default: env var $FTP_USERNAME',
  64. )
  65. argparser.add_argument(
  66. '--pwd-hash', '--password-hash',
  67. metavar='sha256_hexdigest',
  68. dest='password_sha256_hexdigest',
  69. required=True,
  70. envvar='FTP_PASSWORD_SHA256',
  71. help='default: env var $FTP_PASSWORD_SHA256',
  72. )
  73. argparser.add_argument(
  74. '--ctrl-port', '--control-port',
  75. metavar='port',
  76. type=int,
  77. dest='control_port',
  78. envvar='FTP_CONTROL_PORT',
  79. default=2121,
  80. help='default: env var $FTP_CONTROL_PORT or 2121',
  81. )
  82. argparser.add_argument(
  83. '--pasv-port', '--passive-port',
  84. metavar='port',
  85. type=int,
  86. dest='passive_port',
  87. envvar='FTP_PASSIVE_PORT',
  88. default=62121,
  89. help='default: env var $FTP_PASSIVE_PORT or 62121',
  90. )
  91. return argparser
  92. def main(argv):
  93. argparser = _init_argparser()
  94. args = argparser.parse_args(argv[1:])
  95. serve(**vars(args))
  96. return 0
  97. if __name__ == "__main__":
  98. import sys
  99. sys.exit(main(sys.argv))