[TOC]

GRPC certification

This article has participated in the weekend learning program, click the link to see more details: juejin.cn/post/696572…

Let’s review the basic structure of gRPC again

GRPC is a typical C/S model, need to develop client and server, client and server need to reach an agreement, use a certain confirmed transport protocol to transfer data, gRPC usually default to use protobuf as the transport protocol, of course, can also use other customized.

So how does the client know which specific server its data is destined for before communicating with the server? Conversely, does the server need a way to figure out to whom its data is being returned?

Then gRPC certification has to be mentioned

authentication

The authentication mentioned here is not user identity authentication, but refers to how multiple servers and clients can identify each other and securely transfer data

  • SSL/TLS authentication (using HTTP2)
  • Token-based authentication (Based on secure Connection)
  • Do not take any measures to connect, this is not secure connection (default http1)
  • The gRPC provides an interface for extending the user-defined authentication modes

Today I will share with you the SSL/TLS authentication mode and Token based authentication mode, here again to review the last article mentioned

Four types of gRPC message transfer

  • Request-response
  • The server streams messages. Once the client requests a message, the server sends a series of data, called a data stream
  • The client streams the message, the client requests with the data stream, and the server responds
  • Two-way flow, that is, both sides are streaming data

A simple example:

service Example{
	rpc ReqAndRsp(Req) returns (Response)
	rpc ReqAndStream(Req) returns (Stream Response)
	rpc StreamAndRsp(Stream Request) returns (Response)
	rpc BidStream(Stream Request) returns (Stream response)
}
Copy the code

SSL/TLS authentication mode

So what is SSL/TLS?

Transport Layer Security (TLS) is a later version of Secure Socket Layer (SSL). TLS is a protocol used for authentication and encryption between two computers on the Internet.

Channel encryption based on SSL/TLS is a common means of gRPC, so generally how do we use him, his architecture is generally what kind?

By default, GRPC encrypts all data exchanged between the client and server based on HTTP/2 TLS

Does HTTP 2 have encryption by default?

The HTTP 2 protocol does not have encryption by default. It only defines the TLS profile for security

What features does HTTP 2 have?

To recap, HTTP 2 has four important changes from previous versions:

  • Binary framing

Divide all transmitted information into smaller messages and frames and encode them in binary format

  • Multiplex IO multiplexing

By sending requests and responses simultaneously over a shared TCP connection, HTTP messages are broken up into separate frames, sent out of order, and the server reassembles the messages based on identifiers and headers

  • The head of compression
  • Server push Server push

The server can push additional resources to the client without an explicit request from the client

What is the basic practice of SSL/TLS encryption?

SSL/TLS works by binding physical information about websites and companies to encryption keys in digital documents called X.509 certificates.

Each key pair has a private key and a public key. The private key is unique and resides on the server to decrypt the information encrypted by the public key.

The public key is public. Everyone who interacts with the server can hold the public key. Information encrypted with the public key can only be decrypted by the private key.

In a nutshell

In SSL/TLS, the client requests the public key from the server and encrypts information using the public key. After receiving the ciphertext, the server decrypts it using its own private key.

What services does the SSL/TLS protocol provide?

  • Authenticate users and servers to ensure that data is sent to the correct clients and servers;

  • Encrypt data to prevent it from being stolen;

  • Maintain data integrity and ensure that data is not changed during transmission;

What are the features of secure channels provided by the SSL/TLS protocol?

  • Confidentiality: THE SSL protocol uses keys to encrypt communication data.

  • Reliability: Both the server and client are authenticated. Authentication on the client is optional.

  • Integrity: THE SSL protocol checks the integrity of the transmitted data.

With that said, let’s demonstrate how gRPC’s SSL/TLS protocol works in practice

Necessary environment setup

OpenSSL installation

  • Official download address: www.openssl.org/source/, directly under… 支那

Unzip the source code

The tar XZVF openssl 3.0.0 - alpha17. Tar. GzCopy the code

Go to the source directory

CD openssl - 3.0.0 - alpha17Copy the code

Compilation and installation

./Configure
make
sudo make install
Copy the code

After the installation, run the OpenSSL version command to view the OpenSSL version

If the following information is displayed:

openssl: error while loading shared libraries: libssl.so.3: cannot open shared object file: No such file or directory

Usually only need to build a soft link, link past

sudo ln -s /usr/local/lib/libssl.so.3 /usr/lib/libssl.so.3
sudo ln -s /usr/local/lib/libcrypto.so.3 /usr/lib/libcrypto.so.3
Copy the code

TLS Certificate Making

The following is a simple way to generate a key that does not apply to versions later than GO1.15, which deprecated X509

Key 2048 openssl ecparam -genkey -name secp384r1 -out server.key Openssl req -new-x509 -sha256 -key server.key -out server.pem -days 365Copy the code

A DEMO

Start using the key generated above

. ├ ─ ─ client │ ├ ─ ─ keys │ │ ├ ─ ─ for server key │ │ └ ─ ─ for server pem │ ├ ─ ─ main. Go │ ├ ─ ─ myclient │ └ ─ ─ protoc │ └ ─ ─ the hi │ ├ ─ ─ Hi. Pb. Go │ └ ─ ─ the hi. Proto └ ─ ─ server ├ ─ ─ keys │ ├ ─ ─ for server key │ └ ─ ─ for server pem ├ ─ ─ main. Go ├ ─ ─ myserver └ ─ ─ protoc └ ─ ─ the hi ├ ─ ─ the hi. Pb. Go └ ─ ─ hi. The proto

hi.proto

Compile proto into a pb.go file

protoc --go_out=plugins=grpc:. hi.proto

syntax = "proto3"; // Specify the proto version
package hi;     // Specify the default package name

// Specify the golang package name
option go_package = "hi";

// Define the Hi service
service Hi {
// Define SayHi method
rpc SayHi(HiRequest) returns (HiResponse) {}}// HiRequest request structure
message HiRequest {
string name = 1;
}

// HiResponse Response structure
message HiResponse {
string message = 1;
}
Copy the code

server/main.go

package main

import (
   "fmt"
   "log"
   "net"

   pb "myserver/protoc/hi"

   "golang.org/x/net/context"
   "google.golang.org/grpc"
   "google.golang.org/grpc/credentials" // Import GRPC authentication package
)

const (
   // Address gRPC service Address
   Address = "127.0.0.1:9999"
)

// Define HiService and implement the convention interface
type HiService struct{}

// HiService Hello service
var HiSer = HiService{}

SayHi implements the Hi service interface
func (h HiService) SayHi(ctx context.Context, in *pb.HiRequest) (*pb.HiResponse, error) {
   resp := new(pb.HiResponse)
   resp.Message = fmt.Sprintf("Hi %s.", in.Name)

   return resp, nil
}

func main(a) {
   log.SetFlags(log.Ltime | log.Llongfile)
   listen, err := net.Listen("tcp", Address)
   iferr ! =nil {
      log.Panicf("Failed to listen: %v", err)
   }

   / / TLS authentication
   creds, err := credentials.NewServerTLSFromFile("./keys/server.pem"."./keys/server.key")
   iferr ! =nil {
      log.Panicf("Failed to generate credentials %v", err)
   }

   // Instantiate GRPC Server and enable TLS authentication
   s := grpc.NewServer(grpc.Creds(creds))

   / / HelloService registration
   pb.RegisterHiServer(s, HiSer)

   log.Println("Listen on " + Address + " with TLS")

   s.Serve(listen)
}
Copy the code

client/main.go

package main

import (
   "log"
   pb "myclient/protoc/hi" // Introduce the proto package

   "golang.org/x/net/context"
   "google.golang.org/grpc"
   "google.golang.org/grpc/credentials" // Import GRPC authentication package
   "google.golang.org/grpc/grpclog"
)

const (
   // Address gRPC service Address
   Address = "127.0.0.1:9999"
)

func main(a) {
   log.SetFlags(log.Ltime | log.Llongfile)
   // Remember to change XXX to the server address you wrote
   creds, err := credentials.NewClientTLSFromFile("./keys/server.pem"."xiaomotong")
   iferr ! =nil {
      log.Panicf("Failed to create TLS credentials %v", err)
   }

   conn, err := grpc.Dial(Address, grpc.WithTransportCredentials(creds))
   iferr ! =nil {
      grpclog.Fatalln(err)
   }
   defer conn.Close()

   // Initialize the client
   c := pb.NewHiClient(conn)

   // Call the method
   req := &pb.HiRequest{Name: "gRPC"}
   res, err := c.SayHi(context.Background(), req)
   iferr ! =nil {
      log.Panicln(err)
   }

   log.Println(res.Message)
}
Copy the code

If your GO is later than version 1.15, please regenerate the key, refer to the documentation of OpenSSL Certificate Generation Records (Go version 1.15 and later).

After generated, put it in the position of the project response, compile and run the result as follows:

Server:

Client:

Token-based authentication

To optimize the TLS practice DEMO and add the Token mechanism, the following 2 changes are required

  • Client, implementationcredentialsPacket interface,GetRequestMetadataandRequireTransportSecurity
  • The server authenticates client information in metadata

The code structure is the same as in the previous DEMO. Add the corresponding logic to the client and server code respectively.

The following iscredentialsInterface to be implemented in package:

Another DEMO

client/main.go

Add the following logic

package main

import (
	"log"
	pb "myclient/protoc/hi" // Introduce the proto package

	"golang.org/x/net/context"
	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials" // Import GRPC authentication package
	"google.golang.org/grpc/grpclog"
)

const (
	// Address gRPC service Address
	Address = "127.0.0.1:9999"
)

// ====== Add logic START ==============
var IsTls = true

// myCredential custom authentication
type myCredential struct{}

// GetRequestMetadata implements custom authentication interfaces
func (c myCredential) GetRequestMetadata(ctx context.Context, uri ...string) (map[string]string, error) {
	return map[string]string{
		"appid":  "myappid"."appkey": "mykey",},nil
}

// RequireTransportSecurity Specifies whether to enable TLS for authentication
func (c myCredential) RequireTransportSecurity(a) bool {
	return IsTls
}
// ====== Added logic END ==============

func main(a) {
	log.SetFlags(log.Ltime | log.Llongfile)
	
    
// ====== Add logic START ==============
	var err error
	var opts []grpc.DialOption

	if IsTls {
		// Enable TLS to enable TLS authentication
		creds, err := credentials.NewClientTLSFromFile("./keys/server.pem"."Little Devil Boy.")
		iferr ! =nil {
			log.Panicf("Failed to create TLS mycredentials %v", err)
		}
		opts = append(opts, grpc.WithTransportCredentials(creds))
	}else{
		opts = append(opts, grpc.WithInsecure())
	}

	opts = append(opts, grpc.WithPerRPCCredentials(new(myCredential)))
    
    Remember to change XXX to your server address, you can default to 127.0.0.1
	conn, err := grpc.Dial(Address, opts...)
	iferr ! =nil {
		grpclog.Fatalln(err)
	}
// ====== Added logic END ==============
    
	defer conn.Close()

	// Initialize the client
	c := pb.NewHiClient(conn)

	// Call the method
	req := &pb.HiRequest{Name: "gRPC"}
	res, err := c.SayHi(context.Background(), req)
	iferr ! =nil {
		log.Panicln(err)
	}

	log.Println(res.Message)
}

Copy the code

server/main.go

Add the following logic

package main

import (
   "fmt"
   "google.golang.org/grpc/codes"
   "google.golang.org/grpc/metadata"
   "log"
   "net"

   pb "myserver/protoc/hi"

   "golang.org/x/net/context"
   "google.golang.org/grpc"
   "google.golang.org/grpc/credentials" // Import GRPC authentication package
)

const (
   // Address gRPC service Address
   Address = "127.0.0.1:9999"
)

// Define helloService and implement the interface of the convention
type HiService struct{}

// HiService Hello service
var HiSer = HiService{}

SayHello implements the Hello service interface
func (h HiService) SayHi(ctx context.Context, in *pb.HiRequest) (*pb.HiResponse, error) {

// ====== Add logic START ==============
    
   // Parse the information in metada and verify
   md, ok := metadata.FromIncomingContext(ctx)
   if! ok {return nil, grpc.Errorf(codes.Unauthenticated, "no token ")}var (
      appId  string
      appKey string
   )

   // MD is a map[string][]string
   if val, ok := md["appid"]; ok {
      appId = val[0]}if val, ok := md["appkey"]; ok {
      appKey = val[0]}ifappId ! ="myappid"|| appKey ! ="mykey" {
      return nil, grpc.Errorf(codes.Unauthenticated, "token invalide: appid=%s, appkey=%s", appId, appKey)
   }

// ====== Added logic END ==============

   resp := new(pb.HiResponse)
   resp.Message = fmt.Sprintf("Hi %s.", in.Name)

   return resp, nil
}

func main(a) {
   log.SetFlags(log.Ltime | log.Llongfile)
   listen, err := net.Listen("tcp", Address)
   iferr ! =nil {
      log.Panicf("Failed to listen: %v", err)
   }

   / / TLS authentication
   creds, err := credentials.NewServerTLSFromFile("./keys/server.pem"."./keys/server.key")
   iferr ! =nil {
      log.Panicf("Failed to generate credentials %v", err)
   }

   // Instantiate GRPC Server and enable TLS authentication
   s := grpc.NewServer(grpc.Creds(creds))

   / / HelloService registration
   pb.RegisterHiServer(s, HiSer)

   log.Println("Listen on " + Address + " with TLS")

   s.Serve(listen)
}
Copy the code

Well, I’ll stop here and share the gRPC interceptor next time

Technology is open, our mentality, should be more open. Embrace change, live in the sun, and strive to move forward.

I am Nezha, welcome to like, see you next time ~