mirror of
https://github.com/tailscale/tailscale.git
synced 2025-05-16 15:29:28 +00:00
hostinfo,tailcfg: report TPM availability on windows/linux (#15831)
Start collecting fleet data on TPM availability via hostinfo. Updates #15830 Signed-off-by: Andrew Lytvynov <awly@tailscale.com>
This commit is contained in:
parent
f05347a5bf
commit
3105ecd958
@ -41,7 +41,7 @@ while [ "$#" -gt 1 ]; do
|
||||
fi
|
||||
shift
|
||||
ldflags="$ldflags -w -s"
|
||||
tags="${tags:+$tags,}ts_omit_aws,ts_omit_bird,ts_omit_tap,ts_omit_kube,ts_omit_completion,ts_omit_ssh,ts_omit_wakeonlan,ts_omit_capture,ts_omit_relayserver,ts_omit_taildrop"
|
||||
tags="${tags:+$tags,}ts_omit_aws,ts_omit_bird,ts_omit_tap,ts_omit_kube,ts_omit_completion,ts_omit_ssh,ts_omit_wakeonlan,ts_omit_capture,ts_omit_relayserver,ts_omit_taildrop,ts_omit_tpm"
|
||||
;;
|
||||
--box)
|
||||
if [ ! -z "${TAGS:-}" ]; then
|
||||
|
@ -135,6 +135,13 @@ tailscale.com/cmd/k8s-operator dependencies: (generated by github.com/tailscale/
|
||||
github.com/google/go-cmp/cmp/internal/flags from github.com/google/go-cmp/cmp+
|
||||
github.com/google/go-cmp/cmp/internal/function from github.com/google/go-cmp/cmp
|
||||
💣 github.com/google/go-cmp/cmp/internal/value from github.com/google/go-cmp/cmp
|
||||
github.com/google/go-tpm/legacy/tpm2 from github.com/google/go-tpm/tpm2/transport+
|
||||
github.com/google/go-tpm/tpm2 from tailscale.com/feature/tpm
|
||||
github.com/google/go-tpm/tpm2/transport from github.com/google/go-tpm/tpm2/transport/linuxtpm+
|
||||
L github.com/google/go-tpm/tpm2/transport/linuxtpm from tailscale.com/feature/tpm
|
||||
W github.com/google/go-tpm/tpm2/transport/windowstpm from tailscale.com/feature/tpm
|
||||
github.com/google/go-tpm/tpmutil from github.com/google/go-tpm/legacy/tpm2+
|
||||
W 💣 github.com/google/go-tpm/tpmutil/tbs from github.com/google/go-tpm/legacy/tpm2+
|
||||
github.com/google/gofuzz from k8s.io/apimachinery/pkg/apis/meta/v1+
|
||||
github.com/google/gofuzz/bytesource from github.com/google/gofuzz
|
||||
L github.com/google/nftables from tailscale.com/util/linuxfw
|
||||
@ -813,6 +820,7 @@ tailscale.com/cmd/k8s-operator dependencies: (generated by github.com/tailscale/
|
||||
tailscale.com/feature/relayserver from tailscale.com/feature/condregister
|
||||
tailscale.com/feature/taildrop from tailscale.com/feature/condregister
|
||||
L tailscale.com/feature/tap from tailscale.com/feature/condregister
|
||||
tailscale.com/feature/tpm from tailscale.com/feature/condregister
|
||||
tailscale.com/feature/wakeonlan from tailscale.com/feature/condregister
|
||||
tailscale.com/health from tailscale.com/control/controlclient+
|
||||
tailscale.com/health/healthmsg from tailscale.com/ipn/ipnlocal
|
||||
|
@ -109,6 +109,13 @@ tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/de
|
||||
L 💣 github.com/godbus/dbus/v5 from tailscale.com/net/dns+
|
||||
github.com/golang/groupcache/lru from tailscale.com/net/dnscache
|
||||
github.com/google/btree from gvisor.dev/gvisor/pkg/tcpip/header+
|
||||
github.com/google/go-tpm/legacy/tpm2 from github.com/google/go-tpm/tpm2/transport+
|
||||
github.com/google/go-tpm/tpm2 from tailscale.com/feature/tpm
|
||||
github.com/google/go-tpm/tpm2/transport from github.com/google/go-tpm/tpm2/transport/linuxtpm+
|
||||
L github.com/google/go-tpm/tpm2/transport/linuxtpm from tailscale.com/feature/tpm
|
||||
W github.com/google/go-tpm/tpm2/transport/windowstpm from tailscale.com/feature/tpm
|
||||
github.com/google/go-tpm/tpmutil from github.com/google/go-tpm/legacy/tpm2+
|
||||
W 💣 github.com/google/go-tpm/tpmutil/tbs from github.com/google/go-tpm/legacy/tpm2+
|
||||
L github.com/google/nftables from tailscale.com/util/linuxfw
|
||||
L 💣 github.com/google/nftables/alignedbuff from github.com/google/nftables/xt
|
||||
L 💣 github.com/google/nftables/binaryutil from github.com/google/nftables+
|
||||
@ -271,6 +278,7 @@ tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/de
|
||||
tailscale.com/feature/relayserver from tailscale.com/feature/condregister
|
||||
tailscale.com/feature/taildrop from tailscale.com/feature/condregister
|
||||
L tailscale.com/feature/tap from tailscale.com/feature/condregister
|
||||
tailscale.com/feature/tpm from tailscale.com/feature/condregister
|
||||
tailscale.com/feature/wakeonlan from tailscale.com/feature/condregister
|
||||
tailscale.com/health from tailscale.com/control/controlclient+
|
||||
tailscale.com/health/healthmsg from tailscale.com/ipn/ipnlocal
|
||||
|
8
feature/condregister/maybe_tpm.go
Normal file
8
feature/condregister/maybe_tpm.go
Normal file
@ -0,0 +1,8 @@
|
||||
// Copyright (c) Tailscale Inc & AUTHORS
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
//go:build !ios && !ts_omit_tpm
|
||||
|
||||
package condregister
|
||||
|
||||
import _ "tailscale.com/feature/tpm"
|
83
feature/tpm/tpm.go
Normal file
83
feature/tpm/tpm.go
Normal file
@ -0,0 +1,83 @@
|
||||
// Copyright (c) Tailscale Inc & AUTHORS
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
// Package tpm implements support for TPM 2.0 devices.
|
||||
package tpm
|
||||
|
||||
import (
|
||||
"slices"
|
||||
"sync"
|
||||
|
||||
"github.com/google/go-tpm/tpm2"
|
||||
"github.com/google/go-tpm/tpm2/transport"
|
||||
"tailscale.com/feature"
|
||||
"tailscale.com/hostinfo"
|
||||
"tailscale.com/tailcfg"
|
||||
)
|
||||
|
||||
var infoOnce = sync.OnceValue(info)
|
||||
|
||||
func init() {
|
||||
feature.Register("tpm")
|
||||
hostinfo.RegisterHostinfoNewHook(func(hi *tailcfg.Hostinfo) {
|
||||
hi.TPM = infoOnce()
|
||||
})
|
||||
}
|
||||
|
||||
//lint:ignore U1000 used in Linux and Windows builds only
|
||||
func infoFromCapabilities(tpm transport.TPM) *tailcfg.TPMInfo {
|
||||
info := new(tailcfg.TPMInfo)
|
||||
toStr := func(s *string) func(*tailcfg.TPMInfo, uint32) {
|
||||
return func(info *tailcfg.TPMInfo, value uint32) {
|
||||
*s += propToString(value)
|
||||
}
|
||||
}
|
||||
for _, cap := range []struct {
|
||||
prop tpm2.TPMPT
|
||||
apply func(info *tailcfg.TPMInfo, value uint32)
|
||||
}{
|
||||
{tpm2.TPMPTManufacturer, toStr(&info.Manufacturer)},
|
||||
{tpm2.TPMPTVendorString1, toStr(&info.Vendor)},
|
||||
{tpm2.TPMPTVendorString2, toStr(&info.Vendor)},
|
||||
{tpm2.TPMPTVendorString3, toStr(&info.Vendor)},
|
||||
{tpm2.TPMPTVendorString4, toStr(&info.Vendor)},
|
||||
{tpm2.TPMPTRevision, func(info *tailcfg.TPMInfo, value uint32) { info.SpecRevision = int(value) }},
|
||||
{tpm2.TPMPTVendorTPMType, func(info *tailcfg.TPMInfo, value uint32) { info.Model = int(value) }},
|
||||
{tpm2.TPMPTFirmwareVersion1, func(info *tailcfg.TPMInfo, value uint32) { info.FirmwareVersion += uint64(value) << 32 }},
|
||||
{tpm2.TPMPTFirmwareVersion2, func(info *tailcfg.TPMInfo, value uint32) { info.FirmwareVersion += uint64(value) }},
|
||||
} {
|
||||
resp, err := tpm2.GetCapability{
|
||||
Capability: tpm2.TPMCapTPMProperties,
|
||||
Property: uint32(cap.prop),
|
||||
PropertyCount: 1,
|
||||
}.Execute(tpm)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
props, err := resp.CapabilityData.Data.TPMProperties()
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
if len(props.TPMProperty) == 0 {
|
||||
continue
|
||||
}
|
||||
cap.apply(info, props.TPMProperty[0].Value)
|
||||
}
|
||||
return info
|
||||
}
|
||||
|
||||
// propToString converts TPM_PT property value, which is a uint32, into a
|
||||
// string of up to 4 ASCII characters. This encoding applies only to some
|
||||
// properties, see
|
||||
// https://trustedcomputinggroup.org/resource/tpm-library-specification/ Part
|
||||
// 2, section 6.13.
|
||||
func propToString(v uint32) string {
|
||||
chars := []byte{
|
||||
byte(v >> 24),
|
||||
byte(v >> 16),
|
||||
byte(v >> 8),
|
||||
byte(v),
|
||||
}
|
||||
// Delete any non-printable ASCII characters.
|
||||
return string(slices.DeleteFunc(chars, func(b byte) bool { return b < ' ' || b > '~' }))
|
||||
}
|
18
feature/tpm/tpm_linux.go
Normal file
18
feature/tpm/tpm_linux.go
Normal file
@ -0,0 +1,18 @@
|
||||
// Copyright (c) Tailscale Inc & AUTHORS
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
package tpm
|
||||
|
||||
import (
|
||||
"github.com/google/go-tpm/tpm2/transport/linuxtpm"
|
||||
"tailscale.com/tailcfg"
|
||||
)
|
||||
|
||||
func info() *tailcfg.TPMInfo {
|
||||
t, err := linuxtpm.Open("/dev/tpm0")
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
defer t.Close()
|
||||
return infoFromCapabilities(t)
|
||||
}
|
12
feature/tpm/tpm_other.go
Normal file
12
feature/tpm/tpm_other.go
Normal file
@ -0,0 +1,12 @@
|
||||
// Copyright (c) Tailscale Inc & AUTHORS
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
//go:build !linux && !windows
|
||||
|
||||
package tpm
|
||||
|
||||
import "tailscale.com/tailcfg"
|
||||
|
||||
func info() *tailcfg.TPMInfo {
|
||||
return nil
|
||||
}
|
19
feature/tpm/tpm_test.go
Normal file
19
feature/tpm/tpm_test.go
Normal file
@ -0,0 +1,19 @@
|
||||
// Copyright (c) Tailscale Inc & AUTHORS
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
package tpm
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestPropToString(t *testing.T) {
|
||||
for prop, want := range map[uint32]string{
|
||||
0: "",
|
||||
0x4D534654: "MSFT",
|
||||
0x414D4400: "AMD",
|
||||
0x414D440D: "AMD",
|
||||
} {
|
||||
if got := propToString(prop); got != want {
|
||||
t.Errorf("propToString(0x%x): got %q, want %q", prop, got, want)
|
||||
}
|
||||
}
|
||||
}
|
18
feature/tpm/tpm_windows.go
Normal file
18
feature/tpm/tpm_windows.go
Normal file
@ -0,0 +1,18 @@
|
||||
// Copyright (c) Tailscale Inc & AUTHORS
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
package tpm
|
||||
|
||||
import (
|
||||
"github.com/google/go-tpm/tpm2/transport/windowstpm"
|
||||
"tailscale.com/tailcfg"
|
||||
)
|
||||
|
||||
func info() *tailcfg.TPMInfo {
|
||||
t, err := windowstpm.Open()
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
defer t.Close()
|
||||
return infoFromCapabilities(t)
|
||||
}
|
1
go.mod
1
go.mod
@ -44,6 +44,7 @@ require (
|
||||
github.com/golangci/golangci-lint v1.57.1
|
||||
github.com/google/go-cmp v0.6.0
|
||||
github.com/google/go-containerregistry v0.20.2
|
||||
github.com/google/go-tpm v0.9.4
|
||||
github.com/google/gopacket v1.1.19
|
||||
github.com/google/nftables v0.2.1-0.20240414091927-5e242ec57806
|
||||
github.com/google/uuid v1.6.0
|
||||
|
4
go.sum
4
go.sum
@ -486,6 +486,10 @@ github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/go-containerregistry v0.20.2 h1:B1wPJ1SN/S7pB+ZAimcciVD+r+yV/l/DSArMxlbwseo=
|
||||
github.com/google/go-containerregistry v0.20.2/go.mod h1:z38EKdKh4h7IP2gSfUUqEvalZBqs6AoLeWfUy34nQC8=
|
||||
github.com/google/go-tpm v0.9.4 h1:awZRf9FwOeTunQmHoDYSHJps3ie6f1UlhS1fOdPEt1I=
|
||||
github.com/google/go-tpm v0.9.4/go.mod h1:h9jEsEECg7gtLis0upRBQU+GhYVH6jMjrFxI8u6bVUY=
|
||||
github.com/google/go-tpm-tools v0.3.13-0.20230620182252-4639ecce2aba h1:qJEJcuLzH5KDR0gKc0zcktin6KSAwL7+jWKBYceddTc=
|
||||
github.com/google/go-tpm-tools v0.3.13-0.20230620182252-4639ecce2aba/go.mod h1:EFYHy8/1y2KfgTAsx7Luu7NGhoxtuVHnNo8jE7FikKc=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
|
||||
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
|
@ -875,10 +875,37 @@ type Hostinfo struct {
|
||||
// explicitly declared by a node.
|
||||
Location *Location `json:",omitempty"`
|
||||
|
||||
TPM *TPMInfo `json:",omitempty"` // TPM device metadata, if available
|
||||
|
||||
// NOTE: any new fields containing pointers in this type
|
||||
// require changes to Hostinfo.Equal.
|
||||
}
|
||||
|
||||
// TPMInfo contains information about a TPM 2.0 device present on a node.
|
||||
// All fields are read from TPM_CAP_TPM_PROPERTIES, see Part 2, section 6.13 of
|
||||
// https://trustedcomputinggroup.org/resource/tpm-library-specification/.
|
||||
type TPMInfo struct {
|
||||
// Manufacturer is a 4-letter code from section 4.1 of
|
||||
// https://trustedcomputinggroup.org/resource/vendor-id-registry/,
|
||||
// for example "MSFT" for Microsoft.
|
||||
// Read from TPM_PT_MANUFACTURER.
|
||||
Manufacturer string `json:",omitempty"`
|
||||
// Vendor is a vendor ID string, up to 16 characters.
|
||||
// Read from TPM_PT_VENDOR_STRING_*.
|
||||
Vendor string `json:",omitempty"`
|
||||
// Model is a vendor-defined TPM model.
|
||||
// Read from TPM_PT_VENDOR_TPM_TYPE.
|
||||
Model int `json:",omitempty"`
|
||||
// FirmwareVersion is the version number of the firmware.
|
||||
// Read from TPM_PT_FIRMWARE_VERSION_*.
|
||||
FirmwareVersion uint64 `json:",omitempty"`
|
||||
// SpecRevision is the TPM 2.0 spec revision encoded as a single number. All
|
||||
// revisions can be found at
|
||||
// https://trustedcomputinggroup.org/resource/tpm-library-specification/.
|
||||
// Before revision 184, TCG used the "01.83" format for revision 183.
|
||||
SpecRevision int `json:",omitempty"`
|
||||
}
|
||||
|
||||
// ServiceName is the name of a service, of the form `svc:dns-label`. Services
|
||||
// represent some kind of application provided for users of the tailnet with a
|
||||
// MagicDNS name and possibly dedicated IP addresses. Currently (2024-01-21),
|
||||
|
@ -141,6 +141,9 @@ func (src *Hostinfo) Clone() *Hostinfo {
|
||||
if dst.Location != nil {
|
||||
dst.Location = ptr.To(*src.Location)
|
||||
}
|
||||
if dst.TPM != nil {
|
||||
dst.TPM = ptr.To(*src.TPM)
|
||||
}
|
||||
return dst
|
||||
}
|
||||
|
||||
@ -184,6 +187,7 @@ var _HostinfoCloneNeedsRegeneration = Hostinfo(struct {
|
||||
AppConnector opt.Bool
|
||||
ServicesHash string
|
||||
Location *Location
|
||||
TPM *TPMInfo
|
||||
}{})
|
||||
|
||||
// Clone makes a deep copy of NetInfo.
|
||||
|
@ -68,6 +68,7 @@ func TestHostinfoEqual(t *testing.T) {
|
||||
"AppConnector",
|
||||
"ServicesHash",
|
||||
"Location",
|
||||
"TPM",
|
||||
}
|
||||
if have := fieldsOf(reflect.TypeFor[Hostinfo]()); !reflect.DeepEqual(have, hiHandles) {
|
||||
t.Errorf("Hostinfo.Equal check might be out of sync\nfields: %q\nhandled: %q\n",
|
||||
|
@ -301,7 +301,9 @@ func (v HostinfoView) UserspaceRouter() opt.Bool { return v.ж.User
|
||||
func (v HostinfoView) AppConnector() opt.Bool { return v.ж.AppConnector }
|
||||
func (v HostinfoView) ServicesHash() string { return v.ж.ServicesHash }
|
||||
func (v HostinfoView) Location() LocationView { return v.ж.Location.View() }
|
||||
func (v HostinfoView) Equal(v2 HostinfoView) bool { return v.ж.Equal(v2.ж) }
|
||||
func (v HostinfoView) TPM() views.ValuePointer[TPMInfo] { return views.ValuePointerOf(v.ж.TPM) }
|
||||
|
||||
func (v HostinfoView) Equal(v2 HostinfoView) bool { return v.ж.Equal(v2.ж) }
|
||||
|
||||
// A compilation failure here means this code must be regenerated, with the command at the top of this file.
|
||||
var _HostinfoViewNeedsRegeneration = Hostinfo(struct {
|
||||
@ -343,6 +345,7 @@ var _HostinfoViewNeedsRegeneration = Hostinfo(struct {
|
||||
AppConnector opt.Bool
|
||||
ServicesHash string
|
||||
Location *Location
|
||||
TPM *TPMInfo
|
||||
}{})
|
||||
|
||||
// View returns a read-only view of NetInfo.
|
||||
|
Loading…
x
Reference in New Issue
Block a user