Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

restricted access to certain domains #32

Open
tdanecker opened this issue Apr 1, 2023 · 3 comments
Open

restricted access to certain domains #32

tdanecker opened this issue Apr 1, 2023 · 3 comments

Comments

@tdanecker
Copy link

With WASI there's the chance to introduce security guarantees not possible with POSIX. One I'd be really looking forward to, would be to allow only access to certain domain names.

For being able to do so, it would be required to combine name resolution and connect calls. If we would be able to directly call connect with a domain name and (optionally?) the desired address family, we would be able to design a capability that only allows connecting to certain domain names.

Without that, sockets can only be limited to (potentially large) IP ranges, and code calling connect would always have the possibility to connect to any IP address in this range.

As an example: I'd like to use a client library for accessing AWS S3 buckets and I'd like to ensure that this library isn't doing requests to anything else. I'd like to be able to pass a capability to that library that only allows access to the specific domain of the bucket (my_bucket.s3.amazonaws.com) and nothing else.

In a two-step approach with name resolution and address-based connect, there could be a capability that limits the name resolution to only resolve that domain but for the socket I'd need to allow access to all of AWS's IP-ranges for the desired region. There would be nothing that prevents the library from doing any other requests to any of the IPs in those ranges.

If we would be able to directly specify domain names as remote addresses in the socket's connect, we could limit it to only that single domain, and wouldn't need to allow access to the whole set of IP-ranges.

This would be a huge step forward in reducing software supply chain risks.

@badeend
Copy link
Collaborator

badeend commented Apr 2, 2023

Thanks for reaching out!

Even though it is not part of the official spec goals, it is very much a personal goal of mine to facilitate domain name based firewalling. A couple examples of the intended level of permissions can be seen here.

What is a hard goal, however, is that:

Toolchains must be able to provide a POSIX compatible interface on top of the functions introduced in this proposal.

Merging connect & DNS resolution into one call (while from an API point of view much nicer), would immediately break compatibility with all existing software.
A hypothetical alternative could be to provide two distinct APIs: connect-to-ip & connect-to-hostname. However, the same issue then still applies: all existing software would (at least initially) make use of connect-to-ip. This makes the extra security provided by connect-to-hostname "opt-in", which is not a nice track to be on.

In general, the limiting factor is the abstraction level this proposal is targeting. BSD-compatible sockets don't have a notion of domain names. So any solution that applies domainname-based restrictions on sockets is going to be an imperfect solution in some way or another. But that shouldn't stop us from trying to get the most out of it, ofcourse!

The current direction I'm thinking in, is to let the Wasm module "proof" the relationship between a domain name and an ip address to the embedder by performing a DNS lookup. Example:

  1. The embedder has a rule that allows TCP connections to the domain name "example.com" on port 22.
  2. The Wasm module calls resolve-addresses to perform a lookup of "example.com", which resolves to 123.1.2.3
  3. The embedder remembers the address in a dictionary, mapping resolved addresses back to their domain name(s).
  4. The Wasm module calls connect(sock, network, Ipv4SocketAddress("123.1.2.3", 22))
  5. The embedder looks up the ip address in its dictionary and finds that the address is associated with "example.com" (bullet 3). Additionally, the embedder also knows there's a rule allowing connections to "example.com" on port 22 (bullet 1). Therfore the connect call may continue. Otherwise it fails.

A while ago I made a POC to demonstrate this principle.

This workflow is based on the presumption that applications usually perform a DNS lookup using the WASI-provided resolve-addresses function before performing the connect. It does not work for applications that ship their own DNS client.

I don't know if this methodology is sound. There might be edge-cases.

Other solutions are also welcome, ofcourse.
Let me know what you think!

@tdanecker
Copy link
Author

Thanks a lot for sharing your thoughts on this!

I think it makes sense to think about network capabilities in two different ways. For libraries which are not aware of capabilities, the network capability is more like a static link-time capability. It has no way to pass it around, and any call to connect just implicitly passes that static network capability to it, as the caller isn't even aware of that additional parameter. I really like your ideas here as I think that's probably the maximum that is achievable in this context. The network capability would probably be scoped at the granularity of modules. Once some code calls resolve-addresses on a permitted domain name, the network capability acquires also permissions for the resolved IPs. By this, any code in that module also is permitted to connect to those IPs from that point of time on.

I'd also like to keep the door open for a dynamic network capability, though. One that can be explicitly passed around by code that is aware of the existence of such a capability. It would also have a method to derive a more restricted capability from it (e.g. network2 = derive-restricted(network, ["my_bucket.s3.amazonaws.com"]) or something in that regard) and it would be more similar to a directory file descriptor in the WASI filesystem APIs. wasmtime/docs/WASI-capabilities.md also hints at something that would go more into that direction.

The relationship between those two is probably quite similar to the relationship between open and open-at.

Does this make sense?

@badeend
Copy link
Collaborator

badeend commented Apr 3, 2023

I'd also like to keep the door open for a dynamic network capability, though.

I don't think the current proposal closes any of those doors :)
The Component Model already ensures that any WASI interface is fully virtualizable.
In my mind, the exact implementation of attenuation (domain-based, ip-based, portnumber-based, protocol-based, ...) is better handled by each individual Wasm module itself. At least for the MVP.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants