[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