The following pull request was submitted through Github.
It can be accessed and reviewed at: https://github.com/lxc/lxd/pull/8041

This e-mail was sent by the LXC bot, direct replies will not reach the author
unless they happen to be subscribed to this list.

=== Description (from pull-request) ===

From 02adc3b7d536473d65c9225e248cc54434535a88 Mon Sep 17 00:00:00 2001
From: Thomas Hipp <thomas.h...@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.h...@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.h...@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.h...@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.h...@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.h...@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.h...@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.h...@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"
+`))
_______________________________________________
lxc-devel mailing list
lxc-devel@lists.linuxcontainers.org
http://lists.linuxcontainers.org/listinfo/lxc-devel

Reply via email to