I have been trying to set up a WebSocket connection over TLS (so with encryption). I use Golang with Gorilla. A WebSocket connection is implemented as an initial HTTP connection that gets upgraded to the WebSocket protocol connection. The code is like this:
func wsEndpoint(w http.ResponseWriter, r *http.Request) {
// upgrade
ws, err := upgrader.Upgrade(w, r, nil)
if err != nil {
//...
}
log.Println("Client Connected")
err = ws.WriteMessage(1, []byte("Hi Client!"))
if err != nil {
//...
}
// listen indefinitely for new messages coming
}
Then we set up the routing:
func main() {
//...
setupRoutes()
log.Fatal(http.ListenAndServe(":8080", nil))
}
Does it suffice to change the last line to:
...http.ListenAndServeTLS(...)
so in other words to use TLS to establish the first connection?
Does this approach suffice to secure the entire communication over WebSocket from start till the end? Should I be certain that all packet transmission within the connection duration is also protected by TLS? If not, how to set it up in Golang / Gorilla framework?
Use http.ListenAndServeTLS to encrypt the underlying network connections used for the HTTP protocol and the WebSocket protocol.
The approach secures the entire communication on the underlying network connection including all WebSocket traffic.
The Gorilla server code uses the network connection provided by the net/http server. The Gorilla server code does create new network connections.
http.ListenAndServeTLS is a helper function that calls lower-level functions and methods. It also works to call those lower-level functions and methods directly.
One approach to this problem would be to set up a reverse proxy like nginx with certbot to generate certificates.
Here's how it would work
[ Client ] ----------> [ nginx ] --------------> [ golang server ]
Encrypted Not encrypted
Related
I wrote a sample gRPC client a server in Go, both configured for server-authenticated TLS.
The client gRPC call succeeds, giving me the impression the TLS is configured properly, otherwise if the TLS handshake had failed, I would expect the client to fail and not make the gRPC request (i.e. not default to plaintext).
Yet I am puzzled by a result I obtain when I attach Wireshark to that network to sniff TCP packets. I do not see any packet with TLS, for e.g. I do not see the TLS CLIENT HELLO packet.
So is this because I'm misinterpreting what I see in Wireshark, or is my gRPC client actually doing plaintext gRPC?
The client code looks like this, note the grpc.withTransportCredentials which I think means it will use TLS or fail, but never plaintext:
// block the dial until connection is successful or 3 sec timeout
dialOptions := []grpc.DialOption{
grpc.WithBlock(),
grpc.WithTimeout(3 * time.Second),
}
// Load TLS Configuration
tlsCredentials, err := LoadTLSCredentials()
if err != nil {
log.Fatalf("Failed to load TLS credentials: %v", err)
}
dialOptions = append(dialOptions, grpc.WithTransportCredentials(tlsCredentials))
// Dial the gRPC server
log.Printf("Dialing %v", *address)
conn, err := grpc.Dial(*address, dialOptions...)
if err != nil {
log.Fatalf("Failed to connect to the server: %v", err)
}
defer conn.Close()
// then this application sets up a gRPC request, and logs the response to stdout,
// in my testing stdout shows the expected gRPC response, so I'd assume TLS is working.
func LoadTLSCredentials() (credentials.TransportCredentials, error) {
rootCA, err := ioutil.ReadFile("ca.cert")
if err != nil {
return nil, err
}
certPool := x509.NewCertPool()
if !certPool.AppendCertsFromPEM(rootCA) {
return nil, fmt.Errorf("Failed to add rootCA to x509 certificate pool")
}
config := &tls.Config{
MinVersion: tls.VersionTLS12,
RootCAs: certPool,
}
return credentials.NewTLS(config), nil
}
And here's a screenshot of Wireshark showing no TLS packet
whereas I would expect something similar to the following which clearly shows some TLS activity (not my app, image is from the web for illustration purposes)
I'm running Wireshark v2.6.10 on Ubuntu 16.04. The source and destination IPs match my gRPC client and server IPs (both are docker containers on the same docker network).
Not that it really matters, but as can be seen in my client code, I'm sharing a root CA certificate on the client (self signed). I can do this because I deploy both the client and the server.
As #steffanUllrich explained in the comments, this was a case of Wireshark can be better configured to show TLS. I confirmed the gRPC exchange is indeed TLS protected.
You should right click the packet list, and select 'decode as..' menu item, then select 'tls' to force wireshark dissect traffic in this tcp port as TLS.
I am using grpc in my project, if i have a grpc service call helloService, should i use GetNewHelloServiceClient to get a new client in every function? Or just get once in start program?
// for example:
c.GET("/hello", SayHello)
func SayHello() {
c := pb.GetNewHelloServiceClient()
res, err := c.SayHello(context.Background(), &request)
if err != nil {
return
}
fmt.print(res.Hello)
}
Create a gRPC client just once.
Lots of networking concepts in go are designed for reuse: http clients, http transports, sql.DB connection pools etc. They are all go-routine safe & should only be created once but reused many times.
How to reuse http2 stream in golang?
Hi all, recently I tried many ways to reuse a stream to send http2 data but failed, and I didn't find many related solutions in http2, can anyone give me some hints or demos?
client := http.Client{
Transport: &http2.Transport{
DialTLS: func(network, addr string, cfg *tls.Config) (net.Conn, error) {
return net.DialTimeout(network, addr, time.Second)
},
AllowHTTP: true,
},
}
resp, err := client.Get(path)
I tried to use resp as client and write to this resp to send data to server. Is it right way to do it?
In HTTP/2 streams are short lived, and correspond to a request/response exchange. In effect the end of the response closes the stream. Issuing another request via client.Get will automatically open a new stream on the connection, if the underlying connection is using HTTP/2.
I'm wondering if it's a good idea to push data from gRPC server to a client. Basically I want to use a pub/sub pattern with gRPC.
The way I do is that I return a response stream on the server implementation that I never close. Then, the client has a never ending go routine in charge of reading this stream.
Here is an example:
service Service {
rpc RegularChanges (Void) returns (stream Change) {}
}
On the server side:
func (self *MyServiceImpl) RegularChanges(in *pb.Void, stream pb.Service_RegularChangesServer) error {
for {
d, err := time.ParseDuration("1s")
if err != nil {
log.Fatalf("Cannot parse duration")
break;
}
time.Sleep(d)
stream.Send(&pb.Change{Name:"toto", Description:"status changed"})
}
return nil
}
On client:
for {
change, err := streamChanges.Recv()
if err != nil {
log.Fatalf("Error retrieving change")
} else {
log.Println(change)
}
}
I just began with go and gRPC but I know it's based on HTTP2, hence it should support pushing datas. However, I'm not sure this is the way gRPC should be used.
gRPC is intended to be used in this way.
You should still consider how the client should behave on failures and how you may want to re-balance across backends. If your connection is going across the Internet, you may also want to enable keepalive to detect connection breakages by providing KeepaliveParams to the client and server.
I use code.google.com/p/go.net/websocket in server, so client can get notification from server.
however, It seems after client connected to server, if there is no any data tranfer between client and server, server will return EOF error at websocket.JSON.Receive(), it looks like a timeout mechanism.
And I have search in Google, it seems websocket protocol has a ping-pong heartbeat to maintain the connection, I want to ask whether code.google.com/p/go.net/websocket support this ping protocol or not?
What should I do if I want keep connection between client and server alive?
Here's working drop-in solution for gorilla/websocket package.
func keepAlive(c *websocket.Conn, timeout time.Duration) {
lastResponse := time.Now()
c.SetPongHandler(func(msg string) error {
lastResponse = time.Now()
return nil
})
go func() {
for {
err := c.WriteMessage(websocket.PingMessage, []byte("keepalive"))
if err != nil {
return
}
time.Sleep(timeout/2)
if(time.Since(lastResponse) > timeout) {
c.Close()
return
}
}
}()
}
As recently as 2013, the go.net websocket library does not support (automatic) keep-alive messages. You have two options:
Implement an "application level" keep-alive by periodically having your application send a message down the pipe (either direction should work), that is ignored by the other side.
Move to a different websocket library that does support keep-alives (like this one) Edit: it looks like that library has been superseded by Gorilla websockets.