feat(bsr): Sync BSR keys

This commit is contained in:
irenarindos authored and Timothy Messier committed 2023-05-31 12:18:13 +00:00
1 parent 51e6e64d09
commit 444af9151c
14 files changed
+394 -9

No files matched your search

+60
View File
@@ -5,6 +5,7 @@ package bsr
import (
"context"
"errors"
"fmt"
"io"
@@ -12,6 +13,7 @@ import (
"github.com/hashicorp/boundary/internal/bsr/internal/is"
"github.com/hashicorp/boundary/internal/bsr/kms"
"github.com/hashicorp/boundary/internal/storage"
"google.golang.org/protobuf/proto"
)
const (
@@ -20,6 +22,12 @@ const (
channelFile = "%s.channel"
messagesFile = "messages-%s.data"
requestsFile = "requests-%s.data"
bsrPubKeyFile = "bsrKey.pub"
wrappedBsrKeyFile = "wrappedBsrKey"
wrappedPrivKeyFile = "wrappedPrivKey"
pubKeyBsrSignatureFile = "pubKeyBsrSignature.sign"
pubKeySelfSignatureFile = "pubKeySelfSignature.sign"
)
// SessionMeta contains metadata about a session in a BSR.
@@ -63,6 +71,13 @@ func NewSession(ctx context.Context, meta *SessionMeta, f storage.FS, keys *kms.
if err != nil {
return nil, err
}
// Sync keys and signatures
err = persistBsrSessionKeys(ctx, keys, nc)
if err != nil {
return nil, errors.Join(err, fmt.Errorf("%s: %w", op, ErrBsrKeyPersistenceFailure))
}
_, err = nc.WriteMeta(ctx, "id", meta.Id)
if err != nil {
return nil, err
@@ -78,6 +93,51 @@ func NewSession(ctx context.Context, meta *SessionMeta, f storage.FS, keys *kms.
}, nil
}
// persistBsrSessionKeys will marshal, write, and close BSR keys locally before syncing to
// storage. Any error while syncing the key files should result in the termination of
// the session this recording is for.
// The key files synced are:
// * the BSR public key, bsrKey.pub
// * the wrapped BSR key, wrappedBsrKey
// * the wrapped private key, wrappedPrivKey
// * the public key BSR signature, pubKeyBsrSignature.sign
// * the public key self signature, pubKeySelfSignature.sign
func persistBsrSessionKeys(ctx context.Context, keys *kms.Keys, c *container) error {
const op = "bsr.persistBsrSessionKeys"
switch {
case keys.PubKey == nil:
return fmt.Errorf("%s: missing kms pub key: %w", op, ErrInvalidParameter)
case keys.WrappedBsrKey == nil:
return fmt.Errorf("%s: missing kms wrapped BSR key: %w", op, ErrInvalidParameter)
case keys.WrappedPrivKey == nil:
return fmt.Errorf("%s: missing kms wrapped priv key: %w", op, ErrInvalidParameter)
case keys.PubKeyBsrSignature == nil:
return fmt.Errorf("%s: missing kms pub key BSR signature: %w", op, ErrInvalidParameter)
case keys.PubKeySelfSignature == nil:
return fmt.Errorf("%s: missing kms pub key self signature: %w", op, ErrInvalidParameter)
}
keyFiles := map[string]proto.Message{
bsrPubKeyFile: keys.PubKey,
wrappedBsrKeyFile: keys.WrappedBsrKey,
wrappedPrivKeyFile: keys.WrappedPrivKey,
pubKeyBsrSignatureFile: keys.PubKeyBsrSignature,
pubKeySelfSignatureFile: keys.PubKeySelfSignature,
}
for f, k := range keyFiles {
b, err := proto.Marshal(k)
if err != nil {
return fmt.Errorf("%s: failed to marshal data for %s: %w", op, f, err)
}
err = c.syncBsrKey(ctx, f, b)
if err != nil {
return fmt.Errorf("%s: failed syncing bsr key %s: %w", op, f, err)
}
}
return nil
}
// NewConnection creates a Connection container for a given connection id.
func (s *Session) NewConnection(ctx context.Context, meta *ConnectionMeta) (*Connection, error) {
const op = "bsr.(Session).NewConnection"
+105 -3
View File
@@ -11,6 +11,7 @@ import (
"os"
"path/filepath"
"regexp"
"strings"
"testing"
"github.com/google/go-cmp/cmp"
@@ -52,14 +53,23 @@ func assertContainer(ctx context.Context, t *testing.T, path, state string, typ
require.NoError(t, err, "unable to find test data file")
checksums, ok := fs.Files["SHA256SUM"]
require.True(t, ok, "container is missing checksums file")
assert.Equal(t, string(wantChecksums), checksums.Buf.String())
checksumSlice := strings.Split(string(wantChecksums), "\n")
var wChecksumsRegex []*regexp.Regexp
for _, c := range checksumSlice {
r, err := regexp.Compile(c)
require.NoError(t, err)
wChecksumsRegex = append(wChecksumsRegex, r)
}
for _, cr := range wChecksumsRegex {
assert.True(t, cr.MatchString(checksums.Buf.String()))
}
// SHA256SUM.sig signature file
sig, ok := fs.Files["SHA256SUM.sig"]
require.True(t, ok, "container is missing sig file")
switch state {
case "closed":
want, err := keys.SignWithPrivKey(ctx, wantChecksums)
want, err := keys.SignWithPrivKey(ctx, checksums.Buf.Bytes())
require.NoError(t, err)
got := &wrapping.SigInfo{}
@@ -82,7 +92,39 @@ func assertContainer(ctx context.Context, t *testing.T, path, state string, typ
require.NoError(t, err, "unable to find test data file")
journal, ok := fs.Files[".journal"]
require.True(t, ok, "container is missing journal file")
assert.Equal(t, string(wantJournal), journal.Buf.String())
journalSlice := strings.Split(string(wantJournal), "\n")
var wJournalRegex []*regexp.Regexp
for _, c := range journalSlice {
r, err := regexp.Compile(c)
require.NoError(t, err)
wJournalRegex = append(wJournalRegex, r)
}
for _, cr := range wJournalRegex {
assert.True(t, cr.MatchString(journal.Buf.String()))
}
if typ == "session" {
// BSR keys, if this is a session container
bsrPub, ok := fs.Files["bsrKey.pub"]
require.True(t, ok, "container is missing bsrPub file")
assert.NotEmpty(t, bsrPub.Buf.String())
wrappedBsrKey, ok := fs.Files["wrappedBsrKey"]
require.True(t, ok, "container is missing wrappedBsrKey file")
assert.NotEmpty(t, wrappedBsrKey.Buf.String())
wrappedPrivKey, ok := fs.Files["wrappedPrivKey"]
require.True(t, ok, "container is missing wrappedPrivKey file")
assert.NotEmpty(t, wrappedPrivKey.Buf.String())
pubKeyBsrSignature, ok := fs.Files["pubKeyBsrSignature.sign"]
require.True(t, ok, "container is missing pubKeyBsrSignature.sign file")
assert.NotEmpty(t, pubKeyBsrSignature.Buf.String())
pubKeySelfSignature, ok := fs.Files["pubKeySelfSignature.sign"]
require.True(t, ok, "container is missing pubKeySelfSignature.sign file")
assert.NotEmpty(t, pubKeySelfSignature.Buf.String())
}
}
type connection struct {
@@ -362,6 +404,66 @@ func TestNewSessionErrors(t *testing.T) {
nil,
errors.New("bsr.NewSession: missing kms keys: invalid parameter"),
},
{
"missing-bsr-signature",
&bsr.SessionMeta{Id: "session"},
&fstest.MemFS{},
&kms.Keys{
PubKey: keys.PubKey,
WrappedBsrKey: keys.WrappedBsrKey,
WrappedPrivKey: keys.WrappedPrivKey,
PubKeySelfSignature: keys.PubKeySelfSignature,
},
errors.New("bsr.persistBsrSessionKeys: missing kms pub key BSR signature: invalid parameter\nbsr.NewSession: could not persist BSR keys"),
},
{
"missing-pub-signature",
&bsr.SessionMeta{Id: "session"},
&fstest.MemFS{},
&kms.Keys{
PubKey: keys.PubKey,
WrappedBsrKey: keys.WrappedBsrKey,
WrappedPrivKey: keys.WrappedPrivKey,
PubKeyBsrSignature: keys.PubKeyBsrSignature,
},
errors.New("bsr.persistBsrSessionKeys: missing kms pub key self signature: invalid parameter\nbsr.NewSession: could not persist BSR keys"),
},
{
"missing-pub-key",
&bsr.SessionMeta{Id: "session"},
&fstest.MemFS{},
&kms.Keys{
WrappedBsrKey: keys.WrappedBsrKey,
WrappedPrivKey: keys.WrappedPrivKey,
PubKeySelfSignature: keys.PubKeySelfSignature,
PubKeyBsrSignature: keys.PubKeyBsrSignature,
},
errors.New("bsr.persistBsrSessionKeys: missing kms pub key: invalid parameter\nbsr.NewSession: could not persist BSR keys"),
},
{
"missing-wrapped-bsr-key",
&bsr.SessionMeta{Id: "session"},
&fstest.MemFS{},
&kms.Keys{
PubKey: keys.PubKey,
WrappedPrivKey: keys.WrappedPrivKey,
PubKeySelfSignature: keys.PubKeySelfSignature,
PubKeyBsrSignature: keys.PubKeyBsrSignature,
},
errors.New("bsr.persistBsrSessionKeys: missing kms wrapped BSR key: invalid parameter\nbsr.NewSession: could not persist BSR keys"),
},
{
"missing-wrapped-priv-key",
&bsr.SessionMeta{Id: "session"},
&fstest.MemFS{},
&kms.Keys{
PubKey: keys.PubKey,
WrappedBsrKey: keys.WrappedBsrKey,
PubKeySelfSignature: keys.PubKeySelfSignature,
PubKeyBsrSignature: keys.PubKeyBsrSignature,
},
errors.New("bsr.persistBsrSessionKeys: missing kms wrapped priv key: invalid parameter\nbsr.NewSession: could not persist BSR keys"),
},
{
"fs-new-error",
&bsr.SessionMeta{Id: "session"},
+59 -6
View File
@@ -105,21 +105,74 @@ func newContainer(ctx context.Context, t containerType, c storage.Container, key
return cc, nil
}
// Create creates a new file in the container for writing.
func (c *container) create(ctx context.Context, s string) (storage.File, error) {
c.journal.Record("CREATING", s)
f, err := c.container.Create(ctx, s)
// create creates a new file in the container for writing.
func (c *container) create(ctx context.Context, s string, options ...storage.Option) (storage.File, error) {
const op = "bsr.(container).create"
err := c.journal.Record("CREATING", s)
if err != nil {
return nil, err
return nil, fmt.Errorf("%s: %w", op, err)
}
var f storage.File
switch len(options) {
case 0:
f, err = c.container.Create(ctx, s)
if err != nil {
return nil, fmt.Errorf("%s: %w", op, err)
}
default:
f, err = c.container.OpenFile(ctx, s, options...)
if err != nil {
return nil, fmt.Errorf("%s: %w", op, err)
}
}
jf, err := journal.NewFile(ctx, f, c.journal)
if err != nil {
return nil, err
return nil, fmt.Errorf("%s: %w", op, err)
}
defer c.journal.Record("CREATED", s)
return jf, 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
func (c *container) syncBsrKey(ctx context.Context, s string, data []byte) error {
const op = "bsr.(container).syncBsrKey"
switch {
case len(s) == 0:
return fmt.Errorf("%s: missing file name %w", op, ErrInvalidParameter)
case data == nil:
return fmt.Errorf("%s: missing data payload %w", op, ErrInvalidParameter)
}
jf, err := c.create(ctx, s, storage.WithCreateFile(),
storage.WithFileAccessMode(storage.WriteOnly),
storage.WithCloseSyncMode(storage.Synchronous))
if err != nil {
return fmt.Errorf("%s: %w", op, err)
}
cf, err := checksum.NewFile(ctx, jf, c.checksums)
if err != nil {
return fmt.Errorf("%s: %w", op, err)
}
_, err = cf.Write(data)
if err != nil {
return fmt.Errorf("%s: %w", op, err)
}
err = cf.Close()
if err != nil {
return fmt.Errorf("%s: %w", op, err)
}
return nil
}
// writeMetaString writes a string to the containers meta file.
func (c *container) writeMetaString(_ context.Context, s string) (int, error) {
return c.meta.WriteString(s)
+66
View File
@@ -0,0 +1,66 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0
package bsr
import (
"context"
"fmt"
"testing"
"github.com/hashicorp/boundary/internal/bsr/internal/fstest"
"github.com/hashicorp/boundary/internal/bsr/kms"
"github.com/stretchr/testify/require"
)
func TestSyncBsrKeys(t *testing.T) {
ctx := context.Background()
keys, err := kms.CreateKeys(ctx, kms.TestWrapper(t), "session")
require.NoError(t, err)
f := &fstest.MemFS{}
fc, err := f.New(ctx, fmt.Sprintf(bsrFile, "session-id"))
require.NoError(t, err)
c, err := newContainer(ctx, sessionContainer, fc, keys)
require.NoError(t, err)
require.NotNil(t, c)
cases := []struct {
name string
fname string
data []byte
expErr bool
expErrMsg string
}{
{
name: "no filename",
data: []byte("got no name"),
expErr: true,
expErrMsg: "bsr.(container).syncBsrKey: missing file name invalid parameter",
},
{
name: "no data",
fname: "i have a name",
expErr: true,
expErrMsg: "bsr.(container).syncBsrKey: missing data payload invalid parameter",
},
{
name: "success",
fname: "i have a name",
data: []byte("payload coming thru"),
},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
err := c.syncBsrKey(ctx, tc.fname, tc.data)
if tc.expErr {
require.EqualError(t, err, tc.expErrMsg)
return
}
require.NoError(t, err)
})
}
}
+3
View File
@@ -16,3 +16,6 @@ var ErrInvalidParameter = errors.New("invalid parameter")
// ErrNotSupported represents an operation that is not supported for a
// particular protocol.
var ErrNotSupported = errors.New("not supported by protocol")
// ErrBsrKeyPersistenceFailure indicates a failure in persisting BSR encryption keys
var ErrBsrKeyPersistenceFailure = errors.New("could not persist BSR keys")
@@ -90,6 +90,7 @@ func (f *File) Close() error {
var closeErrors *multierror.Error
// Call stat before closure; calling it after results in an err
s, err := f.Stat()
if err != nil {
closeErrors = multierror.Append(closeErrors, fmt.Errorf("%s: %w", op, err))
@@ -6,6 +6,26 @@ CREATING session.meta
CREATED session.meta
CREATING session.summary
CREATED session.summary
CREATING bsrKey.pub
CREATED bsrKey.pub
CLOSING bsrKey.pub
CLOSED bsrKey.pub
CREATING wrappedBsrKey
CREATED wrappedBsrKey
CLOSING wrappedBsrKey
CLOSED wrappedBsrKey
CREATING wrappedPrivKey
CREATED wrappedPrivKey
CLOSING wrappedPrivKey
CLOSED wrappedPrivKey
CREATING pubKeyBsrSignature.sign
CREATED pubKeyBsrSignature.sign
CLOSING pubKeyBsrSignature.sign
CLOSED pubKeyBsrSignature.sign
CREATING pubKeySelfSignature.sign
CREATED pubKeySelfSignature.sign
CLOSING pubKeySelfSignature.sign
CLOSED pubKeySelfSignature.sign
CLOSING session.meta
CLOSED session.meta
CLOSING session.summary
@@ -1,2 +1,7 @@
.* bsrKey.pub
.* wrappedBsrKey
.* wrappedPrivKey
.* pubKeyBsrSignature.sign
.* pubKeySelfSignature.sign
a7b7dc4a1c6032980f84d22afb71d59ccdf498db37f2e855a83d8854bdc297db session.meta
e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 session.summary
@@ -6,3 +6,23 @@ CREATING session.meta
CREATED session.meta
CREATING session.summary
CREATED session.summary
CREATING bsrKey.pub
CREATED bsrKey.pub
CLOSING bsrKey.pub
CLOSED bsrKey.pub
CREATING wrappedBsrKey
CREATED wrappedBsrKey
CLOSING wrappedBsrKey
CLOSED wrappedBsrKey
CREATING wrappedPrivKey
CREATED wrappedPrivKey
CLOSING wrappedPrivKey
CLOSED wrappedPrivKey
CREATING pubKeyBsrSignature.sign
CREATED pubKeyBsrSignature.sign
CLOSING pubKeyBsrSignature.sign
CLOSED pubKeyBsrSignature.sign
CREATING pubKeySelfSignature.sign
CREATED pubKeySelfSignature.sign
CLOSING pubKeySelfSignature.sign
CLOSED pubKeySelfSignature.sign
@@ -0,0 +1,5 @@
.* bsrKey.pub
.* wrappedBsrKey
.* wrappedPrivKey
.* pubKeyBsrSignature.sign
.* pubKeySelfSignature.sign
@@ -6,6 +6,26 @@ CREATING session.meta
CREATED session.meta
CREATING session.summary
CREATED session.summary
CREATING bsrKey.pub
CREATED bsrKey.pub
CLOSING bsrKey.pub
CLOSED bsrKey.pub
CREATING wrappedBsrKey
CREATED wrappedBsrKey
CLOSING wrappedBsrKey
CLOSED wrappedBsrKey
CREATING wrappedPrivKey
CREATED wrappedPrivKey
CLOSING wrappedPrivKey
CLOSED wrappedPrivKey
CREATING pubKeyBsrSignature.sign
CREATED pubKeyBsrSignature.sign
CLOSING pubKeyBsrSignature.sign
CLOSED pubKeyBsrSignature.sign
CREATING pubKeySelfSignature.sign
CREATED pubKeySelfSignature.sign
CLOSING pubKeySelfSignature.sign
CLOSED pubKeySelfSignature.sign
CLOSING session.meta
CLOSED session.meta
CLOSING session.summary
@@ -1,2 +1,7 @@
.* bsrKey.pub
.* wrappedBsrKey
.* wrappedPrivKey
.* pubKeyBsrSignature.sign
.* pubKeySelfSignature.sign
a7b7dc4a1c6032980f84d22afb71d59ccdf498db37f2e855a83d8854bdc297db session.meta
e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 session.summary
@@ -6,3 +6,23 @@ CREATING session.meta
CREATED session.meta
CREATING session.summary
CREATED session.summary
CREATING bsrKey.pub
CREATED bsrKey.pub
CLOSING bsrKey.pub
CLOSED bsrKey.pub
CREATING wrappedBsrKey
CREATED wrappedBsrKey
CLOSING wrappedBsrKey
CLOSED wrappedBsrKey
CREATING wrappedPrivKey
CREATED wrappedPrivKey
CLOSING wrappedPrivKey
CLOSED wrappedPrivKey
CREATING pubKeyBsrSignature.sign
CREATED pubKeyBsrSignature.sign
CLOSING pubKeyBsrSignature.sign
CLOSED pubKeyBsrSignature.sign
CREATING pubKeySelfSignature.sign
CREATED pubKeySelfSignature.sign
CLOSING pubKeySelfSignature.sign
CLOSED pubKeySelfSignature.sign
@@ -0,0 +1,5 @@
.* bsrKey.pub
.* wrappedBsrKey
.* wrappedPrivKey
.* pubKeyBsrSignature.sign
.* pubKeySelfSignature.sign