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

Arbitrary "Context" access for store metadata #255

Closed
dangerousplay opened this issue Feb 1, 2020 · 8 comments · Fixed by #642
Closed

Arbitrary "Context" access for store metadata #255

dangerousplay opened this issue Feb 1, 2020 · 8 comments · Fixed by #642
Milestone

Comments

@dangerousplay
Copy link

Feature Request

Very good project, awesome work. It would be nice if we could pass arbitrary metadata (context) for the request context.

Motivation

I have a use case (Spire the SPIFFE referential implementation use it) where I need to get raw information about the connection such as peer process id (PID) for security reasons.

Proposal

The interceptor could have more access about the information of the connection, or have a way to pass through a Extensions metadata...

Alternatives

I have looked at but could not reason a way to use #177

Thank you.

@dangerousplay dangerousplay changed the title Arbitrary "Context" access Arbitrary "Context" access for store metadata Feb 1, 2020
@mattsre
Copy link
Contributor

mattsre commented Feb 1, 2020

Looking at #177 briefly, its looks like that should cover your use case although I'm also not sure how to necessarily implement what you are proposing either. I'm not sure there is any documentation around the current extensions api, so that could be an action item from this.

@dangerousplay
Copy link
Author

That would help a Lot, a simples example. I'm trying to figure out a way of do this, besides golang implementation uses it's own context. I'm open to work on this.

@LucioFranco
Copy link
Member

@dangerousplay hi! Thanks for the kind words. Can you explain more of your use case? What type of transport layer are you using? Is this server side or also client side?

@dangerousplay
Copy link
Author

@LucioFranco Hi, sure. Our use case is more specific but this feature will be too good. In a simple way, we are using Tonic with Unix Domain Sockets as a IPC. We get the process id from a raw socket file descriptor, using a syscall on Linux getsockopt and on NetBSD, OpenBSD, FreeBSD using getpeereid. With the PID we use it on Linux to watch the process on procfs and so, we get more information about the other side of the connection. After knowing the peer connection we use it to authenticate the peer or not, based on other checks, a process also called Spiffe attestation.

A simple code using the Tonic UDS example:

[cfg(unix)]
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let path = "/tmp/tonic/helloworld";

    tokio::fs::create_dir_all(Path::new(path).parent().unwrap()).await?;

    let mut uds = UnixListener::bind(path)?;

    let greeter = MyGreeter::default();

    Server::builder()
        .add_service(GreeterServer::new(greeter))
        .serve_with_incoming(uds.incoming().map(|a| {
            let fd = a.as_ref().unwrap().as_raw_fd();

            println!("Got the raw file descriptor: {}", fd);

            let ba = nix::sys::socket::getsockopt(fd, sockopt::PeerCredentials).unwrap();
            println!("Got the peer credentials: {:#?}", ba);

            let p = procfs::process::Process::new(ba.pid()).unwrap();

            println!("Process info: {:#?}", p);

            a
        }).map_ok(unix::UnixStream))
        .await?;

    Ok(())
}

The process in our flow occur on the server side.

@amorken
Copy link

amorken commented Sep 4, 2020

Hi!

I'd just like to chime in and +1 this. If there's interest in extending the API to support something like this we'd be open to contributing code too. It would be very very handy to have something like's the http crate's Extension struct optionally attached to a Tonic request.

Our use case is related to authn/authz too, as we decode incoming JWT tokens, and from those we deduce a number of properties about the calling principal including the capabilities and access scope. Those properties are then relevant for use later in request processing, when business logic makes decisions about which data elements to permit the caller to access.

The alternatives end up being either encoding this in request metadata in an interceptor, or "manually" intercepting each call during regular processing and determining this information before passing along both this and the request itself to business logic. One is straight up ugly, the other is also kind of clumsy, so we are interested in other options.

A simple approach could be to attach and expose a http::Extensions struct in the tonic Request interface. Thoughts?

Thanks,
-AndersM

@LucioFranco
Copy link
Member

For now the workaround should be this https://github.com/LucioFranco/tonic-context-example/blob/master/src/main.rs you can use tokio::task_local! to achieve similar things without needing to change the tonic api.

@davidpdrsn
Copy link
Member

Would fixing this be a matter of "just" exposing the Extensions thats already on tonic::Request https://github.com/hyperium/tonic/blob/master/tonic/src/request.rs#L15? I think the only place you'd be able to insert values visible to where ever RPC the request gets routed to would be from interceptors where you get an owned tonic::Request<()>.

It doesn't seem like that would help with the use-case described here since when calling serve_with_incoming there is no request yet to add extensions to. Maybe a task local would work better here?

@LucioFranco
Copy link
Member

Yeah, though I think we'd want to wrap it in our own type, just like how we do it for Request/Response?

davidpdrsn added a commit that referenced this issue May 13, 2021
Adds `tonic::Extensions` which is a newtype around `http::Extensions`.

Extensions can be set by interceptors with `Request::extensions_mut` and
retrieved from RPCs with `Request::extensions`. Extensions can also be
set in tower middleware and will be carried through to the RPC.

Fixes #255
davidpdrsn added a commit that referenced this issue May 13, 2021
Adds `tonic::Extensions` which is a newtype around `http::Extensions`.

Request extensions can be set by interceptors with
`Request::extensions_mut` and retrieved from RPCs with
`Request::extensions`. Extensions can also be set in tower middleware
and will be carried through to the RPC.

Since response extensions cannot be set by interceptors the main use
case is to set them in RPCs and retrieve them in tower middlewares.
Figured that might be useful.

Fixes #255
davidpdrsn added a commit that referenced this issue May 13, 2021
Adds `tonic::Extensions` which is a newtype around `http::Extensions`.

Request extensions can be set by interceptors with
`Request::extensions_mut` and retrieved from RPCs with
`Request::extensions`. Extensions can also be set in tower middleware
and will be carried through to the RPC.

Since response extensions cannot be set by interceptors the main use
case is to set them in RPCs and retrieve them in tower middlewares.
Figured that might be useful.

Fixes #255
davidpdrsn added a commit that referenced this issue May 13, 2021
Adds `tonic::Extensions` which is a newtype around `http::Extensions`.

Request extensions can be set by interceptors with
`Request::extensions_mut` and retrieved from RPCs with
`Request::extensions`. Extensions can also be set in tower middleware
and will be carried through to the RPC.

Since response extensions cannot be set by interceptors the main use
case is to set them in RPCs and retrieve them in tower middlewares.
Figured that might be useful.

Fixes #255
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

Successfully merging a pull request may close this issue.

5 participants