m0hithreddy
m0hithreddy

Reputation: 1829

How to implement username password authenticator for RPyC server

I am trying to secure my RpyC server connections through username and password. The documentation indeed shows an example, but it is too brief. No details were given on how exactly the password is passed from the client-side. Anyone figured out how to do that? Thanks in advance.

Upvotes: 1

Views: 599

Answers (1)

m0hithreddy
m0hithreddy

Reputation: 1829

Answering my own question:

I had to override some internal methods of RPyC on the client side to achieve the desired behaviour. I don't know if much cleaner solution exists, but this seems to be a plausible one.

Server:

import rpyc
from rpyc.utils.authenticators import AuthenticationError

def magic_word_authenticator(sock):
    if sock.recv(5).decode() != "Ma6ik":
        raise AuthenticationError("wrong magic word")
    return sock, None

class SecuredService(rpyc.Service):

    def exposed_secured_op(self):
        return 'Secret String'

rpyc.ThreadedServer(
    service=SecuredService, hostname='localhost',
    port=18812, authenticator=magic_word_authenticator
).start()

Client:

import rpyc
import traceback


class AuthSocketStream(rpyc.SocketStream):

    @classmethod
    def connect(cls, *args, authorizer=None, **kwargs):
        stream_obj =  super().connect(*args, **kwargs)

        if callable(authorizer):
            authorizer(stream_obj.sock)

        return stream_obj


def rpyc_connect(host, port, service=rpyc.VoidService, config={}, ipv6=False, keepalive=False, authorizer=None):
    s = AuthSocketStream.connect(
            host, port, ipv6=ipv6, keepalive=keepalive,
            authorizer=authorizer
    )

    return rpyc.connect_stream(s, service, config)

print('With correct authorizer')

conn1 = rpyc_connect(
        'localhost', 18812, authorizer=lambda sock: sock.send('Ma6ik'.encode())
)

print(conn1.root.secured_op())

print('With wrong authorizer')

conn2 = rpyc_connect(
        'localhost', 18812, authorizer=lambda sock: sock.send('Invalid'.encode())
)

try:
    conn2.root
except Exception:
    print(traceback.format_exc())


print('With no authorizer')

conn3 = rpyc_connect(
        'localhost', 18812
)

try:
    conn3.root
except Exception:
    print(traceback.format_exc())

Client Console Log:

With correct authorizer
Secret String
With wrong authorizer
Traceback (most recent call last):
  File "/home/client.py", line 40, in <module>
    conn2.root
  File "/usr/lib/python3.10/site-packages/rpyc/core/protocol.py", line 507, in root
    self._remote_root = self.sync_request(consts.HANDLE_GETROOT)
  File "/usr/lib/python3.10/site-packages/rpyc/core/protocol.py", line 474, in sync_request
    return self.async_request(handler, *args, timeout=timeout).value
  File "/usr/lib/python3.10/site-packages/rpyc/core/async_.py", line 101, in value
    self.wait()
  File "/usr/lib/python3.10/site-packages/rpyc/core/async_.py", line 48, in wait
    self._conn.serve(self._ttl)
  File "/usr/lib/python3.10/site-packages/rpyc/core/protocol.py", line 387, in serve
    data = self._channel.poll(timeout) and self._channel.recv()
  File "/usr/lib/python3.10/site-packages/rpyc/core/channel.py", line 55, in recv
    header = self.stream.read(self.FRAME_HEADER.size)
  File "/usr/lib/python3.10/site-packages/rpyc/core/stream.py", line 260, in read
    raise EOFError("connection closed by peer")
EOFError: connection closed by peer

With no authorizer
Traceback (most recent call last):
  File "/home/client.py", line 52, in <module>
    conn3.root
  File "/usr/lib/python3.10/site-packages/rpyc/core/protocol.py", line 507, in root
    self._remote_root = self.sync_request(consts.HANDLE_GETROOT)
  File "/usr/lib/python3.10/site-packages/rpyc/core/protocol.py", line 474, in sync_request
    return self.async_request(handler, *args, timeout=timeout).value
  File "/usr/lib/python3.10/site-packages/rpyc/core/async_.py", line 101, in value
    self.wait()
  File "/usr/lib/python3.10/site-packages/rpyc/core/async_.py", line 48, in wait
    self._conn.serve(self._ttl)
  File "/usr/lib/python3.10/site-packages/rpyc/core/protocol.py", line 387, in serve
    data = self._channel.poll(timeout) and self._channel.recv()
  File "/usr/lib/python3.10/site-packages/rpyc/core/channel.py", line 55, in recv
    header = self.stream.read(self.FRAME_HEADER.size)
  File "/usr/lib/python3.10/site-packages/rpyc/core/stream.py", line 260, in read
    raise EOFError("connection closed by peer")
EOFError: connection closed by peer

Upvotes: 5

Related Questions