2019-12-09はじめにみなさんはgRPCサーバの認証処理をどのように実装されているでしょうか?例えば、Go言語で実装する場合、grpc-ecosystemのinterceptorを利用する方法が人気があるようです。https://github.com/grpc-ecosystem/go-grpc-middlewareinterceptorで認証処理を実装するのは便利なのですが、特定のAPI(例えばヘルスチェック用API)のみを認証スキップしたい場合、どのように実装すればよいかぱっとわからなかったので、ここでまとめておきます。Interceptorのみを対象とします。gRPCの基本部分で自信が無い方は、よろしければ、いまさらだけどgRPCに入門したので分かりやすくまとめてみたをご覧ください。interceptorとは前提となるinterceptorの説明をします。ご存知の方は飛ばしてください。interceptorとは、認証処理、ログ、メッセージ仕様、バリデーション、リトライ、監視などのシステム開発における共通的な処理(ミドルウェア)です。grpc-ecosystemに組み込まれているので信頼性は高そうです。Streamingに対応しています。go-grpc-middleware配下にて各ミドルウェアのUnaryServerInterceptor関数が既に実装されています。logrusはこのような実装になっています。err}}下記は使い方の例です。NewServer関数にUnaryInterceptor関数を渡します。UnaryInterceptor関数にはChainUnaryServer関数を渡します。ChainUnaryServer関数には対象ミドルウェアのUnaryServerInterceptor関数を一つ以上指定します。ここでは、色々詰めています。)),)また、ChainUnaryServer関数でなくWithUnaryChainServer関数を使えば、もう少しだけシンプルに実装できます。interceptorは、必要なUnaryServerInterceptor関数を呼び出すだけで簡単にミドルウェアの実装ができるので楽です。(ただし、各種ミドルウェアの事前設定は必要です。ログであればログフォーマットやログレベルなど。)ヘルスチェックメソッドを認証スキップするここからが本題です。ヘルスチェックメソッドの実装まずは、対象となるヘルスチェックメソッドを用意します。ヘルスチェックのIFは下記のhealth.protoの通りです。これは、gRPC公式で用意されているprotoファイルとなります。https://github.com/grpc/grpc/blob/master/doc/health-checking.mdpb.goをprotocコマンドで自動生成しておきます。HealthCheckResponse);}ヘルスチェックサーバの実装は下記のhealth.goです。ここは好きなように実装すれば大丈夫です。構造体SkipAuthHealthServerにはhealth.HealthServerインターフェースを埋め込んでいます。(*SkipAuthHealthServer)(nil)により、構造体SkipAuthHealthServerはgrpc_auth.ServiceAuthFuncOverrideインターフェースを実装していることを明示的に示しています。構造体SkipAuthHealthServerはgrpc_authパッケージのAuthFuncOverrideをオーバーライドすることで、ServiceAuthFuncOverrideインターフェースを満たしています。health.HealthServerインターフェースはCheckメソッドとWatchメソッドのシグネチャを持っており、構造体SkipAuthHealthServerはそれらを実装しているため、health.HealthServerインターフェースを満たしています。今回はWatchメソッドは活用していません。version.")}interceptorを使った認証処理の実装interceptorを使って認証処理を実装していきます。認証方法はAPIキー認証とします。metadata.goは、HTTP2ヘッダ(metadata)に埋め込まれたauthorizationからトークンを抽出するコードです。nil}auth.goは、上記でmetadataから取得したトークンが正しいかを検証してコンテキストを返すコードです。AuthFunc関数の第二引数にfullMethodNameを持たせています。この値が認証パスしたいサービス(今回は/grpc.health.v1.Health/Check)の場合はトークンチェックせずにnilを返し、他のサービスの場合はAPIキーが正しいものかを確認します。fullMethodNameは自動生成されたpb.goで定義されているため、そこで確認できます。サンプルコードなので色々ベタ書きなのはご容赦ください。nil}}interceptor.goは、認証処理をgrpc.UnaryServerInterceptor型として返すことで、Chainできるようにします。nil}}main.goでは、WithUnaryServerChainで認証処理を設定します。本来は、ログ設定などもここで設定します。