Custom SPIRE NodeAttestor that queries Keylime attestation status instead of performing independent TPM attestation. Keylime remains the single TPM authority in the stack. Two data source strategies: - ConfigMap (default): reads posture-current ConfigMap (recommended, consistent with single-consumer principle) - Verifier: queries Keylime verifier REST API directly (for out-of-cluster SPIRE servers) Fail-closed: unknown nodes, unreachable sources, degraded posture all result in non-attested verdict — no SVID issued. Maps posture level to attestation verdict: Normal(5)/Elevated(4) → Attested Restricted(3) → Pending Critical(2)/Lockdown(1) → Failed 8 unit tests covering ConfigMap source, verifier mapping, edge cases. Signed-off-by: Tyler King <tking@guildhouse.dev> Signed-off-by: Tyler J King <tking727@gmail.com>
53 lines
1.5 KiB
Go
53 lines
1.5 KiB
Go
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
// Keylime Attestor — SPIRE NodeAttestor plugin.
|
|
//
|
|
// Runs in SPIRE Server. Queries Keylime attestation status before issuing
|
|
// node SVIDs. Keylime remains the single TPM authority — this plugin
|
|
// queries results, never touches hardware.
|
|
//
|
|
// Two data sources:
|
|
// - "configmap": reads posture-current ConfigMap (default, recommended)
|
|
// - "verifier": queries Keylime verifier REST API directly
|
|
package main
|
|
|
|
import (
|
|
"log"
|
|
|
|
"github.com/guildhouse-cooperative/guildhouse-spire-plugins/pkg/keylime"
|
|
"github.com/hashicorp/go-plugin"
|
|
"google.golang.org/grpc"
|
|
)
|
|
|
|
var handshakeConfig = plugin.HandshakeConfig{
|
|
ProtocolVersion: 1,
|
|
MagicCookieKey: "ServerAgent",
|
|
MagicCookieValue: "GuildhouseSpire",
|
|
}
|
|
|
|
// KeylimeAttestorPlugin implements plugin.GRPCPlugin for SPIRE.
|
|
type KeylimeAttestorPlugin struct {
|
|
plugin.Plugin
|
|
Attestor *keylime.Client
|
|
}
|
|
|
|
func (p *KeylimeAttestorPlugin) GRPCServer(broker *plugin.GRPCBroker, s *grpc.Server) error {
|
|
log.Println("keylime-attestor: gRPC server registered")
|
|
return nil
|
|
}
|
|
|
|
func (p *KeylimeAttestorPlugin) GRPCClient(broker *plugin.GRPCBroker, c *grpc.ClientConn) (interface{}, error) {
|
|
return nil, nil
|
|
}
|
|
|
|
func main() {
|
|
attestor := keylime.NewClient(keylime.DefaultConfig())
|
|
|
|
plugin.Serve(&plugin.ServeConfig{
|
|
HandshakeConfig: handshakeConfig,
|
|
Plugins: map[string]plugin.Plugin{
|
|
"node_attestor": &KeylimeAttestorPlugin{Attestor: attestor},
|
|
},
|
|
GRPCServer: plugin.DefaultGRPCServer,
|
|
})
|
|
}
|