# Server API ## 概述 SPIRE Server 提供 gRPC API 用于管理身份和配置。API 分为以下服务: - **Agent API**: Agent 证明和管理 - **Entry API**: 注册条目管理 - **Bundle API**: 信任包管理 - **SVID API**: SVID 颁发 - **TrustDomain API**: 联邦管理 ## 认证和授权 ### 认证方式 1. **Unix Socket**: 本地管理 2. **mTLS**: Agent 和远程管理 3. **Admin Token**: 可选的管理令牌 ### 授权 SPIRE 使用基于策略的授权(OPA Rego): ```rego package spire default allow = false # 允许管理员执行所有操作 allow { input.caller.admin == true } # 允许 Agent 更新自己的条目 allow { input.method == "entry.Entry/BatchUpdateEntry" input.caller.downstream == true } ``` ## Entry API ### 创建条目 ```go import ( "context" entryv1 "github.com/spiffe/spire-api-sdk/proto/spire/api/server/entry/v1" "github.com/spiffe/spire-api-sdk/proto/spire/api/types" ) func createEntry(client entryv1.EntryClient) error { _, err := client.BatchCreateEntry(context.Background(), &entryv1.BatchCreateEntryRequest{ Entries: []*types.Entry{ { SpiffeId: &types.SPIFFEID{ TrustDomain: "example.org", Path: "/myworkload", }, ParentId: &types.SPIFFEID{ TrustDomain: "example.org", Path: "/myagent", }, Selectors: []*types.Selector{ {Type: "unix", Value: "uid:1000"}, }, X509SvidTtl: 3600, }, }, }) return err } ``` ### 列出条目 ```go func listEntries(client entryv1.EntryClient) ([]*types.Entry, error) { resp, err := client.ListEntries(context.Background(), &entryv1.ListEntriesRequest{ Filter: &entryv1.ListEntriesRequest_Filter{ ByParentId: &types.SPIFFEID{ TrustDomain: "example.org", Path: "/myagent", }, }, }) if err != nil { return nil, err } return resp.Entries, nil } ``` ### 更新条目 ```go func updateEntry(client entryv1.EntryClient, entryID string) error { _, err := client.BatchUpdateEntry(context.Background(), &entryv1.BatchUpdateEntryRequest{ Entries: []*types.Entry{ { Id: entryID, Selectors: []*types.Selector{ {Type: "unix", Value: "uid:2000"}, }, }, }, }) return err } ``` ### 删除条目 ```go func deleteEntry(client entryv1.EntryClient, entryID string) error { _, err := client.BatchDeleteEntry(context.Background(), &entryv1.BatchDeleteEntryRequest{ Ids: []string{entryID}, }) return err } ``` ## Agent API ### 列出 Agent ```go import agentv1 "github.com/spiffe/spire-api-sdk/proto/spire/api/server/agent/v1" func listAgents(client agentv1.AgentClient) ([]*types.Agent, error) { resp, err := client.ListAgents(context.Background(), &agentv1.ListAgentsRequest{}) if err != nil { return nil, err } return resp.Agents, nil } ``` ### 创建 Join Token ```go func createJoinToken(client agentv1.AgentClient) (string, error) { resp, err := client.CreateJoinToken(context.Background(), &agentv1.CreateJoinTokenRequest{ Ttl: 600, // 10 分钟 AgentId: &types.SPIFFEID{ TrustDomain: "example.org", Path: "/myagent", }, }) if err != nil { return "", err } return resp.Value, nil } ``` ### 驱逐 Agent ```go func evictAgent(client agentv1.AgentClient, spiffeID *types.SPIFFEID) error { _, err := client.DeleteAgent(context.Background(), &agentv1.DeleteAgentRequest{ Id: spiffeID, }) return err } ``` ## Bundle API ### 获取本地 Bundle ```go import bundlev1 "github.com/spiffe/spire-api-sdk/proto/spire/api/server/bundle/v1" func getBundle(client bundlev1.BundleClient) (*types.Bundle, error) { return client.GetBundle(context.Background(), &bundlev1.GetBundleRequest{}) } ``` ### 设置联邦 Bundle ```go func setFederatedBundle(client bundlev1.BundleClient, bundle *types.Bundle) error { _, err := client.BatchSetFederatedBundle(context.Background(), &bundlev1.BatchSetFederatedBundleRequest{ Bundle: []*types.Bundle{bundle}, }) return err } ``` ## SVID API ### 颁发 X.509-SVID ```go import svidv1 "github.com/spiffe/spire-api-sdk/proto/spire/api/server/svid/v1" func mintX509SVID(client svidv1.SVIDClient) (*types.X509SVID, error) { resp, err := client.MintX509SVID(context.Background(), &svidv1.MintX509SVIDRequest{ Csr: csrBytes, // PKCS#10 CSR Ttl: 3600, }) if err != nil { return nil, err } return resp.Svid, nil } ``` ### 颁发 JWT-SVID ```go func mintJWTSVID(client svidv1.SVIDClient) (string, error) { resp, err := client.MintJWTSVID(context.Background(), &svidv1.MintJWTSVIDRequest{ Id: &types.SPIFFEID{ TrustDomain: "example.org", Path: "/myworkload", }, Audience: []string{"https://api.example.org"}, Ttl: 300, }) if err != nil { return "", err } return resp.Svid.Token, nil } ``` ## 连接示例 ### 通过 Unix Socket ```go import ( "net" "google.golang.org/grpc" "google.golang.org/grpc/credentials/insecure" ) func connectViaSocket(socketPath string) (*grpc.ClientConn, error) { return grpc.Dial( socketPath, grpc.WithTransportCredentials(insecure.NewCredentials()), grpc.WithContextDialer(func(ctx context.Context, addr string) (net.Conn, error) { return net.Dial("unix", addr) }), ) } ``` ### 通过 mTLS ```go import ( "crypto/tls" "google.golang.org/grpc" "google.golang.org/grpc/credentials" ) func connectViaMTLS(address string, tlsConfig *tls.Config) (*grpc.ClientConn, error) { return grpc.Dial( address, grpc.WithTransportCredentials(credentials.NewTLS(tlsConfig)), ) } ``` ## 完整客户端示例 ```go package main import ( "context" "fmt" "log" "net" entryv1 "github.com/spiffe/spire-api-sdk/proto/spire/api/server/entry/v1" "github.com/spiffe/spire-api-sdk/proto/spire/api/types" "google.golang.org/grpc" "google.golang.org/grpc/credentials/insecure" ) func main() { // 连接 Server conn, err := grpc.Dial( "/tmp/spire-server/private/api.sock", grpc.WithTransportCredentials(insecure.NewCredentials()), grpc.WithContextDialer(func(ctx context.Context, addr string) (net.Conn, error) { return net.Dial("unix", addr) }), ) if err != nil { log.Fatal(err) } defer conn.Close() // 创建客户端 client := entryv1.NewEntryClient(conn) // 列出条目 resp, err := client.ListEntries(context.Background(), &entryv1.ListEntriesRequest{}) if err != nil { log.Fatal(err) } for _, entry := range resp.Entries { fmt.Printf("Entry: %s -> %s\n", entry.ParentId, entry.SpiffeId) } } ``` ## 错误处理 API 使用 gRPC 状态码: | 状态码 | 描述 | |--------|------| | `OK` | 成功 | | `INVALID_ARGUMENT` | 无效参数 | | `NOT_FOUND` | 资源不存在 | | `ALREADY_EXISTS` | 资源已存在 | | `PERMISSION_DENIED` | 权限不足 | | `INTERNAL` | 内部错误 | ```go import "google.golang.org/grpc/status" resp, err := client.GetEntry(ctx, req) if err != nil { st, ok := status.FromError(err) if ok { switch st.Code() { case codes.NotFound: log.Println("条目不存在") case codes.PermissionDenied: log.Println("权限不足") default: log.Printf("错误: %v", st.Message()) } } } ``` ## 参考资源 - [spire-api-sdk](https://github.com/spiffe/spire-api-sdk): 官方 Go SDK - [Proto 定义](https://github.com/spiffe/spire-api-sdk/tree/main/proto): API 协议定义