Skip to content

Commit 579b370

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 579b370

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 {
@@ -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: 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)