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.
Asked
Active
Viewed 323 times
1
-
Hi, I have a similar need now. Have you came up with any solution/suggestion? – ylcnky Mar 25 '22 at 21:09
-
@ylcnky I put that on hold since I am the only consumer of my rpyc service. You can look at [SSL based authenticators](https://rpyc.readthedocs.io/en/latest/api/utils_authenticators.html#rpyc.utils.authenticators.SSLAuthenticator). But at high level I feel we need to override some rpyc internal methods on the client side and send the auth info. For example this might be a qualified [entry point](https://github.com/tomerfiliba-org/rpyc/blob/7ea2d24ab70840fb1324c0d5c8b052fcaca7f635/rpyc/core/stream.py#L121) since ``rpyc.connect`` creates a SocketStream object. – m0hithreddy Mar 26 '22 at 10:33
-
@ylcnky I got curious and tested me theory, I posted an answer. I hope it will help you. – m0hithreddy Mar 27 '22 at 14:03
1 Answers
5
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

m0hithreddy
- 1,752
- 1
- 10
- 17