Skip to content

Commit 97e5410

Browse files
committed
Add vTPM support for Linux
This patch adds vTPM support for Linux to libcontainer. The functionality is based on a recently added vtpm_proxy driver, which is becoming available in Linux 4.8. The driver provides /dev/vtpmx, on which an ioctl is called that spawns a TPM device in the host's /dev directory and returns an anonymous file- descriptor on which a TPM emulator can listen for TPM commands. If we for example created /dev/tpm12 on the host we make this device available as /dev/tpm0 inside the container. We also add its major and minor numbers to the device cgroup. We implement a VTPM class that allows us to create the device and starts a TPM emulator 'swtpm', to which it passes the anonymous file descriptor. Besides that, the user can choose to have the vTPM create certificates in a step that simulates TPM manufacturing. We do this by calling the external swtpm_setup program, which is part of the swtpm project. VTPM support is added inside the JSON configuration as follows: [...] "linux": { "resources": { "devices": [ { "allow": false, "access": "rwm" } ] , "vtpms": [ { "Statepath": "/tmp/tpm-1", "CreateCertificates": true, }, ] }, [...] This JSON markup makes a single TPM available inside the created container. o The Statpath parameter indicates the directory where the TPM emulator 'swtpm' writes the state of the TPM device to. o The CreateCertificates parameter indicates that certificates for the TPM are to be created. The current implementation does not support checkpointing, so checkpointing of a container with an attached vTPM is prevented. The swtpm project is available here : https://github.com/stefanberger/swtpm The libtpms project is available here: https://github.com/stefanberger/libtpms Signed-off-by: Stefan Berger <[email protected]>
1 parent a742c93 commit 97e5410

File tree

7 files changed

+615
-0
lines changed

7 files changed

+615
-0
lines changed

libcontainer/configs/config.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ import (
99

1010
"github.com/Sirupsen/logrus"
1111
"github.com/opencontainers/runtime-spec/specs-go"
12+
13+
"github.com/opencontainers/runc/libcontainer/vtpm"
1214
)
1315

1416
type Rlimit struct {
@@ -186,6 +188,9 @@ type Config struct {
186188

187189
// Rootless specifies whether the container is a rootless container.
188190
Rootless bool `json:"rootless"`
191+
192+
// VTPM configuration
193+
VTPMs []*vtpm.VTPM `json:"vtpms"`
189194
}
190195

191196
type Hooks struct {

libcontainer/container_linux.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -695,6 +695,10 @@ func (c *linuxContainer) Checkpoint(criuOpts *CriuOpts) error {
695695
return fmt.Errorf("cannot checkpoint a rootless container")
696696
}
697697

698+
if len(c.config.VTPMs) > 0 {
699+
return fmt.Errorf("Checkpointing with attached vTPM is not supported")
700+
}
701+
698702
if err := c.checkCriuVersion("1.5.2"); err != nil {
699703
return err
700704
}

libcontainer/specconv/spec_linux.go

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import (
1414
"github.com/opencontainers/runc/libcontainer/configs"
1515
"github.com/opencontainers/runc/libcontainer/seccomp"
1616
libcontainerUtils "github.com/opencontainers/runc/libcontainer/utils"
17+
"github.com/opencontainers/runc/libcontainer/vtpm/vtpm-helper"
1718
"github.com/opencontainers/runtime-spec/specs-go"
1819

1920
"golang.org/x/sys/unix"
@@ -211,6 +212,9 @@ func CreateLibcontainerConfig(opts *CreateOpts) (*configs.Config, error) {
211212
if err := createDevices(spec, config); err != nil {
212213
return nil, err
213214
}
215+
if err := createVTPMs(spec, config); err != nil {
216+
return nil, err
217+
}
214218
if err := setupUserNamespace(spec, config); err != nil {
215219
return nil, err
216220
}
@@ -507,6 +511,37 @@ func stringToDeviceRune(s string) (rune, error) {
507511
}
508512
}
509513

514+
func createVTPMs(spec *specs.Spec, config *configs.Config) error {
515+
r := spec.Linux.Resources
516+
if r == nil {
517+
return nil
518+
}
519+
520+
rootUID, err := config.HostUID(0)
521+
if err != nil {
522+
return err
523+
}
524+
rootGID, err := config.HostGID(0)
525+
if err != nil {
526+
return err
527+
}
528+
529+
devnum := 0
530+
for _, vtpm := range r.VTPMs {
531+
err = vtpmhelper.CreateVTPM(spec, config, &vtpm, devnum, rootUID, rootGID)
532+
if err != nil {
533+
DestroyVTPMs(config)
534+
return err
535+
}
536+
devnum++
537+
}
538+
return nil
539+
}
540+
541+
func DestroyVTPMs(config *configs.Config) {
542+
vtpmhelper.DestroyVTPMs(config)
543+
}
544+
510545
func createDevices(spec *specs.Spec, config *configs.Config) error {
511546
// add whitelisted devices
512547
config.Devices = []*configs.Device{

libcontainer/state_linux.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111
"github.com/opencontainers/runc/libcontainer/configs"
1212
"github.com/opencontainers/runc/libcontainer/utils"
1313

14+
"github.com/opencontainers/runc/libcontainer/vtpm/vtpm-helper"
1415
"golang.org/x/sys/unix"
1516
)
1617

@@ -39,6 +40,7 @@ type containerState interface {
3940
}
4041

4142
func destroy(c *linuxContainer) error {
43+
vtpmhelper.DestroyVTPMs(c.config)
4244
if !c.config.Namespaces.Contains(configs.NEWPID) {
4345
if err := signalAllProcesses(c.cgroupManager, unix.SIGKILL); err != nil {
4446
logrus.Warn(err)
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
// + build linux
2+
3+
package vtpmhelper
4+
5+
import (
6+
"fmt"
7+
"os"
8+
9+
"github.com/opencontainers/runc/libcontainer/configs"
10+
"github.com/opencontainers/runc/libcontainer/vtpm"
11+
12+
"github.com/opencontainers/runtime-spec/specs-go"
13+
)
14+
15+
// Create a VTPM
16+
func CreateVTPM(spec *specs.Spec, config *configs.Config, vtpmdev *specs.VTPM, devnum int, uid int, gid int) error {
17+
18+
vtpm, err := vtpm.NewVTPM(vtpmdev.Statepath, vtpmdev.TPMVersion, vtpmdev.CreateCertificates)
19+
if err != nil {
20+
return err
21+
}
22+
23+
// Start the vTPM process; once stopped, the device pair will
24+
// also disappear
25+
err, createdStatepath := vtpm.Start()
26+
if err != nil {
27+
return err
28+
}
29+
30+
hostdev := vtpm.GetTPMDevname()
31+
major, minor := vtpm.GetMajorMinor()
32+
33+
device := &configs.Device{
34+
Type: 'c',
35+
Path: fmt.Sprintf("/dev/tpm%d", devnum),
36+
Major: int64(major),
37+
Minor: int64(minor),
38+
Permissions: "rwm",
39+
FileMode: 0600,
40+
Allow: true,
41+
Uid: 0,
42+
Gid: 0,
43+
}
44+
45+
config.Devices = append(config.Devices, device)
46+
47+
major_p := new(int64)
48+
*major_p = int64(major)
49+
minor_p := new(int64)
50+
*minor_p = int64(minor)
51+
52+
ld := &specs.LinuxDeviceCgroup{
53+
Allow: true,
54+
Type: "c",
55+
Major: major_p,
56+
Minor: minor_p,
57+
Access: "rwm",
58+
}
59+
spec.Linux.Resources.Devices = append(spec.Linux.Resources.Devices, *ld)
60+
61+
config.VTPMs = append(config.VTPMs, vtpm)
62+
63+
if uid != 0 {
64+
// adapt ownership of the device since only root can access it
65+
if err := os.Chown(hostdev, uid, gid); err != nil {
66+
vtpm.Stop(createdStatepath)
67+
return err
68+
}
69+
}
70+
71+
return nil
72+
}
73+
74+
func DestroyVTPMs(config *configs.Config) {
75+
for _, vtpm := range config.VTPMs {
76+
vtpm.Stop(true)
77+
}
78+
config.VTPMs = make([]*vtpm.VTPM, 0)
79+
}

0 commit comments

Comments
 (0)