Remove AccountURLChecker, the code that relies on it, associated tests, and docs.

This commit is contained in:
Jason R. Coombs 2021-04-09 13:43:43 -04:00
parent 19a3481e5f
commit 1b46f981c6
5 changed files with 6 additions and 92 deletions

View File

@ -78,33 +78,6 @@ start with "ssh-".
Now add an ``accounts.file`` directive to your ``tahoe.cfg`` file, as described in
the next sections.
Running An Account Server (accounts.url)
========================================
The accounts.url directive allows access requests to be controlled by an
HTTP-based login service, useful for centralized deployments. This was used
by AllMyData to provide web-based file access, where the service used a
simple PHP script and database lookups to map an account email address and
password to a Tahoe-LAFS directory cap. The service will receive a
multipart/form-data POST, just like one created with a <form> and <input>
fields, with three parameters:
• action: "authenticate" (this is a static string)
• email: USERNAME (Tahoe-LAFS has no notion of email addresses, but the
authentication service uses them as account names, so the interface
presents this argument as "email" rather than "username").
• passwd: PASSWORD
It should return a single string that either contains a Tahoe-LAFS directory
cap (URI:DIR2:...), or "0" to indicate a login failure.
Tahoe-LAFS recommends the service be secure, preferably localhost-only. This
makes it harder for attackers to brute force the password or use DNS
poisoning to cause the Tahoe-LAFS gateway to talk with the wrong server,
thereby revealing the usernames and passwords.
Public key authentication is not supported when an account server is used.
Configuring SFTP Access
=======================

View File

@ -0,0 +1 @@
Removed support for the Account Server frontend authentication type.

View File

@ -116,7 +116,6 @@ _client_config = configutil.ValidConfiguration(
),
"sftpd": (
"accounts.file",
"accounts.url",
"enabled",
"host_privkey_file",
"host_pubkey_file",
@ -1042,13 +1041,12 @@ class _Client(node.Node, pollmixin.PollMixin):
accountfile = self.config.get_config("sftpd", "accounts.file", None)
if accountfile:
accountfile = self.config.get_config_path(accountfile)
accounturl = self.config.get_config("sftpd", "accounts.url", None)
sftp_portstr = self.config.get_config("sftpd", "port", "tcp:8022")
pubkey_file = self.config.get_config("sftpd", "host_pubkey_file")
privkey_file = self.config.get_config("sftpd", "host_privkey_file")
from allmydata.frontends import sftpd
s = sftpd.SFTPServer(self, accountfile, accounturl,
s = sftpd.SFTPServer(self, accountfile,
sftp_portstr, pubkey_file, privkey_file)
s.setServiceParent(self)

View File

@ -1,14 +1,10 @@
import os
from zope.interface import implementer
from twisted.web.client import getPage
from twisted.internet import defer
from twisted.cred import error, checkers, credentials
from twisted.conch.ssh import keys
from twisted.conch.checkers import SSHPublicKeyChecker, InMemorySSHKeyDB
from allmydata.util.dictutil import BytesKeyDict
from allmydata.util import base32
from allmydata.util.fileutil import abspath_expanduser_unicode
@ -86,54 +82,3 @@ class AccountFileChecker(object):
d = defer.maybeDeferred(creds.checkPassword, correct)
d.addCallback(self._cbPasswordMatch, str(creds.username))
return d
@implementer(checkers.ICredentialsChecker)
class AccountURLChecker(object):
credentialInterfaces = (credentials.IUsernamePassword,)
def __init__(self, client, auth_url):
self.client = client
self.auth_url = auth_url
def _cbPasswordMatch(self, rootcap, username):
return FTPAvatarID(username, rootcap)
def post_form(self, username, password):
sepbase = base32.b2a(os.urandom(4))
sep = "--" + sepbase
form = []
form.append(sep)
fields = {"action": "authenticate",
"email": username,
"passwd": password,
}
for name, value in fields.iteritems():
form.append('Content-Disposition: form-data; name="%s"' % name)
form.append('')
assert isinstance(value, str)
form.append(value)
form.append(sep)
form[-1] += "--"
body = "\r\n".join(form) + "\r\n"
headers = {"content-type": "multipart/form-data; boundary=%s" % sepbase,
}
return getPage(self.auth_url, method="POST",
postdata=body, headers=headers,
followRedirect=True, timeout=30)
def _parse_response(self, res):
rootcap = res.strip()
if rootcap == "0":
raise error.UnauthorizedLogin
return rootcap
def requestAvatarId(self, credentials):
# construct a POST to the login form. While this could theoretically
# be done with something like the stdlib 'email' package, I can't
# figure out how, so we just slam together a form manually.
d = self.post_form(credentials.username, credentials.password)
d.addCallback(self._parse_response)
d.addCallback(self._cbPasswordMatch, str(credentials.username))
return d

View File

@ -1983,7 +1983,7 @@ class ShellSession(PrefixingLogMixin):
components.registerAdapter(ShellSession, SFTPUserHandler, ISession)
from allmydata.frontends.auth import AccountURLChecker, AccountFileChecker, NeedRootcapLookupScheme
from allmydata.frontends.auth import AccountFileChecker, NeedRootcapLookupScheme
@implementer(portal.IRealm)
class Dispatcher(object):
@ -2000,7 +2000,7 @@ class Dispatcher(object):
class SFTPServer(service.MultiService):
name = "frontend:sftp"
def __init__(self, client, accountfile, accounturl,
def __init__(self, client, accountfile,
sftp_portstr, pubkey_file, privkey_file):
precondition(isinstance(accountfile, (str, type(None))), accountfile)
precondition(isinstance(pubkey_file, str), pubkey_file)
@ -2013,12 +2013,9 @@ class SFTPServer(service.MultiService):
if accountfile:
c = AccountFileChecker(self, accountfile)
p.registerChecker(c)
if accounturl:
c = AccountURLChecker(self, accounturl)
p.registerChecker(c)
if not accountfile and not accounturl:
if not accountfile:
# we could leave this anonymous, with just the /uri/CAP form
raise NeedRootcapLookupScheme("must provide an account file or URL")
raise NeedRootcapLookupScheme("must provide an account file")
pubkey = keys.Key.fromFile(pubkey_file.encode(get_filesystem_encoding()))
privkey = keys.Key.fromFile(privkey_file.encode(get_filesystem_encoding()))