Skip to content

Commit 23b636d

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", "createCerts": 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 createCerts 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]> Signed-off-by: Stefan Berger <[email protected]> Signed-off-by: Stefan Berger <[email protected]>
1 parent 679dc11 commit 23b636d

File tree

9 files changed

+736
-3
lines changed

9 files changed

+736
-3
lines changed

libcontainer/configs/config.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"os/exec"
88
"time"
99

10+
"github.com/opencontainers/runc/libcontainer/vtpm"
1011
"github.com/opencontainers/runtime-spec/specs-go"
1112

1213
"github.com/sirupsen/logrus"
@@ -200,6 +201,9 @@ type Config struct {
200201
// RootlessCgroups is set when unlikely to have the full access to cgroups.
201202
// When RootlessCgroups is set, cgroups errors are ignored.
202203
RootlessCgroups bool `json:"rootless_cgroups,omitempty"`
204+
205+
// VTPM configuration
206+
VTPMs []*vtpm.VTPM `json:"vtpms"`
203207
}
204208

205209
type Hooks struct {

libcontainer/configs/device.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,10 @@ type Device struct {
1818
// Path to the device.
1919
Path string `json:"path"`
2020

21+
// the name of the device inside the container (optional)
22+
// allows a host device to appear under different name inside container
23+
Devpath string `json:"devpath"`
24+
2125
// Major is the device's major number.
2226
Major int64 `json:"major"`
2327

libcontainer/container_linux.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -932,6 +932,10 @@ func (c *linuxContainer) Checkpoint(criuOpts *CriuOpts) error {
932932
// support for doing unprivileged dumps, but the setup of
933933
// rootless containers might make this complicated.
934934

935+
if len(c.config.VTPMs) > 0 {
936+
return fmt.Errorf("Checkpointing with attached vTPM is not supported")
937+
}
938+
935939
// criu 1.5.2 => 10502
936940
if err := c.checkCriuVersion(10502); err != nil {
937941
return err

libcontainer/rootfs_linux.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -612,7 +612,12 @@ func bindMountDeviceNode(dest string, node *configs.Device) error {
612612

613613
// Creates the device node in the rootfs of the container.
614614
func createDeviceNode(rootfs string, node *configs.Device, bind bool) error {
615-
dest := filepath.Join(rootfs, node.Path)
615+
var dest string
616+
if node.Devpath != "" {
617+
dest = filepath.Join(rootfs, node.Devpath)
618+
} else {
619+
dest = filepath.Join(rootfs, node.Path)
620+
}
616621
if err := os.MkdirAll(filepath.Dir(dest), 0755); err != nil {
617622
return err
618623
}

libcontainer/specconv/spec_linux.go

Lines changed: 4 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"
1718
"github.com/opencontainers/runtime-spec/specs-go"
1819

1920
"golang.org/x/sys/unix"
@@ -152,6 +153,7 @@ type CreateOpts struct {
152153
Spec *specs.Spec
153154
RootlessEUID bool
154155
RootlessCgroups bool
156+
VTPMs []*vtpm.VTPM
155157
}
156158

157159
// CreateLibcontainerConfig creates a new libcontainer configuration from a
@@ -187,6 +189,7 @@ func CreateLibcontainerConfig(opts *CreateOpts) (*configs.Config, error) {
187189
NoNewKeyring: opts.NoNewKeyring,
188190
RootlessEUID: opts.RootlessEUID,
189191
RootlessCgroups: opts.RootlessCgroups,
192+
VTPMs: opts.VTPMs,
190193
}
191194

192195
exists := false
@@ -608,6 +611,7 @@ func createDevices(spec *specs.Spec, config *configs.Config) error {
608611
device := &configs.Device{
609612
Type: dt,
610613
Path: d.Path,
614+
Devpath: d.Devpath,
611615
Major: d.Major,
612616
Minor: d.Minor,
613617
FileMode: filemode,

libcontainer/state_linux.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"path/filepath"
99

1010
"github.com/opencontainers/runc/libcontainer/configs"
11+
"github.com/opencontainers/runc/libcontainer/vtpm/vtpm-helper"
1112

1213
"github.com/sirupsen/logrus"
1314
"golang.org/x/sys/unix"
@@ -38,6 +39,7 @@ type containerState interface {
3839
}
3940

4041
func destroy(c *linuxContainer) error {
42+
vtpmhelper.DestroyVTPMs(c.config.VTPMs)
4143
if !c.config.Namespaces.Contains(configs.NEWPID) {
4244
if err := signalAllProcesses(c.cgroupManager, unix.SIGKILL); err != nil {
4345
logrus.Warn(err)
Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
// + build linux
2+
3+
package vtpmhelper
4+
5+
import (
6+
"fmt"
7+
"os"
8+
"syscall"
9+
10+
"github.com/opencontainers/runc/libcontainer/configs"
11+
"github.com/opencontainers/runc/libcontainer/vtpm"
12+
13+
"github.com/opencontainers/runtime-spec/specs-go"
14+
"golang.org/x/sys/unix"
15+
)
16+
17+
func addVTPMDevice(spec *specs.Spec, hostpath, devpath string, major, minor uint32) {
18+
var filemode os.FileMode = 0600
19+
20+
device := specs.LinuxDevice{
21+
Path: hostpath,
22+
Devpath: devpath,
23+
Type: "c",
24+
Major: int64(major),
25+
Minor: int64(minor),
26+
FileMode: &filemode,
27+
}
28+
spec.Linux.Devices = append(spec.Linux.Devices, device)
29+
30+
major_p := new(int64)
31+
*major_p = int64(major)
32+
minor_p := new(int64)
33+
*minor_p = int64(minor)
34+
35+
ld := &specs.LinuxDeviceCgroup{
36+
Allow: true,
37+
Type: "c",
38+
Major: major_p,
39+
Minor: minor_p,
40+
Access: "rwm",
41+
}
42+
spec.Linux.Resources.Devices = append(spec.Linux.Resources.Devices, *ld)
43+
}
44+
45+
// Create a VTPM
46+
func CreateVTPM(spec *specs.Spec, vtpmdev *specs.VTPM, devnum int) (*vtpm.VTPM, error) {
47+
48+
vtpm, err := vtpm.NewVTPM(vtpmdev.Statepath, vtpmdev.StatepathIsManaged, vtpmdev.TPMVersion, vtpmdev.CreateCertificates, vtpmdev.Runas, vtpmdev.PcrBanks)
49+
if err != nil {
50+
return nil, err
51+
}
52+
53+
// Start the vTPM process; once stopped, the device pair will
54+
// also disappear
55+
err, vtpm.CreatedStatepath = vtpm.Start()
56+
if err != nil {
57+
return nil, err
58+
}
59+
60+
hostdev := vtpm.GetTPMDevname()
61+
major, minor := vtpm.GetMajorMinor()
62+
63+
devpath := fmt.Sprintf("/dev/tpm%d", devnum)
64+
addVTPMDevice(spec, hostdev, devpath, major, minor)
65+
66+
// check if /dev/vtpmrm%d is available
67+
host_tpmrm := fmt.Sprintf("/dev/tpmrm%d", vtpm.GetTPMDevNum())
68+
if fileInfo, err := os.Lstat(host_tpmrm); err == nil {
69+
if stat_t, ok := fileInfo.Sys().(*syscall.Stat_t); ok {
70+
devNumber := stat_t.Rdev
71+
devpath = fmt.Sprintf("/dev/tpmrm%d", devnum)
72+
addVTPMDevice(spec, host_tpmrm, devpath, unix.Major(devNumber), unix.Minor(devNumber))
73+
}
74+
}
75+
76+
return vtpm, nil
77+
}
78+
79+
func setVTPMHostDevOwner(vtpm *vtpm.VTPM, uid, gid int) error {
80+
hostdev := vtpm.GetTPMDevname()
81+
// adapt ownership of the device since only root can access it
82+
if err := os.Chown(hostdev, uid, gid); err != nil {
83+
return err
84+
}
85+
86+
host_tpmrm := fmt.Sprintf("/dev/tpmrm%d", vtpm.GetTPMDevNum())
87+
if _, err := os.Lstat(host_tpmrm); err == nil {
88+
// adapt ownership of the device since only root can access it
89+
if err := os.Chown(host_tpmrm, uid, gid); err != nil {
90+
return err
91+
}
92+
}
93+
94+
return nil
95+
}
96+
97+
// SetVTPMHostDevsOwner sets the owner of the host devices to the
98+
// container root's mapped user id; if root inside the container is
99+
// uid 1000 on the host, the devices will be owned by uid 1000.
100+
func SetVTPMHostDevsOwner(config *configs.Config, uid, gid int) error {
101+
if uid != 0 {
102+
for _, vtpm := range config.VTPMs {
103+
if err := setVTPMHostDevOwner(vtpm, uid, gid); err != nil {
104+
return err
105+
}
106+
}
107+
}
108+
return nil
109+
}
110+
111+
func DestroyVTPMs(vtpms []*vtpm.VTPM) {
112+
for _, vtpm := range vtpms {
113+
vtpm.Stop(vtpm.CreatedStatepath)
114+
}
115+
}

0 commit comments

Comments
 (0)