Skip to content

Commit

Permalink
add mutual tls example
Browse files Browse the repository at this point in the history
  • Loading branch information
sding3 committed Feb 12, 2022
1 parent fb356db commit 391d124
Show file tree
Hide file tree
Showing 4 changed files with 208 additions and 0 deletions.
21 changes: 21 additions & 0 deletions examples/data/data.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@
package data

import (
"crypto/x509"
"fmt"
"io/ioutil"
"path/filepath"
"runtime"
)
Expand All @@ -42,3 +45,21 @@ func Path(rel string) string {

return filepath.Join(basepath, rel)
}

// NewCertPool returns a x509.CertPool given the absolute paths of a list of
// PEM certificates, or an error if any failure is encountered when loading
// the said certificates.
func NewCertPool(certPaths ...string) (*x509.CertPool, error) {
certPool := x509.NewCertPool()
for _, p := range certPaths {
caBytes, err := ioutil.ReadFile(p)
if err != nil {
return nil, fmt.Errorf("failed to read cert %s: %v", p, err)
}
ok := certPool.AppendCertsFromPEM(caBytes)
if !ok {
return nil, fmt.Errorf("failed to parse %s", p)
}
}
return certPool, nil
}
31 changes: 31 additions & 0 deletions examples/features/mutual_tls/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# Mutual TLS

In mutual TLS, the client and the server authenticates each other. gRPC allows
users to configure mutual TLS at the connection level.

## Try it

```
go run server/main.go
```

```
go run client/main.go
```

## Explanation

### Client

In normal TLS, the client is only concerned with authenticating the server by
using a trusted CA file. In mutual TLS, the client also presents the its client
certificate to the server for authentication. This is done via setting
`tls.Config.Certificates`.

### Server

In normal TLS, the server is only concerned with presenting the server
certificate for clients to verify. In mutual TLS, the server also load in a
list of trusted CA files for verifying client presented certificates with.
This is done via setting `tls.Config.RootCAs` to the list of trusted CA files,
and setting `tls.config.ClientAuth` to `tls.RequireAndVerifyClientCert`.
78 changes: 78 additions & 0 deletions examples/features/mutual_tls/client/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
/*
*
* Copyright 2018 gRPC authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/

// The client demonstrates how to supply an OAuth2 token for every RPC.
package main

import (
"context"
"crypto/tls"
"flag"
"fmt"
"log"
"time"

"google.golang.org/grpc"
"google.golang.org/grpc/credentials"
"google.golang.org/grpc/examples/data"
ecpb "google.golang.org/grpc/examples/features/proto/echo"
)

var addr = flag.String("addr", "localhost:50051", "the address to connect to")

func callUnaryEcho(client ecpb.EchoClient, message string) {
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
resp, err := client.UnaryEcho(ctx, &ecpb.EchoRequest{Message: message})
if err != nil {
log.Fatalf("client.UnaryEcho(_) = _, %v: ", err)
}
fmt.Println("UnaryEcho: ", resp.Message)
}

func main() {
flag.Parse()

clientCredential, err := tls.LoadX509KeyPair(
data.Path("x509/client_cert.pem"),
data.Path("x509/client_key.pem"),
)
if err != nil {
log.Fatalf("failed to load client cert: %v", err)
}

trustedServerCAs, err := data.NewCertPool(data.Path("x509/ca_cert.pem"))
if err != nil {
log.Fatalf("failed to load CA cert: %v", err)
}

tlsConfig := &tls.Config{
ServerName: "x.test.example.com",
Certificates: []tls.Certificate{clientCredential},
RootCAs: trustedServerCAs,
}

conn, err := grpc.Dial(*addr, grpc.WithTransportCredentials(credentials.NewTLS(tlsConfig)))
if err != nil {
log.Fatalf("did not connect: %v", err)
}
defer conn.Close()
rgc := ecpb.NewEchoClient(conn)

callUnaryEcho(rgc, "hello world")
}
78 changes: 78 additions & 0 deletions examples/features/mutual_tls/server/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
/*
*
* Copyright 2018 gRPC authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/

// The server demonstrates how to consume and validate OAuth2 tokens provided by
// clients for each RPC.
package main

import (
"context"
"crypto/tls"
"crypto/x509"
"flag"
"fmt"
"log"
"net"

"google.golang.org/grpc"
"google.golang.org/grpc/credentials"
"google.golang.org/grpc/examples/data"
pb "google.golang.org/grpc/examples/features/proto/echo"
)

var port = flag.Int("port", 50051, "the port to serve on")

func main() {
flag.Parse()
fmt.Printf("server starting on port %d...\n", *port)

serverCert, err := tls.LoadX509KeyPair(data.Path("x509/server_cert.pem"), data.Path("x509/server_key.pem"))
if err != nil {
log.Fatalf("failed to load key pair: %s", err)
}

trustedClientCAs := x509.NewCertPool()
trustedClientCAs, err = data.NewCertPool(data.Path("x509/client_ca_cert.pem"))
if err != nil {
log.Fatalf("failed to load trusted client CAs: %s", err)
}

tlsConfig := &tls.Config{
ClientAuth: tls.RequireAndVerifyClientCert,
Certificates: []tls.Certificate{serverCert},
ClientCAs: trustedClientCAs,
}

s := grpc.NewServer(grpc.Creds(credentials.NewTLS(tlsConfig)))
pb.RegisterEchoServer(s, &ecServer{})
lis, err := net.Listen("tcp", fmt.Sprintf("localhost:%d", *port))
if err != nil {
log.Fatalf("failed to listen: %v", err)
}
if err := s.Serve(lis); err != nil {
log.Fatalf("failed to serve: %v", err)
}
}

type ecServer struct {
pb.UnimplementedEchoServer
}

func (s *ecServer) UnaryEcho(ctx context.Context, req *pb.EchoRequest) (*pb.EchoResponse, error) {
return &pb.EchoResponse{Message: req.Message}, nil
}

0 comments on commit 391d124

Please sign in to comment.