配置第一个工作负载
本教程将指导您如何在应用程序中集成 SPIRE 获取身份凭证。
概述
工作负载通过 Workload API 从 SPIRE Agent 获取 SVID。有多种集成方式:
go-spiffe 库: Go 应用程序
java-spiffe 库: Java 应用程序
spiffe-helper: 任意应用程序
Envoy SDS: 服务网格
Go 应用程序
安装依赖
go get github.com/spiffe/go-spiffe/v2
获取 X.509-SVID
package main
import (
"context"
"log"
"time"
"github.com/spiffe/go-spiffe/v2/workloadapi"
)
func main() {
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
// 创建 X.509 源
source, err := workloadapi.NewX509Source(ctx)
if err != nil {
log.Fatalf("无法创建 X509Source: %v", err)
}
defer source.Close()
// 获取 SVID
svid, err := source.GetX509SVID()
if err != nil {
log.Fatalf("无法获取 SVID: %v", err)
}
log.Printf("SPIFFE ID: %s", svid.ID)
log.Printf("证书过期时间: %v", svid.Certificates[0].NotAfter)
}
mTLS 服务端
package main
import (
"context"
"log"
"net/http"
"github.com/spiffe/go-spiffe/v2/spiffetls/tlsconfig"
"github.com/spiffe/go-spiffe/v2/workloadapi"
)
func main() {
ctx := context.Background()
source, err := workloadapi.NewX509Source(ctx)
if err != nil {
log.Fatal(err)
}
defer source.Close()
// 创建 TLS 配置
tlsConfig := tlsconfig.MTLSServerConfig(source, source, tlsconfig.AuthorizeAny())
server := &http.Server{
Addr: ":8443",
TLSConfig: tlsConfig,
}
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("Hello, SPIFFE!"))
})
log.Println("服务器启动在 :8443")
log.Fatal(server.ListenAndServeTLS("", ""))
}
mTLS 客户端
package main
import (
"context"
"io"
"log"
"net/http"
"github.com/spiffe/go-spiffe/v2/spiffeid"
"github.com/spiffe/go-spiffe/v2/spiffetls/tlsconfig"
"github.com/spiffe/go-spiffe/v2/workloadapi"
)
func main() {
ctx := context.Background()
source, err := workloadapi.NewX509Source(ctx)
if err != nil {
log.Fatal(err)
}
defer source.Close()
// 只允许特定 SPIFFE ID
serverID := spiffeid.RequireFromString("spiffe://example.org/server")
tlsConfig := tlsconfig.MTLSClientConfig(source, source, tlsconfig.AuthorizeID(serverID))
client := &http.Client{
Transport: &http.Transport{
TLSClientConfig: tlsConfig,
},
}
resp, err := client.Get("https://localhost:8443/")
if err != nil {
log.Fatal(err)
}
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
log.Printf("响应: %s", body)
}
Java 应用程序
Maven 依赖
<dependency>
<groupId>io.spiffe</groupId>
<artifactId>java-spiffe</artifactId>
<version>0.8.0</version>
</dependency>
获取 SVID
import io.spiffe.workloadapi.WorkloadApiClient;
import io.spiffe.workloadapi.X509Context;
public class Main {
public static void main(String[] args) throws Exception {
try (WorkloadApiClient client = WorkloadApiClient.newClient()) {
X509Context context = client.fetchX509Context();
System.out.println("SPIFFE ID: " + context.getDefaultSvid().getSpiffeId());
System.out.println("证书: " + context.getDefaultSvid().getChain());
}
}
}
spiffe-helper
对于非 Go/Java 应用程序,可以使用 spiffe-helper 将 SVID 写入文件。
安装
go install github.com/spiffe/spiffe-helper/cmd/spiffe-helper@latest
配置
创建 helper.conf:
agent_address = "/tmp/spire-agent/public/api.sock"
cmd = "/path/to/your/app"
cmd_args = "--cert=/certs/svid.pem"
cert_dir = "/certs"
renew_signal = "SIGHUP"
svid_file_name = "svid.pem"
svid_key_file_name = "svid-key.pem"
svid_bundle_file_name = "bundle.pem"
运行
spiffe-helper -config helper.conf
Envoy SDS
SPIRE Agent 提供 Envoy SDS API,可直接集成 Envoy。
Envoy 配置
node:
id: "envoy-proxy"
cluster: "cluster1"
static_resources:
listeners:
- name: listener_0
address:
socket_address:
address: 0.0.0.0
port_value: 8443
filter_chains:
- filters:
- name: envoy.filters.network.http_connection_manager
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
stat_prefix: ingress_http
route_config:
name: local_route
virtual_hosts:
- name: backend
domains: ["*"]
routes:
- match: { prefix: "/" }
route: { cluster: backend }
http_filters:
- name: envoy.filters.http.router
transport_socket:
name: envoy.transport_sockets.tls
typed_config:
"@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.DownstreamTlsContext
common_tls_context:
tls_certificate_sds_secret_configs:
- name: "spiffe://example.org/envoy"
sds_config:
resource_api_version: V3
api_config_source:
api_type: GRPC
transport_api_version: V3
grpc_services:
- envoy_grpc:
cluster_name: spire_agent
combined_validation_context:
default_validation_context:
match_subject_alt_names:
- prefix: "spiffe://example.org/"
validation_context_sds_secret_config:
name: "spiffe://example.org"
sds_config:
resource_api_version: V3
api_config_source:
api_type: GRPC
transport_api_version: V3
grpc_services:
- envoy_grpc:
cluster_name: spire_agent
clusters:
- name: spire_agent
connect_timeout: 1s
type: STATIC
lb_policy: ROUND_ROBIN
typed_extension_protocol_options:
envoy.extensions.upstreams.http.v3.HttpProtocolOptions:
"@type": type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions
explicit_http_config:
http2_protocol_options: {}
load_assignment:
cluster_name: spire_agent
endpoints:
- lb_endpoints:
- endpoint:
address:
pipe:
path: /tmp/spire-agent/public/api.sock
注册工作负载
无论使用哪种集成方式,都需要在 SPIRE Server 上创建注册条目:
# Unix 进程
spire-server entry create \
-spiffeID spiffe://example.org/myapp \
-parentID spiffe://example.org/agent \
-selector unix:user:appuser
# Kubernetes Pod
spire-server entry create \
-spiffeID spiffe://example.org/myapp \
-parentID spiffe://example.org/agent \
-selector k8s:ns:default \
-selector k8s:sa:myapp-sa
# Docker 容器
spire-server entry create \
-spiffeID spiffe://example.org/myapp \
-parentID spiffe://example.org/agent \
-selector docker:label:app:myapp
最佳实践
建议
使用库而非文件: 优先使用 go-spiffe/java-spiffe 库
实现优雅轮换: 监听 SVID 更新事件
验证对端身份: 不仅仅接受任何 SVID
处理错误: Agent 不可用时有降级策略