class MyFactory(SSHFactory): privateKeys = {'ssh-rsa': Key.fromFile(filename=host_key_file)} publicKeys = {'ssh-rsa': Key.fromFile(filename=host_key_pub_file)} services = { 'ssh-userauth': SSHUserAuthServer, 'ssh-connection': SSHConnection }
def addKeyFile(self, kfile, password=None): if not os.path.exists(kfile): raise Exception("Key file not found %s", kfile) try: self.keys.append(Key.fromFile(kfile)) except EncryptedKeyError: self.keys.append(Key.fromFile(kfile, passphrase=password))
def makeFactory(self): """Create and start the factory that our SSH server uses.""" factory = Factory( get_portal(None, None), private_key=Key.fromFile(get_key_path(PRIVATE_KEY_FILE)), public_key=Key.fromFile(get_key_path(PUBLIC_KEY_FILE))) factory.startFactory() return factory
def readKey(self, path): try: return Key.fromFile(path) except EncryptedKeyError as ex: passphrase = getpass.getpass("%r keyphrase: " % (path, )) return Key.fromFile(path, passphrase=passphrase) except Exception as ex: print(ex) raise
def addKeyFile(self, kfile, password=None): """Import a private key file """ if not os.path.exists(kfile): raise Exception("Key file not found %s" % kfile) try: self.keys.append(Key.fromFile(kfile)) except EncryptedKeyError: self.keys.append(Key.fromFile(kfile, passphrase=password))
def __init__(self, settings): self.settings = settings self.portal = Portal(OpenRukoRealm(settings)) self.portal.registerChecker(OpenRukoCredentialChecker(settings)) self.privateKeys = { 'ssh-rsa': Key.fromFile(settings['gitmouth_private_key']), } self.publicKeys = { 'ssh-rsa': Key.fromFile(settings['gitmouth_public_key']), }
def makeFactory(self): """Create and start the factory that our SSH server uses.""" factory = Factory( get_portal(None, None), private_key=Key.fromFile( get_key_path(PRIVATE_KEY_FILE)), public_key=Key.fromFile( get_key_path(PUBLIC_KEY_FILE))) factory.startFactory() return factory
def create_server_factory(urwid_mind_factories, cred_checkers): """Convenience to create a server factory with a portal that uses a realm serving a given urwid widget against checkers provided. """ rlm = UrwidRealm(urwid_mind_factories) ptl = Portal(rlm, cred_checkers) factory = ConchFactory(ptl) factory.publicKeys[b'ssh-rsa'] = Key.fromFile('test_rsa.pub') factory.privateKeys[b'ssh-rsa'] = Key.fromFile('test_rsa') return factory
def __init__(self, settings): self.settings = settings self.portal = Portal(Realm(settings)) self.portal.registerChecker(Checker(settings)) self.privateKeys = { 'ssh-rsa': Key.fromFile(settings['hadoukngit']['private_key']), } self.publicKeys = { 'ssh-rsa': Key.fromFile(settings['hadoukngit']['public_key']), }
def _testrun(self, keyType, keySize=None): filename = self.mktemp() if keySize is None: subprocess.call(['ckeygen', '-t', keyType, '-f', filename, '--no-passphrase']) else: subprocess.call(['ckeygen', '-t', keyType, '-f', filename, '--no-passphrase', '-b', keySize]) privKey = Key.fromFile(filename) pubKey = Key.fromFile(filename + '.pub') if keyType == 'ecdsa': self.assertEqual(privKey.type(), 'EC') else: self.assertEqual(privKey.type(), keyType.upper()) self.assertTrue(pubKey.isPublic())
def __init__(self, private_key_path, public_key_path, moduli_path=None, banner=None): portal = self._makePortal() ssh_factory = ParsnipSSHFactory( portal, private_key=Key.fromFile(private_key_path), public_key=Key.fromFile(public_key_path), banner=banner, moduli_path=moduli_path ) self.service = strports.service('tcp:22', ssh_factory)
def _testrun(self, keyType, keySize=None, privateKeySubtype=None): filename = self.mktemp() args = ['ckeygen', '-t', keyType, '-f', filename, '--no-passphrase'] if keySize is not None: args.extend(['-b', keySize]) if privateKeySubtype is not None: args.extend(['--private-key-subtype', privateKeySubtype]) subprocess.call(args) privKey = Key.fromFile(filename) pubKey = Key.fromFile(filename + '.pub') if keyType == 'ecdsa': self.assertEqual(privKey.type(), 'EC') else: self.assertEqual(privKey.type(), keyType.upper()) self.assertTrue(pubKey.isPublic())
def _load_key(filename): try: return Key.fromFile(filename) except EncryptedKeyError: for i in xrange(3): passphrase = getpass.getpass("passphrase for %s: " % (filename,)) if not passphrase: continue try: return Key.fromFile(filename, passphrase=passphrase) except BadKeyError: pass raise BadKeyPassphraseError()
def _load_key(filename): try: return Key.fromFile(filename) except EncryptedKeyError: for i in xrange(3): passphrase = getpass.getpass("passphrase for %s: " % (filename, )) if not passphrase: continue try: return Key.fromFile(filename, passphrase=passphrase) except BadKeyError: pass raise BadKeyPassphraseError()
def getPublicKey(self): # this works with rsa too # just change the name here and in getPrivateKey if not os.path.exists(key_path) or self.lastPublicKey: # the file doesn't exist, or we've tried a public key return return Key.fromFile(key_path + ".pub")
def generated(ignored): key = Key.fromFile(id_rsa.path) configuring = deferToThread( self.configure_ssh, self.server.ip, self.server.port) configuring.addCallback(lambda ignored: key) return configuring
def connect(sdata, command, username, host, port=22, key_file=None, password=None): """ Connect to an SSH host (as it happens, persistently). """ sdata.set_conn_state('connecting') try: keys = [Key.fromFile(key_file)] if key_file else None except exceptions.IOError as e: print('### key load error:', str(e)) push_failure_message(str(e), sdata) return endpoint = SSHCommandClientEndpoint.newConnection( reactor, command, username, host, port=int(port), keys=keys, password=password, ui=None, knownHosts=PermissiveKnownHosts()) factory = Factory() factory.protocol = LineProtocol factory.sdata = sdata d = endpoint.connect(factory) # Very small race condition between here and the replacement # in connectionMade() above, but I've never managed to hit it. def disconnect(): sdata.log('Disconnecting while still attempting to connect, by request') d.cancel() sdata.transport_drop_cb = disconnect d.addErrback(lambda reason: push_failure_message(reason, sdata)) return d
def getPublicKey(self): path = self.options['ssh-key'] # this works with rsa too # just change the name here and in getPrivateKey if not os.path.exists(path) or self.lastPublicKey: # the file doesn't exist, or we've tried a public key return return Key.fromFile(filename=path + '.pub')
def getPublicKey(self): path = self.options['ssh-key'] # this works with rsa too # just change the name here and in getPrivateKey if not os.path.exists(path) or self.lastPublicKey: # the file doesn't exist, or we've tried a public key return return Key.fromFile(filename=path+'.pub')
def generated(ignored): id_rsa = self.ssh_config.child(b"id_rsa_flocker") id_rsa_pub = self.ssh_config.child(b"id_rsa_flocker.pub") key = Key.fromFile(id_rsa.path) self.assertEqual( # Avoid comparing the comment key.public().toString("OPENSSH").split()[:2], id_rsa_pub.getContent().split()[:2])
def _testrun(self, keyType, keySize=None, privateKeySubtype=None): filename = self.mktemp() args = ["ckeygen", "-t", keyType, "-f", filename, "--no-passphrase"] if keySize is not None: args.extend(["-b", keySize]) if privateKeySubtype is not None: args.extend(["--private-key-subtype", privateKeySubtype]) subprocess.call(args) privKey = Key.fromFile(filename) pubKey = Key.fromFile(filename + ".pub") if keyType == "ecdsa": self.assertEqual(privKey.type(), "EC") elif keyType == "ed25519": self.assertEqual(privKey.type(), "Ed25519") else: self.assertEqual(privKey.type(), keyType.upper()) self.assertTrue(pubKey.isPublic())
def test_key_not_regenerated(self): """ ``create_keypair`` does not generate a new key pair if one can already be found in ``id_rsa_flocker`` and ``id_rsa_flocker.pub``. """ ssh_config = FilePath(self.mktemp()) configurator = OpenSSHConfiguration( ssh_config_path=ssh_config, flocker_path=None) id_rsa = ssh_config.child(b"id_rsa_flocker") configurator.create_keypair() expected_key = Key.fromFile(id_rsa.path) configurator.create_keypair() self.assertEqual(expected_key, Key.fromFile(id_rsa.path))
def test_key_not_regenerated(self): """ ``create_keypair`` does not generate a new key pair if one can already be found in ``id_rsa_flocker`` and ``id_rsa_flocker.pub``. """ ssh_config = FilePath(self.mktemp()) configurator = OpenSSHConfiguration(ssh_config_path=ssh_config, flocker_path=None) id_rsa = ssh_config.child(b"id_rsa_flocker") configurator.create_keypair() expected_key = Key.fromFile(id_rsa.path) configurator.create_keypair() self.assertEqual(expected_key, Key.fromFile(id_rsa.path))
def __init__(self, portal, private_key_path, public_key_path, oops_configuration, main_log, access_log, access_log_path, strport='tcp:22', factory_decorator=None, banner=None): """Construct an SSH service. :param portal: The `twisted.cred.portal.Portal` that turns authentication requests into views on the system. :param private_key_path: The path to the SSH server's private key. :param public_key_path: The path to the SSH server's public key. :param oops_configuration: The section of the configuration file with the OOPS config details for this server. :param main_log: The name of the logger to log most of the server stuff to. :param access_log: The name of the logger object to log the server access details to. :param access_log_path: The path to the access log file. :param strport: The port to run the server on, expressed in Twisted's "strports" mini-language. Defaults to 'tcp:22'. :param factory_decorator: An optional callable that can decorate the server factory (e.g. with a `twisted.protocols.policies.TimeoutFactory`). It takes one argument, a factory, and must return a factory. :param banner: An announcement printed to users when they connect. By default, announce nothing. """ ssh_factory = Factory(portal, private_key=Key.fromFile(private_key_path), public_key=Key.fromFile(public_key_path), banner=banner) if factory_decorator is not None: ssh_factory = factory_decorator(ssh_factory) self.service = strports.service(strport, ssh_factory) self._oops_configuration = oops_configuration self._main_log = main_log self._access_log = access_log self._access_log_path = access_log_path
def __init__(self, base_path): """ :param FilePath base_path: The path beneath which all of the temporary SSH server-related files will be created. An ``ssh`` directory will be created as a child of this directory to hold the key pair that is generated. An ``sshd`` directory will also be created here to hold the generated host key. A ``home`` directory is also created here and used as the home directory for shell logins to the server. """ self.home = base_path.child(b"home") self.home.makedirs() ssh_path = base_path.child(b"ssh") ssh_path.makedirs() self.key_path = ssh_path.child(b"key") check_call( [ b"ssh-keygen", # Specify the path where the generated key is written. b"-f", self.key_path.path, # Specify an empty passphrase. b"-N", b"", # Generate as little output as possible. b"-q", ] ) key = Key.fromFile(self.key_path.path) sshd_path = base_path.child(b"sshd") sshd_path.makedirs() self.host_key_path = sshd_path.child(b"ssh_host_key") check_call( [ b"ssh-keygen", # See above for option explanations. b"-f", self.host_key_path.path, b"-N", b"", b"-q", ] ) factory = OpenSSHFactory() realm = _UnixSSHRealm(self.home) checker = _InMemoryPublicKeyChecker(public_key=key.public()) factory.portal = Portal(realm, [checker]) factory.dataRoot = sshd_path.path factory.moduliRoot = b"/etc/ssh" self._port = reactor.listenTCP(0, factory, interface=b"127.0.0.1") self.ip = IPAddress(self._port.getHost().host) self.port = self._port.getHost().port
def get_manhole_factory(namespace): """ Build a twisted manhole factory """ from twisted.conch import manhole, manhole_ssh from twisted.cred import portal, checkers # I really hate Twisted's default colors colors = { 'identifier': '\x1b[1;36m', 'keyword': '\x1b[33m', 'parameter': '\x1b[33m', 'variable': '\x1b[36m', 'string': '\x1b[35m', 'number': '\x1b[1;32m', 'op': '\x1b[33m' } manhole.VT102Writer.typeToColor.update(colors) realm = manhole_ssh.TerminalRealm() def get_manhole(_): return manhole.ColoredManhole(namespace) realm.chainedProtocolFactory.protocolFactory = get_manhole p = portal.Portal(realm) checker = checkers.InMemoryUsernamePasswordDatabaseDontUse() checker.addUser(config.backdoor_user, config.backdoor_password) p.registerChecker(checker) factory = manhole_ssh.ConchFactory(p) # As of Twisted~v16.0.0 we now have to give host SSH keys to any subclass of SSHFactory key_path = os.path.expanduser(config.ssh_key_path) factory.publicKeys = { 'ssh-rsa': Key.fromFile(key_path) } factory.privateKeys = { 'ssh-rsa': Key.fromFile(key_path) } return factory
def makeService(config): key = Key.fromFile(config["key"]) datapath = FilePath(config['datadir']) factory = SSHFactory() factory.publicKeys = factory.privateKeys = {key.sshType(): key} factory.portal = Portal( GittoRealm(datapath), [GittoPublicKeyDatabase(datapath.child(".config").child("keys"))]) return strports.service(config['port'], factory)
def postOptions(self): """ Verify the configuration is usable. """ for required in ["auth-host", "auth-port", "host-key", "client-key", "client-cert"]: if self[required] is None: raise UsageError("--%s option is required" % (required,)) try: self["host-key"] = Key.fromFile(self["host-key"].path) except (IOError, BadKeyError), e: raise UsageError("Cannot load host key: %s" % (e,))
def __init__(self, base_path): """ :param FilePath base_path: The path beneath which all of the temporary SSH server-related files will be created. An ``ssh`` directory will be created as a child of this directory to hold the key pair that is generated. An ``sshd`` directory will also be created here to hold the generated host key. A ``home`` directory is also created here and used as the home directory for shell logins to the server. """ self.home = base_path.child(b"home") self.home.makedirs() ssh_path = base_path.child(b"ssh") ssh_path.makedirs() self.key_path = ssh_path.child(b"key") check_call([ b"ssh-keygen", # Specify the path where the generated key is written. b"-f", self.key_path.path, # Specify an empty passphrase. b"-N", b"", # Generate as little output as possible. b"-q" ]) key = Key.fromFile(self.key_path.path) sshd_path = base_path.child(b"sshd") sshd_path.makedirs() self.host_key_path = sshd_path.child(b"ssh_host_key") check_call([ b"ssh-keygen", # See above for option explanations. b"-f", self.host_key_path.path, b"-N", b"", b"-q" ]) factory = OpenSSHFactory() realm = UnixSSHRealm(self.home) checker = _InMemoryPublicKeyChecker(public_key=key.public()) factory.portal = Portal(realm, [checker]) factory.dataRoot = sshd_path.path factory.moduliRoot = b"/etc/ssh" self._port = reactor.listenTCP(0, factory, interface=b"127.0.0.1") self.ip = IPAddress(self._port.getHost().host) self.port = self._port.getHost().port
def getPublicKey(self): """ Return the SSH public key (using the Property KeyPath) or None @return: SSH public key @rtype: string """ # Don't re-send the same public key if we have already been called. # TODO: Would be good to expand to support sending multiple keys. if self._key is not None and not self._sent_pk: self._sent_pk = True keyPath = os.path.expanduser(self.factory.keyPath + ".pub") return Key.fromFile(keyPath)
def __init__(self, portal, private_key_path, public_key_path, oops_configuration, main_log, access_log, access_log_path, strport='tcp:22', factory_decorator=None, banner=None): """Construct an SSH service. :param portal: The `twisted.cred.portal.Portal` that turns authentication requests into views on the system. :param private_key_path: The path to the SSH server's private key. :param public_key_path: The path to the SSH server's public key. :param oops_configuration: The section of the configuration file with the OOPS config details for this server. :param main_log: The name of the logger to log most of the server stuff to. :param access_log: The name of the logger object to log the server access details to. :param access_log_path: The path to the access log file. :param strport: The port to run the server on, expressed in Twisted's "strports" mini-language. Defaults to 'tcp:22'. :param factory_decorator: An optional callable that can decorate the server factory (e.g. with a `twisted.protocols.policies.TimeoutFactory`). It takes one argument, a factory, and must return a factory. :param banner: An announcement printed to users when they connect. By default, announce nothing. """ ssh_factory = Factory( portal, private_key=Key.fromFile(private_key_path), public_key=Key.fromFile(public_key_path), banner=banner) if factory_decorator is not None: ssh_factory = factory_decorator(ssh_factory) self.service = strports.service(strport, ssh_factory) self._oops_configuration = oops_configuration self._main_log = main_log self._access_log = access_log self._access_log_path = access_log_path
def _getKey(self): keyPath = os.path.expanduser(self.factory.keyPath) log.msg("Expanded SSH key path from KeyPath {0} to {1}".format(self.factory.keyPath, keyPath)) key = None if os.path.exists(keyPath): try: key = Key.fromFile(keyPath) except IOError, ex: message = "Unable to read the SSH key file because %s" % (str(ex)) log.warn(message) device = "localhost" # Fallback try: device = socket.getfqdn() except: pass
def _connect(self, host, port, user, password, private_key_file): keys = [Key.fromFile(private_key_file)] ep = SSHCommandClientEndpoint.newConnection( reactor, b'/bin/cat', user, host, port=port, password=password, keys=keys, agentEndpoint=None, knownHosts=EveryoneIsAKnownHostsFile()) factory = Factory() factory.protocol = _PersistentProtocol return ep.connect(factory).addCallback(self._connected)
def connect(sdata, command, username, host, port=22, key_file=None, password=None): """ Connect to an SSH host (as it happens, persistently). """ sdata.set_conn_state('connecting') try: keys = [Key.fromFile(key_file)] if key_file else None except exceptions.IOError as e: print('### key load error:', str(e)) push_failure_message(str(e), sdata) return endpoint = SSHCommandClientEndpoint.newConnection( reactor, command, username, host, port=int(port), keys=keys, password=password, ui=None, knownHosts=PermissiveKnownHosts()) factory = Factory() factory.protocol = LineProtocol factory.sdata = sdata d = endpoint.connect(factory) # Very small race condition between here and the replacement # in connectionMade() above, but I've never managed to hit it. def disconnect(): sdata.log( 'Disconnecting while still attempting to connect, by request') d.cancel() sdata.transport_drop_cb = disconnect d.addErrback(lambda reason: push_failure_message(reason, sdata)) return d
def generate_ssh_key(key_file): """ Generate a ssh key. :param FilePath key_file: Path to create ssh key at. :return Key: The generated key. """ check_call( [b"ssh-keygen", # Specify the path where the generated key is written. b"-f", key_file.path, # Specify an empty passphrase. b"-N", b"", # Generate as little output as possible. b"-q"]) return Key.fromFile(key_file.path)
def test_key_generated(self): """ ``create_keypair`` generates a new key pair and writes it locally to ``id_rsa_flocker`` and ``id_rsa_flocker.pub``. """ ssh_config = FilePath(self.mktemp()) configurator = OpenSSHConfiguration( ssh_config_path=ssh_config, flocker_path=None) configurator.create_keypair() id_rsa = ssh_config.child(b"id_rsa_flocker") id_rsa_pub = ssh_config.child(b"id_rsa_flocker.pub") key = Key.fromFile(id_rsa.path) self.assertEqual( # Avoid comparing the comment key.public().toString( type="OPENSSH", extra='test comment').split(None, 2)[:2], id_rsa_pub.getContent().split(None, 2)[:2])
def test_key_generated(self): """ ``create_keypair`` generates a new key pair and writes it locally to ``id_rsa_flocker`` and ``id_rsa_flocker.pub``. """ ssh_config = FilePath(self.mktemp()) configurator = OpenSSHConfiguration(ssh_config_path=ssh_config, flocker_path=None) configurator.create_keypair() id_rsa = ssh_config.child(b"id_rsa_flocker") id_rsa_pub = ssh_config.child(b"id_rsa_flocker.pub") key = Key.fromFile(id_rsa.path) self.assertEqual( # Avoid comparing the comment key.public().toString(type="OPENSSH", extra='test comment').split(None, 2)[:2], id_rsa_pub.getContent().split(None, 2)[:2])
def __init__(self, remote_config, cmd): """Create a new remote Astrisk CLI Protocol instance Keyword Arguments: remote_config The parameters configuring this remote CLI command cmd The CLI command to run """ if REMOTE_ERROR: raise REMOTE_ERROR self.exitcode = -1 self.output = "" self.err = "" self.config = remote_config self.cmd = cmd self.keys = [] identity = self.config.get('identity') if identity: key_path = os.path.expanduser(identity) if os.path.exists(key_path): passphrase = self.config.get('passphrase') self.keys.append(Key.fromFile(key_path, passphrase=passphrase)) known_hosts_file = self.config.get('known_hosts', '~/.ssh/known_hosts') known_hosts_path = FilePath(os.path.expanduser(known_hosts_file)) if known_hosts_path.exists(): self.known_hosts = KnownHostsFile.fromPath(known_hosts_path) else: self.known_hosts = None no_agent = self.config.get('no-agent') if no_agent or 'SSH_AUTH_SOCK' not in os.environ: self.agent_endpoint = None else: self.agent_endpoint = UNIXClientEndpoint(reactor, os.environ['SSH_AUTH_SOCK'])
def readKey(path): try: return Key.fromFile(path) except EncryptedKeyError: passphrase = getpass.getpass(f"{path!r} keyphrase: ") return Key.fromFile(path, passphrase=passphrase)
def __init__(self, privkey): pubkey = '.'.join((privkey, 'pub')) self.privateKeys = {'ssh-rsa': Key.fromFile(privkey)} self.publicKeys = {'ssh-rsa': Key.fromFile(pubkey)}
def read_config(self, config: JsonDict, **kwargs: Any) -> None: self.server_name = config["server_name"] self.server_context = config.get("server_context", None) try: parse_and_validate_server_name(self.server_name) except ValueError as e: raise ConfigError(str(e)) self.pid_file = self.abspath(config.get("pid_file")) self.soft_file_limit = config.get("soft_file_limit", 0) self.daemonize = bool(config.get("daemonize")) self.print_pidfile = bool(config.get("print_pidfile")) self.user_agent_suffix = config.get("user_agent_suffix") self.use_frozen_dicts = config.get("use_frozen_dicts", False) self.serve_server_wellknown = config.get("serve_server_wellknown", False) # Whether we should serve a "client well-known": # (a) at .well-known/matrix/client on our client HTTP listener # (b) in the response to /login # # ... which together help ensure that clients use our public_baseurl instead of # whatever they were told by the user. # # For the sake of backwards compatibility with existing installations, this is # True if public_baseurl is specified explicitly, and otherwise False. (The # reasoning here is that we have no way of knowing that the default # public_baseurl is actually correct for existing installations - many things # will not work correctly, but that's (probably?) better than sending clients # to a completely broken URL. self.serve_client_wellknown = False public_baseurl = config.get("public_baseurl") if public_baseurl is None: public_baseurl = f"https://{self.server_name}/" logger.info("Using default public_baseurl %s", public_baseurl) else: self.serve_client_wellknown = True if public_baseurl[-1] != "/": public_baseurl += "/" self.public_baseurl = public_baseurl # check that public_baseurl is valid try: splits = urllib.parse.urlsplit(self.public_baseurl) except Exception as e: raise ConfigError(f"Unable to parse URL: {e}", ("public_baseurl",)) if splits.scheme not in ("https", "http"): raise ConfigError( f"Invalid scheme '{splits.scheme}': only https and http are supported" ) if splits.query or splits.fragment: raise ConfigError( "public_baseurl cannot contain query parameters or a #-fragment" ) self.extra_well_known_client_content = config.get( "extra_well_known_client_content", {} ) if not isinstance(self.extra_well_known_client_content, dict): raise ConfigError( "extra_well_known_content must be a dictionary of key-value pairs" ) if "m.homeserver" in self.extra_well_known_client_content: raise ConfigError( "m.homeserver is not supported in extra_well_known_content, " "use public_baseurl in base config instead." ) if "m.identity_server" in self.extra_well_known_client_content: raise ConfigError( "m.identity_server is not supported in extra_well_known_content, " "use default_identity_server in base config instead." ) # Whether to enable user presence. presence_config = config.get("presence") or {} self.use_presence = presence_config.get("enabled") if self.use_presence is None: self.use_presence = config.get("use_presence", True) # Custom presence router module # This is the legacy way of configuring it (the config should now be put in the modules section) self.presence_router_module_class = None self.presence_router_config = None presence_router_config = presence_config.get("presence_router") if presence_router_config: ( self.presence_router_module_class, self.presence_router_config, ) = load_module(presence_router_config, ("presence", "presence_router")) # whether to enable the media repository endpoints. This should be set # to false if the media repository is running as a separate endpoint; # doing so ensures that we will not run cache cleanup jobs on the # master, potentially causing inconsistency. self.enable_media_repo = config.get("enable_media_repo", True) # Whether to require authentication to retrieve profile data (avatars, # display names) of other users through the client API. self.require_auth_for_profile_requests = config.get( "require_auth_for_profile_requests", False ) # Whether to require sharing a room with a user to retrieve their # profile data self.limit_profile_requests_to_users_who_share_rooms = config.get( "limit_profile_requests_to_users_who_share_rooms", False, ) # Whether to retrieve and display profile data for a user when they # are invited to a room self.include_profile_data_on_invite = config.get( "include_profile_data_on_invite", True ) if "restrict_public_rooms_to_local_users" in config and ( "allow_public_rooms_without_auth" in config or "allow_public_rooms_over_federation" in config ): raise ConfigError( "Can't use 'restrict_public_rooms_to_local_users' if" " 'allow_public_rooms_without_auth' and/or" " 'allow_public_rooms_over_federation' is set." ) # Check if the legacy "restrict_public_rooms_to_local_users" flag is set. This # flag is now obsolete but we need to check it for backward-compatibility. if config.get("restrict_public_rooms_to_local_users", False): self.allow_public_rooms_without_auth = False self.allow_public_rooms_over_federation = False else: # If set to 'true', removes the need for authentication to access the server's # public rooms directory through the client API, meaning that anyone can # query the room directory. Defaults to 'false'. self.allow_public_rooms_without_auth = config.get( "allow_public_rooms_without_auth", False ) # If set to 'true', allows any other homeserver to fetch the server's public # rooms directory via federation. Defaults to 'false'. self.allow_public_rooms_over_federation = config.get( "allow_public_rooms_over_federation", False ) default_room_version = config.get("default_room_version", DEFAULT_ROOM_VERSION) # Ensure room version is a str default_room_version = str(default_room_version) if default_room_version not in KNOWN_ROOM_VERSIONS: raise ConfigError( "Unknown default_room_version: %s, known room versions: %s" % (default_room_version, list(KNOWN_ROOM_VERSIONS.keys())) ) # Get the actual room version object rather than just the identifier self.default_room_version = KNOWN_ROOM_VERSIONS[default_room_version] # whether to enable search. If disabled, new entries will not be inserted # into the search tables and they will not be indexed. Users will receive # errors when attempting to search for messages. self.enable_search = config.get("enable_search", True) self.filter_timeline_limit = config.get("filter_timeline_limit", 100) # Whether we should block invites sent to users on this server # (other than those sent by local server admins) self.block_non_admin_invites = config.get("block_non_admin_invites", False) # Options to control access by tracking MAU self.limit_usage_by_mau = config.get("limit_usage_by_mau", False) self.max_mau_value = 0 if self.limit_usage_by_mau: self.max_mau_value = config.get("max_mau_value", 0) self.mau_stats_only = config.get("mau_stats_only", False) self.mau_limits_reserved_threepids = config.get( "mau_limit_reserved_threepids", [] ) self.mau_trial_days = config.get("mau_trial_days", 0) self.mau_appservice_trial_days = config.get("mau_appservice_trial_days", {}) self.mau_limit_alerting = config.get("mau_limit_alerting", True) # How long to keep redacted events in the database in unredacted form # before redacting them. redaction_retention_period = config.get("redaction_retention_period", "7d") if redaction_retention_period is not None: self.redaction_retention_period: Optional[int] = self.parse_duration( redaction_retention_period ) else: self.redaction_retention_period = None # How long to keep entries in the `users_ips` table. user_ips_max_age = config.get("user_ips_max_age", "28d") if user_ips_max_age is not None: self.user_ips_max_age: Optional[int] = self.parse_duration(user_ips_max_age) else: self.user_ips_max_age = None # Options to disable HS self.hs_disabled = config.get("hs_disabled", False) self.hs_disabled_message = config.get("hs_disabled_message", "") # Admin uri to direct users at should their instance become blocked # due to resource constraints self.admin_contact = config.get("admin_contact", None) ip_range_blacklist = config.get( "ip_range_blacklist", DEFAULT_IP_RANGE_BLACKLIST ) # Attempt to create an IPSet from the given ranges # Always blacklist 0.0.0.0, :: self.ip_range_blacklist = generate_ip_set( ip_range_blacklist, ["0.0.0.0", "::"], config_path=("ip_range_blacklist",) ) self.ip_range_whitelist = generate_ip_set( config.get("ip_range_whitelist", ()), config_path=("ip_range_whitelist",) ) # The federation_ip_range_blacklist is used for backwards-compatibility # and only applies to federation and identity servers. if "federation_ip_range_blacklist" in config: # Always blacklist 0.0.0.0, :: self.federation_ip_range_blacklist = generate_ip_set( config["federation_ip_range_blacklist"], ["0.0.0.0", "::"], config_path=("federation_ip_range_blacklist",), ) # 'federation_ip_range_whitelist' was never a supported configuration option. self.federation_ip_range_whitelist = None else: # No backwards-compatiblity requrired, as federation_ip_range_blacklist # is not given. Default to ip_range_blacklist and ip_range_whitelist. self.federation_ip_range_blacklist = self.ip_range_blacklist self.federation_ip_range_whitelist = self.ip_range_whitelist # (undocumented) option for torturing the worker-mode replication a bit, # for testing. The value defines the number of milliseconds to pause before # sending out any replication updates. self.replication_torture_level = config.get("replication_torture_level") # Whether to require a user to be in the room to add an alias to it. # Defaults to True. self.require_membership_for_aliases = config.get( "require_membership_for_aliases", True ) # Whether to allow per-room membership profiles through the send of membership # events with profile information that differ from the target's global profile. self.allow_per_room_profiles = config.get("allow_per_room_profiles", True) # The maximum size an avatar can have, in bytes. self.max_avatar_size = config.get("max_avatar_size") if self.max_avatar_size is not None: self.max_avatar_size = self.parse_size(self.max_avatar_size) # The MIME types allowed for an avatar. self.allowed_avatar_mimetypes = config.get("allowed_avatar_mimetypes") if self.allowed_avatar_mimetypes and not isinstance( self.allowed_avatar_mimetypes, list, ): raise ConfigError("allowed_avatar_mimetypes must be a list") self.listeners = [parse_listener_def(x) for x in config.get("listeners", [])] # no_tls is not really supported any more, but let's grandfather it in # here. if config.get("no_tls", False): l2 = [] for listener in self.listeners: if listener.tls: logger.info( "Ignoring TLS-enabled listener on port %i due to no_tls", listener.port, ) else: l2.append(listener) self.listeners = l2 self.web_client_location = config.get("web_client_location", None) # Non-HTTP(S) web client location is not supported. if self.web_client_location and not ( self.web_client_location.startswith("http://") or self.web_client_location.startswith("https://") ): raise ConfigError("web_client_location must point to a HTTP(S) URL.") self.gc_thresholds = read_gc_thresholds(config.get("gc_thresholds", None)) self.gc_seconds = self.read_gc_intervals(config.get("gc_min_interval", None)) self.limit_remote_rooms = LimitRemoteRoomsConfig( **(config.get("limit_remote_rooms") or {}) ) bind_port = config.get("bind_port") if bind_port: if config.get("no_tls", False): raise ConfigError("no_tls is incompatible with bind_port") self.listeners = [] bind_host = config.get("bind_host", "") gzip_responses = config.get("gzip_responses", True) http_options = HttpListenerConfig( resources=[ HttpResourceConfig(names=["client"], compress=gzip_responses), HttpResourceConfig(names=["federation"]), ], ) self.listeners.append( ListenerConfig( port=bind_port, bind_addresses=[bind_host], tls=True, type="http", http_options=http_options, ) ) unsecure_port = config.get("unsecure_port", bind_port - 400) if unsecure_port: self.listeners.append( ListenerConfig( port=unsecure_port, bind_addresses=[bind_host], tls=False, type="http", http_options=http_options, ) ) manhole = config.get("manhole") if manhole: self.listeners.append( ListenerConfig( port=manhole, bind_addresses=["127.0.0.1"], type="manhole", ) ) manhole_settings = config.get("manhole_settings") or {} validate_config( _MANHOLE_SETTINGS_SCHEMA, manhole_settings, ("manhole_settings",) ) manhole_username = manhole_settings.get("username", "matrix") manhole_password = manhole_settings.get("password", "rabbithole") manhole_priv_key_path = manhole_settings.get("ssh_priv_key_path") manhole_pub_key_path = manhole_settings.get("ssh_pub_key_path") manhole_priv_key = None if manhole_priv_key_path is not None: try: manhole_priv_key = Key.fromFile(manhole_priv_key_path) except Exception as e: raise ConfigError( f"Failed to read manhole private key file {manhole_priv_key_path}" ) from e manhole_pub_key = None if manhole_pub_key_path is not None: try: manhole_pub_key = Key.fromFile(manhole_pub_key_path) except Exception as e: raise ConfigError( f"Failed to read manhole public key file {manhole_pub_key_path}" ) from e self.manhole_settings = ManholeConfig( username=manhole_username, password=manhole_password, priv_key=manhole_priv_key, pub_key=manhole_pub_key, ) metrics_port = config.get("metrics_port") if metrics_port: logger.warning(METRICS_PORT_WARNING) self.listeners.append( ListenerConfig( port=metrics_port, bind_addresses=[config.get("metrics_bind_host", "127.0.0.1")], type="http", http_options=HttpListenerConfig( resources=[HttpResourceConfig(names=["metrics"])] ), ) ) self.cleanup_extremities_with_dummy_events = config.get( "cleanup_extremities_with_dummy_events", True ) # The number of forward extremities in a room needed to send a dummy event. self.dummy_events_threshold = config.get("dummy_events_threshold", 10) self.enable_ephemeral_messages = config.get("enable_ephemeral_messages", False) # Inhibits the /requestToken endpoints from returning an error that might leak # information about whether an e-mail address is in use or not on this # homeserver, and instead return a 200 with a fake sid if this kind of error is # met, without sending anything. # This is a compromise between sending an email, which could be a spam vector, # and letting the client know which email address is bound to an account and # which one isn't. self.request_token_inhibit_3pid_errors = config.get( "request_token_inhibit_3pid_errors", False, ) # Whitelist of domain names that given next_link parameters must have next_link_domain_whitelist: Optional[List[str]] = config.get( "next_link_domain_whitelist" ) self.next_link_domain_whitelist: Optional[Set[str]] = None if next_link_domain_whitelist is not None: if not isinstance(next_link_domain_whitelist, list): raise ConfigError("'next_link_domain_whitelist' must be a list") # Turn the list into a set to improve lookup speed. self.next_link_domain_whitelist = set(next_link_domain_whitelist) templates_config = config.get("templates") or {} if not isinstance(templates_config, dict): raise ConfigError("The 'templates' section must be a dictionary") self.custom_template_directory: Optional[str] = templates_config.get( "custom_template_directory" ) if self.custom_template_directory is not None and not isinstance( self.custom_template_directory, str ): raise ConfigError("'custom_template_directory' must be a string") self.use_account_validity_in_account_status: bool = ( config.get("use_account_validity_in_account_status") or False ) self.rooms_to_exclude_from_sync: List[str] = ( config.get("exclude_rooms_from_sync") or [] ) delete_stale_devices_after: Optional[str] = ( config.get("delete_stale_devices_after") or None ) if delete_stale_devices_after is not None: self.delete_stale_devices_after: Optional[int] = self.parse_duration( delete_stale_devices_after ) else: self.delete_stale_devices_after = None
def __init__(self, settings): self.settings = settings self.portal = Portal(OpenRukoRealm(settings)) self.portal.registerChecker(OpenRukoCredentialChecker(settings)) self.privateKeys = {'ssh-rsa': Key.fromFile(settings['private_key'])} self.publicKeys = {'ssh-rsa': Key.fromFile(settings['public_key'])}
def readKey(path): try: return Key.fromFile(path) except EncryptedKeyError: passphrase = getpass.getpass("{!r} keyphrase: ".format(path)) return Key.fromFile(path, passphrase=passphrase)
def __init__(self, privkey): pubkey = ".".join((privkey, "pub")) self.privateKeys = {"ssh-rsa": Key.fromFile(privkey)} self.publicKeys = {"ssh-rsa": Key.fromFile(pubkey)}
def __init__(self, priv_key): pub_key = '%s.pub' % priv_key self.privateKeys = {'ssh-rsa': Key.fromFile(priv_key)} self.publicKeys = {'ssh-rsa': Key.fromFile(pub_key)} self.primes = {2048: [(transport.DH_GENERATOR, transport.DH_PRIME)]}
def getPrivateKey(self): path = self.options['ssh-key'] return defer.succeed(Key.fromFile(path))
def getPrivateKey(self): return succeed(Key.fromFile(key_path))
def ensure_keys(self, reactor): key = Key.fromFile(os.path.expanduser( "~/.vagrant.d/insecure_private_key")) return ensure_agent_has_ssh_key(reactor, key)
def readKey(path): try: return Key.fromFile(path) except EncryptedKeyError: passphrase = getpass.getpass("%r keyphrase: " % (path,)) return Key.fromFile(path, passphrase=passphrase)