认证
本文预览一下gRPC认证,包含内置已支持的认证机制,如何添加自己的认证系统和如何在支持的语言里使用gRPC认证的例子。
简介
gRPC设计可以和很多认证机制工作,可以很简单安全的使用gRPC与其他系统沟通。可以使用我们已支持的机制 - SSL/TLS使用或者不适用Google基于token认证 - 或者可以添加你自己的认证系统通过扩展我们提供的代码。
gRPC同样提供了简单的认证API,让你提供所有需要的认证信息作为凭证(Credentials)在创建channel或者请求时。
已支持的认证机制
下面的认证机制是gRPC内置的:
-
SSL/TLS: gRPC继承了SSL/TLS,并且提升了SSL/TLS认证服务器的使用,加密所有客户端和服务端的数据交换。为客户端提供相互认证的证书机制是可选的。
-
基于Google的Token认证:gRPC提供了通用的机制(下面介绍)对请求和响应附加metadata。另外支持使用tokens(一般是OAuth2 tokens)在通过gRPC使用Google API提供确认的认证流程:可以在下面代码例子里查看如何使用。一般这种机制必须是SSL/TLS的 - 没有SSL/TLS Google不允许连接,并且大多数gRPC语言实现不会让你在未加密的渠道上发送认证。
警告:谷歌认证只允许用于谷歌服务。如果发送一个谷歌的OAuth2 token到非谷歌服务会导致这个token被窃取然后滥用谷歌的服务。
认证API
gRPC提供了简单的认证API,基于统一认证对象的概念,可以用在创建整个gRPC管道或单个调用的时候。
凭证类型
两种凭证类型:
- Channel凭证,当访问到Channel时,比如SSL凭证。
- Call凭证,当触发一个调用时(或者C++里的ClientContext)。
也可以在CompositeChannelCredentials组合使用,允许你指定,比如SSL 凭证给channel,然后这个channel上的每个请求用call凭证。CompositeChannelCredentials结合ChannelCredentials和CallCredentials来创建一个新的ChannelCredentials。结果发送认证数据与channel上的每个请求CallCredentialswith组合。
比如,你可以创建SslCredentials的ChannelCredentials和AccessTokenCredentials。结果就是在Channel上使用时将会为Channel上的每个请求发送合适的认证token。
单个CallCredentials同样可以与CompositeCallCredentials组合。这会当CallCredentials使用在请求上是将会触发发送认证数据和两个CallCredentials。
使用客户端SSL/TLS
现在我们看一下Credentials如何与我们支持的认证机制合作。这是最简单的认证场景:客户端指向认证服务器并加密所有的数据。例子是用C++,但是API对所有的语言都相同:你可以在我们下面的例子里看到很多语言里如何启用SSL/TLS:
// Create a default SSL ChannelCredentials object.
auto channel_creds = grpc::SslCredentials(grpc::SslCredentialsOptions());
// Create a channel using the credentials created in the previous step.
auto channel = grpc::CreateChannel(server_name, channel_creds);
// Create a stub on the channel.
std::unique_ptr<Greeter::Stub> stub(Greeter::NewStub(channel));
// Make actual RPC calls on the stub.
grpc::Status s = stub->sayHello(&context, *request, response);
对于更高级的案例,比如修改根CA或者使用客户端证书,相应的选项可以在SslCredentialsOptions参数里设置传递给工厂方法。
基于谷歌token认证
gRPC应用可以使用简单的API创建认证,用于和Google各种开发场景认证。同样,我们的例子是C++,但是你可以找到其他语言的例子:
auto creds = grpc::GoogleDefaultCredentials();
// Create a channel, stub and make RPC calls (same as in the previous example)
auto channel = grpc::CreateChannel(server_name, creds);
std::unique_ptr<Greeter::Stub> stub(Greeter::NewStub(channel));
grpc::Status s = stub->sayHello(&context, *request, response);
这个channel认证对象用于使用Service Accounts的应用,以及运行在Google Compute Engine (GCE)的应用.
在前一种情况下,服务账户的私钥会被环境变量里的GOOGLE_APPLICATION_CREDENTIALS文件加载。密钥用于生成无记名令牌附加到每个发送的RPC在相应的channel。
对于运行在GCE里的应用程序,默认的服务账户和相应的OAuth2域可以在VM设置期间配置。在运行时,这个凭证处理和认证系统通信获取OAuth2访问tokens,然后加在相应的channel里的发送的RPC。
扩展gRPC支持其他认证机制
认证插件API允许开发者加入自己的认证类型。这包括:
- MetadataCredentialsPlugin抽象类,包含纯净的虚拟GetMetadata方法,需要被开发者创建的子类实现。
- MetadataCredentialsFromPlugin方法,在MetadataCredentialsPlugin插件里创建一个CallCredentials。
下面是一个简单认证插件例子,在自定义header里设置凭证ticket:
class MyCustomAuthenticator : public grpc::MetadataCredentialsPlugin {
public:
MyCustomAuthenticator(const grpc::string& ticket) : ticket_(ticket) {}
grpc::Status GetMetadata(
grpc::string_ref service_url, grpc::string_ref method_name,
const grpc::AuthContext& channel_auth_context,
std::multimap<grpc::string, grpc::string>* metadata) override {
metadata->insert(std::make_pair("x-custom-auth-ticket", ticket_));
return grpc::Status::OK;
}
private:
grpc::string ticket_;
};
auto call_creds = grpc::MetadataCredentialsFromPlugin(
std::unique_ptr<grpc::MetadataCredentialsPlugin>(
new MyCustomAuthenticator("super-secret-ticket")));
更深层次的插件集成实现gRPC凭证实现是在源代码等级。gRPC内部同样可以与其他加密实现交换SSL/TLS。
示例
这些认证机制可以在gRPC支持的所有语言里可用。下面会演示如何认证和在每个语言里上面讲述的认证特性:更多语言准备中。
Java
基础案例 - 无需加密或认证
ManagedChannel channel = ManagedChannelBuilder.forAddress("localhost", 50051)
.usePlaintext(true)
.build();
GreeterGrpc.GreeterStub stub = GreeterGrpc.newStub(channel);
使用服务端认证SSL/TLS
如果gRPC是TLS的,我们建议在Java里使用OpenSSL。可以在Security文档里查看如何安装和使用OpenSSL和其他必须的库。
在服务端启用TLS,证书链和密钥需要制定为PEM格式。标准的TLS端口是443,但是下面我们使用8443,以免需要系统的额外权限。
Server server = ServerBuilder.forPort(8443)
// Enable TLS
.useTransportSecurity(certChainFile, privateKeyFile)
.addService(TestServiceGrpc.bindService(serviceImplementation))
.build();
server.start();
如果客户端不知道发行证书机构,那么正确的配置SslContext或者SSLSocketFactory应该提供给NettyChannelBuilder或者OkHttpChannelBuilder。
在客户端,服务端的SSL/TLS认证看起来像:
// With server authentication SSL/TLS
ManagedChannel channel = ManagedChannelBuilder.forAddress("myservice.example.com", 443)
.build();
GreeterGrpc.GreeterStub stub = GreeterGrpc.newStub(channel);
// With server authentication SSL/TLS; custom CA root certificates; not on Android
ManagedChannel channel = NettyChannelBuilder.forAddress("myservice.example.com", 443)
.sslContext(GrpcSslContexts.forClient().trustManager(new File("roots.pem")).build())
.build();
GreeterGrpc.GreeterStub stub = GreeterGrpc.newStub(channel);
谷歌认证
下面的代码片段演示如何使用服务账户和gRPC调用Google Cloud PubSub API。证书从已知的位置加载或者通过程序运行环境提供的自动发现,比如,Google Compute Engine。这个例子是指定谷歌和他的服务的,其他服务提供者可以使用相同的模式。
GoogleCredentials creds = GoogleCredentials.getApplicationDefault();
ManagedChannel channel = ManagedChannelBuilder.forTarget("greeter.googleapis.com")
.build();
GreeterGrpc.GreeterStub stub = GreeterGrpc.newStub(channel)
.withCallCredentials(MoreCallCredentials.from(creds));