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

Access to TLS server certificate? #1240

Closed
spikebike opened this issue May 13, 2017 · 8 comments
Closed

Access to TLS server certificate? #1240

spikebike opened this issue May 13, 2017 · 8 comments

Comments

@spikebike
Copy link

spikebike commented May 13, 2017

I have a go protobuf implementation where both ends of a TLS connection have access to the peer's certificate so they know who they are talking to and can act accordingly. Works great.

With protobufs:

conn, err := tls.Dial("tcp", "127.0.0.1:8000", &config)
state := conn.ConnectionState()
for _, v := range state.PeerCertificates {
      fmt.Println("Client: Server public key is:")
      fmt.Println(x509.MarshalPKIXPublicKey(v.PublicKey))
}

Very straight forward: dial returns a connection, conn.ConnectionState returns a state, the state includes the PeerCertificates.

http://www.grpc.io/docs/guides/auth.html mentioned "channels credentials", "such as SSL credentials" used for authentication, that's exactly what I want to do.

grpc.Dial returns ClientConn, there does't appear to b a conn.ConnectionState.

Any suggestions on how to get the server's public key from the client?

On the server side I've managed to get this to work, mentioned at:
#111

peer, ok := peer.FromContext(ctx)
if ok {
   tlsInfo := peer.AuthInfo.(credentials.TLSInfo)
   v := tlsInfo.State.PeerCertificates
   for _, v := range tlsInfo.State.PeerCertificates {
         fmt.Println("Client: Server public key is:")
         fmt.Println(x509.MarshalPKIXPublicKey(v.PublicKey))
    }
}

Unfortunately while that works on the server side, I've not gotten it to work from the client side.

@MakMukhi
Copy link
Contributor

On the client-side there's a call options to access the Peer object.
Note: that a ClientConn doesn't represent that actual transport connected to a server. Each clientconn can have more that one transports depending on the load-balancer. A transport is selected at the time when an RPC is made. Therefore, the peer object is accessible via CallOptions.

@spikebike
Copy link
Author

spikebike commented May 13, 2017

Right, that's in my example, "peer, ok := peer.FromContext(ctx)", but the problem is that the call fails when ctx is context.Background. So how can get access to the peer from the ctx?

Can you explain a bit more exactly what you mean by "the peer object is accessible via CallOptions"?

@MakMukhi
Copy link
Contributor

As you mentioned your example is on the server-side. On the client-side you'd use a call option to see what your peer was:

peer := new(Peer)
res, err := client.RPCCall(ctx, req, grpc.Peer(peer)) // Unary call.

This gives you peer information(about the server on the client-side) once the call is completed.

If you want to do some sort of check on server's creds on the client-side, right after the handshake, then, you could wrap around TLS creds in your own struct and override the client and server handshake methods. Not sure though if I understand your use-case completely.

@spikebike
Copy link
Author

spikebike commented May 13, 2017

Ah, thanks. I do want the client to check the server cert, and the server to check the client cert. Much like ssh. I think the overriding the handshake methods is best. Thanks, that's a very helpful suggestion.

@MakMukhi
Copy link
Contributor

Glad that helped. As a reference here's the default TLS creds implementation of gRPC-go (client-side): https://github.com/grpc/grpc-go/blob/master/credentials/credentials.go#L202

@spikebike
Copy link
Author

I'm going to try to modify the grpc/examples/route_guide example to override the handshake like mentioned at #111 (comment)

Seems like that would allow me to do what I want.

@MakMukhi
Copy link
Contributor

Closing this, feel free to comment if there's more information needed. We can re-open too, if need be.

@spikebike
Copy link
Author

spikebike commented May 17, 2017

My goal is to associate some metadata with the peer on the other end of the connection. By tying it to the public key you can prevent peers from impersonating other peers (at least without the private key). Much like ssh.

You mentioned "If you want to do some sort of check on server's creds on the client-side, right after the handshake, then, you could wrap around TLS creds in your own struct and override the client and server handshake methods"

I have this working and it looks good. Especially the documentation of WithTransportCredentials saying "WithTransportCredentials returns a DialOption which configures a connection level security credentials (e.g., TLS/SSL)." That's exactly what I want, connection level security with TLS/SSL.

With that working the question becomes where to store the metadata related to the peer on the otherwise of the connection. On the client side, ClientHandshake is passed context.Context. https://blog.golang.org/context was very helpful, and seems designed to do exactly what I need.

Problem is the ServerHandshake(net.Conn) (net.Conn, AuthInfo, error) is not passed context.Context.

Any suggestions on how to get some information from the per connection ServerHandshake to the gRPC calls running in the server on behalf of the client?

@lock lock bot locked as resolved and limited conversation to collaborators Sep 26, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants