背景
SPIREではNode AttestationやWorkload Attestationなどのいくつかの機能について、plugin形式で任意の実装に差し替えることが可能になっています。pluginは組み込みのものもあれば、別のバイナリとして提供されたものを利用することも可能です。
参考: https://spiffe.io/docs/latest/spire/developing/extending/
2020/12月現在の最新のspireのバージョンは0.11.2ですが、0.10.0がリリースされた際に UpstreamAuthority pluginという新しいpluginのインタフェースが追加されました。
UpstreamAuthority pluginは従来の UpstreamCA pluginを置き換える目的で追加されたものです。
このあたりの話は「KubeCon + CloudNativeCon North America 2020 Virtual」のco-locatedイベントして開催された「**Production Identity Day: SPIFFE + SPIRE hosted by CNCF」**のセッションでも触れられています。
- SPIRE Project Updates: https://youtu.be/VJCso4FzM9U
元々pluginの一つとしてUpstreamCA pluginというインタフェースが提供されており、これはSPIRE Serverが持つ内部のCAを既存の外部PKIに合流させるためのpluginになります。上流のCAに何を選択するかによって実装が異なるイメージです。 例えば、SPIRE Serverが発行するX.509 証明書(X.509 SVID)について、VaultのPKI Secret Engineを上流とするPKI TreeのLeaf証明書として扱いたい場合には UpstreamCA vault
pluginを使うといった具合です。
また、SPIRE Serverはプロセス毎にSVIDの署名に使う秘密鍵を持つ仕様となっており、KeyManager pluginによって管理されます。ビルトインのpluginとしてはdisk
とmemory
が提供されています。
そのため、SPIREではSVIDとしてX.509証明書とJWTの2つのフォーマットをサポートしていますが、異なるSPIRE Serverから発行されたSVIDはそれぞれ別の鍵で署名されたものとなります。
X.509証明書の場合、UpstreamCA pluginを使うことでそれぞれのSPIRE CAは同じルートに繋がることが可能になります。証明書を検証する側がUpstream CAを信頼できるものとして扱っていれば、サーバ毎に鍵が異なっていても証明書の検証で問題になることはありません。
一方でJWTの場合には、複数のSPIRE Serverが異なる鍵を使って署名することが問題になってしまいます。これは、SPIREの仕様によりSVIDはSPIRE Serverが自身が発行すること、JWT自体がトラストチェーンによる検証の仕組みを持っていないこと、SPIRE Agentは一つのSPIRE Serverにしか接続しないこと、などの理由によります。さらにUpstreamCA pluginはあくまでX.509証明書の仕組みのためのpluginであり、JWTのことは考慮されていませんでした。
この話は第2回のSPIFFE Meetup Tokyoで「Challenging Multiple SPIRE Servers」という発表をした際にも触れた内容になります。もし興味があればスライドやYoutubeにアップロードされている動画アーカイブをご覧頂ければと思います。
そこからSPIREコミュニティでの議論が進み、JWT SVIDの問題を解決するために登場したのがUpstreamAuthority pluginとなります。
UpstreamAuthority plugin
UpstreamAuthority plugin**は新たに、JWTの署名検証鍵を配布するためのメソッドが追加されました。
message PublishJWTKeyRequest {
spire.common.PublicKey jwt_key = 1;
}
message PublishJWTKeyResponse {
repeated spire.common.PublicKey upstream_jwt_keys = 1;
}
service UpstreamAuthority {
...
rpc PublishJWTKey(PublishJWTKeyRequest) returns (stream PublishJWTKeyResponse);
...
}
Upstream Authorityに対して自身が発行したJWTの署名検証鍵を登録すると、その応答としてUpstream Authorityが持つ署名検証鍵の束が返ってくるようなものです。SPIRE Serverは鍵を更新するたびにUpstreamAuthority pluginのPublishJWTKey メソッドを呼び出し、新しい鍵の登録を行います。
ただし、これでは新しい鍵を登録した際にしか最新の検証鍵の束を手にいれることができないため、定期的に検証鍵の束を手にいれる必要があります。
例えば、Upstream Authorityは最新の検証鍵の束を取得するためのAPIを提供してpluginが定期的に取得するような方法や、pluginが定期的にPublishJWTKeyを呼び出すが、Upstream Authorityはすでに登録されている鍵の場合は一覧に追加せず、最新の検証鍵の束を返すようにするなどの方法がありそうです。
現在PublishJWTKeyメソッドを実装しているpluginは UpstreamAuthority spire
pluginのみとなっていますが、こちらは別途検証鍵の束を定期的に入手しており、常にすべてのSPIRE Serverが他のSPIRE Serverが登録した最新の検証鍵を入手できるような仕組みになっています。
この新しいメソッドの追加により、ワークロードはどのSPIRE ServerからでもすべてのSPIRE Serverの検証鍵を手に入れることができ、複数台でSPIREを構成している場合であってもX.509証明書、JWTいずれのフォーマットのSVIDも扱うことが可能になり、ひとつの大きな課題が解消されました。
UpstreamAuthority pluginの登場により、UpstreamCA pluginはDeprecatedとなりました。現在ビルトインのpluginはすべてUpstreamAuthority pluginに移行済みとなっています。
ただし、先ほども触れたようにすべてのUpstreamAuthority pluginの実装でJWT SVIDを扱えるわけではありません。JWT SVIDを扱う要件がある場合には現在唯一PublishJWTKeyを実装しているUpstreamAuthority spire
pluginを使ってSPIREを構成する必要があります。この構成は「Nested SPIRE」と呼ばれ、Upstream Authorityとして振る舞うUpstream SPIRE Serverと、それを参照するDownstream SPIRE Serverから構成されます。
明日のエントリでは、実際にNested SPIREを構成してみたいと思います。