認証

組み込みの認証メカニズムと独自の認証システムのプラグイン方法を含む、gRPC認証の概要です。

認証

組み込みの認証メカニズムと独自の認証システムのプラグイン方法を含む、gRPC認証の概要です。

概要

gRPCは様々な認証メカニズムと連携するように設計されており、他のシステムとの安全な通信にgRPCを容易に使用できます。サポートされているメカニズム(Googleトークンベース認証の有無にかかわらずSSL/TLS)を使用するか、提供されたコードを拡張することで独自の認証システムをプラグインできます。

gRPCは、チャネルの作成時または呼び出しを行う際に、必要な認証情報をすべてCredentialsとして提供できるシンプルな認証APIも提供します。

サポートされている認証メカニズム

以下の認証メカニズムはgRPCに組み込まれています。

  • SSL/TLS: gRPCはSSL/TLS統合を備えており、サーバーの認証と、クライアントとサーバー間で交換されるすべてのデータの暗号化にSSL/TLSの使用を推奨しています。相互認証のためにクライアントが証明書を提供するためのオプションメカニズムも用意されています。
  • ALTS: アプリケーションがCompute EngineまたはGoogle Kubernetes Engine (GKE)上で実行されている場合、gRPCはトランスポートセキュリティメカニズムとしてALTSをサポートしています。詳細については、以下の言語固有のページを参照してください。C++でのALTSGoでのALTSJavaでのALTSPythonでのALTS.
  • Googleによるトークンベース認証: gRPCは、メタデータベースの資格情報をリクエストとレスポンスに添付するための汎用メカニズム(下記参照)を提供します。gRPCを介してGoogle APIにアクセスする際のアクセストークン(通常はOAuth2トークン)の取得に関する追加のサポートは、特定の認証フローに対して提供されています。これは、下記のコード例で確認できます。一般的に、このメカニズムはチャネル上のSSL/TLSと併用する必要があります。GoogleはSSL/TLSを使用しない接続を許可せず、ほとんどのgRPC言語実装では暗号化されていないチャネルで資格情報を送信することはできません。

認証API

gRPCは、統一された概念であるCredentialsオブジェクトに基づいたシンプルな認証APIを提供します。これは、gRPCチャネル全体または個々の呼び出しを作成する際に使用できます。

資格の種類

資格情報は2つのタイプがあります。

  • チャネル資格情報は、SSL資格情報など、Channelに添付されます。
  • 呼び出し資格情報は、呼び出し(またはC++のClientContext)に添付されます。

これらをCompositeChannelCredentialsで組み合わせることもでき、たとえば、チャネルのSSL詳細と、そのチャネルで行われる各呼び出しの呼び出し資格情報を指定できます。CompositeChannelCredentialsChannelCredentialsCallCredentialsを関連付けて新しいChannelCredentialsを作成します。その結果、合成されたCallCredentialsに関連付けられた認証データが、チャネルで行われたすべての呼び出しで送信されます。

たとえば、SslCredentialsAccessTokenCredentialsからChannelCredentialsを作成できます。Channelに適用されると、このチャネルの各呼び出しに適切なアクセストークンが送信されます。

個々のCallCredentialsは、CompositeCallCredentialsを使用して合成することもできます。呼び出しで使用される結果のCallCredentialsは、2つのCallCredentialsに関連付けられた認証データの送信をトリガーします。

クライアントサイドSSL/TLSの使用

では、サポートされている認証メカニズムの1つで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パラメーターで対応するオプションを設定できます。

OAuthトークンベース認証の使用

OAuth 2.0プロトコルは、承認のための業界標準プロトコルです。OAuthトークンを使用して、Webサイトまたはアプリケーションがユーザーアカウントへの限定的なアクセスを取得できるようにします。

gRPCは、OAuth 2.0をアプリケーションに統合するためのシンプルなAPIセットを提供し、認証を簡素化します。

高いレベルでは、OAuthトークンベース認証の使用には3つのステップが含まれます。

  1. クライアント側でOAuthトークンを取得または生成します。
    • 以下の手順に従って、Google固有のトークンを生成できます。
  2. OAuthトークンを使用して資格情報を生成します。
    • OAuthトークンは常に呼び出しごとの資格情報の一部です。呼び出しごとの資格情報をいくつかのチャネル資格情報に添付することもできます。
    • トークンは、通常HTTP Authorizationヘッダーの一部としてサーバーに送信されます。
  3. サーバー側はトークンを検証します。
    • ほとんどの実装では、検証はサーバーサイドインターセプターを使用して行われます。

様々な言語でOAuthトークンを使用する方法の詳細については、以下の例を参照してください。

Googleトークンベース認証の使用

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

このチャネル資格情報オブジェクトは、サービスアカウントを使用するアプリケーションと、Google Compute Engine (GCE)で実行されているアプリケーションの両方で機能します。前者の場合、サービスアカウントの秘密鍵は環境変数GOOGLE_APPLICATION_CREDENTIALSで指定されたファイルからロードされます。キーを使用して、対応するチャネルの各発信RPCに添付されるベアラートークンが生成されます。

GCEで実行されているアプリケーションの場合、VM設定時にデフォルトのサービスアカウントと対応するOAuth2スコープを設定できます。実行時に、この資格情報は認証システムとの通信を処理してOAuth2アクセストークンを取得し、対応するチャネルの各発信RPCに添付します。

他の認証メカニズムをサポートするためのgRPCの拡張

CredentialsプラグインAPIにより、開発者は独自の資格の種類をプラグインできます。これには以下が含まれます。

  • MetadataCredentialsPlugin抽象クラス。これは、開発者が作成したサブクラスで実装する必要がある純粋仮想GetMetadataメソッドを含みます。
  • MetadataCredentialsFromPlugin関数。これは、MetadataCredentialsPluginからCallCredentialsを作成します。

カスタムヘッダーに認証チケットを設定するシンプルな資格情報プラグインの例を以下に示します。

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のサポートされているすべての言語で使用できます。次の表は、様々な言語での認証と認可を示す例へのリンクを示しています。

言語ドキュメント
C++N/AN/A
GoGoの例Goのドキュメント
JavaJavaの例(TLS) (Javaの例(ATLS))Javaのドキュメント
PythonPythonの例Pythonのドキュメント

OAuthトークンベース認証の言語ガイドと例

次の表は、様々な言語でのOAuthトークンベース認証と認可を示す例へのリンクを示しています。

言語ドキュメント
C++N/AN/A
GoGoのOAuth例GoのOAuthドキュメント
JavaJavaのOAuth例JavaのOAuthドキュメント
PythonPythonのOAuth例PythonのOAuthドキュメント

追加の例

次のセクションでは、上記で説明した認証と認可機能が、上記にリストされていない他の言語でどのように表示されるかを示します。

Ruby

基本ケース - 暗号化または認証なし

stub = Helloworld::Greeter::Stub.new('localhost:50051', :this_channel_is_insecure)
...
サーバー認証SSL/TLS付き
creds = GRPC::Core::ChannelCredentials.new(load_certs)  # load_certs typically loads a CA roots file
stub = Helloworld::Greeter::Stub.new('myservice.example.com', creds)
Googleによる認証
require 'googleauth'  # from http://www.rubydoc.info/gems/googleauth/0.1.0
...
ssl_creds = GRPC::Core::ChannelCredentials.new(load_certs)  # load_certs typically loads a CA roots file
authentication = Google::Auth.get_application_default()
call_creds = GRPC::Core::CallCredentials.new(authentication.updater_proc)
combined_creds = ssl_creds.compose(call_creds)
stub = Helloworld::Greeter::Stub.new('greeter.googleapis.com', combined_creds)

Node.js

基本ケース - 暗号化/認証なし
var stub = new helloworld.Greeter('localhost:50051', grpc.credentials.createInsecure());
サーバー認証SSL/TLS付き
const root_cert = fs.readFileSync('path/to/root-cert');
const ssl_creds = grpc.credentials.createSsl(root_cert);
const stub = new helloworld.Greeter('myservice.example.com', ssl_creds);
Googleによる認証
// Authenticating with Google
var GoogleAuth = require('google-auth-library'); // from https://www.npmjs.com/package/google-auth-library
...
var ssl_creds = grpc.credentials.createSsl(root_certs);
(new GoogleAuth()).getApplicationDefault(function(err, auth) {
  var call_creds = grpc.credentials.createFromGoogleCredential(auth);
  var combined_creds = grpc.credentials.combineChannelCredentials(ssl_creds, call_creds);
  var stub = new helloworld.Greeter('greeter.googleapis.com', combined_credentials);
});
OAuth2トークンを使用したGoogleによる認証(レガシーアプローチ)
var GoogleAuth = require('google-auth-library'); // from https://www.npmjs.com/package/google-auth-library
...
var ssl_creds = grpc.Credentials.createSsl(root_certs); // load_certs typically loads a CA roots file
var scope = 'https://www.googleapis.com/auth/grpc-testing';
(new GoogleAuth()).getApplicationDefault(function(err, auth) {
  if (auth.createScopeRequired()) {
    auth = auth.createScoped(scope);
  }
  var call_creds = grpc.credentials.createFromGoogleCredential(auth);
  var combined_creds = grpc.credentials.combineChannelCredentials(ssl_creds, call_creds);
  var stub = new helloworld.Greeter('greeter.googleapis.com', combined_credentials);
});
サーバー認証SSL/TLSとトークン付きカスタムヘッダー
const rootCert = fs.readFileSync('path/to/root-cert');
const channelCreds = grpc.credentials.createSsl(rootCert);
const metaCallback = (_params, callback) => {
    const meta = new grpc.Metadata();
    meta.add('custom-auth-header', 'token');
    callback(null, meta);
}
const callCreds = grpc.credentials.createFromMetadataGenerator(metaCallback);
const combCreds = grpc.credentials.combineChannelCredentials(channelCreds, callCreds);
const stub = new helloworld.Greeter('myservice.example.com', combCreds);

PHP

基本ケース - 暗号化/認証なし
$client = new helloworld\GreeterClient('localhost:50051', [
    'credentials' => Grpc\ChannelCredentials::createInsecure(),
]);
サーバー認証SSL/TLS付き
$client = new helloworld\GreeterClient('myservice.example.com', [
    'credentials' => Grpc\ChannelCredentials::createSsl(file_get_contents('roots.pem')),
]);
Googleによる認証
function updateAuthMetadataCallback($context)
{
    $auth_credentials = ApplicationDefaultCredentials::getCredentials();
    return $auth_credentials->updateMetadata($metadata = [], $context->service_url);
}
$channel_credentials = Grpc\ChannelCredentials::createComposite(
    Grpc\ChannelCredentials::createSsl(file_get_contents('roots.pem')),
    Grpc\CallCredentials::createFromPlugin('updateAuthMetadataCallback')
);
$opts = [
  'credentials' => $channel_credentials
];
$client = new helloworld\GreeterClient('greeter.googleapis.com', $opts);
OAuth2トークンを使用したGoogleによる認証(レガシーアプローチ)
// the environment variable "GOOGLE_APPLICATION_CREDENTIALS" needs to be set
$scope = "https://www.googleapis.com/auth/grpc-testing";
$auth = Google\Auth\ApplicationDefaultCredentials::getCredentials($scope);
$opts = [
  'credentials' => Grpc\Credentials::createSsl(file_get_contents('roots.pem'));
  'update_metadata' => $auth->getUpdateMetadataFunc(),
];
$client = new helloworld\GreeterClient('greeter.googleapis.com', $opts);

Dart

基本ケース - 暗号化または認証なし
final channel = new ClientChannel('localhost',
      port: 50051,
      options: const ChannelOptions(
          credentials: const ChannelCredentials.insecure()));
final stub = new GreeterClient(channel);
サーバー認証SSL/TLS付き
// Load a custom roots file.
final trustedRoot = new File('roots.pem').readAsBytesSync();
final channelCredentials =
    new ChannelCredentials.secure(certificates: trustedRoot);
final channelOptions = new ChannelOptions(credentials: channelCredentials);
final channel = new ClientChannel('myservice.example.com',
    options: channelOptions);
final client = new GreeterClient(channel);
Googleによる認証
// Uses publicly trusted roots by default.
final channel = new ClientChannel('greeter.googleapis.com');
final serviceAccountJson =
     new File('service-account.json').readAsStringSync();
final credentials = new JwtServiceAccountAuthenticator(serviceAccountJson);
final client =
    new GreeterClient(channel, options: credentials.toCallOptions);
単一RPC呼び出しの認証
// Uses publicly trusted roots by default.
final channel = new ClientChannel('greeter.googleapis.com');
final client = new GreeterClient(channel);
...
final serviceAccountJson =
     new File('service-account.json').readAsStringSync();
final credentials = new JwtServiceAccountAuthenticator(serviceAccountJson);
final response =
    await client.sayHello(request, options: credentials.toCallOptions);