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>
116 lines
2.7 KiB
Go
116 lines
2.7 KiB
Go
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
package keylime
|
|
|
|
import (
|
|
"os"
|
|
"path/filepath"
|
|
"testing"
|
|
)
|
|
|
|
func TestAttestedStatus(t *testing.T) {
|
|
s := &AttestationStatus{Verdict: "Attested", PostureLevel: 5}
|
|
if !s.IsAttested() {
|
|
t.Error("expected IsAttested() = true")
|
|
}
|
|
}
|
|
|
|
func TestFailedStatus(t *testing.T) {
|
|
s := &AttestationStatus{Verdict: "Failed", PostureLevel: 2}
|
|
if s.IsAttested() {
|
|
t.Error("expected IsAttested() = false for Failed")
|
|
}
|
|
}
|
|
|
|
func TestUnknownStatus(t *testing.T) {
|
|
s := &AttestationStatus{Verdict: "Unknown"}
|
|
if s.IsAttested() {
|
|
t.Error("expected IsAttested() = false for Unknown")
|
|
}
|
|
}
|
|
|
|
func TestConfigMapSourceNormal(t *testing.T) {
|
|
dir := t.TempDir()
|
|
if err := os.WriteFile(filepath.Join(dir, "level"), []byte("5"), 0644); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
c := NewClient(&Config{
|
|
Source: "configmap",
|
|
PostureConfigMapPath: dir,
|
|
})
|
|
status, err := c.GetStatus("node-1")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if status.Verdict != "Attested" {
|
|
t.Errorf("expected Attested, got %s", status.Verdict)
|
|
}
|
|
if status.PostureLevel != 5 {
|
|
t.Errorf("expected level 5, got %d", status.PostureLevel)
|
|
}
|
|
}
|
|
|
|
func TestConfigMapSourceLockdown(t *testing.T) {
|
|
dir := t.TempDir()
|
|
if err := os.WriteFile(filepath.Join(dir, "level"), []byte("1"), 0644); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
c := NewClient(&Config{
|
|
Source: "configmap",
|
|
PostureConfigMapPath: dir,
|
|
})
|
|
status, err := c.GetStatus("node-1")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if status.Verdict != "Failed" {
|
|
t.Errorf("expected Failed for lockdown, got %s", status.Verdict)
|
|
}
|
|
}
|
|
|
|
func TestConfigMapSourceMissing(t *testing.T) {
|
|
c := NewClient(&Config{
|
|
Source: "configmap",
|
|
PostureConfigMapPath: "/nonexistent/path",
|
|
})
|
|
status, err := c.GetStatus("node-1")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if status.Verdict != "Unknown" {
|
|
t.Errorf("expected Unknown for missing configmap, got %s", status.Verdict)
|
|
}
|
|
}
|
|
|
|
func TestMapOpState(t *testing.T) {
|
|
approved := 7
|
|
if v := mapOpState(&approved); v != "Attested" {
|
|
t.Errorf("state 7 should be Attested, got %s", v)
|
|
}
|
|
|
|
failed := 9
|
|
if v := mapOpState(&failed); v != "Failed" {
|
|
t.Errorf("state 9 should be Failed, got %s", v)
|
|
}
|
|
|
|
pending := 3
|
|
if v := mapOpState(&pending); v != "Pending" {
|
|
t.Errorf("state 3 should be Pending, got %s", v)
|
|
}
|
|
|
|
if v := mapOpState(nil); v != "Pending" {
|
|
t.Errorf("nil state (push model) should be Pending, got %s", v)
|
|
}
|
|
}
|
|
|
|
func TestDefaultConfig(t *testing.T) {
|
|
cfg := DefaultConfig()
|
|
if cfg.Source != "configmap" {
|
|
t.Errorf("default source should be configmap, got %s", cfg.Source)
|
|
}
|
|
if cfg.APIVersion != "2.1" {
|
|
t.Errorf("default api version should be 2.1, got %s", cfg.APIVersion)
|
|
}
|
|
}
|