Skip to content

Commit a47cfd5

Browse files
tpm: Retrieve the CA EK certs at build
retrieve the EK certs at build time and create a list of possible devices. Then link these into the spiffe workflow, so that we can validate and verify the TPM for enrollment as an attestor. Signed-off-by: Brian McGillion <bmg.avoin@gmail.com>
1 parent 0c9dde0 commit a47cfd5

File tree

20 files changed

+684
-129
lines changed

20 files changed

+684
-129
lines changed

lib/global-config.nix

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,32 @@ rec {
123123
};
124124
tpmAttestation = {
125125
enable = mkEnableOption "TPM DevID node attestation for system VMs";
126+
endorsementCaCerts = mkOption {
127+
type = types.listOf types.path;
128+
default = [ ];
129+
description = ''
130+
TPM manufacturer endorsement CA certs (PEM files).
131+
Deprecated: use endorsementCaBundle instead.
132+
Kept for backward compatibility.
133+
'';
134+
};
135+
endorsementCaBundle = mkOption {
136+
type = types.path;
137+
default = "";
138+
description = ''
139+
Combined PEM file with all TPM manufacturer endorsement CAs.
140+
Populated from tpm-endorsement.nix wiring module (points to all.pem).
141+
Used by SPIRE server as endorsement_ca_path.
142+
'';
143+
};
144+
endorsementCaVendors = mkOption {
145+
type = types.listOf types.str;
146+
default = [ ];
147+
description = ''
148+
Expected TPM vendor names (advisory, for warning on mismatch).
149+
Populated from hardware definition.
150+
'';
151+
};
126152
};
127153
};
128154

modules/common/security/spiffe/agent.nix

Lines changed: 67 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@ let
2020

2121
useTpmDevid = cfg.attestationMode == "tpm_devid";
2222

23-
agentConf = ''
23+
# Common agent config (shared between tpm_devid and join_token modes)
24+
agentConfCommon = ''
2425
agent {
2526
data_dir = "${cfg.dataDir}"
2627
log_level = "${cfg.logLevel}"
@@ -29,34 +30,40 @@ let
2930
trust_domain = "${cfg.trustDomain}"
3031
trust_bundle_path = "${cfg.trustBundlePath}"
3132
socket_path = "${cfg.socketPath}"
32-
''
33-
+ lib.optionalString (!useTpmDevid) ''
34-
join_token_file = "${cfg.joinTokenFile}"
35-
''
36-
+ ''
33+
'';
34+
35+
agentConfTpmDevid = agentConfCommon + ''
3736
}
3837
3938
plugins {
40-
''
41-
+ (
42-
if useTpmDevid then
43-
''
44-
NodeAttestor "tpm_devid" {
45-
plugin_data {
46-
devid_cert_path = "${cfg.tpmDevid.certPath}"
47-
devid_priv_path = "${cfg.tpmDevid.privPath}"
48-
devid_pub_path = "${cfg.tpmDevid.pubPath}"
49-
}
39+
NodeAttestor "tpm_devid" {
40+
plugin_data {
41+
devid_cert_path = "${cfg.tpmDevid.certPath}"
42+
devid_priv_path = "${cfg.tpmDevid.privPath}"
43+
devid_pub_path = "${cfg.tpmDevid.pubPath}"
5044
}
51-
''
52-
else
53-
''
54-
NodeAttestor "join_token" {
55-
plugin_data {}
45+
}
46+
47+
WorkloadAttestor "unix" {
48+
plugin_data {}
49+
}
50+
51+
KeyManager "disk" {
52+
plugin_data {
53+
directory = "${cfg.dataDir}/keys"
5654
}
57-
''
58-
)
59-
+ ''
55+
}
56+
}
57+
'';
58+
59+
agentConfJoinToken = agentConfCommon + ''
60+
join_token_file = "${cfg.joinTokenFile}"
61+
}
62+
63+
plugins {
64+
NodeAttestor "join_token" {
65+
plugin_data {}
66+
}
6067
6168
WorkloadAttestor "unix" {
6269
plugin_data {}
@@ -69,6 +76,25 @@ let
6976
}
7077
}
7178
'';
79+
80+
# For tpm_devid mode, generate both configs and select at runtime
81+
82+
# Runtime config selector: checks if DevID files exist, falls back to join_token
83+
agentConfSelector = pkgs.writeShellScript "spire-agent-select-config" ''
84+
if [ "${toString useTpmDevid}" = "1" ]; then
85+
if [ -f "${cfg.tpmDevid.certPath}" ] && \
86+
[ -f "${cfg.tpmDevid.privPath}" ] && \
87+
[ -f "${cfg.tpmDevid.pubPath}" ]; then
88+
echo "DevID files found, using tpm_devid attestation"
89+
ln -sf /etc/spire/agent-tpm-devid.conf /run/spire/agent.conf
90+
else
91+
echo "DevID files not found, falling back to join_token attestation"
92+
ln -sf /etc/spire/agent-join-token.conf /run/spire/agent.conf
93+
fi
94+
else
95+
ln -sf /etc/spire/agent-join-token.conf /run/spire/agent.conf
96+
fi
97+
'';
7298
in
7399
{
74100
_file = ./agent.nix;
@@ -185,7 +211,10 @@ in
185211
extraGroups = lib.mkAfter [ cfg.workloadApiGroup ];
186212
}));
187213

188-
environment.etc."spire/agent.conf".text = agentConf;
214+
environment.etc."spire/agent-join-token.conf".text = agentConfJoinToken;
215+
environment.etc."spire/agent-tpm-devid.conf" = lib.mkIf useTpmDevid {
216+
text = agentConfTpmDevid;
217+
};
189218

190219
# Own /run/spire via tmpfiles with group access for spiffe users
191220
systemd.tmpfiles.rules = [
@@ -200,8 +229,17 @@ in
200229
after = [
201230
"network-online.target"
202231
]
203-
++ lib.optionals useTpmDevid [ "spire-devid-provision.service" ];
204-
wants = [ "network-online.target" ];
232+
++ lib.optionals useTpmDevid [
233+
"tpm-vendor-detect.service"
234+
"tpm-ek-verify.service"
235+
"spire-devid-provision.service"
236+
];
237+
wants = [
238+
"network-online.target"
239+
]
240+
++ lib.optionals useTpmDevid [
241+
"spire-devid-provision.service"
242+
];
205243
unitConfig = {
206244
RequiresMountsFor = [ "/etc/common" ];
207245
};
@@ -221,7 +259,8 @@ in
221259
config.security.tpm2.tssGroup or "tss"
222260
];
223261

224-
ExecStart = "${pkgs.spire}/bin/spire-agent run -config /etc/spire/agent.conf";
262+
ExecStartPre = "+${agentConfSelector}";
263+
ExecStart = "${pkgs.spire}/bin/spire-agent run -config /run/spire/agent.conf";
225264

226265
StateDirectory = "spire/agent";
227266
StateDirectoryMode = "0750";

modules/common/security/spiffe/default.nix

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ in
1818
./agent.nix
1919
./devid-ca.nix
2020
./devid-provision.nix
21+
./tpm-vendor-detect.nix
22+
./tpm-ek-verify.nix
2123
];
2224

2325
options.ghaf.security.spiffe = {

modules/common/security/spiffe/devid-ca.nix

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -56,18 +56,6 @@ let
5656
cp "$CA_CERT" "${caPublishPath}"
5757
chmod 0644 "${caPublishPath}"
5858
echo "Published CA cert to ${caPublishPath}"
59-
60-
# Create placeholder endorsement CA for SPIRE tpm_devid plugin.
61-
# The tpm_devid NodeAttestor requires endorsement_ca_path (TPM manufacturer
62-
# EK root cert). For production, replace with real manufacturer CAs
63-
# (e.g. Infineon). This placeholder lets SPIRE server start; actual TPM
64-
# DevID attestation will fail EK verification until real CAs are bundled.
65-
ENDORSEMENT_CA="${caDir}/endorsement-ca.pem"
66-
if [ ! -f "$ENDORSEMENT_CA" ]; then
67-
cp "$CA_CERT" "$ENDORSEMENT_CA"
68-
chmod 0644 "$ENDORSEMENT_CA"
69-
echo "Created placeholder endorsement CA at $ENDORSEMENT_CA"
70-
fi
7159
'';
7260
};
7361

modules/common/security/spiffe/devid-provision.nix

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,8 @@ let
3737
CERT_DIR="${cfg.certDir}"
3838
3939
mkdir -p "$DEVID_DIR"
40-
chmod 0700 "$DEVID_DIR"
40+
chmod 0750 "$DEVID_DIR"
41+
chown root:spire "$DEVID_DIR"
4142
4243
PRIV_BLOB="$DEVID_DIR/devid.priv"
4344
PUB_BLOB="$DEVID_DIR/devid.pub"
@@ -57,8 +58,15 @@ let
5758
else
5859
echo "Generating DevID key for $VM_NAME..."
5960
60-
# Create ephemeral primary key
61-
tpm2_createprimary -C owner -c "$DEVID_DIR/primary.ctx" -Q
61+
# Try to create primary key — hierarchies may be disabled when TPM is shared
62+
if ! tpm2_createprimary -C owner -c "$DEVID_DIR/primary.ctx" -Q 2>/dev/null && \
63+
! tpm2_createprimary -C endorsement -c "$DEVID_DIR/primary.ctx" -Q 2>/dev/null; then
64+
echo "WARNING: Cannot create TPM primary key — all hierarchies disabled"
65+
echo "This is expected when TPM is shared across VMs with LUKS encryption"
66+
echo "Falling back to join_token attestation"
67+
echo "no-hierarchy" > /run/tpm/devid-status 2>/dev/null || true
68+
exit 0
69+
fi
6270
6371
# Create RSA-2048 signing key under the primary
6472
tpm2_create -C "$DEVID_DIR/primary.ctx" -G rsa2048:rsassa:sha256 \
@@ -89,7 +97,8 @@ let
8997
# Clean up transient context files
9098
rm -f "$DEVID_DIR/primary.ctx" "$DEVID_DIR/devid.ctx"
9199
92-
chmod 0600 "$PRIV_BLOB" "$PUB_BLOB"
100+
chown root:spire "$PRIV_BLOB" "$PUB_BLOB"
101+
chmod 0640 "$PRIV_BLOB" "$PUB_BLOB"
93102
echo "DevID key blobs created"
94103
95104
# Submit CSR to admin-vm via virtiofs
@@ -105,6 +114,7 @@ let
105114
for i in $(seq 1 120); do
106115
if [ -f "$CERT_DIR/$VM_NAME.pem" ]; then
107116
cp "$CERT_DIR/$VM_NAME.pem" "$CERT_FILE"
117+
chown root:spire "$CERT_FILE"
108118
chmod 0644 "$CERT_FILE"
109119
echo "DevID certificate received and stored at $CERT_FILE"
110120
exit 0

modules/common/security/spiffe/server.nix

Lines changed: 30 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@
1616
let
1717
cfg = config.ghaf.security.spiffe.server;
1818

19-
tpmAttestationEnabled = cfg.tpmAttestation.enable;
19+
# tpm_devid requires the enable flag and a valid endorsement CA bundle
20+
tpmAttestationEffective = cfg.tpmAttestation.enable && cfg.tpmAttestation.endorsementCaBundle != "";
2021

2122
# Generate server.conf HCL
2223
serverConf = ''
@@ -44,11 +45,11 @@ let
4445
plugin_data {}
4546
}
4647
''
47-
+ lib.optionalString tpmAttestationEnabled ''
48+
+ lib.optionalString tpmAttestationEffective ''
4849
NodeAttestor "tpm_devid" {
4950
plugin_data {
5051
devid_ca_path = "${cfg.tpmAttestation.devidCaPath}"
51-
endorsement_ca_path = "${cfg.tpmAttestation.endorsementCaPath}"
52+
endorsement_ca_path = "${cfg.tpmAttestation.endorsementCaBundle}"
5253
}
5354
}
5455
''
@@ -163,7 +164,7 @@ let
163164
echo "Waiting for all $EXPECTED_AGENTS agents to register..."
164165
AGENT_COUNT=0
165166
for i in $(seq 1 90); do
166-
AGENT_COUNT=$(spire-server agent list -socketPath "$SOCKET" 2>/dev/null | grep "SPIFFE ID" | wc -l)
167+
AGENT_COUNT=$(spire-server agent list -socketPath "$SOCKET" 2>/dev/null | grep -c "SPIFFE ID" || true)
167168
if [ "$AGENT_COUNT" -ge "$EXPECTED_AGENTS" ]; then
168169
echo "All agents registered: $AGENT_COUNT/$EXPECTED_AGENTS"
169170
break
@@ -209,7 +210,7 @@ let
209210
SPIFFE_ID="spiffe://$TRUST_DOMAIN/workload/$WORKLOAD"
210211
211212
# Check if entry exists for this agent+workload combo
212-
EXISTS=$(spire-server entry show -socketPath "$SOCKET" 2>/dev/null | grep -A1 "$SPIFFE_ID" | grep "$PARENT_ID" | wc -l)
213+
EXISTS=$(spire-server entry show -socketPath "$SOCKET" 2>/dev/null | grep -A1 "$SPIFFE_ID" | grep -c "$PARENT_ID" || true)
213214
214215
if [ "$EXISTS" -gt 0 ]; then
215216
echo " [skip] $WORKLOAD"
@@ -339,24 +340,40 @@ in
339340

340341
devidCaPath = lib.mkOption {
341342
type = lib.types.str;
342-
default = "/var/lib/spire/ca/ca.pem";
343-
description = "Path to the DevID CA certificate";
343+
default = "/etc/common/spire/ca/ca.pem";
344+
description = "Path to the DevID CA certificate (virtiofs-published by devid-ca service)";
344345
};
345346

346-
endorsementCaPath = lib.mkOption {
347+
endorsementCaBundle = lib.mkOption {
347348
type = lib.types.str;
348-
default = "/var/lib/spire/ca/endorsement-ca.pem";
349+
default = "";
349350
description = ''
350-
Path to TPM manufacturer endorsement key CA certificate(s).
351-
Used to verify TPM genuineness during DevID attestation.
352-
For production, bundle real manufacturer root CAs (e.g. Infineon).
353-
The DevID CA setup creates a placeholder here on first boot.
351+
Combined PEM file with all TPM manufacturer endorsement CAs.
352+
Used as endorsement_ca_path in SPIRE server tpm_devid plugin.
353+
Populated from globalConfig.
354+
'';
355+
};
356+
357+
endorsementCaCerts = lib.mkOption {
358+
type = lib.types.listOf lib.types.path;
359+
default = [ ];
360+
description = ''
361+
Deprecated: use endorsementCaBundle instead.
362+
Kept for backward compatibility.
354363
'';
355364
};
356365
};
357366
};
358367

359368
config = lib.mkIf cfg.enable {
369+
warnings =
370+
lib.optional (cfg.tpmAttestation.enable && cfg.tpmAttestation.endorsementCaBundle == "")
371+
''
372+
SPIRE: tpm_devid requested but no endorsement CA bundle configured.
373+
Falling back to join_token only attestation.
374+
Ensure tpm-endorsement.nix is imported in your hardware definition.
375+
'';
376+
360377
environment.systemPackages = [ pkgs.spire ];
361378

362379
users.groups.spire = { };

0 commit comments

Comments
 (0)