Skip to content

Commit 510c3c7

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 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 e7abf30 commit 510c3c7

File tree

6 files changed

+613
-0
lines changed

6 files changed

+613
-0
lines changed

Godeps/_workspace/src/github.com/opencontainers/runtime-spec/specs-go/config.go

Lines changed: 9 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

libcontainer/configs/config.go

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

1010
"github.com/Sirupsen/logrus"
11+
12+
"github.com/opencontainers/runc/libcontainer/vtpm"
1113
)
1214

1315
type Rlimit struct {
@@ -182,6 +184,9 @@ type Config struct {
182184
// NoNewKeyring will not allocated a new session keyring for the container. It will use the
183185
// callers keyring in this case.
184186
NoNewKeyring bool `json:"no_new_keyring"`
187+
188+
// VTPM configuration
189+
VTPMs []*vtpm.VTPM `json:"vtpms"`
185190
}
186191

187192
type Hooks struct {

libcontainer/specconv/spec_linux.go

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

@@ -205,6 +206,9 @@ func CreateLibcontainerConfig(opts *CreateOpts) (*configs.Config, error) {
205206
if err := createDevices(spec, config); err != nil {
206207
return nil, err
207208
}
209+
if err := createVTPMs(spec, config); err != nil {
210+
return nil, err
211+
}
208212
if err := setupUserNamespace(spec, config); err != nil {
209213
return nil, err
210214
}
@@ -480,6 +484,37 @@ func stringToDeviceRune(s string) (rune, error) {
480484
}
481485
}
482486

487+
func createVTPMs(spec *specs.Spec, config *configs.Config) error {
488+
r := spec.Linux.Resources
489+
if r == nil {
490+
return nil
491+
}
492+
493+
rootUID, err := config.HostUID()
494+
if err != nil {
495+
return err
496+
}
497+
rootGID, err := config.HostGID()
498+
if err != nil {
499+
return err
500+
}
501+
502+
devnum := 0
503+
for _, vtpm := range r.VTPMs {
504+
err = vtpmhelper.CreateVTPM(spec, config, &vtpm, devnum, rootUID, rootGID)
505+
if err != nil {
506+
DestroyVTPMs(config)
507+
return err
508+
}
509+
devnum++
510+
}
511+
return nil
512+
}
513+
514+
func DestroyVTPMs(config *configs.Config) {
515+
vtpmhelper.DestroyVTPMs(config)
516+
}
517+
483518
func createDevices(spec *specs.Spec, config *configs.Config) error {
484519
// add whitelisted devices
485520
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/Sirupsen/logrus"
1212
"github.com/opencontainers/runc/libcontainer/configs"
1313
"github.com/opencontainers/runc/libcontainer/utils"
14+
"github.com/opencontainers/runc/libcontainer/vtpm/vtpm-helper"
1415
)
1516

1617
func newStateTransitionError(from, to containerState) error {
@@ -38,6 +39,7 @@ type containerState interface {
3839
}
3940

4041
func destroy(c *linuxContainer) error {
42+
vtpmhelper.DestroyVTPMs(c.config)
4143
if !c.config.Namespaces.Contains(configs.NEWPID) {
4244
if err := killCgroupProcesses(c.cgroupManager); err != nil {
4345
logrus.Warn(err)
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
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+
dtype := string("c")
48+
major_p := new(int64)
49+
*major_p = int64(major)
50+
minor_p := new(int64)
51+
*minor_p = int64(minor)
52+
access := string("rwm")
53+
54+
ld := &specs.DeviceCgroup{
55+
Allow: true,
56+
Type: &dtype,
57+
Major: major_p,
58+
Minor: minor_p,
59+
Access: &access,
60+
}
61+
spec.Linux.Resources.Devices = append(spec.Linux.Resources.Devices, *ld)
62+
63+
config.VTPMs = append(config.VTPMs, vtpm)
64+
65+
if uid != 0 {
66+
// adapt ownership of the device since only root can access it
67+
if err := os.Chown(hostdev, uid, gid); err != nil {
68+
vtpm.Stop(createdStatepath)
69+
return err
70+
}
71+
}
72+
73+
return nil
74+
}
75+
76+
func DestroyVTPMs(config *configs.Config) {
77+
for _, vtpm := range config.VTPMs {
78+
vtpm.Stop(true)
79+
}
80+
config.VTPMs = make([]*vtpm.VTPM, 0)
81+
}

0 commit comments

Comments
 (0)