Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Adds replication state helper to framework.Backend #21743

Merged
merged 4 commits into from
Jul 11, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 2 additions & 7 deletions builtin/logical/aws/backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import (
"github.com/aws/aws-sdk-go/service/iam/iamiface"
"github.com/aws/aws-sdk-go/service/sts/stsiface"
"github.com/hashicorp/vault/sdk/framework"
"github.com/hashicorp/vault/sdk/helper/consts"
"github.com/hashicorp/vault/sdk/logical"
"github.com/hashicorp/vault/sdk/queue"
)
Expand All @@ -32,7 +31,7 @@ func Factory(ctx context.Context, conf *logical.BackendConfig) (logical.Backend,
return b, nil
}

func Backend(conf *logical.BackendConfig) *backend {
func Backend(_ *logical.BackendConfig) *backend {
var b backend
b.credRotationQueue = queue.New()
b.Backend = &framework.Backend{
Expand Down Expand Up @@ -67,11 +66,7 @@ func Backend(conf *logical.BackendConfig) *backend {
WALRollback: b.walRollback,
WALRollbackMinAge: minAwsUserRollbackAge,
PeriodicFunc: func(ctx context.Context, req *logical.Request) error {
repState := conf.System.ReplicationState()
if (conf.System.LocalMount() ||
!repState.HasState(consts.ReplicationPerformanceSecondary)) &&
!repState.HasState(consts.ReplicationDRSecondary) &&
!repState.HasState(consts.ReplicationPerformanceStandby) {
if b.WriteSafeReplicationState() {
return b.rotateExpiredStaticCreds(ctx, req)
}
return nil
Expand Down
2 changes: 1 addition & 1 deletion builtin/logical/database/backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ func Factory(ctx context.Context, conf *logical.BackendConfig) (logical.Backend,

b.credRotationQueue = queue.New()
// Load queue and kickoff new periodic ticker
go b.initQueue(b.queueCtx, conf, conf.System.ReplicationState())
go b.initQueue(b.queueCtx, conf)

// collect metrics on number of plugin instances
var err error
Expand Down
7 changes: 2 additions & 5 deletions builtin/logical/database/rotation.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ import (
"github.com/hashicorp/go-secure-stdlib/strutil"
v5 "github.com/hashicorp/vault/sdk/database/dbplugin/v5"
"github.com/hashicorp/vault/sdk/framework"
"github.com/hashicorp/vault/sdk/helper/consts"
"github.com/hashicorp/vault/sdk/helper/locksutil"
"github.com/hashicorp/vault/sdk/logical"
"github.com/hashicorp/vault/sdk/queue"
Expand Down Expand Up @@ -517,14 +516,12 @@ func (b *databaseBackend) setStaticAccount(ctx context.Context, s logical.Storag
// not wait for success or failure of it's tasks before continuing. This is to
// avoid blocking the mount process while loading and evaluating existing roles,
// etc.
func (b *databaseBackend) initQueue(ctx context.Context, conf *logical.BackendConfig, replicationState consts.ReplicationState) {
func (b *databaseBackend) initQueue(ctx context.Context, conf *logical.BackendConfig) {
// Verify this mount is on the primary server, or is a local mount. If not, do
// not create a queue or launch a ticker. Both processing the WAL list and
// populating the queue are done sequentially and before launching a
// go-routine to run the periodic ticker.
if (conf.System.LocalMount() || !replicationState.HasState(consts.ReplicationPerformanceSecondary)) &&
!replicationState.HasState(consts.ReplicationDRSecondary) &&
!replicationState.HasState(consts.ReplicationPerformanceStandby) {
if b.WriteSafeReplicationState() {
b.Logger().Info("initializing database rotation queue")

// Poll for a PutWAL call that does not return a "read-only storage" error.
Expand Down
3 changes: 1 addition & 2 deletions builtin/logical/database/rotation_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ import (
postgreshelper "github.com/hashicorp/vault/helper/testhelpers/postgresql"
v5 "github.com/hashicorp/vault/sdk/database/dbplugin/v5"
"github.com/hashicorp/vault/sdk/framework"
"github.com/hashicorp/vault/sdk/helper/consts"
"github.com/hashicorp/vault/sdk/helper/dbtxn"
"github.com/hashicorp/vault/sdk/helper/pluginutil"
"github.com/hashicorp/vault/sdk/logical"
Expand Down Expand Up @@ -1224,7 +1223,7 @@ func TestStoredWALsCorrectlyProcessed(t *testing.T) {
b.credRotationQueue = queue.New()

// Now finish the startup process by populating the queue, which should discard the WAL
b.initQueue(ctx, config, consts.ReplicationUnknown)
b.initQueue(ctx, config)

if tc.shouldRotate {
requireWALs(t, storage, 1)
Expand Down
3 changes: 3 additions & 0 deletions changelog/21743.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:improvement
sdk/framework: Adds replication state helper for backends to check for read-only storage
```
20 changes: 20 additions & 0 deletions sdk/framework/backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,11 @@ type Backend struct {

// InitializeFunc is the callback, which if set, will be invoked via
// Initialize() just after a plugin has been mounted.
//
// Note that storage writes should only occur on the active instance within a
// primary cluster or local mount on a performance secondary. If your InitializeFunc
// writes to storage, you can use the backend's WriteSafeReplicationState() method
// to prevent it from attempting to write on a Vault instance with read-only storage.
InitializeFunc InitializeFunc

// PeriodicFunc is the callback, which if set, will be invoked when the
Expand All @@ -70,6 +75,11 @@ type Backend struct {
// entries in backend's storage, while the backend is still being used.
// (Note the difference between this action and `Clean`, which is
// invoked just before the backend is unmounted).
//
// Note that storage writes should only occur on the active instance within a
// primary cluster or local mount on a performance secondary. If your PeriodicFunc
// writes to storage, you can use the backend's WriteSafeReplicationState() method
// to prevent it from attempting to write on a Vault instance with read-only storage.
PeriodicFunc periodicFunc

// WALRollback is called when a WAL entry (see wal.go) has to be rolled
Expand Down Expand Up @@ -466,6 +476,16 @@ func (b *Backend) Secret(k string) *Secret {
return nil
}

// WriteSafeReplicationState returns true if this backend instance is capable of writing
// to storage without receiving an ErrReadOnly error. The active instance in a primary
// cluster or a local mount on a performance secondary is capable of writing to storage.
func (b *Backend) WriteSafeReplicationState() bool {
Copy link
Contributor Author

@austingebauer austingebauer Jul 11, 2023

Choose a reason for hiding this comment

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

We could also invert this to something like HasReadOnlyStorage(). This PKI conditional backend.go#L454 is an example of what that could look like.

I'm open either way. Lmk your thoughts!

Copy link
Contributor

Choose a reason for hiding this comment

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

I prefer WriteSafeReplicationState.

replicationState := b.System().ReplicationState()
return (b.System().LocalMount() || !replicationState.HasState(consts.ReplicationPerformanceSecondary)) &&
!replicationState.HasState(consts.ReplicationDRSecondary) &&
!replicationState.HasState(consts.ReplicationPerformanceStandby)
}

func (b *Backend) init() {
b.pathsRe = make([]*regexp.Regexp, len(b.Paths))
for i, p := range b.Paths {
Expand Down