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

feature: secrets/auth plugin multiplexing #14946

Merged
merged 69 commits into from Aug 30, 2022
Merged
Show file tree
Hide file tree
Changes from 61 commits
Commits
Show all changes
69 commits
Select commit Hold shift + click to select a range
a702d4b
enable registering backend muxed plugins in plugin catalog
fairclothjm Mar 30, 2022
999c5d5
set the sysview on the pluginconfig to allow enabling secrets/auth pl…
fairclothjm Mar 31, 2022
9dd31c5
store backend instances in map
fairclothjm Mar 31, 2022
d25e042
store single implementations in the instances map
fairclothjm Apr 1, 2022
c7b4e26
fix system backend unit tests
fairclothjm Apr 6, 2022
270de0a
return error(s) if we can't get the plugin client
fairclothjm Apr 6, 2022
7a9f116
refactor/move GetMultiplexIDFromContext test
fairclothjm Apr 6, 2022
a65eb24
add changelog
fairclothjm Apr 6, 2022
db60f6e
remove unnecessary field on pluginClient
fairclothjm Apr 7, 2022
151b439
add unit tests to PluginCatalog for secrets/auth plugins
fairclothjm Apr 8, 2022
fa1aa94
fix comment
fairclothjm Apr 8, 2022
bfdafb8
return pluginClient from TestRunTestPlugin
fairclothjm Apr 8, 2022
e498420
add multiplexed backend test
fairclothjm Apr 11, 2022
53713ad
honor metadatamode value in newbackend pluginconfig
fairclothjm Apr 12, 2022
9ae3b45
check that connection exists on cleanup
fairclothjm Apr 29, 2022
10a5bdc
add automtls to secrets/auth plugins
fairclothjm Apr 20, 2022
b55b86a
don't remove apiclientmeta parsing
fairclothjm May 6, 2022
4f93d8f
use formatting directive for fmt.Errorf
fairclothjm May 6, 2022
b0a2ebf
fix ut: remove tls provider func
fairclothjm May 6, 2022
d108d0d
remove tlsproviderfunc from backend plugin tests
fairclothjm May 6, 2022
92c4217
use env var to prevent test plugin from running as a unit test
fairclothjm May 6, 2022
62753b8
WIP: remove lazy loading
fairclothjm May 4, 2022
8c14cc4
move non lazy loaded backend to new package
fairclothjm May 11, 2022
3fa3702
use version wrapper for backend plugin factory
fairclothjm May 12, 2022
c9a39a6
remove backendVersionWrapper type
fairclothjm May 12, 2022
90b5789
implement getBackendPluginType for plugin catalog
fairclothjm May 12, 2022
b4d1202
handle backend plugin v4 registration
fairclothjm May 13, 2022
774a6dd
add plugin automtls env guard
fairclothjm May 16, 2022
8805c19
modify plugin factory to determine the backend to use
fairclothjm May 16, 2022
18ca49c
remove old pluginsets from v5 and log pid in plugin catalog
fairclothjm May 17, 2022
493995a
add reload mechanism via context
fairclothjm May 17, 2022
9759a0e
readd v3 and v4 to pluginset
fairclothjm May 18, 2022
0c2cde6
call cleanup from reload if non-muxed
fairclothjm May 18, 2022
063f452
move v5 backend code to new package
fairclothjm May 19, 2022
1bfeadf
use context reload for for ErrPluginShutdown case
fairclothjm Aug 3, 2022
dcd3637
add wrapper on v5 backend
fairclothjm Aug 3, 2022
0d60783
fix run config UTs
fairclothjm Aug 3, 2022
787baeb
fix unit tests
fairclothjm Aug 4, 2022
bce9c30
remove comment and update AutoMTLS field in test
fairclothjm Aug 5, 2022
03e4cdc
remove comment
fairclothjm Aug 5, 2022
09a5e34
remove errwrap and unused context
fairclothjm Aug 9, 2022
012ae79
only support metadatamode false for v5 backend plugins
fairclothjm Aug 9, 2022
2b92594
update plugin catalog errors
fairclothjm Aug 18, 2022
acf94f7
use const for env variables
fairclothjm Aug 18, 2022
18882ff
rename locks and remove unused
fairclothjm Aug 18, 2022
3d3a1e8
remove unneeded nil check
fairclothjm Aug 18, 2022
3c826e8
improvements based on staticcheck recommendations
fairclothjm Aug 19, 2022
10b2546
use const for single implementation string
fairclothjm Aug 19, 2022
31c3e88
use const for context key
fairclothjm Aug 19, 2022
b32bc62
use info default log level
fairclothjm Aug 19, 2022
31711be
move pid to pluginClient struct
fairclothjm Aug 23, 2022
c0b71fa
remove v3 and v4 from multiplexed plugin set
fairclothjm Aug 23, 2022
70d99f0
return from reload when non-multiplexed
fairclothjm Aug 23, 2022
766f5fe
update automtls env string
fairclothjm Aug 23, 2022
3b08d5c
combine getBackend and getBrokeredClient
fairclothjm Aug 23, 2022
748b676
update comments for plugin reload, Backend return val and log
fairclothjm Aug 23, 2022
80b8b11
revert Backend return type
fairclothjm Aug 23, 2022
7f3f47e
Merge remote-tracking branch 'origin/main' into feat/mux-secrets-auth
fairclothjm Aug 23, 2022
86eafcd
allow non-muxed plugins to serve v5
fairclothjm Aug 24, 2022
bc2f3e1
move v5 code to existing sdk plugin package
fairclothjm Aug 24, 2022
4f886f4
do next export sdk fields now that we have removed extra plugin pkg
fairclothjm Aug 24, 2022
3f3b0bf
set TLSProvider in ServeMultiplex for backwards compat
fairclothjm Aug 24, 2022
fa40253
use bool to flag multiplexing support on grpc backend server
fairclothjm Aug 24, 2022
dc77bc1
Merge remote-tracking branch 'origin/main' into feat/mux-secrets-auth
fairclothjm Aug 26, 2022
4d7dbd1
revert userpass main.go
fairclothjm Aug 29, 2022
9431c2d
refactor plugin sdk
fairclothjm Aug 29, 2022
6afe64e
update comment and use multierr
fairclothjm Aug 29, 2022
828148e
attempt v4 if dispense fails on getPluginTypeForUnknown
fairclothjm Aug 29, 2022
fd833f1
update comments on sdk plugin backend
fairclothjm Aug 29, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
94 changes: 49 additions & 45 deletions api/plugin_helpers.go
Expand Up @@ -16,59 +16,63 @@ import (
"github.com/hashicorp/errwrap"
)

var (
const (
// PluginAutoMTLSEnv is used to ensure AutoMTLS is used. This will override
// setting a TLSProviderFunc for a plugin.
PluginAutoMTLSEnv = "VAULT_PLUGIN_AUTOMTLS_ENABLED"

// PluginMetadataModeEnv is an ENV name used to disable TLS communication
// to bootstrap mounting plugins.
PluginMetadataModeEnv = "VAULT_PLUGIN_METADATA_MODE"

// PluginUnwrapTokenEnv is the ENV name used to pass unwrap tokens to the
// plugin.
PluginUnwrapTokenEnv = "VAULT_UNWRAP_TOKEN"

// sudoPaths is a map containing the paths that require a token's policy
// to have the "sudo" capability. The keys are the paths as strings, in
// the same format as they are returned by the OpenAPI spec. The values
// are the regular expressions that can be used to test whether a given
// path matches that path or not (useful specifically for the paths that
// contain templated fields.)
sudoPaths = map[string]*regexp.Regexp{
"/auth/token/accessors/": regexp.MustCompile(`^/auth/token/accessors/$`),
"/pki/root": regexp.MustCompile(`^/pki/root$`),
"/pki/root/sign-self-issued": regexp.MustCompile(`^/pki/root/sign-self-issued$`),
"/sys/audit": regexp.MustCompile(`^/sys/audit$`),
"/sys/audit/{path}": regexp.MustCompile(`^/sys/audit/.+$`),
"/sys/auth/{path}": regexp.MustCompile(`^/sys/auth/.+$`),
"/sys/auth/{path}/tune": regexp.MustCompile(`^/sys/auth/.+/tune$`),
"/sys/config/auditing/request-headers": regexp.MustCompile(`^/sys/config/auditing/request-headers$`),
"/sys/config/auditing/request-headers/{header}": regexp.MustCompile(`^/sys/config/auditing/request-headers/.+$`),
"/sys/config/cors": regexp.MustCompile(`^/sys/config/cors$`),
"/sys/config/ui/headers/": regexp.MustCompile(`^/sys/config/ui/headers/$`),
"/sys/config/ui/headers/{header}": regexp.MustCompile(`^/sys/config/ui/headers/.+$`),
"/sys/leases": regexp.MustCompile(`^/sys/leases$`),
"/sys/leases/lookup/": regexp.MustCompile(`^/sys/leases/lookup/$`),
"/sys/leases/lookup/{prefix}": regexp.MustCompile(`^/sys/leases/lookup/.+$`),
"/sys/leases/revoke-force/{prefix}": regexp.MustCompile(`^/sys/leases/revoke-force/.+$`),
"/sys/leases/revoke-prefix/{prefix}": regexp.MustCompile(`^/sys/leases/revoke-prefix/.+$`),
"/sys/plugins/catalog/{name}": regexp.MustCompile(`^/sys/plugins/catalog/[^/]+$`),
"/sys/plugins/catalog/{type}": regexp.MustCompile(`^/sys/plugins/catalog/[\w-]+$`),
"/sys/plugins/catalog/{type}/{name}": regexp.MustCompile(`^/sys/plugins/catalog/[\w-]+/[^/]+$`),
"/sys/raw": regexp.MustCompile(`^/sys/raw$`),
"/sys/raw/{path}": regexp.MustCompile(`^/sys/raw/.+$`),
"/sys/remount": regexp.MustCompile(`^/sys/remount$`),
"/sys/revoke-force/{prefix}": regexp.MustCompile(`^/sys/revoke-force/.+$`),
"/sys/revoke-prefix/{prefix}": regexp.MustCompile(`^/sys/revoke-prefix/.+$`),
"/sys/rotate": regexp.MustCompile(`^/sys/rotate$`),

// enterprise-only paths
"/sys/replication/dr/primary/secondary-token": regexp.MustCompile(`^/sys/replication/dr/primary/secondary-token$`),
"/sys/replication/performance/primary/secondary-token": regexp.MustCompile(`^/sys/replication/performance/primary/secondary-token$`),
"/sys/replication/primary/secondary-token": regexp.MustCompile(`^/sys/replication/primary/secondary-token$`),
"/sys/replication/reindex": regexp.MustCompile(`^/sys/replication/reindex$`),
"/sys/storage/raft/snapshot-auto/config/": regexp.MustCompile(`^/sys/storage/raft/snapshot-auto/config/$`),
"/sys/storage/raft/snapshot-auto/config/{name}": regexp.MustCompile(`^/sys/storage/raft/snapshot-auto/config/[^/]+$`),
}
)

// sudoPaths is a map containing the paths that require a token's policy
// to have the "sudo" capability. The keys are the paths as strings, in
// the same format as they are returned by the OpenAPI spec. The values
// are the regular expressions that can be used to test whether a given
// path matches that path or not (useful specifically for the paths that
// contain templated fields.)
var sudoPaths = map[string]*regexp.Regexp{
"/auth/token/accessors/": regexp.MustCompile(`^/auth/token/accessors/$`),
"/pki/root": regexp.MustCompile(`^/pki/root$`),
"/pki/root/sign-self-issued": regexp.MustCompile(`^/pki/root/sign-self-issued$`),
"/sys/audit": regexp.MustCompile(`^/sys/audit$`),
"/sys/audit/{path}": regexp.MustCompile(`^/sys/audit/.+$`),
"/sys/auth/{path}": regexp.MustCompile(`^/sys/auth/.+$`),
"/sys/auth/{path}/tune": regexp.MustCompile(`^/sys/auth/.+/tune$`),
"/sys/config/auditing/request-headers": regexp.MustCompile(`^/sys/config/auditing/request-headers$`),
"/sys/config/auditing/request-headers/{header}": regexp.MustCompile(`^/sys/config/auditing/request-headers/.+$`),
"/sys/config/cors": regexp.MustCompile(`^/sys/config/cors$`),
"/sys/config/ui/headers/": regexp.MustCompile(`^/sys/config/ui/headers/$`),
"/sys/config/ui/headers/{header}": regexp.MustCompile(`^/sys/config/ui/headers/.+$`),
"/sys/leases": regexp.MustCompile(`^/sys/leases$`),
"/sys/leases/lookup/": regexp.MustCompile(`^/sys/leases/lookup/$`),
"/sys/leases/lookup/{prefix}": regexp.MustCompile(`^/sys/leases/lookup/.+$`),
"/sys/leases/revoke-force/{prefix}": regexp.MustCompile(`^/sys/leases/revoke-force/.+$`),
"/sys/leases/revoke-prefix/{prefix}": regexp.MustCompile(`^/sys/leases/revoke-prefix/.+$`),
"/sys/plugins/catalog/{name}": regexp.MustCompile(`^/sys/plugins/catalog/[^/]+$`),
"/sys/plugins/catalog/{type}": regexp.MustCompile(`^/sys/plugins/catalog/[\w-]+$`),
"/sys/plugins/catalog/{type}/{name}": regexp.MustCompile(`^/sys/plugins/catalog/[\w-]+/[^/]+$`),
"/sys/raw": regexp.MustCompile(`^/sys/raw$`),
"/sys/raw/{path}": regexp.MustCompile(`^/sys/raw/.+$`),
"/sys/remount": regexp.MustCompile(`^/sys/remount$`),
"/sys/revoke-force/{prefix}": regexp.MustCompile(`^/sys/revoke-force/.+$`),
"/sys/revoke-prefix/{prefix}": regexp.MustCompile(`^/sys/revoke-prefix/.+$`),
"/sys/rotate": regexp.MustCompile(`^/sys/rotate$`),

// enterprise-only paths
"/sys/replication/dr/primary/secondary-token": regexp.MustCompile(`^/sys/replication/dr/primary/secondary-token$`),
"/sys/replication/performance/primary/secondary-token": regexp.MustCompile(`^/sys/replication/performance/primary/secondary-token$`),
"/sys/replication/primary/secondary-token": regexp.MustCompile(`^/sys/replication/primary/secondary-token$`),
"/sys/replication/reindex": regexp.MustCompile(`^/sys/replication/reindex$`),
"/sys/storage/raft/snapshot-auto/config/": regexp.MustCompile(`^/sys/storage/raft/snapshot-auto/config/$`),
"/sys/storage/raft/snapshot-auto/config/{name}": regexp.MustCompile(`^/sys/storage/raft/snapshot-auto/config/[^/]+$`),
}

// PluginAPIClientMeta is a helper that plugins can use to configure TLS connections
// back to Vault.
type PluginAPIClientMeta struct {
Expand Down Expand Up @@ -120,7 +124,7 @@ func VaultPluginTLSProvider(apiTLSConfig *TLSConfig) func() (*tls.Config, error)
// VaultPluginTLSProviderContext is run inside a plugin and retrieves the response
// wrapped TLS certificate from vault. It returns a configured TLS Config.
func VaultPluginTLSProviderContext(ctx context.Context, apiTLSConfig *TLSConfig) func() (*tls.Config, error) {
if os.Getenv(PluginMetadataModeEnv) == "true" {
if os.Getenv(PluginAutoMTLSEnv) == "true" || os.Getenv(PluginMetadataModeEnv) == "true" {
return nil
}

Expand Down
6 changes: 1 addition & 5 deletions builtin/credential/userpass/cmd/userpass/main.go
Expand Up @@ -14,12 +14,8 @@ func main() {
flags := apiClientMeta.FlagSet()
flags.Parse(os.Args[1:])

tlsConfig := apiClientMeta.GetTLSConfig()
tlsProviderFunc := api.VaultPluginTLSProvider(tlsConfig)

if err := plugin.Serve(&plugin.ServeOpts{
if err := plugin.ServeMultiplex(&plugin.ServeOpts{
BackendFactoryFunc: userpass.Factory,
TLSProviderFunc: tlsProviderFunc,
fairclothjm marked this conversation as resolved.
Show resolved Hide resolved
}); err != nil {
logger := hclog.New(&hclog.LoggerOptions{})

Expand Down
17 changes: 15 additions & 2 deletions builtin/plugin/backend.go
Expand Up @@ -9,7 +9,9 @@ import (

log "github.com/hashicorp/go-hclog"

"github.com/hashicorp/go-multierror"
uuid "github.com/hashicorp/go-uuid"
v5 "github.com/hashicorp/vault/builtin/plugin/v5"
"github.com/hashicorp/vault/sdk/framework"
"github.com/hashicorp/vault/sdk/helper/consts"
"github.com/hashicorp/vault/sdk/logical"
Expand All @@ -23,13 +25,24 @@ var (

// Factory returns a configured plugin logical.Backend.
func Factory(ctx context.Context, conf *logical.BackendConfig) (logical.Backend, error) {
merr := &multierror.Error{}
_, ok := conf.Config["plugin_name"]
if !ok {
return nil, fmt.Errorf("plugin_name not provided")
}
b, err := Backend(ctx, conf)
b, err := v5.Backend(ctx, conf)
if err == nil {
if err := b.Setup(ctx, conf); err != nil {
return nil, err
}
return b, nil
}
merr = multierror.Append(merr, err)

b, err = Backend(ctx, conf)
if err != nil {
return nil, err
merr = multierror.Append(merr, err)
return nil, fmt.Errorf("invalid backend version: %s", merr)
}

if err := b.Setup(ctx, conf); err != nil {
fairclothjm marked this conversation as resolved.
Show resolved Hide resolved
Expand Down
66 changes: 53 additions & 13 deletions builtin/plugin/backend_test.go
Expand Up @@ -24,26 +24,66 @@ func TestBackend_impl(t *testing.T) {
}

func TestBackend(t *testing.T) {
config, cleanup := testConfig(t)
defer cleanup()

_, err := plugin.Backend(context.Background(), config)
if err != nil {
t.Fatal(err)
pluginCmds := []string{"TestBackend_PluginMain", "TestBackend_PluginMain_Multiplexed"}

for _, pluginCmd := range pluginCmds {
t.Run(pluginCmd, func(t *testing.T) {
config, cleanup := testConfig(t, pluginCmd)
defer cleanup()

_, err := plugin.Backend(context.Background(), config)
if err != nil {
t.Fatal(err)
}
})
}
}

func TestBackend_Factory(t *testing.T) {
config, cleanup := testConfig(t)
defer cleanup()
pluginCmds := []string{"TestBackend_PluginMain", "TestBackend_PluginMain_Multiplexed"}

for _, pluginCmd := range pluginCmds {
t.Run(pluginCmd, func(t *testing.T) {
config, cleanup := testConfig(t, pluginCmd)
defer cleanup()

_, err := plugin.Factory(context.Background(), config)
if err != nil {
t.Fatal(err)
}
})
}
}

func TestBackend_PluginMain(t *testing.T) {
args := []string{}
if os.Getenv(pluginutil.PluginUnwrapTokenEnv) == "" && os.Getenv(pluginutil.PluginMetadataModeEnv) != "true" {
return
}

caPEM := os.Getenv(pluginutil.PluginCACertPEMEnv)
if caPEM == "" {
t.Fatal("CA cert not passed in")
}

args = append(args, fmt.Sprintf("--ca-cert=%s", caPEM))

_, err := plugin.Factory(context.Background(), config)
apiClientMeta := &api.PluginAPIClientMeta{}
flags := apiClientMeta.FlagSet()
flags.Parse(args)
tlsConfig := apiClientMeta.GetTLSConfig()
tlsProviderFunc := api.VaultPluginTLSProvider(tlsConfig)

err := logicalPlugin.Serve(&logicalPlugin.ServeOpts{
BackendFactoryFunc: mock.Factory,
TLSProviderFunc: tlsProviderFunc,
})
if err != nil {
t.Fatal(err)
}
}

func TestBackend_PluginMain(t *testing.T) {
func TestBackend_PluginMain_Multiplexed(t *testing.T) {
args := []string{}
if os.Getenv(pluginutil.PluginUnwrapTokenEnv) == "" && os.Getenv(pluginutil.PluginMetadataModeEnv) != "true" {
return
Expand All @@ -62,7 +102,7 @@ func TestBackend_PluginMain(t *testing.T) {
tlsConfig := apiClientMeta.GetTLSConfig()
tlsProviderFunc := api.VaultPluginTLSProvider(tlsConfig)

err := logicalPlugin.Serve(&logicalPlugin.ServeOpts{
err := logicalPlugin.ServeMultiplex(&logicalPlugin.ServeOpts{
BackendFactoryFunc: mock.Factory,
TLSProviderFunc: tlsProviderFunc,
})
Expand All @@ -71,7 +111,7 @@ func TestBackend_PluginMain(t *testing.T) {
}
}

func testConfig(t *testing.T) (*logical.BackendConfig, func()) {
func testConfig(t *testing.T, pluginCmd string) (*logical.BackendConfig, func()) {
cluster := vault.NewTestCluster(t, nil, &vault.TestClusterOptions{
HandlerFunc: vaulthttp.Handler,
})
Expand All @@ -93,7 +133,7 @@ func testConfig(t *testing.T) (*logical.BackendConfig, func()) {

os.Setenv(pluginutil.PluginCACertPEMEnv, cluster.CACertPEMFile)

vault.TestAddTestPlugin(t, core.Core, "mock-plugin", consts.PluginTypeSecrets, "TestBackend_PluginMain", []string{}, "")
vault.TestAddTestPlugin(t, core.Core, "mock-plugin", consts.PluginTypeSecrets, pluginCmd, []string{}, "")

return config, func() {
cluster.Cleanup()
Expand Down