Discussion:
[capnproto] Getting client IP... Somehow?
The Cheaterman
2018-03-19 21:59:38 UTC
Permalink
Hi everyone, I hope you're doing great!

As you may already know, I'm doing a small chat system to familiarize
myself with Capnp before I do more ambitious things. I would like to have
some sort of way to implement a banlist on the chat server. I do realize
the whole point of capabilities is to have the same behavior no matter
where the capability is called from. However, I feel like users
(administrators) of server software are used to filter users by IP (when it
comes to that). Alternatively, I'd like to find something unique (but
persistent for a given computer - OS install? hardware? not sure) I could
send during the handshake, to filter undesired users. Basically, I feel
like I need some sort of persistent authentication system that's relatively
hard to refresh, if I can't get access to the IP:port of the user even in
the bootstrap object. I currently managed to hack pycapnp to get a method
called on the bootstrap object when a client connects with IP and port as
arguments, but even if I store them I have no way of knowing which client
calls a given callback (which is a design choice I imagine).

I'd like to know your thoughts on the subject :-) thanks a lot in advance!
--
You received this message because you are subscribed to the Google Groups "Cap'n Proto" group.
To unsubscribe from this group and stop receiving emails from it, send an email to capnproto+***@googlegroups.com.
Visit this group at https://groups.google.com/group/capnproto.
'Kenton Varda' via Cap'n Proto
2018-03-19 23:09:45 UTC
Permalink
If you've already managed to get the information to your bootstrap object,
then the right thing to do from there is to have the bootstrap object add
wrappers to other objects which add knowledge of the IP to them.

For example:

interface Bootstrap {
getRoom @0 (name :Text) -> (room :Room);
}

The getRoom() method might do something like:

getRoom(this, name):
room = rooms.find(name)
return RoomWrapper(room, this.client_ip)

RoomWrapper is a class that implements the `Room` RPC interface for a
specific client, with knowledge of that client's IP. Whenever it receives
an RPC, it can call into the wrapped room object and pass along the IP
address as well.

This is a common design pattern in Cap'n Proto. Since capnp is
capability-based, we like to avoid "ambient authority" (information about
the call context that is not expressed in the parameters).

As for how the bootstrap interface itself gets the info: In C++ there's a
concept of a BootstrapFactory which receives a callback each time a client
connects, and receives the identity of the client. I imagine this isn't
exposed yet in Python, but this would be the way to do it.

-Kenton
Post by The Cheaterman
Hi everyone, I hope you're doing great!
As you may already know, I'm doing a small chat system to familiarize
myself with Capnp before I do more ambitious things. I would like to have
some sort of way to implement a banlist on the chat server. I do realize
the whole point of capabilities is to have the same behavior no matter
where the capability is called from. However, I feel like users
(administrators) of server software are used to filter users by IP (when it
comes to that). Alternatively, I'd like to find something unique (but
persistent for a given computer - OS install? hardware? not sure) I could
send during the handshake, to filter undesired users. Basically, I feel
like I need some sort of persistent authentication system that's relatively
hard to refresh, if I can't get access to the IP:port of the user even in
the bootstrap object. I currently managed to hack pycapnp to get a method
called on the bootstrap object when a client connects with IP and port as
arguments, but even if I store them I have no way of knowing which client
calls a given callback (which is a design choice I imagine).
I'd like to know your thoughts on the subject :-) thanks a lot in advance!
--
You received this message because you are subscribed to the Google Groups
"Cap'n Proto" group.
To unsubscribe from this group and stop receiving emails from it, send an
Visit this group at https://groups.google.com/group/capnproto.
--
You received this message because you are subscribed to the Google Groups "Cap'n Proto" group.
To unsubscribe from this group and stop receiving emails from it, send an email to capnproto+***@googlegroups.com.
Visit this group at https://groups.google.com/group/capnproto.
The Cheaterman
2018-03-21 16:43:05 UTC
Permalink
Hi Kenton!

Thanks for your fantastic answer as usual, I'm sorry I didn't take the time
to respond yesterday but I was implementing the first thing you suggested!
I noticed PyCapnp serves the same instance of the bootstrap object to all
clients (as it is now) so I made the necessary changes to both
re-instanciate the bootstrap object for every cilent, and call its
constructor (and a customizable method) with the client host/port as
arguments, so one can easily track which object is served to which user ;
although in my case - and possibly many others - I will be serving a
"login" interface to the client as bootstrap, and then serve another
capability that will have access to this data as necessary, while tracking
users and rooms in a singleton as I currently do. Why a singleton? Well, I
may need, for instance, all logged in users to be able to communicate with
each other, so I need to keep track of the client capability they send me
at login and their associated nickname (but I probably don't want to keep
track of their host/port there) so that I can serve a client capability to
another user given a username ; or more simply to hold a list of the
available "rooms" so that I can serve them to my clients as requested.
(this might not be the right way to do things in capability-based
environments, so please let me know! I had made an example "new" proto
here, don't know how good it is yet:
https://gist.github.com/Cheaterman/767214e4fd04278e1f1eea11c50a83e7 - Login
is the bootstrap, and Server is the capability that will have access to its
associated client host/port - I would wrap its methods just like you
suggested with some sort of decorator or possibly a motherclass to make
everything look nice and pretty!)

I'm going to be cleaning up the commits to PyCapnp I made to have the
bootstrap class be instanced once per client and have its client host/port
be passed to constructor and optional callback, I would love if you could
take a look (I will try to point the relevant locations) to what I did as
the codebase seems to be based on EzRPC so I imagine (despite some of it
being Cython) you would have some great insights as to what seems to be
done right and wrong!

Speaking of PyCapnp PR's, I will probably be implementing UDP support into
it very soon as well because it seems like it's super simple with KJ? All I
would need is to call bindDatagramPort() instead of listen() on the
NetworkAddress it seems, and I feel like I found the right place, so I'm
going to do that as well relatively soon.

I don't know if I'll be posting the PR tonight (I need to test it more) but
at least you can get an idea how many things you just set in motion with
your great answer :-)

Thanks a lot again! I'll keep you in touch about the PR :-)
Post by 'Kenton Varda' via Cap'n Proto
If you've already managed to get the information to your bootstrap object,
then the right thing to do from there is to have the bootstrap object add
wrappers to other objects which add knowledge of the IP to them.
interface Bootstrap {
}
room = rooms.find(name)
return RoomWrapper(room, this.client_ip)
RoomWrapper is a class that implements the `Room` RPC interface for a
specific client, with knowledge of that client's IP. Whenever it receives
an RPC, it can call into the wrapped room object and pass along the IP
address as well.
This is a common design pattern in Cap'n Proto. Since capnp is
capability-based, we like to avoid "ambient authority" (information about
the call context that is not expressed in the parameters).
As for how the bootstrap interface itself gets the info: In C++ there's a
concept of a BootstrapFactory which receives a callback each time a client
connects, and receives the identity of the client. I imagine this isn't
exposed yet in Python, but this would be the way to do it.
-Kenton
Post by The Cheaterman
Hi everyone, I hope you're doing great!
As you may already know, I'm doing a small chat system to familiarize
myself with Capnp before I do more ambitious things. I would like to have
some sort of way to implement a banlist on the chat server. I do realize
the whole point of capabilities is to have the same behavior no matter
where the capability is called from. However, I feel like users
(administrators) of server software are used to filter users by IP (when it
comes to that). Alternatively, I'd like to find something unique (but
persistent for a given computer - OS install? hardware? not sure) I could
send during the handshake, to filter undesired users. Basically, I feel
like I need some sort of persistent authentication system that's relatively
hard to refresh, if I can't get access to the IP:port of the user even in
the bootstrap object. I currently managed to hack pycapnp to get a method
called on the bootstrap object when a client connects with IP and port as
arguments, but even if I store them I have no way of knowing which client
calls a given callback (which is a design choice I imagine).
I'd like to know your thoughts on the subject :-) thanks a lot in advance!
--
You received this message because you are subscribed to the Google Groups
"Cap'n Proto" group.
To unsubscribe from this group and stop receiving emails from it, send an
Visit this group at https://groups.google.com/group/capnproto.
--
You received this message because you are subscribed to the Google Groups "Cap'n Proto" group.
To unsubscribe from this group and stop receiving emails from it, send an email to capnproto+***@googlegroups.com.
Visit this group at https://groups.google.com/group/capnproto.
The Cheaterman
2018-03-29 15:58:07 UTC
Permalink
Hi again Kenton!

I finally got around to make the PR (wanted to check it extensively first),
it seems to work well: https://github.com/capnproto/pycapnp/pull/169 - I
think it works more or less like the BootstrapFactory you described earlier.

This also allowed me to find and fix a little (dangerous) shortcut I took
when writing the client - message authorship data was basically sent by
client instead of enforced by server, so clients could spoof names if they
hacked the program or did some packet filtering. I indeed used a room
wrapper for this - the interface is instanced once per client, but the
underlying "data-holding" object is instanced once per room (and passed to
the interface constructor when the client joins a room). As of yet, rooms
don't need to access the client IP, but since they now have a reference to
the client object, they can of course get more than just the name.

I've been using all the knowledge this allowed me to get on capnchat:
https://github.com/Cheaterman/capnchat/ - seems to work fine right now :-)
I'm mostly happy with it (except some visual bugs) and will now move on to
more serious networking things (multiplayer games come to mind as mentioned
:-D).

Quick question: as mentioned, my next PR might be about exposing UDP to
Python. I think the most maintained way to establish a connection in
TwoPartyServer/Client on pycapnp is using connection strings. I was
wondering if you think using udp:// prefix in a connection string sounds
like a nice way to expose this feature to users (since we already have
unix:// for UNIX sockets)? Obviously I'm not asking for a change in Capnp
or the C++ side of TwoPartyServer, I'd just implement it at Python level
and use bindDatagramPort() instead of listen() at C++ level as mentioned
earlier.

Thanks again for all your help, and thanks in advance for your answer ^__^
Post by The Cheaterman
Hi Kenton!
Thanks for your fantastic answer as usual, I'm sorry I didn't take the
time to respond yesterday but I was implementing the first thing you
suggested! I noticed PyCapnp serves the same instance of the bootstrap
object to all clients (as it is now) so I made the necessary changes to
both re-instanciate the bootstrap object for every cilent, and call its
constructor (and a customizable method) with the client host/port as
arguments, so one can easily track which object is served to which user ;
although in my case - and possibly many others - I will be serving a
"login" interface to the client as bootstrap, and then serve another
capability that will have access to this data as necessary, while tracking
users and rooms in a singleton as I currently do. Why a singleton? Well, I
may need, for instance, all logged in users to be able to communicate with
each other, so I need to keep track of the client capability they send me
at login and their associated nickname (but I probably don't want to keep
track of their host/port there) so that I can serve a client capability to
another user given a username ; or more simply to hold a list of the
available "rooms" so that I can serve them to my clients as requested.
(this might not be the right way to do things in capability-based
environments, so please let me know! I had made an example "new" proto
here, don't know how good it is yet: https://gist.github.com/Cheaterman/
767214e4fd04278e1f1eea11c50a83e7 - Login is the bootstrap, and Server is
the capability that will have access to its associated client host/port - I
would wrap its methods just like you suggested with some sort of decorator
or possibly a motherclass to make everything look nice and pretty!)
I'm going to be cleaning up the commits to PyCapnp I made to have the
bootstrap class be instanced once per client and have its client host/port
be passed to constructor and optional callback, I would love if you could
take a look (I will try to point the relevant locations) to what I did as
the codebase seems to be based on EzRPC so I imagine (despite some of it
being Cython) you would have some great insights as to what seems to be
done right and wrong!
Speaking of PyCapnp PR's, I will probably be implementing UDP support into
it very soon as well because it seems like it's super simple with KJ? All I
would need is to call bindDatagramPort() instead of listen() on the
NetworkAddress it seems, and I feel like I found the right place, so I'm
going to do that as well relatively soon.
I don't know if I'll be posting the PR tonight (I need to test it more)
but at least you can get an idea how many things you just set in motion
with your great answer :-)
Thanks a lot again! I'll keep you in touch about the PR :-)
Post by 'Kenton Varda' via Cap'n Proto
If you've already managed to get the information to your bootstrap
object, then the right thing to do from there is to have the bootstrap
object add wrappers to other objects which add knowledge of the IP to them.
interface Bootstrap {
}
room = rooms.find(name)
return RoomWrapper(room, this.client_ip)
RoomWrapper is a class that implements the `Room` RPC interface for a
specific client, with knowledge of that client's IP. Whenever it receives
an RPC, it can call into the wrapped room object and pass along the IP
address as well.
This is a common design pattern in Cap'n Proto. Since capnp is
capability-based, we like to avoid "ambient authority" (information about
the call context that is not expressed in the parameters).
As for how the bootstrap interface itself gets the info: In C++ there's a
concept of a BootstrapFactory which receives a callback each time a client
connects, and receives the identity of the client. I imagine this isn't
exposed yet in Python, but this would be the way to do it.
-Kenton
Post by The Cheaterman
Hi everyone, I hope you're doing great!
As you may already know, I'm doing a small chat system to familiarize
myself with Capnp before I do more ambitious things. I would like to have
some sort of way to implement a banlist on the chat server. I do realize
the whole point of capabilities is to have the same behavior no matter
where the capability is called from. However, I feel like users
(administrators) of server software are used to filter users by IP (when it
comes to that). Alternatively, I'd like to find something unique (but
persistent for a given computer - OS install? hardware? not sure) I could
send during the handshake, to filter undesired users. Basically, I feel
like I need some sort of persistent authentication system that's relatively
hard to refresh, if I can't get access to the IP:port of the user even in
the bootstrap object. I currently managed to hack pycapnp to get a method
called on the bootstrap object when a client connects with IP and port as
arguments, but even if I store them I have no way of knowing which client
calls a given callback (which is a design choice I imagine).
I'd like to know your thoughts on the subject :-) thanks a lot in advance!
--
You received this message because you are subscribed to the Google
Groups "Cap'n Proto" group.
To unsubscribe from this group and stop receiving emails from it, send
Visit this group at https://groups.google.com/group/capnproto.
--
You received this message because you are subscribed to the Google Groups "Cap'n Proto" group.
To unsubscribe from this group and stop receiving emails from it, send an email to capnproto+***@googlegroups.com.
Visit this group at https://groups.google.com/group/capnproto.
Continue reading on narkive:
Loading...