Delegated Identity APIとは
SPIRE Agentが直接的に認証(attestation)できないワークロードのかわりに、委任されたワークロード(delegated workload)がSVIDとBundleを取得できる機能です。
この機能はSPIRE AgentのAdmin APIのエンドで提供されます。(Admin APIはデフォルト無効。)
なぜ必要となったのか
これは Ciliumコミュニティからの提案から始まったもので、この機能が実装されるまでのSPIREでは、Workload APIを呼び出したプロセス(ワークロード)に対して認証(attestation)しSVIDを発行するように設計されていました。
しかし、あるノードに単一のエージェントプロセスがあり、それが他のプロセスに変わって処理を実行するようなケースなどをカバーしきれていませんでした。
必要な設定
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