CreateOperation should only be implemented alongside ExistenceCheck (#18492)

* CreateOperation should only be implemented alongside ExistenceCheck

Closes #12329

Vault treats all POST or PUT HTTP requests equally - they default to
being treated as UpdateOperations, but, if a backend implements an
ExistenceCheck function, CreateOperations can be separated out when the
existence check returns false.

It follows, then, that if a CreateOperation handler is implemented
without an ExistenceCheck function, this is unreachable code - a coding
error. It's a fairly minor error in the grand scheme of things, but it
causes the generated OpenAPI spec to include x-vault-createSupported for
operations on which create can never actually be invoked - and promotes
muddled understanding of the create/update feature.

In this PR:

1) Implement a new test, which checks all builtin auth methods and
   secrets engines can be successfully initialized. (This is important
   to validate the next part.)

2) Expand upon the existing coding error checks built in to
   framework.Backend, adding a check for this misuse of CreateOperation.

3) Fix up instances of improper CreateOperation within the Vault
   repository - just two, transit and mock.

Note: At this point, the newly added test will **fail**.

There are improper uses of CreateOperation in all of the following:

    vault-plugin-auth-cf
    vault-plugin-auth-kerberos
    vault-plugin-auth-kubernetes
    vault-plugin-secrets-ad
    vault-plugin-secrets-gcpkms
    vault-plugin-secrets-kubernetes
    vault-plugin-secrets-kv
    vault-plugin-secrets-openldap
    vault-plugin-secrets-terraform

each of which needs to be fixed and updated in go.mod here, before this
new check can be added.

* Add subtests

* Add in testing of KV v2, which otherwise doesn't get tested

This is a surprisingly complicated special case

* The database plugin needs special handling as well, and add in help invocations of the builtin backends too

* Fix extra package prefix

* Add changelog

* Update 6 out of 9 plugins to needed new versions

Note, this IS an upgrade despite the apparent version numbers going
down. (That's a consequence of slightly odd release management occurring
in the plugin repositories.)

* Update to deal with code changes since branch originally created

* Perform necessary update of vault-plugin-secrets-kubernetes so that CI checks on PR can run

* Fix another instance of incorrect CreateOperation, for a test-only endpoint

By being hidden behind a Go build constraint, it had evaded notice until
now.

* Add an opportunistic test of sys/internal/specs/openapi too
This commit is contained in:
Max Bowsher
2023-07-18 13:44:15 +01:00
committed by GitHub
parent a9977fab80
commit 00e13abf1f
8 changed files with 188 additions and 19 deletions

View File

@@ -486,12 +486,28 @@ func (b *Backend) WriteSafeReplicationState() bool {
!replicationState.HasState(consts.ReplicationPerformanceStandby)
}
// init runs as a sync.Once function from any plugin entry point which needs to route requests by paths.
// It may panic if a coding error in the plugin is detected.
// For builtin plugins, this is unit tested in helper/builtinplugins/builtinplugins_test.go.
// For other plugins, any unit test that attempts to perform any request to the plugin will exercise these checks.
func (b *Backend) init() {
b.pathsRe = make([]*regexp.Regexp, len(b.Paths))
for i, p := range b.Paths {
// Detect the coding error of failing to initialise Pattern
if len(p.Pattern) == 0 {
panic(fmt.Sprintf("Routing pattern cannot be blank"))
}
// Detect the coding error of attempting to define a CreateOperation without defining an ExistenceCheck
if p.ExistenceCheck == nil {
if _, ok := p.Operations[logical.CreateOperation]; ok {
panic(fmt.Sprintf("Pattern %v defines a CreateOperation but no ExistenceCheck", p.Pattern))
}
if _, ok := p.Callbacks[logical.CreateOperation]; ok {
panic(fmt.Sprintf("Pattern %v defines a CreateOperation but no ExistenceCheck", p.Pattern))
}
}
// Automatically anchor the pattern
if p.Pattern[0] != '^' {
p.Pattern = "^" + p.Pattern
@@ -499,6 +515,8 @@ func (b *Backend) init() {
if p.Pattern[len(p.Pattern)-1] != '$' {
p.Pattern = p.Pattern + "$"
}
// Detect the coding error of an invalid Pattern
b.pathsRe[i] = regexp.MustCompile(p.Pattern)
}
}