Skip to content

Initial implementation for TPM extension #39059

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions extension/tpmextension/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,29 @@

The Trusted Platform Module (TPM) extension retrieves TLS certificates from the TPM device.

The extension implements `extensionauth.HTTPClient` interface therefore it can be used only with HTTP exporters (e.g. otlphttp exporter).

## Configuration

* `path` (required): The path to the TPM device. For example, `/dev/tpmrm0`.
* `key_file` (required): The path to the client TSS2 private key file.
* `cert_file` (required): The path to the client certificate file.
* `ca_file` (required): The path to the CA certificate file.
* `server_name_override` (optional): The server name override for the TLS connection. This is useful when the server name does not match the certificate.
* `owner_auth` (optional): The owner authorization password for the TPM device. This is required if the TPM device is protected by a password.
* `auth` (optional): The password for the TPM device. This is required if the TPM device is protected by a password.

Example:

```yaml
extensions:
tpm:
path: /dev/tpmrm0
key_file: client_key.key
cert_file: server.crt
ca_file: ca.crt
server_name_override: example.com
owner_auth: tpm-password
auth: password
```

19 changes: 18 additions & 1 deletion extension/tpmextension/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,32 @@
package tpmextension // import "github.com/open-telemetry/opentelemetry-collector-contrib/extension/tpmextension"

import (
"errors"

"go.opentelemetry.io/collector/component"
)

type Config struct{}
type Config struct {
// The path to the TPM device or Unix domain socket.
// For instance /dev/tpm0 or /dev/tpmrm0.
Path string `mapstructure:"path"`
// TSS2 key file
ClientKeyFile string `mapstructure:"key_file"`
ClientCertFile string `mapstructure:"cert_file"`
CaFile string `mapstructure:"ca_file"`
ServerName string `mapstructure:"server_name_override"`

OwnerAuth string `mapstructure:"owner_auth"`
Auth string `mapstructure:"auth"`
}

func createDefaultConfig() component.Config {
return &Config{}
}

func (cfg *Config) Validate() error {
if cfg.Path == "" {
return errors.New("path must be non-empty")
}
return nil
}
105 changes: 100 additions & 5 deletions extension/tpmextension/extension.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,37 +5,132 @@

import (
"context"
"crypto/tls"
"crypto/x509"
"encoding/pem"
"fmt"
"net/http"
"os"

keyfile "github.com/foxboron/go-tpm-keyfiles"
"github.com/google/go-tpm/tpm2/transport"
"github.com/google/go-tpm/tpmutil"

"go.opentelemetry.io/collector/component"
"go.opentelemetry.io/collector/extension"
"go.opentelemetry.io/collector/extension/extensionauth"

Check failure on line 21 in extension/tpmextension/extension.go

View workflow job for this annotation

GitHub Actions / govulncheck (extension)

could not import go.opentelemetry.io/collector/extension/extensionauth (invalid package name: "")
)

type tpmExtension struct {
type TPMExtension struct {
config *Config
cancel context.CancelFunc
telemetrySettings component.TelemetrySettings

tlsConfig *tls.Config
}

var _ extension.Extension = (*tpmExtension)(nil)
var (
_ extension.Extension = (*TPMExtension)(nil)
_ extensionauth.HTTPClient = (*TPMExtension)(nil)
//_ extensionauth.GRPCClient = (*TPMExtension)(nil)
)

var _ extension.Extension = (*TPMExtension)(nil)

func newTPMExtension(extensionCfg *Config, settings extension.Settings) (extension.Extension, error) {
settingsExtension := &tpmExtension{
settingsExtension := &TPMExtension{
config: extensionCfg,
telemetrySettings: settings.TelemetrySettings,
}
return settingsExtension, nil
}

func (extension *tpmExtension) Start(_ context.Context, _ component.Host) error {
func (extension *TPMExtension) Start(_ context.Context, _ component.Host) error {
extension.telemetrySettings.Logger.Info("starting up tpm extension")
tpm, err := tpmutil.OpenTPM(extension.config.Path)
if err != nil {
return err
}
c, err := os.ReadFile(extension.config.ClientKeyFile)
if err != nil {
return err
}
tss2Key, err := keyfile.Decode(c)
if err != nil {
return fmt.Errorf("failed to load %s: %w", extension.config.ClientKeyFile, err)
}

clientCert, err := loadCert(extension.config.ClientCertFile)
if err != nil {
return fmt.Errorf("failed to load %s: %w", extension.config.ClientCertFile, err)
}
caCert, err := loadCert(extension.config.CaFile)
if err != nil {
return fmt.Errorf("failed to load %s: %w", extension.config.CaFile, err)
}

caCertPool := x509.NewCertPool()
caCertPool.AddCert(caCert)

signer, err := tss2Key.Signer(transport.FromReadWriteCloser(tpm), []byte(extension.config.OwnerAuth), []byte(extension.config.Auth))
if err != nil {
return fmt.Errorf("failed to create TPM signer: %w", err)
}

tlsCert := tls.Certificate{
Certificate: [][]byte{clientCert.Raw},
PrivateKey: signer,
Leaf: clientCert,
}
tlsCfg := &tls.Config{
Certificates: []tls.Certificate{tlsCert},
RootCAs: caCertPool,
ServerName: extension.config.ServerName,
}

extension.tlsConfig = tlsCfg
return nil
}

func (extension *tpmExtension) Shutdown(_ context.Context) error {
func (extension *TPMExtension) Shutdown(_ context.Context) error {
extension.telemetrySettings.Logger.Info("shutting down tmp extension")
if extension.cancel != nil {
extension.cancel()
}
return nil
}

func (extension *TPMExtension) RoundTripper(base http.RoundTripper) (http.RoundTripper, error) {
return &TPMRoundTripper{
baseTransport: base,
tpmTLSTransport: &http.Transport{
TLSClientConfig: extension.tlsConfig,
},
}, nil
}

type TPMRoundTripper struct {
baseTransport http.RoundTripper
tpmTLSTransport *http.Transport
}

// RoundTrip modifies the original request and adds Bearer token Authorization headers. Incoming requests support multiple tokens, but outgoing requests only use one.
func (interceptor *TPMRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
return interceptor.tpmTLSTransport.RoundTrip(req)
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is unfortunate that it skips other interceptors.

}

func loadCert(cert string) (*x509.Certificate, error) {
certPEM, err := os.ReadFile(cert)
if err != nil {
return nil, err
}
certDER, _ := pem.Decode(certPEM)
if certDER == nil {
return nil, err
}
leafCert, err := x509.ParseCertificate(certDER.Bytes)
if err != nil {
return nil, err
}
return leafCert, nil
}
4 changes: 4 additions & 0 deletions extension/tpmextension/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,14 @@ module github.com/open-telemetry/opentelemetry-collector-contrib/extension/tpmex
go 1.23.0

require (
github.com/foxboron/go-tpm-keyfiles v0.0.0-20250323135004-b31fac66206e
github.com/google/go-tpm v0.9.3
github.com/stretchr/testify v1.10.0
go.opentelemetry.io/collector/component v1.28.2-0.20250319144947-41a9ea7f7402
go.opentelemetry.io/collector/component/componenttest v0.122.2-0.20250319144947-41a9ea7f7402
go.opentelemetry.io/collector/confmap v1.28.2-0.20250319144947-41a9ea7f7402
go.opentelemetry.io/collector/extension v1.28.2-0.20250319144947-41a9ea7f7402
go.opentelemetry.io/collector/extension/extensionauth v0.122.2-0.20250319144947-41a9ea7f7402
go.opentelemetry.io/collector/extension/extensiontest v0.122.2-0.20250319144947-41a9ea7f7402
go.uber.org/goleak v1.3.0
)
Expand Down Expand Up @@ -36,6 +39,7 @@ require (
go.opentelemetry.io/otel/trace v1.35.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
go.uber.org/zap v1.27.0 // indirect
golang.org/x/crypto v0.36.0 // indirect
golang.org/x/net v0.37.0 // indirect
golang.org/x/sys v0.31.0 // indirect
golang.org/x/text v0.23.0 // indirect
Expand Down
13 changes: 13 additions & 0 deletions extension/tpmextension/go.sum

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading