[lxc-devel] [lxd/master] Add TPM device type

monstermunchkin on Github lxc-bot at linuxcontainers.org
Fri Oct 16 09:29:54 UTC 2020


A non-text attachment was scrubbed...
Name: not available
Type: text/x-mailbox
Size: 301 bytes
Desc: not available
URL: <http://lists.linuxcontainers.org/pipermail/lxc-devel/attachments/20201016/515e163c/attachment-0001.bin>
-------------- next part --------------
From 02adc3b7d536473d65c9225e248cc54434535a88 Mon Sep 17 00:00:00 2001
From: Thomas Hipp <thomas.hipp at canonical.com>
Date: Fri, 16 Oct 2020 11:27:22 +0200
Subject: [PATCH 1/4] lxd/device/config: Add TPMDevice to RunConfig

Signed-off-by: Thomas Hipp <thomas.hipp at canonical.com>
---
 lxd/device/config/device_runconfig.go | 1 +
 1 file changed, 1 insertion(+)

diff --git a/lxd/device/config/device_runconfig.go b/lxd/device/config/device_runconfig.go
index f68d77b27e..e4280392e5 100644
--- a/lxd/device/config/device_runconfig.go
+++ b/lxd/device/config/device_runconfig.go
@@ -43,4 +43,5 @@ type RunConfig struct {
 	PostHooks        []func() error   // Functions to be run after device attach/detach.
 	GPUDevice        []RunConfigItem  // GPU device configuration settings.
 	USBDevice        []RunConfigItem  // USB device configuration settings.
+	TPMDevice        []RunConfigItem  // TPM device configuration settings.
 }

From 7c5bf5ae724f9d99603228c274c10f71f91a4ec8 Mon Sep 17 00:00:00 2001
From: Thomas Hipp <thomas.hipp at canonical.com>
Date: Fri, 16 Oct 2020 11:25:39 +0200
Subject: [PATCH 2/4] lxd/device: Add TPM device type

Signed-off-by: Thomas Hipp <thomas.hipp at canonical.com>
---
 lxd/device/device_load.go |   2 +
 lxd/device/tpm.go         | 221 ++++++++++++++++++++++++++++++++++++++
 2 files changed, 223 insertions(+)
 create mode 100644 lxd/device/tpm.go

diff --git a/lxd/device/device_load.go b/lxd/device/device_load.go
index c89161224c..cf4b521a59 100644
--- a/lxd/device/device_load.go
+++ b/lxd/device/device_load.go
@@ -66,6 +66,8 @@ func load(inst instance.Instance, state *state.State, projectName string, name s
 		dev = &disk{}
 	case "none":
 		dev = &none{}
+	case "tpm":
+		dev = &tpm{}
 	}
 
 	// Check a valid device type has been found.
diff --git a/lxd/device/tpm.go b/lxd/device/tpm.go
new file mode 100644
index 0000000000..4a01d5a089
--- /dev/null
+++ b/lxd/device/tpm.go
@@ -0,0 +1,221 @@
+package device
+
+import (
+	"bufio"
+	"fmt"
+	"os"
+	"os/exec"
+	"path/filepath"
+	"strings"
+	"time"
+
+	"golang.org/x/sys/unix"
+
+	deviceConfig "github.com/lxc/lxd/lxd/device/config"
+	"github.com/lxc/lxd/lxd/instance"
+	"github.com/lxc/lxd/lxd/instance/instancetype"
+	"github.com/lxc/lxd/lxd/revert"
+	"github.com/lxc/lxd/lxd/util"
+	"github.com/lxc/lxd/shared"
+	"github.com/lxc/lxd/shared/validate"
+)
+
+type tpm struct {
+	deviceCommon
+	emulator *exec.Cmd
+}
+
+// isRequired indicates whether the device config requires this device to start OK.
+func (d *tpm) isRequired() bool {
+	// Defaults to not required.
+	if shared.IsTrue(d.config["required"]) {
+		return true
+	}
+
+	return false
+}
+
+// validateConfig checks the supplied config for correctness.
+func (d *tpm) validateConfig(instConf instance.ConfigReader) error {
+	if !instanceSupported(instConf.Type(), instancetype.Container, instancetype.VM) {
+		return ErrUnsupportedDevType
+	}
+
+	rules := map[string]func(string) error{
+		"required": validate.Optional(validate.IsBool),
+	}
+
+	err := d.config.Validate(rules)
+	if err != nil {
+		return err
+	}
+
+	return nil
+}
+
+// validateEnvironment checks if the TPM emulator is available.
+func (d *tpm) validateEnvironment() error {
+	// Validate the required binary.
+	_, err := exec.LookPath("swtpm")
+	if err != nil {
+		return fmt.Errorf("Required tool '%s' is missing", "swtpm")
+	}
+
+	if d.inst.Type() == instancetype.Container {
+		// Load module tpm_vtpm_proxy which creates the /dev/vtpmx device, required
+		// by the TPM emulator.
+		module := "tpm_vtpm_proxy"
+
+		err := util.LoadModule(module)
+		if err != nil {
+			return fmt.Errorf("Failed to load kernel module %q: %s", module, err)
+		}
+	}
+
+	return nil
+}
+
+// Start is run when the device is added to the instance.
+func (d *tpm) Start() (*deviceConfig.RunConfig, error) {
+	err := d.validateEnvironment()
+	if err != nil {
+		return nil, err
+	}
+
+	if d.inst.Type() == instancetype.VM {
+		return d.startVM()
+	}
+
+	return d.startContainer()
+}
+
+func (d *tpm) startContainer() (*deviceConfig.RunConfig, error) {
+	tpmPath := filepath.Join(d.inst.Path(), "tpm")
+	d.emulator = exec.Command("swtpm", "chardev", "--vtpm-proxy", "--tpm2", "--tpmstate", fmt.Sprintf("dir=%s", tpmPath))
+
+	cmdOut, err := d.emulator.StdoutPipe()
+	if err != nil {
+		return nil, err
+	}
+
+	err = d.emulator.Start()
+	if err != nil {
+		return nil, err
+	}
+
+	revert := revert.New()
+	defer revert.Fail()
+
+	// Stop the TPM emulator if anything goes wrong.
+	revert.Add(func() { d.Stop() })
+
+	var devPath string
+	var major, minor int
+
+	// Get the device path
+	scanner := bufio.NewScanner(cmdOut)
+	for scanner.Scan() {
+		// The output will be something like:
+		//   New TPM device: /dev/tpm1 (major/minor = 253/1)
+		// We just need device path and the major/minor numbers.
+		fields := strings.Split(scanner.Text(), " ")
+
+		if len(fields) < 7 {
+			return nil, fmt.Errorf("Failed to TPM device information")
+		}
+
+		devPath = fields[3]
+
+		_, err := fmt.Sscanf(fields[6], "%d/%d)", &major, &minor)
+		if err != nil {
+			return nil, err
+		}
+
+		// Stop reading from stdout as that's the only information we care about.
+		break
+	}
+
+	runConf := deviceConfig.RunConfig{}
+
+	err = unixDeviceSetupCharNum(d.state, d.inst.DevicesPath(), "unix", d.name, d.config, uint32(major), uint32(minor), devPath, false, &runConf)
+	if err != nil {
+		return nil, err
+	}
+
+	if d.isRequired() && len(runConf.Mounts) <= 0 {
+		return nil, fmt.Errorf("Required TPM device not found")
+	}
+
+	revert.Success()
+
+	return &runConf, nil
+}
+
+func (d *tpm) startVM() (*deviceConfig.RunConfig, error) {
+	tpmPath := filepath.Join(d.inst.Path(), "tpm")
+	socketPath := filepath.Join(tpmPath, fmt.Sprintf("swtpm-%s.sock", d.name))
+	runConf := deviceConfig.RunConfig{
+		TPMDevice: []deviceConfig.RunConfigItem{
+			{Key: "devName", Value: d.name},
+			{Key: "path", Value: socketPath},
+		},
+	}
+
+	err := os.MkdirAll(tpmPath, 0755)
+	if err != nil {
+		return nil, err
+	}
+
+	d.emulator = exec.Command("swtpm", "socket", "--ctrl", fmt.Sprintf("type=unixio,path=%s", socketPath), "--tpmstate", fmt.Sprintf("dir=%s", tpmPath), "--tpm2")
+
+	// Start the TPM emulator.
+	err = d.emulator.Start()
+	if err != nil {
+		return nil, err
+	}
+
+	return &runConf, nil
+}
+
+// Stop terminates the TPM emulator.
+func (d *tpm) Stop() (*deviceConfig.RunConfig, error) {
+	runConf := deviceConfig.RunConfig{
+		PostHooks: []func() error{d.postStop},
+	}
+
+	if d.inst.Type() == instancetype.Container {
+		err := unixDeviceRemove(d.inst.DevicesPath(), "unix", d.name, "", &runConf)
+		if err != nil {
+			return nil, err
+		}
+
+		return &runConf, nil
+	}
+
+	doneChan := make(chan interface{})
+
+	go func() {
+		d.emulator.Wait()
+		doneChan <- nil
+	}()
+
+	// Terminate the TPM emulator gracefully.
+	d.emulator.Process.Signal(unix.SIGTERM)
+
+	// Wait for the process to be terminated before cleaning up. Kill it if
+	// it hasn't terminated within 2 seconds.
+	select {
+	case <-doneChan:
+	case <-time.After(time.Second * 2):
+		d.emulator.Process.Kill()
+		close(doneChan)
+	}
+
+	return &runConf, nil
+}
+
+func (d *tpm) postStop() error {
+	tpmPath := filepath.Join(d.inst.Path(), "tpm")
+
+	return os.RemoveAll(tpmPath)
+}

From 3fa4145a57cce46c5f4225b5f182b05c99d2a5ce Mon Sep 17 00:00:00 2001
From: Thomas Hipp <thomas.hipp at canonical.com>
Date: Fri, 16 Oct 2020 11:28:03 +0200
Subject: [PATCH 3/4] lxd/db: Add device type "tpm"

Signed-off-by: Thomas Hipp <thomas.hipp at canonical.com>
---
 lxd/db/devices.go | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/lxd/db/devices.go b/lxd/db/devices.go
index adbf52d0db..8c402b80cf 100644
--- a/lxd/db/devices.go
+++ b/lxd/db/devices.go
@@ -28,6 +28,8 @@ func deviceTypeToString(t int) (string, error) {
 		return "proxy", nil
 	case 9:
 		return "unix-hotplug", nil
+	case 10:
+		return "tpm", nil
 	default:
 		return "", fmt.Errorf("Invalid device type %d", t)
 	}
@@ -55,6 +57,8 @@ func deviceTypeToInt(t string) (int, error) {
 		return 8, nil
 	case "unix-hotplug":
 		return 9, nil
+	case "tpm":
+		return 10, nil
 	default:
 		return -1, fmt.Errorf("Invalid device type %s", t)
 	}

From caf3fba2161fec0bbd49f07726575197f0360b36 Mon Sep 17 00:00:00 2001
From: Thomas Hipp <thomas.hipp at canonical.com>
Date: Fri, 16 Oct 2020 11:28:59 +0200
Subject: [PATCH 4/4] lxd/instance/drivers: Support TPM devices in VMs

Signed-off-by: Thomas Hipp <thomas.hipp at canonical.com>
---
 lxd/instance/drivers/driver_qemu.go           | 30 +++++++++++++++++++
 lxd/instance/drivers/driver_qemu_templates.go | 14 +++++++++
 2 files changed, 44 insertions(+)

diff --git a/lxd/instance/drivers/driver_qemu.go b/lxd/instance/drivers/driver_qemu.go
index 1abaddbbb2..67f665e506 100644
--- a/lxd/instance/drivers/driver_qemu.go
+++ b/lxd/instance/drivers/driver_qemu.go
@@ -1857,6 +1857,15 @@ func (vm *qemu) generateQemuConfigFile(busName string, devConfs []*deviceConfig.
 				return "", err
 			}
 		}
+
+		// Add TPM device.
+		if len(runConf.TPMDevice) > 0 {
+			err = vm.addTPMDeviceConfig(sb, runConf.TPMDevice)
+			if err != nil {
+				return "", err
+			}
+		}
+
 	}
 
 	// Write the agent mount config.
@@ -2298,6 +2307,27 @@ func (vm *qemu) addUSBDeviceConfig(sb *strings.Builder, bus *qemuBus, usbConfig
 	return nil
 }
 
+func (vm *qemu) addTPMDeviceConfig(sb *strings.Builder, tpmConfig []deviceConfig.RunConfigItem) error {
+	var socketPath string
+
+	for _, tpmItem := range tpmConfig {
+		if tpmItem.Key == "path" {
+			socketPath = tpmItem.Value
+		}
+	}
+
+	tplFields := map[string]interface{}{
+		"path": socketPath,
+	}
+
+	err := qemuTMP.Execute(sb, tplFields)
+	if err != nil {
+		return err
+	}
+
+	return nil
+}
+
 // pidFilePath returns the path where the qemu process should write its PID.
 func (vm *qemu) pidFilePath() string {
 	return filepath.Join(vm.LogPath(), "qemu.pid")
diff --git a/lxd/instance/drivers/driver_qemu_templates.go b/lxd/instance/drivers/driver_qemu_templates.go
index 29d4aa6b40..408aae14d8 100644
--- a/lxd/instance/drivers/driver_qemu_templates.go
+++ b/lxd/instance/drivers/driver_qemu_templates.go
@@ -540,3 +540,17 @@ driver = "usb-host"
 bus = "qemu_usb.0"
 hostdevice = "{{.hostDevice}}"
 `))
+
+var qemuTMP = template.Must(template.New("qemuTMP").Parse(`
+[chardev "qemu_tpm-chardev_{{.devName}}"]
+backend = "socket"
+path = "{{.path}}"
+
+[tpmdev "qemu_tpm-tpmdev_{{.devName}}"]
+type = "emulator"
+chardev = "qemu_tpm-chardev"
+
+[device "dev-lxd_{{.devName}}"]
+driver = "tpm-tis"
+tpmdev = "qemu_tpm-tpmdev"
+`))


More information about the lxc-devel mailing list