SnykがGloo Edgeで認証戦略を標準化する方法
Joakim Bajoul Kakaei
Jack Schofield
David Harrigan
Gareth Visagie
James Bowes
2021年7月20日
0 分で読めますSnykのAPIでは、複数の認証(authN)方式をサポートしています。これまで、主な認証方式はAPIキーでしたが、最近ではOAuth連携によって生成される署名付きJWTを使った認証にも対応しました。現在、この方式はAWS CodePipelineおよびBitbucketとの連携で使用されています。
Snykは当初、中央のモノリスが認証を判断するハブ&スポーク型アーキテクチャを採用していました。市場への適合性を探りながら迅速に改善を重ねる段階では、これでうまく機能していました。しかし、組織と顧客基盤の規模が拡大するにつれて、サービス指向アーキテクチャ(SOA)への移行ニーズも高まりました。ただし、このアーキテクチャへの変更には、各サービスが認証を判断できるようにする必要があります。つまり、各サービスが複数の認証方式に対応しなければならず、時間とともに管理が煩雑になり、拡張も難しくなります。
SOAへの移行を進めるにあたり、最も安全で現実的な方法は、単一の認証機関が認証方式を暗号学的に保証されたIDトークンに標準化することだと結論づけました。各サービスはこのトークンを受け取り、検証するだけでよく、複数の認証方式をサポートする複雑さを抱える必要がありません。簡単に言えば、単一の認証機関が認証を判断し、署名付きJWTをシステムのほかの部分に渡す仕組みです。
これを実現するには、リクエストを認証して標準化されたトークンを発行する新たなレイヤーをシステムに導入する必要がありました。そこで登場するのが、API Gatewayの一種であるGloo Edgeです。
Glooであれこれつなげよう!
API Gatewayとは?
私たちはAPI Gatewayの専門家ではありませんが、できることを簡単にご紹介します。
リクエストの検証
レート制限
認証(または、認証機能の拡張!)
ほかにも、まだまだたくさん!(専門家ではないと言いましたよね…)
そして、重要なのは、これらすべてをエッジで実行できることです!上記の処理をアプリで行っているなら、API Gatewayに任せられる可能性があります。つまり、コードを削減できるということ。コードを減らすのが好きなエンジニアにはうれしいですよね!?
複数のAPI Gatewayを検討した結果、プラットフォームの進化を支えるためにGloo Edge(この記事ではGlooと呼びます)を採用しました。このブログ記事では、Glooを選んだ方法や理由については説明しません。それはまた別の記事で取り上げます!
Gloo Edgeとは?
Gloo EdgeはSolo.ioが開発したAPI Gatewayです。Envoyを抽象化し、Kubernetesのカスタムリソース定義(CRD)を使ってEnvoyの設定を作成できます。そのため、導入のハードルが非常に低くなっています。Kubernetesの設定を書けるなら、Gloo独自のCRDを使ってEnvoyの設定も書けるでしょう。
Glooの概要やEnvoyの活用方法について詳しく知りたい方は、Gloo Edgeのドキュメントをご覧ください(本当に充実したドキュメントです!)。
SnykにおけるGloo Edgeの導入
以下では、APIの前段にGlooを配置し、興味深いパートナー連携の一部で利用できるようにするために行った作業をご紹介します。
移行経路を見極める(すでにAPIが利用されている場合)
Snyk APIには常にトラフィックが届いているため、単純に前段へGlooを配置することはできませんでした。前述のとおり、これまで顧客向けAPIでは、リクエストの認証にAPIキーのみを使用していました。しかし、API GatewayでJWTとAPIキーの両方を受け付けるようにしたことで、従来の方式と新しい方式が分かれることになりました。
幸い、チームではAPIのドメインをhttps://snyk.io/api/からhttps://api.snyk.ioに変更する案がすでに検討されていました。しかし、新しいAPI Gatewayを前段に置くにあたり、元のDNSへのすべてのリクエストを新しいドメインにリダイレクトするのは避けたいと考えました。そこで、旧APIドメイン全体に対し、トラフィックの割合を段階的に増やすカナリアリリース方式でAPI Gatewayを展開しています。
つまり現在は、元のDNSに届くトラフィックの一部をAPI Gateway経由で処理しています。それ以外のトラフィックはモノリスに渡し、認証の判断を行います。
Glooは標準でカナリアリリースに対応しています。しかし、まだAPIトラフィックのすべてをGateway経由にしていないため、今回はEnvoy Proxyのトラフィックシフトパターンを使用しています。以下の図は、その仕組みと活用方法を大まかに示したものです。

JWT検証をエッジに移行する
前述のOAuthの概念実証(PoC)ではOAuthを実装していましたが、IDハブとの結合度が高く、モノリスに実装されていました。この新しい認証ライブラリでは、IDハブから送られるJWTを検証する必要があり、次のような処理を行います。
JWTペイロード(sub、iat、aud)の検証
JWK(JSON Web Key Sets)エンドポイントを使ったJWT署名の検証
これは技術的には簡単な課題ですが、テストを除いても、通常はかなりの量のコーディングが必要です(100行未満ではあります)。Glooでは、VirtualServiceに20行未満のコードを追加するだけで同じことができます。
この設定は、ヘッダーからJWTを取り出し、いくつかの基本的な検証を行ってから、JWTをExternal Auth(ExtAuth)プラグインに渡します(詳しくは後ほど説明します)。
現在時刻がJWTのiat(発行時刻)とexp(有効期限)のクレームの範囲内であることを検証
オーディエンスを検証
発行者を検証
外部JWKエンドポイント(アップストリーム)を使って署名を検証
モノリスで行っていた検証の多くをエッジに移せました。いいですよね???
認証方式を統一する
エッジでJWTを検証しても、モノリスの負荷を軽減できただけです。記事のタイトルや導入からお察しのとおり、エッジでJWTを検証するだけでは不十分です。ユーザーが提供する鍵情報に基づいてGlooが認証を判断できるようにし、認証の判断そのものもエッジに移したいと考えています。
これを実現するため、External Authorization(ExtAuth)プラグインを導入しました。Gloo独自のプラグインで(Enterpriseスイートでのみ利用可能)、Goで記述され、Envoy External Authorizationフィルターにコンパイルされます。このプラグインはまず、ユーザーが提供した鍵情報の種類を識別して本人確認を行います。IDが確認されると、プラグインがトークンに暗号学的な署名を付与して下流のAPIに渡します。APIはトークンの署名を検証し、ユーザーに代わって要求された処理を実行します。以下の図は、その仕組みを簡単に示したものです。

つまりこの時点で、クラスター内のあらゆるサービスが利用できる内部トークンを作成できました。このトークンには、ユーザーデータベース内のユーザーにひも付けられる情報が含まれています。Gloo経由でルーティングされたサービスでは、JWTがGlooによって署名されていることを確認します。大きな変化には見えないかもしれませんが、これまでは各サービスからユーザーのIDに関する情報を取得するために、モノリスへ明示的にユーザーデータを要求する必要がありました。
さらにGlooを導入することで、サービスは単一の認証方式を扱うだけで済み、エッジでの複数の認証方式の管理はGlooに任せられます。
この仕組みは、コードをほとんど追加せずに、比較的スムーズに導入できます(その大半はSoloが用意したボイラープレートです)。各サービスで独自の認証方式を実装する場合と比べて、手間を大幅に減らせます。
今後の取り組み
今後のスプリントでは、ExtensibilityチームがAPI Gatewayに関する次の2つの領域に注力します。
Glooで処理するAPIトラフィックを段階的に増やす(つまり、カナリアリリース!)。パートナー固有ではない通常のトラフィックをAPI Gateway経由でルーティングし始める予定です。そのために、必要なオブザーバビリティとアラートを整備し、オンコールエンジニアが新しいインフラをサポートできるようにします。また、現在のAPIのインターフェースに届いているトラフィックの一部をGlooのEnvoyプロキシへルーティングする作業も必要です。
Glooを拡張し、エッジでリクエストを検証できるようにする。現在、Glooはこの機能を標準ではサポートしていません。
ご自身の環境でGlooの利用を検討している方にとって、この記事がお役に立てば幸いです。今後のブログ記事では、GlooがSnykのエンジニアリングチームとプロダクトチームに提供する優れた基本的なオブザーバビリティや、SnykのCI/CDプラクティスの改善をどのように促したかをご紹介します。
最先端のエンジニアリングチームの一員になりませんか? 世界各地で採用中です。


