【レポート】Backend Engineer’s meetup ~マイクロサービスにおける認証認可基盤~

2019.08.22

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

本記事は、2019年8月21日に行われたmerpay社の主催するイベント、「Backend Engineer’s meetup ~マイクロサービスにおける認証認可基盤~」の参加レポートです。

Backend Engineer’s meetup ~マイクロサービスにおける認証認可基盤~

レポート

セッションの概要

メルペイ社の認証基盤チームの担当している

  • 外部向けに提供しているOIDCなどの認可の仕組み
  • 内部のマイクロサービス間通信の認証の仕組み

のうち、後者についてのお話を聞いてきました。セッションのスライドはこちらで公開されています。

チームがやっていること

  • 認証基盤チームではユーザーアカウント管理とかログインはやってない
  • 従業員の管理とかはやってない
  • セキュリティについてはセキュリティチームと相談しながらやっている
  • それ以外の認証認可を認証基盤チームがやっている

現状のアーキテクチャ

AuthorityServiceというマイクロサービスが、

  • 外部向けに提供しているOIDCなどの認可の仕組み
  • 内部のマイクロサービス間通信の認証の仕組み

を提供している。

  • メルカリAPIを利用するパターン
  • マイクロサービスを利用するパターン

の2つのデータフローがあるが、AuthorityServiceが関連するのはマイクロサービスを利用するパターンのみ。

AuthorityServiceがやっていること

外部からのリクエスト検証

内部通信用トークンの生成

クライアントから受け取るトークンと内部通信用のトークンが別れていて、リクエストの検証後に内部通信用のトークンを生成している。

各マイクロサービスは、内部通信用のトークンの検証をしている。

  • 1リクエストに対して、1つトークンが払い出される
  • 各マイクロサービスが検証する内部トークンは同じもの
  • JWT
    • マイクロサービスはAuthorityServiceが提供する公開鍵で検証
    • 鍵はマイクロサービス側でキャッシュしている

内部トークン用のSDK

Go言語のSDKのみ提供している。

内部スコープ

内部トークンが持つスコープで、マイクロサービス間のアクセス制御を行うための仕組み。

サードパーティからのリクエストに対してのみ、内部スコープを利用している。 外部スコープの値をそのまま内部スコープにセットしているが、それだけだとクライアントのユースケースの数だけマイクロサービスが内部スコープを管理しなくてはいけない。

外部クライアント用のスコープと、内部のスコープは本来1対1ではない。 外部スコープはユースケースベースで、内部スコープはリソースベースで持つべき。

ここを吸収するため、AuthorityServiceで外部スコープと内部スコープのマッピングテーブルを持っている。

例えば、決済処理を行うには「transaction」と「user」リソースに対する内部スコープが必要だが、クライアントからは「決済」が行いたいので「transaction」だけの指定で済むのが自然。

その他内部トークンのメリット

QA

セッションの最後にQAの時間がありました。

  • 可用性に関するインフラの工夫とかある?
    • 特別なことはしてない、しなくても良いくらい安定している
    • 冗長化して分散、リトライ、タイムアウト、みたいなところをやる
  • サービス間のアクセス制御はどうしてる?どのサービスがどのサービスにアクセスできるのか?をどう管理してる?
    • マイクロサービス間の認証は現状してない
    • やろうとはしている
    • ただ、内部トークンで誰がどこにアクセスできるのか?は制御できる
  • 内部トークンの不正利用について監視とかしてる?
    • 現状してない
    • 寿命が短いのでそこまでリスキーではないと考えている
      • やったほうがいいのはそう
  • スコープとかいれるとトークンでかくなると思う。復号とかでパフォーマンスの問題はおきない?
    • 長い問題は考えてる
    • JWTをやめたい
      • バイナリにしたい(と勝手に思っている)
        • CWT?
  • AuthorityServiceについて、性能面で意識したこととかある?
    • レイテンシーについて
      • マイクロサービス化したときに、レイテンシーをあまり考えないようにした
        • そこにフォーカスするならモノリスにすべき
          • とはいえ必ず通るところが遅いと辛い
            • ローカルメモリでデータをキャッシュしたりとかそういう工夫はある
  • SDKをサイドカープロキシにはしないのか?
    • そうすることを考えている
      • SDKをどう使うか、がService側の責務になっているし、Goだけなので多言語どうする?みたいな話がある
  • 内部トークンの有効期限に対する検討軸は?
    • クライアントがレスポンスをどれだけ待ってくれるか?で考えた
      • 10分にしても、そんな待ってくれないよね、という考え方
  • サービスからサービスにバッチ処理を依頼する場合とかある?
    • 内部トークンで依頼する
      • 各サービスが秘密鍵を持っていて、これで内部トークンを取得してバッチ処理を依頼
  • 一番システムが複雑化しそうなユースケースは?
    • pub-Sub
      • 内部トークンは有効期限が短い
      • 途中で切れちゃう可能性があるので、それをどうするか?
    • バッチでも使えてるのは?
      • バッチを依頼するサービスが自分で内部トークンを生成してる(ゲートウェイから最初に受け取ったやつをそのままつかわない)

感想

外部クライアント用のスコープと、内部のスコープは1対1でマッチせず、前者はユースケースベース、後者はリソースベースという考え方は本当にその通りだなと思いました。

私からは以上です。