世界線航跡蔵

Mad web programmerのYuguiが技術ネタや日々のあれこれをお送りします。

2015年08月24日

grpc-gateway機能ひとめぐり

@vvakameさんがTechBoosterの新刊"JavaScriptoon"の中でgRPCを解説していて、その中でgrpc-gatewayにも触れている。これはとてもよい記事だったので、みんなこの本の電子書籍版を買えば良いと思う。 ただし、grpc-gatewayは記事中で使われているだけで主題ではないので、すべてのトピックをカバーしてくれているわけではない。それは仕方が無いが、そろそろgrpc-gatewayの機能を見渡す日本語記事が欲しいと思ったので自分で書くことにする。

grpc-gatewayとは

gRPC (HTTP/2 + ProtocolBuffers)をwrapして古典的なJSON API (HTTP 1.1 + JSON)を提供するリバースプロキシを生成するコード生成機だ。別記事にも書いた。

何ができるのか

gRPCで使うサービス定義(IDLみたいなやつ)を元にリバースプロキシの中核部分を実装するgolangのHTTP handlerライブラリを生成する。 リバースプロキシはgolangで書かれているが、これはwrapされるgRPCサービスとは別プロセスで動作するためサービス自体をgolangで書く必要はない。C++, Java, Ruby, Python, PHP, C#, Node.jsなどgRPCがサポートする任意の言語で書いて良い。

では、生成されたレバースプロキシは何ができるのか。 基本的にはHTTP/1.1 リクエストをサービス定義ファイルに埋め込まれた情報に基づいて解釈し、gRPC用のリクエストメッセージを構築したうえでgRPCエンドポイントに転送、 その後はエンドポイントからレスポンスを受け取ってからJSONに変換し直してクライアントに転送する。

Request parameter

もうちょっと詳しく見てみよう。まず、RPCのパラメータは次の3種類の形で受け付けて、3つを任意に組み合わせることもできる。

  • Body parameter

    プロキシが受けたHTTP request bodyをProtocol Buffers messageのJSON表現として解釈し、そのmessageをgRPCエンドポイントに転送する gRPCに引き渡すmessageの一部分だけをrequest bodyから受け取ることもできる。その場合は、他の部分はpath parameterやquery parameterとして受け取る必要がある。

    勿論、HTTP methodがGETやDELETEなどの場合はbody parameterは受け取れないので、path parameterやquery parameterで受け取る必要がある。

  • Path parameter

    プロキシが受けるHTTP request pathパターンの中に変数部分を埋め込んでおくことができる。この変数部分の値を対応するmessageのフィールドに反映してからgRPCエンドポイントに転送する

  • Query parameter

    同じく、query stringをmessageのフィールドに反映する

これらのマッピングの詳細は、Googleが公開しているgoogle.api.httpというカスタムオプションを用いてサービス定義ファイルの中に指定する。下記はその例である。

service EchoService {
	rpc Echo(SimpleMessage) returns (SimpleMessage) {
		option (google.api.http) = {
			post: "/v1/example/echo/{id}"
		};
	}
	rpc EchoBody(SimpleMessage) returns (SimpleMessage) {
		option (google.api.http) = {
			post: "/v1/example/echo_body"
			body: "*"
		};
	}
}

次のような項目が設定できる。

  • HTTP request pathのパターン(+ HTTP method)とgRPCメソッドの対応関係
    • 例では全部POSTで受けているが、任意のHTTPメソッドを利用できる
  • Protocol Buffers messageのどの部分を(または全部を)bodyとして受け取るのか
    • 例のEchoBodyではSimpleMessage内の全fieldをbodyで受けている
  • Path pattern内の可変部分とmessage fieldの対応
    • 例のEchoではパスの最後にある可変部分をSimpleMessageidフィールドに対応させている
  • Bodyやpathで受け付けなかったパラメータはすべて省略可能のquery parameterであると仮定される
Header

gRPCはリクエストを受け取る際にリクエストメッセージ本体の他にmetadataという名前=値ペアの列を受け取ることができる。 これをHTTP/1.1で提供するため、以下のようにHTTP headerをmetadataに変換する

  • Authorizationヘッダ: そのままAuthorization metadataとして転送する
  • Grpc-metadata-(varname)ヘッダ: (varname) metadataとして転送する

ところで今RFC 2617を見ていたらWWW-Authenticateヘッダを完全に忘れていたことに気がついたので、あとでサポートした方が良いかもしれない。

Streaming

gRPCにはstreamingという機能がある。1つリクエストを受け取って1つレスポンスを返す代わりに、リクエストのストリームやレスポンスのストリームを受け付けるものだ。 更に言うと次の3パターンがある。

  • client streaming: 徐々に、あるいは散発的に送られてくるリクエストメッセージの列が完了してからレスポンスを1つ返す
  • server streaming: リクエストを1つ受け取ってから徐々に、あるいは散発的にレスポンスの列を返す
  • bidirectional streaming: リクエストとレスポンスを任意のタイミングで任意回送り合う

grpc-gatewayは最初の2つをサポートし、メッセージの列はJSON Streamとして表現される。 bidirectionalなstreamingも限定的にサポートしているが、すべてのリクエストを受け取ってからレスポンスを返し始めるという使い方しかできない。本当にbidirectionalにしようと思ったらWebSocketsにugpradeとかしなければならないだろう。現在はそこまでする予定はない。

なお、client streamingとbidirectional streamingではすべてのリクエストパラメータをbodyで渡す必要がある。リクエストメッセージが複数個である以上、特定のリクエストメッセージに紐付かない形のパラメータは解釈が曖昧になるからだ。

エラー処理

gRPCサービスがエラーを返した場合はエラーコードを適切なHTTP response statusに対応させて返す。またエラー時のresponse bodyにはgRPCサービスが返したエラーメッセージを含むJSON objectが入っている。

トラックバック

http://yugui.jp/articles/893/ping

現在のところトラックバックはありません

コメント閲覧/投稿

2015年07月01日

Automation Account Pattern

Context

The service you are developing provides APIs for users. When an user calls an API, (s)he must show a short-living authorization credential, e.g. an OAuth2 token.

The user creates, owns, updates or deletes resources in the service on behalf of the account to which the credential is associated. Here, the `user' might be (an user-agent operated by) a human but also it might be an automated program running in background.

Also you want to use captcha to prevent huge number of accounts from being registered for some fraud purpose. And you might even want to let users register their billing address for charging.

Problem

It is hard for users to securely manage accounts which automated programs use.

"manage" here basically means to create/update credentials as necessary and handle bills. A human (or a specialized billing-system, at least) must handle these things instead of the automated program because such a program tends to be bad at solving captcha -- it is by design and raison d'être of captcha --, and it is even worse at receiving bills by mail and going to bank to pay.

Then, how can (s)he manage the account for the program? Although some easy solutions come to the mind of the user, they do not work fine enough.

Reuse the account of the human for the program

Individual members in the team which manage the program can leave the team.

So suppose that the owner of the account leaves the team. Then the resources owned by the person can be deleted, or access to the resources can be restricted. At least, it would get hard to manage authentication credentials of the account.

Create an account only for the program

In this approach, (s)he creates a new account for the program in the same way as (s)he creates an account for human. Then the person lets the program use the new account. This account is shared by the team. So even if someone leaves the team, someone else in the team can manage the account.

There are, however, some scenarios in which this approach will not work fine. First, members in the team must share the authentication credential, e.g. passwords, to manage the account. But it is hard to track usage of the credential for audit. Second, even though it would get harder to keep the authentication credential secret as the team gets larger and the team needs to update the credential when necessary, it would also get harder to notify the update to all members in the team. Finally, even if they solve the issues above, it still tends to happen that someone in the team creates an account but (s)he leaves the team without sharing the account with others. Then none can manage the account.

To make things worse, they need to keep the account for the program having the minimum rights for security. It means that they will have more number of accounts per usage and the issues above becomes more serious.

Examples

1. Github - Travis integration

System
github
Resource
private repositories
Account
github account
short-living authorization credential
OAuth2 token issued by github
Authentication information to manage the account
Password of the github account
Automated Program
Build processes running on Travis CI.

2. Cookpad - AWS

System
Amazon Web Services
Resource
S3 bucket
Account
AWS account
short-living authorization credential
AWS access-key
Authentication information to manage the account
AWS password
Authomated program
The feature that allows users of Cookpad to upload their pictures of dishes.

Solution

  1. Define another kind of accounts (Automation Account), which is only for automated programs.
  2. Distinguish this kind of accounts from ordinal accounts for humans.
  3. Do not assign any passwords to automation accounts. Instead, associate the automation account to a list of human accounts who can manage the automation account.

Humans in the list just need to login to the system with their own human accounts when they manage the automation account. So they don't need to share the password of the automation account. Moreover, the automation account does not need to have its own password.

Diagram of Automation Account

Implementatinos

1. Service Accounts in Google Cloud Platform (GCP)

  • Most of resources provided by APIs in GCP belong to a unit called "project".
  • There are two kinds of accounts: ordinal (human) accounts and service accounts
    • User accounts for humans (Gmail accounts or accounts of Google Apps for Work) are independent from projects, and those accounts can join arbitrary number of projects as read-only, writable or owners.
    • On the other hand, service accounts are accounts only for automated programs. A service account uniquely belongs to a project. And when the project is deleted, the service account is also deleted together with other resources in the project.
  • Since service accounts do not have passwords for login, you can not use service accounts when you log into web pages but you can use them only for API calls with their OAuth2 tokens. Also you have to use your own human account and the the account must be an owner of the project when you manage the service account.
  • Billing information is registered per project. Any operations on resources in a project are charged to the billing information of the project.

Diagram of Service Account

2. AWS Identity and Access Management (IAM)

  • Resources provided by APIs in AWS belong to an AWS user account (root account).
  • You can create arbitrary number of IAM accounts under the root account.
    • You can assign an IAM account to human, but also you can assign an IAM account to an automated program without giving any password to the account.
  • You have to use the root account or an IAM account for human when you need to login to the web console to manage IAM accounts.
  • Billing information is registered per root account. Any operations done by IAM accounts are charged to their root accounts.

Diagram of Service Account

Let's compare the two implementations.

The approach of GCP is a bit more complicated for hobby developers because GCP requires the developer to create a "project" in addition to his/her human account even though (s)he is the only developer in the development team. On the other hand, (s)he would be able to do everything just with his/her root accounts in AWS.

Next, let's think about larger development projects. AWS is a bit less secure because root account itself is an account with username and password and the dev team must keep the account secure in addition to their IAM accounts. On the other hand, GCP is secure because it does not have this kind of extra authentication credential which is not usually used.

トラックバック

http://yugui.jp/articles/892/ping

現在のところトラックバックはありません

コメント閲覧/投稿

2015年06月07日

Automation Accountパターン

コンテキスト

あなたが開発しているサービスはユーザー向けにAPIを提供している。そして、APIを利用するにはユーザーは短寿命の認可情報(たとえばOAuth2トークン)を提示しなければならない。

ユーザーは認可情報が紐付いているアカウントの権限でリソースを読み取ったり、作成したり、所有したり、編集または削除したりする。 ここで、ユーザーは人間(が操作するユーザーエージェント)であることもあるが、人間の手を離れてバックグラウンドで自動実行されるプログラムかもしれない。

また、あなたは悪用目的でアカウントが大量登録されるのを防ぐためにCaptchaを利用したいと思っている。 さらに、課金目的で請求書送付先を登録させたいとも思っているかもしれない。

問題

プログラムが利用するアカウントを安全に管理するのがユーザーにとって困難である。

プログラムは自分でCaptchaを解いたりEメールを受け取るのが苦手だし(そうでないとCaptchaの意味がない)、 また郵便物で請求書を受け取って銀行に振り込みに行くのは更に苦手である。 よって、プログラムが利用するアカウントを作成し、管理し、対応する請求書を処理するのは人間(か、少なくともそれ専用の別のシステム)でなければならない。

ではその人間はどのようにそのプログラムが利用するアカウントを管理すれば良いのか。幾つかの簡単な解決策を思いつくが、いずれも十分とは言えない。

人間のアカウントを使い回す

プログラムを管理しているチームの誰かのアカウントを使う、という解決法。

しかし、個々の人間は自動プログラムを管理ししているチームを離れることがある。このアカウントを所有していた人物がいなくなると、 アカウントの元に所有されていたリソースは消滅したり、アクセスが制限されたりする可能性がある。 最低でも、そのアカウントの認可情報を更新するのは困難となる。

プログラム専用のアカウントを共有する

人間用のアカウントを作るのと同様にして、人間がプログラム専用のアカウントを作成する。そしてそのアカウントをプログラムに利用させる、という解決法。 アカウントは自動プログラムを管理する人間たちによって共有されており、たとえ誰かがチームから居なくなっても 他の者がアカウントの管理、認可情報の更新などを行える。

しかし、これもまた十分とは言えない。人間たちはアカウントを管理するためのパスワードなどの認証情報を共有しなければならないが、 その周知の情報をいつ誰が利用したのか監査記録を付けるのは大きなチームでは難しい。 また大きくなるほど認証情報漏洩のリスクは高まって適時の更新が必要とされるが、大きくなるほど更新したという事実を全員に伝えるのも難しい。 苦労してこれらの問題を解決したところで、誰かが開発用にアカウントを作ったが良いが周知するのを忘れたままチームを去って、結局誰も管理できないアカウントになったりするのもありがちな問題である。

更に、セキュリティ上はプログラム専用アカウントは最小限の権限を持つのが望ましいが、最小化するために権限ごとにアカウントを設定すると 管理すべきアカウントの数が増えて管理にまつわる問題は酷くなっていく。

具体例

1. Github - Travis連携

システム
github
リソース
プライベートリポジトリ
アカウント
githubアカウント
短寿命認可情報
githubのOAuth2 token
管理のための認証情報
githubのパスワード
自動化プログラム
travisによるビルドプロセス

2. クックパッド - AWS

システム
Amazon Web Services
リソース
S3 bucket
アカウント
AWSアカウント
短寿命認可情報
AWSアクセスキー
管理のための認証情報
AWSパスワード
自動化プログラム
料理の画像をアップロードする機能

解決策

自動化プログラム用のアカウント種別(Automation Account)を作って、人間用のアカウントとは区別する。 また、Automation Accountには管理用のパスワードを割り当てず、代わりに「このAutomation Accountを管理できる人間用アカウントのリスト」を対応させておく。

人間たちは自分自身の人間用アカウントでログインするだけで(リストに載っていれば)Automation Accountを管理できる。 このため、Automation Accountのパスワードを共有する必要はない。 それどころか、プログラム用のアカウントはどうせ人間の誰かが自分のアカウントで管理するのだからAutomation Accountにはパスワードがなくても差し支えない。

Automation Accountの図解

実現例

1. Google Cloud Platform (GCP)のサービスアカウント

GCPのAPIが提供するほとんどのリソースは「プロジェクト」という単位に所属する。

ユーザーアカウントはプロジェクトとは独立したアカウント(GmailアカウントまたはGoogle Apps for Workアカウント)で、 任意の数のプロジェクトに読み取り専用、書き込み可、またはオーナーとして参加できる。

これに対してサービスアカウントはプログラム専用のアカウントで、ただ1つのプロジェクトに一意に属し、プロジェクトが削除されると他のリソースと同様サービスアカウントも削除される。

サービスアカウントはログイン用のパスワードを持たないためWebページのログインには利用できず、OAuth2トークンを用いてプログラムからAPIを呼ぶためにしか利用できない。 サービスアカウントを管理するには、そのプロジェクトのオーナーになっている人間のアカウントを利用する。

課金先情報はプロジェクトごとに登録管理される。サービスアカウントであれ一般のユーザーアカウントであれ、プロジェクト内のリソースに対する操作はプロジェクトに対して課金される。

Service Accountの図解

2. AWS Identity and Access Management (IAM)

APIが提供するリソースは特定のAWSユーザーアカウント(rootアカウント)に属する。

権限を分割するために、ユーザーアカウント内に任意個のIAMアカウントを作成できる。 IAMアカウントはパスワードを設定して人間に割り振ることもできるし、パスワードを与えないでおいてプログラムに利用させることもできる。 プログラム用のIAMアカウントを管理するためにwebコンソールにアクセスする場合は、パスワードを割り振られた人間用のIAMアカウントまたはrootアカウントを利用する。

課金情報はrootアカウントごとに登録管理される。IAMアカウントが利用したリソースはその属するrootアカウントに対して課金される。

IAMの図解

2つの実現例を比べてみよう。

GCPのアプローチは一個人が趣味で開発しているような小規模開発においても、開発者のアカウントの他にプロジェクトという単位を作成する必要があるのでやや煩雑である。 一方AWSのアプローチは個人の小規模開発であれば人間用にはIAMアカウントを発行せず、rootアカウントだけですべてを済ませることも可能なので単純である。

大規模な場合に目を移すと、AWSのアプローチはrootアカウントそれ自体がユーザー名とパスワードを持つアカウントであるため、守るべき対象が1つ多い分だけほんの少しセキュアでない。 またそもそもこのrootアカウントのパスワードをどう管理するかという点において上記と類似の問題が残されたままである。 これに対してGCPでは、この通常使われることのない余分な認証情報はそもそも存在しないのでセキュアである。

トラックバック

http://yugui.jp/articles/891/ping

現在のところトラックバックはありません

コメント閲覧/投稿

ご案内

最近の記事

タグ一覧

過去ログ

  1. 2015年08月
  2. 2015年07月
  3. 2015年06月
  4. 2015年05月
  5. 過去ログ一覧

フィード

フィードとは

その他

Powered by "rhianolethe" the blog system