Skip to content

Commit

Permalink
Merge pull request #521 from flatcar/kai/pcr-bind
Browse files Browse the repository at this point in the history
kola: Add tests for encrypted root disks with TPM PCR binding
  • Loading branch information
pothos authored Apr 11, 2024
2 parents 745d0b9 + 3c4d632 commit a262f21
Showing 1 changed file with 177 additions and 7 deletions.
184 changes: 177 additions & 7 deletions kola/tests/misc/tpm.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"fmt"
"os"
"strings"
"time"

"github.com/coreos/go-semver/semver"
"github.com/coreos/pkg/capnslog"
Expand All @@ -17,16 +18,22 @@ import (
"github.com/flatcar/mantle/util"
)

const (
VariantDefault string = ""
VariantNoUpdate string = "noupdate"
VariantWithUpdate string = "withupdate"
)

var (
// For now Ignition has no systemd-cryptenroll support and a helper service is used
IgnitionConfigRootCryptenroll = conf.Butane(`---
variant: flatcar
version: 1.0.0
storage:
luks:
- name: rootencrypted
wipe_volume: true
device: "/dev/disk/by-partlabel/ROOT"
- name: rootencrypted
wipe_volume: true
device: "/dev/disk/by-partlabel/ROOT"
filesystems:
- device: /dev/mapper/rootencrypted
format: ext4
Expand All @@ -47,6 +54,127 @@ systemd:
ExecStart=rm /etc/luks/rootencrypted
[Install]
WantedBy=multi-user.target
`)
// Note: Keep the two below configs in sync with those
// documented in the Flatcar TPM docs.
// Ideally the reboot wouldn't be needed (or done in the initrd?)
IgnitionConfigRootCryptenrollPcrNoUpdate = conf.Butane(`---
variant: flatcar
version: 1.0.0
storage:
files:
- path: /etc/flatcar/update.conf
overwrite: true
contents:
inline: |
SERVER=disabled
luks:
- name: rootencrypted
wipe_volume: true
device: "/dev/disk/by-partlabel/ROOT"
filesystems:
- device: /dev/mapper/rootencrypted
format: ext4
label: ROOT
systemd:
units:
- name: cryptenroll-helper-first.service
enabled: true
contents: |
[Unit]
ConditionFirstBoot=true
OnFailure=emergency.target
OnFailureJobMode=isolate
After=first-boot-complete.target multi-user.target
[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=systemd-cryptenroll --tpm2-device=auto --unlock-key-file=/etc/luks/rootencrypted --tpm2-pcrs= /dev/disk/by-partlabel/ROOT
ExecStart=mv /etc/luks/rootencrypted /etc/luks/rootencrypted-bind
ExecStart=sleep 10
ExecStart=systemctl reboot
[Install]
WantedBy=multi-user.target
- name: cryptenroll-helper-bind.service
enabled: true
contents: |
[Unit]
ConditionFirstBoot=false
ConditionPathExists=/etc/luks/rootencrypted-bind
OnFailure=emergency.target
OnFailureJobMode=isolate
[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=systemd-cryptenroll --tpm2-device=auto --unlock-key-file=/etc/luks/rootencrypted-bind --tpm2-pcrs=4+7+8+9+11+12+13 --wipe-slot=tpm2 /dev/disk/by-partlabel/ROOT
ExecStart=mv /etc/luks/rootencrypted-bind /etc/luks/rootencrypted
[Install]
WantedBy=multi-user.target
`)
// The rebinding for the update is due to how GRUB measures things and
// we can only make this work without rebinding if we switch to sd-boot
IgnitionConfigRootCryptenrollPcrWithUpdate = conf.Butane(`---
variant: flatcar
version: 1.0.0
storage:
files:
- path: /oem/bin/oem-postinst
overwrite: true
mode: 0755
contents:
inline: |
#!/bin/bash
set -euo pipefail
# When the update fails to correctly apply, this runs again
if [ -e /etc/luks/rootencrypted-bound ]; then
mv /etc/luks/rootencrypted-bound /etc/luks/rootencrypted-bind
fi
# But since a reboot inbetween could have bound it again,
# remove the PCR binding for every run
systemd-cryptenroll --tpm2-device=auto --unlock-key-file=/etc/luks/rootencrypted-bind --wipe-slot=tpm2 --tpm2-pcrs= /dev/disk/by-partlabel/ROOT
luks:
- name: rootencrypted
wipe_volume: true
device: "/dev/disk/by-partlabel/ROOT"
filesystems:
- device: /dev/mapper/rootencrypted
format: ext4
label: ROOT
systemd:
units:
- name: cryptenroll-helper-first.service
enabled: true
contents: |
[Unit]
ConditionFirstBoot=true
OnFailure=emergency.target
OnFailureJobMode=isolate
After=first-boot-complete.target multi-user.target
[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=systemd-cryptenroll --tpm2-device=auto --unlock-key-file=/etc/luks/rootencrypted --tpm2-pcrs= /dev/disk/by-partlabel/ROOT
ExecStart=mv /etc/luks/rootencrypted /etc/luks/rootencrypted-bind
ExecStart=sleep 10
ExecStart=systemctl reboot
[Install]
WantedBy=multi-user.target
- name: cryptenroll-helper-bind.service
enabled: true
contents: |
[Unit]
ConditionFirstBoot=false
ConditionPathExists=/etc/luks/rootencrypted-bind
OnFailure=emergency.target
OnFailureJobMode=isolate
Before=update-engine.service
[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=systemd-cryptenroll --tpm2-device=auto --unlock-key-file=/etc/luks/rootencrypted-bind --tpm2-pcrs=4+7+8+9+11+12+13 --wipe-slot=tpm2 /dev/disk/by-partlabel/ROOT
ExecStart=mv /etc/luks/rootencrypted-bind /etc/luks/rootencrypted-bound
[Install]
WantedBy=multi-user.target
`)
IgnitionConfigRootTPM = `{
"ignition": {
Expand Down Expand Up @@ -124,7 +252,7 @@ systemd:

func init() {
runRootTPMCryptenroll := func(c cluster.TestCluster) {
tpmTest(c, IgnitionConfigRootCryptenroll, "/")
tpmTest(c, IgnitionConfigRootCryptenroll, "/", VariantDefault)
}
register.Register(&register.Test{
Run: runRootTPMCryptenroll,
Expand All @@ -134,9 +262,31 @@ func init() {
Distros: []string{"cl"},
MinVersion: semver.Version{Major: 3913, Minor: 0, Patch: 1},
})
runRootTPMCryptenrollPcrNoUpdate := func(c cluster.TestCluster) {
tpmTest(c, IgnitionConfigRootCryptenrollPcrNoUpdate, "/", VariantNoUpdate)
}
register.Register(&register.Test{
Run: runRootTPMCryptenrollPcrNoUpdate,
ClusterSize: 0,
Platforms: []string{"qemu"},
Name: "cl.tpm.root-cryptenroll-pcr-noupdate",
Distros: []string{"cl"},
MinVersion: semver.Version{Major: 3913, Minor: 0, Patch: 1},
})
runRootTPMCryptenrollPcrWithUpdate := func(c cluster.TestCluster) {
tpmTest(c, IgnitionConfigRootCryptenrollPcrWithUpdate, "/", VariantWithUpdate)
}
register.Register(&register.Test{
Run: runRootTPMCryptenrollPcrWithUpdate,
ClusterSize: 0,
Platforms: []string{"qemu"},
Name: "cl.tpm.root-cryptenroll-pcr-withupdate",
Distros: []string{"cl"},
MinVersion: semver.Version{Major: 3913, Minor: 0, Patch: 1},
})

runRootTPM := func(c cluster.TestCluster) {
tpmTest(c, conf.Ignition(IgnitionConfigRootTPM), "/")
tpmTest(c, conf.Ignition(IgnitionConfigRootTPM), "/", VariantDefault)
}
register.Register(&register.Test{
Run: runRootTPM,
Expand All @@ -148,7 +298,7 @@ func init() {
})

runNonRootTPM := func(c cluster.TestCluster) {
tpmTest(c, conf.Ignition(IgnitionConfigNonRootTPM), "/mnt/data")
tpmTest(c, conf.Ignition(IgnitionConfigNonRootTPM), "/mnt/data", VariantDefault)
}
register.Register(&register.Test{
Run: runNonRootTPM,
Expand All @@ -160,7 +310,7 @@ func init() {
})
}

func tpmTest(c cluster.TestCluster, userData *conf.UserData, mountpoint string) {
func tpmTest(c cluster.TestCluster, userData *conf.UserData, mountpoint string, variant string) {
swtpm, err := startSwtpm()
if err != nil {
c.Fatalf("could not start software TPM emulation: %v", err)
Expand Down Expand Up @@ -188,6 +338,13 @@ func tpmTest(c cluster.TestCluster, userData *conf.UserData, mountpoint string)
c.Fatal(err)
}

if variant == VariantNoUpdate || variant == VariantWithUpdate {
// Wait for the first reboot
time.Sleep(1 * time.Minute)
// Verify that the machine rebooted
_ = c.MustSSH(m, "grep -v flatcar.first_boot /proc/cmdline")
}

checkIfMountpointIsEncrypted(c, m, mountpoint)

// Make sure the change is reboot-safe. This is especially important for the case of an encrypted root disk because the
Expand All @@ -199,6 +356,19 @@ func tpmTest(c cluster.TestCluster, userData *conf.UserData, mountpoint string)
}

checkIfMountpointIsEncrypted(c, m, mountpoint)

if variant == VariantWithUpdate {
// Simulate update effect by
// affecting the GRUB PCR values for the next boot
_ = c.MustSSH(m, "echo 'set linux_append=\"flatcar.autologin console=ttyS0,115200 quiet\"' | sudo tee -a /oem/grub.cfg")
// and calling the OEM hook we set up
_ = c.MustSSH(m, "sudo /oem/bin/oem-postinst")
err := m.Reboot()
if err != nil {
c.Fatalf("could not reboot machine: %v", err)
}
checkIfMountpointIsEncrypted(c, m, "/")
}
}

type softwareTPM struct {
Expand Down

0 comments on commit a262f21

Please sign in to comment.