Go 言語による gRPC の認証
Go 言語での gRPC の開発を行う際には interceptor というミドルウェアを使用して認証を行うことが多いと思います。gRPC における interceptor とは認証以外にもログ、メッセージ仕様、バリデーション、監視などを共通で処理出来るもので grpc-ecosystem に組み込まれています。
https://github.com/grpc-ecosystem/go-grpc-middleware
その中でも今回は 認証処理をする grpc-auth について見ていきます。
https://github.com/grpc-ecosystem/go-grpc-middleware/tree/master/auth
はじめに今回のサンプルに使用するプロトコルを定義します。
リクエストメッセージを受け取り、レスポンスメッセージを返すだけの簡単なものです。
・プロトコル定義
service EchoSVC {
rpc Echo(Request) returns (Response);
rpc EchoAuthSkip(Request) returns (Response);
}
message Request {
string message = 1;
}
message Response {
string message = 1;
}
続いて gRPC サーバーを実装します。
grpc-auth は import の部分で grpc_auth と定義しており、main 関数のNewServer の部分で使用しています。grpc_auth.UnaryServerInterceptor の引数に自分で定義した認証の関数 authenticate を渡します。(今回はサンプルなので実装は適当です。)
・gRPC サーバーの実装
package main
import (
"fmt"
"grpc-example/pb"
"log"
"net"
grpc_auth "github.com/grpc-ecosystem/go-grpc-middleware/auth"
"golang.org/x/net/context"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)
const (
port = ":8080"
)
// server is used to implement EchoSVCServer
type server struct{}
// Echo is endpoint to demo
func (s *server) Echo(ctx context.Context, req *pb.Request) (*pb.Response, error) {
return nil, status.Errorf(codes.Unimplemented, "method Echo not implemented")
}
func authenticate(ctx context.Context) (context.Context, error) {
token, err := grpc_auth.AuthFromMD(ctx, "bearer")
if err != nil {
return nil, err
}
fmt.Print(token)
return ctx, err
}
func main() {
lis, err := net.Listen("tcp", port)
if err != nil {
log.Fatalf("failed to listen: %v", err)
}
s := grpc.NewServer(grpc.UnaryInterceptor(grpc_auth.UnaryServerInterceptor(authenticate)))
pb.RegisterEchoSVCServer(s, &server{})
if err := s.Serve(lis); err != nil {
log.Fatalf("failed to serve: %v", err)
}
}
このようにするだけで簡単に gRPC サーバーで共通の認証処理を実装することができます。
また、メソッドごとに認証をスキップしたい場合があることもあるかと思います。その場合には各サーバー(今回の場合は EchoServer)に対して AuthFuncOverride という関数を実装することによって実現できます。これは grpc_auth の中の ServiceAuthFuncOverride インターフェースに定義されています。
先程のプロトコル定義に認証をスキップする rpc を追加します。
service EchoSVC {
rpc Echo(Request) returns (Response);
rpc EchoAuthSkip(Request) returns (Response);
}
go のコードに以下を追加します。このように AuthFuncOverride の中で EchoAuthSkip メソッドの場合だけ authenticate を通さずに return を行うことにより特定のメソッドだけ認証をスキップすることができます。
// AuthFuncOverride is to handle authentication
func (s *server) AuthFuncOverride(ctx context.Context, fullMethodName string) (context.Context, error) {
if fullMethodName == "/pb.EchoSVC/EchoAuthSkip" {
return ctx, nil
}
ctx, err := authenticate(ctx)
if err != nil {
return ctx, err
}
return ctx, nil
}
// Echo is endpoint to demo
func (s *server) EchoAuthSkip(ctx context.Context, req *pb.Request) (*pb.Response, error) {
return nil, status.Errorf(codes.Unimplemented, "method EchoAuth not implemented")
}