Exemple #1
0
 def __init__(self,
              address,
              host_key=None,
              authurl=None,
              max_children=20,
              keystone=None,
              no_scp=False,
              split_size=0,
              hide_part_dir=False,
              auth_timeout=None,
              negotiation_timeout=0):
     self.log = paramiko.util.get_logger("paramiko")
     self.log.debug("%s: start server" % self.__class__.__name__)
     self.fs = ObjectStorageFS(None,
                               None,
                               authurl=authurl,
                               keystone=keystone,
                               hide_part_dir=hide_part_dir)  # unauthorized
     self.host_key = host_key
     self.max_children = max_children
     self.no_scp = no_scp
     ObjectStorageSFTPRequestHandler.auth_timeout = auth_timeout
     ObjectStorageSFTPRequestHandler.negotiation_timeout = negotiation_timeout
     ForkingTCPServer.__init__(self, address,
                               ObjectStorageSFTPRequestHandler)
     ObjectStorageFD.split_size = split_size
Exemple #2
0
    def setUp(self):
        if not hasattr(self, 'username'):
            cls = self.__class__
            if not all(['OS_API_KEY' in os.environ,
                        'OS_API_USER' in os.environ,
                        'OS_AUTH_URL' in os.environ,
                        ]):
                print "env OS_API_USER/OS_API_KEY/OS_AUTH_URL not found."
                sys.exit(1)
            cls.username = os.environ.get('OS_API_USER')
            cls.api_key  = os.environ.get('OS_API_KEY')
            cls.auth_url = os.environ.get('OS_AUTH_URL')
            if 'OS_KEYSTONE_REGION_NAME' in os.environ:
                keystone = {'region_name'      : os.environ.get('OS_KEYSTONE_REGION_NAME'),
                            'tenant_separator' : os.environ.get('OS_KEYSTONE_TENANT_SEPARATOR', ':'),
                            'service_type'     : os.environ.get('OS_KEYSTONE_SERVICE_TYPE', 'object-store'),
                            'endpoint_type'    : os.environ.get('OS_KEYSTONE_ENDPOINT_TYPE', 'publicURL')}
                cls.cnx = ObjectStorageFS(self.username, self.api_key, self.auth_url, keystone)
                tenant_name, username = cls.username.split(keystone['tenant_separator'], 1)
                cls.conn = client.Connection(user=username, tenant_name=tenant_name,key=self.api_key, authurl=self.auth_url, auth_version='2.0')

            else:
                cls.cnx = ObjectStorageFS(self.username, self.api_key, self.auth_url)
                cls.conn = client.Connection(user=self.username, key=self.api_key, authurl=self.auth_url)
        self.container = "ftpcloudfs_testing"
        self.cnx.mkdir("/%s" % self.container)
        self.cnx.chdir("/%s" % self.container)
Exemple #3
0
 def __init__(self,
              address,
              host_key=None,
              authurl=None,
              max_children=20,
              keystone=None,
              no_scp=False,
              split_size=0,
              hide_part_dir=False,
              auth_timeout=None,
              negotiation_timeout=0,
              keepalive=0,
              insecure=False,
              secopts=None,
              server_ident=None,
              storage_policy=None,
              proxy_protocol=None,
              rsync_bin=None,
              large_object_container_suffix=None,
              fail2ban=None):
     self.log = paramiko.util.get_logger("paramiko")
     self.log.debug("%s: start server" % self.__class__.__name__)
     self.fs = ObjectStorageFS(
         None,
         None,
         authurl=authurl,
         keystone=keystone,
         hide_part_dir=hide_part_dir,
         insecure=insecure,
         storage_policy=storage_policy)  # unauthorized
     self.host_key = host_key
     self.max_children = max_children
     self.no_scp = no_scp
     self.rsync_bin = rsync_bin
     self.split_size = split_size
     self.fail2ban = fail2ban
     if fail2ban:
         self.f2b = Fail2ban(fail2ban)
     ObjectStorageSFTPRequestHandler.auth_timeout = auth_timeout
     ObjectStorageSFTPRequestHandler.negotiation_timeout = negotiation_timeout
     ObjectStorageSFTPRequestHandler.keepalive = keepalive
     ObjectStorageSFTPRequestHandler.secopts = secopts
     ObjectStorageSFTPRequestHandler.server_ident = server_ident
     ObjectStorageSFTPRequestHandler.proxy_protocol = proxy_protocol
     ForkingTCPServer.__init__(self, address,
                               ObjectStorageSFTPRequestHandler)
     ObjectStorageFD.split_size = split_size
     ObjectStorageFD.storage_policy = storage_policy
     ObjectStorageFD.large_object_container_suffix = large_object_container_suffix
Exemple #4
0
class ObjectStorageSFTPServer(ForkingTCPServer, paramiko.ServerInterface):
    """
    Expose a ObjectStorageFS object over SFTP.
    """
    allow_reuse_address = True

    def __init__(self, address, host_key=None, authurl=None, max_children=20, keystone=None):
        self.log = paramiko.util.get_logger("paramiko")
        self.log.debug("%s: start server" % self.__class__.__name__)
        self.fs = ObjectStorageFS(None, None, authurl=authurl, keystone=keystone) # unauthorized
        self.host_key = host_key
        self.max_children = max_children
        ForkingTCPServer.__init__(self, address, ObjectStorageSFTPRequestHandler)

    def check_channel_request(self, kind, chanid):
        if kind == 'session':
            return paramiko.OPEN_SUCCEEDED
        self.log.warning("Channel request denied from %s, kind=%s" \
                         % (self.client_address, kind))
        # all the check_channel_*_request return False by default but
        # sftp subsystem because of the set_subsystem_handler call in
        # the ObjectStorageSFTPRequestHandler
        return paramiko.OPEN_FAILED_ADMINISTRATIVELY_PROHIBITED

    def check_auth_none(self, username):
        """Check whether the user can proceed without authentication."""
        return paramiko.AUTH_FAILED

    def check_auth_publickey(self, username, key):
        """Check whether the given public key is valid for authentication."""
        return paramiko.AUTH_FAILED

    def check_auth_password(self, username, password):
        """Check whether the given password is valid for authentication."""
        self.log.info("Auth request (type=password), username=%s, from=%s" \
                      % (username, self.client_address))
        try:
            if not password:
                raise EnvironmentError("no password provided")
            self.fs.authenticate(username, password)
        except EnvironmentError, e:
            self.log.warning("%s: Failed to authenticate: %s" % (self.client_address, e))
            self.log.error("Authentication failure for %s from %s port %s" % (username,
                           self.client_address[0], self.client_address[1]))
            return paramiko.AUTH_FAILED
        self.fs.conn.real_ip = self.client_address[0]
        self.log.info("%s authenticated from %s" % (username, self.client_address))
        return paramiko.AUTH_SUCCESSFUL
Exemple #5
0
 def __init__(self, address, host_key=None, authurl=None, max_children=20, keystone=None):
     self.log = paramiko.util.get_logger("paramiko")
     self.log.debug("%s: start server" % self.__class__.__name__)
     self.fs = ObjectStorageFS(None, None, authurl=authurl, keystone=keystone) # unauthorized
     self.host_key = host_key
     self.max_children = max_children
     ForkingTCPServer.__init__(self, address, ObjectStorageSFTPRequestHandler)
Exemple #6
0
 def __init__(
     self,
     address,
     host_key=None,
     authurl=None,
     max_children=20,
     keystone=None,
     no_scp=False,
     split_size=0,
     hide_part_dir=False,
     auth_timeout=None,
     negotiation_timeout=0,
     keepalive=0,
     insecure=False,
     secopts=None,
     server_ident=None,
 ):
     self.log = paramiko.util.get_logger("paramiko")
     self.log.debug("%s: start server" % self.__class__.__name__)
     self.fs = ObjectStorageFS(
         None, None, authurl=authurl, keystone=keystone, hide_part_dir=hide_part_dir, insecure=insecure
     )  # unauthorized
     self.host_key = host_key
     self.max_children = max_children
     self.no_scp = no_scp
     ObjectStorageSFTPRequestHandler.auth_timeout = auth_timeout
     ObjectStorageSFTPRequestHandler.negotiation_timeout = negotiation_timeout
     ObjectStorageSFTPRequestHandler.keepalive = keepalive
     ObjectStorageSFTPRequestHandler.secopts = secopts
     ObjectStorageSFTPRequestHandler.server_ident = server_ident
     ForkingTCPServer.__init__(self, address, ObjectStorageSFTPRequestHandler)
     ObjectStorageFD.split_size = split_size
Exemple #7
0
class ObjectStorageSFTPServer(ForkingTCPServer, paramiko.ServerInterface):
    """
    Expose a ObjectStorageFS object over SFTP.
    """
    allow_reuse_address = True

    def __init__(self, address, host_key=None, authurl=None, max_children=20, keystone=None,
            no_scp=False, split_size=0, hide_part_dir=False, auth_timeout=None, negotiation_timeout=0):
        self.log = paramiko.util.get_logger("paramiko")
        self.log.debug("%s: start server" % self.__class__.__name__)
        self.fs = ObjectStorageFS(None, None, authurl=authurl, keystone=keystone, hide_part_dir=hide_part_dir) # unauthorized
        self.host_key = host_key
        self.max_children = max_children
        self.no_scp = no_scp
        ObjectStorageSFTPRequestHandler.auth_timeout = auth_timeout
        ObjectStorageSFTPRequestHandler.negotiation_timeout = negotiation_timeout
        ForkingTCPServer.__init__(self, address, ObjectStorageSFTPRequestHandler)
        ObjectStorageFD.split_size = split_size

    def check_channel_request(self, kind, chanid):
        if kind == 'session':
            return paramiko.OPEN_SUCCEEDED
        self.log.warning("Channel request denied from %s, kind=%s" \
                         % (self.client_address, kind))
        # all the check_channel_*_request return False by default but
        # sftp subsystem because of the set_subsystem_handler call in
        # the ObjectStorageSFTPRequestHandler
        return paramiko.OPEN_FAILED_ADMINISTRATIVELY_PROHIBITED

    def check_channel_exec_request(self, channel, command):
        """Determine if a shell command will be executed for the client."""

        # Parse the command
        if ' -- ' in command:
            # scp will use -- to delimit the begining of the unscaped filename
            # so translate it to something that shelex can manage
            command = command.replace(' -- ', ' "') + '"'
        command = shlex.split(command)
        self.log.debug('check_channel_exec_request %r' % command)

        try:
            if command[0] == 'scp':
                if self.no_scp:
                    self.log.info("scp exec request denied from=%s (scp is disabled)" % (self.client_address,))
                    return False
                self.log.info('invoking %r from=%s' % (command, self.client_address))
                # handle the command execution
                SCPHandler(command[1:], channel, self.fs, self.log).start()
                return True
        except:
            self.log.exception("command %r failed from=%s" % (command, self.client_address))
            return False

        return False

    def check_auth_none(self, username):
        """Check whether the user can proceed without authentication."""
        return paramiko.AUTH_FAILED

    def check_auth_publickey(self, username, key):
        """Check whether the given public key is valid for authentication."""
        return paramiko.AUTH_FAILED

    def check_auth_password(self, username, password):
        """Check whether the given password is valid for authentication."""
        self.log.info("Auth request (type=password), username=%s, from=%s" \
                      % (username, self.client_address))
        try:
            if not password:
                raise EnvironmentError("no password provided")
            self.fs.authenticate(username, password)
        except EnvironmentError, e:
            self.log.warning("%s: Failed to authenticate: %s" % (self.client_address, e))
            self.log.error("Authentication failure for %s from %s port %s" % (username,
                           self.client_address[0], self.client_address[1]))
            return paramiko.AUTH_FAILED
        self.fs.conn.real_ip = self.client_address[0]
        self.log.info("%s authenticated from %s" % (username, self.client_address))
        return paramiko.AUTH_SUCCESSFUL
Exemple #8
0
class ObjectStorageSFTPServer(ForkingTCPServer, paramiko.ServerInterface):
    """
    Expose a ObjectStorageFS object over SFTP.
    """
    allow_reuse_address = True

    def __init__(self,
                 address,
                 host_key=None,
                 authurl=None,
                 max_children=20,
                 keystone=None,
                 no_scp=False,
                 split_size=0,
                 hide_part_dir=False,
                 auth_timeout=None,
                 negotiation_timeout=0,
                 keepalive=0,
                 insecure=False,
                 secopts=None,
                 server_ident=None,
                 storage_policy=None,
                 proxy_protocol=None,
                 rsync_bin=None,
                 large_object_container_suffix=None,
                 fail2ban=None):
        self.log = paramiko.util.get_logger("paramiko")
        self.log.debug("%s: start server" % self.__class__.__name__)
        self.fs = ObjectStorageFS(
            None,
            None,
            authurl=authurl,
            keystone=keystone,
            hide_part_dir=hide_part_dir,
            insecure=insecure,
            storage_policy=storage_policy)  # unauthorized
        self.host_key = host_key
        self.max_children = max_children
        self.no_scp = no_scp
        self.rsync_bin = rsync_bin
        self.split_size = split_size
        self.fail2ban = fail2ban
        if fail2ban:
            self.f2b = Fail2ban(fail2ban)
        ObjectStorageSFTPRequestHandler.auth_timeout = auth_timeout
        ObjectStorageSFTPRequestHandler.negotiation_timeout = negotiation_timeout
        ObjectStorageSFTPRequestHandler.keepalive = keepalive
        ObjectStorageSFTPRequestHandler.secopts = secopts
        ObjectStorageSFTPRequestHandler.server_ident = server_ident
        ObjectStorageSFTPRequestHandler.proxy_protocol = proxy_protocol
        ForkingTCPServer.__init__(self, address,
                                  ObjectStorageSFTPRequestHandler)
        ObjectStorageFD.split_size = split_size
        ObjectStorageFD.storage_policy = storage_policy
        ObjectStorageFD.large_object_container_suffix = large_object_container_suffix

    def check_channel_request(self, kind, chanid):
        if kind == 'session':
            return paramiko.OPEN_SUCCEEDED
        self.log.warning("Channel request denied from %s, kind=%s" \
                         % (self.client_address, kind))
        # all the check_channel_*_request return False by default but
        # sftp subsystem because of the set_subsystem_handler call in
        # the ObjectStorageSFTPRequestHandler
        return paramiko.OPEN_FAILED_ADMINISTRATIVELY_PROHIBITED

    def check_channel_exec_request(self, channel, command):
        """Determine if a shell command will be executed for the client."""

        # Parse the command
        if ' -- ' in command:
            # scp will use -- to delimit the beginning of the unscaped filename
            # so translate it to something that shelex can manage
            command = command.replace(' -- ', ' "') + '"'
        command = shlex.split(command)
        self.log.debug('check_channel_exec_request %r' % command)

        try:
            if command[0] == 'scp':
                if self.no_scp:
                    self.log.info(
                        "scp exec request denied from=%s (scp is disabled)" %
                        (self.client_address, ))
                    return False
                self.log.info('invoking %r from=%s' %
                              (command, self.client_address))
                # handle the command execution
                SCPHandler(command[1:], channel, self.fs, self.log).start()
                return True
            if command[0] == 'rsync':
                if self.rsync_bin is None:
                    self.log.info(
                        "rsync exec request denied from=%s (rsync_bin is not set)"
                        % (self.client_address, ))
                    return False
                self.log.info('invoking %s %r from=%s' %
                              (self.rsync_bin, command, self.client_address))
                RsyncHandler(self.rsync_bin, command[1:], channel, self.fs,
                             self.log, self.split_size).start()
                return True
        except:
            self.log.exception("command %r failed from=%s" %
                               (command, self.client_address))
            return False

        return False

    def check_auth_none(self, username):
        """Check whether the user can proceed without authentication."""
        return paramiko.AUTH_FAILED

    def check_auth_publickey(self, username, key):
        """Check whether the given public key is valid for authentication."""
        return paramiko.AUTH_FAILED

    def check_auth_password(self, username, password):
        """Check whether the given password is valid for authentication."""
        self.log.info("Auth request (type=password), username=%s, from=%s" \
                      % (username, self.client_address))

        ip = self.client_address[0]
        if self.fail2ban:
            try:
                if self.f2b.get(ip) == 0:
                    self.log.info(
                        "New authentication request from banned address %s" %
                        ip)
                    return paramiko.AUTH_FAILED
            except Exception as e:
                self.log.exception("Failed to get %s value from memcache: %s" %
                                   (ip, e))
                pass

        try:
            if not password:
                raise EnvironmentError("no password provided")
            self.fs.authenticate(username, password)
        except EnvironmentError, e:
            self.log.warning("%s: Failed to authenticate: %s" %
                             (self.client_address, e))
            self.log.error(
                "Authentication failure for %s from %s port %s" %
                (username, self.client_address[0], self.client_address[1]))
            if self.fail2ban:
                try:
                    self.f2b.set(ip)
                except Exception as e:
                    self.log.exception(
                        "Failed to set new value in memcache: %s" % e)
                    pass
            return paramiko.AUTH_FAILED
        self.fs.conn.real_ip = ip
        self.log.info("%s authenticated from %s" %
                      (username, self.client_address))
        return paramiko.AUTH_SUCCESSFUL
Exemple #9
0
class Main(object):
    def __init__(self):
        """Parse configuration and CLI options."""
        global config_file

        # look for an alternative configuration file
        alt_config_file = False
        # used to show errors before we actually start parsing stuff
        parser = OptionParser()
        for arg in sys.argv:
            if arg == '--config':
                try:
                    alt_config_file = sys.argv[sys.argv.index(arg) + 1]
                    config_file = alt_config_file
                except IndexError:
                    pass
            elif arg.startswith('--config='):
                _, alt_config_file = arg.split('=', 1)
                if alt_config_file == '':
                    parser.error("--config option requires an argument")
                config_file = alt_config_file

        config = RawConfigParser({
            'auth-url': None,
            'insecure': False,
            'host-key-file': None,
            'bind-address': "127.0.0.1",
            'port': 8022,
            'server-ident': 'sftpcloudfs_%s' % version,
            'memcache': None,
            'max-children': "20",
            'auth-timeout': "60",
            'negotiation-timeout': "0",
            'keepalive': "0",
            'ciphers': None,
            'digests': None,
            'log-file': None,
            'syslog': 'no',
            'verbose': 'no',
            'scp-support': 'yes',
            'pid-file': None,
            'uid': None,
            'gid': None,
            'split-large-files': "0",
            'hide-part-dir': "no",
            # keystone auth support
            'keystone-auth': False,
            'keystone-auth-version': '2.0',
            'keystone-region-name': None,
            'keystone-tenant-separator': default_ks_tenant_separator,
            'keystone-domain-separator': '@',
            'keystone-service-type': default_ks_service_type,
            'keystone-endpoint-type': default_ks_endpoint_type,
            'storage-policy': None,
            'proxy-protocol': 'no',
            'rsync-bin': None,
            'large-object-container': 'no',
            'large-object-container-suffix': '_segments',
            'fail2ban': False,
            'ban-time': 600,
            'find-time': 600,
            'max-retry': 3,
        })

        try:
            if not config.read(config_file) and alt_config_file:
                # the default conf file is optional
                parser.error("failed to read %s" % config_file)
        except ParsingError as ex:
            parser.error("failed to read %s: %s" % (config_file, ex.message))

        if not config.has_section('sftpcloudfs'):
            config.add_section('sftpcloudfs')

        parser = OptionParser(version="%prog " + version,
                              description="This is a SFTP interface to OpenStack " + \
                                    "Object Storage (Swift).",
                              epilog="Contact and support at: %s" % project_url)

        parser.add_option("-a",
                          "--auth-url",
                          dest="authurl",
                          default=config.get('sftpcloudfs', 'auth-url'),
                          help="Authentication URL")

        parser.add_option(
            "--insecure",
            dest="insecure",
            action="store_true",
            default=config.get('sftpcloudfs', 'insecure'),
            help="Allow to access servers without checking SSL certs")

        host_key = config.get('sftpcloudfs', 'host-key-file')
        if host_key:
            host_key = [x.strip() for x in host_key.split(',')]
        parser.add_option("-k",
                          "--host-key-file",
                          type="str",
                          dest="host_key",
                          action="append",
                          default=host_key,
                          help="Host key(s) used by the server")

        parser.add_option("-b",
                          "--bind-address",
                          dest="bind_address",
                          default=config.get('sftpcloudfs', 'bind-address'),
                          help="Address to bind (default: 127.0.0.1)")

        parser.add_option("-p",
                          "--port",
                          dest="port",
                          type="int",
                          default=config.get('sftpcloudfs', 'port'),
                          help="Port to bind (default: 8022)")

        parser.add_option("--server-ident", dest="server_ident",
                          type="str",
                          default=config.get('sftpcloudfs', 'server-ident'),
                          help="Server ident to use when sending the SSH banner to the " + \
                                  "client (default: sftpcloudfs_%s)" % version)

        memcache = config.get('sftpcloudfs', 'memcache')
        if memcache:
            memcache = [x.strip() for x in memcache.split(',')]
        parser.add_option(
            '--memcache',
            type="str",
            dest="memcache",
            action="append",
            default=memcache,
            help="Memcache server(s) to be used for cache (ip:port)")

        parser.add_option("-l",
                          "--log-file",
                          dest="log_file",
                          default=config.get('sftpcloudfs', 'log-file'),
                          help="Log into provided file")

        parser.add_option(
            "-f",
            "--foreground",
            dest="foreground",
            action="store_true",
            default=False,
            help="Run in the foreground (don't detach from terminal)")

        parser.add_option(
            "--disable-scp",
            dest="no_scp",
            action="store_true",
            default=not config.getboolean('sftpcloudfs', 'scp-support'),
            help="Disable SCP support (default: enabled)")

        parser.add_option(
            "--syslog",
            dest="syslog",
            action="store_true",
            default=config.getboolean('sftpcloudfs', 'syslog'),
            help="Enable logging to system logger (daemon facility)")

        parser.add_option("-v",
                          "--verbose",
                          dest="verbose",
                          action="store_true",
                          default=config.getboolean('sftpcloudfs', 'verbose'),
                          help="Show detailed information on logging")

        parser.add_option('--pid-file',
                          type="str",
                          dest="pid_file",
                          default=config.get('sftpcloudfs', 'pid-file'),
                          help="Full path to the pid file location")

        parser.add_option(
            '--uid',
            dest="uid",
            default=config.get('sftpcloudfs', 'uid'),
            help="UID to drop the privileges to when in daemon mode")

        parser.add_option(
            '--gid',
            dest="gid",
            default=config.get('sftpcloudfs', 'gid'),
            help="GID to drop the privileges to when in daemon mode")

        parser.add_option('--keystone-auth',
                          action="store_true",
                          dest="keystone",
                          default=config.get('sftpcloudfs', 'keystone-auth'),
                          help="Use Keystone auth (requires keystoneclient)")

        parser.add_option(
            '--keystone-auth-version',
            type="str",
            dest="auth_version",
            default=config.get('sftpcloudfs', 'keystone-auth-version'),
            help="Identity API version to be used (default: 2.0)")

        parser.add_option('--keystone-region-name',
                          type="str",
                          dest="region_name",
                          default=config.get('sftpcloudfs',
                                             'keystone-region-name'),
                          help="Region name to be used in Keystone auth")

        parser.add_option('--keystone-tenant-separator',
                          type="str",
                          dest="tenant_separator",
                          default=config.get('sftpcloudfs', 'keystone-tenant-separator'),
                          help="Character used to separate tenant_name/username in Keystone auth, " + \
                              "default: TENANT%sUSERNAME" % default_ks_tenant_separator)

        parser.add_option('--keystone-domain-separator',
                          type="str",
                          dest="domain_separator",
                          default=config.get('sftpcloudfs', 'keystone-domain-separator'),
                          help="Character used to separate project_name/project_domain_name " + \
                               "and username/user_domain_name in Keystone auth v3 (default: @)")

        parser.add_option(
            '--keystone-service-type',
            type="str",
            dest="service_type",
            default=config.get('sftpcloudfs', 'keystone-service-type'),
            help="Service type to be used in Keystone auth, default: %s" %
            default_ks_service_type)

        parser.add_option(
            '--keystone-endpoint-type',
            type="str",
            dest="endpoint_type",
            default=config.get('sftpcloudfs', 'keystone-endpoint-type'),
            help="Endpoint type to be used in Keystone auth, default: %s" %
            default_ks_endpoint_type)

        parser.add_option('--config',
                          type="str",
                          dest="config",
                          default=config_file,
                          help="Use an alternative configuration file")

        parser.add_option("--storage-policy",
                          type="str",
                          dest="storage_policy",
                          default=config.get('sftpcloudfs', 'storage-policy'),
                          help="Swift storage policy to be used")

        parser.add_option("--proxy-protocol",
                          action="store_true",
                          dest="proxy_protocol",
                          default=config.getboolean('sftpcloudfs',
                                                    'proxy-protocol'),
                          help="Enable the Proxy protocol header parser")

        parser.add_option("--rsync-bin",
                          type="str",
                          dest="rsync_bin",
                          default=config.get('sftpcloudfs', 'rsync-bin'),
                          help="Custom rsync binary to be used")

        parser.add_option('--large-object-container',
                          action="store_true",
                          dest="large_object_container",
                          default=config.getboolean('sftpcloudfs',
                                                    'large-object-container'),
                          help="Enable large object container support")

        parser.add_option(
            '--large-object-container-suffix',
            type="str",
            dest="large_object_container_suffix",
            default=config.get('sftpcloudfs', 'large-object-container-suffix'),
            help="Large object container suffix (default: '_segments'")

        parser.add_option('--fail2ban',
                          action="store_true",
                          dest="fail2ban",
                          default=config.get('sftpcloudfs', 'fail2ban'),
                          help="Enable fail2ban feature (requires memcache)")

        parser.add_option('--ban-time',
                          type="int",
                          dest="ban_time",
                          default=config.get('sftpcloudfs', 'ban-time'),
                          help="Ban duration in seconds (default: 600)")

        parser.add_option(
            '--find-time',
            type="int",
            dest="find_time",
            default=config.get('sftpcloudfs', 'find-time'),
            help=
            "Duration in seconds before counter reset if no match is found (default: 600)"
        )

        parser.add_option(
            '--max-retry',
            type="int",
            dest="max_retry",
            default=config.get('sftpcloudfs', 'max-retry'),
            help=
            "Number of matches before triggering the ban action (default: 3)")

        (options, args) = parser.parse_args()

        # required parameters
        if not options.authurl:
            parser.error("No auth-url provided")

        if not options.host_key:
            parser.error("No host-key-file provided")

        self.host_key = []
        try:
            [
                self.host_key.append(self._get_pkey_object(k))
                for k in options.host_key
            ]
        except (IOError, paramiko.SSHException), e:
            parser.error("host-key-file: %s" % e)

        if options.memcache:
            ObjectStorageFS.memcache_hosts = options.memcache
            try:
                ObjectStorageFS(None, None, None)
            except (ValueError, TypeError):
                parser.error(
                    "memcache: invalid server address, ip:port expected")

        if options.pid_file:
            self.pidfile = PIDFile(options.pid_file)
            if self.pidfile.is_locked():
                parser.error(
                    "pid-file found: %s\nIs the server already running?" %
                    options.pid_file)
        else:
            self.pidfile = None

        try:
            options.max_children = int(
                config.get('sftpcloudfs', 'max-children'))
        except ValueError:
            parser.error('max-children: invalid value, integer expected')

        try:
            options.auth_timeout = int(
                config.get('sftpcloudfs', 'auth-timeout'))
        except ValueError:
            parser.error('auth-timeout: invalid value, integer expected')

        if options.auth_timeout <= 0:
            parser.error('auth-timeout: invalid value')

        try:
            options.negotiation_timeout = int(
                config.get('sftpcloudfs', 'negotiation-timeout'))
        except ValueError:
            parser.error(
                'negotiation-timeout: invalid value, integer expected')

        if options.negotiation_timeout < 0:
            parser.error('negotiation-timeout: invalid value')

        try:
            options.keepalive = int(config.get('sftpcloudfs', 'keepalive'))
        except ValueError:
            parser.error('keepalive: invalid value, integer expected')

        if options.keepalive < 0:
            parser.error('keepalive: invalid value')

        options.secopts = {}
        ciphers = config.get('sftpcloudfs', 'ciphers')
        if ciphers:
            options.secopts["ciphers"] = [
                x.strip() for x in ciphers.split(',')
            ]

        digests = config.get('sftpcloudfs', 'digests')
        if digests:
            options.secopts["digests"] = [
                x.strip() for x in digests.split(',')
            ]

        try:
            options.split_size = int(
                config.get('sftpcloudfs', 'split-large-files')) * 2**20
        except ValueError:
            parser.error('split-large-files: invalid size, integer expected')

        options.hide_part_dir = config.getboolean('sftpcloudfs',
                                                  'hide-part-dir')

        if options.keystone:
            keystone_keys = ('auth_version', 'region_name', 'tenant_separator',
                             'domain_separator', 'service_type',
                             'endpoint_type')
            options.keystone = dict(
                (key, getattr(options, key)) for key in keystone_keys)

        if options.uid:
            try:
                options.uid = int(options.uid)
            except ValueError:
                try:
                    options.uid = pwd.getpwnam(options.uid).pw_uid
                except KeyError:
                    parser.error("uid: Invalid uid: %s" % options.uid)

        if options.gid:
            try:
                options.gid = int(options.gid)
            except ValueError:
                try:
                    options.gid = pwd.getpwnam(options.gid).pw_gid
                except KeyError:
                    parser.error("gid: Invalid gid: %s" % options.gid)

        if config.getboolean('sftpcloudfs', 'large-object-container'):
            try:
                options.large_object_container_suffix = config.get(
                    'sftpcloudfs', 'large-object-container-suffix')
            except ValueError:
                parser.error(
                    'large-object-container-suffix: invalid value, string expected'
                )
        else:
            options.large_object_container_suffix = None

        if options.fail2ban:
            if not options.memcache:
                parser.error('memcache is mandatory to use fail2ban feature')
            fail2ban_keys = ('ban_time', 'find_time', 'max_retry', 'memcache')
            options.fail2ban = dict(
                (key, getattr(options, key)) for key in fail2ban_keys)

        self.options = options
Exemple #10
0
class ObjectStorageSFTPServer(ForkingTCPServer, paramiko.ServerInterface):
    """
    Expose a ObjectStorageFS object over SFTP.
    """
    allow_reuse_address = True

    def __init__(self,
                 address,
                 host_key=None,
                 authurl=None,
                 max_children=20,
                 keystone=None,
                 no_scp=False,
                 split_size=0,
                 hide_part_dir=False,
                 auth_timeout=None,
                 negotiation_timeout=0):
        self.log = paramiko.util.get_logger("paramiko")
        self.log.debug("%s: start server" % self.__class__.__name__)
        self.fs = ObjectStorageFS(None,
                                  None,
                                  authurl=authurl,
                                  keystone=keystone,
                                  hide_part_dir=hide_part_dir)  # unauthorized
        self.host_key = host_key
        self.max_children = max_children
        self.no_scp = no_scp
        ObjectStorageSFTPRequestHandler.auth_timeout = auth_timeout
        ObjectStorageSFTPRequestHandler.negotiation_timeout = negotiation_timeout
        ForkingTCPServer.__init__(self, address,
                                  ObjectStorageSFTPRequestHandler)
        ObjectStorageFD.split_size = split_size

    def check_channel_request(self, kind, chanid):
        if kind == 'session':
            return paramiko.OPEN_SUCCEEDED
        self.log.warning("Channel request denied from %s, kind=%s" \
                         % (self.client_address, kind))
        # all the check_channel_*_request return False by default but
        # sftp subsystem because of the set_subsystem_handler call in
        # the ObjectStorageSFTPRequestHandler
        return paramiko.OPEN_FAILED_ADMINISTRATIVELY_PROHIBITED

    def check_channel_exec_request(self, channel, command):
        """Determine if a shell command will be executed for the client."""

        # Parse the command
        if ' -- ' in command:
            # scp will use -- to delimit the begining of the unscaped filename
            # so translate it to something that shelex can manage
            command = command.replace(' -- ', ' "') + '"'
        command = shlex.split(command)
        self.log.debug('check_channel_exec_request %r' % command)

        try:
            if command[0] == 'scp':
                if self.no_scp:
                    self.log.info(
                        "scp exec request denied from=%s (scp is disabled)" %
                        (self.client_address, ))
                    return False
                self.log.info('invoking %r from=%s' %
                              (command, self.client_address))
                # handle the command execution
                SCPHandler(command[1:], channel, self.fs, self.log).start()
                return True
        except:
            self.log.exception("command %r failed from=%s" %
                               (command, self.client_address))
            return False

        return False

    def check_auth_none(self, username):
        """Check whether the user can proceed without authentication."""
        return paramiko.AUTH_FAILED

    def check_auth_publickey(self, username, key):
        """Check whether the given public key is valid for authentication."""
        return paramiko.AUTH_FAILED

    def check_auth_password(self, username, password):
        """Check whether the given password is valid for authentication."""
        self.log.info("Auth request (type=password), username=%s, from=%s" \
                      % (username, self.client_address))
        try:
            if not password:
                raise EnvironmentError("no password provided")

            result = re.search('(@|^acc-)', username)
            if (result is None):
                raise EnvironmentError("bogus username")

            self.fs.authenticate(username, password)
        except EnvironmentError, e:
            self.log.warning("%s: Failed to authenticate: %s" %
                             (self.client_address, e))
            self.log.error(
                "Authentication failure for %s from %s port %s" %
                (username, self.client_address[0], self.client_address[1]))
            return paramiko.AUTH_FAILED
        self.fs.conn.real_ip = self.client_address[0]
        self.log.info("%s authenticated from %s" %
                      (username, self.client_address))
        return paramiko.AUTH_SUCCESSFUL
Exemple #11
0
class Main(object):
    def __init__(self):
        """Parse configuration and CLI options."""
        global config_file

        # look for an alternative configuration file
        alt_config_file = False
        # used to show errors before we actually start parsing stuff
        parser = OptionParser()
        for arg in sys.argv:
            if arg == '--config':
                try:
                    alt_config_file = sys.argv[sys.argv.index(arg) + 1]
                    config_file = alt_config_file
                except IndexError:
                    pass
            elif arg.startswith('--config='):
                _, alt_config_file = arg.split('=', 1)
                if alt_config_file == '':
                    parser.error("--config option requires an argument")
                config_file = alt_config_file

        config = RawConfigParser({
            'auth-url':
            None,
            'host-key-file':
            None,
            'bind-address':
            "127.0.0.1",
            'port':
            8022,
            'memcache':
            None,
            'max-children':
            "40",
            'auth-timeout':
            "30",
            'negotiation-timeout':
            "30",
            'log-file':
            None,
            'syslog':
            'no',
            'verbose':
            'no',
            'scp-support':
            'yes',
            'pid-file':
            None,
            'uid':
            None,
            'gid':
            None,
            'split-large-files':
            "1073741824",
            'hide-part-dir':
            "no",
            # keystone auth 2.0 support
            'keystone-auth':
            False,
            'keystone-region-name':
            None,
            'keystone-tenant-separator':
            default_ks_tenant_separator,
            'keystone-service-type':
            default_ks_service_type,
            'keystone-endpoint-type':
            default_ks_endpoint_type,
        })

        if not config.read(config_file) and alt_config_file:
            # the default conf file is optional
            parser.error("failed to read %s" % config_file)

        if not config.has_section('sftpcloudfs'):
            config.add_section('sftpcloudfs')

        parser = OptionParser(version="%prog " + version,
                              description="This is a SFTP interface to OpenStack " + \
                                    "Object Storage (Swift).",
                              epilog="Contact and support at: %s" % project_url)

        parser.add_option("-a",
                          "--auth-url",
                          dest="authurl",
                          default=config.get('sftpcloudfs', 'auth-url'),
                          help="Authentication URL")

        parser.add_option("-k",
                          "--host-key-file",
                          dest="host_key",
                          default=config.get('sftpcloudfs', 'host-key-file'),
                          help="Host RSA key used by the server")

        parser.add_option("-b",
                          "--bind-address",
                          dest="bind_address",
                          default=config.get('sftpcloudfs', 'bind-address'),
                          help="Address to bind (default: 127.0.0.1)")

        parser.add_option("-p",
                          "--port",
                          dest="port",
                          type="int",
                          default=config.get('sftpcloudfs', 'port'),
                          help="Port to bind (default: 8022)")

        memcache = config.get('sftpcloudfs', 'memcache')
        if memcache:
            memcache = [x.strip() for x in memcache.split(',')]
        parser.add_option(
            '--memcache',
            type="str",
            dest="memcache",
            action="append",
            default=memcache,
            help="Memcache server(s) to be used for cache (ip:port)")

        parser.add_option("-l",
                          "--log-file",
                          dest="log_file",
                          default=config.get('sftpcloudfs', 'log-file'),
                          help="Log into provided file")

        parser.add_option(
            "-f",
            "--foreground",
            dest="foreground",
            action="store_true",
            default=False,
            help="Run in the foreground (don't detach from terminal)")

        parser.add_option(
            "--disable-scp",
            dest="no_scp",
            action="store_true",
            default=not config.getboolean('sftpcloudfs', 'scp-support'),
            help="Disable SCP support (default: enabled)")

        parser.add_option(
            "--syslog",
            dest="syslog",
            action="store_true",
            default=config.getboolean('sftpcloudfs', 'syslog'),
            help="Enable logging to system logger (daemon facility)")

        parser.add_option("-v",
                          "--verbose",
                          dest="verbose",
                          action="store_true",
                          default=config.getboolean('sftpcloudfs', 'verbose'),
                          help="Show detailed information on logging")

        parser.add_option('--pid-file',
                          type="str",
                          dest="pid_file",
                          default=config.get('sftpcloudfs', 'pid-file'),
                          help="Pid file location when in daemon mode")

        parser.add_option(
            '--uid',
            type="int",
            dest="uid",
            default=config.get('sftpcloudfs', 'uid'),
            help="UID to drop the privileges to when in daemon mode")

        parser.add_option(
            '--gid',
            type="int",
            dest="gid",
            default=config.get('sftpcloudfs', 'gid'),
            help="GID to drop the privileges to when in daemon mode")

        parser.add_option(
            '--keystone-auth',
            action="store_true",
            dest="keystone",
            default=config.get('sftpcloudfs', 'keystone-auth'),
            help="Use auth 2.0 (Keystone, requires keystoneclient)")

        parser.add_option('--keystone-region-name',
                          type="str",
                          dest="region_name",
                          default=config.get('sftpcloudfs',
                                             'keystone-region-name'),
                          help="Region name to be used in auth 2.0")

        parser.add_option('--keystone-tenant-separator',
                          type="str",
                          dest="tenant_separator",
                          default=config.get('sftpcloudfs', 'keystone-tenant-separator'),
                          help="Character used to separate tenant_name/username in auth 2.0, " + \
                              "default: TENANT%sUSERNAME" % default_ks_tenant_separator)

        parser.add_option(
            '--keystone-service-type',
            type="str",
            dest="service_type",
            default=config.get('sftpcloudfs', 'keystone-service-type'),
            help="Service type to be used in auth 2.0, default: %s" %
            default_ks_service_type)

        parser.add_option(
            '--keystone-endpoint-type',
            type="str",
            dest="endpoint_type",
            default=config.get('sftpcloudfs', 'keystone-endpoint-type'),
            help="Endpoint type to be used in auth 2.0, default: %s" %
            default_ks_endpoint_type)

        parser.add_option('--config',
                          type="str",
                          dest="config",
                          default=config_file,
                          help="Use an alternative configuration file")

        (options, args) = parser.parse_args()

        # required parameters
        if not options.authurl:
            parser.error("No auth-url provided")

        if not options.host_key:
            parser.error("No host-key-file provided")

        try:
            self.host_key = paramiko.RSAKey(filename=options.host_key)
        except (IOError, paramiko.SSHException), e:
            parser.error("host-key-file: %s" % e)

        if not options.pid_file:
            options.pid_file = "%s/%s.pid" % (tempfile.gettempdir(),
                                              __package__)

        if options.memcache:
            ObjectStorageFS.memcache_hosts = options.memcache
            try:
                ObjectStorageFS(None, None, None)
            except (ValueError, TypeError):
                parser.error(
                    "memcache: invalid server address, ip:port expected")

        self.pidfile = PIDFile(options.pid_file)
        if self.pidfile.is_locked():
            parser.error("pid-file found: %s\nIs the server already running?" %
                         options.pid_file)

        try:
            options.max_children = int(
                config.get('sftpcloudfs', 'max-children'))
        except ValueError:
            parser.error('max-children: invalid value, integer expected')

        try:
            options.auth_timeout = int(
                config.get('sftpcloudfs', 'auth-timeout'))
        except ValueError:
            parser.error('auth-timeout: invalid value, integer expected')

        if options.auth_timeout <= 0:
            parser.error('auth-timeout: invalid value')

        try:
            options.negotiation_timeout = int(
                config.get('sftpcloudfs', 'negotiation-timeout'))
        except ValueError:
            parser.error(
                'negotiation-timeout: invalid value, integer expected')

        if options.negotiation_timeout < 0:
            parser.error('negotiation-timeout: invalid value')

        try:
            options.split_size = int(
                config.get('sftpcloudfs', 'split-large-files')) * 10**6
        except ValueError:
            parser.error('split-large-files: invalid size, integer expected')

        options.hide_part_dir = config.getboolean('sftpcloudfs',
                                                  'hide-part-dir')

        if options.keystone:
            keystone_keys = ('region_name', 'tenant_separator', 'service_type',
                             'endpoint_type')
            options.keystone = dict(
                (key, getattr(options, key)) for key in keystone_keys)

        self.options = options