UpstreamAuthority spire pluginを使ってNested SPIREを構成する


昨日のエントリでは**、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となってもよいです。

![](https://paper-attachments.dropbox.com/s_E822259DA76E16AF64CC226C53246B06FE1E4219F83C2CF7E5389A6E189B0EEB_1607040820715_nested-spire-desc.jpg)


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に接続するような方法を取っています。

![](https://paper-attachments.dropbox.com/s_E822259DA76E16AF64CC226C53246B06FE1E4219F83C2CF7E5389A6E189B0EEB_1607047110643_downstream-sprie-as-workload.jpg)

## 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については、明日のエントリで紹介したいと思います。**

```hcl
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は起動パラメータまたは、設定ファイルで指定する必要があり、今回は設定ファイル内で指定しています。

```hcl
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の検証鍵の配布の確認だけが目的なのでローカルのデータストアを参照しています。**

参考: https://github.com/spiffe/spire/blob/master/doc/scaling_spire.md#spire-servers-in-high-availability-mode

```hcl
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の設定

```hcl
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経由で検証鍵の束を取得します。


```go
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について触れてみたいと思います。