安全认证

认证

gRPC 被设计成可以利用插件的形式支持多种授权机制。本文档对多种支持的授权机制提供了一个概览,并且用例子来论述对应API,最后就其扩展性作了讨论。 马上将会推出更多文档和例子。

支持的授权机制

SSL/TLS

gRP 集成 SSL/TLS 并对服务端授权所使用的 SSL/TLS 进行了改良,对客户端和服务端交换的所有数据进行了加密。对客户端来讲提供了可选的机制提供凭证来获得共同的授权。

OAuth 2.0

gRPC 提供通用的机制(后续进行描述)来对请求和应答附加基于元数据的凭证。当通过 gRPC 访问 Google API 时,会为一定的授权流程提供额外的获取访问令牌的支持,这将通过以下代码例子进行展示。 警告:Google OAuth2 凭证应该仅用于连接 Google 的服务。把 Google 对应的 OAuth2 令牌发往非 Google 的服务会导致令牌被窃取用作冒充客户端来访问 Google 的服务。

API

为了减少复杂性和将混乱最小化, gRPC 以一个统一的凭证对象来进行工作。 凭证可以是以下两类:

  • 频道凭证, 被附加在 频道上, 比如 SSL 凭证。
  • 调用凭证, 被附加在调用上(或者 C++ 里的 客户端上下文)。 凭证可以用组合频道凭证来进行组合。一个组合频道凭证可以将一个频道凭证和一个调用凭证关联创建一个新的频道凭证。结果在这个频道上的每次调用会发送组合的调用凭证来作为授权数据。 例如,一各频道凭证可以由一个Ssl 凭证和一个访问令牌凭证生成。结果是在这个频道上的每次调用都会发送对应的访问令牌。 调用凭证可以用 组合凭证来组装。组装后的 调用凭证应用到一个客户端上下文里,将触发发送这两个调用凭证的授权数据。

服务端认证加密使用的 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, 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 参数里的相应选项进行设置。

通过 Google 进行认证

gRPC应用可以使用一个简单的API来创建一个可以工作在不同部署场景下的凭证。

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);

这个应用使用的频道凭证对象就像 Google 计算引擎 (GCE)里运行的应用一样使用服务账号。在前面的案例里,服务账号的密钥从环境变量 GOOGLE_APPLICATION_CREDENTIALS 对应的文件里加载。这些密钥被用来生成承载令牌附加在在相应频道的每次 RPC 调用里。 对于 GCE 里运行的应用,可以在虚拟机设置的时候为其配置一个默认的服务账号和相应的 OAuth2 范围。在运行时,这个凭证被用来与认证系统通讯来获取 OAuth2 访问令牌并且把令牌用作在相应的频道上的 RPC 调用。

扩展 gRPC 支持其他的认证机制

相应的凭证插件 API 允许开发者开发自己的凭证插件。

  • MetadataCredentialsPlugin 抽象类包含需要被开发者创建的子类实现的纯虚方法 GetMetadata
  • MetadataCredentialsFromPlugin 方法可以从 MetadataCredentialsPlugin 创建一个 调用者凭证。 这类有个简单的凭证插件例子,是通过在自定义头了设置一个认证票据。
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 支持的语言里提供。以下的一些节里展示了上文提到的认证和授权在每种语言里如何实现:很快将会推出更多语言的支持。

Go

基本案例 – 无加密/授权

Client:

conn, _ := grpc.Dial("localhost:50051", grpc.WithTransportCredentials(insecure.NewCredentials()))
// error handling omitted
client := pb.NewGreeterClient(conn)
// ...

Server:

s := grpc.NewServer()
lis, _ := net.Listen("tcp", "localhost:50051")
// error handling omitted
s.Serve(lis)

通过 SSL/TLS 进行服务端授权和加密

Client:

creds, _ := credentials.NewClientTLSFromFile(certFile, "")
conn, _ := grpc.Dial("localhost:50051", grpc.WithTransportCredentials(creds))
// error handling omitted
client := pb.NewGreeterClient(conn)
// ...

Server:

creds, _ := credentials.NewServerTLSFromFile(certFile, keyFile)
s := grpc.NewServer(grpc.Creds(creds))
lis, _ := net.Listen("tcp", "localhost:50051")
// error handling omitted
s.Serve(lis)

通过 Google 进行授权

pool, _ := x509.SystemCertPool()
// error handling omitted
creds := credentials.NewClientTLSFromCert(pool, "")
perRPC, _ := oauth.NewServiceAccountFromFile("service-account.json", scope)
conn, _ := grpc.Dial(
    "greeter.googleapis.com",
    grpc.WithTransportCredentials(creds),
    grpc.WithPerRPCCredentials(perRPC),
)
// error handling omitted
client := pb.NewGreeterClient(conn)
// ...