Looking for an IPC crate

Long, long ago in a galaxy far, far away I wrote a multiprocess application with the processes communicating over Unix, not Linux, pipes. (I told you it was a long time ago.) Since some of the processes ran untrusted code, I precluded name guessing or name learning by using anonymous pipes.

I'm now starting a project that could use that pattern, but I don't know which IPC crate to use. I've got some preliminary code running with parity-tokio-ipc, but it doesn't appear to support anonymous pipes. I also tried ipipe, but it doesn't look like it supports async. Other crates I looked at lacked various features. I would like

  • Async support
  • Cross platform, Linux and Mac, Windows would be nice
  • Anonymous pipes, which I may have to give up

What crate should I use for IPC?

Have a look at ZeroMQ. Its API and semantics aren't perfect, but it should meet your needs given the criteria you provided.

I may need more sleep, but the only methods I see for connecting or binding to the socket take a name. Am I missing an anonymous version?

If you're referring to the string that needs to be supplied as an argument to those fns, for bind I generally use "tcp://*:PORT" (for some specific port PORT) and for the paired connect I provide something like "tcp://localhost:PORT". Of course that assumes that both sides are running on the same host. If the hosts differ then you'll need to supply an IP address in the argument to connect() IIRC.

While not technically anonymous, it comes pretty close. Does that help?

I don't think it helps. Let me explain.

I start with a single process that will spawn several other processes, each of which talks to several of the others. Those child processes will spawn other processes running untrusted code. I want that untrusted code to be able to talk only the the process that spawned it. I can guarantee that with anonymous pipes but not with anything that takes a name. As a fallback, I can use hard to guess names for the pipes.

You could emulate the "child processes can only talk to parent process" by spawning each child process with a secret (the nature of which depend on how much secrecy you want) and requiring any incoming connection to provide that secret before accepting it.

There are many such ideas that can work, but there's always the risk that the secret will leak. Anonymous pipes don't rely on secrets, which is why I prefer using them.

I'm on my phone so I'll add details later, but I also use ZeroMQ for IPC with great results. You can use named IPC sockets, like ipc://@myprog-service, which you might be able to exploit by eg. adding a random string. I do note your point about passing secrets around, so I'll see if there's a better way.

Then it might be useful to point out that e.g. sockets using the REQ/REP protocol are intended to be used as a pair, and so are not so easily listened in on.

In addition, when it's important to keep a secret safe while being transmitted, you can always encrypt the payload at the cost of having to solve key distribution.

Servo created ipc-channel for communication internally. It can both be used within a single process as if it is a regular mpsc channel or between processes. The content processes of servo are running untrusted code, so it is safe to assume that ipc-channel is written for communication with untrusted processes.

https://docs.rs/ipc-channel/0.15.0/ipc_channel/ipc/fn.channel.html

2 Likes

I definitely wouldn't rely on this, especially for security — any REP socket can be connected to by multiple DEALER sockets, and similarly for REQ/ROUTER.

They can be, but only when done explicitly i.e. given the setup work it won't happen by accident, which leaves malice.

And also, in recognition of that fact there's the 2nd paragraph of my response about encrypting the payload.

1 Like

I looked at ipc-channel. It's got the best API for my needs, but it doesn't appear to support async. I also looked at the source code to make sure it wasn't using names under the covers. I found two places that did, but I can't tell if that's just an option.

I'm currently looking at sanbox-ipc. It appears to meet my requirements, but it's hardly used, and the last update was 3 years ago.

I'm still looking.

I built a crate to do just this last year. You may need to update it to use tokio ^1.0 (which shouldn't take much work): GitHub - tbraun96/tokio-agnostic-uds: A platform-agnostic async UDS implementation

It might be worth the effort to convert if it supported unnamed connections. Can that be done?

See the example here (from the same crate). All you need to do is bind/connect to a sock file (e.g., temp.sock)

Right. That's where I saw the name being used. What if untrusted code uses that name to connect?

See the first answer here that suggests the use of UDS over TCP for IPC coms and security

Essentially, the security would be controlled by file permissions. You can lock-down access to the sockfile by user, program, etc. This would just require some pre-configuration. You can also implement a thin protocol over your communications to help weed-out any bad input, but seems unnecessary if you limit access to the programs you expect to use the sockfile

The best way to thwart an attack is to make it inexpressible, which anonymous pipes do for the threat posed by name guessing. The next best thing is to follow the advice of several of the posters to this thread. I may end up doing that, but I'm not willing to give up on inexpressibility just yet.