Skip to content

Commit

Permalink
test: add grpc tls test
Browse files Browse the repository at this point in the history
  • Loading branch information
ppzqh committed Jan 11, 2024
1 parent 10d932d commit 85cd5e2
Show file tree
Hide file tree
Showing 4 changed files with 433 additions and 0 deletions.
43 changes: 43 additions & 0 deletions grpc/tls/cert/gen.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
#! /bin/bash
# Copyright 2024 CloudWeGo 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.

set -e

rm -f *.pem
rm -f *.srl

# 1. Generate CA's private key and self-signed certificate
openssl req -x509 -newkey rsa:4096 -days 365 -nodes -keyout ca-key.pem -out ca-cert.pem -subj "/C=FR/ST=Occitanie/L=Toulouse/O=Tech School/OU=Education/CN=*.techschool.guru/[email protected]"

echo "CA's self-signed certificate"
openssl x509 -in ca-cert.pem -noout -text

# 2. Generate web server's private key and certificate signing request (CSR)
openssl req -newkey rsa:4096 -nodes -keyout server-key.pem -out server-req.pem -subj "/C=FR/ST=Ile de France/L=Paris/O=PC Book/OU=Computer/CN=*.pcbook.com/[email protected]"

# 3. Use CA's private key to sign web server's CSR and get back the signed certificate
openssl x509 -req -in server-req.pem -days 60 -CA ca-cert.pem -CAkey ca-key.pem -CAcreateserial -out server-cert.pem

echo "Server's signed certificate"
openssl x509 -in server-cert.pem -noout -text

# 4. Generate client's private key and certificate signing request (CSR)
openssl req -newkey rsa:4096 -nodes -keyout client-key.pem -out client-req.pem -subj "/C=FR/ST=Alsace/L=Strasbourg/O=PC Client/OU=Computer/CN=*.pcclient.com/[email protected]"

# 5. Use CA's private key to sign client's CSR and get back the signed certificate
openssl x509 -req -in client-req.pem -days 60 -CA ca-cert.pem -CAkey ca-key.pem -CAcreateserial -out client-cert.pem

echo "Client's signed certificate"
openssl x509 -in client-cert.pem -noout -text
130 changes: 130 additions & 0 deletions grpc/tls/grpc_server.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
// Copyright 2024 CloudWeGo 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.

package tls

import (
"context"
"crypto/tls"
"crypto/x509"
"fmt"
"io"
"io/ioutil"
"net"
"strings"

"google.golang.org/grpc"
"google.golang.org/grpc/credentials"

grpc_demo "github.com/cloudwego/kitex-tests/grpc_gen/protobuf/grpc_demo_2"
)

func RunGRPCTLSServer(hostport string) (*grpc.Server, error) {
tlsCredentials, err := serverLoadTLSCredentials()
if err != nil {
return nil, err
}
cred := grpc.Creds(tlsCredentials)

svr := grpc.NewServer(cred)
ms := &GrpcServiceA{}
grpc_demo.RegisterServiceAServer(svr, ms)
listener, err := net.Listen("tcp", hostport)
if err != nil {
return nil, err
}
go svr.Serve(listener)
return svr, nil
}

type GrpcServiceA struct {
grpc_demo.UnimplementedServiceAServer
}

func (s *GrpcServiceA) CallUnary(ctx context.Context, req *grpc_demo.Request) (*grpc_demo.Reply, error) {
res := &grpc_demo.Reply{Message: req.Name + " Hello!"}
return res, nil
}

func (s *GrpcServiceA) CallClientStream(stream grpc_demo.ServiceA_CallClientStreamServer) error {
var msgs []string
for {
req, err := stream.Recv()
if err != nil {
if err == io.EOF {
break
}
return err
}
msgs = append(msgs, req.Name)
}
return stream.SendAndClose(&grpc_demo.Reply{Message: "all message: " + strings.Join(msgs, ", ")})
}
func (s *GrpcServiceA) CallServerStream(req *grpc_demo.Request, stream grpc_demo.ServiceA_CallServerStreamServer) error {
resp := &grpc_demo.Reply{}
for i := 0; i < 3; i++ {
resp.Message = fmt.Sprintf("%v-%d", req.Name, i)
err := stream.Send(resp)
if err != nil {
return err
}
}
return nil
}
func (s *GrpcServiceA) CallBidiStream(stream grpc_demo.ServiceA_CallBidiStreamServer) error {
for {
recv, err := stream.Recv()
if err != nil {
if err == io.EOF {
break
}
return err
}
resp := &grpc_demo.Reply{}
resp.Message = recv.Name
err = stream.Send(resp)
if err != nil {
return err
}
}
return nil
}

func serverLoadTLSCredentials() (credentials.TransportCredentials, error) {
// Load certificate of the CA who signed client's certificate
pemClientCA, err := ioutil.ReadFile("cert/ca-cert.pem")
if err != nil {
return nil, err
}

certPool := x509.NewCertPool()
if !certPool.AppendCertsFromPEM(pemClientCA) {
return nil, fmt.Errorf("failed to add client CA's certificate")
}

// Load server's certificate and private key
serverCert, err := tls.LoadX509KeyPair("cert/server-cert.pem", "cert/server-key.pem")
if err != nil {
return nil, err
}

// Create the credentials and return it
config := &tls.Config{
Certificates: []tls.Certificate{serverCert},
ClientAuth: tls.RequireAndVerifyClientCert, // mTLS
ClientCAs: certPool,
}

return credentials.NewTLS(config), nil
}
169 changes: 169 additions & 0 deletions grpc/tls/kitex_client.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
// Copyright 2024 CloudWeGo 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.

package tls

import (
"context"
"crypto/tls"
"crypto/x509"
"fmt"
"io"
"io/ioutil"
"strconv"

"github.com/cloudwego/kitex/client"
"github.com/cloudwego/kitex/client/callopt"
"github.com/cloudwego/kitex/transport"

"github.com/cloudwego/kitex-tests/kitex_gen/protobuf/grpc_demo"
"github.com/cloudwego/kitex-tests/kitex_gen/protobuf/grpc_demo/servicea"
)

type ClientWrapper struct {
client servicea.Client
}

func GetClient(hostport string, clientOptions ...client.Option) (*ClientWrapper, error) {
opts := append(clientOptions, client.WithTransportProtocol(transport.GRPC), client.WithHostPorts(hostport))
client, err := servicea.NewClient("kitex-test", opts...)
if err != nil {
return nil, err
}
return &ClientWrapper{client: client}, nil
}

func (c *ClientWrapper) RunUnary(callOptions ...callopt.Option) (*grpc_demo.Reply, error) {
req := &grpc_demo.Request{Name: "Kitex"}
ctx := context.Background()
return c.client.CallUnary(ctx, req, callOptions...)
}

func (c *ClientWrapper) RunClientStream(callOptions ...callopt.Option) (*grpc_demo.Reply, error) {

ctx := context.Background()
streamCli, err := c.client.CallClientStream(ctx)
if err != nil {
return nil, err
}
for i := 0; i < 3; i++ {
req := &grpc_demo.Request{Name: "kitex-" + strconv.Itoa(i)}
err = streamCli.SendMsg(req)
if err != nil {
return nil, err
}
}
return streamCli.CloseAndRecv()
}

func (c *ClientWrapper) RunServerStream(callOptions ...callopt.Option) ([]*grpc_demo.Reply, error) {
ctx := context.Background()
req := &grpc_demo.Request{Name: "kitex"}
streamCli, err := c.client.CallServerStream(ctx, req)
if err != nil {
return nil, err
}
replies := []*grpc_demo.Reply{}
for {
reply, er := streamCli.Recv()
if er != nil {
if er != io.EOF {
return nil, er
}
break
}
replies = append(replies, reply)
}
return replies, nil
}

func (c *ClientWrapper) RunBidiStream(callOptions ...callopt.Option) ([]*grpc_demo.Reply, error) {

ctx := context.Background()
stream, err := c.client.CallBidiStream(ctx)
if err != nil {
return nil, err
}
errChan := make(chan error)
go func() {
for i := 0; i < 3; i++ {
req := &grpc_demo.Request{Name: "kitex-" + strconv.Itoa(i)}
err = stream.Send(req)
if err != nil {
errChan <- err
return
}
}
er := stream.Close()
if er != nil {
errChan <- er
return
}
errChan <- nil
}()
repliesChan := make(chan []*grpc_demo.Reply)
errRecvChan := make(chan error)
go func() {
replies := []*grpc_demo.Reply{}
for {
reply, err := stream.Recv()
if err == io.EOF {
break
}
if err != nil {
errRecvChan <- err
return
}
replies = append(replies, reply)
}
repliesChan <- replies
}()
err = <-errChan
if err != nil {
return nil, err
}
select {
case replies := <-repliesChan:
return replies, nil
case err = <-errRecvChan:
return nil, err
}
}

func clientLoadTLSCredentials() (*tls.Config, error) {
// Load certificate of the CA who signed server's certificate
pemServerCA, err := ioutil.ReadFile("cert/ca-cert.pem")
if err != nil {
return nil, err
}

certPool := x509.NewCertPool()
if !certPool.AppendCertsFromPEM(pemServerCA) {
return nil, fmt.Errorf("failed to add server CA's certificate")
}

// Load client's certificate and private key
clientCert, err := tls.LoadX509KeyPair("cert/client-cert.pem", "cert/client-key.pem")
if err != nil {
return nil, err
}

// Create the credentials and return it
config := &tls.Config{
Certificates: []tls.Certificate{clientCert},
RootCAs: certPool,
InsecureSkipVerify: true,
}
return config, nil
}
Loading

0 comments on commit 85cd5e2

Please sign in to comment.