昨日のエントリでは**、UpstreamCA** pluginには複数台でSPIRE Serverを構成したときにJWT SVIDを正しく扱えないことあったという話と、それを解決するためにUpstreamAuthority pluginが登場した話をしました。
本日は実際にUpstreamAuthority spire
pluginを使って、複数台でSPIRE Serverを構成した場合にもJWT SVIDの検証鍵がワークロードに正しく配布されることを確認します。
Nested SPIREとは
UpstreamAuthority spire
pluginでは、Upstream AuthorityとなるUpstreamのSPIREとそれを参照するDownstreamのSPIREによる多段の構成となり、SPIREプロジェクトではこれをNested SPIREと呼んでいます。
DownstreamのSPIRE Serverは、UpstreamのSPIRE ServerにX.509 SVIDのCA証明書発行の依頼やJWTの検証鍵を登録したりします。UpstreamのSPIRE Serverはさらに外部のCAと接続してもよいですし、自身がルートCAとなってもよいです。
Nested SPIREを構成するにあたって必要となる、Downstream SPIRE ServerのためのCA証明書発行のエンドポイントは認証・認可によるアクセス制御がおこなわれています。SPIRE Serverはリクエスト元のSPIFFE IDに対して、自身がもつRegistrationEntryで対象SPIFFE IDのエントリにdownstream
のフラグが立っていることを確認します。
したがって、Nested SPIRE構成では、UpstreamのSPIRE ServerにDownstream SPIREのためのRegistrationEntryの登録が必要になり、DownstreamのSPIRE ServerはそのSPIFFE IDを含むSVIDをUpstreamのSPIREから取得し、それを使ってCA証明書発行用のエンドポイントに接続しなければなりません。
e.g. downstream フラグが立っているRegistrationEntryの例
Entry ID : e79c6ae1-186f-4430-ad2d-dfe33bdacd3f
SPIFFE ID : spiffe://example.org/my-downstream-spire
Parent ID : spiffe://example.org/my-node
Revision : 0
Downstream : true
TTL : default
Selector : unix:uid:0
そのため、UpstreamAuthority spire
pluginでは、DownstreamのSPIRE ServerのノードにSPIRE Agentを同居させ、Downstream SPIRE ServerはAgentが提供するWorkload API経由でUpstream SPIREが署名したSVIDを取得し、これを使ってUpstream SPIRE Serverに接続するような方法を取っています。
Upstream SPIRE Serverのセットアップ
設定ファイルは公式リポジトリで提供されている設定ファイル例を参考に以下のようにしています。
Upstream SPIRE Serverの設定
Upstream SPIRE ServerはUpstreamAuthority disk
pluginを使っており、今回の設定ではこのファイルで配置されたCA証明書がルートCAの証明書となります。disk
pluginは、ファイルとして配置された秘密鍵を使ってUpstream SPIRE ServerのCA証明書を発行します。
NodeAttestationについては、プラットフォームに依存しないpluginとして join_token
, x509pop
, sshpop
の3つがビルトインとして提供されています。ここでは一番簡単なJoin Tokenというワンタイムトークンを使ったjoin_token
pluginを使ってセットアップします。
※ **x509pop**
, **sshpop**
を使ったNode Attestationについては、明日のエントリで紹介したいと思います。
server {
bind_address = "0.0.0.0"
bind_port = "8081"
registration_uds_path = "/tmp/spire-registration.sock"
trust_domain = "example.org"
data_dir = "/var/lib/spire/data"
log_level = "DEBUG"
default_svid_ttl = "1h"
ca_subject = {
country = ["US"]
organization = ["SPIFFE"]
common_name = ""
}
}
plugins {
DataStore "sql" {
plugin_data {
database_type = "sqlite3"
connection_string = "/var/lib/spire/data/datastore.sqlite3"
}
}
NodeAttestor "join_token" {
plugin_data {
}
}
NodeResolver "noop" {
plugin_data {}
}
KeyManager "memory" {
plugin_data = {}
}
UpstreamAuthority "disk" {
plugin_data {
key_file_path = "/var/lib/spire/ca/dummy_upstream_ca.key"
cert_file_path = "/var/lib/spire/ca/dummy_upstream_ca.crt"
}
}
}
Upstream SPIRE Serverの起動
upstream-spire-0 $ spire-server run -config server.conf
Upstream SPIRE ServerにてJoin Tokenの発行
DownstreamのSPIRE Agentが接続するためのワンタイムトークンを発行します。
upstream-spire-0 $ sudo spire-server token generate \
-spiffeID spiffe://example.org/nodes/downstream-spire
Token: <Token String...>
Upstream SPIRE ServerでのJWT検証鍵一覧
以下のコマンドでSPIRE Serverが持っているJWT検証鍵を確認することができます(use
がjwt-svid
になっているもの)。
この時点ではUpstream SPIRE Server一台しか存在しないため、検証鍵も当然一つしか表示されていません。
upstream-spire-0 $ sudo spire-server experimental bundle show
{
"keys": [
{
"use": "x509-svid",
"kty": "EC",
"crv": "P-384",
"x": "WjB-nSGSxIYiznb84xu5WGDZj80nL7W1c3zf48Why0ma7Y7mCBKzfQkrgDguI4j0",
"y": "Z-0_tDH_r8gtOtLLrIpuMwWHoe4vbVBFte1vj6Xt6WeE8lXwcCvLs_mcmvPqVK9j",
"x5c": [
"MIIBzDCCAVOgAwIBAgIJAJM4DhRH0vmuMAoGCCqGSM49BAMEMB4xCzAJBgNVBAYTAlVTMQ8wDQYDVQQKDAZTUElGRkUwHhcNMTgwNTEzMTkzMzQ3WhcNMjMwNTEyMTkzMzQ3WjAeMQswCQYDVQQGEwJVUzEPMA0GA1UECgwGU1BJRkZFMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEWjB+nSGSxIYiznb84xu5WGDZj80nL7W1c3zf48Why0ma7Y7mCBKzfQkrgDguI4j0Z+0/tDH/r8gtOtLLrIpuMwWHoe4vbVBFte1vj6Xt6WeE8lXwcCvLs/mcmvPqVK9jo10wWzAdBgNVHQ4EFgQUh6XzV6LwNazA+GTEVOdu07o5yOgwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwGQYDVR0RBBIwEIYOc3BpZmZlOi8vbG9jYWwwCgYIKoZIzj0EAwQDZwAwZAIwE4Me13qMC9i6Fkx0h26y09QZIbuRqA9puLg9AeeAAyo5tBzRl1YL0KNEp02VKSYJAjBdeJvqjJ9wW55OGj1JQwDFD7kWeEB6oMlwPbI/5hEY3azJi16I0uN1JSYTSWGSqWc="
]
},
{
"use": "jwt-svid",
"kty": "EC",
"kid": "4J21ZX6a0KC4SvtapHZ3Lixwte2YKYNP",
"crv": "P-256",
"x": "flO4U6AEGTAVw9PGXY8kjXzy9AxVMACbYdOMZKqZkAc",
"y": "SFS9LYhUNdm1ja1HCYE_eBJ9IB-2EPydikaI2eaom_A"
}
]
}
Upstream SPIRE ServerにてRegistrationEntryの作成
Join Token発行時に指定したSPIFFE IDをParent IDとして指定し、Downstream SPIRE Serverに対応するRegistrationEntryのレコードを作成します。
upstream-spire-0 $ sudo spire-server entry create
-parentID spiffe://example.org/nodes/downstream-spire
-spiffeID spiffe://example.org/workloads/downstream-spire
-selector unix:uid:0
-downstream
Entry ID : 20029c0e-f4b1-42df-af1e-fcd75609c631
SPIFFE ID : spiffe://example.org/workloads/downstream-spire
Parent ID : spiffe://example.org/nodes/downstream-spire
Downstream : true
TTL : 3600
Selector : unix:uid:0
Downstream SPIRE Agentの設定
Downstream SPIRE Agentはserver_address
にUpstream SPIRE ServerのIPアドレスやFQDNを指定します。また、Node Attestationに必要なJoin Tokenは起動パラメータまたは、設定ファイルで指定する必要があり、今回は設定ファイル内で指定しています。
agent {
data_dir = "/var/lib/spire/data"
log_level = "DEBUG"
server_address = "${UPSTREAM_SPIRE_ADDR}"
server_port = "8081"
socket_path ="/tmp/agent.sock"
# upstream spire serverで指定したUpstream CAの証明書
trust_bundle_path = "/etc/spire/dummy_upstream_ca.crt"
trust_domain = "example.org"
# upstream spire serverで発行したトークン
join_token = "${JOIN_TOKEN}"
}
plugins {
NodeAttestor "join_token" {
plugin_data {
}
}
# 再起動のたびにトークン再発行にならないように今回は秘密鍵をdiskに保管
KeyManager "disk" {
plugin_data {
directory = "/var/lib/spire/data/key"
}
}
# 今回は単純にするためunix pluginのみ
WorkloadAttestor "unix" {
plugin_data {
}
}
}
Downstream SPIRE Agentの起動
downstream-spire-0 $ sudo spire-agent run -config agent.conf
Downstream SPIRE Serverの設定
Downstream SPIRE ServerではUpstreamAuthority spire
pluginを利用し、pluginの設定にてUpstream SPIRE Serverのアドレスを指定しています。
※ Downstream SPIRE Serverはスケールアップなどの要件で複数台で構成されることが予想されるため、本来はデータストアとしてMySQLなどを選択し複数のDownstream SPIRE Serverでデータストアを共有する構成にする必要があります。今回はJWTの検証鍵の配布の確認だけが目的なのでローカルのデータストアを参照しています。
server {
bind_address = "0.0.0.0"
bind_port = "8081"
registration_uds_path = "/tmp/spire-registration.sock"
trust_domain = "example.org"
data_dir = "/var/lib/spire/data"
log_level = "DEBUG"
default_svid_ttl = "1h"
ca_subject = {
country = ["US"]
organization = ["SPIFFE"]
common_name = ""
}
}
plugins {
DataStore "sql" {
plugin_data {
# 本来は複数台で構成する場合はMySQLなどを利用する
database_type = "sqlite3"
connection_string = "/var/lib/spire/data/datastore.sqlite3"
}
}
NodeAttestor "join_token" {
plugin_data {
}
}
NodeResolver "noop" {
plugin_data {}
}
KeyManager "memory" {
plugin_data = {}
}
UpstreamAuthority "spire" {
plugin_data {
server_address = "${UPSTREAM_SPIRE_ADDR}",
server_port = "8081",
workload_api_socket = "/tmp/agent.sock"
}
}
}
Downstream SPIRE Serverの起動
downstream-spire-0 $ spire-server run -config server.conf
JWT検証鍵の確認
Upstream SPIRE Serverにて先ほどと同じコマンドを実行してみると、JWT検証鍵が一つ増えていることが確認できます。
upstream spire server
upstream-spire-0 $ sudo spire-server experimental bundle show
{
"keys": [
{
"use": "x509-svid",
"kty": "EC",
"crv": "P-384",
"x": "WjB-nSGSxIYiznb84xu5WGDZj80nL7W1c3zf48Why0ma7Y7mCBKzfQkrgDguI4j0",
"y": "Z-0_tDH_r8gtOtLLrIpuMwWHoe4vbVBFte1vj6Xt6WeE8lXwcCvLs_mcmvPqVK9j",
"x5c": [
"MIIBzDCCAVOgAwIBAgIJAJM4DhRH0vmuMAoGCCqGSM49BAMEMB4xCzAJBgNVBAYTAlVTMQ8wDQYDVQQKDAZTUElGRkUwHhcNMTgwNTEzMTkzMzQ3WhcNMjMwNTEyMTkzMzQ3WjAeMQswCQYDVQQGEwJVUzEPMA0GA1UECgwGU1BJRkZFMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEWjB+nSGSxIYiznb84xu5WGDZj80nL7W1c3zf48Why0ma7Y7mCBKzfQkrgDguI4j0Z+0/tDH/r8gtOtLLrIpuMwWHoe4vbVBFte1vj6Xt6WeE8lXwcCvLs/mcmvPqVK9jo10wWzAdBgNVHQ4EFgQUh6XzV6LwNazA+GTEVOdu07o5yOgwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwGQYDVR0RBBIwEIYOc3BpZmZlOi8vbG9jYWwwCgYIKoZIzj0EAwQDZwAwZAIwE4Me13qMC9i6Fkx0h26y09QZIbuRqA9puLg9AeeAAyo5tBzRl1YL0KNEp02VKSYJAjBdeJvqjJ9wW55OGj1JQwDFD7kWeEB6oMlwPbI/5hEY3azJi16I0uN1JSYTSWGSqWc="
]
},
{ # upstream-spire-0の検証鍵
"use": "jwt-svid",
"kty": "EC",
"kid": "4J21ZX6a0KC4SvtapHZ3Lixwte2YKYNP",
"crv": "P-256",
"x": "flO4U6AEGTAVw9PGXY8kjXzy9AxVMACbYdOMZKqZkAc",
"y": "SFS9LYhUNdm1ja1HCYE_eBJ9IB-2EPydikaI2eaom_A"
},
{ # downstream-spire-0の検証鍵
"use": "jwt-svid",
"kty": "EC",
"kid": "v7WFMeyjcmRi895PP0Kzis6iKVFcjrgt",
"crv": "P-256",
"x": "TTiFdYM2b2IZ0otfPD3oB14rULysd0PsQ-V3eZf56SM",
"y": "cXFVTawAcfyv2sYD0vo3Nh0MLT7BdJ_bIXKR2z33YWM"
}
]
}
さらにDownstream SPIRE Serverも同様に鍵が伝搬されていることを確認します。
downstream spire server
downstream-sprie-0 $ sudo spire-server experimental bundle show
{
"keys": [
{
"use": "x509-svid",
"kty": "EC",
"crv": "P-384",
"x": "WjB-nSGSxIYiznb84xu5WGDZj80nL7W1c3zf48Why0ma7Y7mCBKzfQkrgDguI4j0",
"y": "Z-0_tDH_r8gtOtLLrIpuMwWHoe4vbVBFte1vj6Xt6WeE8lXwcCvLs_mcmvPqVK9j",
"x5c": [
"MIIBzDCCAVOgAwIBAgIJAJM4DhRH0vmuMAoGCCqGSM49BAMEMB4xCzAJBgNVBAYTAlVTMQ8wDQYDVQQKDAZTUElGRkUwHhcNMTgwNTEzMTkzMzQ3WhcNMjMwNTEyMTkzMzQ3WjAeMQswCQYDVQQGEwJVUzEPMA0GA1UECgwGU1BJRkZFMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEWjB+nSGSxIYiznb84xu5WGDZj80nL7W1c3zf48Why0ma7Y7mCBKzfQkrgDguI4j0Z+0/tDH/r8gtOtLLrIpuMwWHoe4vbVBFte1vj6Xt6WeE8lXwcCvLs/mcmvPqVK9jo10wWzAdBgNVHQ4EFgQUh6XzV6LwNazA+GTEVOdu07o5yOgwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwGQYDVR0RBBIwEIYOc3BpZmZlOi8vbG9jYWwwCgYIKoZIzj0EAwQDZwAwZAIwE4Me13qMC9i6Fkx0h26y09QZIbuRqA9puLg9AeeAAyo5tBzRl1YL0KNEp02VKSYJAjBdeJvqjJ9wW55OGj1JQwDFD7kWeEB6oMlwPbI/5hEY3azJi16I0uN1JSYTSWGSqWc="
]
},
{ # upstream-spire-0の検証鍵
"use": "jwt-svid",
"kty": "EC",
"kid": "4J21ZX6a0KC4SvtapHZ3Lixwte2YKYNP",
"crv": "P-256",
"x": "flO4U6AEGTAVw9PGXY8kjXzy9AxVMACbYdOMZKqZkAc",
"y": "SFS9LYhUNdm1ja1HCYE_eBJ9IB-2EPydikaI2eaom_A"
},
{ # downstream-spire-0の検証鍵
"use": "jwt-svid",
"kty": "EC",
"kid": "v7WFMeyjcmRi895PP0Kzis6iKVFcjrgt",
"crv": "P-256",
"x": "TTiFdYM2b2IZ0otfPD3oB14rULysd0PsQ-V3eZf56SM",
"y": "cXFVTawAcfyv2sYD0vo3Nh0MLT7BdJ_bIXKR2z33YWM"
}
]
}
Downstream SPIRE Serverの追加
ここでDownstream SPIRE Serverをもう一台追加してみます。
DownstreamのAgentとServerの設定は同じですが、Join Tokenは追加するNode用に発行する必要があります。
upstream-spire-0 $ sudo spire-server token generate \
-spiffeID spiffe://example.org/nodes/downstream-spire
Token: <Token String...>
downstream-spire-1 $ spire-agent run -config agent.conf
downstream-spire-1 $ spire-server run -config server.conf
JWT検証鍵一覧の確認
Downstream SPRIE Serverを一台追加したところで、再度JWTの検証鍵を確認してみると、すべてのSPIRE Serverで検証鍵が一つ追加されていることが確認できます。
upstream-spire-0 $ sudo spire-server experimental bundle show
{
"keys": [
{
"use": "x509-svid",
"kty": "EC",
"crv": "P-384",
"x": "WjB-nSGSxIYiznb84xu5WGDZj80nL7W1c3zf48Why0ma7Y7mCBKzfQkrgDguI4j0",
"y": "Z-0_tDH_r8gtOtLLrIpuMwWHoe4vbVBFte1vj6Xt6WeE8lXwcCvLs_mcmvPqVK9j",
"x5c": [
"MIIBzDCCAVOgAwIBAgIJAJM4DhRH0vmuMAoGCCqGSM49BAMEMB4xCzAJBgNVBAYTAlVTMQ8wDQYDVQQKDAZTUElGRkUwHhcNMTgwNTEzMTkzMzQ3WhcNMjMwNTEyMTkzMzQ3WjAeMQswCQYDVQQGEwJVUzEPMA0GA1UECgwGU1BJRkZFMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEWjB+nSGSxIYiznb84xu5WGDZj80nL7W1c3zf48Why0ma7Y7mCBKzfQkrgDguI4j0Z+0/tDH/r8gtOtLLrIpuMwWHoe4vbVBFte1vj6Xt6WeE8lXwcCvLs/mcmvPqVK9jo10wWzAdBgNVHQ4EFgQUh6XzV6LwNazA+GTEVOdu07o5yOgwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwGQYDVR0RBBIwEIYOc3BpZmZlOi8vbG9jYWwwCgYIKoZIzj0EAwQDZwAwZAIwE4Me13qMC9i6Fkx0h26y09QZIbuRqA9puLg9AeeAAyo5tBzRl1YL0KNEp02VKSYJAjBdeJvqjJ9wW55OGj1JQwDFD7kWeEB6oMlwPbI/5hEY3azJi16I0uN1JSYTSWGSqWc="
]
},
{ # upstream-spire-0の検証鍵
"use": "jwt-svid",
"kty": "EC",
"kid": "4J21ZX6a0KC4SvtapHZ3Lixwte2YKYNP",
"crv": "P-256",
"x": "flO4U6AEGTAVw9PGXY8kjXzy9AxVMACbYdOMZKqZkAc",
"y": "SFS9LYhUNdm1ja1HCYE_eBJ9IB-2EPydikaI2eaom_A"
},
{ # downstream-spire-0の検証鍵
"use": "jwt-svid",
"kty": "EC",
"kid": "v7WFMeyjcmRi895PP0Kzis6iKVFcjrgt",
"crv": "P-256",
"x": "TTiFdYM2b2IZ0otfPD3oB14rULysd0PsQ-V3eZf56SM",
"y": "cXFVTawAcfyv2sYD0vo3Nh0MLT7BdJ_bIXKR2z33YWM"
},
{ # downstream-spire-1の検証鍵
"use": "jwt-svid",
"kty": "EC",
"kid": "5rtNSCHGanu4zZxHEMElcD40EpEXWWBy",
"crv": "P-256",
"x": "JcAOx-y9l0irle-4dIn6XN7ei5Kxtq9TGlDNTyRpRec",
"y": "At-3XXCrVs5OBiTKcwCIhA9lj_iGf05lWaOS6LxBo_4"
}
]
}
downstream-spire-0 $ sudo spire-server experimental bundle show
{
"keys": [
{
"use": "x509-svid",
"kty": "EC",
"crv": "P-384",
"x": "WjB-nSGSxIYiznb84xu5WGDZj80nL7W1c3zf48Why0ma7Y7mCBKzfQkrgDguI4j0",
"y": "Z-0_tDH_r8gtOtLLrIpuMwWHoe4vbVBFte1vj6Xt6WeE8lXwcCvLs_mcmvPqVK9j",
"x5c": [
"MIIBzDCCAVOgAwIBAgIJAJM4DhRH0vmuMAoGCCqGSM49BAMEMB4xCzAJBgNVBAYTAlVTMQ8wDQYDVQQKDAZTUElGRkUwHhcNMTgwNTEzMTkzMzQ3WhcNMjMwNTEyMTkzMzQ3WjAeMQswCQYDVQQGEwJVUzEPMA0GA1UECgwGU1BJRkZFMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEWjB+nSGSxIYiznb84xu5WGDZj80nL7W1c3zf48Why0ma7Y7mCBKzfQkrgDguI4j0Z+0/tDH/r8gtOtLLrIpuMwWHoe4vbVBFte1vj6Xt6WeE8lXwcCvLs/mcmvPqVK9jo10wWzAdBgNVHQ4EFgQUh6XzV6LwNazA+GTEVOdu07o5yOgwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwGQYDVR0RBBIwEIYOc3BpZmZlOi8vbG9jYWwwCgYIKoZIzj0EAwQDZwAwZAIwE4Me13qMC9i6Fkx0h26y09QZIbuRqA9puLg9AeeAAyo5tBzRl1YL0KNEp02VKSYJAjBdeJvqjJ9wW55OGj1JQwDFD7kWeEB6oMlwPbI/5hEY3azJi16I0uN1JSYTSWGSqWc="
]
},
{ # upstream-spire-0の検証鍵
"use": "jwt-svid",
"kty": "EC",
"kid": "4J21ZX6a0KC4SvtapHZ3Lixwte2YKYNP",
"crv": "P-256",
"x": "flO4U6AEGTAVw9PGXY8kjXzy9AxVMACbYdOMZKqZkAc",
"y": "SFS9LYhUNdm1ja1HCYE_eBJ9IB-2EPydikaI2eaom_A"
},
{ # downstream-spire-0の検証鍵
"use": "jwt-svid",
"kty": "EC",
"kid": "v7WFMeyjcmRi895PP0Kzis6iKVFcjrgt",
"crv": "P-256",
"x": "TTiFdYM2b2IZ0otfPD3oB14rULysd0PsQ-V3eZf56SM",
"y": "cXFVTawAcfyv2sYD0vo3Nh0MLT7BdJ_bIXKR2z33YWM"
},
{ # downstream-spire-1の検証鍵
"use": "jwt-svid",
"kty": "EC",
"kid": "5rtNSCHGanu4zZxHEMElcD40EpEXWWBy",
"crv": "P-256",
"x": "JcAOx-y9l0irle-4dIn6XN7ei5Kxtq9TGlDNTyRpRec",
"y": "At-3XXCrVs5OBiTKcwCIhA9lj_iGf05lWaOS6LxBo_4"
}
]
}
downstream-spire-1 $ sudo spire-server experimental bundle show
{
"keys": [
{
"use": "x509-svid",
"kty": "EC",
"crv": "P-384",
"x": "WjB-nSGSxIYiznb84xu5WGDZj80nL7W1c3zf48Why0ma7Y7mCBKzfQkrgDguI4j0",
"y": "Z-0_tDH_r8gtOtLLrIpuMwWHoe4vbVBFte1vj6Xt6WeE8lXwcCvLs_mcmvPqVK9j",
"x5c": [
"MIIBzDCCAVOgAwIBAgIJAJM4DhRH0vmuMAoGCCqGSM49BAMEMB4xCzAJBgNVBAYTAlVTMQ8wDQYDVQQKDAZTUElGRkUwHhcNMTgwNTEzMTkzMzQ3WhcNMjMwNTEyMTkzMzQ3WjAeMQswCQYDVQQGEwJVUzEPMA0GA1UECgwGU1BJRkZFMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEWjB+nSGSxIYiznb84xu5WGDZj80nL7W1c3zf48Why0ma7Y7mCBKzfQkrgDguI4j0Z+0/tDH/r8gtOtLLrIpuMwWHoe4vbVBFte1vj6Xt6WeE8lXwcCvLs/mcmvPqVK9jo10wWzAdBgNVHQ4EFgQUh6XzV6LwNazA+GTEVOdu07o5yOgwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwGQYDVR0RBBIwEIYOc3BpZmZlOi8vbG9jYWwwCgYIKoZIzj0EAwQDZwAwZAIwE4Me13qMC9i6Fkx0h26y09QZIbuRqA9puLg9AeeAAyo5tBzRl1YL0KNEp02VKSYJAjBdeJvqjJ9wW55OGj1JQwDFD7kWeEB6oMlwPbI/5hEY3azJi16I0uN1JSYTSWGSqWc="
]
},
{ # upstream-spire-0の検証鍵
"use": "jwt-svid",
"kty": "EC",
"kid": "4J21ZX6a0KC4SvtapHZ3Lixwte2YKYNP",
"crv": "P-256",
"x": "flO4U6AEGTAVw9PGXY8kjXzy9AxVMACbYdOMZKqZkAc",
"y": "SFS9LYhUNdm1ja1HCYE_eBJ9IB-2EPydikaI2eaom_A"
},
{ # downstream-spire-0の検証鍵
"use": "jwt-svid",
"kty": "EC",
"kid": "v7WFMeyjcmRi895PP0Kzis6iKVFcjrgt",
"crv": "P-256",
"x": "TTiFdYM2b2IZ0otfPD3oB14rULysd0PsQ-V3eZf56SM",
"y": "cXFVTawAcfyv2sYD0vo3Nh0MLT7BdJ_bIXKR2z33YWM"
},
{ # downstream-spire-1の検証鍵
"use": "jwt-svid",
"kty": "EC",
"kid": "5rtNSCHGanu4zZxHEMElcD40EpEXWWBy",
"crv": "P-256",
"x": "JcAOx-y9l0irle-4dIn6XN7ei5Kxtq9TGlDNTyRpRec",
"y": "At-3XXCrVs5OBiTKcwCIhA9lj_iGf05lWaOS6LxBo_4"
}
]
}
Workload APIからJWT検証鍵一覧を取得してみる
ここまででSPIRE Serverには鍵が正しく伝搬されていることが確認できました。
最後に、WorkloadがSPIRE Agentを使ってDownstream SPIRE Serverから鍵の束を取得できることを確認してみたいと思います。
Node Attestation用Join Tokenの発行
downstream-spire-0 $ sudo spire-server token generate -spiffeID spiffe://example.org/nodes/my-node
Token: 679f5b05-ca6c-4a10-8362-13aaa61ef86c
テスト用WorkloadのEntry作成
downstream-spire-0 $ sudo spire-server entry create
-parentID spiffe://example.org/nodes/my-node
-spiffeID spiffe://example.org/workloads/my-test-app
-selector unix:uid:1000
Entry ID : 9b892440-9227-4840-9f45-9eb9bd55080d
SPIFFE ID : spiffe://example.org/workloads/my-test-app
Parent ID : spiffe://example.org/nodes/my-node
TTL : 3600
Selector : unix:uid:1000
Agentの設定
agent {
data_dir = "/var/lib/spire/data"
log_level = "DEBUG"
server_address = "${DOWNSTREAM_SPIRE_ADDR}"
server_port = "8081"
socket_path ="/tmp/agent.sock"
trust_bundle_path = "/etc/spire/dummy_upstream_ca.crt"
trust_domain = "example.org"
join_token = "{JOIN_TOKEN}"
}
plugins {
NodeAttestor "join_token" {
plugin_data {
}
}
KeyManager "disk" {
plugin_data {
directory = "/var/lib/spire/data/key"
}
}
WorkloadAttestor "unix" {
plugin_data {
}
}
}
Agentの起動
my-node $ sudo spire-agent run -config agent.conf
以下のようなテストアプリケーションを作成し、Workload API経由で検証鍵の束を取得します。
package main
import (
"context"
"fmt"
"log"
"time"
"github.com/spiffe/go-spiffe/v2/spiffeid"
"github.com/spiffe/go-spiffe/v2/workloadapi"
)
const (
socketPath = "unix:///tmp/agent.sock"
trustDomain = "example.org"
)
func main() {
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
clientOptions := workloadapi.WithClientOptions(workloadapi.WithAddr(socketPath))
jwtSource, err := workloadapi.NewJWTSource(ctx, clientOptions)
if err != nil {
log.Fatalf("Unable to create JWTSource: %v", err)
}
defer jwtSource.Close()
td, err := spiffeid.TrustDomainFromString(trustDomain)
if err != nil {
log.Fatalf("Unable to parse TrustDomain: %v", err)
}
bundles, err := jwtSource.GetJWTBundleForTrustDomain(td)
if err != nil {
log.Fatalf("Unable to fetch SVID: %v", err)
}
b, err := bundles.Marshal()
if err != nil {
log.Fatalf("Unable to marshal bundles: %v", err)
}
fmt.Println(string(b))
}
テスト用のアプリケーションはRegistrationEntryにしたがってuid=1000のユーザで実行します。
$ ./get-bundle | jq .
{
"keys": [
{
"kty": "EC",
"kid": "4J21ZX6a0KC4SvtapHZ3Lixwte2YKYNP",
"crv": "P-256",
"x": "flO4U6AEGTAVw9PGXY8kjXzy9AxVMACbYdOMZKqZkAc",
"y": "SFS9LYhUNdm1ja1HCYE_eBJ9IB-2EPydikaI2eaom_A"
},
{
"kty": "EC",
"kid": "v7WFMeyjcmRi895PP0Kzis6iKVFcjrgt",
"crv": "P-256",
"x": "TTiFdYM2b2IZ0otfPD3oB14rULysd0PsQ-V3eZf56SM",
"y": "cXFVTawAcfyv2sYD0vo3Nh0MLT7BdJ_bIXKR2z33YWM"
},
{
"kty": "EC",
"kid": "5rtNSCHGanu4zZxHEMElcD40EpEXWWBy",
"crv": "P-256",
"x": "JcAOx-y9l0irle-4dIn6XN7ei5Kxtq9TGlDNTyRpRec",
"y": "At-3XXCrVs5OBiTKcwCIhA9lj_iGf05lWaOS6LxBo_4"
}
]
}
無事JWK Set形式ですべてのSPIRE Server分のJWT検証用の鍵を取得することができました。
まとめ
冗長化のためにSPIRE Serverを複数台で構成する場合、かつJWT SVIDを扱う必要がある場合にはUpstreamAuthority spire
pluginでNested SPIREを構成する必要があります。
Upstream SPIRE Serverが提供する、CA証明書発行のエンドポイントへの接続には、Upstream SPIRE ServerのRegistrationEntryでdownstream
フラグがたっているSPIFFE IDを含むSVIDが必要になります。
UpstreamAuthority spire
pluginは、SPIRE Agent経由でUpstream SPIRE Serverへの接続に必要なSVIDを取得するため、Downstream SPIREのノードにはSPIRE ServerとSPIRE Agentを同居させる必要があります。
UpstreamAuthority pluguinの話はこのあたりで終わりにして、明日のエントリではワンタイムトークンを使ったNode Attestationではなく、証明書を使ったNode Attestationについて触れてみたいと思います。