First passing end-to-end test of SFTP

This commit is contained in:
Itamar Turner-Trauring 2021-01-07 12:50:31 -05:00
parent 3b29a5f707
commit a536a1a970
3 changed files with 71 additions and 4 deletions

View File

@ -8,6 +8,7 @@ from os.path import join, exists
from tempfile import mkdtemp, mktemp from tempfile import mkdtemp, mktemp
from functools import partial from functools import partial
from json import loads from json import loads
from subprocess import check_call
from foolscap.furl import ( from foolscap.furl import (
decode_furl, decode_furl,
@ -342,6 +343,12 @@ def storage_nodes(reactor, temp_dir, introducer, introducer_furl, flog_gatherer,
return nodes return nodes
def generate_ssh_key(path):
"""Create a new SSH private/public key pair."""
check_call(["ckeygen", "--type", "rsa", "--no-passphrase", "--bits", "512",
"--file", path])
@pytest.fixture(scope='session') @pytest.fixture(scope='session')
@log_call(action_type=u"integration:alice", include_args=[], include_result=False) @log_call(action_type=u"integration:alice", include_args=[], include_result=False)
def alice(reactor, temp_dir, introducer_furl, flog_gatherer, storage_nodes, request): def alice(reactor, temp_dir, introducer_furl, flog_gatherer, storage_nodes, request):
@ -353,14 +360,36 @@ def alice(reactor, temp_dir, introducer_furl, flog_gatherer, storage_nodes, requ
) )
) )
await_client_ready(process) await_client_ready(process)
# 1. Create a new RW directory cap:
cli(process, "create-alias", "test") cli(process, "create-alias", "test")
rwcap = loads(cli(process, "list-aliases", "--json"))["test"]["readwrite"] rwcap = loads(cli(process, "list-aliases", "--json"))["test"]["readwrite"]
# TODO at this point we need to:
# 1. configure sftpd # 2. Enable SFTP on the node:
# 2. add an sftp access file with username, password, and rwcap ssh_key_path = join(process.node_dir, "private", "ssh_host_rsa_key")
# 3. eventually, add sftp access with public key accounts_path = join(process.node_dir, "private", "accounts")
with open(join(process.node_dir, "tahoe.cfg"), "a") as f:
f.write("""\
[sftpd]
enabled = true
port = tcp:8022:interface=127.0.0.1
host_pubkey_file = {ssh_key_path}.pub
host_privkey_file = {ssh_key_path}
accounts.file = {accounts_path}
""".format(ssh_key_path=ssh_key_path, accounts_path=accounts_path))
generate_ssh_key(ssh_key_path)
# 3. Add a SFTP access file with username, password, and rwcap.
with open(accounts_path, "w") as f:
f.write("""\
alice password {}
""".format(rwcap))
# TODO add sftp access with public key
# 4. Restart the node with new SFTP config.
process.kill() process.kill()
pytest_twisted.blockon(_run_node(reactor, process.node_dir, request, None)) pytest_twisted.blockon(_run_node(reactor, process.node_dir, request, None))
await_client_ready(process) await_client_ready(process)
return process return process

37
integration/test_sftp.py Normal file
View File

@ -0,0 +1,37 @@
"""
It's possible to create/rename/delete files and directories in Tahoe-LAFS using
SFTP.
"""
from __future__ import unicode_literals
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
from future.utils import PY2
if PY2:
from future.builtins import filter, map, zip, ascii, chr, hex, input, next, oct, open, pow, round, super, bytes, dict, list, object, range, str, max, min # noqa: F401
from paramiko import SSHClient
from paramiko.client import AutoAddPolicy
from paramiko.sftp_client import SFTPClient
def test_read_write_files(alice):
"""It's possible to upload and download files."""
client = SSHClient()
client.set_missing_host_key_policy(AutoAddPolicy)
client.connect(
"localhost", username="alice", password="password", port=8022,
look_for_keys=False
)
sftp = SFTPClient.from_transport(client.get_transport())
f = sftp.file("myfile", "wb")
f.write(b"abc")
f.write(b"def")
f.close()
f = sftp.file("myfile", "rb")
assert f.read(4) == b"abcd"
assert f.read(2) == b"ef"
assert f.read(1) == b""
f.close()

View File

@ -399,6 +399,7 @@ setup(name="tahoe-lafs", # also set in __init__.py
"html5lib", "html5lib",
"junitxml", "junitxml",
"tenacity", "tenacity",
"paramiko",
] + tor_requires + i2p_requires, ] + tor_requires + i2p_requires,
"tor": tor_requires, "tor": tor_requires,
"i2p": i2p_requires, "i2p": i2p_requires,