diff --git a/internal/bsr/bsr.go b/internal/bsr/bsr.go index f6710b975..c58998056 100644 --- a/internal/bsr/bsr.go +++ b/internal/bsr/bsr.go @@ -4,13 +4,11 @@ package bsr import ( - "bufio" "context" + "encoding/json" "errors" "fmt" "io" - "strconv" - "strings" "github.com/hashicorp/boundary/internal/bsr/internal/checksum" "github.com/hashicorp/boundary/internal/bsr/internal/is" @@ -21,11 +19,12 @@ import ( ) const ( - bsrFile = "%s.bsr" - connectionFile = "%s.connection" - channelFile = "%s.channel" - messagesFile = "messages-%s.data" - requestsFile = "requests-%s.data" + bsrFile = "%s.bsr" + connectionFile = "%s.connection" + channelFile = "%s.channel" + messagesFile = "messages-%s.data" + requestsFile = "requests-%s.data" + sessionMetaFile = "session-meta.json" bsrPubKeyFile = "bsrKey.pub" wrappedBsrKeyFile = "wrappedBsrKey" @@ -34,479 +33,38 @@ const ( pubKeySelfSignatureFile = "pubKeySelfSignature.sign" ) -// decodeSessionMeta will populate a SessionMeta for an opened BSR Session -// TODO Unmarshal without brute force -func decodeSessionMeta(ctx context.Context, r io.Reader) (*SessionMeta, error) { - const op = "bsr.decodeSessionMeta" - - switch { - case r == nil: - return nil, fmt.Errorf("%s: missing session meta file: %w", op, ErrInvalidParameter) - } - - connections := make(map[string]bool) - s := &SessionMeta{} - user := &User{Scope: Scope{}} - target := &Target{Scope: Scope{}} - worker := &Worker{} - sHost := &StaticHost{Catalog: StaticHostCatalog{}} - dHost := &DynamicHost{Catalog: DynamicHostCatalog{}} - staticCredStore := make(map[string]StaticCredentialStore) - scStoreToJsonCreds := make(map[string]StaticJsonCredential) - scStoreToUPCreds := make(map[string]StaticUsernamePasswordCredential) - scStoreToSshCreds := make(map[string]StaticSshPrivateKeyCredential) - vaultCredStore := make(map[string]VaultCredentialStore) - vsStoreToVaultLib := make(map[string]VaultLibrary) - vsStoreToVaultSshCertLib := make(map[string]VaultSshCertLibrary) - - scanner := bufio.NewScanner(r) - for scanner.Scan() { - l := scanner.Text() - if len(l) == 0 { - continue - } - k, v, ok := strings.Cut(l, ":") - if !ok { - return nil, fmt.Errorf("%s: session meta file contains invalid entry; expecting k: v pair:%s", op, l) - } - v = strings.TrimSpace(v) - switch { - case k == "id": - s.Id = v - case k == "protocol": - s.Protocol = Protocol(v) - - // User - case k == "user_publicId": - user.PublicId = v - case k == "user_name": - user.Name = v - case k == "user_description": - user.Description = v - case k == "user_scope_publicId": - user.Scope.PublicId = v - case k == "user_scope_name": - user.Scope.Name = v - case k == "user_scope_description": - user.Scope.Description = v - case k == "user_scope_type": - user.Scope.Type = v - case k == "user_scope_parentId": - user.Scope.ParentId = v - case k == "user_scope_primaryAuthMethod": - user.Scope.PrimaryAuthMethodId = v - - // Target - case k == "target_publicId": - target.PublicId = v - case k == "target_name": - target.Name = v - case k == "target_description": - target.Description = v - case k == "target_defaultPort": - dp, err := strconv.Atoi(v) - if err != nil { - return nil, err - } - target.DefaultPort = uint32(dp) - case k == "target_sessionMaxSeconds": - sms, err := strconv.Atoi(v) - if err != nil { - return nil, err - } - target.SessionMaxSeconds = uint32(sms) - case k == "target_sessionConnectionLimit": - scl, err := strconv.Atoi(v) - if err != nil { - return nil, err - } - target.SessionConnectionLimit = int32(scl) - case k == "target_workerFilter": - target.WorkerFilter = v - case k == "target_egressWorkerFilter": - target.EgressWorkerFilter = v - case k == "target_ingressWorkerFilter": - target.IngressWorkerFilter = v - case k == "target_scope_publicId": - target.Scope.PublicId = v - case k == "target_scope_name": - target.Scope.Name = v - case k == "target_scope_description": - target.Scope.Description = v - case k == "target_scope_type": - target.Scope.Type = v - case k == "target_scope_parentId": - target.Scope.ParentId = v - case k == "target_scope_primaryAuthMethod": - target.Scope.PrimaryAuthMethodId = v - - // Worker - case k == "worker_publicId": - worker.PublicId = v - case k == "worker_version": - worker.Version = v - case k == "worker_sha": - worker.Sha = v - - // Static Host - case k == "staticHost_publicId": - sHost.PublicId = v - case k == "staticHost_name": - sHost.Name = v - case k == "staticHost_description": - sHost.Description = v - case k == "staticHost_address": - sHost.Address = v - case k == "staticHostCatalog_publicId": - sHost.Catalog.PublicId = v - case k == "staticHostCatalog_projectId": - sHost.Catalog.ProjectId = v - case k == "staticHostCatalog_name": - sHost.Catalog.Name = v - case k == "staticHostCatalog_description": - sHost.Catalog.Description = v - - // Dynamic Host - case k == "dynamicHost_publicId": - dHost.PublicId = v - case k == "dynamicHost_name": - dHost.Name = v - case k == "dynamicHost_description": - dHost.Description = v - case k == "dynamicHost_externalId": - dHost.ExternalId = v - case k == "dynamicHostCatalog_publicId": - dHost.Catalog.PublicId = v - case k == "dynamicHostCatalog_projectId": - dHost.Catalog.ProjectId = v - case k == "dynamicHostCatalog_name": - dHost.Catalog.Name = v - case k == "dynamicHostCatalog_description": - dHost.Catalog.Description = v - case k == "dynamicHostCatalog_pluginId": - dHost.Catalog.PluginId = v - case k == "dynamicHostCatalog_attributes": - dHost.Catalog.Attributes = v - - // Static Credential Store - case strings.Contains(k, "staticCredentialStore_"): - idx := strings.Index(k, "_staticCredentialStore") - if idx == -1 { - break - } - sId := k[:idx] - scs, ok := staticCredStore[sId] - if !ok { - scs = StaticCredentialStore{ - PublicId: sId, - } - } - switch { - case strings.Contains(k, "staticJsonCredential"): - idxEnd := strings.Index(k, "_staticJsonCredential") - idxStart := strings.Index(k, "_credential_") + len("_credential_") - if idxStart == -1 || idxEnd == -1 { - break - } - credId := k[idxStart:idxEnd] - // credstoreid - staticJsonCredentialid in the map - mapId := fmt.Sprintf("%s-%s", sId, credId) - c, ok := scStoreToJsonCreds[mapId] - if !ok { - c = StaticJsonCredential{ - PublicId: credId, - } - } - switch { - case strings.Contains(k, "_projectId"): - c.ProjectId = v - case strings.Contains(k, "_name"): - c.Name = v - case strings.Contains(k, "_description"): - c.Description = v - case strings.Contains(k, "_objectHmac"): - c.ObjectHmac = []byte(v) - } - scStoreToJsonCreds[mapId] = c - case strings.Contains(k, "staticUsernamePasswordCredential"): - idxEnd := strings.Index(k, "_staticJUsernamePasswordCredential") - idxStart := strings.Index(k, "_credential_") + len("_credential_") - if idxStart == -1 || idxEnd == -1 { - break - } - credId := k[idxStart:idxEnd] - // credstoreid - UsernamePasswordCredentialid in the map - mapId := fmt.Sprintf("%s-%s", sId, credId) - c, ok := scStoreToUPCreds[mapId] - if !ok { - c = StaticUsernamePasswordCredential{ - PublicId: credId, - } - } - switch { - case strings.Contains(k, "_projectId"): - c.ProjectId = v - case strings.Contains(k, "_name"): - c.Name = v - case strings.Contains(k, "_description"): - c.Description = v - case strings.Contains(k, "_passwordHmac"): - c.PasswordHmac = []byte(v) - } - scStoreToUPCreds[mapId] = c - case strings.Contains(k, "staticSshPrivateKeyCredential"): - idxEnd := strings.Index(k, "_staticSshPrivateKeyCredential") - idxStart := strings.Index(k, "_credential_") + len("_credential_") - if idxStart == -1 || idxEnd == -1 { - break - } - credId := k[idxStart:idxEnd] - // credstoreid - staticSshPrivateKeyid in the map - mapId := fmt.Sprintf("%s-%s", sId, credId) - c, ok := scStoreToSshCreds[mapId] - if !ok { - c = StaticSshPrivateKeyCredential{ - PublicId: credId, - } - } - switch { - case strings.Contains(k, "_projectId"): - c.ProjectId = v - case strings.Contains(k, "_name"): - c.Name = v - case strings.Contains(k, "_description"): - c.Description = v - case strings.Contains(k, "_privateKeyHmac"): - c.PrivateKeyHmac = []byte(v) - case strings.Contains(k, "_privateKeyPassphraseHmac"): - c.PrivateKeyPassphraseHmac = []byte(v) - } - scStoreToSshCreds[mapId] = c - case strings.Contains(k, "_projectId"): - scs.ProjectId = v - case strings.Contains(k, "_name"): - scs.Name = v - case strings.Contains(k, "_description"): - scs.Description = v - } - staticCredStore[sId] = scs - - // Vault Credential Store - case strings.Contains(k, "vaultCredentialStore_"): - idx := strings.Index(k, "_vaultCredentialStore") - if idx == -1 { - break - } - vId := k[:idx] - vcs, ok := vaultCredStore[vId] - if !ok { - vcs = VaultCredentialStore{ - PublicId: vId, - } - } - switch { - case strings.Contains(k, "vaultLibrary"): - idxEnd := strings.Index(k, "_vaultLibrary") - idxStart := strings.Index(k, "_credential_") + len("_credential_") - if idxStart == -1 || idxEnd == -1 { - break - } - credId := k[idxStart:idxEnd] - // credstoreid - vaultLibraryid in the map - mapId := fmt.Sprintf("%s-%s", vId, credId) - c, ok := vsStoreToVaultLib[mapId] - if !ok { - c = VaultLibrary{ - PublicId: credId, - } - } - switch { - case strings.Contains(k, "_projectId"): - c.ProjectId = v - case strings.Contains(k, "_name"): - c.Name = v - case strings.Contains(k, "_description"): - c.Description = v - case strings.Contains(k, "_vaultPath"): - c.VaultPath = v - case strings.Contains(k, "_httpMethod"): - c.HttpMethod = v - case strings.Contains(k, "_httpRequestBody"): - c.HttpRequestBody = []byte(v) - case strings.Contains(k, "_credentialType"): - c.CredentialType = v - } - vsStoreToVaultLib[mapId] = c - case strings.Contains(k, "vaultSshCertLibrary"): - idxEnd := strings.Index(k, "_vaultSshCertLibrary") - idxStart := strings.Index(k, "_credential_") + len("_credential_") - if idxStart == -1 || idxEnd == -1 { - break - } - credId := k[idxStart:idxEnd] - // credstoreid - vaultSshCertLibraryid in the map - mapId := fmt.Sprintf("%s-%s", vId, credId) - c, ok := vsStoreToVaultSshCertLib[mapId] - if !ok { - c = VaultSshCertLibrary{ - PublicId: credId, - } - } - switch { - case strings.Contains(k, "_projectId"): - c.ProjectId = v - case strings.Contains(k, "_username"): - c.Username = v - case strings.Contains(k, "_name"): - c.Name = v - case strings.Contains(k, "_description"): - c.Description = v - case strings.Contains(k, "_vaultPath"): - c.VaultPath = v - case strings.Contains(k, "_keyType"): - c.KeyType = v - case strings.Contains(k, "_keyBits"): - var err error - c.KeyBits, err = strconv.Atoi(v) - if err != nil { - return nil, err - } - case strings.Contains(k, "_ttl"): - c.Ttl = v - case strings.Contains(k, "_criticalOptions"): - c.CriticalOptions = []byte(v) - case strings.Contains(k, "_extensions"): - c.Extensions = []byte(v) - case strings.Contains(k, "_credentialType"): - c.CredentialType = v - } - vsStoreToVaultSshCertLib[mapId] = c - case strings.Contains(k, "_projectId"): - vcs.ProjectId = v - case strings.Contains(k, "_namespace"): - vcs.Namespace = v - case strings.Contains(k, "_name"): - vcs.Name = v - case strings.Contains(k, "_description"): - vcs.Description = v - case strings.Contains(k, "_vaultAddress"): - vcs.VaultAddress = v - - case strings.Contains(k, "_tlsServerName"): - vcs.TlsServerName = v - case strings.Contains(k, "_tlsSkipVerify"): - var err error - vcs.TlsSkipVerify, err = strconv.ParseBool(v) - if err != nil { - return nil, err - } - case strings.Contains(k, "_workerFilter"): - vcs.WorkerFilter = v - } - vaultCredStore[vId] = vcs - - // connections - case k == "connection": - connections[v] = true - } - } - - s.Target = target - s.Worker = worker - s.User = user - if sHost.PublicId != "" { - s.StaticHost = sHost - } - if dHost.PublicId != "" { - s.DynamicHost = dHost - } - - for k, v := range scStoreToJsonCreds { - scId, _, ok := strings.Cut(k, "-") - if ok { - staticCred := staticCredStore[scId] - staticCred.Credentials = append(staticCred.Credentials, v) - staticCredStore[scId] = staticCred - } - } - for k, v := range scStoreToUPCreds { - scId, _, ok := strings.Cut(k, "-") - if ok { - staticCred := staticCredStore[scId] - staticCred.Credentials = append(staticCred.Credentials, v) - staticCredStore[scId] = staticCred - } - } - for k, v := range scStoreToSshCreds { - scId, _, ok := strings.Cut(k, "-") - if ok { - staticCred := staticCredStore[scId] - staticCred.Credentials = append(staticCred.Credentials, v) - staticCredStore[scId] = staticCred - } - } - scStores := make([]StaticCredentialStore, 0) - for _, v := range staticCredStore { - scStores = append(scStores, v) - } - - for k, v := range vsStoreToVaultSshCertLib { - vlId, _, ok := strings.Cut(k, "-") - if ok { - vaultLib := vaultCredStore[vlId] - vaultLib.CredentialLibraries = append(vaultLib.CredentialLibraries, v) - vaultCredStore[vlId] = vaultLib - } - } - for k, v := range vsStoreToVaultLib { - vlId, _, ok := strings.Cut(k, "-") - if ok { - vaultLib := vaultCredStore[vlId] - vaultLib.CredentialLibraries = append(vaultLib.CredentialLibraries, v) - vaultCredStore[vlId] = vaultLib - } - } - vcStores := make([]VaultCredentialStore, 0) - for _, v := range vaultCredStore { - vcStores = append(vcStores, v) - } - - s.StaticCredentialStore = scStores - s.VaultCredentialStore = vcStores - s.connections = connections - - return s, nil -} - // Session is the top level container in a bsr that contains the files for // a recorded session. type Session struct { *container multiplexed bool - Meta *SessionMeta + Meta *SessionRecordingMeta + SessionMeta *SessionMeta } // NewSession creates a Session container for a given session id. -func NewSession(ctx context.Context, meta *SessionMeta, f storage.FS, keys *kms.Keys, options ...Option) (*Session, error) { +func NewSession(ctx context.Context, meta *SessionRecordingMeta, sessionMeta *SessionMeta, f storage.FS, keys *kms.Keys, options ...Option) (*Session, error) { const op = "bsr.NewSession" switch { case is.Nil(meta): + return nil, fmt.Errorf("%s: missing meta: %w", op, ErrInvalidParameter) + case is.Nil(sessionMeta): return nil, fmt.Errorf("%s: missing session meta: %w", op, ErrInvalidParameter) case meta.Id == "": return nil, fmt.Errorf("%s: missing session id: %w", op, ErrInvalidParameter) - case is.Nil(meta.StaticHost) && is.Nil(meta.DynamicHost): + case is.Nil(sessionMeta.StaticHost) && is.Nil(sessionMeta.DynamicHost): return nil, fmt.Errorf("%s: missing session host information: %w", op, ErrInvalidParameter) - case !is.Nil(meta.StaticHost) && !is.Nil(meta.DynamicHost): - return nil, fmt.Errorf("%s: meta cannot contain both static and dynamic host information: %w", op, ErrInvalidParameter) - case len(meta.StaticCredentialStore) == 0 && len(meta.VaultCredentialStore) == 0: + case !is.Nil(sessionMeta.StaticHost) && !is.Nil(sessionMeta.DynamicHost): + return nil, fmt.Errorf("%s: sessionMeta cannot contain both static and dynamic host information: %w", op, ErrInvalidParameter) + case len(sessionMeta.StaticCredentialStore) == 0 && len(sessionMeta.VaultCredentialStore) == 0: return nil, fmt.Errorf("%s: missing credential information: %w", op, ErrInvalidParameter) - case is.Nil(meta.User): + case is.Nil(sessionMeta.User): return nil, fmt.Errorf("%s: missing session user: %w", op, ErrInvalidParameter) - case is.Nil(meta.Target): + case is.Nil(sessionMeta.Target): return nil, fmt.Errorf("%s: missing session target: %w", op, ErrInvalidParameter) - case is.Nil(meta.Worker): + case is.Nil(sessionMeta.Worker): return nil, fmt.Errorf("%s: missing session worker: %w", op, ErrInvalidParameter) case is.Nil(f): return nil, fmt.Errorf("%s: missing storage fs: %w", op, ErrInvalidParameter) @@ -532,6 +90,23 @@ func NewSession(ctx context.Context, meta *SessionMeta, f storage.FS, keys *kms. return nil, errors.Join(err, fmt.Errorf("%s: %w", op, ErrBsrKeyPersistenceFailure)) } + sm, err := nc.create(ctx, sessionMetaFile) + if err != nil { + return nil, err + } + smw, err := checksum.NewFile(ctx, sm, nc.checksums) + if err != nil { + return nil, err + } + enc := json.NewEncoder(smw) + if err := enc.Encode(sessionMeta); err != nil { + smw.Close() + return nil, err + } + if err := smw.Close(); err != nil { + return nil, err + } + err = meta.writeMeta(ctx, nc) if err != nil { return nil, err @@ -541,6 +116,7 @@ func NewSession(ctx context.Context, meta *SessionMeta, f storage.FS, keys *kms. container: nc, multiplexed: opts.withSupportsMultiplex, Meta: meta, + SessionMeta: sessionMeta, }, nil } @@ -618,12 +194,12 @@ func OpenSession(ctx context.Context, sessionRecordingId string, f storage.FS, k return nil, err } - // Load and verify session metadata + // Load and verify recording metadata sha256Reader, err := crypto.NewSha256SumReader(ctx, cc.metaFile) if err != nil { return nil, fmt.Errorf("%s: %w", op, err) } - sm, err := decodeSessionMeta(ctx, sha256Reader) + meta, err := decodeSessionRecordingMeta(ctx, sha256Reader) if err != nil { return nil, err } @@ -632,9 +208,15 @@ func OpenSession(ctx context.Context, sessionRecordingId string, f storage.FS, k return nil, err } + sessionMeta := &SessionMeta{} + if err := cc.decodeJsonFile(ctx, sessionMetaFile, sessionMeta); err != nil { + return nil, err + } + session := &Session{ - container: cc, - Meta: sm, + container: cc, + Meta: meta, + SessionMeta: sessionMeta, } return session, nil @@ -645,66 +227,17 @@ func (s *Session) Close(ctx context.Context) error { return s.container.close(ctx) } -// ConnectionMeta contains metadata about a connection in a BSR. -type ConnectionMeta struct { - Id string - channels map[string]bool -} - -func (c ConnectionMeta) isValid() bool { - switch { - case c.Id == "": - return false - default: - return true - } -} - // Connection is a container in a bsr for a specific connection in a session // container. It contains the files for the recorded connection. type Connection struct { *container multiplexed bool - Meta *ConnectionMeta -} - -// decodeConnectionMeta will populate the ConnectionMeta for a BSR Connection -// TODO Unmarshal without brute force -func decodeConnectionMeta(ctx context.Context, r io.Reader) (*ConnectionMeta, error) { - const op = "bsr.decodeConnectionMeta" - - switch { - case r == nil: - return nil, fmt.Errorf("%s: missing connection meta file: %w", op, ErrInvalidParameter) - } - c := &ConnectionMeta{} - channels := make(map[string]bool) - scanner := bufio.NewScanner(r) - for scanner.Scan() { - l := scanner.Text() - if len(l) == 0 { - continue - } - k, v, ok := strings.Cut(l, ":") - if !ok { - return nil, fmt.Errorf("%s: connection meta file contains invalid entry; expecting k: v pair:%s", op, l) - } - v = strings.TrimSpace(v) - switch { - case k == "id": - c.Id = v - case k == "channel": - channels[v] = true - } - } - c.channels = channels - - return c, nil + Meta *ConnectionRecordingMeta } // NewConnection creates a Connection container for a given connection id. -func (s *Session) NewConnection(ctx context.Context, meta *ConnectionMeta) (*Connection, error) { +func (s *Session) NewConnection(ctx context.Context, meta *ConnectionRecordingMeta) (*Connection, error) { const op = "bsr.(Session).NewConnection" switch { @@ -770,7 +303,7 @@ func (s *Session) OpenConnection(ctx context.Context, connId string) (*Connectio if err != nil { return nil, fmt.Errorf("%s: %w", op, err) } - sm, err := decodeConnectionMeta(ctx, sha256Reader) + sm, err := decodeConnectionRecordingMeta(ctx, sha256Reader) if err != nil { return nil, err } @@ -787,58 +320,8 @@ func (s *Session) OpenConnection(ctx context.Context, connId string) (*Connectio return connection, nil } -// ChannelMeta contains metadata about a channel in a BSR. -type ChannelMeta struct { - Id string - Type string -} - -func (c ChannelMeta) isValid() bool { - switch { - case c.Id == "": - return false - case c.Type == "": - return false - default: - return true - } -} - -// decodeChannelMeta will populate the ChannelMeta for a BSR Channel -// TODO Unmarshal without brute force -func decodeChannelMeta(ctx context.Context, r io.Reader) (*ChannelMeta, error) { - const op = "bsr.decodeChannelMeta" - - switch { - case r == nil: - return nil, fmt.Errorf("%s: missing channel meta file: %w", op, ErrInvalidParameter) - } - - c := &ChannelMeta{} - scanner := bufio.NewScanner(r) - for scanner.Scan() { - l := scanner.Text() - if len(l) == 0 { - continue - } - k, v, ok := strings.Cut(l, ":") - if !ok { - return nil, fmt.Errorf("%s: channel meta file contains invalid entry; expecting k: v pair:%s", op, l) - } - v = strings.TrimSpace(v) - switch { - case k == "id": - c.Id = v - case k == "channelType": - c.Type = v - } - } - - return c, nil -} - // NewChannel creates a Channel container for a given channel id. -func (c *Connection) NewChannel(ctx context.Context, meta *ChannelMeta) (*Channel, error) { +func (c *Connection) NewChannel(ctx context.Context, meta *ChannelRecordingMeta) (*Channel, error) { const op = "bsr.(Connection).NewChannel" if !c.multiplexed { @@ -908,7 +391,7 @@ func (c *Connection) OpenChannel(ctx context.Context, chanId string) (*Channel, if err != nil { return nil, fmt.Errorf("%s: %w", op, err) } - sm, err := decodeChannelMeta(ctx, sha256Reader) + sm, err := decodeChannelRecordingMeta(ctx, sha256Reader) if err != nil { return nil, err } @@ -979,7 +462,7 @@ func (c *Connection) Close(ctx context.Context) error { type Channel struct { *container - Meta *ChannelMeta + Meta *ChannelRecordingMeta } // Close closes the Channel container. diff --git a/internal/bsr/bsr_open_test.go b/internal/bsr/bsr_open_test.go index a4f713120..74c765d00 100644 --- a/internal/bsr/bsr_open_test.go +++ b/internal/bsr/bsr_open_test.go @@ -10,7 +10,9 @@ import ( "github.com/hashicorp/boundary/internal/bsr/internal/fstest" "github.com/hashicorp/boundary/internal/bsr/kms" "github.com/hashicorp/boundary/internal/storage" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "gopkg.in/square/go-jose.v2/json" ) func TestPopulateMeta(t *testing.T) { @@ -19,34 +21,42 @@ func TestPopulateMeta(t *testing.T) { keys, err := kms.CreateKeys(ctx, kms.TestWrapper(t), "session") require.NoError(t, err) - sessionId := "session" - sessionProtocol := Protocol("TEST") + sessionId := "s_01234567890" + srm := &SessionRecordingMeta{ + Id: "sr_012344567890", + Protocol: Protocol("TEST"), + } // Populate session meta - sessionMeta := TestSessionMeta(sessionId, sessionProtocol) - s, err := NewSession(ctx, sessionMeta, &fstest.MemFS{}, keys, WithSupportsMultiplex(true)) + sessionMeta := TestSessionMeta(sessionId) + s, err := NewSession(ctx, srm, sessionMeta, &fstest.MemFS{}, keys, WithSupportsMultiplex(true)) require.NoError(t, err) require.NotNil(t, s) // Reset meta and populate it from the meta file - s.Meta = &SessionMeta{} - sm, err := decodeSessionMeta(ctx, s.container.meta) + s.Meta = &SessionRecordingMeta{} + sm, err := decodeSessionRecordingMeta(ctx, s.container.meta) require.NoError(t, err) s.Meta = sm - require.Equal(t, s.Meta.Id, sessionId) - require.Equal(t, s.Meta.Protocol, sessionProtocol) - require.Equal(t, s.Meta.Target, sessionMeta.Target) - require.Equal(t, s.Meta.User, sessionMeta.User) - require.Equal(t, s.Meta.StaticHost, sessionMeta.StaticHost) + require.Equal(t, s.Meta.Id, srm.Id) + require.Equal(t, s.Meta.Protocol, srm.Protocol) + + gotSessionMeta := &SessionMeta{} + r, err := s.container.container.OpenFile(ctx, sessionMetaFile) + require.NoError(t, err) + dec := json.NewDecoder(r) + err = dec.Decode(gotSessionMeta) + require.NoError(t, err) + assert.Equal(t, sessionMeta, gotSessionMeta) // Populate connection meta connectionId := "connection" - c, err := s.NewConnection(ctx, &ConnectionMeta{Id: connectionId}) + c, err := s.NewConnection(ctx, &ConnectionRecordingMeta{Id: connectionId}) require.NoError(t, err) require.NotNil(t, c) - c.Meta = &ConnectionMeta{} - cm, err := decodeConnectionMeta(ctx, c.container.meta) + c.Meta = &ConnectionRecordingMeta{} + cm, err := decodeConnectionRecordingMeta(ctx, c.container.meta) require.NoError(t, err) c.Meta = cm require.Equal(t, c.Meta.Id, connectionId) @@ -54,12 +64,12 @@ func TestPopulateMeta(t *testing.T) { // Populate channel meta channelId := "channel" channelType := "mythical" - ch, err := c.NewChannel(ctx, &ChannelMeta{Id: channelId, Type: channelType}) + ch, err := c.NewChannel(ctx, &ChannelRecordingMeta{Id: channelId, Type: channelType}) require.NoError(t, err) require.NotNil(t, ch) - ch.Meta = &ChannelMeta{} - chM, err := decodeChannelMeta(ctx, ch.container.meta) + ch.Meta = &ChannelRecordingMeta{} + chM, err := decodeChannelRecordingMeta(ctx, ch.container.meta) require.NoError(t, err) ch.Meta = chM require.Equal(t, ch.Meta.Id, channelId) @@ -73,21 +83,25 @@ func TestOpenBSRMethods(t *testing.T) { require.NoError(t, err) f := &fstest.MemFS{} - sessionId := "session" - sessionMeta := TestSessionMeta(sessionId, Protocol("test")) + sessionId := "s_01234567890" + srm := &SessionRecordingMeta{ + Id: "sr_012344567890", + Protocol: Protocol("TEST"), + } + sessionMeta := TestSessionMeta(sessionId) - sesh, err := NewSession(ctx, sessionMeta, f, keys, WithSupportsMultiplex(true)) + sesh, err := NewSession(ctx, srm, sessionMeta, f, keys, WithSupportsMultiplex(true)) require.NoError(t, err) require.NotNil(t, sesh) connectionId := "connection" - connMeta := &ConnectionMeta{Id: connectionId} + connMeta := &ConnectionRecordingMeta{Id: connectionId} conn, err := sesh.NewConnection(ctx, connMeta) require.NoError(t, err) require.NotNil(t, conn) channelId := "channel" - chanMeta := &ChannelMeta{ + chanMeta := &ChannelRecordingMeta{ Id: channelId, Type: "chan", } @@ -107,7 +121,7 @@ func TestOpenBSRMethods(t *testing.T) { return u, nil } - opSesh, err := OpenSession(ctx, sessionId, f, keyFn) + opSesh, err := OpenSession(ctx, srm.Id, f, keyFn) require.NoError(t, err) require.NotNil(t, opSesh) sesh.Meta.connections = opSesh.Meta.connections @@ -191,8 +205,13 @@ func TestOpenConnection(t *testing.T) { connMap := make(map[string]bool) connMap["connection"] = true - meta := TestSessionMeta("sessionId", Protocol("test")) - meta.connections = connMap + sessionId := "s_01234567890" + srm := &SessionRecordingMeta{ + Id: "sr_012344567890", + Protocol: Protocol("TEST"), + connections: connMap, + } + meta := TestSessionMeta(sessionId) cases := []struct { name string @@ -216,7 +235,8 @@ func TestOpenConnection(t *testing.T) { for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { sesh := Session{ - Meta: meta, + Meta: srm, + SessionMeta: meta, } got, err := sesh.OpenConnection(ctx, tc.id) if tc.wantErr { @@ -258,7 +278,7 @@ func TestOpenChannel(t *testing.T) { for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { conn := Connection{ - Meta: &ConnectionMeta{ + Meta: &ConnectionRecordingMeta{ channels: chanMap, }, } diff --git a/internal/bsr/bsr_test.go b/internal/bsr/bsr_test.go index 06c81dcc9..dee324c64 100644 --- a/internal/bsr/bsr_test.go +++ b/internal/bsr/bsr_test.go @@ -5,6 +5,7 @@ package bsr_test import ( "context" + "encoding/json" "errors" "fmt" "io" @@ -32,18 +33,18 @@ func assertContainer(ctx context.Context, t *testing.T, path, state string, typ td := filepath.Join("testdata", t.Name(), state, path) // meta - wantMeta, err := os.ReadFile(filepath.Join(td, string(typ)+".meta")) + wantMeta, err := os.ReadFile(filepath.Join(td, string(typ)+"-recording.meta")) require.NoError(t, err, "unable to find test data file") - meta, ok := fs.Files[string(typ)+".meta"] + meta, ok := fs.Files[string(typ)+"-recording.meta"] require.True(t, ok, "container is missing meta file") wantChecksumsRegex, err := regexp.Compile(string(wantMeta)) require.NoError(t, err) assert.True(t, wantChecksumsRegex.MatchString(meta.Buf.String())) // summary - wantSummary, err := os.ReadFile(filepath.Join(td, string(typ)+".summary")) + wantSummary, err := os.ReadFile(filepath.Join(td, string(typ)+"-recording-summary.json")) require.NoError(t, err, "unable to find test data file") - summary, ok := fs.Files[string(typ)+".summary"] + summary, ok := fs.Files[string(typ)+"-recording-summary.json"] require.True(t, ok, "container is missing summary file") assert.Equal(t, string(wantSummary), summary.Buf.String()) @@ -60,7 +61,7 @@ func assertContainer(ctx context.Context, t *testing.T, path, state string, typ wChecksumsRegex = append(wChecksumsRegex, r) } for _, cr := range wChecksumsRegex { - assert.True(t, cr.MatchString(checksums.Buf.String())) + assert.True(t, cr.MatchString(checksums.Buf.String()), "got:\n%s\nmust match:\n%s", checksums.Buf.String(), cr.String()) } // SHA256SUM.sig signature file @@ -99,7 +100,7 @@ func assertContainer(ctx context.Context, t *testing.T, path, state string, typ wJournalRegex = append(wJournalRegex, r) } for _, cr := range wJournalRegex { - assert.True(t, cr.MatchString(journal.Buf.String())) + assert.True(t, cr.MatchString(journal.Buf.String()), "got:\n%s\nmust match:\n%s", journal.Buf.String(), cr.String()) } if typ == "session" { @@ -123,6 +124,14 @@ func assertContainer(ctx context.Context, t *testing.T, path, state string, typ pubKeySelfSignature, ok := fs.Files["pubKeySelfSignature.sign"] require.True(t, ok, "container is missing pubKeySelfSignature.sign file") assert.NotEmpty(t, pubKeySelfSignature.Buf.String()) + + sessionMeta, ok := fs.Files["session-meta.json"] + require.True(t, ok, "container is missing session-meta.json file") + assert.NotEmpty(t, sessionMeta.Buf.String()) + sm := &bsr.SessionMeta{} + err = json.Unmarshal(sessionMeta.Buf.Bytes(), sm) + require.NoError(t, err) + assert.Equal(t, sm, bsr.TestSessionMeta(strings.ReplaceAll(fs.Name, ".bsr", ""))) } } @@ -233,8 +242,12 @@ func TestBsr(t *testing.T) { for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { - sessionMeta := bsr.TestSessionMeta(tc.id, "TEST") - s, err := bsr.NewSession(ctx, sessionMeta, tc.c, tc.keys, tc.opts...) + srm := &bsr.SessionRecordingMeta{ + Id: tc.id, + Protocol: bsr.Protocol("TEST"), + } + sessionMeta := bsr.TestSessionMeta(tc.id) + s, err := bsr.NewSession(ctx, srm, sessionMeta, tc.c, tc.keys, tc.opts...) require.NoError(t, err) require.NotNil(t, s) @@ -247,7 +260,7 @@ func TestBsr(t *testing.T) { // create all the things for _, conn := range tc.conns { - c, err := s.NewConnection(ctx, &bsr.ConnectionMeta{Id: conn.id}) + c, err := s.NewConnection(ctx, &bsr.ConnectionRecordingMeta{Id: conn.id}) require.NoError(t, err) require.NotNil(t, c) @@ -281,7 +294,7 @@ func TestBsr(t *testing.T) { createdChannels := make([]*channel, 0, len(conn.channels)) for _, chann := range conn.channels { - ch, err := c.NewChannel(ctx, &bsr.ChannelMeta{Id: chann.id, Type: "chan"}) + ch, err := c.NewChannel(ctx, &bsr.ChannelRecordingMeta{Id: chann.id, Type: "chan"}) require.NoError(t, err) require.NotNil(t, ch) @@ -370,43 +383,57 @@ func TestNewSessionErrors(t *testing.T) { require.NoError(t, err) cases := []struct { - name string - meta *bsr.SessionMeta - f storage.FS - keys *kms.Keys - wantError error + name string + meta *bsr.SessionRecordingMeta + sessionMeta *bsr.SessionMeta + f storage.FS + keys *kms.Keys + wantError error }{ { "nil-meta", nil, + bsr.TestSessionMeta("session"), + &fstest.MemFS{}, + keys, + errors.New("bsr.NewSession: missing meta: invalid parameter"), + }, + { + "nil-session-meta", + bsr.TestSessionRecordingMeta("session_recording", bsr.Protocol("TEST")), + nil, &fstest.MemFS{}, keys, errors.New("bsr.NewSession: missing session meta: invalid parameter"), }, { "empty-session-id", - bsr.TestSessionMeta("", "TEST"), + bsr.TestSessionRecordingMeta("", bsr.Protocol("TEST")), + bsr.TestSessionMeta("session"), &fstest.MemFS{}, keys, errors.New("bsr.NewSession: missing session id: invalid parameter"), }, { "nil-fs", - bsr.TestSessionMeta("session", "TEST"), + bsr.TestSessionRecordingMeta("session_recording", bsr.Protocol("TEST")), + bsr.TestSessionMeta("session"), nil, keys, errors.New("bsr.NewSession: missing storage fs: invalid parameter"), }, { "nil-keys", - bsr.TestSessionMeta("session", "TEST"), + bsr.TestSessionRecordingMeta("session_recording", bsr.Protocol("TEST")), + bsr.TestSessionMeta("session"), &fstest.MemFS{}, nil, errors.New("bsr.NewSession: missing kms keys: invalid parameter"), }, { "missing-bsr-signature", - bsr.TestSessionMeta("session", "TEST"), + bsr.TestSessionRecordingMeta("session_recording", bsr.Protocol("TEST")), + bsr.TestSessionMeta("session"), &fstest.MemFS{}, &kms.Keys{ PubKey: keys.PubKey, @@ -418,7 +445,8 @@ func TestNewSessionErrors(t *testing.T) { }, { "missing-pub-signature", - bsr.TestSessionMeta("session", "TEST"), + bsr.TestSessionRecordingMeta("session_recording", bsr.Protocol("TEST")), + bsr.TestSessionMeta("session"), &fstest.MemFS{}, &kms.Keys{ PubKey: keys.PubKey, @@ -430,7 +458,8 @@ func TestNewSessionErrors(t *testing.T) { }, { "missing-pub-key", - bsr.TestSessionMeta("session", "TEST"), + bsr.TestSessionRecordingMeta("session_recording", bsr.Protocol("TEST")), + bsr.TestSessionMeta("session"), &fstest.MemFS{}, &kms.Keys{ WrappedBsrKey: keys.WrappedBsrKey, @@ -442,7 +471,8 @@ func TestNewSessionErrors(t *testing.T) { }, { "missing-wrapped-bsr-key", - bsr.TestSessionMeta("session", "TEST"), + bsr.TestSessionRecordingMeta("session_recording", bsr.Protocol("TEST")), + bsr.TestSessionMeta("session"), &fstest.MemFS{}, &kms.Keys{ PubKey: keys.PubKey, @@ -454,7 +484,8 @@ func TestNewSessionErrors(t *testing.T) { }, { "missing-wrapped-priv-key", - bsr.TestSessionMeta("session", "TEST"), + bsr.TestSessionRecordingMeta("session_recording", bsr.Protocol("TEST")), + bsr.TestSessionMeta("session"), &fstest.MemFS{}, &kms.Keys{ PubKey: keys.PubKey, @@ -466,7 +497,8 @@ func TestNewSessionErrors(t *testing.T) { }, { "fs-new-error", - bsr.TestSessionMeta("session", "TEST"), + bsr.TestSessionRecordingMeta("session_recording", bsr.Protocol("TEST")), + bsr.TestSessionMeta("session"), fstest.NewMemFS(fstest.WithNewFunc(func(_ context.Context, _ string) (storage.Container, error) { return nil, errors.New("fs new error") })), @@ -477,7 +509,7 @@ func TestNewSessionErrors(t *testing.T) { for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { - _, err := bsr.NewSession(ctx, tc.meta, tc.f, tc.keys) + _, err := bsr.NewSession(ctx, tc.meta, tc.sessionMeta, tc.f, tc.keys) require.Error(t, err) assert.EqualError(t, err, tc.wantError.Error()) }) @@ -493,13 +525,13 @@ func TestNewConnectionErrors(t *testing.T) { cases := []struct { name string session *bsr.Session - meta *bsr.ConnectionMeta + meta *bsr.ConnectionRecordingMeta wantError error }{ { "nil-meta", func() *bsr.Session { - s, err := bsr.NewSession(ctx, bsr.TestSessionMeta("session", "TEST"), &fstest.MemFS{}, keys) + s, err := bsr.NewSession(ctx, bsr.TestSessionRecordingMeta("session_recording", bsr.Protocol("TEST")), bsr.TestSessionMeta("session"), &fstest.MemFS{}, keys) require.NoError(t, err) return s }(), @@ -509,11 +541,11 @@ func TestNewConnectionErrors(t *testing.T) { { "empty-connection-id", func() *bsr.Session { - s, err := bsr.NewSession(ctx, bsr.TestSessionMeta("session", "TEST"), &fstest.MemFS{}, keys) + s, err := bsr.NewSession(ctx, bsr.TestSessionRecordingMeta("session_recording", bsr.Protocol("TEST")), bsr.TestSessionMeta("session"), &fstest.MemFS{}, keys) require.NoError(t, err) return s }(), - &bsr.ConnectionMeta{Id: ""}, + &bsr.ConnectionRecordingMeta{Id: ""}, errors.New("bsr.(Session).NewConnection: missing connection id: invalid parameter"), }, } @@ -533,20 +565,21 @@ func TestNewChannelErrors(t *testing.T) { keys, err := kms.CreateKeys(ctx, kms.TestWrapper(t), "session") require.NoError(t, err) - sessionMeta := bsr.TestSessionMeta("session", "TEST") + srm := bsr.TestSessionRecordingMeta("session_recording", bsr.Protocol("TEST")) + sessionMeta := bsr.TestSessionMeta("session") cases := []struct { name string connection *bsr.Connection - meta *bsr.ChannelMeta + meta *bsr.ChannelRecordingMeta wantError error }{ { "nil-meta", func() *bsr.Connection { - s, err := bsr.NewSession(ctx, sessionMeta, &fstest.MemFS{}, keys, bsr.WithSupportsMultiplex(true)) + s, err := bsr.NewSession(ctx, srm, sessionMeta, &fstest.MemFS{}, keys, bsr.WithSupportsMultiplex(true)) require.NoError(t, err) - c, err := s.NewConnection(ctx, &bsr.ConnectionMeta{Id: "connection"}) + c, err := s.NewConnection(ctx, &bsr.ConnectionRecordingMeta{Id: "connection"}) require.NoError(t, err) return c }(), @@ -556,40 +589,40 @@ func TestNewChannelErrors(t *testing.T) { { "empty-connection-id", func() *bsr.Connection { - s, err := bsr.NewSession(ctx, sessionMeta, &fstest.MemFS{}, keys, bsr.WithSupportsMultiplex(true)) + s, err := bsr.NewSession(ctx, srm, sessionMeta, &fstest.MemFS{}, keys, bsr.WithSupportsMultiplex(true)) require.NoError(t, err) - c, err := s.NewConnection(ctx, &bsr.ConnectionMeta{Id: "connection"}) + c, err := s.NewConnection(ctx, &bsr.ConnectionRecordingMeta{Id: "connection"}) require.NoError(t, err) return c }(), - &bsr.ChannelMeta{Id: ""}, + &bsr.ChannelRecordingMeta{Id: ""}, errors.New("bsr.(Connection).NewChannel: missing channel id: invalid parameter"), }, { "not-multiplexed", func() *bsr.Connection { - s, err := bsr.NewSession(ctx, sessionMeta, &fstest.MemFS{}, keys, bsr.WithSupportsMultiplex(false)) + s, err := bsr.NewSession(ctx, srm, sessionMeta, &fstest.MemFS{}, keys, bsr.WithSupportsMultiplex(false)) require.NoError(t, err) - c, err := s.NewConnection(ctx, &bsr.ConnectionMeta{Id: "connection"}) + c, err := s.NewConnection(ctx, &bsr.ConnectionRecordingMeta{Id: "connection"}) require.NoError(t, err) return c }(), - &bsr.ChannelMeta{Id: ""}, + &bsr.ChannelRecordingMeta{Id: ""}, errors.New("bsr.(Connection).NewChannel: connection cannot make channels: not supported by protocol"), }, { "not-multiplexed-default", func() *bsr.Connection { - s, err := bsr.NewSession(ctx, sessionMeta, &fstest.MemFS{}, keys) + s, err := bsr.NewSession(ctx, srm, sessionMeta, &fstest.MemFS{}, keys) require.NoError(t, err) - c, err := s.NewConnection(ctx, &bsr.ConnectionMeta{Id: "connection"}) + c, err := s.NewConnection(ctx, &bsr.ConnectionRecordingMeta{Id: "connection"}) require.NoError(t, err) return c }(), - &bsr.ChannelMeta{Id: ""}, + &bsr.ChannelRecordingMeta{Id: ""}, errors.New("bsr.(Connection).NewChannel: connection cannot make channels: not supported by protocol"), }, } @@ -609,7 +642,8 @@ func TestChannelNewMessagesWriterErrors(t *testing.T) { keys, err := kms.CreateKeys(ctx, kms.TestWrapper(t), "session") require.NoError(t, err) - sessionMeta := bsr.TestSessionMeta("session", "TEST") + srm := bsr.TestSessionRecordingMeta("session_recording", bsr.Protocol("TEST")) + sessionMeta := bsr.TestSessionMeta("session") cases := []struct { name string channel *bsr.Channel @@ -619,13 +653,13 @@ func TestChannelNewMessagesWriterErrors(t *testing.T) { { "invalid-dir", func() *bsr.Channel { - s, err := bsr.NewSession(ctx, sessionMeta, &fstest.MemFS{}, keys, bsr.WithSupportsMultiplex(true)) + s, err := bsr.NewSession(ctx, srm, sessionMeta, &fstest.MemFS{}, keys, bsr.WithSupportsMultiplex(true)) require.NoError(t, err) - c, err := s.NewConnection(ctx, &bsr.ConnectionMeta{Id: "connection"}) + c, err := s.NewConnection(ctx, &bsr.ConnectionRecordingMeta{Id: "connection"}) require.NoError(t, err) - ch, err := c.NewChannel(ctx, &bsr.ChannelMeta{Id: "channel"}) + ch, err := c.NewChannel(ctx, &bsr.ChannelRecordingMeta{Id: "channel"}) require.NoError(t, err) return ch }(), @@ -649,7 +683,8 @@ func TestChannelNewRequestsWriterErrors(t *testing.T) { keys, err := kms.CreateKeys(ctx, kms.TestWrapper(t), "session") require.NoError(t, err) - sessionMeta := bsr.TestSessionMeta("session", "TEST") + srm := bsr.TestSessionRecordingMeta("session_recording", bsr.Protocol("TEST")) + sessionMeta := bsr.TestSessionMeta("session") cases := []struct { name string channel *bsr.Channel @@ -659,13 +694,13 @@ func TestChannelNewRequestsWriterErrors(t *testing.T) { { "invalid-dir", func() *bsr.Channel { - s, err := bsr.NewSession(ctx, sessionMeta, &fstest.MemFS{}, keys, bsr.WithSupportsMultiplex(true)) + s, err := bsr.NewSession(ctx, srm, sessionMeta, &fstest.MemFS{}, keys, bsr.WithSupportsMultiplex(true)) require.NoError(t, err) - c, err := s.NewConnection(ctx, &bsr.ConnectionMeta{Id: "connection"}) + c, err := s.NewConnection(ctx, &bsr.ConnectionRecordingMeta{Id: "connection"}) require.NoError(t, err) - ch, err := c.NewChannel(ctx, &bsr.ChannelMeta{Id: "channel"}) + ch, err := c.NewChannel(ctx, &bsr.ChannelRecordingMeta{Id: "channel"}) require.NoError(t, err) return ch }(), diff --git a/internal/bsr/container.go b/internal/bsr/container.go index d77e48bb7..9d92ca8a8 100644 --- a/internal/bsr/container.go +++ b/internal/bsr/container.go @@ -6,6 +6,7 @@ package bsr import ( "bytes" "context" + "encoding/json" "errors" "fmt" "io" @@ -22,8 +23,8 @@ import ( ) const ( - metaFile = "%s.meta" - summaryFile = "%s.summary" + metaFile = "%s-recording.meta" + summaryFile = "%s-recording-summary.json" checksumFile = "SHA256SUM" sigFile = "SHA256SUM.sig" journalFile = ".journal" @@ -45,12 +46,13 @@ type container struct { container storage.Container // Fields primarily used for writing - journal *journal.Journal - sumName string - meta *checksum.File - sum *checksum.File - checksums *sign.File - sigs storage.File + journal *journal.Journal + sumName string + meta *checksum.File + sum *checksum.File + sumEncoder *json.Encoder + checksums *sign.File + sigs storage.File // Field used for reading shaSums checksum.Sha256Sums @@ -113,6 +115,8 @@ func newContainer(ctx context.Context, t containerType, c storage.Container, key if err != nil { return nil, err } + cc.sumEncoder = json.NewEncoder(cc.sum) + cc.sumEncoder.SetIndent("", " ") return cc, nil } @@ -369,6 +373,53 @@ func (c *container) create(ctx context.Context, s string, options ...storage.Opt return jf, nil } +func (c *container) decodeJsonFile(ctx context.Context, s string, v any) error { + const op = "bsr.(container).decodeJsonFile" + + return c.decodeFile(ctx, s, func(_ context.Context, r io.Reader) error { + dec := json.NewDecoder(r) + if err := dec.Decode(v); err != nil { + return fmt.Errorf("%s: %w", op, err) + } + return nil + }) +} + +func (c *container) decodeFile(ctx context.Context, s string, fn func(context.Context, io.Reader) error) error { + const op = "bsr.(container).decodeFile" + + expectedSum, err := c.shaSums.Sum(s) + if err != nil { + return fmt.Errorf("%s: %w", op, err) + } + + f, err := c.container.OpenFile(ctx, s) + if err != nil { + return fmt.Errorf("%s: %w", op, err) + } + sha256Reader, err := crypto.NewSha256SumReader(ctx, f) + if err != nil { + f.Close() + return fmt.Errorf("%s: %w", op, err) + } + defer sha256Reader.Close() + + if err := fn(ctx, sha256Reader); err != nil { + return fmt.Errorf("%s: %w", op, err) + } + + gotSum, err := sha256Reader.Sum(ctx, crypto.WithHexEncoding(true)) + if err != nil { + return fmt.Errorf("%s: %w", op, err) + } + + if !bytes.Equal(expectedSum, gotSum) { + return fmt.Errorf("%s: checksum did not match expected value", op) + } + + return nil +} + // syncBsrKey will take the marshalled bytes of a key, write its contents to a local file, // and then close it. The key file is created using the synchronous storage option, so // close will block until the file is synced to remote storage @@ -421,19 +472,9 @@ func (c *container) WriteMeta(_ context.Context, k, v string) (int, error) { return c.meta.WriteString(fmt.Sprintf("%s: %s\n", k, v)) } -// writeSummaryString writes a string to the container's summary file. -func (c *container) writeSummaryString(_ context.Context, s string) (int, error) { - return c.sum.WriteString(s) -} - -// WriteSummary writes a new line terminated key : value pair to the container's summary file -func (c *container) WriteSummary(_ context.Context, k, v string) (int, error) { - return c.sum.WriteString(fmt.Sprintf("%s: %s\n", k, v)) -} - -// WriteSummaryLine writes a new line terminated string to the container's summary file. -func (c *container) WriteSummaryLine(_ context.Context, s string) (int, error) { - return c.sum.WriteString(s + "\n") +// EncodeSummary writes a new line terminated key : value pair to the container's summary file +func (c *container) EncodeSummary(_ context.Context, s any) error { + return c.sumEncoder.Encode(s) } // WriteBinaryChecksum writes a checksum for a binary file to the checksum file. diff --git a/internal/bsr/meta.go b/internal/bsr/meta.go deleted file mode 100644 index 2c818033a..000000000 --- a/internal/bsr/meta.go +++ /dev/null @@ -1,787 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package bsr - -import ( - "context" - "fmt" - "strconv" -) - -// Scope contains information about the scope of a Boundary domain object -type Scope struct { - PublicId string - Name string // optional - Description string // optional - Type string - ParentId string // optional - PrimaryAuthMethodId string // optional -} - -func (s Scope) writeMeta(ctx context.Context, c *container, domainObj string) error { - _, err := c.WriteMeta(ctx, fmt.Sprintf("%s_scope_publicId", domainObj), s.PublicId) - if err != nil { - return err - } - if s.Name != "" { - _, err = c.WriteMeta(ctx, fmt.Sprintf("%s_scope_name", domainObj), s.Name) - if err != nil { - return err - } - } - if s.Description != "" { - _, err = c.WriteMeta(ctx, fmt.Sprintf("%s_scope_description", domainObj), s.Description) - if err != nil { - return err - } - } - _, err = c.WriteMeta(ctx, fmt.Sprintf("%s_scope_type", domainObj), s.Type) - if err != nil { - return err - } - if s.ParentId != "" { - _, err = c.WriteMeta(ctx, fmt.Sprintf("%s_scope_parentId", domainObj), s.ParentId) - if err != nil { - return err - } - } - if s.PrimaryAuthMethodId != "" { - _, err = c.WriteMeta(ctx, fmt.Sprintf("%s_scope_primaryAuthMethodId", domainObj), s.PrimaryAuthMethodId) - if err != nil { - return err - } - } - - return nil -} - -// User contains information about user who initiated this session -type User struct { - PublicId string - Scope Scope - Name string // optional field - Description string // optional field -} - -func (u User) writeMeta(ctx context.Context, c *container) error { - _, err := c.WriteMeta(ctx, "user_publicId", u.PublicId) - if err != nil { - return err - } - err = u.Scope.writeMeta(ctx, c, "user") - if err != nil { - return err - } - if u.Name != "" { - _, err = c.WriteMeta(ctx, "user_name", u.Name) - if err != nil { - return err - } - } - if u.Description != "" { - _, err = c.WriteMeta(ctx, "user_description", u.Description) - if err != nil { - return err - } - } - - return nil -} - -// Target contains information about the target for this session -type Target struct { - PublicId string - Scope Scope - Name string // optional field - Description string // optional field - DefaultPort uint32 - SessionMaxSeconds uint32 - SessionConnectionLimit int32 - WorkerFilter string // optional field - EgressWorkerFilter string // optional field - IngressWorkerFilter string // optional field -} - -func (t Target) writeMeta(ctx context.Context, c *container) error { - _, err := c.WriteMeta(ctx, "target_publicId", t.PublicId) - if err != nil { - return err - } - err = t.Scope.writeMeta(ctx, c, "target") - if err != nil { - return err - } - if t.Name != "" { - _, err = c.WriteMeta(ctx, "target_name", t.Name) - if err != nil { - return err - } - } - if t.Description != "" { - _, err = c.WriteMeta(ctx, "target_description", t.Description) - if err != nil { - return err - } - } - _, err = c.WriteMeta(ctx, "target_defaultPort", fmt.Sprintf("%d", t.DefaultPort)) - if err != nil { - return err - } - _, err = c.WriteMeta(ctx, "target_sessionMaxSeconds", fmt.Sprintf("%d", t.SessionMaxSeconds)) - if err != nil { - return err - } - _, err = c.WriteMeta(ctx, "target_sessionConnectionLimit", fmt.Sprintf("%d", t.SessionConnectionLimit)) - if err != nil { - return err - } - if t.WorkerFilter != "" { - _, err = c.WriteMeta(ctx, "target_workerFilter", t.WorkerFilter) - if err != nil { - return err - } - } - if t.IngressWorkerFilter != "" { - _, err = c.WriteMeta(ctx, "target_ingressWorkerFilter", t.IngressWorkerFilter) - if err != nil { - return err - } - } - if t.EgressWorkerFilter != "" { - _, err = c.WriteMeta(ctx, "target_egressWorkerFilter", t.EgressWorkerFilter) - if err != nil { - return err - } - } - - return nil -} - -// Worker contains information about the worker used to record this session -type Worker struct { - PublicId string - Version string - Sha string -} - -func (w Worker) writeMeta(ctx context.Context, c *container) error { - _, err := c.WriteMeta(ctx, "worker_publicId", w.PublicId) - if err != nil { - return err - } - _, err = c.WriteMeta(ctx, "worker_version", w.Version) - if err != nil { - return err - } - _, err = c.WriteMeta(ctx, "worker_sha", w.Sha) - if err != nil { - return err - } - return nil -} - -// StaticHostCatalog contains information about the static host catalog for this session -type StaticHostCatalog struct { - PublicId string - ProjectId string - Name string // optional field - Description string // optional field -} - -func (h StaticHostCatalog) writeMeta(ctx context.Context, c *container) error { - _, err := c.WriteMeta(ctx, "staticHostCatalog_publicId", h.PublicId) - if err != nil { - return err - } - _, err = c.WriteMeta(ctx, "staticHostCatalog_projectId", h.ProjectId) - if err != nil { - return err - } - if h.Name != "" { - _, err = c.WriteMeta(ctx, "staticHostCatalog_name", h.Name) - if err != nil { - return err - } - } - if h.Description != "" { - _, err = c.WriteMeta(ctx, "staticHostCatalog_description", h.Description) - if err != nil { - return err - } - } - - return nil -} - -// StaticHost contains information about the static host for this session -type StaticHost struct { - PublicId string - Catalog StaticHostCatalog - Name string // optional field - Description string // optional field - Address string -} - -func (h StaticHost) writeMeta(ctx context.Context, c *container) error { - _, err := c.WriteMeta(ctx, "staticHost_publicId", h.PublicId) - if err != nil { - return err - } - err = h.Catalog.writeMeta(ctx, c) - if err != nil { - return err - } - if h.Name != "" { - _, err = c.WriteMeta(ctx, "staticHost_name", h.Name) - if err != nil { - return err - } - } - if h.Description != "" { - _, err = c.WriteMeta(ctx, "staticHost_description", h.Description) - if err != nil { - return err - } - } - _, err = c.WriteMeta(ctx, "staticHost_address", h.Address) - if err != nil { - return err - } - - return nil -} - -// DynamicHostCatalog contains information about the dynamic host catalog for this session -type DynamicHostCatalog struct { - PublicId string - ProjectId string - Name string // optional field - Description string // optional field - PluginId string - Attributes string -} - -func (h DynamicHostCatalog) writeMeta(ctx context.Context, c *container) error { - _, err := c.WriteMeta(ctx, "dynamicHostCatalog_publicId", h.PublicId) - if err != nil { - return err - } - _, err = c.WriteMeta(ctx, "dynamicHostCatalog_projectId", h.ProjectId) - if err != nil { - return err - } - if h.Name != "" { - _, err = c.WriteMeta(ctx, "dynamicHostCatalog_name", h.Name) - if err != nil { - return err - } - } - if h.Description != "" { - _, err = c.WriteMeta(ctx, "dynamicHostCatalog_description", h.Description) - if err != nil { - return err - } - } - _, err = c.WriteMeta(ctx, "dynamicHostCatalog_pluginId", h.PluginId) - if err != nil { - return err - } - _, err = c.WriteMeta(ctx, "dynamicHostCatalog_attributes", h.Attributes) - if err != nil { - return err - } - - return nil -} - -// DynamicHost contains information about the dynamic host for this session -type DynamicHost struct { - PublicId string - Catalog DynamicHostCatalog - Name string // optional field - Description string // optional field - ExternalId string -} - -func (h DynamicHost) writeMeta(ctx context.Context, c *container) error { - _, err := c.WriteMeta(ctx, "dynamicHost_publicId", h.PublicId) - if err != nil { - return err - } - err = h.Catalog.writeMeta(ctx, c) - if err != nil { - return err - } - if h.Name != "" { - _, err = c.WriteMeta(ctx, "dynamicHost_name", h.Name) - if err != nil { - return err - } - } - if h.Description != "" { - _, err = c.WriteMeta(ctx, "dynamicHost_description", h.Description) - if err != nil { - return err - } - } - _, err = c.WriteMeta(ctx, "dynamicHost_externalId", h.ExternalId) - if err != nil { - return err - } - - return nil -} - -// StaticCredentialStore represents a static credential store used for this session -type StaticCredentialStore struct { - PublicId string - ProjectId string - Name string // optional field - Description string // optional field - Credentials []StaticCredential -} - -func (s StaticCredentialStore) writeMeta(ctx context.Context, c *container) error { - prefix := fmt.Sprintf("%s_staticCredentialStore", s.PublicId) - _, err := c.WriteMeta(ctx, fmt.Sprintf("%s_publicId", prefix), s.PublicId) - if err != nil { - return err - } - _, err = c.WriteMeta(ctx, fmt.Sprintf("%s_projectId", prefix), s.ProjectId) - if err != nil { - return err - } - if s.Name != "" { - _, err = c.WriteMeta(ctx, fmt.Sprintf("%s_name", prefix), s.Name) - if err != nil { - return err - } - } - if s.Description != "" { - _, err = c.WriteMeta(ctx, fmt.Sprintf("%s_description", prefix), s.Description) - if err != nil { - return err - } - } - if len(s.Credentials) > 0 { - credentialPrefix := fmt.Sprintf("%s_credential", prefix) - for _, sc := range s.Credentials { - err = sc.writeStaticCredentialMeta(ctx, c, credentialPrefix) - if err != nil { - return err - } - } - } - return nil -} - -// StaticCredential represents a static credential used for this session -type StaticCredential interface { - writeStaticCredentialMeta(ctx context.Context, c *container, prefix string) error -} - -// StaticJsonCredential represents a static json credential used for this session -type StaticJsonCredential struct { - PublicId string - ProjectId string - Name string // optional field - Description string // optional field - ObjectHmac []byte -} - -func (s StaticJsonCredential) writeStaticCredentialMeta(ctx context.Context, c *container, p string) error { - prefix := fmt.Sprintf("%s_%s_staticJsonCredential", p, s.PublicId) - _, err := c.WriteMeta(ctx, fmt.Sprintf("%s_publicId", prefix), s.PublicId) - if err != nil { - return err - } - _, err = c.WriteMeta(ctx, fmt.Sprintf("%s_projectId", prefix), s.ProjectId) - if err != nil { - return err - } - if s.Name != "" { - _, err = c.WriteMeta(ctx, fmt.Sprintf("%s_name", prefix), s.Name) - if err != nil { - return err - } - } - if s.Description != "" { - _, err = c.WriteMeta(ctx, fmt.Sprintf("%s_description", prefix), s.Description) - if err != nil { - return err - } - } - _, err = c.WriteMeta(ctx, fmt.Sprintf("%s_objectHmac", prefix), string(s.ObjectHmac)) - if err != nil { - return err - } - return nil -} - -// StaticUsernamePasswordCredential represents a Static username password credential used for this session -type StaticUsernamePasswordCredential struct { - PublicId string - ProjectId string - Name string // optional field - Description string // optional field - PasswordHmac []byte -} - -func (s StaticUsernamePasswordCredential) writeStaticCredentialMeta(ctx context.Context, c *container, p string) error { - prefix := fmt.Sprintf("%s_%s_staticUsernamePasswordCredential", p, s.PublicId) - _, err := c.WriteMeta(ctx, fmt.Sprintf("%s_publicId", prefix), s.PublicId) - if err != nil { - return err - } - _, err = c.WriteMeta(ctx, fmt.Sprintf("%s_projectId", prefix), s.ProjectId) - if err != nil { - return err - } - if s.Name != "" { - _, err = c.WriteMeta(ctx, fmt.Sprintf("%s_name", prefix), s.Name) - if err != nil { - return err - } - } - if s.Description != "" { - _, err = c.WriteMeta(ctx, fmt.Sprintf("%s_description", prefix), s.Description) - if err != nil { - return err - } - } - _, err = c.WriteMeta(ctx, fmt.Sprintf("%s_passwordHmac", prefix), string(s.PasswordHmac)) - if err != nil { - return err - } - return nil -} - -// StaticSshPrivateKeyCredential represents a Static Ssh private key credential used for this session -type StaticSshPrivateKeyCredential struct { - PublicId string - ProjectId string - Name string // optional field - Description string // optional field - PrivateKeyHmac []byte - PrivateKeyPassphraseHmac []byte // optional field -} - -func (s StaticSshPrivateKeyCredential) writeStaticCredentialMeta(ctx context.Context, c *container, p string) error { - prefix := fmt.Sprintf("%s_%s_staticSshPrivateKeyCredential", p, s.PublicId) - _, err := c.WriteMeta(ctx, fmt.Sprintf("%s_publicId", prefix), s.PublicId) - if err != nil { - return err - } - _, err = c.WriteMeta(ctx, fmt.Sprintf("%s_projectId", prefix), s.ProjectId) - if err != nil { - return err - } - if s.Name != "" { - _, err = c.WriteMeta(ctx, fmt.Sprintf("%s_name", prefix), s.Name) - if err != nil { - return err - } - } - if s.Description != "" { - _, err = c.WriteMeta(ctx, fmt.Sprintf("%s_description", prefix), s.Description) - if err != nil { - return err - } - } - _, err = c.WriteMeta(ctx, fmt.Sprintf("%s_privateKeyHmac", prefix), string(s.PrivateKeyHmac)) - if err != nil { - return err - } - if len(s.PrivateKeyPassphraseHmac) > 0 { - _, err = c.WriteMeta(ctx, fmt.Sprintf("%s_privateKeyPassphraseHmac", prefix), string(s.PrivateKeyPassphraseHmac)) - if err != nil { - return err - } - } - return nil -} - -// VaultCredentialStore represents a Vault credential store used for this session -type VaultCredentialStore struct { - PublicId string - ProjectId string - Name string // optional field - Description string // optional field - VaultAddress string - Namespace string - TlsServerName string - TlsSkipVerify bool - WorkerFilter string // optional field - CredentialLibraries []DynamicCredentialLibraries -} - -func (v VaultCredentialStore) writeMeta(ctx context.Context, c *container) error { - prefix := fmt.Sprintf("%s_vaultCredentialStore", v.PublicId) - _, err := c.WriteMeta(ctx, fmt.Sprintf("%s_publicId", prefix), v.PublicId) - if err != nil { - return err - } - _, err = c.WriteMeta(ctx, fmt.Sprintf("%s_projectId", prefix), v.ProjectId) - if err != nil { - return err - } - if v.Name != "" { - _, err = c.WriteMeta(ctx, fmt.Sprintf("%s_name", prefix), v.Name) - if err != nil { - return err - } - } - if v.Description != "" { - _, err = c.WriteMeta(ctx, fmt.Sprintf("%s_description", prefix), v.Description) - if err != nil { - return err - } - } - _, err = c.WriteMeta(ctx, fmt.Sprintf("%s_vaultAddress", prefix), v.VaultAddress) - if err != nil { - return err - } - _, err = c.WriteMeta(ctx, fmt.Sprintf("%s_namespace", prefix), v.Namespace) - if err != nil { - return err - } - _, err = c.WriteMeta(ctx, fmt.Sprintf("%s_tlsServerName", prefix), v.TlsServerName) - if err != nil { - return err - } - _, err = c.WriteMeta(ctx, fmt.Sprintf("%s_tlsSkipVerify", prefix), strconv.FormatBool(v.TlsSkipVerify)) - if err != nil { - return err - } - if v.WorkerFilter != "" { - _, err = c.WriteMeta(ctx, fmt.Sprintf("%s_workerFilter", prefix), v.WorkerFilter) - if err != nil { - return err - } - } - if len(v.CredentialLibraries) > 0 { - credentialPrefix := fmt.Sprintf("%s_credential", prefix) - for _, cl := range v.CredentialLibraries { - err = cl.writeDynamicCredentialLibraryMeta(ctx, c, credentialPrefix) - if err != nil { - return err - } - } - } - return nil -} - -// DynamicCredentialLibraries represents a dynamic credential library used for this session -type DynamicCredentialLibraries interface { - writeDynamicCredentialLibraryMeta(ctx context.Context, c *container, prefix string) error -} - -// VaultLibrary contains information about the Vault library used for this session -type VaultLibrary struct { - PublicId string - ProjectId string - Name string // optional field - Description string // optional field - VaultPath string - HttpMethod string - HttpRequestBody []byte // optional field - CredentialType string -} - -func (v VaultLibrary) writeDynamicCredentialLibraryMeta(ctx context.Context, c *container, p string) error { - prefix := fmt.Sprintf("%s_%s_vaultLibrary", p, v.PublicId) - _, err := c.WriteMeta(ctx, fmt.Sprintf("%s_publicId", prefix), v.PublicId) - if err != nil { - return err - } - _, err = c.WriteMeta(ctx, fmt.Sprintf("%s_projectId", prefix), v.ProjectId) - if err != nil { - return err - } - if v.Name != "" { - _, err = c.WriteMeta(ctx, fmt.Sprintf("%s_name", prefix), v.Name) - if err != nil { - return err - } - } - if v.Description != "" { - _, err = c.WriteMeta(ctx, fmt.Sprintf("%s_description", prefix), v.Description) - if err != nil { - return err - } - } - _, err = c.WriteMeta(ctx, fmt.Sprintf("%s_vaultPath", prefix), v.VaultPath) - if err != nil { - return err - } - _, err = c.WriteMeta(ctx, fmt.Sprintf("%s_httpMethod", prefix), v.HttpMethod) - if err != nil { - return err - } - if len(v.HttpRequestBody) > 0 { - _, err = c.WriteMeta(ctx, fmt.Sprintf("%s_httpRequestBody", prefix), string(v.HttpRequestBody)) - if err != nil { - return err - } - } - _, err = c.WriteMeta(ctx, fmt.Sprintf("%s_credentialType", prefix), v.CredentialType) - if err != nil { - return err - } - return nil -} - -// VaultSshCertLibrary contains information about a Vault Ssh Cert library for this session -type VaultSshCertLibrary struct { - PublicId string - ProjectId string - Name string // optional field - Description string // optional field - VaultPath string - Username string - KeyType string - KeyBits int - Ttl string // optional field - CriticalOptions []byte // optional field - Extensions []byte // optional field - CredentialType string // optional field -} - -func (v VaultSshCertLibrary) writeDynamicCredentialLibraryMeta(ctx context.Context, c *container, p string) error { - prefix := fmt.Sprintf("%s_%s_vaultSshCertLibrary", p, v.PublicId) - _, err := c.WriteMeta(ctx, fmt.Sprintf("%s_publicId", prefix), v.PublicId) - if err != nil { - return err - } - _, err = c.WriteMeta(ctx, fmt.Sprintf("%s_projectId", prefix), v.ProjectId) - if err != nil { - return err - } - if v.Name != "" { - _, err = c.WriteMeta(ctx, fmt.Sprintf("%s_name", prefix), v.Name) - if err != nil { - return err - } - } - if v.Description != "" { - _, err = c.WriteMeta(ctx, fmt.Sprintf("%s_description", prefix), v.Description) - if err != nil { - return err - } - } - _, err = c.WriteMeta(ctx, fmt.Sprintf("%s_vaultPath", prefix), v.VaultPath) - if err != nil { - return err - } - _, err = c.WriteMeta(ctx, fmt.Sprintf("%s_username", prefix), v.Username) - if err != nil { - return err - } - _, err = c.WriteMeta(ctx, fmt.Sprintf("%s_keyType", prefix), v.KeyType) - if err != nil { - return err - } - _, err = c.WriteMeta(ctx, fmt.Sprintf("%s_keyBits", prefix), strconv.Itoa(v.KeyBits)) - if err != nil { - return err - } - if v.Ttl != "" { - _, err = c.WriteMeta(ctx, fmt.Sprintf("%s_ttl", prefix), v.Ttl) - if err != nil { - return err - } - } - if len(v.CriticalOptions) > 0 { - _, err = c.WriteMeta(ctx, fmt.Sprintf("%s_criticalOptions", prefix), string(v.CriticalOptions)) - if err != nil { - return err - } - } - if len(v.Extensions) > 0 { - _, err = c.WriteMeta(ctx, fmt.Sprintf("%s_extensions", prefix), string(v.Extensions)) - if err != nil { - return err - } - } - if v.Name != "" { - _, err = c.WriteMeta(ctx, fmt.Sprintf("%s_credentialType", prefix), v.CredentialType) - if err != nil { - return err - } - } - return nil -} - -// SessionMeta contains metadata about a session in a BSR. -// Most fields are written to the meta file as k:v pairs -// Slice fields are written to the meta file as id_k:v -// Nested slice fields are written as parentId_parentKey_id_k:v -type SessionMeta struct { - Id string - Protocol Protocol - User *User - Target *Target - Worker *Worker - // StaticHost and DynamicHost are mutually exclusive - StaticHost *StaticHost - DynamicHost *DynamicHost - - StaticCredentialStore []StaticCredentialStore - VaultCredentialStore []VaultCredentialStore - - connections map[string]bool -} - -func (s SessionMeta) writeMeta(ctx context.Context, c *container) error { - _, err := c.WriteMeta(ctx, "id", s.Id) - if err != nil { - return err - } - _, err = c.WriteMeta(ctx, "protocol", string(s.Protocol)) - if err != nil { - return err - } - err = s.User.writeMeta(ctx, c) - if err != nil { - return err - } - err = s.Target.writeMeta(ctx, c) - if err != nil { - return err - } - err = s.Worker.writeMeta(ctx, c) - if err != nil { - return err - } - if s.StaticHost != nil { - err = s.StaticHost.writeMeta(ctx, c) - if err != nil { - return err - } - } - if s.DynamicHost != nil { - err = s.DynamicHost.writeMeta(ctx, c) - if err != nil { - return err - } - } - if len(s.StaticCredentialStore) > 0 { - for _, sc := range s.StaticCredentialStore { - err = sc.writeMeta(ctx, c) - if err != nil { - return err - } - } - } - if len(s.VaultCredentialStore) > 0 { - for _, dc := range s.VaultCredentialStore { - err = dc.writeMeta(ctx, c) - if err != nil { - return err - } - } - } - - return nil -} diff --git a/internal/bsr/meta_recording.go b/internal/bsr/meta_recording.go new file mode 100644 index 000000000..ede688ccf --- /dev/null +++ b/internal/bsr/meta_recording.go @@ -0,0 +1,170 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package bsr + +import ( + "bufio" + "context" + "fmt" + "io" + "strings" +) + +// SessionRecordingMeta contains metadata about a session in a BSR. +// Most fields are written to the meta file as k:v pairs +// Slice fields are written to the meta file as id_k:v +// Nested slice fields are written as parentId_parentKey_id_k:v +type SessionRecordingMeta struct { + Id string + Protocol Protocol + connections map[string]bool +} + +func (s *SessionRecordingMeta) writeMeta(ctx context.Context, c *container) error { + _, err := c.WriteMeta(ctx, "id", s.Id) + if err != nil { + return err + } + _, err = c.WriteMeta(ctx, "protocol", string(s.Protocol)) + if err != nil { + return err + } + return nil +} + +// decodeSessionRecordingMeta will populate a SessionRecordingMeta for an opened BSR Session +func decodeSessionRecordingMeta(ctx context.Context, r io.Reader) (*SessionRecordingMeta, error) { + const op = "bsr.decodeSessionRecordingMeta" + + switch { + case r == nil: + return nil, fmt.Errorf("%s: missing session meta file: %w", op, ErrInvalidParameter) + } + + connections := make(map[string]bool) + s := &SessionRecordingMeta{} + scanner := bufio.NewScanner(r) + for scanner.Scan() { + l := scanner.Text() + if len(l) == 0 { + continue + } + k, v, ok := strings.Cut(l, ":") + if !ok { + return nil, fmt.Errorf("%s: session meta file contains invalid entry; expecting k: v pair:%s", op, l) + } + v = strings.TrimSpace(v) + switch { + case k == "id": + s.Id = v + case k == "protocol": + s.Protocol = Protocol(v) + + // connections + case k == "connection": + connections[v] = true + } + } + + s.connections = connections + + return s, nil +} + +// ConnectionRecordingMeta contains metadata about a connection in a BSR. +type ConnectionRecordingMeta struct { + Id string + channels map[string]bool +} + +func (c ConnectionRecordingMeta) isValid() bool { + switch { + case c.Id == "": + return false + default: + return true + } +} + +// decodeConnectionRecordingMeta will populate the ConnectionRecordingMeta for a BSR Connection +func decodeConnectionRecordingMeta(ctx context.Context, r io.Reader) (*ConnectionRecordingMeta, error) { + const op = "bsr.decodeConnectionRecordingMeta" + + switch { + case r == nil: + return nil, fmt.Errorf("%s: missing connection meta file: %w", op, ErrInvalidParameter) + } + c := &ConnectionRecordingMeta{} + channels := make(map[string]bool) + scanner := bufio.NewScanner(r) + for scanner.Scan() { + l := scanner.Text() + if len(l) == 0 { + continue + } + k, v, ok := strings.Cut(l, ":") + if !ok { + return nil, fmt.Errorf("%s: connection meta file contains invalid entry; expecting k: v pair:%s", op, l) + } + v = strings.TrimSpace(v) + switch { + case k == "id": + c.Id = v + case k == "channel": + channels[v] = true + } + } + c.channels = channels + + return c, nil +} + +// ChannelRecordingMeta contains metadata about a channel in a BSR. +type ChannelRecordingMeta struct { + Id string + Type string +} + +func (c ChannelRecordingMeta) isValid() bool { + switch { + case c.Id == "": + return false + case c.Type == "": + return false + default: + return true + } +} + +// decodeChannelRecordingMeta will populate the ChannelRecordingMeta for a BSR Channel +func decodeChannelRecordingMeta(ctx context.Context, r io.Reader) (*ChannelRecordingMeta, error) { + const op = "bsr.decodeChannelRecordingMeta" + + switch { + case r == nil: + return nil, fmt.Errorf("%s: missing channel meta file: %w", op, ErrInvalidParameter) + } + + c := &ChannelRecordingMeta{} + scanner := bufio.NewScanner(r) + for scanner.Scan() { + l := scanner.Text() + if len(l) == 0 { + continue + } + k, v, ok := strings.Cut(l, ":") + if !ok { + return nil, fmt.Errorf("%s: channel meta file contains invalid entry; expecting k: v pair:%s", op, l) + } + v = strings.TrimSpace(v) + switch { + case k == "id": + c.Id = v + case k == "channelType": + c.Type = v + } + } + + return c, nil +} diff --git a/internal/bsr/meta_session.go b/internal/bsr/meta_session.go new file mode 100644 index 000000000..a3008bbfe --- /dev/null +++ b/internal/bsr/meta_session.go @@ -0,0 +1,174 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package bsr + +// Scope contains information about the scope of a Boundary domain object +type Scope struct { + PublicId string + Name string // optional + Description string // optional + Type string + ParentId string // optional + PrimaryAuthMethodId string // optional +} + +// User contains information about user who initiated this session +type User struct { + PublicId string + Scope Scope + Name string // optional field + Description string // optional field +} + +// Target contains information about the target for this session +type Target struct { + PublicId string + Scope Scope + Name string // optional field + Description string // optional field + DefaultPort uint32 + SessionMaxSeconds uint32 + SessionConnectionLimit int32 + WorkerFilter string // optional field + EgressWorkerFilter string // optional field + IngressWorkerFilter string // optional field +} + +// Worker contains information about the worker used to record this session +type Worker struct { + PublicId string + Version string + Sha string +} + +// StaticHostCatalog contains information about the static host catalog for this session +type StaticHostCatalog struct { + PublicId string + ProjectId string + Name string // optional field + Description string // optional field +} + +// StaticHost contains information about the static host for this session +type StaticHost struct { + PublicId string + Catalog StaticHostCatalog + Name string // optional field + Description string // optional field + Address string +} + +// DynamicHostCatalog contains information about the dynamic host catalog for this session +type DynamicHostCatalog struct { + PublicId string + ProjectId string + Name string // optional field + Description string // optional field + PluginId string + Attributes string +} + +// DynamicHost contains information about the dynamic host for this session +type DynamicHost struct { + PublicId string + Catalog DynamicHostCatalog + Name string // optional field + Description string // optional field + ExternalId string +} + +// StaticCredentialStore represents a static credential store used for this session +type StaticCredentialStore struct { + PublicId string + ProjectId string + Name string // optional field + Description string // optional field + StaticJsonCredentials []StaticJsonCredential + StaticUsernamePasswordCredentials []StaticUsernamePasswordCredential +} + +// StaticJsonCredential represents a static json credential used for this session +type StaticJsonCredential struct { + PublicId string + ProjectId string + Name string // optional field + Description string // optional field + ObjectHmac []byte +} + +// StaticUsernamePasswordCredential represents a Static username password credential used for this session +type StaticUsernamePasswordCredential struct { + PublicId string + ProjectId string + Name string // optional field + Description string // optional field + PasswordHmac []byte +} + +// StaticSshPrivateKeyCredential represents a Static Ssh private key credential used for this session +type StaticSshPrivateKeyCredential struct { + PublicId string + ProjectId string + Name string // optional field + Description string // optional field + PrivateKeyHmac []byte + PrivateKeyPassphraseHmac []byte // optional field +} + +// VaultCredentialStore represents a Vault credential store used for this session +type VaultCredentialStore struct { + PublicId string + ProjectId string + Name string // optional field + Description string // optional field + VaultAddress string + Namespace string + TlsServerName string + TlsSkipVerify bool + WorkerFilter string // optional field + VaultGenericLibraries []VaultLibrary + VaultSshCertLibraries []VaultSshCertLibrary +} + +// VaultLibrary contains information about the Vault library used for this session +type VaultLibrary struct { + PublicId string + ProjectId string + Name string // optional field + Description string // optional field + VaultPath string + HttpMethod string + HttpRequestBody []byte // optional field + CredentialType string +} + +// VaultSshCertLibrary contains information about a Vault Ssh Cert library for this session +type VaultSshCertLibrary struct { + PublicId string + ProjectId string + Name string // optional field + Description string // optional field + VaultPath string + Username string + KeyType string + KeyBits int + Ttl string // optional field + CriticalOptions []byte // optional field + Extensions []byte // optional field + CredentialType string // optional field +} + +// SessionMeta contains metadata about a session in a BSR. +type SessionMeta struct { + PublicId string + User *User + Target *Target + Worker *Worker + // StaticHost and DynamicHost are mutually exclusive + StaticHost *StaticHost + DynamicHost *DynamicHost + + StaticCredentialStore []StaticCredentialStore + VaultCredentialStore []VaultCredentialStore +} diff --git a/internal/bsr/testdata/TestBsr/session_multiplexed/closed/.journal b/internal/bsr/testdata/TestBsr/session_multiplexed/closed/.journal index 7b5c879e6..52ea32b67 100644 --- a/internal/bsr/testdata/TestBsr/session_multiplexed/closed/.journal +++ b/internal/bsr/testdata/TestBsr/session_multiplexed/closed/.journal @@ -2,10 +2,13 @@ CREATING SHA256SUM.sig CREATED SHA256SUM.sig CREATING SHA256SUM CREATED SHA256SUM -CREATING session.meta -CREATED session.meta -CREATING session.summary -CREATED session.summary +CREATING session-recording.meta +CREATED session-recording.meta +CREATING session-meta.json +CREATED session-meta.json +CLOSED session-meta.json +CREATING session-recording-summary.json +CREATED session-recording-summary.json CREATING bsrKey.pub CREATED bsrKey.pub CLOSING bsrKey.pub @@ -26,10 +29,10 @@ CREATING pubKeySelfSignature.sign CREATED pubKeySelfSignature.sign CLOSING pubKeySelfSignature.sign CLOSED pubKeySelfSignature.sign -CLOSING session.meta -CLOSED session.meta -CLOSING session.summary -CLOSED session.summary +CLOSING session-recording.meta +CLOSED session-recording.meta +CLOSING session-recording-summary.json +CLOSED session-recording-summary.json CLOSING SHA256SUM CLOSED SHA256SUM CLOSING SHA256SUM.sig diff --git a/internal/bsr/testdata/TestBsr/session_multiplexed/closed/SHA256SUM b/internal/bsr/testdata/TestBsr/session_multiplexed/closed/SHA256SUM index 0dd161724..7ffc94c38 100644 --- a/internal/bsr/testdata/TestBsr/session_multiplexed/closed/SHA256SUM +++ b/internal/bsr/testdata/TestBsr/session_multiplexed/closed/SHA256SUM @@ -3,5 +3,6 @@ .* wrappedPrivKey .* pubKeyBsrSignature.sign .* pubKeySelfSignature.sign -1aac36a2c36e4de69c5bf3522ada1c7f080ac807f2182c013ec8ddf60bedae10 session.meta -e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 session.summary +.* session-meta.json +523569b06adc4a1996c7d3833105a48485428e663bf8a11ed9b4d8f84f68b85c session-recording.meta +e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 session-recording-summary.json diff --git a/internal/bsr/testdata/TestBsr/session_multiplexed/closed/conn_1/.journal b/internal/bsr/testdata/TestBsr/session_multiplexed/closed/conn_1/.journal index 0fc9cf086..1f4c1cbbb 100644 --- a/internal/bsr/testdata/TestBsr/session_multiplexed/closed/conn_1/.journal +++ b/internal/bsr/testdata/TestBsr/session_multiplexed/closed/conn_1/.journal @@ -2,10 +2,10 @@ CREATING SHA256SUM.sig CREATED SHA256SUM.sig CREATING SHA256SUM CREATED SHA256SUM -CREATING connection.meta -CREATED connection.meta -CREATING connection.summary -CREATED connection.summary +CREATING connection-recording.meta +CREATED connection-recording.meta +CREATING connection-recording-summary.json +CREATED connection-recording-summary.json CREATING requests-inbound.data CREATED requests-inbound.data CREATING requests-outbound.data @@ -14,10 +14,10 @@ CLOSING requests-inbound.data CLOSED requests-inbound.data CLOSING requests-outbound.data CLOSED requests-outbound.data -CLOSING connection.meta -CLOSED connection.meta -CLOSING connection.summary -CLOSED connection.summary +CLOSING connection-recording.meta +CLOSED connection-recording.meta +CLOSING connection-recording-summary.json +CLOSED connection-recording-summary.json CLOSING SHA256SUM CLOSED SHA256SUM CLOSING SHA256SUM.sig diff --git a/internal/bsr/testdata/TestBsr/session_multiplexed/closed/conn_1/SHA256SUM b/internal/bsr/testdata/TestBsr/session_multiplexed/closed/conn_1/SHA256SUM index c159afd4c..c8e3e6f56 100644 --- a/internal/bsr/testdata/TestBsr/session_multiplexed/closed/conn_1/SHA256SUM +++ b/internal/bsr/testdata/TestBsr/session_multiplexed/closed/conn_1/SHA256SUM @@ -1,4 +1,4 @@ e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 requests-inbound.data e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 requests-outbound.data -e4df3504aaa6fee4426f67ba998cdd90f84c579d98db979e589d8ca2ffd1e733 connection.meta -e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 connection.summary +e4df3504aaa6fee4426f67ba998cdd90f84c579d98db979e589d8ca2ffd1e733 connection-recording.meta +e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 connection-recording-summary.json diff --git a/internal/bsr/testdata/TestBsr/session_multiplexed/closed/conn_1/chan_1/.journal b/internal/bsr/testdata/TestBsr/session_multiplexed/closed/conn_1/chan_1/.journal index 227731795..d4c63d8d3 100644 --- a/internal/bsr/testdata/TestBsr/session_multiplexed/closed/conn_1/chan_1/.journal +++ b/internal/bsr/testdata/TestBsr/session_multiplexed/closed/conn_1/chan_1/.journal @@ -2,10 +2,10 @@ CREATING SHA256SUM.sig CREATED SHA256SUM.sig CREATING SHA256SUM CREATED SHA256SUM -CREATING channel.meta -CREATED channel.meta -CREATING channel.summary -CREATED channel.summary +CREATING channel-recording.meta +CREATED channel-recording.meta +CREATING channel-recording-summary.json +CREATED channel-recording-summary.json CREATING messages-inbound.data CREATED messages-inbound.data CREATING requests-inbound.data @@ -22,10 +22,10 @@ CLOSING messages-outbound.data CLOSED messages-outbound.data CLOSING requests-outbound.data CLOSED requests-outbound.data -CLOSING channel.meta -CLOSED channel.meta -CLOSING channel.summary -CLOSED channel.summary +CLOSING channel-recording.meta +CLOSED channel-recording.meta +CLOSING channel-recording-summary.json +CLOSED channel-recording-summary.json CLOSING SHA256SUM CLOSED SHA256SUM CLOSING SHA256SUM.sig diff --git a/internal/bsr/testdata/TestBsr/session_multiplexed/closed/conn_1/chan_1/SHA256SUM b/internal/bsr/testdata/TestBsr/session_multiplexed/closed/conn_1/chan_1/SHA256SUM index e3eaa85aa..e9294a6e0 100644 --- a/internal/bsr/testdata/TestBsr/session_multiplexed/closed/conn_1/chan_1/SHA256SUM +++ b/internal/bsr/testdata/TestBsr/session_multiplexed/closed/conn_1/chan_1/SHA256SUM @@ -2,5 +2,5 @@ e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 messages-inbou e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 requests-inbound.data e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 messages-outbound.data e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 requests-outbound.data -27a58452605f6e30ca048cc540e9d963c7270212fcb2c7b73fc69e314ffd1902 channel.meta -e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 channel.summary +27a58452605f6e30ca048cc540e9d963c7270212fcb2c7b73fc69e314ffd1902 channel-recording.meta +e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 channel-recording-summary.json diff --git a/internal/bsr/testdata/TestBsr/session_multiplexed/closed/conn_1/chan_1/channel.summary b/internal/bsr/testdata/TestBsr/session_multiplexed/closed/conn_1/chan_1/channel-recording-summary.json similarity index 100% rename from internal/bsr/testdata/TestBsr/session_multiplexed/closed/conn_1/chan_1/channel.summary rename to internal/bsr/testdata/TestBsr/session_multiplexed/closed/conn_1/chan_1/channel-recording-summary.json diff --git a/internal/bsr/testdata/TestBsr/session_multiplexed/closed/conn_1/chan_1/channel.meta b/internal/bsr/testdata/TestBsr/session_multiplexed/closed/conn_1/chan_1/channel-recording.meta similarity index 100% rename from internal/bsr/testdata/TestBsr/session_multiplexed/closed/conn_1/chan_1/channel.meta rename to internal/bsr/testdata/TestBsr/session_multiplexed/closed/conn_1/chan_1/channel-recording.meta diff --git a/internal/bsr/testdata/TestBsr/session_multiplexed/closed/conn_1/connection.summary b/internal/bsr/testdata/TestBsr/session_multiplexed/closed/conn_1/connection-recording-summary.json similarity index 100% rename from internal/bsr/testdata/TestBsr/session_multiplexed/closed/conn_1/connection.summary rename to internal/bsr/testdata/TestBsr/session_multiplexed/closed/conn_1/connection-recording-summary.json diff --git a/internal/bsr/testdata/TestBsr/session_multiplexed/closed/conn_1/connection.meta b/internal/bsr/testdata/TestBsr/session_multiplexed/closed/conn_1/connection-recording.meta similarity index 100% rename from internal/bsr/testdata/TestBsr/session_multiplexed/closed/conn_1/connection.meta rename to internal/bsr/testdata/TestBsr/session_multiplexed/closed/conn_1/connection-recording.meta diff --git a/internal/bsr/testdata/TestBsr/session_multiplexed/closed/session.summary b/internal/bsr/testdata/TestBsr/session_multiplexed/closed/session-recording-summary.json similarity index 100% rename from internal/bsr/testdata/TestBsr/session_multiplexed/closed/session.summary rename to internal/bsr/testdata/TestBsr/session_multiplexed/closed/session-recording-summary.json diff --git a/internal/bsr/testdata/TestBsr/session_multiplexed/closed/session-recording.meta b/internal/bsr/testdata/TestBsr/session_multiplexed/closed/session-recording.meta new file mode 100644 index 000000000..e54a47b9c --- /dev/null +++ b/internal/bsr/testdata/TestBsr/session_multiplexed/closed/session-recording.meta @@ -0,0 +1,3 @@ +id: session_123456789 +protocol: TEST +connection: conn_1.connection diff --git a/internal/bsr/testdata/TestBsr/session_multiplexed/closed/session.meta b/internal/bsr/testdata/TestBsr/session_multiplexed/closed/session.meta deleted file mode 100644 index cb1e86b02..000000000 --- a/internal/bsr/testdata/TestBsr/session_multiplexed/closed/session.meta +++ /dev/null @@ -1,36 +0,0 @@ -id: session_123456789 -protocol: TEST -user_publicId: user123 -user_scope_publicId: global -user_scope_type: global -target_publicId: target123 -target_scope_publicId: proj123 -target_scope_type: project -target_scope_parentId: org123 -target_defaultPort: 0 -target_sessionMaxSeconds: 0 -target_sessionConnectionLimit: 0 -worker_publicId: w_12345 -worker_version: 0.25.5 -worker_sha: beepboopgitsha -staticHost_publicId: host123 -staticHostCatalog_publicId: staticcat123 -staticHostCatalog_projectId: proj123 -staticHost_address: 127.0.0.1 -scs123_staticCredentialStore_publicId: scs123 -scs123_staticCredentialStore_projectId: proj123 -scs123_staticCredentialStore_credential_scjson123_staticJsonCredential_publicId: scjson123 -scs123_staticCredentialStore_credential_scjson123_staticJsonCredential_projectId: proj123 -scs123_staticCredentialStore_credential_scjson123_staticJsonCredential_objectHmac: hmac -vcs123_vaultCredentialStore_publicId: vcs123 -vcs123_vaultCredentialStore_projectId: proj123 -vcs123_vaultCredentialStore_vaultAddress: an/address -vcs123_vaultCredentialStore_namespace: name -vcs123_vaultCredentialStore_tlsServerName: imaserver -vcs123_vaultCredentialStore_tlsSkipVerify: false -vcs123_vaultCredentialStore_credential_vl123_vaultLibrary_publicId: vl123 -vcs123_vaultCredentialStore_credential_vl123_vaultLibrary_projectId: proj123 -vcs123_vaultCredentialStore_credential_vl123_vaultLibrary_vaultPath: /a/path -vcs123_vaultCredentialStore_credential_vl123_vaultLibrary_httpMethod: GET -vcs123_vaultCredentialStore_credential_vl123_vaultLibrary_credentialType: magic -connection: conn_1.connection diff --git a/internal/bsr/testdata/TestBsr/session_multiplexed/opened/.journal b/internal/bsr/testdata/TestBsr/session_multiplexed/opened/.journal index 6b3594f5b..6f93e76e5 100644 --- a/internal/bsr/testdata/TestBsr/session_multiplexed/opened/.journal +++ b/internal/bsr/testdata/TestBsr/session_multiplexed/opened/.journal @@ -2,10 +2,13 @@ CREATING SHA256SUM.sig CREATED SHA256SUM.sig CREATING SHA256SUM CREATED SHA256SUM -CREATING session.meta -CREATED session.meta -CREATING session.summary -CREATED session.summary +CREATING session-recording.meta +CREATED session-recording.meta +CREATING session-meta.json +CREATED session-meta.json +CLOSED session-meta.json +CREATING session-recording-summary.json +CREATED session-recording-summary.json CREATING bsrKey.pub CREATED bsrKey.pub CLOSING bsrKey.pub diff --git a/internal/bsr/testdata/TestBsr/session_multiplexed/opened/SHA256SUM b/internal/bsr/testdata/TestBsr/session_multiplexed/opened/SHA256SUM index 819c21648..a710d3ed0 100644 --- a/internal/bsr/testdata/TestBsr/session_multiplexed/opened/SHA256SUM +++ b/internal/bsr/testdata/TestBsr/session_multiplexed/opened/SHA256SUM @@ -3,3 +3,4 @@ .* wrappedPrivKey .* pubKeyBsrSignature.sign .* pubKeySelfSignature.sign +.* session-meta.json diff --git a/internal/bsr/testdata/TestBsr/session_multiplexed/opened/conn_1/.journal b/internal/bsr/testdata/TestBsr/session_multiplexed/opened/conn_1/.journal index 815f145f4..e24aacf7c 100644 --- a/internal/bsr/testdata/TestBsr/session_multiplexed/opened/conn_1/.journal +++ b/internal/bsr/testdata/TestBsr/session_multiplexed/opened/conn_1/.journal @@ -2,7 +2,7 @@ CREATING SHA256SUM.sig CREATED SHA256SUM.sig CREATING SHA256SUM CREATED SHA256SUM -CREATING connection.meta -CREATED connection.meta -CREATING connection.summary -CREATED connection.summary +CREATING connection-recording.meta +CREATED connection-recording.meta +CREATING connection-recording-summary.json +CREATED connection-recording-summary.json diff --git a/internal/bsr/testdata/TestBsr/session_multiplexed/opened/conn_1/chan_1/.journal b/internal/bsr/testdata/TestBsr/session_multiplexed/opened/conn_1/chan_1/.journal index bab36873d..635dbbd95 100644 --- a/internal/bsr/testdata/TestBsr/session_multiplexed/opened/conn_1/chan_1/.journal +++ b/internal/bsr/testdata/TestBsr/session_multiplexed/opened/conn_1/chan_1/.journal @@ -2,7 +2,7 @@ CREATING SHA256SUM.sig CREATED SHA256SUM.sig CREATING SHA256SUM CREATED SHA256SUM -CREATING channel.meta -CREATED channel.meta -CREATING channel.summary -CREATED channel.summary +CREATING channel-recording.meta +CREATED channel-recording.meta +CREATING channel-recording-summary.json +CREATED channel-recording-summary.json diff --git a/internal/bsr/testdata/TestBsr/session_multiplexed/opened/conn_1/chan_1/channel.summary b/internal/bsr/testdata/TestBsr/session_multiplexed/opened/conn_1/chan_1/channel-recording-summary.json similarity index 100% rename from internal/bsr/testdata/TestBsr/session_multiplexed/opened/conn_1/chan_1/channel.summary rename to internal/bsr/testdata/TestBsr/session_multiplexed/opened/conn_1/chan_1/channel-recording-summary.json diff --git a/internal/bsr/testdata/TestBsr/session_multiplexed/opened/conn_1/chan_1/channel.meta b/internal/bsr/testdata/TestBsr/session_multiplexed/opened/conn_1/chan_1/channel-recording.meta similarity index 100% rename from internal/bsr/testdata/TestBsr/session_multiplexed/opened/conn_1/chan_1/channel.meta rename to internal/bsr/testdata/TestBsr/session_multiplexed/opened/conn_1/chan_1/channel-recording.meta diff --git a/internal/bsr/testdata/TestBsr/session_multiplexed/opened/conn_1/connection.summary b/internal/bsr/testdata/TestBsr/session_multiplexed/opened/conn_1/connection-recording-summary.json similarity index 100% rename from internal/bsr/testdata/TestBsr/session_multiplexed/opened/conn_1/connection.summary rename to internal/bsr/testdata/TestBsr/session_multiplexed/opened/conn_1/connection-recording-summary.json diff --git a/internal/bsr/testdata/TestBsr/session_multiplexed/opened/conn_1/connection.meta b/internal/bsr/testdata/TestBsr/session_multiplexed/opened/conn_1/connection-recording.meta similarity index 100% rename from internal/bsr/testdata/TestBsr/session_multiplexed/opened/conn_1/connection.meta rename to internal/bsr/testdata/TestBsr/session_multiplexed/opened/conn_1/connection-recording.meta diff --git a/internal/bsr/testdata/TestBsr/session_multiplexed/opened/session.summary b/internal/bsr/testdata/TestBsr/session_multiplexed/opened/session-recording-summary.json similarity index 100% rename from internal/bsr/testdata/TestBsr/session_multiplexed/opened/session.summary rename to internal/bsr/testdata/TestBsr/session_multiplexed/opened/session-recording-summary.json diff --git a/internal/bsr/testdata/TestBsr/session_multiplexed/opened/session-recording.meta b/internal/bsr/testdata/TestBsr/session_multiplexed/opened/session-recording.meta new file mode 100644 index 000000000..cdd180f95 --- /dev/null +++ b/internal/bsr/testdata/TestBsr/session_multiplexed/opened/session-recording.meta @@ -0,0 +1,2 @@ +id: session_123456789 +protocol: TEST diff --git a/internal/bsr/testdata/TestBsr/session_multiplexed/opened/session.meta b/internal/bsr/testdata/TestBsr/session_multiplexed/opened/session.meta deleted file mode 100644 index ca2f4b0c5..000000000 --- a/internal/bsr/testdata/TestBsr/session_multiplexed/opened/session.meta +++ /dev/null @@ -1,35 +0,0 @@ -id: session_123456789 -protocol: TEST -user_publicId: user123 -user_scope_publicId: global -user_scope_type: global -target_publicId: target123 -target_scope_publicId: proj123 -target_scope_type: project -target_scope_parentId: org123 -target_defaultPort: 0 -target_sessionMaxSeconds: 0 -target_sessionConnectionLimit: 0 -worker_publicId: w_12345 -worker_version: 0.25.5 -worker_sha: beepboopgitsha -staticHost_publicId: host123 -staticHostCatalog_publicId: staticcat123 -staticHostCatalog_projectId: proj123 -staticHost_address: 127.0.0.1 -scs123_staticCredentialStore_publicId: scs123 -scs123_staticCredentialStore_projectId: proj123 -scs123_staticCredentialStore_credential_scjson123_staticJsonCredential_publicId: scjson123 -scs123_staticCredentialStore_credential_scjson123_staticJsonCredential_projectId: proj123 -scs123_staticCredentialStore_credential_scjson123_staticJsonCredential_objectHmac: hmac -vcs123_vaultCredentialStore_publicId: vcs123 -vcs123_vaultCredentialStore_projectId: proj123 -vcs123_vaultCredentialStore_vaultAddress: an/address -vcs123_vaultCredentialStore_namespace: name -vcs123_vaultCredentialStore_tlsServerName: imaserver -vcs123_vaultCredentialStore_tlsSkipVerify: false -vcs123_vaultCredentialStore_credential_vl123_vaultLibrary_publicId: vl123 -vcs123_vaultCredentialStore_credential_vl123_vaultLibrary_projectId: proj123 -vcs123_vaultCredentialStore_credential_vl123_vaultLibrary_vaultPath: /a/path -vcs123_vaultCredentialStore_credential_vl123_vaultLibrary_httpMethod: GET -vcs123_vaultCredentialStore_credential_vl123_vaultLibrary_credentialType: magic diff --git a/internal/bsr/testdata/TestBsr/session_not_multiplexed/closed/.journal b/internal/bsr/testdata/TestBsr/session_not_multiplexed/closed/.journal index 7b5c879e6..52ea32b67 100644 --- a/internal/bsr/testdata/TestBsr/session_not_multiplexed/closed/.journal +++ b/internal/bsr/testdata/TestBsr/session_not_multiplexed/closed/.journal @@ -2,10 +2,13 @@ CREATING SHA256SUM.sig CREATED SHA256SUM.sig CREATING SHA256SUM CREATED SHA256SUM -CREATING session.meta -CREATED session.meta -CREATING session.summary -CREATED session.summary +CREATING session-recording.meta +CREATED session-recording.meta +CREATING session-meta.json +CREATED session-meta.json +CLOSED session-meta.json +CREATING session-recording-summary.json +CREATED session-recording-summary.json CREATING bsrKey.pub CREATED bsrKey.pub CLOSING bsrKey.pub @@ -26,10 +29,10 @@ CREATING pubKeySelfSignature.sign CREATED pubKeySelfSignature.sign CLOSING pubKeySelfSignature.sign CLOSED pubKeySelfSignature.sign -CLOSING session.meta -CLOSED session.meta -CLOSING session.summary -CLOSED session.summary +CLOSING session-recording.meta +CLOSED session-recording.meta +CLOSING session-recording-summary.json +CLOSED session-recording-summary.json CLOSING SHA256SUM CLOSED SHA256SUM CLOSING SHA256SUM.sig diff --git a/internal/bsr/testdata/TestBsr/session_not_multiplexed/closed/SHA256SUM b/internal/bsr/testdata/TestBsr/session_not_multiplexed/closed/SHA256SUM index 0dd161724..7ffc94c38 100644 --- a/internal/bsr/testdata/TestBsr/session_not_multiplexed/closed/SHA256SUM +++ b/internal/bsr/testdata/TestBsr/session_not_multiplexed/closed/SHA256SUM @@ -3,5 +3,6 @@ .* wrappedPrivKey .* pubKeyBsrSignature.sign .* pubKeySelfSignature.sign -1aac36a2c36e4de69c5bf3522ada1c7f080ac807f2182c013ec8ddf60bedae10 session.meta -e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 session.summary +.* session-meta.json +523569b06adc4a1996c7d3833105a48485428e663bf8a11ed9b4d8f84f68b85c session-recording.meta +e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 session-recording-summary.json diff --git a/internal/bsr/testdata/TestBsr/session_not_multiplexed/closed/conn_1/.journal b/internal/bsr/testdata/TestBsr/session_not_multiplexed/closed/conn_1/.journal index 8fe25773c..ee3788d7d 100644 --- a/internal/bsr/testdata/TestBsr/session_not_multiplexed/closed/conn_1/.journal +++ b/internal/bsr/testdata/TestBsr/session_not_multiplexed/closed/conn_1/.journal @@ -2,10 +2,10 @@ CREATING SHA256SUM.sig CREATED SHA256SUM.sig CREATING SHA256SUM CREATED SHA256SUM -CREATING connection.meta -CREATED connection.meta -CREATING connection.summary -CREATED connection.summary +CREATING connection-recording.meta +CREATED connection-recording.meta +CREATING connection-recording-summary.json +CREATED connection-recording-summary.json CREATING messages-inbound.data CREATED messages-inbound.data CREATING messages-outbound.data @@ -22,10 +22,10 @@ CLOSING requests-inbound.data CLOSED requests-inbound.data CLOSING requests-outbound.data CLOSED requests-outbound.data -CLOSING connection.meta -CLOSED connection.meta -CLOSING connection.summary -CLOSED connection.summary +CLOSING connection-recording.meta +CLOSED connection-recording.meta +CLOSING connection-recording-summary.json +CLOSED connection-recording-summary.json CLOSING SHA256SUM CLOSED SHA256SUM CLOSING SHA256SUM.sig diff --git a/internal/bsr/testdata/TestBsr/session_not_multiplexed/closed/conn_1/SHA256SUM b/internal/bsr/testdata/TestBsr/session_not_multiplexed/closed/conn_1/SHA256SUM index 8f318a5f7..925aa3a35 100644 --- a/internal/bsr/testdata/TestBsr/session_not_multiplexed/closed/conn_1/SHA256SUM +++ b/internal/bsr/testdata/TestBsr/session_not_multiplexed/closed/conn_1/SHA256SUM @@ -2,5 +2,5 @@ e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 messages-inbou e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 messages-outbound.data e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 requests-inbound.data e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 requests-outbound.data -03edd8ed2a4d678ea79d14538e504b6bb9aaec51c7e3ebf3a4891083d97ac7a9 connection.meta -e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 connection.summary +03edd8ed2a4d678ea79d14538e504b6bb9aaec51c7e3ebf3a4891083d97ac7a9 connection-recording.meta +e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 connection-recording-summary.json diff --git a/internal/bsr/testdata/TestBsr/session_not_multiplexed/closed/conn_1/connection.summary b/internal/bsr/testdata/TestBsr/session_not_multiplexed/closed/conn_1/connection-recording-summary.json similarity index 100% rename from internal/bsr/testdata/TestBsr/session_not_multiplexed/closed/conn_1/connection.summary rename to internal/bsr/testdata/TestBsr/session_not_multiplexed/closed/conn_1/connection-recording-summary.json diff --git a/internal/bsr/testdata/TestBsr/session_not_multiplexed/closed/conn_1/connection.meta b/internal/bsr/testdata/TestBsr/session_not_multiplexed/closed/conn_1/connection-recording.meta similarity index 100% rename from internal/bsr/testdata/TestBsr/session_not_multiplexed/closed/conn_1/connection.meta rename to internal/bsr/testdata/TestBsr/session_not_multiplexed/closed/conn_1/connection-recording.meta diff --git a/internal/bsr/testdata/TestBsr/session_not_multiplexed/closed/session.summary b/internal/bsr/testdata/TestBsr/session_not_multiplexed/closed/session-recording-summary.json similarity index 100% rename from internal/bsr/testdata/TestBsr/session_not_multiplexed/closed/session.summary rename to internal/bsr/testdata/TestBsr/session_not_multiplexed/closed/session-recording-summary.json diff --git a/internal/bsr/testdata/TestBsr/session_not_multiplexed/closed/session-recording.meta b/internal/bsr/testdata/TestBsr/session_not_multiplexed/closed/session-recording.meta new file mode 100644 index 000000000..e54a47b9c --- /dev/null +++ b/internal/bsr/testdata/TestBsr/session_not_multiplexed/closed/session-recording.meta @@ -0,0 +1,3 @@ +id: session_123456789 +protocol: TEST +connection: conn_1.connection diff --git a/internal/bsr/testdata/TestBsr/session_not_multiplexed/closed/session.meta b/internal/bsr/testdata/TestBsr/session_not_multiplexed/closed/session.meta deleted file mode 100644 index cb1e86b02..000000000 --- a/internal/bsr/testdata/TestBsr/session_not_multiplexed/closed/session.meta +++ /dev/null @@ -1,36 +0,0 @@ -id: session_123456789 -protocol: TEST -user_publicId: user123 -user_scope_publicId: global -user_scope_type: global -target_publicId: target123 -target_scope_publicId: proj123 -target_scope_type: project -target_scope_parentId: org123 -target_defaultPort: 0 -target_sessionMaxSeconds: 0 -target_sessionConnectionLimit: 0 -worker_publicId: w_12345 -worker_version: 0.25.5 -worker_sha: beepboopgitsha -staticHost_publicId: host123 -staticHostCatalog_publicId: staticcat123 -staticHostCatalog_projectId: proj123 -staticHost_address: 127.0.0.1 -scs123_staticCredentialStore_publicId: scs123 -scs123_staticCredentialStore_projectId: proj123 -scs123_staticCredentialStore_credential_scjson123_staticJsonCredential_publicId: scjson123 -scs123_staticCredentialStore_credential_scjson123_staticJsonCredential_projectId: proj123 -scs123_staticCredentialStore_credential_scjson123_staticJsonCredential_objectHmac: hmac -vcs123_vaultCredentialStore_publicId: vcs123 -vcs123_vaultCredentialStore_projectId: proj123 -vcs123_vaultCredentialStore_vaultAddress: an/address -vcs123_vaultCredentialStore_namespace: name -vcs123_vaultCredentialStore_tlsServerName: imaserver -vcs123_vaultCredentialStore_tlsSkipVerify: false -vcs123_vaultCredentialStore_credential_vl123_vaultLibrary_publicId: vl123 -vcs123_vaultCredentialStore_credential_vl123_vaultLibrary_projectId: proj123 -vcs123_vaultCredentialStore_credential_vl123_vaultLibrary_vaultPath: /a/path -vcs123_vaultCredentialStore_credential_vl123_vaultLibrary_httpMethod: GET -vcs123_vaultCredentialStore_credential_vl123_vaultLibrary_credentialType: magic -connection: conn_1.connection diff --git a/internal/bsr/testdata/TestBsr/session_not_multiplexed/opened/.journal b/internal/bsr/testdata/TestBsr/session_not_multiplexed/opened/.journal index 6b3594f5b..6f93e76e5 100644 --- a/internal/bsr/testdata/TestBsr/session_not_multiplexed/opened/.journal +++ b/internal/bsr/testdata/TestBsr/session_not_multiplexed/opened/.journal @@ -2,10 +2,13 @@ CREATING SHA256SUM.sig CREATED SHA256SUM.sig CREATING SHA256SUM CREATED SHA256SUM -CREATING session.meta -CREATED session.meta -CREATING session.summary -CREATED session.summary +CREATING session-recording.meta +CREATED session-recording.meta +CREATING session-meta.json +CREATED session-meta.json +CLOSED session-meta.json +CREATING session-recording-summary.json +CREATED session-recording-summary.json CREATING bsrKey.pub CREATED bsrKey.pub CLOSING bsrKey.pub diff --git a/internal/bsr/testdata/TestBsr/session_not_multiplexed/opened/SHA256SUM b/internal/bsr/testdata/TestBsr/session_not_multiplexed/opened/SHA256SUM index 84e7230dd..a710d3ed0 100644 --- a/internal/bsr/testdata/TestBsr/session_not_multiplexed/opened/SHA256SUM +++ b/internal/bsr/testdata/TestBsr/session_not_multiplexed/opened/SHA256SUM @@ -2,4 +2,5 @@ .* wrappedBsrKey .* wrappedPrivKey .* pubKeyBsrSignature.sign -.* pubKeySelfSignature.sign \ No newline at end of file +.* pubKeySelfSignature.sign +.* session-meta.json diff --git a/internal/bsr/testdata/TestBsr/session_not_multiplexed/opened/conn_1/.journal b/internal/bsr/testdata/TestBsr/session_not_multiplexed/opened/conn_1/.journal index 815f145f4..e24aacf7c 100644 --- a/internal/bsr/testdata/TestBsr/session_not_multiplexed/opened/conn_1/.journal +++ b/internal/bsr/testdata/TestBsr/session_not_multiplexed/opened/conn_1/.journal @@ -2,7 +2,7 @@ CREATING SHA256SUM.sig CREATED SHA256SUM.sig CREATING SHA256SUM CREATED SHA256SUM -CREATING connection.meta -CREATED connection.meta -CREATING connection.summary -CREATED connection.summary +CREATING connection-recording.meta +CREATED connection-recording.meta +CREATING connection-recording-summary.json +CREATED connection-recording-summary.json diff --git a/internal/bsr/testdata/TestBsr/session_not_multiplexed/opened/conn_1/connection.summary b/internal/bsr/testdata/TestBsr/session_not_multiplexed/opened/conn_1/connection-recording-summary.json similarity index 100% rename from internal/bsr/testdata/TestBsr/session_not_multiplexed/opened/conn_1/connection.summary rename to internal/bsr/testdata/TestBsr/session_not_multiplexed/opened/conn_1/connection-recording-summary.json diff --git a/internal/bsr/testdata/TestBsr/session_not_multiplexed/opened/conn_1/connection.meta b/internal/bsr/testdata/TestBsr/session_not_multiplexed/opened/conn_1/connection-recording.meta similarity index 100% rename from internal/bsr/testdata/TestBsr/session_not_multiplexed/opened/conn_1/connection.meta rename to internal/bsr/testdata/TestBsr/session_not_multiplexed/opened/conn_1/connection-recording.meta diff --git a/internal/bsr/testdata/TestBsr/session_not_multiplexed/opened/session.summary b/internal/bsr/testdata/TestBsr/session_not_multiplexed/opened/session-recording-summary.json similarity index 100% rename from internal/bsr/testdata/TestBsr/session_not_multiplexed/opened/session.summary rename to internal/bsr/testdata/TestBsr/session_not_multiplexed/opened/session-recording-summary.json diff --git a/internal/bsr/testdata/TestBsr/session_not_multiplexed/opened/session-recording.meta b/internal/bsr/testdata/TestBsr/session_not_multiplexed/opened/session-recording.meta new file mode 100644 index 000000000..cdd180f95 --- /dev/null +++ b/internal/bsr/testdata/TestBsr/session_not_multiplexed/opened/session-recording.meta @@ -0,0 +1,2 @@ +id: session_123456789 +protocol: TEST diff --git a/internal/bsr/testdata/TestBsr/session_not_multiplexed/opened/session.meta b/internal/bsr/testdata/TestBsr/session_not_multiplexed/opened/session.meta deleted file mode 100644 index ca2f4b0c5..000000000 --- a/internal/bsr/testdata/TestBsr/session_not_multiplexed/opened/session.meta +++ /dev/null @@ -1,35 +0,0 @@ -id: session_123456789 -protocol: TEST -user_publicId: user123 -user_scope_publicId: global -user_scope_type: global -target_publicId: target123 -target_scope_publicId: proj123 -target_scope_type: project -target_scope_parentId: org123 -target_defaultPort: 0 -target_sessionMaxSeconds: 0 -target_sessionConnectionLimit: 0 -worker_publicId: w_12345 -worker_version: 0.25.5 -worker_sha: beepboopgitsha -staticHost_publicId: host123 -staticHostCatalog_publicId: staticcat123 -staticHostCatalog_projectId: proj123 -staticHost_address: 127.0.0.1 -scs123_staticCredentialStore_publicId: scs123 -scs123_staticCredentialStore_projectId: proj123 -scs123_staticCredentialStore_credential_scjson123_staticJsonCredential_publicId: scjson123 -scs123_staticCredentialStore_credential_scjson123_staticJsonCredential_projectId: proj123 -scs123_staticCredentialStore_credential_scjson123_staticJsonCredential_objectHmac: hmac -vcs123_vaultCredentialStore_publicId: vcs123 -vcs123_vaultCredentialStore_projectId: proj123 -vcs123_vaultCredentialStore_vaultAddress: an/address -vcs123_vaultCredentialStore_namespace: name -vcs123_vaultCredentialStore_tlsServerName: imaserver -vcs123_vaultCredentialStore_tlsSkipVerify: false -vcs123_vaultCredentialStore_credential_vl123_vaultLibrary_publicId: vl123 -vcs123_vaultCredentialStore_credential_vl123_vaultLibrary_projectId: proj123 -vcs123_vaultCredentialStore_credential_vl123_vaultLibrary_vaultPath: /a/path -vcs123_vaultCredentialStore_credential_vl123_vaultLibrary_httpMethod: GET -vcs123_vaultCredentialStore_credential_vl123_vaultLibrary_credentialType: magic diff --git a/internal/bsr/testing.go b/internal/bsr/testing.go index fa80f052e..9f706b9ba 100644 --- a/internal/bsr/testing.go +++ b/internal/bsr/testing.go @@ -3,60 +3,30 @@ package bsr -func TestSessionMeta(s string, p Protocol) *SessionMeta { - scope := Scope{ - PublicId: "global", - Type: "global", - } - staticCatalog := StaticHostCatalog{ - PublicId: "staticcat123", - ProjectId: "proj123", - } - creds := make([]StaticCredential, 0) - c := StaticJsonCredential{ - PublicId: "scjson123", - ProjectId: "proj123", - ObjectHmac: []byte("hmac"), - } - creds = append(creds, c) - staticCredentialStore := make([]StaticCredentialStore, 0) - scs := StaticCredentialStore{ - PublicId: "scs123", - ProjectId: "proj123", - Credentials: creds, - } - staticCredentialStore = append(staticCredentialStore, scs) - vcreds := make([]DynamicCredentialLibraries, 0) - v := VaultLibrary{ - PublicId: "vl123", - ProjectId: "proj123", - VaultPath: "/a/path", - HttpMethod: "GET", - CredentialType: "magic", - } - vcreds = append(vcreds, v) - vaultCredentialStore := make([]VaultCredentialStore, 0) - vcs := VaultCredentialStore{ - PublicId: "vcs123", - ProjectId: "proj123", - VaultAddress: "an/address", - Namespace: "name", - TlsServerName: "imaserver", - TlsSkipVerify: false, - CredentialLibraries: vcreds, - } - vaultCredentialStore = append(vaultCredentialStore, vcs) - sessionMeta := &SessionMeta{ +func TestSessionRecordingMeta(s string, p Protocol) *SessionRecordingMeta { + return &SessionRecordingMeta{ Id: s, Protocol: p, + } +} + +func TestSessionMeta(s string) *SessionMeta { + return &SessionMeta{ + PublicId: s, User: &User{ PublicId: "user123", - Scope: scope, + Scope: Scope{ + PublicId: "global", + Type: "global", + }, }, StaticHost: &StaticHost{ PublicId: "host123", - Catalog: staticCatalog, - Address: "127.0.0.1", + Catalog: StaticHostCatalog{ + PublicId: "staticcat123", + ProjectId: "proj123", + }, + Address: "127.0.0.1", }, Target: &Target{ PublicId: "target123", @@ -74,8 +44,37 @@ func TestSessionMeta(s string, p Protocol) *SessionMeta { Version: "0.25.5", Sha: "beepboopgitsha", }, - StaticCredentialStore: staticCredentialStore, - VaultCredentialStore: vaultCredentialStore, + StaticCredentialStore: []StaticCredentialStore{ + { + PublicId: "scs123", + ProjectId: "proj123", + StaticJsonCredentials: []StaticJsonCredential{ + { + PublicId: "scjson123", + ProjectId: "proj123", + ObjectHmac: []byte("hmac"), + }, + }, + }, + }, + VaultCredentialStore: []VaultCredentialStore{ + { + PublicId: "vcs123", + ProjectId: "proj123", + VaultAddress: "an/address", + Namespace: "name", + TlsServerName: "imaserver", + TlsSkipVerify: false, + VaultGenericLibraries: []VaultLibrary{ + { + PublicId: "vl123", + ProjectId: "proj123", + VaultPath: "/a/path", + HttpMethod: "GET", + CredentialType: "magic", + }, + }, + }, + }, } - return sessionMeta } diff --git a/internal/bsr/types.go b/internal/bsr/types.go index 3b78f4429..6f318c061 100644 --- a/internal/bsr/types.go +++ b/internal/bsr/types.go @@ -25,6 +25,7 @@ type ( EndTime time.Time BytesUp uint64 BytesDown uint64 + Errors error } // ChannelSummary encapsulates data for a channel, including its id, channel type,