Skip to content
Merged
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
12 changes: 12 additions & 0 deletions lib/auth/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ import (
"github.com/gravitational/teleport/lib/auth/machineid/workloadidentityv1"
"github.com/gravitational/teleport/lib/auth/okta"
"github.com/gravitational/teleport/lib/auth/recordingencryption"
"github.com/gravitational/teleport/lib/auth/recordingmetadata"
"github.com/gravitational/teleport/lib/auth/summarizer"
"github.com/gravitational/teleport/lib/auth/userloginstate"
wanlib "github.com/gravitational/teleport/lib/auth/webauthn"
Expand Down Expand Up @@ -498,6 +499,9 @@ func NewServer(cfg *InitConfig, opts ...ServerOption) (as *Server, err error) {
if cfg.SessionSummarizerProvider == nil {
cfg.SessionSummarizerProvider = summarizer.NewSessionSummarizerProvider()
}
if cfg.RecordingMetadataProvider == nil {
cfg.RecordingMetadataProvider = recordingmetadata.NewProvider()
}
if cfg.WorkloadIdentityX509Revocations == nil {
cfg.WorkloadIdentityX509Revocations, err = local.NewWorkloadIdentityX509RevocationService(cfg.Backend)
if err != nil {
Expand Down Expand Up @@ -662,6 +666,7 @@ func NewServer(cfg *InitConfig, opts ...ServerOption) (as *Server, err error) {
accessMonitoringEnabled: cfg.AccessMonitoringEnabled,
logger: cfg.Logger,
sessionSummarizerProvider: cfg.SessionSummarizerProvider,
recordingMetadataProvider: cfg.RecordingMetadataProvider,
}
as.inventory = inventory.NewController(as, services,
inventory.WithAuthServerID(cfg.HostUUID),
Expand Down Expand Up @@ -1343,9 +1348,16 @@ type Server struct {
// plugin. The summarizer itself summarizes session recordings.
sessionSummarizerProvider *summarizer.SessionSummarizerProvider

// recordingMetadataProvider provides recording metadata for session recordings.
recordingMetadataProvider *recordingmetadata.Provider

// BotInstanceVersionReporter is called periodically to generate a report of
// the number of bot instances by version and update group.
BotInstanceVersionReporter *machineidv1.AutoUpdateVersionReporter

// EncryptedIO provides encryption for session related data such as
// recordings, thumbnails, and metadata.
EncryptedIO *recordingencryption.EncryptedIO
}

// SetSAMLService registers svc as the SAMLService that provides the SAML
Expand Down
4 changes: 4 additions & 0 deletions lib/auth/authtest/authtest.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ import (
"github.com/gravitational/teleport/lib/auth"
"github.com/gravitational/teleport/lib/auth/accesspoint"
"github.com/gravitational/teleport/lib/auth/authclient"
"github.com/gravitational/teleport/lib/auth/recordingmetadata"
"github.com/gravitational/teleport/lib/auth/state"
"github.com/gravitational/teleport/lib/auth/summarizer"
authority "github.com/gravitational/teleport/lib/auth/testauthority"
Expand Down Expand Up @@ -96,6 +97,8 @@ type AuthServerConfig struct {
// SessionSummarizerProvider allows a test to configure its own session
// summarizer provider.
SessionSummarizerProvider *summarizer.SessionSummarizerProvider
// RecordingMetadataProvider allows a test to configure its own recording
RecordingMetadataProvider *recordingmetadata.Provider
// TraceClient allows a test to configure the trace client
TraceClient otlptrace.Client
// AuthPreferenceSpec is custom initial AuthPreference spec for the test.
Expand Down Expand Up @@ -338,6 +341,7 @@ func NewAuthServer(cfg AuthServerConfig) (*AuthServer, error) {
KeyStoreConfig: cfg.KeystoreConfig,
MultipartHandler: cfg.UploadHandler,
SessionSummarizerProvider: cfg.SessionSummarizerProvider,
RecordingMetadataProvider: cfg.RecordingMetadataProvider,
},
WithClock(cfg.Clock),
// Reduce auth.Server bcrypt costs when testing.
Expand Down
14 changes: 8 additions & 6 deletions lib/auth/grpcserver.go
Original file line number Diff line number Diff line change
Expand Up @@ -5170,7 +5170,6 @@ func (g *GRPCServer) rangeDefaultInstallers(ctx context.Context, start, end stri
return
}
}

}
}

Expand All @@ -5190,7 +5189,6 @@ func (g *GRPCServer) ListInstallers(ctx context.Context, req *authpb.ListInstall
int(req.PageSize),
types.Installer.GetName,
)

if err != nil {
return nil, trace.Wrap(err)
}
Expand Down Expand Up @@ -6201,10 +6199,13 @@ func NewGRPCServer(cfg GRPCServerConfig) (*GRPCServer, error) {
userloginstatev1pb.RegisterUserLoginStateServiceServer(server, userLoginStateServer)

recordingEncryptionService, err := recordingencryptionv1.NewService(recordingencryptionv1.ServiceConfig{
Authorizer: cfg.Authorizer,
Uploader: cfg.AuthServer.Services,
KeyRotater: cfg.AuthServer.Services,
Logger: cfg.AuthServer.logger.With(teleport.ComponentKey, teleport.ComponentRecordingEncryption),
Authorizer: cfg.Authorizer,
Uploader: cfg.AuthServer.Services,
KeyRotater: cfg.AuthServer.Services,
Logger: cfg.AuthServer.logger.With(teleport.ComponentKey, teleport.ComponentRecordingEncryption),
SessionSummarizerProvider: cfg.APIConfig.AuthServer.sessionSummarizerProvider,
RecordingMetadataProvider: cfg.AuthServer.recordingMetadataProvider,
SessionStreamer: cfg.AuthServer,
})
if err != nil {
return nil, trace.Wrap(err)
Expand Down Expand Up @@ -6335,6 +6336,7 @@ func NewGRPCServer(cfg GRPCServerConfig) (*GRPCServer, error) {
),
Streamer: cfg.AuthServer,
DownloadHandler: cfg.AuthServer,
Decrypter: cfg.AuthServer.EncryptedIO,
})
if err != nil {
return nil, trace.Wrap(err, "creating recording metadata service")
Expand Down
4 changes: 4 additions & 0 deletions lib/auth/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ import (
"github.com/gravitational/teleport/lib/auth/migration"
"github.com/gravitational/teleport/lib/auth/recordingencryption"
"github.com/gravitational/teleport/lib/auth/recordingencryption/recordingencryptionv1"
"github.com/gravitational/teleport/lib/auth/recordingmetadata"
"github.com/gravitational/teleport/lib/auth/state"
"github.com/gravitational/teleport/lib/auth/summarizer"
"github.com/gravitational/teleport/lib/backend"
Expand Down Expand Up @@ -403,6 +404,9 @@ type InitConfig struct {
// plugin. The summarizer itself summarizes session recordings.
SessionSummarizerProvider *summarizer.SessionSummarizerProvider

// RecordingMetadataProvider provides recording metadata for session recordings.
RecordingMetadataProvider *recordingmetadata.Provider

// RunWhileLockedRetryInterval defines the interval at which the auth server retries
// a locking operation for backend objects.
// This setting is particularly useful in test environments,
Expand Down
70 changes: 70 additions & 0 deletions lib/auth/recordingencryption/buf_decrypter.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
// Teleport
// Copyright (C) 2025 Gravitational, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.

package recordingencryption

import (
"bytes"
"context"
"io"

"github.com/gravitational/trace"
)

// ageEncryptionPrefix is the prefix used to identify age-encrypted data.
// age always uses "age-encryption.org/v1" as the prefix for its encrypted files.
const ageEncryptionPrefix = "age-encryption.org"

var ageEncryptionPrefixBytes = []byte(ageEncryptionPrefix)

// Decrypter wraps an io.Reader with decryption if the data is age-encrypted.
type Decrypter interface {
WithDecryption(ctx context.Context, reader io.Reader) (io.Reader, error)
}

// DecryptBufferIfEncrypted checks whether the provided buffer contains
// age-encrypted data and decrypts it if necessary.
//
// The function looks for the standard age encryption header prefix to determine
// whether the data is encrypted. If the buffer is not age-encrypted, it returns
// the original data unchanged.
//
// If the buffer is encrypted, a Decrypter must be provided. The function uses
// the Decrypter to read and decrypt the buffer, returning the plaintext bytes.
// If no Decrypter is configured when encrypted data is detected, an error is
// returned.
func DecryptBufferIfEncrypted(ctx context.Context, buf []byte, decrypter Decrypter) ([]byte, error) {
if !bytes.HasPrefix(buf, ageEncryptionPrefixBytes) {
return buf, nil
}

if decrypter == nil {
return nil, trace.BadParameter("recording metadata decrypter is not configured")
}

decryptedReader, err := decrypter.WithDecryption(ctx, bytes.NewReader(buf))
if err != nil {
return nil, trace.Wrap(err, "decrypting recording metadata")
}

decryptedBuf := bytes.NewBuffer(make([]byte, 0, len(buf)))
_, err = io.Copy(decryptedBuf, decryptedReader)
if err != nil {
return nil, trace.Wrap(err, "reading decrypted recording metadata")
}

return decryptedBuf.Bytes(), nil
}
13 changes: 13 additions & 0 deletions lib/auth/recordingencryption/encryptedio_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@ func TestEncryptedIO(t *testing.T) {
err = writer.Close()
require.NoError(t, err)

encryptedBytes := out.Bytes()

reader, err := encryptedIO.WithDecryption(ctx, out)
require.NoError(t, err)

Expand All @@ -65,6 +67,11 @@ func TestEncryptedIO(t *testing.T) {

require.Equal(t, msg, plaintext)

// Test buffer decryption directly
decryptedBuf, err := recordingencryption.DecryptBufferIfEncrypted(ctx, encryptedBytes, encryptedIO)
require.NoError(t, err)
require.Equal(t, msg, decryptedBuf)

// creating an EncryptedIO without a SessionRecordingConfigGetter or keyfinder should be an error
_, err = recordingencryption.NewEncryptedIO(nil, nil)
require.Error(t, err)
Expand All @@ -79,6 +86,12 @@ func TestEncryptedIO(t *testing.T) {

_, err = encryptedIO.WithEncryption(ctx, &writeCloser{Writer: out})
require.ErrorIs(t, err, recordingencryption.ErrEncryptionDisabled)

// Decrypting an unencrypted buffer should return the original buffer
origBuf := []byte("this is not encrypted")
decryptedBuf, err = recordingencryption.DecryptBufferIfEncrypted(ctx, origBuf, encryptedIO)
require.NoError(t, err)
require.Equal(t, origBuf, decryptedBuf)
}

type fakeSRCGetter struct {
Expand Down
52 changes: 48 additions & 4 deletions lib/auth/recordingencryption/recordingencryptionv1/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,11 @@ import (

"github.com/gravitational/teleport"
recordingencryptionv1 "github.com/gravitational/teleport/api/gen/proto/go/teleport/recordingencryption/v1"
"github.com/gravitational/teleport/lib/auth/recordingmetadata"
"github.com/gravitational/teleport/lib/auth/summarizer"
"github.com/gravitational/teleport/lib/authz"
"github.com/gravitational/teleport/lib/events"
sessionpostprocessing "github.com/gravitational/teleport/lib/events/sessionpostprocessing"
"github.com/gravitational/teleport/lib/session"
)

Expand All @@ -46,6 +49,14 @@ type ServiceConfig struct {
Logger *slog.Logger
Uploader events.MultipartUploader
KeyRotater KeyRotater
// SessionSummarizerProvider is a provider of the session summarizer service.
// It can be nil or provide a nil summarizer if summarization is not needed.
// The summarizer itself summarizes session recordings.
SessionSummarizerProvider *summarizer.SessionSummarizerProvider
// RecordingMetadataProvider is a provider of the recording metadata service.
RecordingMetadataProvider *recordingmetadata.Provider
// SessionStreamer is a streamer for session events.
SessionStreamer events.SessionStreamer
}

// NewService returns a new [Service] based on the given [ServiceConfig].
Expand All @@ -57,17 +68,26 @@ func NewService(cfg ServiceConfig) (*Service, error) {
return nil, trace.BadParameter("uploader is required")
case cfg.KeyRotater == nil:
return nil, trace.BadParameter("key rotater is required")
case cfg.SessionStreamer == nil:
return nil, trace.BadParameter("session streamer is required")
case cfg.RecordingMetadataProvider == nil:
return nil, trace.BadParameter("recording metadata provider is required")
case cfg.SessionSummarizerProvider == nil:
return nil, trace.BadParameter("session summarizer provider is required")
}

if cfg.Logger == nil {
cfg.Logger = slog.With(teleport.ComponentKey, teleport.ComponentRecordingEncryption)
}

return &Service{
logger: cfg.Logger,
uploader: cfg.Uploader,
auth: cfg.Authorizer,
rotater: cfg.KeyRotater,
logger: cfg.Logger,
uploader: cfg.Uploader,
auth: cfg.Authorizer,
rotater: cfg.KeyRotater,
sessionSummarizerProvider: cfg.SessionSummarizerProvider,
recordingMetadataProvider: cfg.RecordingMetadataProvider,
streamer: cfg.SessionStreamer,
}, nil
}

Expand All @@ -79,6 +99,13 @@ type Service struct {
logger *slog.Logger
uploader events.MultipartUploader
rotater KeyRotater
// SessionSummarizerProvider is a provider of the session summarizer service.
// It can be nil or provide a nil summarizer if summarization is not needed.
// The summarizer itself summarizes session recordings.
sessionSummarizerProvider *summarizer.SessionSummarizerProvider
// RecordingMetadataProvider is a provider of the recording metadata service.
recordingMetadataProvider *recordingmetadata.Provider
streamer events.SessionStreamer
}

func streamUploadAsProto(upload events.StreamUpload) *recordingencryptionv1.Upload {
Expand Down Expand Up @@ -201,6 +228,23 @@ func (s *Service) CompleteUpload(ctx context.Context, req *recordingencryptionv1
return nil, trace.Wrap(err)
}

sessionEnd, err := events.FindSessionEndEvent(ctx, s.streamer, upload.SessionID)
if err != nil || sessionEnd == nil {
return &recordingencryptionv1.CompleteUploadResponse{}, nil
}

if err := sessionpostprocessing.Process(
ctx,
sessionpostprocessing.Config{
SessionEnd: sessionEnd,
SessionID: upload.SessionID,
SessionSummarizerProvider: s.sessionSummarizerProvider,
RecordingMetadataProvider: s.recordingMetadataProvider,
},
); err != nil {
s.logger.WarnContext(ctx, "session post-processing failed", "error", err)
}

return &recordingencryptionv1.CompleteUploadResponse{}, nil
}

Expand Down
Loading
Loading