preface
Hi, I’m Fish fry. In the previous chapter, we introduced four ways to use the gRPC API. Isn’t that easy?
In this case, there is a security problem. In the previous example, gRPC Client/Server is transmitted in plaintext. Is there any risk of eavesdropping?
In conclusion, yes. In the case of plaintext communication, your request is naked and could be maliciously tampered with or falsified as “illegal” data by a third party
Grab a bag
Well, clear text transmission is correct. This is a problem, next we will transform our gRPC, in order to solve this problem 😤
Certificate generated
The private key
openssl ecparam -genkey -name secp384r1 -out server.key
Copy the code
Since the sign the public key
openssl req -new -x509 -sha256 -key server.key -out server.pem -days 3650
Copy the code
Fill in the information
Country Name (2 letter code) []:
State or Province Name (full name) []:
Locality Name (eg, city) []:
Organization Name (eg, company) []:
Organizational Unit Name (eg, section) []:
Common Name (eg, fully qualified host name) []:go-grpc-example
Email Address []:
Copy the code
Is generated
After the certificate is generated, place the certificate file in conf/.
$├─ ├─ ├─ $├─ ├─ ├─ $├─ ├── ├─ ├─ $├─ ├── ├─ ├─ $├─ ├── ├── ├── ─ $├─ ├── ── ── ── ── ── ── ── ── Simple_server └ ─ ─ stream_serverCopy the code
As this article is biased towards gRPC, please refer to “Making Certificates” for details. Further details may be provided 👌
Why didn’t you need a certificate before
In Simple_server, why does “nothing” work without a certificate?
Server
grpc.NewServer()
Copy the code
There are obviously no DialOptions passed in on the server side
Client
conn, err := grpc.Dial(":"+PORT, grpc.WithInsecure())
Copy the code
Notice the grpc.withInsecure () method on the client side
func WithInsecure() DialOption {
return newFuncDialOption(func(o *dialOptions) {
o.insecure = true})}Copy the code
Within the method you can see that WithInsecure returns a DialOption, and it will eventually disable secure transport by reading the set value
So where does it end up? Let’s move to the grpc.dial () method
func DialContext(ctx context.Context, target string, opts ... DialOption) (conn *ClientConn, err error) { ...for _, opt := range opts {
opt.apply(&cc.dopts)
}
...
if! cc.dopts.insecure {if cc.dopts.copts.TransportCredentials == nil {
return nil, errNoTransportSecurity
}
} else {
ifcc.dopts.copts.TransportCredentials ! = nil {return nil, errCredentialsConflict
}
for _, cd := range cc.dopts.copts.PerRPCCredentials {
if cd.RequireTransportSecurity() {
return nil, errTransportCredentialsMissing
}
}
}
...
creds := cc.dopts.copts.TransportCredentials
ifcreds ! = nil && creds.Info().ServerName ! ="" {
cc.authority = creds.Info().ServerName
} else ifcc.dopts.insecure && cc.dopts.authority ! ="" {
cc.authority = cc.dopts.authority
} else {
// Use endpoint from "scheme://authority/endpoint" as the default
// authority for ClientConn.
cc.authority = cc.parsedTarget.Endpoint
}
...
}
Copy the code
gRPC
Next we will start coding and implement TLS certificate authentication support 🤔 on gRPC Client/Server
TLS Server
package main
import (
"context"
"log"
"net"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials"
pb "github.com/EDDYCJY/go-grpc-example/proto")... const PORT ="9001"
func main() {
c, err := credentials.NewServerTLSFromFile(".. /.. /conf/server.pem".".. /.. /conf/server.key")
iferr ! = nil { log.Fatalf("credentials.NewServerTLSFromFile err: %v", err)
}
server := grpc.NewServer(grpc.Creds(c))
pb.RegisterSearchServiceServer(server, &SearchService{})
lis, err := net.Listen("tcp".":"+PORT)
iferr ! = nil { log.Fatalf("net.Listen err: %v", err)
}
server.Serve(lis)
}
Copy the code
- Credentials. NewServerTLSFromFile: according to the service side input the certificate files and key structure of the TLS certificate
func NewServerTLSFromFile(certFile, keyFile string) (TransportCredentials, error) {
cert, err := tls.LoadX509KeyPair(certFile, keyFile)
iferr ! = nil {return nil, err
}
return NewTLS(&tls.Config{Certificates: []tls.Certificate{cert}}), nil
}
Copy the code
- Grpc.creds () : Returns a ServerOption that sets the credentials for the server connection. Used for
grpc.NewServer(opt ... ServerOption)
Set connection options for gRPC Server
func Creds(c credentials.TransportCredentials) ServerOption {
return func(o *options) {
o.creds = c
}
}
Copy the code
After the above two simple steps, gRPC Server set up the certificate authentication service 🤔
TLS Client
package main
import (
"context"
"log"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials"
pb "github.com/EDDYCJY/go-grpc-example/proto"
)
const PORT = "9001"
func main() {
c, err := credentials.NewClientTLSFromFile(".. /.. /conf/server.pem"."go-grpc-example")
iferr ! = nil { log.Fatalf("credentials.NewClientTLSFromFile err: %v", err)
}
conn, err := grpc.Dial(":"+PORT, grpc.WithTransportCredentials(c))
iferr ! = nil { log.Fatalf("grpc.Dial err: %v", err)
}
defer conn.Close()
client := pb.NewSearchServiceClient(conn)
resp, err := client.Search(context.Background(), &pb.SearchRequest{
Request: "gRPC",})iferr ! = nil { log.Fatalf("client.Search err: %v", err)
}
log.Printf("resp: %s", resp.GetResponse())
}
Copy the code
- Credentials. NewClientTLSFromFile () : according to the client input the certificate files and key structure of the TLS certificate. ServerNameOverride is the service name
func NewClientTLSFromFile(certFile, serverNameOverride string) (TransportCredentials, error) {
b, err := ioutil.ReadFile(certFile)
iferr ! = nil {return nil, err
}
cp := x509.NewCertPool()
if! cp.AppendCertsFromPEM(b) {return nil, fmt.Errorf("credentials: failed to append certificates")}return NewTLS(&tls.Config{ServerName: serverNameOverride, RootCAs: cp}), nil
}
Copy the code
- GRPC. WithTransportCredentials () : returns a DialOption configuration connection options. Used for
grpc.Dial(target string, opts ... DialOption)
Setting connection Options
func WithTransportCredentials(creds credentials.TransportCredentials) DialOption {
return newFuncDialOption(func(o *dialOptions) {
o.copts.TransportCredentials = creds
})
}
Copy the code
validation
request
Restart server.go and execute client.go to get the result
$ go run client.go
2018/09/30 20:00:21 resp: gRPC Server
Copy the code
Grab a bag
Success.
conclusion
In this chapter we have implemented gRPC TLS Client/Servert, do you think you are done? I don’t 😤
The problem
If you look carefully, the Client establishes the request based on the certificate and service name of the Server. In this case, you need to send the Server’s certificate to the Client through various means, otherwise this task cannot be completed
The problem is, you can’t guarantee that your “means” are safe. After all, the current Internet environment is very dangerous.
We will address this problem in the next section to ensure its reliability.
?
If you have any questions or mistakes, welcome to raise questions or give correction opinions on issues. If you like or are helpful to you, welcome Star, which is a kind of encouragement and promotion for the author.
My official account
reference
Sample code for this series
- go-grpc-example