Skip to content
Closed
Show file tree
Hide file tree
Changes from 1 commit
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 @@ package tpmextension // import "github.com/open-telemetry/opentelemetry-collecto

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"
)

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 @@ -14,10 +14,12 @@ require (

require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/foxboron/go-tpm-keyfiles v0.0.0-20250323135004-b31fac66206e // indirect
github.com/go-logr/logr v1.4.2 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/go-viper/mapstructure/v2 v2.2.1 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/google/go-tpm v0.9.3 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/hashicorp/go-version v1.7.0 // indirect
github.com/knadh/koanf/maps v0.1.1 // indirect
Expand All @@ -27,6 +29,7 @@ require (
github.com/mitchellh/reflectwalk v1.0.2 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
go.opentelemetry.io/collector/extension/extensionauth v0.122.1 // indirect
go.opentelemetry.io/collector/featuregate v1.28.2-0.20250319144947-41a9ea7f7402 // indirect
go.opentelemetry.io/collector/pdata v1.28.2-0.20250319144947-41a9ea7f7402 // indirect
go.opentelemetry.io/otel v1.35.0 // indirect
Expand All @@ -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
8 changes: 8 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