SPIRE's chart renders plugin_data as JSON via reformat-and-yaml2json, so hclsimple.Decode with "plugin.json" filename triggers HCL v2 JSON mode. Falls back to native HCL for direct testing. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
83 lines
2.3 KiB
Go
83 lines
2.3 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"sync"
|
|
|
|
"github.com/hashicorp/hcl/v2/hclsimple"
|
|
configv1 "github.com/spiffe/spire-plugin-sdk/proto/spire/service/common/config/v1"
|
|
workloadattestorv1 "github.com/spiffe/spire-plugin-sdk/proto/spire/plugin/agent/workloadattestor/v1"
|
|
"github.com/spiffe/spire-plugin-sdk/pluginmain"
|
|
"google.golang.org/grpc/codes"
|
|
"google.golang.org/grpc/status"
|
|
)
|
|
|
|
type Plugin struct {
|
|
workloadattestorv1.UnimplementedWorkloadAttestorServer
|
|
configv1.UnimplementedConfigServer
|
|
|
|
mu sync.RWMutex
|
|
attestor *GsapAttestor
|
|
}
|
|
|
|
func (p *Plugin) Attest(ctx context.Context, req *workloadattestorv1.AttestRequest) (*workloadattestorv1.AttestResponse, error) {
|
|
p.mu.RLock()
|
|
a := p.attestor
|
|
p.mu.RUnlock()
|
|
|
|
if a == nil {
|
|
return nil, status.Error(codes.FailedPrecondition, "plugin not configured")
|
|
}
|
|
|
|
selectors, err := a.Attest(ctx, req.Pid)
|
|
if err != nil {
|
|
return nil, status.Errorf(codes.Internal, "attestation failed: %v", err)
|
|
}
|
|
|
|
return &workloadattestorv1.AttestResponse{
|
|
SelectorValues: selectors,
|
|
}, nil
|
|
}
|
|
|
|
func decodeConfig(data string) (GsapAttestorConfig, error) {
|
|
var cfg GsapAttestorConfig
|
|
if err := hclsimple.Decode("plugin.json", []byte(data), nil, &cfg); err != nil {
|
|
if err2 := hclsimple.Decode("plugin.hcl", []byte(data), nil, &cfg); err2 != nil {
|
|
return cfg, err2
|
|
}
|
|
}
|
|
return cfg, nil
|
|
}
|
|
|
|
func (p *Plugin) Configure(_ context.Context, req *configv1.ConfigureRequest) (*configv1.ConfigureResponse, error) {
|
|
cfg, err := decodeConfig(req.HclConfiguration)
|
|
if err != nil {
|
|
return nil, status.Errorf(codes.InvalidArgument, "failed to decode configuration: %v", err)
|
|
}
|
|
|
|
a := &GsapAttestor{}
|
|
if err := a.Configure(cfg); err != nil {
|
|
return nil, status.Errorf(codes.InvalidArgument, "invalid configuration: %v", err)
|
|
}
|
|
|
|
p.mu.Lock()
|
|
p.attestor = a
|
|
p.mu.Unlock()
|
|
|
|
return &configv1.ConfigureResponse{}, nil
|
|
}
|
|
|
|
func (p *Plugin) Validate(_ context.Context, req *configv1.ValidateRequest) (*configv1.ValidateResponse, error) {
|
|
if _, err := decodeConfig(req.HclConfiguration); err != nil {
|
|
return nil, status.Errorf(codes.InvalidArgument, "failed to decode configuration: %v", err)
|
|
}
|
|
return &configv1.ValidateResponse{}, nil
|
|
}
|
|
|
|
func main() {
|
|
p := &Plugin{}
|
|
pluginmain.Serve(
|
|
workloadattestorv1.WorkloadAttestorPluginServer(p),
|
|
configv1.ConfigServiceServer(p),
|
|
)
|
|
}
|