Exemplo n.º 1
0
        async def start(self):
            """
            Start offering and selling data encryption keys in the background.
            """
            assert self._run_loop is None

            self.log.info(
                'Starting key rotation every {interval} seconds for api_id="{api_id}" ..',
                interval=hl(self._interval),
                api_id=hl(uuid.UUID(bytes=self._api_id)))

            self.running = True

            self._run_loop = LoopingCall(
                lambda: ensureDeferred(self._rotate()))
            self._started = self._run_loop.start(self._interval)

            return self._started
Exemplo n.º 2
0
        async def start(self):
            """
            Start offering and selling data encryption keys in the background.
            """
            assert not self.running

            self.log.info(
                'Starting key rotation every {interval} seconds for api_id="{api_id}" ..',
                interval=hl(self._interval),
                api_id=hl(uuid.UUID(bytes=self._api_id)))
            self.running = True

            async def rotate_with_interval():
                while self.running:
                    await self._rotate()
                    await asyncio.sleep(self._interval)

            asyncio.create_task(rotate_with_interval())
Exemplo n.º 3
0
    async def authenticate(realm, authid, details):
        """
        this is our dynamic authenticator procedure that will be called by Crossbar.io
        when a session is authenticating
        """
        log.info(
            'authenticate(realm="{realm}", authid="{authid}", details={details}) {func}',
            realm=hl(realm),
            authid=hl(authid),
            details=details,
            func=hltype(create_rlink_authenticator),
        )

        assert ('authmethod' in details)
        assert (details['authmethod'] == 'cryptosign')
        assert ('authextra' in details)
        assert ('pubkey' in details['authextra'])

        pubkey = details['authextra']['pubkey']
        log.info(
            'authenticating session using realm="{realm}", pubkey={pubkey} .. {func}',
            realm=hl(realm),
            pubkey=hl(pubkey),
            func=hltype(create_rlink_authenticator),
        )

        if pubkey in pubkey_to_principals:
            principal = pubkey_to_principals[pubkey]
            auth = {
                'pubkey': pubkey,
                'realm': principal['realm'],
                'authid': principal['authid'],
                'role': principal['role'],
                'extra': principal['extra'],
                'cache': True
            }

            # Note: with WAMP-cryptosign, even though a client may or may not request a `realm`, but in any case, the
            # effective realm the client is authenticated will be returned in the principal `auth['role']` (!)
            effective_realm = auth['realm']

            log.info(
                'found valid principal authid="{authid}", authrole="{authrole}", realm="{realm}" matching given client public key {func}',
                func=hltype(create_rlink_authenticator),
                authid=hl(auth['authid']),
                authrole=hl(auth['role']),
                realm=hl(effective_realm),
            )

            # only now that we know the effective realm a client is to be joined to (see above), maybe active (start)
            # the desired application realm to let the client join to subsequently
            # await _maybe_activate_realm(controller, effective_realm)

            return auth
        else:
            msg = 'no principal with matching public key 0x{}'.format(pubkey)
            log.warn(msg)
            raise ApplicationError('com.example.no_such_user', msg)
Exemplo n.º 4
0
class RLinkRemoteSession(BridgeSession):
    """
    This session is the remote leg of the router-to-router link.
    """

    log = make_logger()

    IS_REMOTE_LEG = True

    # directory in which events are flowing (published via this session
    DIR = hl('from local to remote', color='yellow', bold=True)

    def __init__(self, config):
        BridgeSession.__init__(self, config)

        # import here to resolve import dependency issues
        from crossbar.worker.router import RouterController

        self._subs = {}
        self._rlink_manager: RLinkManager = self.config.extra['rlink_manager']
        self._router_controller: RouterController = self._rlink_manager.controller

    # FIXME: async? see below
    def onConnect(self):
        self.log.info('{func}() ...', func=hltype(self.onConnect))

        authid = self.config.extra.get('authid', None)
        authrole = self.config.extra.get('authrole', None)
        authextra = self.config.extra.get('authextra', {})

        # FIXME: use cryptosign-proxy
        authmethods = ['cryptosign']

        # use WorkerController.get_public_key to call node controller
        # FIXME: the following does _not_ work with onConnect (?!)
        # _public_key = await self._router_controller.get_public_key()

        def actually_join(_public_key):
            authextra.update({
                # forward the client pubkey: this allows us to omit authid as
                # the router can identify us with the pubkey already
                'pubkey': _public_key,

                # not yet implemented. a public key the router should provide
                # a trustchain for its public key. the trustroot can eg be
                # hard-coded in the client, or come from a command line option.
                'trustroot': None,

                # not yet implemented. for authenticating the router, this
                # challenge will need to be signed by the router and send back
                # in AUTHENTICATE for client to verify. A string with a hex
                # encoded 32 bytes random value.
                'challenge': None,

                # https://tools.ietf.org/html/rfc5929
                'channel_binding': 'tls-unique'
            })

            self.log.info(
                '{func} joining with realm="{realm}", authmethods={authmethods}, authid="{authid}", authrole="{authrole}", authextra={authextra}',
                func=hltype(self.onConnect),
                realm=hlval(self.config.realm),
                authmethods=hlval(authmethods),
                authid=hlval(authid),
                authrole=hlval(authrole),
                authextra=authextra)

            self.join(self.config.realm,
                      authmethods=authmethods,
                      authid=authid,
                      authrole=authrole,
                      authextra=authextra)

        res = self._rlink_manager._controller.get_public_key()
        res.addCallback(actually_join)
        self.log.info('{func}() done (res={res}).',
                      func=hltype(self.onConnect),
                      res=res)
        return res

    # FIXME: async? see below
    def onChallenge(self, challenge):
        self.log.debug('{func}(challenge={challenge})',
                       func=hltype(self.onChallenge),
                       challenge=challenge)

        if challenge.method == 'cryptosign':
            # alright, we've got a challenge from the router.

            # sign the challenge with our private key.
            channel_id_type = 'tls-unique'
            channel_id_map = self._router_controller._transport.transport_details.channel_id
            if channel_id_type in channel_id_map:
                channel_id = channel_id_map[channel_id_type]
            else:
                channel_id = None
                channel_id_type = None

            # use WorkerController.get_public_key to call node controller
            # FIXME: await?
            signed_challenge = self._router_controller.sign_challenge(
                challenge, channel_id, channel_id_type)

            # send back the signed challenge for verification
            return signed_challenge

        else:
            raise Exception(
                'internal error: we asked to authenticate using wamp-cryptosign, but now received a challenge for {}'
                .format(challenge.method))

    @inlineCallbacks
    def onJoin(self, details):
        self.log.debug('{klass}.onJoin(details={details})',
                       klass=self.__class__.__name__,
                       details=details)

        assert self.config.extra and 'on_ready' in self.config.extra
        assert self.config.extra and 'other' in self.config.extra

        local = self.config.extra['other']
        assert isinstance(local, RLinkLocalSession)
        local._tracker.connected = True

        self._exclude_authid = self.config.extra.get('exclude_authid', None)
        self._exclude_authrole = self.config.extra.get('exclude_authrole',
                                                       None)

        # setup remote->local event forwarding
        forward_events = self.config.extra.get('forward_events', False)
        if forward_events:
            yield self._setup_event_forwarding(local)

        # setup remote->local invocation forwarding
        forward_invocations = self.config.extra.get('forward_invocations',
                                                    False)
        if forward_invocations:
            yield self._setup_invocation_forwarding(local)

        self.log.info(
            '{klass}.onJoin(): rlink remote session ready (forward_events={forward_events}, forward_invocations={forward_invocations}, realm={realm}, authid={authid}, authrole={authrole}, session={session}) {method}',
            klass=self.__class__.__name__,
            method=hltype(RLinkRemoteSession.onJoin),
            forward_events=hluserid(forward_events),
            forward_invocations=hluserid(forward_invocations),
            realm=hluserid(details.realm),
            authid=hluserid(details.authid),
            authrole=hluserid(details.authrole),
            session=hlid(details.session))

        # we are ready!
        on_ready = self.config.extra.get('on_ready', None)
        if on_ready and not on_ready.called:
            self.config.extra['on_ready'].callback(self)

    @inlineCallbacks
    def onLeave(self, details):
        # When the rlink is going down, make sure to unsubscribe to
        # all events that are subscribed on the local-leg.
        # This avoids duplicate events that would otherwise arrive
        # See: https://github.com/crossbario/crossbar/issues/1916
        for k, v in self._subs.items():
            if v['sub'].active:
                yield v['sub'].unsubscribe()

        self._subs = {}

        self.config.extra['other']._tracker.connected = False
        self.log.warn(
            '{klass}.onLeave(): rlink remote session left! (realm={realm}, authid={authid}, authrole={authrole}, session={session}, details={details}) {method}',
            klass=self.__class__.__name__,
            method=hltype(RLinkLocalSession.onLeave),
            realm=hluserid(self.config.realm),
            authid=hluserid(self._authid),
            authrole=hluserid(self._authrole),
            session=hlid(self._session_id),
            details=details)

        BridgeSession.onLeave(self, details)
Exemplo n.º 5
0
class RLinkLocalSession(BridgeSession):
    """
    This session is the local leg of the router-to-router link and runs embedded inside the local router.
    """

    log = make_logger()

    IS_REMOTE_LEG = False

    # direction in which events are flowing (published) via this session
    DIR = hl('from remote to local', color='yellow', bold=True)

    def onConnect(self):
        self.log.info('{klass}.onConnect()', klass=self.__class__.__name__)
        # _BridgeSession.onConnect(self)
        authextra = {'rlink': self.config.extra['rlink']}
        self.join(self.config.realm,
                  authid=self.config.extra['rlink'],
                  authextra=authextra)
        self._tracker = self.config.extra['tracker']

    @inlineCallbacks
    def onJoin(self, details):
        assert self.config.extra and 'on_ready' in self.config.extra
        assert self.config.extra and 'other' in self.config.extra

        remote = self.config.extra['other']
        assert isinstance(remote, RLinkRemoteSession)

        self._exclude_authid = self.config.extra.get('exclude_authid', None)
        self._exclude_authrole = self.config.extra.get('exclude_authrole',
                                                       None)

        # setup local->remote event forwarding
        forward_events = self.config.extra.get('forward_events', False)
        if forward_events:
            yield self._setup_event_forwarding(remote)

        # setup local->remote invocation forwarding
        forward_invocations = self.config.extra.get('forward_invocations',
                                                    False)
        if forward_invocations:
            yield self._setup_invocation_forwarding(remote)

        self.log.debug(
            'Router link local session ready (forward_events={forward_events}, forward_invocations={forward_invocations}, realm={realm}, authid={authid}, authrole={authrole}, session={session}) {method}',
            method=hltype(RLinkLocalSession.onJoin),
            forward_events=hluserid(forward_events),
            forward_invocations=hluserid(forward_invocations),
            realm=hluserid(details.realm),
            authid=hluserid(details.authid),
            authrole=hluserid(details.authrole),
            session=hlid(details.session))

        on_ready = self.config.extra.get('on_ready', None)
        if on_ready and not on_ready.called:
            self.config.extra['on_ready'].callback(self)

    def onLeave(self, details):
        self.log.warn(
            'Router link local session down! (realm={realm}, authid={authid}, authrole={authrole}, session={session}, details={details}) {method}',
            method=hltype(RLinkLocalSession.onLeave),
            realm=hluserid(self.config.realm),
            authid=hluserid(self._authid),
            authrole=hluserid(self._authrole),
            details=details,
            session=hlid(self._session_id))

        BridgeSession.onLeave(self, details)