Skip to content

Commit 9f5d608

Browse files
committed
100daysofcode - day51 - grpc mtls client
1 parent 2b0da35 commit 9f5d608

File tree

4 files changed

+114
-2
lines changed

4 files changed

+114
-2
lines changed
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
package main
2+
3+
import (
4+
"context"
5+
"crypto/tls"
6+
"crypto/x509"
7+
"flag"
8+
"io/ioutil"
9+
"log"
10+
11+
pb "github.com/deepns/codegym/go/learning/grpc/echo/echo"
12+
"github.com/deepns/codegym/go/learning/grpc/features/sslcerts"
13+
"google.golang.org/grpc"
14+
"google.golang.org/grpc/credentials"
15+
)
16+
17+
func getCredentials() credentials.TransportCredentials {
18+
cert, err := tls.LoadX509KeyPair(
19+
sslcerts.Path("client_cert.pem"), sslcerts.Path("client_key.pem"))
20+
if err != nil {
21+
log.Fatalf("failed to load key pair: %v", err)
22+
}
23+
24+
certPool := x509.NewCertPool()
25+
ca, err := ioutil.ReadFile(sslcerts.Path("ca_cert.pem"))
26+
if err != nil {
27+
log.Fatalf("failed to read CA certificate: %v", err)
28+
}
29+
30+
if ok := certPool.AppendCertsFromPEM(ca); !ok {
31+
log.Fatalf("failed to append CA certificate")
32+
}
33+
34+
return credentials.NewTLS(&tls.Config{
35+
Certificates: []tls.Certificate{cert},
36+
ServerName: "abc.test.example.com",
37+
RootCAs: certPool,
38+
})
39+
}
40+
41+
func main() {
42+
addr := flag.String("addr", "localhost:50505", "address to connect to")
43+
flag.Parse()
44+
45+
conn, err := grpc.Dial(*addr, grpc.WithTransportCredentials(getCredentials()))
46+
if err != nil {
47+
log.Fatalf("failed to dial: %v", err)
48+
}
49+
defer conn.Close()
50+
51+
client := pb.NewEchoServiceClient(conn)
52+
resp, err := client.UnaryEcho(context.Background(), &pb.EchoRequest{Message: "hello from mtls client"})
53+
if err != nil {
54+
log.Fatalf("failed to call UnaryEcho: %v", err)
55+
}
56+
57+
log.Printf("echo: %s", resp.Message)
58+
}

go/learning/grpc/features/mTLS/readme.md

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,3 +57,49 @@ if err != nil {
5757
The AddCert method is typically used to add a single self-signed certificate to a pool, whereas in the case of mutual TLS, the CA may have issued multiple certificates that are needed to validate the client's certificate.
5858

5959
## Client side
60+
61+
When I configured TLS on the client side, I used the convenient function `NewClientTLSFromFile` to get the transport credentials.
62+
63+
```go
64+
creds, err := credentials.NewClientTLSFromFile(sslcerts.Path("ca_cert.pem"), "abc.test.example.com")
65+
if err != nil {
66+
log.Fatalf("failed to load TLS cert: %v", err)
67+
}
68+
```
69+
70+
Like the server side, this also creates a new cert pool, include the root CA to validate server certificate and create a TL config with those parameters. If client certificate needs to be included for mutual TLS, I got to use **NewTLS** instead.
71+
72+
```go
73+
// NewClientTLSFromFile constructs TLS credentials from the provided root
74+
// certificate authority certificate file(s) to validate server connections. If
75+
// certificates to establish the identity of the client need to be included in
76+
// the credentials (eg: for mTLS), use NewTLS instead, where a complete
77+
// tls.Config can be specified.
78+
// serverNameOverride is for testing only. If set to a non empty string,
79+
// it will override the virtual host name of authority (e.g. :authority header
80+
// field) in requests.
81+
func NewClientTLSFromFile(certFile, serverNameOverride string) (TransportCredentials, error) {
82+
b, err := os.ReadFile(certFile)
83+
if err != nil {
84+
return nil, err
85+
}
86+
cp := x509.NewCertPool()
87+
if !cp.AppendCertsFromPEM(b) {
88+
return nil, fmt.Errorf("credentials: failed to append certificates")
89+
}
90+
return NewTLS(&tls.Config{ServerName: serverNameOverride, RootCAs: cp}), nil
91+
```
92+
93+
In addition to the **ServerName** and **RootCAs**, need to provide client certificate in **Certificates**.
94+
95+
```go
96+
cert, err := tls.LoadX509KeyPair(
97+
sslcerts.Path("client_cert.pem"), sslcerts.Path("client_key.pem"))
98+
//...
99+
//...
100+
return credentials.NewTLS(&tls.Config{
101+
Certificates: []tls.Certificate{cert},
102+
ServerName: "abc.test.example.com",
103+
RootCAs: certPool,
104+
})
105+
```

go/learning/grpc/features/mTLS/server/main.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,13 @@ func (s *echoServer) UnaryEcho(_ context.Context, req *pb.EchoRequest) (*pb.Echo
2626
}
2727

2828
func getTLSConfig() *tls.Config {
29-
serverCert, err := tls.LoadX509KeyPair(sslcerts.Path("server_cert.pem"), sslcerts.Path("server_key.pem"))
29+
serverCert, err := tls.LoadX509KeyPair(
30+
sslcerts.Path("server_cert.pem"), sslcerts.Path("server_key.pem"))
3031
if err != nil {
3132
log.Fatalf("failed to load server key pair: %v", err)
3233
}
3334

34-
caCert, err := ioutil.ReadFile(sslcerts.Path("ca_cert.pem"))
35+
caCert, err := ioutil.ReadFile(sslcerts.Path("client_ca_cert.pem"))
3536
if err != nil {
3637
log.Fatalf("failed to load CA certificate: %v", err)
3738
}

notes/journal.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,8 @@
7272
- [ ] authorization
7373
- [x] debugging
7474
- [ ] encryption
75+
- [x] tls
76+
- [x] mtls
7577
- [x] metadata
7678
- [x] interceptor
7779
- [x] metadata-interceptor
@@ -89,6 +91,11 @@
8991

9092
Been a while I lost in touch with my daily exercise. Restarting the practice.
9193

94+
### Day 51 (grpc mutual TLS client)
95+
96+
- added the client side code for mTLS client
97+
- fixed the certificate issue on the server side. had the wrong CA configured on the server
98+
9299
### Day 50 (grpc mutual TLS)
93100

94101
- Yay, day 50 successfully. Started with gRPC basics on day 1, ~50 days ago.

0 commit comments

Comments
 (0)