Slide 1

Slide 1 text

Envoy External AuthZとgRPC Extensionを利用した「頑張らない」 Microservices認証認可基盤 Web Developer Conference 2024 @andoshin11

Slide 2

Slide 2 text

自己紹介 ● 安藤真 (@andoshin11) ● フリーランスエンジニア ● お手伝いしてます👇 ○ カーナベル as a Platform Owner 2021年4月〜 ○ Medixpost as a Lead Developer 2022年9月〜 ● 好きな技術: ○ TypeScript ○ Terraform ○ Cilium

Slide 3

Slide 3 text

カーナベル株式会社 ● トレカ(遊戯王、デュエマ、ポケカ、MTG)の買 取 & 販売を行う日本最大級のトレカECサイト ● 法人化: 2009年(創業は2003年ごろ) ● 本社: 三重県四日市市 ● スタッフ: 約100名 ● 開発チーム: 10名

Slide 4

Slide 4 text

コンテキスト: サイトの老朽化 ● 10年以上前に作ったPHPモノリスサーバー(Ethna)の耐用限界 ● プロダクトの多機能化 & 多角化に対してコードの継ぎ足しでなんとかこれ まで粘ってきた

Slide 5

Slide 5 text

コンテキスト: サイトの老朽化 ● 10年以上前に作ったPHPモノリスサーバー(Ethna)の耐用限界 ● プロダクトの多機能化 & 多角化に対してコードの継ぎ足しでなんとかこれ まで粘ってきた ● マジで頑張ってる↓ ■ 自動仕分けロボット用の画像認識AIを作ったり ■ MirroringしたAuroraに対してLaravelのAPIサーバーを立てたり ■ jQuery templateの中にWeb Components(Lit)を埋め込んだり

Slide 6

Slide 6 text

コンテキスト: サイトの老朽化 ● 10年以上前に作ったPHPモノリスサーバー(Ethna)の耐用限界 ● プロダクトの多機能化 & 多角化に対してコードの継ぎ足しでなんとかこれ まで粘ってきた ● マジで頑張ってる↓ ■ 自動仕分けロボット用の画像認識AIを作ったり ■ MirroringしたAuroraに対してLaravelのAPIサーバーを立てたり ■ jQuery templateの中にWeb Components(Lit)を埋め込んだり そしてフルスクラッチリニューアルへ...

Slide 7

Slide 7 text

Architecture

Slide 8

Slide 8 text

お客様 ECサイト (Nuxt.js) Contour (Ingress controller) External AuthZ Server Argo CD・ Workflows Cilium OTel Collector Microservice A Microservice B Microservice C スタッフ 管理画面 (Nuxt.js)

Slide 9

Slide 9 text

本日のテーマ

Slide 10

Slide 10 text

課題 1: 会員認証と社員認証をどう共存させるか

Slide 11

Slide 11 text

課題 1: 会員認証と社員認証をどう共存させるか 課題 2: Microservicesの認可をどうするか

Slide 12

Slide 12 text

課題 1: 会員認証と社員認証をどう共存させるか

Slide 13

Slide 13 text

お客様(Customer)と社員(Member)それぞれで要求される認証要件は異なる ● メール所有権確認 ● パスワードリセット ● クレカ登録 ● 不正ユーザー判定 ● etc… ● 入退社管理 ● 二段階認証強制 ● パスワードローテーション ● 細かな認可権限管理 ● 情シス制御 ● etc… お客様向けの要件 社員向けの要件

Slide 14

Slide 14 text

よくある解決方法: 同じシステム上でフラグ管理 Good: 一元管理・ロジックの使い回しが可能 Bad: 大量のif文・実装ミスによるアクセス範囲超過のリスク

Slide 15

Slide 15 text

弊社の場合

Slide 16

Slide 16 text

認証プロバイダー自体を分離 Firebase for Customers Google Workspace for Members ● 利用実績あり ● スケーラビリティ ● 利用料金の安さ ● 対応認証手段の多さ ● メール送信機能内包 ● etc… ● 全社のデフォルト認証手段 ● 業務アプリでSSO利用中 ● 入退社管理の容易さ ● etc…

Slide 17

Slide 17 text

認証プロバイダー自体を分離 Firebase for Customers Google Workspace for Members ● 利用実績あり ● スケーラビリティ ● 利用料金の安さ ● 対応認証手段の多さ ● メール送信機能内包 ● etc… ● 全社のデフォルト認証手段 ● 業務アプリでSSO利用中 ● 入退社管理の容易さ ● etc… 正確にはAmazon Cognitoを利用 ・ProviderとしてGoogle Workspaceを設定 ・社員にS3アクセスを許可するため

Slide 18

Slide 18 text

Multi Auth Providersならではの課題

Slide 19

Slide 19 text

認証プロバイダーごとにJWT Payloadが異なる iss, sub, iat, expなどは同様だが、メタフィールドに大きな違いがある

Slide 20

Slide 20 text

Point of failure・運用負担の増加 ● 各Microservicesが賢くなりすぎると保守が大変 ● bug修正・仕様修正のたびに全体再デプロイ。最悪の場合は都度サービスメンテ ● 認証認可という根幹の部分の変更反映はなるべく数を減らしたい → Microservicesを賢くさせたくない

Slide 21

Slide 21 text

Microservicesを賢くさせたくない

Slide 22

Slide 22 text

API GatewayとSTSで解決しよう!

Slide 23

Slide 23 text

API Gateway

Slide 24

Slide 24 text

中央集権的GWによる責務のオフロード ● 外部認証プロバイダーとはAPI Gatewayでのみ通信 ● MicroservicesからはFirebaseおよびCognitoの知識を完全に隠蔽 Microservice A Microservice B Microservice C API Gateway 1. ユーザーアクセス 2. 署名検証 3. 検証済リクエスト

Slide 25

Slide 25 text

CONTOUR as API Gateway ● オープンソースのKubernetes向けingress controller (CNCF Incubating Project) ● 実装はEnvoyのwrapper → External Authorizationが設定できる

Slide 26

Slide 26 text

What is ExtAuthZ ? ● Envoyが提唱するEnvoy ↔ 外部認証サーバー間の通信プロトコル ● Envoyを通過するリクエストに対する中間処理を切り出せる

Slide 27

Slide 27 text

External Authorization Serverで行える中間処理 ExtAuthZ Server側はexternal_auth.proto に則って実装することで下記の ような中間処理を行える(言語非依存) ○ リクエストの認証チェック・エラーthrow ○ Metadataの上書き ○ リクエストヘッダーの上書き ○ クエリパラメータの上書き ○ 後続filterへのdynamic metadataの設定 ○ etc…

Slide 28

Slide 28 text

External Authorization Serverで行える中間処理 ExtAuthZ Server側はexternal_auth.proto に則って実装することで下記の ような中間処理を行える(言語非依存) ○ リクエストの認証チェック・エラーthrow ○ Metadataの上書き ○ リクエストヘッダーの上書き ○ クエリパラメータの上書き ○ 後続filterへのdynamic metadataの設定 ○ etc… 弊社ではこの辺の機能を利用

Slide 29

Slide 29 text

Contour + ExtAuthZを活用したArchitecture ● external_auth.protoを実装したAuthority Serviceを自前実装 ● routing、流量調整、gRPC-Web変換等を行うContourと責務分離 Microservice A Microservice B Microservice C 1. ユーザーリクエスト 3. 署名検証 5. 検証済みリクエスト Contour Authority Service 2. ExtAuthZ 4. Success

Slide 30

Slide 30 text

Contour + ExtAuthZを活用したArchitecture Microservice A Microservice B Microservice C 3. 署名検証 Contour Authority Service 2. ExtAuthZ 5. Denied 4. 認証エラー ● エラーの場合はExtAuthZサーバー(Authority Service)からDenied Responseを返す ● Microservicesにはリクエストは到達しない 6. 認証エラー 1. ユーザーリクエスト

Slide 31

Slide 31 text

STS(Security Token Service)

Slide 32

Slide 32 text

Authority Service as Security Token Service おさらい👇 ● External Token(FirebaseとCognito)のJWT Payloadは型が異なる ● ExtAuthZサーバー(Authority Service)ではMetadataの上書きが可能

Slide 33

Slide 33 text

Authority ServiceをSTSとして活用しよう! (署名検証 & 署名発行)

Slide 34

Slide 34 text

Authority Service as Security Token Service ● Authority ServiceでExternal Tokenを正規化して新たにInternal Tokenを 署名発行する → Token Exchangeと呼ばれる手法

Slide 35

Slide 35 text

Authority Service as Security Token Service ● Authority ServiceでExternal Tokenを正規化して新たにInternal Tokenを 署名発行する → Token Exchangeと呼ばれる手法 ● What is Internal Token? ○ IssuerがAuthority ServiceのJWT ○ 内部通信でのみ利用 ○ 正規化済みのPayload ○ 1リクエストごとに発行する ○ External Tokenよりも寿命が短い(expが短命な)JWT = 60秒 ■ → 万が一の漏出に対する耐性

Slide 36

Slide 36 text

Authroity ServiceでToken Exchange処理を行う Microservice A 1. Request (External Token) 3. Validate (External Token) 5. Request (Internal Token) Contour Authority Service 2. ExtAuthZ (External Token) 4. Success (Internal Token) Step 2 & Step 4の処理が一般的に Token Exchangeと呼ばれる

Slide 37

Slide 37 text

Microservices側の実装

Slide 38

Slide 38 text

JSON Web Keysを活用したStandaloneな署名検証方式 Microservice A Request with Internal Token Contour Authority Service Token Exchange AWS Secret Manager Key-pairを取得 (直近3世代) 定期的に新規の Key-pairを生成 ● Authority ServiceからJWKsを取得し、 Internal Tokenを検証 ● JWKはkid単位でcache → Authority Serviceのダウンに耐性アリ・通信量削減も ● JWTの検証にはaws-jwt-verifyを利用 JWKsを取得(cached)

Slide 39

Slide 39 text

awslabs/aws-jwt-verify ● OIDC-compatなJWTの検証に利用できるAWSチームのライブラリ ● Zero dependencies ← メンテが楽!! ● コードもシンプルで読みやすく、必要最低限な機能のみを提供 ● カスタムJWKs fetcherを差し込める(デフォルトはHTTP fetcher) ○ 弊社ではAuthority Serviceと通信するgRPC fetcherをinject ● 地味にNode.js & Web Browser両方に対応 ● 非常に高品質かつ爆速でJWT verifierを定義できるため長期betする 価値はありそう ← 激推しです

Slide 40

Slide 40 text

社内向けライブラリの提供 ● Private PackageとしてNest.js向けのAuthGuardを提供 ● 実態はInternal TokenのJWT verifier + 認可チェック機構(後述)

Slide 41

Slide 41 text

Nest.js向け共通ライブラリの社内提供

Slide 42

Slide 42 text

課題 1: 会員認証と社員認証をどう共存させるか

Slide 43

Slide 43 text

API GatewayとSTSを組み合わせることで複雑性 の隠蔽が可能になり、外部サービスの変更にも強 いアーキテクチャが実現できた

Slide 44

Slide 44 text

課題 2: Microservicesの認可をどうするか

Slide 45

Slide 45 text

セキュリティ・コンプライアンス観点における認可要求 カーナベルの部署構成(抜粋) ● 開発課 ● 経営企画 ● カスタマーサポート ● プライシング ● ロジスティクス(買取・封入) ● 総務 ● etc…

Slide 46

Slide 46 text

セキュリティ・コンプライアンス観点における認可要求 カーナベルの部署構成(抜粋) ● 開発課 → 顧客情報アクセス禁止 ● 経営企画 → 顧客情報アクセス禁止 ● カスタマーサポート ● プライシング → 顧客情報アクセス禁止 ● ロジスティクス(買取・封入) ● 総務 → 顧客情報アクセス禁止 ● etc…

Slide 47

Slide 47 text

セキュリティ・コンプライアンス観点における認可要求 カーナベルの部署構成(抜粋) ● 開発課 → 顧客情報アクセス禁止 ● 経営企画 → 顧客情報アクセス禁止、在庫数変更禁止 ● カスタマーサポート → 在庫数変更禁止 ● プライシング → 顧客情報アクセス禁止、在庫数変更禁止 ● ロジスティクス(買取・封入) ● 総務 → 顧客情報アクセス禁止、在庫数変更禁止 ● 情シス → 顧客情報アクセス禁止、在庫数変更禁止 ● etc…

Slide 48

Slide 48 text

セキュリティ・コンプライアンス観点における認可要求 カーナベルの部署構成(抜粋) ● 開発課 → 顧客情報アクセス禁止 ● 経営企画 → 顧客情報アクセス禁止、在庫数変更禁止、価格変更禁止 ● カスタマーサポート → 在庫数変更禁止、価格変更禁止 ● プライシング → 顧客情報アクセス禁止、在庫数変更禁止 ● ロジスティクス(買取・封入) → 価格変更禁止 ● 総務 → 顧客情報アクセス禁止、在庫数変更禁止、価格変更禁止 ● 情シス → 顧客情報アクセス禁止、在庫数変更禁止、価格変更禁止 ● etc…

Slide 49

Slide 49 text

セキュリティ・コンプライアンス観点における認可要求 カーナベルの部署構成(抜粋) ● 開発課 → 顧客情報アクセス禁止 ● 経営企画 → 顧客情報アクセス禁止、在庫数変更禁止、価格変更禁止 ● カスタマーサポート → 在庫数変更禁止、価格変更禁止 ● プライシング → 顧客情報アクセス禁止、在庫数変更禁止 ● ロジスティクス(買取・封入) → 価格変更禁止 ● 総務 → 顧客情報アクセス禁止、在庫数変更禁止、価格変更禁止 ● 情シス → 顧客情報アクセス禁止、在庫数変更禁止、価格変更禁止 ● etc… 部署ごとの細やかな権限管理が求められる

Slide 50

Slide 50 text

こんな認可処理はイヤだ 👉

Slide 51

Slide 51 text

● Controller method内にaccess scopeごとのif文を記述する方式 ● 記述が冗長 & method単位でのテストが必須で保守性低 こんな認可処理はイヤだ

Slide 52

Slide 52 text

アプリケーションロジックに認可処理 をベタ書きしたくない

Slide 53

Slide 53 text

認可機構に求める要件 ① 可読性 ○ API Methodsの仔細まで読まなくても認可スコープを瞬時に把握でき ること ② 記述性 ○ 言語非依存・FW非依存・ドメイン非依存であること = 個別のMicroservicesのドメイン知識が無くても容易に認可スコー プを記述できること ③ 変更容易性 ○ 全てのMicroservices repositoriesに修正を加えなくても安全に Platform全体への認可スコープ追加/削除が行えること

Slide 54

Slide 54 text

Protobufに集約しよう!

Slide 55

Slide 55 text

Method Optionで認可スコープを定義する

Slide 56

Slide 56 text

Method Optionで認可スコープを定義する option blockにallowed_scopesを 記述していく optionのschemaはextensionで定義

Slide 57

Slide 57 text

Protobufに認可スコープを記載するメリット ① 可読性 ○ Methodごとの認可スコープを一瞥できる ② 記述性 ○ Protobuf syntaxさえ知っていれば誰でも記述できる ③ 変更容易性 ○ Protobuf repository内をgrep/sedするだけで網羅的に認可スコープ の追加/削除を行える

Slide 58

Slide 58 text

How to define custom extensions?

Slide 59

Slide 59 text

google.protobuf.MethodOptionsをextendして定義する

Slide 60

Slide 60 text

google.protobuf.MethodOptionsをextendして定義する descriptor.protoをimport extend記法でfieldを追加

Slide 61

Slide 61 text

google.protobuf.MethodOptionsをextendして定義する AuthorizationRule型のauthorization fieldをcustom optionとして定義

Slide 62

Slide 62 text

google.protobuf.MethodOptionsをextendして定義する

Slide 63

Slide 63 text

google.protobuf.MethodOptionsをextendして定義する このfiled numはどこからでてきた?

Slide 64

Slide 64 text

google/protobuf/descriptor.proto

Slide 65

Slide 65 text

google/protobuf/descriptor.proto custom optionを定義する際は1000番以降 のfiled numを利用するよう指定がある

Slide 66

Slide 66 text

How to use custom extensions?

Slide 67

Slide 67 text

Method Optionの利用

Slide 68

Slide 68 text

Method Optionの利用 識別子の役割。 “authorization”というcustom extensionを利用すること明示

Slide 69

Slide 69 text

Method Optionの利用 option blockの中は AuthorizationRule型で記述

Slide 70

Slide 70 text

AuthGuard側の実装

Slide 71

Slide 71 text

No content

Slide 72

Slide 72 text

リクエストを通過させるか どうかの判定を行う

Slide 73

Slide 73 text

Internal Tokenの署名検証 = 認証 対象Methodの認可チェック = 認可

Slide 74

Slide 74 text

subやscpは署名発行時 に自動で埋め込まれる

Slide 75

Slide 75 text

method pathはリクエストの ExecutionContextから特定できる

Slide 76

Slide 76 text

protobufjsでmethod optionをパースす る。@grpc/proto-loaderは未対応

Slide 77

Slide 77 text

scpとallowed_scopesを比較

Slide 78

Slide 78 text

課題 2: Microservicesの認可をどうするか

Slide 79

Slide 79 text

Protobufに認可スコープを記述することで可読性・ 記述性・変更容易性が担保された仕組みが実現

Slide 80

Slide 80 text

まとめ

Slide 81

Slide 81 text

色々話した(まだ話したりない)けど 実はそんなに難しいことはやってない

Slide 82

Slide 82 text

● 会員登録・ログイン機能 → FirebasesとCognito(Google Workspace)のSDKを利用 ● JWTの検証 → 3rd-party libraryを利用 ● API GatewayとToken Exchange → ContourのExternal Authorization機能を利用 ● Protobufの拡張 → 標準のCustom Extensionを利用 ● Nest.jsの認証Middleware → 標準のAuth Guardのを利用 ● 自分たちで発明したものはほとんど無い

Slide 83

Slide 83 text

● 会員登録・ログイン機能 → FirebasesとCognito(Google Workspace)のSDKを利用 ● JWTの検証 → 3rd-party libraryを利用 ● API GatewayとToken Exchange → ContourのExternal Authorization機能を利用 ● Protobufの拡張 → 標準のCustom Extensionを利用 ● Nest.jsの認証Middleware → 標準のAuth Guardのを利用 ● 自分たちで発明したものはほとんど無い 実際の開発工数は20人日程度

Slide 84

Slide 84 text

伝えたいこと

Slide 85

Slide 85 text

適切な技術選定と設計力があれば 小規模チームでも高品質な認証認可基盤を導入できる ※今回の基盤はセキュリティ会社の 脆弱性診断に合格済みの内容です

Slide 86

Slide 86 text

WE ARE HIRING!! ● リモート勤務可能 ● こんなことやってます ○ OTel + Grafanaでの監視基盤構築 ○ Cloudflare Workersの実装 ○ K6を利用した負荷試験 ○ Web Vitalsの継続的計測と改善 ○ 在庫回転率と需供分析でのDynamic Pricing ○ Elasticsearchで検索基盤構築 ○ Terraform Providerの開発 ○ Custom Jest Environmentの整備 ○ gRPC ServerのE2E coverage取得の仕組み作り ○ Nuxt pluginの開発 ○ etc… ● 興味がある方は [email protected] 又は自分まで

Slide 87

Slide 87 text

Thank you!