LoginSignup
4
0

SPIRE Delegated Identity API

Last updated at Posted at 2023-12-04

Delegated Identity APIとは

SPIRE Agentが直接的に認証(attestation)できないワークロードのかわりに、委任されたワークロード(delegated workload)がSVIDとBundleを取得できる機能です。
この機能はSPIRE AgentのAdmin APIのエンドで提供されます。(Admin APIはデフォルト無効。)

なぜ必要となったのか

これは Ciliumコミュニティからの提案から始まったもので、この機能が実装されるまでのSPIREでは、Workload APIを呼び出したプロセス(ワークロード)に対して認証(attestation)しSVIDを発行するように設計されていました。
しかし、あるノードに単一のエージェントプロセスがあり、それが他のプロセスに変わって処理を実行するようなケースなどをカバーしきれていませんでした。

image.png

必要な設定

Delegated Identity APIはAdmin API Endpointで提供されるため、Admin API Endpointの有効化と委任するワークロードのSPIFFE IDを設定ファイルで指定する必要があります。

agent {  
    server_address = "127.0.0.1"  
    ...
  
    admin_socket_path = "/tmp/admin.sock"  
    authorized_delegates = [  
        "spiffe://example.org/authorized-delegate",  
    ]  
}  

他のSVIDの取得方法

Delegated Identity APIを呼び出すワークロードは、リクエストパラメータとしてかわりに取得するワークロードに対応するSelectorを指定する必要があります。
以下は、uid=1002のワークロードのSVIDをかわりに取得するリクエスト例です。

streamSubscribeToX509SVIDs, err := client.SubscribeToX509SVIDs(ctx, &delegatedidentityv1.SubscribeToX509SVIDsRequest{  
    Selectors: []*types.Selector{  
       {  
          Type:  "unix",  
          Value: "uid:1002",  
       },  
    },  
})  

試して見る

$ git clone https://github.com/spiffe/spire.git; cd spire
$ make dev-shell

// SPIRE本体をビルド
# make build
# PATH=.build/linux-x86_64/go/1.21.4/bin:$PATH

// テストツールをビルド
# go build -o delegatedidentityclient test/integration/setup/delegatedidentity/main.go

Serverの起動。設定ファイルの内容は設定ファイル参照。ただしServer側は特にDelegated Identity API向けの設定は必要ありません。

# ./bin/spire-server run -config conf/server/server.conf

JoinTokenの発行

# ./bin/spire-server token generate -spiffeID spiffe://example.org/token

RegistrationEntryの作成

# ./bin/spire-server entry create \
  -parentID spiffe://example.org/token \
  -spiffeID spiffe://example.org/authorized-delegate \
  -selector unix:uid:1001

# ./bin/spire-server entry create \
  -parentID spiffe://example.org/token \
  -spiffeID spiffe://example.org/test-workload \
  -selector unix:uid:1002

Agentの起動。設定ファイルの内容は設定ファイル参照。冒頭で紹介した必要となる設定を追加しています。

# ./bin/spire-agent run -config conf/agent/agent.conf -joinToken xxx

ワークロード=uid=1001によるSVIDの取得

$ docker exec -it --user 1001 <Container ID> bash

$ ./bin/spire-agent api fetch x509
Received 1 svid after 2.340039ms

SPIFFE ID:              spiffe://example.org/authorized-delegate
SVID Valid After:       2023-11-21 06:25:37 +0000 UTC
SVID Valid Until:       2023-11-21 07:25:47 +0000 UTC
Intermediate #1 Valid After:    2023-11-21 06:15:09 +0000 UTC
Intermediate #1 Valid Until:    2023-11-22 06:15:19 +0000 UTC
CA #1 Valid After:      2023-05-15 02:05:06 +0000 UTC
CA #1 Valid Until:      2028-05-13 02:05:06 +0000 UTC

uid=1001のワークロードはSPIRE AgentのWorkload APIにアクセスし、SVIDを取得することができました。この通常の認証(attestation)プロセスでは、自身のSVIDしか取得することができません。

Delegated Identity APIの例として、ここでuid=1002のワークロードがWorkload APIにアクセスできないものとし、かわりにuid=1001のワークロードに取得してもらいます。

$ ./delegatedidentityclient -adminSocketPath unix:///tmp/admin.sock
SPIFFE ID: spiffe://example.org/test-workload

uid=1001のワークロードの識別子(SPIFFE ID) spiffe://example.org/authorized_delegateはあらかじめ設定ファイルでdelegationを許可されているため、リクエストしたuid=1002のワークロード分のSVIDを取得することができます。

SPIRE Agentのログ

DEBU[0003] Caller authorized as delegate                 delegate_id="spiffe://example.org/authorized-delegate" delegate_selectors="[type:\"unix\"  value:\"uid:1001\" type:\"unix\"  value:\"gid:0\" type:\"unix\"  value:\"group:root\" type:\"unix\"  value:\"supplementary_gid:0\" type:\"unix\"  value:\"supplementary_group:root\"]" method=SubscribeToX509SVIDs pid=5848 service=DelegatedIdentity

DEBU[0003] Fetched X.509 SVID for delegated identity     count=1 method=SubscribeToX509SVIDs pid=5848 service=DelegatedIdentity spiffe_id="trust_domain:\"example.org\"  path:\"/test-workload\"" ttl=3596.540921648

テスト用の設定など

Server設定ファイル

server {  
    bind_address = "127.0.0.1"  
    bind_port = "8081"  
    socket_path = "/tmp/spire-server/private/api.sock"  
    trust_domain = "example.org"  
    data_dir = "./.data"  
    log_level = "DEBUG"  
}  
  
plugins {  
    DataStore "sql" {  
        plugin_data {  
            database_type = "sqlite3"  
            connection_string = "./.data/datastore.sqlite3"  
        }  
    }  
  
    NodeAttestor "join_token" {  
        plugin_data {  
        }  
    }  
  
    KeyManager "memory" {  
        plugin_data = {}  
    }  
  
    UpstreamAuthority "disk" {  
        plugin_data {  
            key_file_path = "./conf/server/dummy_upstream_ca.key"  
            cert_file_path = "./conf/server/dummy_upstream_ca.crt"  
        }  
    }  
}

Agent 設定ファイル

agent {  
    data_dir = "./.data"  
    log_level = "DEBUG"  
    server_address = "127.0.0.1"  
    server_port = "8081"  
    socket_path ="/tmp/spire-agent/public/api.sock"  
    trust_bundle_path = "./conf/agent/dummy_root_ca.crt"  
    trust_domain = "example.org"  
  
    admin_socket_path = "/tmp/admin.sock"  
    authorized_delegates = [  
        "spiffe://example.org/authorized-delegate",  
    ]  
}  
  
plugins {  
    NodeAttestor "join_token" {  
        plugin_data {  
        }  
    }  
    KeyManager "disk" {  
        plugin_data {  
            directory = "./.data"  
        }  
    }  
    WorkloadAttestor "unix" {  
        plugin_data {  
        }  
    }  
}

コード

package main  
  
import (  
    "context"  
    "flag"    "fmt"    "log"    "time"  
    delegatedidentityv1 "github.com/spiffe/spire-api-sdk/proto/spire/api/agent/delegatedidentity/v1"  
    "github.com/spiffe/spire-api-sdk/proto/spire/api/types"    "github.com/spiffe/spire/pkg/common/idutil"    "google.golang.org/grpc"    "google.golang.org/grpc/credentials/insecure")  
  
var (  
    socketPathFlag = flag.String("adminSocketPath", "unix:///opt/admin.sock", "admin agent socket path")  
)  
  
func main() {  
    flag.Parse()  
  
    if err := run(); err != nil {  
       log.Fatalf("Test failed: %v", err)  
    }  
}  
  
func run() error {  
    ctx, cancel := context.WithTimeout(context.Background(), 1*time.Minute)  
    defer cancel()  
  
    conn, err := grpc.Dial(*socketPathFlag, grpc.WithTransportCredentials(insecure.NewCredentials()))  
    if err != nil {  
       return fmt.Errorf("failed to connect server: %w", err)  
    }  
    defer conn.Close()  
  
    client := delegatedidentityv1.NewDelegatedIdentityClient(conn)  
  
    streamSubscribeToX509SVIDs, err := client.SubscribeToX509SVIDs(ctx, &delegatedidentityv1.SubscribeToX509SVIDsRequest{  
       Selectors: []*types.Selector{  
          {  
             Type:  "unix",  
             Value: "uid:1002",  
          },  
       },  
    })  
    if err != nil {  
       return err  
    }  
  
    subscribeToX509SVIDsResp, err := streamSubscribeToX509SVIDs.Recv()  
    if err != nil {  
       return err  
    }  
  
    for _, v := range subscribeToX509SVIDsResp.X509Svids {  
       fmt.Printf("SPIFFE ID: %s\n", idutil.RequireIDFromProto(v.X509Svid.Id).String())  
    }  
  
    return nil  
}

Links

PRs

Issues

4
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
0