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