gRPCでセキュアなAPIを構築する
Vitalis Ogbonna
2022年8月25日
0 分で読めますGoogleのリモートプロシージャコール(gRPC)は、リモートプロシージャコール(RPC)フレームワークのGoogleによるオープンソース版です。HTTP/2とProtocol Buffers(protobuf)技術を活用した通信プロトコルです。gRPCを使うと、リモートのクライアントやサーバーは、受信側サーバーの関数をローカルにあるかのように呼び出すだけで、そのサーバーと通信できます。これにより、分散システムでクライアントとサーバー間の通信や大規模なデータセットの転送が簡単になります。
他のRPCシステムと同様に、gRPCではサービスを定義します。サービスのメソッドと戻り値の型は、Googleのシリアライズ/デシリアライズプロトコルであるprotobufを使って指定します。これにより、サービスを簡単に定義し、クライアントライブラリを自動生成できます。gRPCでは現在バージョン3のこのプロトコルを、インターフェース定義言語およびシリアライズツールセットとして使用します。
ほとんどの最新アプリケーションにとって、gRPCはあらゆるデータ型を優れた形でサポートする選択肢です。ストリーミングデータなど、大量のデータを扱う用途に最適です。一方、大規模なデータ転送があまり重要でないシンプルなアプリケーションでは、オーバースペックになる可能性があります。
この記事では、2つのNode.jsアプリケーション間でクライアントとサーバーのような通信を行い、gRPCを使用する方法を紹介します。また、サービスの通信手段としてgRPCを使う際の安全対策についても解説します。
チュートリアルの前提条件
このチュートリアルを進めるには、PCにOpenSSLとNode.js(バージョン4.0以降)がインストールされている必要があります。Node.jsとJavaScriptの基本的な知識も必要です。また、作業環境で管理者権限があることを確認してください。
Node.jsプロジェクトのセットアップ
まず、アプリケーションのフォルダー構成を設定します。event-app-node-grpcというフォルダーを作成し、次のコマンドを入力してnpmでNode.jsプロジェクトを初期化します。
アプリケーションを初期化したら、次のフォルダー構成を作成します。このチュートリアルで使用する動作するコード一式はGitHubで確認できます。
パッケージのインストール
ターミナルでアプリケーションのルートディレクトリに移動します。次のコードスニペットのように、npm installコマンドを使って以下のパッケージをインストールします。
先ほどのコードスニペットでインストールしたパッケージを確認しましょう。
Expressは、アプリケーションのHTTPサーバーです。@grpc/grpc-jsはNode.js用のgRPCライブラリです。Node.jsランタイムでgRPCサービスを作成できます。@grpc/proto-loaderは、gRPCで使用するprotobufファイルの読み込みに必要なパッケージです。protobuf.jsのバージョン3パッケージを使用します。
上記のパッケージをインストールしたら、package.jsonファイルを開き、次のコードスニペットのようにscriptsタグへ追加の設定を記述します。
上のコードスニペットに示した追加設定は、アプリケーションの実行時設定とSSL証明書の生成に使用します。これらの設定を追加すると、更新後のpackage.jsonファイルは次のコードスニペットのようになります。
上のコードスニペットは、アプリケーションの実行時設定とSSL証明書の生成コマンドをscriptsタグに追加した後のpackage.jsonファイルです。
Protocol Buffersの定義
このチュートリアルでは、シンプルなイベント追跡アプリケーションでgRPCを使う方法を紹介します。このデモアプリケーションでは、イベントの詳細を受け取り、インメモリデータベースに保存します。また、イベントデータの更新、取得、削除もできます。
gRPCアプリケーションでは、異なるアプリケーション間の通信を可能にするため、サービスインターフェースと必要なペイロードをprotobufファイルに記述します。プロジェクトのセットアップスキーマに示すように、protobufファイルの拡張子は.protoです。
次に、アプリケーションのルートディレクトリにevents.protoファイルを作成し、以下のコードを追加します。参考として、先ほど定義したプロジェクト構造のスキーマを確認してください。
上記のproto定義のコードスニペットでは、まずsyntax = "proto3"という定義でProtocol Buffersのバージョンを指定し、続いてプロトコルサービスを定義しました。
次に、プロトコルのイベントサービスの説明でEventServiceというサービスを作成しました。このサービス内にrpc関数を作成し、必要なパラメーターと期待される戻り値も指定しました。アプリケーションの要件に応じてサービスをいくつでも定義できますが、ここではシンプルにするため1つだけ定義します。
また、EventServiceの定義内でrpc関数のデータ型と戻り値を定義し、gRPC固有のフィールド番号を指定しました。これはエンコード時に使用されるバイト数を表します。詳しくはprotobufの公式ドキュメントをご覧ください。
gRPCサーバーの作成
上記のフォルダー構成に従い、アプリケーションのルートディレクトリにserverフォルダーを作成し、その中にindex.jsファイルを作成します。新しく作成したserver/index.jsファイルに、次のコードスニペットを貼り付けます。
上記のコードスニペットでは、先ほど定義したevents.protoファイルをインポートし、変数PROTO_PATHに格納しました。次に、protoLoaderライブラリのloadSyncメソッドを使って読み込みました。その後、すべてのproto定義を格納するeventsProto変数に、proto定義を保存しました。
次に、先ほど定義したserver/index.jsファイルで、eventsProto変数の直後に以下のコードスニペットを追加します。
上記のコードスニペットでは、node:cryptoパッケージとそのrandomUUID関数を読み込みました。この関数はイベントID用のランダムで一意な文字列を生成します。このチュートリアルではインメモリデータベースを使うため、イベント一覧を保存する配列を定義し、新しいgrpc.Serverメソッドを呼び出してサーバーインスタンスを設定します。
次に、アプリケーションサービスを登録します。そのために、以下のコードスニペットを追加してください。上記のコードスニペットにあるserver変数の直後に配置します。
上記のコードスニペットでは、gRPCサーバーインスタンスでaddServiceメソッドを呼び出して、アプリケーションサービスを登録しました。これは基本的に、イベントの作成、読み取り、更新を行う処理です。
アプリケーションサーバーを起動できるようにするには、上記のコードスニペットにあるaddServiceメソッドの直後に以下のコードスニペットを貼り付けます。
gRPCクライアントの作成
上記のフォルダー構成に従い、アプリケーションのルートディレクトリにclientフォルダーを作成します。作成したclientフォルダー内にindex.jsファイルとapp.jsファイルを作成し、client/app.jsファイルに以下のコードスニペットを貼り付けます。
上記のコードスニペットでは、先ほど作成したproto定義をインポートし、protoLoaderで読み込みました。また、grpcクライアントをサーバーアプリケーションのIPアドレスに接続し、clientという変数名でイベントサービスをエクスポートしました。さらに、クライアントとサーバー間の通信を認証・暗号化するため、クライアントにSSL証明書を設定しました。
次に、client/index.jsファイルに以下のコードスニペットを貼り付けます。
上記のコードスニペットでは、client/app.jsファイルからevent-serviceをインポートしています。次に、gRPCを使ってサーバーアプリケーションをリモートで呼び出し、イベントのcreation、update、fetch、deleteを行うシンプルなエンドポイントを備えたExpressサーバーを設定しました。
サーバーとクライアントのアプリケーションをテストする
ここまでできたら、作業が正しく進んでいるか確認するためにテストしましょう。
サーバー
ターミナルでプロジェクトのルートディレクトリに移動し、以下のコマンドを実行します。
サーバーアプリケーションがhttp://localhost:50051で起動します。
クライアント
新しいターミナルウィンドウを開き、アプリケーションのルートディレクトリからclientフォルダーに移動して、以下のコマンドを実行します。
アプリケーションがhttp://localhost:50050で起動します。
テストするには、ブラウザーでlocalhost:50050にアクセスするか、PostmanなどのAPIテストツールを使用します。最初にevents配列へ追加したデフォルトのイベントが表示されます。次のスクリーンショットと同じレスポンスが返されるはずです。

gRPC APIの認証とセキュリティ保護
gRPCプロトコルはさまざまな認証方式をサポートしており、新規システムにも既存システムにも簡単に適応できます。gRPCのクライアントとサーバー間の通信には、Googleのトークンベース認証を併用するかどうかにかかわらず、SSLやTLSなどの推奨方式を使って認証を実装できます。また、gRPCに組み込まれた認証関数を拡張するだけで、カスタム認証を構築することも可能です。
gRPCには、デフォルトで次の認証方式が用意されています。
SSLとTLS:サーバーを認証し、クライアントとサーバー間でやり取りするデータを暗号化
ALTS(Googleが設計した相互トランスポート認証プロトコル):Google Cloud Platform(GCP)で稼働するアプリケーションのRPC通信を保護
汎用トークンベース認証:メタデータベースの認証情報をリクエストとレスポンスに付加
チュートリアルの導入部で触れたとおり、ここではSSLを使った認証を実装します。その後、この変更に対応するため、client/app.jsファイルとserver/index.jsファイルのコードを修正します。
OpenSSLでSSL証明書を生成する
まず、OpenSSLを使ってSSL証明書を生成します。この手順ではOpenSSLがインストールされている必要があります。また、bashスクリプトを実行する権限も必要です。権限エラーを避けるために、これらを確認しておきましょう。
フォルダー構成にscriptsフォルダーを作成し、その中にgenerate-certs.shというファイルを作成します。以下のコードスニペットをそのファイルに貼り付けます。
上記のコードは、サーバーとクライアントのアプリケーション間でセキュアに暗号化された接続を確立するために必要なSSL証明書を生成します。実行するとcertsフォルダーが作成され、OpenSSLを使ってサーバーとクライアントのSSL証明書が生成されて、certsフォルダーに保存されます。これらの設定の詳細や役割については、OpenSSLのウェブサイトをご覧ください。
npmでアプリケーションのSSL証明書を生成する
次に、アプリケーションのルートディレクトリでターミナルを開き、以下のコマンドを実行してスクリプトからアプリケーション用のSSL証明書を生成します。
生成されたSSL証明書を含むcertsフォルダーが作成されます。
管理者権限が必要です。スクリプトの実行時に権限エラーが発生した場合は、以下のコマンドでスクリプトに実行権限を付与してから、もう一度試してください。
ターミナルに次のような出力が表示されます。
client/app.jsファイルとserver/index.jsファイルの更新
ここまでで、gRPC APIの認証に必要なSSL証明書を生成しました。次に、生成した証明書を使えるようにclient/index.jsファイルとserver/index.jsファイルを修正します。
以下に示す更新後のclient/app.jsファイルでは、生成された証明書を読み込むためにfsモジュールを追加しました。続いて、その証明書を使ってgRPCのSSL認証情報を作成し、最後にその認証情報をgRPCサービスに適用しました。
以下に示す更新後のserver/index.jsファイルにも、生成された証明書を読み込むためにfsモジュールを追加しました。その証明書を使ってgRPCのSSL認証情報を作成し、サーバーに適用しました。
サーバーとクライアントのアプリケーションを実行する
gRPCの仕様を使ったイベント管理ソリューションを実装できました。エンドポイントをテストするには、以下の手順に沿ってターミナルからアプリケーションを起動します。
サーバー
ターミナルでプロジェクトのルートディレクトリに移動し、次のコマンドを実行します。
実行すると、サーバーアプリケーションが http://0.0.0.0:50051 で起動します。
クライアント
新しいターミナルウィンドウを開き、アプリケーションのルートディレクトリから client フォルダに移動して、次のコマンドを実行します。
実行すると、クライアントアプリケーションが http://localhost:50050 で起動します。
アプリケーションをテストするには、ブラウザーで localhost:50050 にアクセスするか、Postman などの API テストツールを使用します。最初に events 配列に追加したデフォルトのイベントが表示されるはずです。レスポンスは、以下のスクリーンショットと同じになるはずです。

アプリケーションに追加したほかのエンドポイントもテストし、すべてが想定どおりに動作することを確認しましょう。
gRPCでセキュアなAPIを構築しました!
このチュートリアルでは、Node.jsを使ってgRPCでシンプルなAPIを構築し、その動作の仕組みと、APIセキュリティを高めるエンドツーエンドの認証・暗号化を実現するHTTP/2やSSL/TLSなど、数多くのメリットを紹介しました。
こうしたメリットがある一方で、gRPCにはブラウザーのサポートが限られていること、人間が読めないデータ形式、学習曲線の急さ、エッジキャッシュのサポートが不十分なことなど、弱点もあります。しかし、こうした制約があるにもかかわらず、優れたパフォーマンスと多言語対応により、内部マイクロサービス間の通信にはgRPCが最適です。gRPCプロトコルは優れており、2016年8月のリリース以来、業界で大きく普及してきました。今後も成長を続けていくでしょう。
gRPCでできることは、ほかにもたくさんあります。このチュートリアルの例は、gRPCで実現できることのほんの一部にすぎません。ドキュメントを確認してgRPCに関する知識を深め、アプリケーションの通信プロセスやgRPCセキュリティを維持するための戦略を強化しましょう。
