Skip to content

Commit a50c766

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 74317ea commit a50c766

File tree

6 files changed

+655
-0
lines changed

6 files changed

+655
-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 {
@@ -187,6 +189,9 @@ type Config struct {
187189
// NoNewKeyring will not allocated a new session keyring for the container. It will use the
188190
// callers keyring in this case.
189191
NoNewKeyring bool `json:"no_new_keyring"`
192+
193+
// VTPM configuration
194+
VTPMs []*vtpm.VTPM `json:"vtpms"`
190195
}
191196

192197
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

@@ -202,6 +203,9 @@ func CreateLibcontainerConfig(opts *CreateOpts) (*configs.Config, error) {
202203
if err := createDevices(spec, config); err != nil {
203204
return nil, err
204205
}
206+
if err := createVTPMs(spec, config); err != nil {
207+
return nil, err
208+
}
205209
if err := setupUserNamespace(spec, config); err != nil {
206210
return nil, err
207211
}
@@ -473,6 +477,37 @@ func stringToDeviceRune(s string) (rune, error) {
473477
}
474478
}
475479

480+
func createVTPMs(spec *specs.Spec, config *configs.Config) error {
481+
r := spec.Linux.Resources
482+
if r == nil {
483+
return nil
484+
}
485+
486+
rootUID, err := config.HostUID()
487+
if err != nil {
488+
return err
489+
}
490+
rootGID, err := config.HostGID()
491+
if err != nil {
492+
return err
493+
}
494+
495+
devnum := 0
496+
for _, vtpm := range r.VTPMs {
497+
err = vtpmhelper.CreateVTPM(spec, config, &vtpm, devnum, rootUID, rootGID)
498+
if err != nil {
499+
DestroyVTPMs(config)
500+
return err
501+
}
502+
devnum++
503+
}
504+
return nil
505+
}
506+
507+
func DestroyVTPMs(config *configs.Config) {
508+
vtpmhelper.DestroyVTPMs(config)
509+
}
510+
476511
func createDevices(spec *specs.Spec, config *configs.Config) error {
477512
// add whitelisted devices
478513
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: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
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/Sirupsen/logrus"
13+
"github.com/opencontainers/runtime-spec/specs-go"
14+
)
15+
16+
// Create a VTPM
17+
func CreateVTPM(spec *specs.Spec, config *configs.Config, vtpmdev *specs.VTPM, devnum int, uid int, gid int) error {
18+
debug := false
19+
20+
vtpm, err := vtpm.NewVTPM(vtpmdev.Statepath, vtpmdev.TPMVersion, vtpmdev.CreateCertificates, debug)
21+
if err != nil {
22+
return err
23+
}
24+
25+
// Start the vTPM process; once stopped, the device pair will
26+
// also disappear
27+
err = vtpm.Start()
28+
if err != nil {
29+
return err
30+
}
31+
32+
hostdev := vtpm.GetTPMDevname()
33+
major, minor := vtpm.GetMajorMinor()
34+
35+
if debug {
36+
logrus.Infof("Device %s has major %d and minor %d; err: %v", hostdev, major, minor, err)
37+
}
38+
39+
device := &configs.Device{
40+
Type: 'c',
41+
Path: fmt.Sprintf("/dev/tpm%d", devnum),
42+
Major: int64(major),
43+
Minor: int64(minor),
44+
Permissions: "rwm",
45+
FileMode: 0600,
46+
Allow: true,
47+
Uid: 0,
48+
Gid: 0,
49+
}
50+
51+
config.Devices = append(config.Devices, device)
52+
53+
dtype := string("c")
54+
major_p := new(int64)
55+
*major_p = int64(major)
56+
minor_p := new(int64)
57+
*minor_p = int64(minor)
58+
access := string("rwm")
59+
60+
ld := &specs.DeviceCgroup{
61+
Allow: true,
62+
Type: &dtype,
63+
Major: major_p,
64+
Minor: minor_p,
65+
Access: &access,
66+
}
67+
spec.Linux.Resources.Devices = append(spec.Linux.Resources.Devices, *ld)
68+
69+
config.VTPMs = append(config.VTPMs, vtpm)
70+
71+
if uid != 0 {
72+
// adapt ownership of the device since only root can access it
73+
if err := os.Chown(hostdev, uid, gid); err != nil {
74+
vtpm.Stop()
75+
return err
76+
}
77+
if debug {
78+
logrus.Infof("Changed ownership of %s for access by non-root", hostdev)
79+
}
80+
}
81+
82+
return nil
83+
}
84+
85+
func DestroyVTPMs(config *configs.Config) {
86+
for _, vtpm := range config.VTPMs {
87+
vtpm.Stop()
88+
}
89+
config.VTPMs = make([]*vtpm.VTPM, 0)
90+
}

0 commit comments

Comments
 (0)