内容简介:在客户端和服务器通过gRPC相互通信,由于我们使用了protobuf来序列化和反序列化消息,因此消息的数据是二进制形式的。但是我们的通信是明文传输的,这在一些安全需求较高的场景中是不允许的,因此需要使用安全传输。幸好,在gRPC中,我们可以直接使用SSL/TLS,用来验证服务器,并对通信过程进行加密。首先,需要生成证书。
在 前一篇 文章中,我们使用gRPC从零开始定义了一个服务。在这篇文章中,我们更近一步,在这个服务中添加TLS特性。
1. 制作证书
客户端和服务器通过gRPC相互通信,由于我们使用了protobuf来序列化和反序列化消息,因此消息的数据是二进制形式的。但是我们的通信是明文传输的,这在一些安全需求较高的场景中是不允许的,因此需要使用安全传输。幸好,在gRPC中,我们可以直接使用SSL/TLS,用来验证服务器,并对通信过程进行加密。
首先,需要生成证书。
1.1 生成私钥
在我们的simplemath项目目录中新建目录 cert
,进入这个目录,执行下面的命令来生成私钥:
$ openssl genrsa -out server.key 2048
我们使用 openssl genrsa
命令来生成私钥,并用 -out
选项指定输出。最后一个参数 2048
表示的是生成密钥的位数,如果没有指定,那么默认就是512位。
1.2 根据私钥生成CSR
如果想从一个认证中心( Certificate Authority,CA )获取一个SSL证书,我们需要生成一个证书签名请求( Certificate Signing Reqeusts,CRSs )。一个CSR主要包含钥匙对中的公钥,以及其它一些重要的信息。
我们可以根据前面生成的私钥生成一个CSR:
$ openssl req -new -sha256 -key server.key -out server.csr
执行上面的命令后,需要完成一些信息的填写,主要有:
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) []: Email Address []:
填写完这些信息后,就会生成一个证书签名请求( server.csr
)。
1.3 生成证书
如果想使用一个SSL证书来对通信进行加密,但是不需要使用CA签字的证书,那么我们可以生成一个自签名的证书。
使用前面生成的私钥( server.key
)以及证书签名请求( server.csr
),我们可以生成一个自签名的证书:
$ openssl x509 -req -sha256 -in server.csr -signkey server.key -out server.crt -days 3650
选项 -x509
指定 req
来生成一个自签名的证书。 -days 3650
指定了证书的有效期是3650天。 -signkey
指定了私钥,而 -in
指定了证书签名请求。
这样,就能生成一个自签名的证书( server.crt
)。
此时,整个项目的目录结构如下:
$GOPATH // your $GOPATH folder |__src // the source folder |__simplemath // the simplemath project folder |__cert // the certificate folder | |__server.key // the private key | |__server.csr // the certificate signing request | |__server.crt // the self-signed certificate |__client // the client folder stores the client codes | |__rpc // the rpc folder stores the call function | | |__simplemath.go // the logic code for remote call | |__main.go // client program goes from here | |__client // the executeable client file |__api // folder that stores .proto and .pb.go files | |__simplemath.proto // file defines the messages and services | |__simplemath.pb.go // file generated by protoc |__server // the server folder stores the server codes |__rpcimpl // the rpcimpl folder stores the logic codes | |__simplemath.go // the logic code related to simplemath |__main.go // server program goes from here |__server // the executable server file ...
证书生成后,就可以在我们的代码中加入TLS了。
2. Server+TLS
gRPC协议本身没什么变动,主要的变动就是在gRPC对象的生成,包括服务器和客户端。如果只改动一边的话是不成功的,需要两端都修改。
2.1 修改服务器端代码
在服务器端,需要修改 main.go
,代码如下:
package main import ( "google.golang.org/grpc" // import the credentials package "google.golang.org/grpc/credentials" "google.golang.org/grpc/reflection" "log" "net" pb "simplemath/api" "simplemath/server/rpcimpl" ) const ( port = ":50051" ) func main() { lis, err := net.Listen("tcp", port) if err != nil { log.Fatalf("failed to listen: %v", err) } // create the TLS credentials from files creds, err := credentials.NewServerTLSFromFile("../cert/server.crt", "../cert/server.key") if err != nil { log.Fatalf("could not load TLS keys: %s", err) } // create a gRPC option array with the credentials opts := []grpc.ServerOption{grpc.Creds(creds)} // create a gRPC server object with server options(opts) s := grpc.NewServer(opts...) pb.RegisterSimpleMathServer(s, &rpcimpl.SimpleMathServer{}) reflection.Register(s) if err := s.Serve(lis); err != nil { log.Fatalf("failed to serve: %v", err) } }
2.2 我们变了什么?
这个代码和之前的有什么变化呢?
首先,为了使用TLS,我们需要导入 google.golang.org/credentials
包。
然后,使用我们之前生成的私钥( server.key
)以及自签名证书( server.crt
)构建一个credentials对象 creds
。
由于这个TLS是在我们原来的服务器上增加的特性,所以需要创建一个 grpc.ServerOption
,参数就是我们刚才创建的 creds
。
最后,我们将创建的 grpc.ServerOption
传入生成grpc服务器的构造函数 grpc.NewServer()
中。
注意 grpc.NewServer()
函数是一个可变数量参数的函数,我们可以传入零个或多个参数( grpc.ServerOption
),以后我们会用到更多。
2.3 注意Common Name
有一个需要注意的问题是,我们在生成服务器时绑定的地主必须和在生成CSR时候填写的 Common Name
信息一致,不然会出现下面的错误:
2018/09/25 17:04:02 cound not compute: rpc error: code = Unavailable desc = all SubConns are in TransientFailure, latest connection error: connection error: desc = "transport: authentication handshake failed: x509: certificate is valid for example.com, not localhost"
就是说,我们在生成CSR是填写的信息是 example.com
,但是使用的时候却是 localhost
,那么这是认证不通过的。因此,我们在生成CSR的时候, Common Name
信息需要填成 localhost
。
2.4 The First Try
当我们只修改服务器端,而客户端使用的是之前的版本,那么会出现如下的错误:
2018/09/25 16:58:43 cound not compute: rpc error: code = Unavailable desc = transport is closing
连接失败。因为两端都需要修改。
3. Client+TLS
在客户端这一部分,我们需要使用相同的证书来创建一个grpc客户端。由于我们是在 client/rpc/simplemath.go
中创建grpc客户端并建立连接,因此需要修改这个文件:
ackage rpc import ( "golang.org/x/net/context" // import credentials package "google.golang.org/grpc/credentials" "google.golang.org/grpc" "log" pb "simplemath/api" "strconv" "time" ) const ( address = "localhost:50051" ) func GreatCommonDivisor(first, second string) { // create the client TLS credentials creds, err := credentials.NewClientTLSFromFile("../cert/server.crt", "") // initiate a connection with the server using creds conn, err := grpc.Dial(address, grpc.WithTransportCredentials(creds)) if err != nil { log.Fatalf("did not connect: %v", err) } defer conn.Close() c := pb.NewSimpleMathClient(conn) a, _ := strconv.ParseInt(first, 10, 32) b, _ := strconv.ParseInt(second, 10, 32) ctx, cancel := context.WithTimeout(context.Background(), time.Second) defer cancel() r, err := c.GreatCommonDivisor(ctx, &pb.GCDRequest{First: int32(a), Second: int32(b)}) if err != nil { log.Fatalf("cound not compute: %v", err) } log.Printf("The Greatest Common Divisor of %d and %d is %d", a, b, r.Result) }
和服务器端类似,我们使用 credentials.NewClientTLSFromFile()
函数创建一个credentials对象 creds
,并用这个对象创建连接。这个函数也是可变数量参数的函数,可以用来指定多个参数,用来指导建立连接时的行为。
注意,在创建 creds
的时候我们没有用到私钥( server.key
),从名字可知,这个私钥是服务器的。
这样,客户端和服务器端都使用了credentials,因此可以通过加密的方式进行通信了。
和原来一样,我们编译并运行服务器和客户端的代码,结果如下:
2018/09/25 18:11:26 The Greatest Common Divisor of 12 and 15 is 3
对话成功。
To Be Continued~
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Python神经网络编程
[英]塔里克·拉希德(Tariq Rashid) / 林赐 / 人民邮电出版社 / 2018-4 / 69.00元
神经网络是一种模拟人脑的神经网络,以期能够实现类人工智能的机器学习 技术。 本书揭示神经网络背后的概念,并介绍如何通过Python实现神经网络。全书 分为3章和两个附录。第1章介绍了神经网络中所用到的数学思想。第2章介绍使 用Python实现神经网络,识别手写数字,并测试神经网络的性能。第3章带领读 者进一步了解简单的神经网络,观察已受训练的神经网络内部,尝试进一步改......一起来看看 《Python神经网络编程》 这本书的介绍吧!